DrawTrace.hpp 18.8 KB
Newer Older
1 2 3 4 5 6 7
/*!
 *\file DrawTrace.hpp
 */

#ifndef DRAW_TRACE_HPP
#define DRAW_TRACE_HPP

8 9
#include <string>

10 11
#include "resource.hpp"

12
//#include "../render/render_opengl.hpp"
13
#include "../message/Message.hpp"
14

Clément Vuchener's avatar
Clément Vuchener committed
15
#include "DrawTree.hpp"
16 17

/*!
18 19
 * \class DrawTrace
 * \brief Browse the trace and call back T drawing methods
20 21 22 23 24 25 26 27
 */
template<class T>
class DrawTrace 
{

protected:

    //  Interface_graphic* _window;
28 29 30 31 32 33 34 35 36 37 38 39 40

    /*!
     * \brief Containers with states or events
     */
    std::list<const Container *> _entity_containers;
    /*!
     * \brief Containers with links
     */
    std::list<const Container *> _link_containers;
    /*!
     * \brief Containers with variables
     */
    std::list<const Container *> _variable_containers; 
41
    std::map<const Container *, Element_pos, std::less<const Container *> > _container_positions;
42 43
   
    /*!
44 45 46
     * \brief The default height for containers.
     */
    static const float _DRAWING_CONTAINER_HEIGHT_DEFAULT = 1.2f;
47
     
48 49 50 51
    /*!
     * \brief The default width for containers.
     */
    static const float _DRAWING_CONTAINER_WIDTH_DEFAULT = 2.5f;
52

53 54 55 56
    /*!
     * \brief The default horizontal space between containers.
     */
    static const float _DRAWING_CONTAINER_H_SPACE_DEFAULT = 0.1f;
57

58 59 60 61
    /*!
     * \brief The default vertical space between containers.
     */
    static const float _DRAWING_CONTAINER_V_SPACE_DEFAULT = 0.2f;
62 63 64


    /*!
65 66 67
     * \brief The default height for states.
     */
    static const float _DRAWING_STATE_HEIGHT_DEFAULT = 1.2f;
68
 
69 70 71 72
    /*!
     * \brief The default vertical space between states.
     */
    static const float _DRAWING_STATE_V_SPACE_DEFAULT = 0.2f;
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97


    /*!
     * \brief Geometrical informations about the trace shape.
     */
    Element_pos _container_width;
    Element_pos _container_height;
    Element_pos _container_h_space;
    Element_pos _container_v_space;
    
    Element_pos _state_height;
    Element_pos _state_v_space;
 

public:

    /***********************************
     *
     * Constructor and destructor.
     *
     **********************************/

    /*!
     * \brief The default constructor
     */
98
    DrawTrace() {
99 100

        //   _window = NULL;
Olivier Lagrasse's avatar
Olivier Lagrasse committed
101 102 103 104
        _container_width = _DRAWING_CONTAINER_WIDTH_DEFAULT;
        _container_height = _DRAWING_CONTAINER_HEIGHT_DEFAULT;
        _container_h_space = _DRAWING_CONTAINER_H_SPACE_DEFAULT;
        _container_v_space = _DRAWING_CONTAINER_V_SPACE_DEFAULT;
105
        
Olivier Lagrasse's avatar
Olivier Lagrasse committed
106 107
        _state_height = _DRAWING_STATE_HEIGHT_DEFAULT;
        _state_v_space = _DRAWING_STATE_V_SPACE_DEFAULT;
108 109 110 111 112 113 114

    }


    /*!
     * \brief The destructor
     */
115 116
    virtual ~DrawTrace() {
    
117 118 119 120 121 122 123 124 125 126 127 128 129 130
    }



    /***********************************
      *
      * Building functions.
      *
      **********************************/


    /*!
     * \brief The trace building function.
     * \param draw_object the kind of object which will be drawn (OpenGL, SVG...).
131
     * \param trace the trace data.
132
     */
Kevin Coulomb's avatar
+1  
Kevin Coulomb committed
133
    void build(T* draw_object, Trace* trace, double zoom, const Interval & interval) {
134 135 136 137

        draw_object->start_draw();

        draw_object->start_draw_containers();
138
        browse_container_tree(draw_object, trace);
139 140
        draw_object->end_draw_containers();

Kevin Coulomb's avatar
+1  
Kevin Coulomb committed
141
        browse_entities(draw_object,zoom,interval);
142 143 144 145 146 147 148 149 150 151 152

        draw_object->end_draw();
    }


