Minsky: 3.17.0
godleyIcon.cc
Go to the documentation of this file.
1 /*
2  @copyright Steve Keen 2012
3  @author Russell Standish
4  This file is part of Minsky.
5 
6  Minsky is free software: you can redistribute it and/or modify it
7  under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  Minsky is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with Minsky. If not, see <http://www.gnu.org/licenses/>.
18 */
19 #include "minsky.h"
20 #include "variable.h"
21 #include "godleyIcon.h"
22 #include "godleyTableWindow.h"
23 #include "cairoItems.h"
24 #include "selection.h"
25 #include <flowCoef.h>
26 #include <evalGodley.h>
27 #include <arrays.h>
28 #include <cairo_base.h>
29 #include <ctype.h>
30 #include "godleyIcon.rcd"
31 #include "itemT.rcd"
32 #include "godleyTableWindow.xcd"
33 #include "minsky_epilogue.h"
34 #include <boost/locale.hpp>
35 using namespace boost::locale::conv;
36 using namespace ecolab::cairo;
37 using namespace ecolab;
38 using namespace std;
39 
40 // width of draggable border
41 const float border=10;
42 
43 namespace minsky
44 {
45  namespace
46  {
47  struct OrderByName
48  {
49  bool operator()(const VariablePtr& x, const VariablePtr& y) const
50  {assert(x&&y); return x->valueId() < y->valueId();}
51  };
52 
53  struct DrawVars
54  {
55  cairo_t* cairo;
56  float x, y; // position of this icon
57  DrawVars(cairo_t* cairo, float x, float y):
58  cairo(cairo), x(x), y(y) {}
59 
60  void operator()(const GodleyIcon::Variables& vars) const
61  {
62  for (GodleyIcon::Variables::const_iterator v=vars.begin();
63  v!=vars.end(); ++v)
64  {
65  const ecolab::cairo::CairoSave cs(cairo);
66  const VariableBase& vv=**v;
67  // coordinates of variable within the cairo context
68  cairo_translate(cairo, vv.x()-x, vv.y()-y);
69  vv.draw(cairo);
70  }
71  }
72  };
73 
74  // determine the width and maximum height on screen of variables in vars
76  float& height, float& width)
77  {
78  float h=0;
79  for (const auto& v: vars)
80  {
81  const RenderVariable rv(*v);
82  h+=2*rv.height();
83  if (h>height) height=h;
84  const float w=2*rv.width();
85  if (w>width) width=w;
86  }
87  }
88  }
89 
90  bool GodleyIcon::inItem(float xx, float yy) const
91  {
92  return abs(xx-x())<0.5*width()-border && abs(yy-y())<0.5*height()-border;
93  }
94 
95  void GodleyIcon::updateVars(GodleyIcon::Variables& vars,
96  const vector<string>& varNames,
97  VariableType::Type varType)
98  {
99  // update the map of variables from the Godley table
100  set<VariablePtr, OrderByName> oldVars(vars.begin(), vars.end());
101  set<string> alreadyAdded;
102 
103  vars.clear();
104  shared_ptr<GodleyIcon> self;
105  if (auto g=group.lock())
106  self=dynamic_pointer_cast<GodleyIcon>(g->findItem(*this));
107 
108  for (vector<string>::const_iterator nm=varNames.begin(); nm!=varNames.end(); ++nm)
109  {
110  const VariablePtr newVar(varType, *nm);
111  auto myGroup=group.lock();
112  if (myGroup) myGroup->addItem(newVar); // get scope right
113  auto v=oldVars.find(newVar);
114 
115  if (v==oldVars.end())
116  {
117  // allow for the possibility that multiple names map to the same valueId
118  if (!alreadyAdded.contains(newVar->valueId()))
119  {
120  // add new variable
121  vars.push_back(newVar);
122  alreadyAdded.insert(newVar->valueId());
123  if (varType==VariableType::stock) // newly added variables should take the default dimension
124  newVar->setUnits(currency);
125  }
126  else
127  if (myGroup)
128  myGroup->removeItem(*newVar);
129  }
130  else
131  {
132  // move existing variable
133  assert(*v);
134  vars.push_back(*v);
135  alreadyAdded.insert(newVar->valueId());
136  oldVars.erase(v);
137  if (myGroup) myGroup->removeItem(*newVar);
138  }
139  if (myGroup) myGroup->addItem(vars.back(),true);
140  vars.back()->controller=self;
141  // ensure variable type is consistent
142  minsky::minsky().convertVarType(vars.back()->valueId(), varType);
143  }
144  // remove any previously existing variables
145  if (auto g=group.lock())
146  for (const auto& v: oldVars)
147  g->deleteItem(*v);
148  }
149 
150  void GodleyIcon::toggleVariableDisplay()
151  {
152  m_variableDisplay=!m_variableDisplay;
153  update();
154  }
155 
156  void GodleyIcon::toggleEditorMode()
157  {
158  m_editorMode=!m_editorMode;
159  updateBoundingBox();
160  }
161 
162  bool GodleyIcon::buttonDisplay() const {return m_editorMode && editor.drawButtons;}
163  void GodleyIcon::toggleButtons()
164  {
165  if (editor.drawButtons)
166  editor.disableButtons();
167  else
168  editor.enableButtons();
169  updateBoundingBox();
170  }
171 
172  void GodleyIcon::scaleIcon(float w, float h)
173  {
174  update();
175  scaleFactor(min(w/(leftMargin()+iWidth()*zoomFactor()),h/(bottomMargin()+iHeight()*zoomFactor())));
176  }
177 
178  void GodleyIcon::resize(const LassoBox& b)
179  {
180  const float invZ=1.0/(this->zoomFactor());
181  auto bw=abs(b.x0-b.x1), bh=abs(b.y0-b.y1);
182  if (bw<=leftMargin() || bh<=bottomMargin()) return;
183  moveTo(0.5*(b.x0+b.x1), 0.5*(b.y0+b.y1));
184  iWidth((bw-leftMargin())*invZ);
185  iHeight((bh-bottomMargin())*invZ);
186  scaleIcon(bw,bh);
187  updateBoundingBox();
188  }
189 
190  void GodleyIcon::removeControlledItems(GroupItems& g)
191  {
192  for (auto& i: m_flowVars)
193  {
194  assert(i);
195  i->deleteAttachedWires();
196  auto item=g.removeItem(*i);
197  assert(item.use_count()!=1);
198  }
199  m_flowVars.clear();
200  for (auto& i: m_stockVars)
201  {
202  assert(i);
203  i->deleteAttachedWires();
204  auto item=g.removeItem(*i);
205  assert(item.use_count()!=1);
206  }
207  m_stockVars.clear();
208  }
209 
210  void GodleyIcon::setCell(int row, int col, const string& newVal)
211  {
212  if (!table.cellInTable(row,col)) return;
213  // if this operation is clearing an initial condition cell, set it to 0
214  string& c=table.cell(row,col);
215  if (newVal.empty() && !c.empty() && table.initialConditionRow(row))
216  c="0";
217  else
218  c=newVal;
219  if (row==0)
220  minsky().importDuplicateColumn(table, col);
221  else
222  minsky().balanceDuplicateColumns(*this, col);
223  table.markEdited();
224  }
225 
226  void GodleyIcon::deleteRow(unsigned row)
227  {
228  if (row>0 && row<=table.rows())
229  {
230  table.deleteRow(row-1);
231  // if shared column data is deleted, remove it from the other tables too
232  for (size_t col=1; col<table.cols(); ++col)
233  minsky().balanceDuplicateColumns(*this, col);
234  table.markEdited();
235  }
236  }
237 
238  //void GodleyIcon::moveCell(int srcRow, int srcCol, int destRow, int destCol)
239  void GodleyIcon::moveCell(const MoveCellArgs& args)
240  {
241  if (!table.cellInTable(args.srcRow, args.srcCol) || !table.cellInTable(args.destRow, args.destCol)) return;
242  if (args.srcCol!=args.destCol) // if moving between columns, we can delete and
243  // add, which ensures any linked columns are
244  // correctly updated
245  {
246  // create a copy here, as setCell may resize the array, changing
247  // what the reference referred to.
248  const string oldVal=table.cell(args.srcRow, args.srcCol);
249  setCell(args.destRow, args.destCol, oldVal);
250  setCell(args.srcRow, args.srcCol, "");
251  }
252  else // within a column, just move the data without affecting any linked column
253  {
254  table.cell(args.destRow, args.destCol)=table.cell(args.srcRow, args.srcCol);
255  table.cell(args.srcRow, args.srcCol)="";
256  }
257  }
258 
259  map<string,double> GodleyIcon::flowSignature(unsigned col) const
260  {
261  map<string,double> r;
262  for (size_t row=1; row<table.rows() && col<table.cols(); ++row)
263  if (!table.initialConditionRow(row))
264  {
265  const FlowCoef fc(table.cell(row,col));
266  if (!fc.name.empty())
267  r[fc.name]+=fc.coef;
268  }
269  return r;
270  }
271 
272  vector<Summary> GodleyIcon::summarise() const
273  {
274  vector<Summary> r;
275  for (auto& i: stockVars())
276  if (auto vv=i->vValue())
277  {
278  r.push_back(vv->summary());
279  r.back().godley=table.title.empty()? id(): table.title;
280  }
281  for (auto& i: flowVars())
282  if (auto vv=i->vValue())
283  {
284  r.push_back(vv->summary());
285  r.back().godley=table.title.empty()? id(): table.title;
286  }
287  return r;
288  }
289 
290  void GodleyIcon::update()
291  {
292  updateVars(m_stockVars, table.getColumnVariables(), VariableType::stock);
293  updateVars(m_flowVars, table.getVariables(), VariableType::flow);
294 
295  // retrieve initial conditions, if any
296  for (size_t r=1; r<table.rows(); ++r)
297  if (table.initialConditionRow(r))
298  for (size_t c=1; c<table.cols(); ++c)
299  {
300  const string name=trimWS(table.cell(0,c));
301  auto vi=minsky().variableValues.find(minsky::valueId(group.lock(),name));
302  if (vi==minsky().variableValues.end()) continue;
303  VariableValue& v=*vi->second;
304  v.godleyOverridden=false;
305  const string::size_type start=table.cell(r,c).find_first_not_of(' ');
306  if (start!=string::npos)
307  {
308  const FlowCoef fc(table.cell(r,c).substr(start));
309  v.init(fc.str());
310  // set initial value of stock var to init value of flow that is defined by a parameter or a constant. for ticket 1137
311  if (auto initVar=minsky().definingVar(minsky::valueId(group.lock(),fc.str())))
312  if (initVar->inputWired() && initVar->type()==VariableType::flow)
313  if (auto* lhsVar=initVar->ports(1).lock()->wires()[0]->from()->item().variableCast()) {
314  FlowCoef fc1(lhsVar->vValue()->init());
315  fc1.coef*=fc.coef;
316  v.init(fc1.str());
317  }
318  v.godleyOverridden=true;
319  }
320  else
321  {
322  // populate cell with current variable's initial value
323  const FlowCoef fc(v.init());
324  table.cell(r,c)=fc.str();
325  v.godleyOverridden=true;
326  }
327 
328  }
329 
330  if (m_variableDisplay)
331  {
332  // determine height of variables part of icon
333  float stockH=0, flowH=0;
334  stockMargin=0;
335  flowMargin=0;
336  accumulateWidthHeight(m_stockVars, stockH, stockMargin);
337  accumulateWidthHeight(m_flowVars, flowH, flowMargin);
338  // allow for notches on variables
339  stockMargin+=4;
340  flowMargin+=4;
341  const float iw=this->iWidth(), ih=this->iHeight();
342  this->iWidth(max(iw, 1.8f*stockH));
343  this->iHeight(max(ih, 1.8f*flowH));
344  }
345  else
346  { // if any variables have attached wires, create a copy to carry the wire
347  for (auto& v: m_stockVars)
348  if (auto p=v->ports(0).lock())
349  if (!p->wires().empty())
350  {
351  if (auto g=group.lock())
352  {
353  auto newVar=g->addItem(v->clone());
354  if (auto w=p->wires().front())
355  if (auto from=newVar->ports(0).lock())
356  {
357  w->moveToPorts(from, w->to());
358  continue;
359  }
360  }
361  // in falling through to here, we've failed to create and wire a variable copy
362  m_variableDisplay=true;
363  throw_error("Cowardly refusing to hide a wired variable");
364  }
365 
366  for (auto& v: m_flowVars)
367  if (auto p=v->ports(1).lock())
368  if (!p->wires().empty())
369  {
370  if (auto g=group.lock())
371  {
372  auto newVar=g->addItem(v->clone());
373  if (auto w=p->wires().front())
374  if (auto to=newVar->ports(1).lock())
375  {
376  w->moveToPorts(w->from(), to);
377  continue;
378  }
379  }
380  // in falling through to here, we've failed to create and wire a variable copy
381  m_variableDisplay=true;
382  throw_error("Cowardly refusing to hide a wired variable");
383  }
384  }
385 
386  positionVariables();
387  updateBB();
388  }
389 
390  void GodleyIcon::positionVariables() const
391  {
392  // position of margin in absolute canvas coordinate
393  const float z=this->zoomFactor()*scaleFactor();
394  float x= this->x() - 0.5*iWidth()*z+0.5*leftMargin();
395  float y= this->y() - 0.5*bottomMargin()-0.15*iHeight()*z;
396  for (const auto& v: m_flowVars)
397  {
398  // right justification if displayed, left otherwisw
399  v->rotation(0);
400  v->moveTo(x+v->x() - (m_variableDisplay? v->right(): v->left()), y);
401  y+=v->height();
402  }
403  x= this->x() + 0.55*leftMargin()-0.45*iWidth()*z;
404  y= this->y() + 0.5*iHeight()*z-0.5*bottomMargin();
405 
406  for (const auto& v: m_stockVars)
407  {
408  // top justification at bottom of icon if displayed, bottom justified otherwise
409  v->rotation(90);
410  v->moveTo(x, y + v->y() - (m_variableDisplay? v->top(): v->bottom()));
411  x+=v->width();
412  }
413  }
414 
415  ItemPtr GodleyIcon::select(float x, float y) const
416  {
417  if (m_variableDisplay) // Disable selection of stock and flow vars when they are hidden. for tickets 1217 and 1220.
418  {
419  for (const auto& v: m_flowVars)
420  if (v->contains(x,y))
421  return v;
422  for (const auto& v: m_stockVars)
423  if (v->contains(x,y))
424  return v;
425  }
426  return ItemPtr();
427  }
428 
429  bool GodleyIcon::wiresAttached() const
430  {
431  bool r=false;
432  for (auto& v: m_flowVars)
433  if (auto p=v->ports(1).lock())
434  r|=p->wires().size();
435  for (auto& v: m_stockVars)
436  if (auto p=v->ports(0).lock())
437  r|=p->wires().size();
438  return r;
439  }
440 
441 
442  void GodleyIcon::draw(cairo_t* cairo) const
443  {
444  positionVariables();
445  const float z=zoomFactor()*scaleFactor();
446  float w=iWidth()*z+leftMargin(), h=iHeight()*z+bottomMargin(), left=-0.5*w, top=-0.5*h;
447  double titley;
448 
449  if (m_editorMode)
450  {
451  const CairoSave cs(cairo);
452  cairo_rectangle(cairo, left, top, w, h);
453  cairo_rectangle(cairo, left-border*z, top-border*z, w+2*border*z, h+2*border*z);
454  cairo_stroke_preserve(cairo);
455  if (onBorder)
456  { // shadow the border when mouse is over it
457  const cairo::CairoSave cs(cairo);
458  cairo_set_source_rgba(cairo,0.5,0.5,0.5,0.5);
459  cairo_set_fill_rule(cairo,CAIRO_FILL_RULE_EVEN_ODD);
460  cairo_fill(cairo);
461  }
462  cairo_new_path(cairo);
463  cairo_rectangle(cairo, left, top, w, h);
464  cairo_clip(cairo);
465  cairo_translate(cairo,left+border*z+leftMargin(),top+border*z+titleOffs()/* space for title*/);
466  // render to a recording surface to determine size of editor table
467  // TODO - maybe move this stuff into update()
468  const Surface surf(cairo_recording_surface_create(CAIRO_CONTENT_COLOR, nullptr));
469  const_cast<GodleyTableEditor&>(editor).zoomFactor=1;
470  const_cast<GodleyTableEditor&>(editor).draw(surf.cairo());
471  const_cast<GodleyTableEditor&>(editor).zoomFactor=min((w-leftMargin()-2*border*z)/surf.width(),(h-bottomMargin()-2*border*z-titleOffs())/surf.height());
472  const_cast<GodleyTableEditor&>(editor).draw(cairo);
473  titley=-0.5*h;
474  w+=2*border*z;
475  h+=2*border*z;
476  left-=border*z;
477  top-=border*z;
478  }
479  else
480  {
481  const CairoSave cs(cairo);
482  cairo_translate(cairo,left+leftMargin(),top);
483  svgRenderer.render(cairo, w-leftMargin(), h-bottomMargin());
484  titley=top+0.1*(h-bottomMargin());
485  }
486 
487  if (!table.title.empty())
488  {
489  const CairoSave cs(cairo);
490  Pango pango(cairo);
491  pango.setMarkup("<b>"+latexToPango(table.title)+"</b>");
492  pango.setFontSize(titleOffs());
493  cairo_move_to(cairo,-0.5*(pango.width()-leftMargin()), titley);
494  pango.show();
495  }
496 
497 
498 
499  if (m_variableDisplay && (!m_editorMode || wiresAttached()))
500  {
501  // render the variables
502  const DrawVars drawVars(cairo,x(),y());
503  drawVars(m_flowVars);
504  drawVars(m_stockVars);
505  }
506 
507  if (mouseFocus)
508  {
509  drawPorts(cairo);
510  displayTooltip(cairo,tooltip());
511  drawResizeHandles(cairo);
512  }
513 
514  cairo_rectangle(cairo, left,top, w, h);
515  cairo_clip(cairo);
516  if (selected)
517  {
518  drawSelected(cairo);
519  }
520  }
521 
522  string GodleyIcon::rowSum(int row) const
523  {
524  if (row==0) // A-L-E sum values across stockvars
525  {
526  map<string,VariablePtr> stockVars;
527  for (const auto& i: m_stockVars)
528  stockVars[i->valueId()]=i;
529  double sum=0, absSum=0;
530  for (size_t c=1; c<table.cols(); ++c)
531  {
532  auto i=stockVars.find(minsky::valueId(group.lock(), trimWS(table.cell(0,c))));
533  if (i!=stockVars.end())
534  {
535  sum+=(table.signConventionReversed(c)? -1: 1)*i->second->value();
536  absSum+=abs(i->second->value());
537  }
538  }
539  if (sum<1E-4*absSum) sum=0; // eschew excess precision. For #1328
540  return str(sum);
541  }
542  return table.rowSum(row);
543  }
544 
545  Units GodleyIcon::stockVarUnits(const string& stockName, bool check) const
546  {
547  unsigned stockCol=1;
548  auto valId=valueId(stockName);
549  for (; stockCol<table.cols(); ++stockCol)
550  if (valueId(table.cell(0,stockCol))==valId)
551  break;
552 
553  if (stockCol>=table.cols()) return {};
554 
555  bool foundFlow=false;
556  Units units;
557  for (unsigned row=1; row<table.rows(); ++row)
558  {
559  if (table.initialConditionRow(row)) continue;
560  const FlowCoef fc(table.cell(row,stockCol));
561  if (fc.coef!=0)
562  {
563  auto vid=valueId(fc.name);
564  // find variable assciated with this flow
565  for (const auto& v: flowVars())
566  if (v->valueId()==vid)
567  {
568  auto flowUnits=v->units(check);
569  if (check && foundFlow && units!=flowUnits)
570  throw_error("incompatible units: "+flowUnits.str()+"≠"+units.str()+" on stock "+stockName);
571  foundFlow=true;
572  units=flowUnits;
573  }
574  }
575  }
576  if (!cminsky().timeUnit.empty())
577  units[cminsky().timeUnit]++;
578  return foundFlow? units: cminsky().variableValues[valId]->units;
579  }
580 
581  void GodleyIcon::insertControlled(Selection& selection)
582  {
583  for (const auto& i: flowVars())
584  selection.ensureItemInserted(i);
585  for (const auto& i: stockVars())
586  selection.ensureItemInserted(i);
587  }
588 
589  ClickType::Type GodleyIcon::clickType(float x, float y) const
590  {
591  if (m_editorMode) return Item::clickType(x,y);
592  const double dx=fabs(x-this->x()), dy=fabs(y-this->y());
593  auto z=zoomFactor()*scaleFactor();
594  const double w=0.5*iWidth()*z, h=0.5*iHeight()*z;
595  if (onResizeHandle(x,y)) return ClickType::onResize;
596  // Make it possible to pull wires from variables attached to Godley icons. For ticket 940
597  if (auto item=select(x,y))
598  return item->clickType(x,y);
599  if (dx < w && dy < h)
600  return ClickType::onItem;
601  return ClickType::outside;
602  }
603 
604  float GodleyIcon::toEditorX(float xx) const
605  {return xx-x()+0.5f*width()-2*border*zoomFactor()-leftMargin();}
606  float GodleyIcon::toEditorY(float yy) const
607  {return yy-y()+0.5f*height()-2*border*zoomFactor()-titleOffs();}
608 
609  void GodleyIcon::onMouseDown(float x, float y)
610  {if (m_editorMode) editor.mouseDown(toEditorX(x),toEditorY(y));}
611 
612  void GodleyIcon::onMouseUp(float x, float y)
613  {if (m_editorMode) editor.mouseUp(toEditorX(x),toEditorY(y));}
614 
615  bool GodleyIcon::onMouseMotion(float x, float y)
616  {
617  if (m_editorMode) editor.mouseMoveB1(toEditorX(x),toEditorY(y));
618  return false;
619  }
620 
621  bool GodleyIcon::onMouseOver(float x, float y)
622  {
623  if (m_editorMode) editor.mouseMove(toEditorX(x),toEditorY(y));
624  return false;
625  }
626 
627  void GodleyIcon::onMouseLeave()
628  {
629  if (m_editorMode)
630  {
631  editor.mouseMove(-1,-1);
632  // May be a bit overzealous, but it solves bug 1273, which is caused by a flow which has not yet fully come into existence....
633  editor.selectedCol=-1;
634  editor.selectedRow=-1;
635  editor.update();
636  }
637  }
638 
639  bool GodleyIcon::onKeyPress(int keySym, const std::string& utf8, int)
640  {
641  if (m_editorMode) editor.keyPress(keySym, utf8);
642  return m_editorMode;
643  }
644 
645  SVGRenderer GodleyIcon::svgRenderer;
646 }
void importDuplicateColumn(GodleyTable &srcTable, int srcCol)
find any duplicate column, and use it as a source column for balanceDuplicateColumns ...
Definition: minsky.cc:617
represents items that have been selected
Definition: selection.h:32
function f
Definition: canvas.m:1
void balanceDuplicateColumns(const GodleyIcon &srcTable, int srcCol)
makes all duplicated columns consistent with srcTable, srcCol
Definition: minsky.cc:752
std::string latexToPango(const char *s)
Definition: latexMarkup.h:30
std::string str() const
Definition: variableType.cc:33
virtual float x() const
Definition: item.cc:107
VariableValues variableValues
Definition: minsky.h:188
virtual float y() const
Definition: item.cc:114
minsky::Minsky minsky
Definition: pyminsky.cc:28
DrawVars(cairo_t *cairo, float x, float y)
Definition: godleyIcon.cc:57
represents rectangular region of a lasso operation
Definition: lasso.h:28
STL namespace.
std::shared_ptr< Item > ItemPtr
Definition: item.h:55
string valueId(const string &name)
construct a valueId from fully qualified name @ name should not be canonicalised
Definition: valueId.cc:75
const std::string & init() const
std::string str() const
Definition: flowCoef.cc:81
move the contents of cell at (srcRow, srcCol) to (destRow, destCol).
Definition: godleyIcon.h:103
void operator()(const GodleyIcon::Variables &vars) const
Definition: godleyIcon.cc:60
std::string timeUnit
Definition: simulation.h:35
std::string trimWS(const std::string &s)
Definition: str.h:49
represents the units (in sense of dimensional analysis) of a variable.
Definition: units.h:34
std::string str(T x)
utility function to create a string representation of a numeric type
Definition: str.h:33
VariablePtr definingVar(const std::string &valueId) const
returns reference to variable defining (ie input wired) for valueId
Definition: minsky.cc:204
const Minsky & cminsky()
const version to help in const correctness
Definition: minsky.h:538
bool operator()(const VariablePtr &x, const VariablePtr &y) const
Definition: godleyIcon.cc:49
const float border
Definition: godleyIcon.cc:41
void ensureItemInserted(const ItemPtr &item)
check if item already present, and if not, inserts item delegates to ensureGroupInserted if passed a ...
Definition: selection.cc:60
represents a numerical coefficient times a variable (a "flow")
Definition: flowCoef.h:27
std::vector< VariablePtr > Variables
Definition: godleyIcon.h:111
float x0
Definition: lasso.h:30
float y1
Definition: lasso.h:30
float x1
Definition: lasso.h:30
void convertVarType(const std::string &name, VariableType::Type type)
Converts variable(s) named by name into a variable of type type.
Definition: minsky.cc:1456
float y0
Definition: lasso.h:30
void draw(cairo_t *) const override
Definition: variable.cc:698
Minsky & minsky()
global minsky object
Definition: addon.cc:545
void accumulateWidthHeight(const GodleyIcon::Variables &vars, float &height, float &width)
Definition: godleyIcon.cc:75
CLASSDESC_ACCESS_EXPLICIT_INSTANTIATION(minsky::GodleyIcon)
float height() const
half height of unrotated image
Definition: cairoItems.h:47
ItemPtr removeItem(const Item &)
Definition: group.cc:212
float width() const
half width of unrotated image
Definition: cairoItems.h:45