COIN-OR::LEMON - Graph Library

source: lemon-0.x/gui/graph_displayer_canvas.cc @ 1496:c60369a1c987

Last change on this file since 1496:c60369a1c987 was 1496:c60369a1c987, checked in by Hegyi Péter, 14 years ago

At last, the most simple task, the graph-item deletion is solved...

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