    /***********************************
     *
     * Browsing functions.
     *
     **********************************/
    
153 154 155 156
    /*
     * \fn browse_container_tree(T* draw_object, Trace* trace)
     * \brief Function that browses the containers of the trace argument and make them painted with a T object
     */
157
    inline void browse_container_tree(T* draw_object, Trace* trace){
158
        /*** Drawing containers ***/
159
        int position = 0;
160
        
161 162
        const list<Container *> *root_containers = trace->get_root_containers();
        if (root_containers->empty()) {
163
            *Message::get_instance() << "There is no container. The trace can not be drawn." << Message::endw;
164
        }
165
        else
166 167 168 169 170
            for (list<Container *>::const_iterator i = root_containers->begin();
                i != root_containers->end();
                i++)
                position += browse_container(draw_object, *i, position, 0);
        
171
    }/* end browse_container_tree */
172
    
173 174 175 176 177

    /*
     * \fn browse_container(T* draw_object, const Container *container, int position, int depth)
     * \brief Recursive function that browse a container to draw it with a T painting object in position, knowing the current depth in the tree
     */
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
    int browse_container(T* draw_object, const Container *container, int position, int depth) {
        int size = 0;
        
        // Draw children
        const list<Container *> *children = container->get_children();
        for (list<Container *>::const_iterator i = children->begin();
            i != children->end();
            i++) {
            size += browse_container(draw_object, (*i), position+size, depth+1);
        }
        
        // Store the position to draw links
        _container_positions[container] = position + size; // First line after children
        
        // Use one line for states and events
        if (!container->get_states()->empty() || !container->get_events()->empty()) {
            size++;
            _entity_containers.push_back(container);
        }
        
198
        // Use one line for each variable
199 200 201 202 203 204 205 206 207 208 209 210 211 212
        if (container->get_variable_number() > 0) {
            _variable_containers.push_back(container);
            size += container->get_variable_number();
        }
        
        // Push containers with links to draw
        if (!container->get_links()->empty())
            _link_containers.push_back(container);
        
        // Draw this container
        draw_container(draw_object, position, size, depth, container->get_name().to_string());
        
        return size;
    }
213

214 215 216 217 218 219 220 221
    /*!
     * \brief Draw a container
     * \param draw_object Object that contains drawing methods to call
     * \param position Starting line of the container
     * \param size Height of the container in number of line
     * \param depth Depth of container in the tree
     * \param text Name of the container
     */
222 223 224 225 226 227 228
    inline void draw_container(T* draw_object, int position, int size, int depth, const std::string &text) {
        Element_pos x = depth*(_container_width+_container_h_space) + _container_h_space/2;
        Element_pos y = position*(_container_height+_container_v_space) + _container_v_space/2;
        Element_pos height = size*(_container_height+_container_v_space) - _container_v_space;
        draw_object->draw_container(x, y, _container_width, height);
        draw_object->draw_container_text(x, y+height/2, text);
    }
229

Clément Vuchener's avatar
Clément Vuchener committed
230 231 232
    /*!
     * \brief Browse the states list and draw them
     */
Kevin Coulomb's avatar
+1  
Kevin Coulomb committed
233
    inline void browse_entities(T* draw_object, double zoom, const Interval& interval) {
234
        const Container *container;
235
        BinaryTree<StateChange> *state_tree;
Clément Vuchener's avatar
Clément Vuchener committed
236
        BinaryTree<Event> *event_tree;
237 238
        const list<Link *> *link_list;
        Link *link;
239 240 241 242
        const map<VariableType *, Variable *> *variable_map;
        Variable *var;
        const list<pair<Date, Double> > *variable_values;
        int position;
Kevin Coulomb's avatar
+1  
Kevin Coulomb committed
243 244 245 246 247
        int lvl_zoom;
        if(zoom>=0)
            lvl_zoom = zoom;
        else
            lvl_zoom = 0;
248
        
249 250 251 252 253 254
        draw_object->start_draw_states();
        for (list<const Container *>::const_iterator c = _entity_containers.begin();
            c != _entity_containers.end();
            c++) {
            container = *c;
            position = _container_positions[container];
255
	        
256 257 258 259 260 261
            state_tree = container->get_states();
            event_tree = container->get_events();
            if (!state_tree->empty() || !event_tree->empty()) {
                // Browse states

        
Kevin Coulomb's avatar
+1  
Kevin Coulomb committed
262
                DrawTree<T, StateChange>(draw_object, position, lvl_zoom,
263
                                   _container_height, _container_v_space, _state_height, _state_v_space)
Kevin Coulomb's avatar
+1  
Kevin Coulomb committed
264
                    .draw_tree(state_tree, interval);
Clément Vuchener's avatar
Clément Vuchener committed
265
                
266
                // Browse events
Kevin Coulomb's avatar
+1  
Kevin Coulomb committed
267
                DrawTree<T, Event>(draw_object, position, lvl_zoom,
268
                                   _container_height, _container_v_space, _state_height, _state_v_space)
Kevin Coulomb's avatar
+1  
Kevin Coulomb committed
269
                    .draw_tree(event_tree, interval);
270 271
            }
            
Clément Vuchener's avatar
Clément Vuchener committed
272 273
            
            
274
        }/* end while (!_stack_states.empty()) */
Olivier Lagrasse's avatar
Olivier Lagrasse committed
275 276

        draw_object->end_draw_states();
277
        
Olivier Lagrasse's avatar
Olivier Lagrasse committed
278 279 280

        draw_object->start_draw_arrows();

281 282 283 284
        for (list<const Container *>::const_iterator c = _link_containers.begin();
            c != _link_containers.end();
            c++) {
            container = *c;
285 286 287
	        
	        // Browse links
	        link_list = container->get_links();
288 289 290 291 292 293 294 295 296 297
                for (list<Link *>::const_iterator it = link_list->begin();
                     it != link_list->end();
                     it++) {
                    
                    link = *it;
                    draw_link(draw_object, link->get_start_time().get_value(), link->get_end_time().get_value(), 
                              _container_positions[link->get_source()], _container_positions[link->get_destination()]); 
                }/* end for */
                
                
298
        }/* end while (!_stack_states.empty()) */
299
        
Olivier Lagrasse's avatar
Olivier Lagrasse committed
300
        draw_object->end_draw_arrows();
301
        
302
        draw_object->start_draw_counter();
303
     
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
        for (list<const Container *>::const_iterator c = _entity_containers.begin();
            c != _entity_containers.end();
            c++) {
            container = *c;
            position = _container_positions[container];
            if (!container->get_states()->empty() || !container->get_events()->empty())
                position++;
            // Browse variables
	        variable_map = container->get_variables();
            for (map<VariableType *, Variable *>::const_iterator i = variable_map->begin();
                i != variable_map->end();
                i++) {
    	        
    	        var = (*i).second;
                double min = var->get_min().get_value();
                double max = var->get_max().get_value();
                variable_values = var->get_values();
                
322
                draw_variable_value(draw_object, 0.0, 0.0, position);
323 324 325 326 327 328
                for (list<pair<Date, Double> >::const_iterator value = variable_values->begin();
                    value != variable_values->end();
                    value++) {
                    /* Call the object state drawing function */ 
                    draw_variable_value(draw_object, (*value).first.get_value(), ((*value).second.get_value()-min)/(max-min), position); 
                }
329
                draw_variable_value(draw_object, 0.0, 0.0, position);
330 331
                position++; // One line was used
            }/* end for */
332
        }
333
        draw_object->end_draw_counter();
334 335

    }
336
  
337
    
338 339 340 341 342 343 344
    /*!
     * \brief Draw a point of a variable curve
     * \param draw_object Object that contains the drawing methods
     * \param time Time of the point
     * \param value Value of of the variable (between 0.0 and 1.0)
     * \param position Line where the variable is drawn
     */
345 346 347 348 349 350
    inline void draw_variable_value(T *draw_object, double time, double value, int position) {
        Element_pos y = (position+1)*(_container_height+_container_v_space) - _container_v_space/2 -
            value*_container_height;
        draw_object->draw_counter(time, y);
    }
    
351 352 353 354 355 356 357 358
    /*!
     * \brief Draw a link
     * \param draw_object Object that contains the drawing methods
     * \param starttime Time of the start of the link
     * \param endtime Time of the end of the link
     * \param start Line of the start of the link
     * \param end Line of the end of the link
     */
359 360 361 362 363
    inline void draw_link(T *draw_object, double starttime, double endtime, int start, int end) {
        Element_pos y1 = ((double)start+0.5)*(_container_height+_container_v_space);
        Element_pos y2 = ((double)end+0.5)*(_container_height+_container_v_space);
        draw_object->draw_arrow(starttime, endtime, y1, y2);
    }
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
    
