COIN-OR::LEMON - Graph Library

source: lemon-0.x/gui/graph_displayer_canvas.cc @ 1498:352b1ee13bc0

Last change on this file since 1498:352b1ee13bc0 was 1497:529e48eb9786, checked in by Hegyi Péter, 19 years ago

Broken edges are appearing.

  • Property exe set to *
File size: 18.7 KB
Line 
1#include <graph_displayer_canvas.h>
2#include <broken_edge.h>
3#include <math.h>
4
5GraphDisplayerCanvas::GraphDisplayerCanvas(Graph & gr, CoordinatesMap & cm, MapStorage & ms):g(gr),nodesmap(g),edgesmap(g),edgetextmap(g),displayed_graph(*(root()), 0, 0),mapstorage(ms),isbutton(false),active_item(NULL),target_item(NULL)
6{
7 
8  actual_handler=displayed_graph.signal_event().connect(sigc::mem_fun(*this, &GraphDisplayerCanvas::create_edge_event_handler), false);
9
10  active_node=INVALID;
11  active_edge=INVALID;
12
13  //set_center_scroll_region(true);
14
15  //first edges are drawn, to hide joining with nodes later
16
17  for (EdgeIt i(g); i!=INVALID; ++i)
18  {
19
20    //drawing green lines, coordinates are from cm
21
22    Gnome::Canvas::Points coos;
23    coos.push_back(Gnome::Art::Point(cm[g.source(i)].x,cm[g.source(i)].y));
24    coos.push_back(Gnome::Art::Point(cm[g.target(i)].x,cm[g.target(i)].y));
25   
26    //edgesmap[i]=new Gnome::Canvas::Line(displayed_graph, coos);
27    edgesmap[i]=new BrokenEdge(displayed_graph, coos);
28    *(edgesmap[i]) << Gnome::Canvas::Properties::fill_color("green");
29    edgesmap[i]->property_width_pixels().set_value(10);   
30   
31    //initializing edge-text as well, to empty string
32
33    double x1, x2, y1, y2;
34    edgesmap[i]->get_bounds(x1, y1, x2, y2);
35   
36    edgetextmap[i]=new Gnome::Canvas::Text(displayed_graph,(x1+x2)/2, (y1+y2)/2, "");
37    edgetextmap[i]->property_fill_color().set_value("black");
38  }
39
40  //afterwards nodes come to be drawn
41
42  NodeIt i(g);
43  int maxx=0, maxy=0, minx=(int)cm[i].x, miny=(int)cm[i].y;
44
45  for (; i!=INVALID; ++i)
46  {
47    //minimum and maximum is gathered to be able to zoom to the graph correctly (whole figure should be seen)
48
49    if(cm[i].x>maxx)maxx=(int)cm[i].x;
50    if(cm[i].y>maxy)maxy=(int)cm[i].y;
51    if(cm[i].x<minx)minx=(int)cm[i].x;
52    if(cm[i].y<miny)miny=(int)cm[i].y;
53
54    //drawing bule nodes, with black line around them
55
56    nodesmap[i]=new Gnome::Canvas::Ellipse(displayed_graph, cm[i].x-20, cm[i].y-20, cm[i].x+20, cm[i].y+20);
57    *(nodesmap[i]) << Gnome::Canvas::Properties::fill_color("blue");
58    *(nodesmap[i]) << Gnome::Canvas::Properties::outline_color("black");
59    //!!!!!!! (nodesmap[i])->signal_event().connect(sigc::bind(sigc::mem_fun(*this, &GraphDisplayerCanvas::event_handler),i));
60  }
61
62  updateScrollRegion();
63}
64
65GraphDisplayerCanvas::~GraphDisplayerCanvas()
66{
67
68  //writing out the end state of the graph
69  //\todo all the maps has to be write out!
70
71  Graph::NodeMap <int> id(g);
72  Graph::NodeMap <double> xc(g);
73  Graph::NodeMap <double> yc(g);
74 
75  int j=1;
76 
77  for (NodeIt i(g); i!=INVALID; ++i)
78  {
79    double x1,y1,x2,y2;
80    nodesmap[i]->get_bounds(x1, y1, x2, y2);
81   
82    id[i]=j++;
83    xc[i]=(x1+x2)/2;
84    yc[i]=(y1+y2)/2;
85  }
86
87  GraphWriter<Graph> writer(std::cout,g);
88 
89  writer.writeNodeMap("id", id);
90  writer.writeNodeMap("coordinates_x", xc);
91  writer.writeNodeMap("coordinates_y", yc);
92  writer.run();
93}
94
95int GraphDisplayerCanvas::changeLineWidth (std::string mapname)
96{
97  for (EdgeIt i(g); i!=INVALID; ++i)
98  {
99    int w=(int)(*(mapstorage.edgemap_storage)[mapname])[i];
100    edgesmap[i]->property_width_pixels().set_value(w);
101  }
102  return 0;
103};
104
105int GraphDisplayerCanvas::changeColor (std::string mapname)
106
107
108  //function maps the range of the maximum and
109  //the minimum of the nodemap to the range of
110  //green in RGB
111
112  for (EdgeIt i(g); i!=INVALID; ++i)
113  {
114    double w=(*(mapstorage.edgemap_storage)[mapname])[i];
115    double max=mapstorage.maxOfEdgeMap(mapname);
116    double min=mapstorage.minOfEdgeMap(mapname);
117     
118    //std::cout<<w<<" "<<max<<" "<<min<<" "<<100*(w-min)/(max-min)<<std::endl;
119    Gdk::Color color;
120    if(max!=min)
121    {
122      color.set_rgb_p (0, 100*(w-min)/(max-min), 0);
123    }
124    else
125    {
126      color.set_rgb_p (0, 100, 0);
127    }
128
129    edgesmap[i]->property_fill_color_gdk().set_value(color);
130  }
131  return 0;
132};
133
134int GraphDisplayerCanvas::changeText (std::string mapname)
135{
136
137  //the number in the map will be written on the edge
138  //EXCEPT when the name of the map is Text, because
139  //in that case empty string will be written, because
140  //that is the deleter map
141  //\todo isn't it a bit woodcutter?
142
143  for (EdgeIt i(g); i!=INVALID; ++i)
144  {
145    if(mapname!="Text")
146    {
147      double number=(*(mapstorage.edgemap_storage)[mapname])[i];
148      int length=(int)(floor(log(number)/log(10)))+1;
149      int maxpos=(int)(pow(10,length-1));
150      int strl=length+1+RANGE;
151      char * str=new char[strl];
152      str[length]='.';
153      str[strl]='\0';
154     
155      for(int j=0;j<strl;j++)
156      {
157        if(j!=length)
158        {
159          int digit=(int)(number/maxpos);
160          str[j]=(digit+'0');
161          number-=digit*maxpos;
162          number*=10;
163        }
164      }
165     
166      edgetextmap[i]->property_text().set_value(str);
167    }
168    else
169    {
170      edgetextmap[i]->property_text().set_value("");
171    }
172  }
173  return 0;
174};
175
176bool GraphDisplayerCanvas::event_handler(GdkEvent* e, Node n)
177{
178  switch(e->type)
179  {
180    case GDK_BUTTON_PRESS:
181      //we mark the location of the event to be able to calculate parameters of dragging
182      clicked_x=e->button.x;
183      clicked_y=e->button.y;
184      active_item=(get_item_at(e->button.x, e->button.y));
185      isbutton=true;
186      break;
187    case GDK_BUTTON_RELEASE:
188      isbutton=false;
189      active_item=NULL;
190      updateScrollRegion();
191      break;
192    case GDK_MOTION_NOTIFY:
193      //we only have to do sg. if the mouse button is pressed
194      if(isbutton)
195      {
196        //new coordinates will be the old values,
197        //because the item will be moved to the
198        //new coordinate therefore the new movement
199        //has to be calculated from here
200
201        double dx=e->motion.x-clicked_x;
202        double dy=e->motion.y-clicked_y;
203        active_item->move(dx, dy);
204        clicked_x=e->motion.x;
205        clicked_y=e->motion.y;
206
207        //all the edges connected to the moved point has to be redrawn
208
209        EdgeIt e;
210        g.firstOut(e,n);
211        for(;e!=INVALID;g.nextOut(e))
212        {
213            Gnome::Canvas::Points coos;
214            double x1, x2, y1, y2;
215
216            nodesmap[g.source(e)]->get_bounds(x1, y1, x2, y2);
217            coos.push_back(Gnome::Art::Point((x1+x2)/2,(y1+y2)/2));
218
219            nodesmap[g.target(e)]->get_bounds(x1, y1, x2, y2);
220            coos.push_back(Gnome::Art::Point((x1+x2)/2,(y1+y2)/2));
221
222            edgesmap[e]->property_points().set_value(coos);
223
224            edgesmap[e]->get_bounds(x1, y1, x2, y2);
225
226            edgetextmap[e]->property_x().set_value((x1+x2)/2);
227            edgetextmap[e]->property_y().set_value((y1+y2)/2);
228        }
229
230        g.firstIn(e,n);
231        for(;e!=INVALID;g.nextIn(e))
232        {
233            Gnome::Canvas::Points coos;
234            double x1, x2, y1, y2;
235
236            nodesmap[g.source(e)]->get_bounds(x1, y1, x2, y2);
237            coos.push_back(Gnome::Art::Point((x1+x2)/2,(y1+y2)/2));
238
239            nodesmap[g.target(e)]->get_bounds(x1, y1, x2, y2);
240            coos.push_back(Gnome::Art::Point((x1+x2)/2,(y1+y2)/2));
241
242            edgesmap[e]->property_points().set_value(coos);
243
244            edgesmap[e]->get_bounds(x1, y1, x2, y2);
245
246            edgetextmap[e]->property_x().set_value((x1+x2)/2);
247            edgetextmap[e]->property_y().set_value((y1+y2)/2);
248        }
249      }
250    default: break;
251  }
252  return true;
253}
254
255bool GraphDisplayerCanvas::on_expose_event(GdkEventExpose *event)
256{
257  Gnome::Canvas::CanvasAA::on_expose_event(event);
258  //usleep(10000);
259  //rezoom();
260  return true;
261}
262
263void GraphDisplayerCanvas::zoomIn()
264{
265  set_pixels_per_unit(
266      (1.0 + (double) zoom_step / 100.0) * get_pixels_per_unit());
267}
268
269void GraphDisplayerCanvas::zoomOut()
270{
271  set_pixels_per_unit(
272      (1.0 - (double) zoom_step / 100.0) * get_pixels_per_unit());
273}
274
275void GraphDisplayerCanvas::zoomFit()
276{
277  // get the height and width of the canvas
278  Gtk::Allocation a = get_allocation();
279  int aw = a.get_width();
280  int ah = a.get_height();
281  // add some space
282  aw -= 5; if (aw < 0) aw = 0;
283  ah -= 5; if (ah < 0) ah = 0;
284
285  // get the bounding box of the graph
286  double wx1, wy1, wx2, wy2;
287  Gnome::Canvas::Item* pCanvasItem = root();
288  pCanvasItem->get_bounds(wx1, wy1, wx2, wy2);
289
290  // fit the graph to the window
291  double ppu1 = (double) aw / fabs(wx2 - wx1);
292  double ppu2 = (double) ah / fabs(wy2 - wy1);
293  set_pixels_per_unit((ppu1 < ppu2) ? ppu1 : ppu2);
294}
295
296void GraphDisplayerCanvas::zoom100()
297{
298  set_pixels_per_unit(1.0);
299}
300
301void GraphDisplayerCanvas::updateScrollRegion()
302{
303  double wx1, wy1, wx2, wy2;
304  Gnome::Canvas::Item* pCanvasItem = root();
305  pCanvasItem->get_bounds(wx1, wy1, wx2, wy2);
306  set_scroll_region(wx1, wy1, wx2, wy2);
307}
308
309void GraphDisplayerCanvas::changeEditorialTool(int newtool)
310{
311  actual_handler.disconnect();
312
313  switch(newtool)
314    {
315    case MOVE:
316      actual_handler=displayed_graph.signal_event().connect(sigc::mem_fun(*this, &GraphDisplayerCanvas::move_event_handler), false);
317      break;
318
319      //it has to assigned to canvas, because all the canvas has to be monitored, not only the elements of the already drawn group
320    case CREATE_NODE:
321      actual_handler=signal_event().connect(sigc::mem_fun(*this, &GraphDisplayerCanvas::create_node_event_handler), false);
322      break;
323
324    case CREATE_EDGE:
325      actual_handler=displayed_graph.signal_event().connect(sigc::mem_fun(*this, &GraphDisplayerCanvas::create_edge_event_handler), false);
326      break;
327
328    case ERASER:
329      actual_handler=displayed_graph.signal_event().connect(sigc::mem_fun(*this, &GraphDisplayerCanvas::eraser_event_handler), false);
330      break;
331
332    default:
333      break;
334    }
335}
336
337bool GraphDisplayerCanvas::move_event_handler(GdkEvent* e)
338{
339  switch(e->type)
340  {
341    case GDK_BUTTON_PRESS:
342      //we mark the location of the event to be able to calculate parameters of dragging
343      clicked_x=e->button.x;
344      clicked_y=e->button.y;
345      active_item=(get_item_at(e->button.x, e->button.y));
346      active_node=INVALID;
347      for (NodeIt i(g); i!=INVALID; ++i)
348        {
349          if(nodesmap[i]==active_item)
350            {
351              active_node=i;
352            }
353        }
354      isbutton=true;
355      break;
356    case GDK_BUTTON_RELEASE:
357      isbutton=false;
358      active_item=NULL;
359      active_node=INVALID;
360      updateScrollRegion();
361      break;
362    case GDK_MOTION_NOTIFY:
363      //we only have to do sg. if the mouse button is pressed AND the click was on a node that was found in the set of nodes
364      if(active_node!=INVALID)
365      {
366        //new coordinates will be the old values,
367        //because the item will be moved to the
368        //new coordinate therefore the new movement
369        //has to be calculated from here
370
371        double dx=e->motion.x-clicked_x;
372        double dy=e->motion.y-clicked_y;
373
374        active_item->move(dx, dy);
375
376        clicked_x=e->motion.x;
377        clicked_y=e->motion.y;
378
379        //all the edges connected to the moved point has to be redrawn
380        EdgeIt e;
381
382        g.firstOut(e,active_node);
383
384        for(;e!=INVALID;g.nextOut(e))
385        {
386            Gnome::Canvas::Points coos;
387            double x1, x2, y1, y2;
388
389            nodesmap[g.source(e)]->get_bounds(x1, y1, x2, y2);
390            coos.push_back(Gnome::Art::Point((x1+x2)/2,(y1+y2)/2));
391
392            nodesmap[g.target(e)]->get_bounds(x1, y1, x2, y2);
393            coos.push_back(Gnome::Art::Point((x1+x2)/2,(y1+y2)/2));
394
395            edgesmap[e]->property_points().set_value(coos);
396
397            edgesmap[e]->get_bounds(x1, y1, x2, y2);
398
399            edgetextmap[e]->property_x().set_value((x1+x2)/2);
400            edgetextmap[e]->property_y().set_value((y1+y2)/2);
401        }
402
403        g.firstIn(e,active_node);
404        for(;e!=INVALID;g.nextIn(e))
405        {
406            Gnome::Canvas::Points coos;
407            double x1, x2, y1, y2;
408
409            nodesmap[g.source(e)]->get_bounds(x1, y1, x2, y2);
410            coos.push_back(Gnome::Art::Point((x1+x2)/2,(y1+y2)/2));
411
412            nodesmap[g.target(e)]->get_bounds(x1, y1, x2, y2);
413            coos.push_back(Gnome::Art::Point((x1+x2)/2,(y1+y2)/2));
414
415            edgesmap[e]->property_points().set_value(coos);
416
417            edgesmap[e]->get_bounds(x1, y1, x2, y2);
418
419            edgetextmap[e]->property_x().set_value((x1+x2)/2);
420            edgetextmap[e]->property_y().set_value((y1+y2)/2);
421        }
422      }
423    default: break;
424  }
425
426  return true;
427}
428
429bool GraphDisplayerCanvas::create_node_event_handler(GdkEvent* e)
430{
431  switch(e->type)
432    {
433
434      //draw the new node in red at the clicked place
435    case GDK_BUTTON_PRESS:
436      isbutton=true;
437
438      active_node=NodeIt(g,g.addNode());
439
440      window_to_world (e->button.x, e->button.y, clicked_x, clicked_y);
441
442      nodesmap[active_node]=new Gnome::Canvas::Ellipse(displayed_graph, clicked_x-20, clicked_y-20, clicked_x+20, clicked_y+20);
443      active_item=(Gnome::Canvas::Item *)(nodesmap[active_node]);
444      *(nodesmap[active_node]) << Gnome::Canvas::Properties::fill_color("red");
445      *(nodesmap[active_node]) << Gnome::Canvas::Properties::outline_color("black");
446      (nodesmap[active_node])->show();
447      break;
448
449      //move the new node
450    case GDK_MOTION_NOTIFY:
451      {
452        double world_motion_x, world_motion_y;
453        GdkEvent * generated=new GdkEvent();
454        window_to_world (e->motion.x, e->motion.y, world_motion_x, world_motion_y);
455        generated->motion.x=world_motion_x;
456        generated->motion.y=world_motion_y;
457        generated->type=GDK_MOTION_NOTIFY;
458        move_event_handler(generated);     
459        break;
460      }
461
462      //finalize the new node
463    case GDK_BUTTON_RELEASE:
464      isbutton=false;
465      *active_item << Gnome::Canvas::Properties::fill_color("blue");
466      active_item=NULL;
467      active_node=INVALID;
468      updateScrollRegion();
469      break;
470    default:
471      break;
472    }
473  return false;
474}
475
476bool GraphDisplayerCanvas::create_edge_event_handler(GdkEvent* e)
477{
478  switch(e->type)
479    {
480    case GDK_BUTTON_PRESS:
481      //in edge creatino right button has special meaning
482      if(e->button.button!=3)
483        {
484          //there is not yet selected node
485          if(active_node==INVALID)
486            {
487              //we mark the location of the event to be able to calculate parameters of dragging
488              clicked_x=e->button.x;
489              clicked_y=e->button.y;
490              active_item=(get_item_at(e->button.x, e->button.y));
491              active_node=INVALID;
492              for (NodeIt i(g); i!=INVALID; ++i)
493                {
494                  if(nodesmap[i]==active_item)
495                    {
496                      active_node=i;
497                    }
498                }
499              //the clicked item is really a node
500              if(active_node!=INVALID)
501                {
502                  *(nodesmap[active_node]) << Gnome::Canvas::Properties::fill_color("red");
503                  isbutton=true;
504                }
505              //clicked item was not a node. It could be e.g. edge.
506              else
507                {
508                  active_item=NULL;
509                }
510            }
511          //we only have to do sg. if the mouse button
512          // is pressed already once AND the click was
513          // on a node that was found in the set of
514          //nodes, and now we only search for the second
515          //node
516          else
517            {
518              target_item=(get_item_at(e->button.x, e->button.y));
519              Graph::NodeIt target_node=INVALID;
520              for (NodeIt i(g); i!=INVALID; ++i)
521                {
522                  if(nodesmap[i]==target_item)
523                    {
524                      target_node=i;
525                    }
526                }
527              //the clicked item is a node, the edge can be drawn
528              if(target_node!=INVALID)
529                {
530                  *(nodesmap[target_node]) << Gnome::Canvas::Properties::fill_color("red");
531
532                  //creating new edge
533                  active_edge=EdgeIt(g,g.addEdge(active_node, target_node));
534         
535                  //calculating coordinates of new edge
536                  Gnome::Canvas::Points coos;
537                  double x1, x2, y1, y2;
538         
539                  active_item->get_bounds(x1, y1, x2, y2);
540                  coos.push_back(Gnome::Art::Point((x1+x2)/2,(y1+y2)/2));
541
542                  target_item->get_bounds(x1, y1, x2, y2);
543                  coos.push_back(Gnome::Art::Point((x1+x2)/2,(y1+y2)/2));
544
545                  //drawing new edge
546                  edgesmap[active_edge]=new Gnome::Canvas::Line(displayed_graph, coos);
547                  *(edgesmap[active_edge]) << Gnome::Canvas::Properties::fill_color("green");
548                  edgesmap[active_edge]->property_width_pixels().set_value(10);
549
550                  //redraw nodes to blank terminations of the new edge
551                  target_item->raise_to_top();
552                  active_item->raise_to_top();
553
554                  //initializing edge-text as well, to empty string
555                  edgesmap[active_edge]->get_bounds(x1, y1, x2, y2);
556                  edgetextmap[active_edge]=new Gnome::Canvas::Text(displayed_graph,(x1+x2)/2, (y1+y2)/2, "");
557                  edgetextmap[active_edge]->property_fill_color().set_value("black");
558                }
559              //clicked item was not a node. it could be an e.g. edge. we do not deal with it furthermore.
560              else
561                {
562                  target_item=NULL;
563                }
564            }
565        }
566      break;
567    case GDK_BUTTON_RELEASE:
568      isbutton=false;
569      //we clear settings in two cases
570      //1: the edge is ready (target_item has valid value)
571      //2: the edge creation is cancelled with right button
572      if((target_item)||(e->button.button==3))
573        {
574          if(active_item)
575            {
576              *active_item << Gnome::Canvas::Properties::fill_color("blue");
577              active_item=NULL;
578            }
579          if(target_item)
580            {
581              *target_item << Gnome::Canvas::Properties::fill_color("blue");
582              target_item=NULL;
583            }
584          active_node=INVALID;
585          active_edge=INVALID;
586        }
587      break;
588    default:
589      break;
590    }
591  return false;
592}
593
594bool GraphDisplayerCanvas::eraser_event_handler(GdkEvent* e)
595{
596  switch(e->type)
597    {
598    case GDK_BUTTON_PRESS:
599      active_item=(get_item_at(e->button.x, e->button.y));
600      active_node=INVALID;
601      active_edge=INVALID;
602      for (NodeIt i(g); i!=INVALID; ++i)
603        {
604          if(nodesmap[i]==active_item)
605            {
606              active_node=i;
607            }
608        }
609      if(active_node==INVALID)
610        {
611          for (EdgeIt i(g); i!=INVALID; ++i)
612            {
613              if(edgesmap[i]==active_item)
614                {
615                  active_edge=i;
616                }
617            }
618        }
619    *active_item << Gnome::Canvas::Properties::fill_color("red");
620      break;
621
622    case GDK_BUTTON_RELEASE:
623      if(active_item==(get_item_at(e->button.x, e->button.y)))
624        {
625          if(active_node!=INVALID)
626            {
627
628              //collecting edges to delete
629              EdgeIt e;
630              std::set<Graph::Edge> edges_to_delete;
631
632              g.firstOut(e,active_node);
633              for(;e!=INVALID;g.nextOut(e))
634                {
635                      edges_to_delete.insert(e);
636                }
637
638              g.firstIn(e,active_node);
639              for(;e!=INVALID;g.nextIn(e))
640                {
641                      edges_to_delete.insert(e);
642                }
643
644              //deleting collected edges
645              for(std::set<Graph::Edge>::iterator edge_set_it=edges_to_delete.begin();edge_set_it!=edges_to_delete.end();edge_set_it++)
646                {
647                  delete_item(*edge_set_it);
648                }
649              delete_item(active_node);
650            }
651          //a simple edge was chosen
652          else
653            {
654              delete_item(active_edge);
655            }
656
657         
658        }
659      //pointer was moved, deletion is cancelled
660      else
661        {
662          if(active_node!=INVALID)
663            {
664              *active_item << Gnome::Canvas::Properties::fill_color("blue");
665            }
666          else
667            {
668              *active_item << Gnome::Canvas::Properties::fill_color("green");
669            }
670        }
671      //reseting datas
672      active_item=NULL;
673      active_edge=INVALID;
674      active_node=INVALID;
675      break;
676
677    case GDK_MOTION_NOTIFY:
678      break;
679
680    default:
681      break;
682    }
683  return true;
684}
685
686void GraphDisplayerCanvas::delete_item(NodeIt node_to_delete)
687{
688  delete(nodesmap[node_to_delete]);
689  g.erase(node_to_delete);
690}
691
692void GraphDisplayerCanvas::delete_item(EdgeIt edge_to_delete)
693{
694  delete(edgesmap[edge_to_delete]);
695  g.erase(edge_to_delete);
696}
697
698void GraphDisplayerCanvas::delete_item(Graph::Edge edge_to_delete)
699{
700  delete(edgesmap[edge_to_delete]);
701  g.erase(edge_to_delete);
702}
703
Note: See TracBrowser for help on using the repository browser.