COIN-OR::LEMON - Graph Library

Changeset 160:14a76109b561 in glemon-0.x


Ignore:
Timestamp:
10/12/06 13:39:29 (13 years ago)
Author:
Hegyi Péter
Branch:
default
Phase:
public
Convert:
svn:c9d7d8f5-90d6-0310-b91f-818b3a526b0e/glemon/trunk@2982
Message:

Node antigravity and edge elasticity based graph layout redesigner.

Files:
8 edited

Legend:

Unmodified
Added
Removed
  • Makefile.am

    r142 r160  
    4343        guipixbufs.h \
    4444        i18n.h \
    45         gettext.h
     45        gettext.h \
     46        design_win.h \
     47        design_win.cc
    4648
    4749glemon_CXXFLAGS = $(GTK_CFLAGS) $(LEMON_CFLAGS)
  • graph_displayer_canvas-event.cc

    r153 r160  
    8282  static Gnome::Canvas::Text *coord_text = 0;
    8383  switch(e->type)
    84   {
     84    {
    8585    case GDK_BUTTON_PRESS:
    8686      //we mark the location of the event to be able to calculate parameters of dragging
    8787      window_to_world (e->button.x, e->button.y, clicked_x, clicked_y);
    88 
     88     
    8989      active_item=(get_item_at(clicked_x, clicked_y));
    9090      active_node=INVALID;
     
    100100    case GDK_BUTTON_RELEASE:
    101101      if (coord_text)
    102       {
    103         delete coord_text;
    104         coord_text = 0;
    105       }
     102        {
     103          delete coord_text;
     104          coord_text = 0;
     105        }
    106106      isbutton=0;
    107107      active_item=NULL;
     
    111111      //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
    112112      if(active_node!=INVALID)
    113       {
    114         (mytab.mapstorage).modified = true;
    115 
    116         //new coordinates will be the old values,
    117         //because the item will be moved to the
    118         //new coordinate therefore the new movement
    119         //has to be calculated from here
    120 
    121         double new_x, new_y;
    122 
    123         window_to_world (e->motion.x, e->motion.y, new_x, new_y);
    124 
    125         double dx=new_x-clicked_x;
    126         double dy=new_y-clicked_y;
    127 
    128         //repositioning node and its text
    129         active_item->move(dx, dy);
    130         nodetextmap[active_node]->move(dx, dy);
    131 
    132         // the new coordinates of the centre of the node
    133         double coord_x = new_x - (clicked_x - (mytab.mapstorage).coords[active_node].x);
    134         double coord_y = new_y - (clicked_y - (mytab.mapstorage).coords[active_node].y);
    135 
    136         // write back the new coordinates to the coords map
    137         (mytab.mapstorage).coords.set(active_node, XY(coord_x, coord_y));
    138 
    139         clicked_x=new_x;
    140         clicked_y=new_y;
    141 
    142         // reposition the coordinates text
    143         std::ostringstream ostr;
    144         ostr << "(" <<
    145           (mytab.mapstorage).coords[active_node].x << ", " <<
    146           (mytab.mapstorage).coords[active_node].y << ")";
    147         double radius =
    148           (nodesmap[active_node]->property_x2().get_value() -
    149           nodesmap[active_node]->property_x1().get_value()) / 2.0;
    150         if (coord_text)
    151         {
    152           coord_text->property_text().set_value(ostr.str());
    153           coord_text->property_x().set_value((mytab.mapstorage).coords[active_node].x +
    154               radius);
    155           coord_text->property_y().set_value((mytab.mapstorage).coords[active_node].y -
    156               radius);
    157         }
    158         else
    159         {
    160           coord_text = new Gnome::Canvas::Text(
    161               displayed_graph,
    162               (mytab.mapstorage).coords[active_node].x + radius,
    163               (mytab.mapstorage).coords[active_node].y - radius,
    164               ostr.str());
    165           coord_text->property_fill_color().set_value("black");
    166           coord_text->property_anchor().set_value(Gtk::ANCHOR_SOUTH_WEST);
    167         }
    168 
    169         //all the edges connected to the moved point has to be redrawn
    170         for(OutEdgeIt ei((mytab.mapstorage).graph,active_node);ei!=INVALID;++ei)
    171         {
    172           XY arrow_pos;
    173 
    174           if (mytab.mapstorage.graph.source(ei) == mytab.mapstorage.graph.target(ei))
    175           {
    176             arrow_pos = mytab.mapstorage.arrow_pos[ei] + XY(dx, dy);
    177           }
    178           else
    179           {
    180             XY moved_node_1(coord_x - dx, coord_y - dy);
    181             XY moved_node_2(coord_x, coord_y);
    182             Node target = mytab.mapstorage.graph.target(ei);
    183             XY fix_node(mytab.mapstorage.coords[target].x,
    184                 mytab.mapstorage.coords[target].y);
    185             XY old_arrow_pos(mytab.mapstorage.arrow_pos[ei]);
    186 
    187             arrow_pos = calcArrowPos(moved_node_1, moved_node_2, fix_node, old_arrow_pos, isbutton);
    188           }
    189 
    190           mytab.mapstorage.arrow_pos.set(ei, arrow_pos);
    191           edgesmap[ei]->draw();
    192 
    193           //reposition of edgetext
    194           XY text_pos=mytab.mapstorage.arrow_pos[ei];
    195           text_pos+=(XY(10,10));
    196           edgetextmap[ei]->property_x().set_value(text_pos.x);
    197           edgetextmap[ei]->property_y().set_value(text_pos.y);
    198         }
    199 
    200         for(InEdgeIt ei((mytab.mapstorage).graph,active_node);ei!=INVALID;++ei)
    201         {
    202           if (mytab.mapstorage.graph.source(ei) != mytab.mapstorage.graph.target(ei))
    203           {
    204             XY moved_node_1(coord_x - dx, coord_y - dy);
    205             XY moved_node_2(coord_x, coord_y);
    206             Node source = mytab.mapstorage.graph.source(ei);
    207             XY fix_node(mytab.mapstorage.coords[source].x,
    208                 mytab.mapstorage.coords[source].y);
    209             XY old_arrow_pos(mytab.mapstorage.arrow_pos[ei]);
    210 
    211             XY arrow_pos;
    212             arrow_pos = calcArrowPos(moved_node_1, moved_node_2, fix_node, old_arrow_pos, isbutton);
    213 
    214             mytab.mapstorage.arrow_pos.set(ei, arrow_pos);
    215             edgesmap[ei]->draw();
    216 
    217             //reposition of edgetext
    218             XY text_pos=mytab.mapstorage.arrow_pos[ei];
    219             text_pos+=(XY(10,10));
    220             edgetextmap[ei]->property_x().set_value(text_pos.x);
    221             edgetextmap[ei]->property_y().set_value(text_pos.y);
    222           }
    223         }
    224       }
     113        {
     114          (mytab.mapstorage).modified = true;
     115         
     116          //new coordinates will be the old values,
     117          //because the item will be moved to the
     118          //new coordinate therefore the new movement
     119          //has to be calculated from here
     120         
     121          double new_x, new_y;
     122         
     123          window_to_world (e->motion.x, e->motion.y, new_x, new_y);
     124         
     125          double dx=new_x-clicked_x;
     126          double dy=new_y-clicked_y;
     127         
     128          moveNode(dx, dy);
     129
     130          clicked_x=new_x;
     131          clicked_y=new_y;
     132
     133          // reposition the coordinates text
     134          std::ostringstream ostr;
     135          ostr << "(" <<
     136            (mytab.mapstorage).coords[active_node].x << ", " <<
     137            (mytab.mapstorage).coords[active_node].y << ")";
     138          double radius =
     139            (nodesmap[active_node]->property_x2().get_value() -
     140             nodesmap[active_node]->property_x1().get_value()) / 2.0;
     141          if (coord_text)
     142            {
     143              coord_text->property_text().set_value(ostr.str());
     144              coord_text->property_x().set_value((mytab.mapstorage).coords[active_node].x +
     145                                                 radius);
     146              coord_text->property_y().set_value((mytab.mapstorage).coords[active_node].y -
     147                                                 radius);
     148            }
     149          else
     150            {
     151              coord_text = new Gnome::Canvas::Text(
     152                                                   displayed_graph,
     153                                                   (mytab.mapstorage).coords[active_node].x + radius,
     154                                                   (mytab.mapstorage).coords[active_node].y - radius,
     155                                                   ostr.str());
     156              coord_text->property_fill_color().set_value("black");
     157              coord_text->property_anchor().set_value(Gtk::ANCHOR_SOUTH_WEST);
     158            }
     159
     160
     161        }
    225162    default: break;
    226   }
    227 
    228   return false;
     163    }
     164
     165return false;
    229166}
    230167
     
    824761  }
    825762  else
    826   {
    827     if(forming_edge!=INVALID)
    828     {
    829       forming_edge=INVALID;
    830     }
    831     else
    832     {
    833       std::cerr << "ERROR!!!! Invalid edge found!" << std::endl;
    834     }
    835   }
    836 }
     763    {
     764      if(forming_edge!=INVALID)
     765        {
     766          forming_edge=INVALID;
     767        }
     768      else
     769        {
     770          std::cerr << "ERROR!!!! Invalid edge found!" << std::endl;
     771        }
     772    }
     773}
     774
     775void GraphDisplayerCanvas::moveNode(double dx, double dy, Gnome::Canvas::Item * item, Node node)
     776{
     777  Gnome::Canvas::Item * moved_item=item;
     778  Node moved_node=node;
     779
     780  if(item==NULL && node==INVALID)
     781    {
     782      moved_item=active_item;
     783      moved_node=active_node;
     784    }
     785  else
     786    {
     787      isbutton=1;
     788    }
     789
     790  //repositioning node and its text
     791  moved_item->move(dx, dy);
     792  nodetextmap[moved_node]->move(dx, dy);
     793
     794  // the new coordinates of the centre of the node
     795  double coord_x = dx + (mytab.mapstorage).coords[moved_node].x;
     796  double coord_y = dy + (mytab.mapstorage).coords[moved_node].y;
     797
     798  // write back the new coordinates to the coords map
     799  (mytab.mapstorage).coords.set(moved_node, XY(coord_x, coord_y));
     800
     801  //all the edges connected to the moved point has to be redrawn
     802  for(OutEdgeIt ei((mytab.mapstorage).graph,moved_node);ei!=INVALID;++ei)
     803    {
     804      XY arrow_pos;
     805
     806      if (mytab.mapstorage.graph.source(ei) == mytab.mapstorage.graph.target(ei))
     807        {
     808          arrow_pos = mytab.mapstorage.arrow_pos[ei] + XY(dx, dy);
     809        }
     810      else
     811        {
     812          XY moved_node_1(coord_x - dx, coord_y - dy);
     813          XY moved_node_2(coord_x, coord_y);
     814          Node target = mytab.mapstorage.graph.target(ei);
     815          XY fix_node(mytab.mapstorage.coords[target].x,
     816                      mytab.mapstorage.coords[target].y);
     817          XY old_arrow_pos(mytab.mapstorage.arrow_pos[ei]);
     818
     819          arrow_pos = calcArrowPos(moved_node_1, moved_node_2, fix_node, old_arrow_pos, isbutton);
     820        }
     821
     822      mytab.mapstorage.arrow_pos.set(ei, arrow_pos);
     823      edgesmap[ei]->draw();
     824
     825      //reposition of edgetext
     826      XY text_pos=mytab.mapstorage.arrow_pos[ei];
     827      text_pos+=(XY(10,10));
     828      edgetextmap[ei]->property_x().set_value(text_pos.x);
     829      edgetextmap[ei]->property_y().set_value(text_pos.y);
     830    }
     831
     832  for(InEdgeIt ei((mytab.mapstorage).graph,moved_node);ei!=INVALID;++ei)
     833    {
     834      if (mytab.mapstorage.graph.source(ei) != mytab.mapstorage.graph.target(ei))
     835        {
     836          XY moved_node_1(coord_x - dx, coord_y - dy);
     837          XY moved_node_2(coord_x, coord_y);
     838          Node source = mytab.mapstorage.graph.source(ei);
     839          XY fix_node(mytab.mapstorage.coords[source].x,
     840                      mytab.mapstorage.coords[source].y);
     841          XY old_arrow_pos(mytab.mapstorage.arrow_pos[ei]);
     842
     843          XY arrow_pos;
     844          arrow_pos = calcArrowPos(moved_node_1, moved_node_2, fix_node, old_arrow_pos, isbutton);
     845
     846          mytab.mapstorage.arrow_pos.set(ei, arrow_pos);
     847          edgesmap[ei]->draw();
     848
     849          //reposition of edgetext
     850          XY text_pos=mytab.mapstorage.arrow_pos[ei];
     851          text_pos+=(XY(10,10));
     852          edgetextmap[ei]->property_x().set_value(text_pos.x);
     853          edgetextmap[ei]->property_y().set_value(text_pos.y);
     854        }
     855    }
     856}
  • graph_displayer_canvas.cc

    r157 r160  
    66  nodetextmap(mainw.mapstorage.graph), displayed_graph(*(root()), 0, 0),
    77  isbutton(0), active_item(NULL), target_item(NULL), nodemap_to_edit(""),
    8   edgemap_to_edit(""), autoscale(true), zoomtrack(false), radius_size(20), edge_width(10), mytab(mainw)
     8  edgemap_to_edit(""), autoscale(true), zoomtrack(false), radius_size(20), edge_width(10),
     9  iterations(20), attraction(0.05), propulsation(40000), mytab(mainw)
    910{
    1011  //base event handler is move tool
     
    252253  radius_p=radius_size;
    253254}
     255
     256void GraphDisplayerCanvas::reDesignGraph()
     257{
     258  double min_dist=40;
     259
     260  //iteration counter
     261  for(int l=0;l<iterations;l++)
     262    {
     263      Graph::NodeMap<double> x(mytab.mapstorage.graph);
     264      Graph::NodeMap<double> y(mytab.mapstorage.graph);
     265      XYMap<Graph::NodeMap<double> > actual_forces;
     266      actual_forces.setXMap(x);
     267      actual_forces.setYMap(y);
     268
     269      lemon::dim2::Point<double> delta;
     270
     271      //count actual force for each nodes
     272      for (NodeIt i((mytab.mapstorage).graph); i!=INVALID; ++i)
     273        {
     274          //propulsation of nodes
     275          for (NodeIt j((mytab.mapstorage).graph); j!=INVALID; ++j)
     276            {
     277              if(i!=j)
     278                {
     279                  delta=((mytab.mapstorage).coords[i]-(mytab.mapstorage).coords[j]);
     280
     281                  double length_sqr=delta.normSquare();
     282                  double length=sqrt(length_sqr);
     283                  if(length_sqr<min_dist)
     284                    {
     285                      length_sqr=min_dist;
     286                    }
     287
     288                  //normalize vector
     289                  delta/=length;
     290
     291                  //calculating propulsation strength
     292                  //greater distance menas smaller propulsation strength
     293                  delta*=propulsation/length_sqr;
     294                   
     295                  actual_forces.set(i,(actual_forces[i]+delta));
     296                }
     297            }
     298          //attraction of nodes, to which actual node is bound
     299          for(OutEdgeIt ei((mytab.mapstorage).graph,i);ei!=INVALID;++ei)
     300            {
     301              delta=((mytab.mapstorage).coords[i]-(mytab.mapstorage).coords[mytab.mapstorage.graph.target(ei)]);
     302
     303              double length_sqr=delta.normSquare();
     304              double length=sqrt(length_sqr);
     305              if(length_sqr<min_dist)
     306                {
     307                  length_sqr=min_dist;
     308                }
     309
     310              //normalize vector
     311              delta/=length;
     312
     313              //calculating attraction strength
     314              //greater distance means greater strength
     315              delta*=attraction*length;
     316
     317              actual_forces.set(i,actual_forces[i]-delta);
     318            }
     319          for(InEdgeIt ei((mytab.mapstorage).graph,i);ei!=INVALID;++ei)
     320            {
     321              delta=((mytab.mapstorage).coords[i]-(mytab.mapstorage).coords[mytab.mapstorage.graph.source(ei)]);
     322
     323              double length_sqr=delta.normSquare();
     324              double length=sqrt(length_sqr);
     325              if(length_sqr<min_dist)
     326                {
     327                  length_sqr=min_dist;
     328                }
     329
     330              //normalize vector
     331              delta/=length;
     332
     333              //calculating attraction strength
     334              //greater distance means greater strength
     335              delta*=attraction*length;
     336
     337              actual_forces.set(i,actual_forces[i]-delta);
     338            }
     339        }
     340      for (NodeIt i((mytab.mapstorage).graph); i!=INVALID; ++i)
     341        {
     342          moveNode(actual_forces[i].x, actual_forces[i].y, nodesmap[i], i);
     343        }
     344    }
     345}
     346
     347void GraphDisplayerCanvas::get_design_data(double & attraction_p, double & propulsation_p, int & iterations_p)
     348{
     349  attraction_p=attraction;
     350  propulsation_p=propulsation;
     351  iterations_p=iterations;
     352}
     353
     354void GraphDisplayerCanvas::set_attraction(double attraction_p)
     355{
     356  attraction=attraction_p;
     357}
     358
     359void GraphDisplayerCanvas::set_propulsation(double propulsation_p)
     360{
     361  propulsation=propulsation_p;
     362}
     363
     364void GraphDisplayerCanvas::set_iteration(int iterations_p)
     365{
     366  iterations=iterations_p;
     367}
     368
  • graph_displayer_canvas.h

    r157 r160  
    269269  bool mapEditEventHandler(GdkEvent*);
    270270
     271private:
     272  ///moves node according to the given parameters
     273  void moveNode(double, double,  Gnome::Canvas::Item * item=NULL, Node node=INVALID);
     274
    271275public:
    272276  ///Moves the text to new place
     
    314318  ///\param mapname name of new map
    315319  int addNewEdgeMap(double init,std::string mapname);
     320
     321  void reDesignGraph();
     322
     323  void get_design_data(double &, double &, int &);
     324  void set_attraction(double);
     325  void set_propulsation(double);
     326  void set_iteration(int);
    316327
    317328private:
     
    399410  double edge_width;
    400411
     412  ///Iteration number during graph design
     413  int iterations;
     414
     415  ///Attraction factor during graph design
     416  double attraction;
     417
     418  ///Propulsation factor during graph design
     419  double propulsation;
     420 
    401421private:
    402422
  • main_win.cc

    r158 r160  
    100100  ag->add( Gtk::Action::create("ShowMaps", _("_Maps")),
    101101           sigc::mem_fun(*this, &MainWin::createMapWin));
     102  ag->add( Gtk::Action::create("ShowDesign", _("_Design")),
     103           sigc::mem_fun(*this, &MainWin::createDesignWin));
    102104
    103105  ag->add( Gtk::Action::create("AlgoMenu", _("_Algorithms")) );
     
    122124  ag->add( Gtk::Action::create("AddMap", Gtk::StockID("gd-newmap")),
    123125      sigc::mem_fun ( *this , &MainWin::createNewMapWin ) );
     126
     127  ag->add( Gtk::Action::create("DesignGraph", Gtk::Stock::REFRESH),
     128      sigc::mem_fun ( *this , &MainWin::reDesignGraph ) );
    124129
    125130  uim=Gtk::UIManager::create();
     
    150155      "    <menu action='ShowMenu'>"
    151156      "      <menuitem action='ShowMaps'/>"
     157      "      <menuitem action='ShowDesign'/>"
    152158      "    </menu>"
    153159      "    <menu action='AlgoMenu'>"
     
    174180      "    <separator />"
    175181      "    <toolitem action='AddMap' />"
     182      "    <toolitem action='DesignGraph' />"
    176183      "  </toolbar>"
    177184      "</ui>";
     
    449456}
    450457
     458void MainWin::createDesignWin()
     459{
     460  if(active_tab!=-1)
     461    {
     462      tabs[active_tab]->createDesignWin(tabnames[active_tab]);
     463    }
     464}
     465
    451466void MainWin::createAlgoWin(int algoid)
    452467{
     
    537552  tabs[active_tab]->setView(autoscale, zoomtrack, width, radius);
    538553}
     554
     555void MainWin::reDesignGraph()
     556{
     557  tabs[active_tab]->reDesignGraph();
     558}
  • main_win.h

    r157 r160  
    145145  ///\ref NoteBookTab
    146146  virtual void createMapWin();
     147
     148  ///Callback for Show Design menupoint.
     149
     150  ///It calls the appropriate function in
     151  ///\ref NoteBookTab
     152  virtual void createDesignWin();
    147153
    148154  ///Pops up an Algorithm window.
     
    238244
    239245  virtual void nodeViewChanged();
     246
     247  virtual void reDesignGraph();
    240248};
    241249
  • nbtab.cc

    r157 r160  
    11#include <nbtab.h>
    22
    3 NoteBookTab::NoteBookTab():mapwinexists(false)
     3NoteBookTab::NoteBookTab():mapwinexists(false), designwinexists(false)
    44{
    55  Gtk::ScrolledWindow *pScrolledWindow = manage(new Gtk::ScrolledWindow);
     
    208208}
    209209
     210void NoteBookTab::createDesignWin(std::string name)
     211{
     212  if(!designwinexists)
     213    {
     214      double attraction, propulsation;
     215      int iterations;
     216      gd_canvas->get_design_data(attraction, propulsation, iterations);
     217      designwin=new DesignWin("Design Setup - "+name, attraction, propulsation, iterations);
     218
     219      designwin->signal_attraction().connect(sigc::mem_fun(*this, &NoteBookTab::attraction_ch));
     220      designwin->signal_propulsation().connect(sigc::mem_fun(*this, &NoteBookTab::propulsation_ch));
     221      designwin->signal_iteration().connect(sigc::mem_fun(*gd_canvas, &GraphDisplayerCanvas::set_iteration));
     222      designwin->close_run().connect(sigc::mem_fun(*gd_canvas, &GraphDisplayerCanvas::reDesignGraph));
     223
     224      designwin->signal_delete_event().connect(sigc::mem_fun(*this, &NoteBookTab::closeDesignWin));
     225
     226      designwin->show();
     227      designwinexists=true;
     228    }
     229}
     230
    210231void NoteBookTab::closeMapWin()
    211232{
     
    214235}
    215236
     237bool NoteBookTab::closeDesignWin(GdkEventAny * e)
     238{
     239  if(e->type==GDK_DELETE)
     240    {
     241      designwinexists=false;
     242      delete designwin;
     243    }
     244}
     245
    216246sigc::signal<void, std::string> NoteBookTab::signal_title_ch()
    217247{
     
    228258  gd_canvas->getView(autoscale, zoomtrack, width, radius);
    229259}
     260
     261void NoteBookTab::reDesignGraph()
     262{
     263  gd_canvas->reDesignGraph();
     264}
     265
     266void NoteBookTab::attraction_ch(double v)
     267{
     268  gd_canvas->set_attraction(v);
     269}
     270
     271void NoteBookTab::propulsation_ch(double v)
     272{
     273  gd_canvas->set_propulsation(v);
     274}
     275
     276void NoteBookTab::iteration_ch(int v)
     277{
     278  gd_canvas->set_iteration(v);
     279}
     280
  • nbtab.h

    r157 r160  
    88#include "mapstorage.h"
    99#include "map_win.h"
     10#include "design_win.h"
    1011#include "graph_displayer_canvas.h"
    1112#include <libgnomecanvasmm.h>
     
    7879  bool mapwinexists;
    7980
     81  ///Indicates whether the \ref DesignWin is opened or not. See \ref designwin.
     82  bool designwinexists;
     83
    8084  ///Address of the only \ref MapWin that the \ref NoteBookTab can open.
    8185
     
    8690  MapWin * mapwin;
    8791   
     92  ///Address of the only \ref DesignWin that the \ref NoteBookTab can open.
     93
     94  ///Only one of this window can be opened at the same time (\ref designwinexists),
     95  ///because there is no need for more, one per tab is enough.
     96  ///There won't be benefit of more than one, but it would be
     97  ///more complicated to synchronize them.
     98  DesignWin * designwin;
     99
    88100public:
    89101  ///Callback for 'FileNew' action.
     
    152164  void createMapWin(std::string);
    153165
     166  ///Pops up and registrates the \ref DesignWin of \ref NoteBookTab.
     167 
     168  ///See also
     169  ///\ref mapwin.
     170  void createDesignWin(std::string);
     171
    154172  ///Closes and deregistrates the \ref MapWin of \ref NoteBookTab.
    155173 
     
    158176  void closeMapWin();
    159177
     178  bool closeDesignWin(GdkEventAny *);
     179
    160180  ///Sets node representation settings
    161181  void setView(bool, bool, double, double);
     
    163183  ///Gets node representation settings
    164184  void getView(bool &, bool &, double&, double&);
     185
     186  void reDesignGraph();
     187
     188  void attraction_ch(double);
     189
     190  void propulsation_ch(double);
     191
     192  void iteration_ch(int);
    165193};
    166194
Note: See TracChangeset for help on using the changeset viewer.