    /*
     * \brief Assuming someone has clicked in (x, y), display the description corresponding to the item clicked
     */
    void display_information(const Trace *trace, double x, double y, double d) {
        const Container *container = NULL;
        const Container *ancestor = NULL;
        const Link *link;
        const Event *event;
        const State *state;
        const Variable *variable;
        // find container needs to know the position of each container
        double yr = y;
        const list<Container *> *root_containers = trace->get_root_containers();
        if (!root_containers->empty())
            for (list<Container *>::const_iterator i = root_containers->begin();
                i != root_containers->end();
                i++)
                if ((container = search_container_by_position(*i, yr)))
                    break;
        
        // If the clic is out
        if (!container)
            return;
        
        ancestor = container;
        
        // First we browse to find a communication
        while (ancestor) {
            if (link = get_link(ancestor, x, y, d)) {
                *Message::get_instance() << "Link: " << link->get_value()->get_name().to_string() << Message::endi;
                return;
            }
            else
                ancestor = ancestor->get_parent();
        }
        
        // Now browsing for the events of the container root
        // Verification if it is a clic on an event
        if ((!container->get_events()->empty() || !container->get_states()->empty()) && yr < _container_height+_container_v_space) {
            if ((event = find_event(container, x, d))) {
                *Message::get_instance() << "Event: " << event->get_value()->get_name().to_string() << Message::endi;
                return;
            }
            if ((state = find_state(container, x))) {
                *Message::get_instance() << "State: " << state->get_value()->get_name().to_string() << Message::endi;
                return;
            }
        }
        else {
            if (!container->get_events()->empty() || !container->get_states()->empty())
                yr -= _container_height+_container_v_space;
            const map<VariableType *, Variable *> *variable_map = container->get_variables();
            map<VariableType *, Variable *>::const_iterator i = variable_map->begin();
            while (yr > _container_height+_container_v_space) {
                yr -= _container_height+_container_v_space;
                i++;                
            }
            if (i != variable_map->end()) {
                *Message::get_instance() << "Variable: " << (*i).second->get_type()->get_name().to_string() << Message::endi;
                return;
            }
        }
        
        // Nothing has been found
        return;// NULL;
        
        
    }
       
    const Container *search_container_by_position(const Container *container, double &y) {
        const Container *result;
        // Search if the result is a descendant
        const list<Container *> *children = container->get_children();
        for (list<Container *>::const_iterator i = children->begin();
            i != children->end();
            i++) {
            if ((result = search_container_by_position(*i, y)) != NULL)
                return result;
        }
        
        // Calculate the size of the container (without its children)
        int size;
        if (!container->get_states()->empty() || !container->get_events()->empty()) {
            size++;
            _entity_containers.push_back(container);
        }
        if (container->get_variable_number() > 0) {
            _variable_containers.push_back(container);
            size += container->get_variable_number();
        }
        
        // Test if the position is in this container
        if (y < size*(_container_height+_container_v_space))
            return container;
        
        // The position is outside this container
        return NULL;
    }   
463

464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
    /*!
     * \brief Tries to find a link passing by x and y in the container
     */
    const Link* get_link(const Container *container, Element_pos x, Element_pos y, Element_pos d) {
        const list<Link *> *link_list;
        Link *link;
        double a, b, c; // Equation: ax + by + c = 0
        double x1, x2, y1, y2;
    
        if(!container)
            return NULL;
        
        // Browse links
        link_list = container->get_links();
        for (list<Link *>::const_iterator it = link_list->begin();
             it != link_list->end();
             it++) {
            
            link = *it;
            x1 = link->get_start_time().get_value();
            x2 = link->get_end_time().get_value();
            y1 = ((double)_container_positions[link->get_source()]+0.5)*(_container_height+_container_v_space);
            y2 = ((double)_container_positions[link->get_destination()]+0.5)*(_container_height+_container_v_space);
            if (x1-d <= x && x <= x2+d || x2-d <= x && x <= x1+d) { // Test the interval
                a = y1 - y2;
                b = x2 - x1;
                c = -(a*x1 + b*x2);
            
                double e = a*x + b*y + c;
                if (e*e/(a*a + b*b) < d*d) // Test the distance
                    return link;
            }
        }
        
        return NULL;
    }
    
    /*
     * \brief Returns the event that occurs at the time x in the container
     */
    const Event *find_event(const Container *container, Element_pos x, Element_pos d) {
        if(!container)
            return NULL;
    
        Node<Event> *node = container->get_events()->get_root();
    
        while(node) {
            Element_pos t = node->get_element()->get_time().get_value();
            if (x < t) {
                if (t - x < d)
                    return node->get_element();
                node = node->get_left_child();
            }
            else {
                if (x - t < d)
                    return node->get_element();
                node = node->get_right_child();
            }
        }
        
        return NULL;
    }
    
    /*
     * \brief Returns the state at the time x
     */
    const State * find_state(const Container *container, Element_pos x) {
        if(!container)
            return NULL;
    
        Node<StateChange> *node = container->get_states()->get_root();
    
        while(node) {
            Element_pos t = node->get_element()->get_time().get_value();
            if (x < t) {
                if (node->get_element()->get_left_state()->get_start_time().get_value() < x)
                    return node->get_element()->get_left_state();
                node = node->get_left_child();
            }
            else {
                if (x < node->get_element()->get_right_state()->get_end_time().get_value())
                    return node->get_element()->get_right_state();
                node = node->get_right_child();
            }
        }
        
        return NULL;
    }
};
    
554
#endif