Minsky
variable.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 
20 #include "minsky.h"
21 #include "geometry.h"
22 #include "valueId.h"
23 #include "variable.h"
24 #include "cairoItems.h"
25 #include "equations.h"
26 #include "nobble.h"
27 
28 #include "dimension.rcd"
29 #include "engNotation.rcd"
30 #include "hypercube.xcd"
31 #include "index.xcd"
32 #include "itemT.rcd"
33 #include "slider.rcd"
34 #include "tensorInterface.xcd"
35 #include "tensorVal.rcd"
36 #include "tensorVal.xcd"
37 #include "variable.rcd"
38 
39 #include <error.h>
40 #include "minsky_epilogue.h"
41 
42 #include <algorithm>
43 
44 #include <boost/locale.hpp>
45 #include <boost/filesystem.hpp>
46 using namespace boost::locale::conv;
47 using boost::filesystem::file_size;
48 
49 using namespace classdesc;
50 using namespace ecolab;
51 
52 using namespace minsky;
53 using namespace ecolab::cairo;
54 
55 namespace minsky
56 {
57  namespace VarAccessors
58  {
59  NameAccessor::NameAccessor(): ecolab::TCLAccessor<VariableBase,std::string,0>
60  ("name",(Getter)&VariableBase::name,(Setter)&VariableBase::name) {}
62  ("init",(Getter)&VariableBase::init,(Setter)&VariableBase::init) {}
64  ("value",(Getter)&VariableBase::value,(Setter)&VariableBase::value) {}
65  }
66 }
67 
68 
69 void VariableBase::addPorts()
70 {
71 #ifndef NDEBUG
72  for (auto& i: m_ports)
73  assert(i.use_count()==1);
74 #endif
75  m_ports.clear();
76  if (numPorts()>0)
77  m_ports.emplace_back(make_shared<Port>(*this));
78  for (size_t i=1; i<numPorts(); ++i)
79  m_ports.emplace_back(make_shared<InputPort>(*this));
80 }
81 
83 {
84  if (auto p=ports(1).lock())
85  return !p->wires().empty();
86  return false;
87 }
88 
89 std::vector<std::string> VariableBase::accessibleVars() const
90 {
91  if (auto g=group.lock())
92  return g->accessibleVars();
93  return {};
94 }
95 
96 
97 ClickType::Type VariableBase::clickType(float xx, float yy) const
98 {
99  const Rotate r(rotation()+(flipped(rotation())? 180: 0),0,0); // rotate into variable's frame of reference
100  const RenderVariable rv(*this);
101  const double z=zoomFactor();
102  try
103  {
104  const double hpx=z*rv.handlePos();
105  double hpy=-z*rv.height();
106  if (rv.height()<0.5*iHeight()) hpy=-z*0.5*iHeight();
107  const double dx=xx-x(), dy=yy-y();
108  if (type()!=constant && hypot(dx - r.x(hpx,hpy), dy-r.y(hpx,hpy)) < 5)
109  return ClickType::inItem;
110  }
111  catch (...) {}
112  return Item::clickType(xx,yy);
113 }
114 
115 //Factory
117 {
118  switch (type)
119  {
120  case undefined: return new Variable<undefined>; break;
121  case constant: return new VarConstant; break;
122  case parameter: return new Variable<parameter>; break;
123  case flow: return new Variable<flow>; break;
124  case stock: return new Variable<stock>; break;
125  case tempFlow: return new Variable<tempFlow>; break;
126  case integral: return new Variable<integral>; break;
127  default:
128  throw error("unknown variable type %s", typeName(type).c_str());
129  }
130 }
131 
133 {
134  if (type==this->type()) return; // nothing to do
135  if (auto vv=vValue())
136  if (type==vv->type())
137  if (auto g=group.lock())
138  for (auto& i: g->items)
139  if (i.get()==this)
140  {
141  VariablePtr vp{i};
142  vp.retype(type);
143  i=vp;
144  return;
145  }
147 }
148 
149 
151 {
152  if (ioVar())
153  if (auto g=group.lock())
154  return g->edgeScale();
155  // scale by GodleyIcon::scaleFactor if part of an Godley icon
156  if (auto g=dynamic_cast<GodleyIcon*>(controller.lock().get()))
157  return g->scaleFactor() * Item::zoomFactor();
158  return Item::zoomFactor();
159 }
160 
161 shared_ptr<VariableValue> VariableBase::vValue() const
162 {
163  auto vv=cminsky().variableValues.find(valueId());
164  if (vv!=minsky().variableValues.end())
165  return vv->second;
166  return {};
167 }
168 
169 vector<unsigned> VariableBase::dims() const
170 {
171  try
172  {
173  if (auto v=vValue()) return v->hypercube().dims();
174  }
175  catch (...) {} // ignore any exceptions caused by evaluating RHS.
176  return {};
177 }
178 
179 vector<string> VariableBase::dimLabels() const
180 {
181  if (auto v=vValue()) return v->hypercube().dimLabels();
182  return {};
183 }
184 
185 
186 string VariableBase::valueId() const
187 {
188  return minsky::valueId(group.lock(), m_name);
189 }
190 
191 std::string VariableBase::valueIdInCurrentScope(const std::string& nm) const
192 {
193  return minsky::valueId(group.lock(), nm);
194 }
195 
197 {
198  return m_name[0]!=':' && group.lock()!=cminsky().canvas.model;
199 }
200 
201 string VariableBase::name() const
202 {
203  if (m_name==":_") return "";
204  // hide any leading ':' in upper level
205  if (m_name[0]==':')
206  {
207  return m_name.substr(1);
208  }
209  return utf_to_utf<char>(m_name);
210 }
211 
212 namespace
213 {
214  const char specialLatex[]="#$%&";
215  // find and quote certain LaTeX characters
216  string quoteLaTeX(const std::string& x)
217  {
218  string quotedName;
219  int next=0;
220  for (auto p=x.find_first_of(specialLatex); p!=string::npos;
221  p=x.find_first_of(specialLatex,p+1))
222  {
223  if (p==0||x[p-1]!='\\')
224  quotedName+=x.substr(next,p-next)+'\\'+x[p];
225  else
226  quotedName+=x.substr(next,p-next+1);
227  next=p+1;
228  }
229  return quotedName+x.substr(next);
230  }
231 }
232 
233 string VariableBase::name(const std::string& name)
234 {
235  // cowardly refuse to set a blank name
236  if (name.empty() || name==":") return name;
237 
238  // check if we need to quote certain characters that have meaning in LaTeX
239  for (auto p=name.find_first_of(specialLatex); p!=string::npos;
240  p=name.find_first_of(specialLatex,p+1))
241  if (p==0||name[p-1]!='\\')
242  return this->name(quoteLaTeX(name));
243 
244  // Ensure value of variable is preserved after rename. For ticket 1106.
245  auto tmpVV=vValue();
246  m_name=name;
248  ensureValueExists(tmpVV.get(),name);
249  assert(vValue()->valueId()==valueId());
250  cachedNameRender.reset();
251  bb.update(*this); // adjust bounding box for new name - see ticket #704
252  if (auto controllingItem=controller.lock())
253  // integrals in particular may have had their size changed with intVar changing name
254  controllingItem->updateBoundingBox();
255  return this->name();
256 }
257 
259 {return dynamic_cast<Group*>(controller.lock().get());}
260 
261 
262 void VariableBase::ensureValueExists(VariableValue* vv, const std::string&/* nm*/) const
263 {
264  string valueId=this->valueId();
265  // disallow blank names
266  if (valueId.length()>1 && valueId.substr(valueId.length()-2)!=":_" &&
267  minsky().variableValues.count(valueId)==0)
268  {
269  assert(isValueId(valueId));
270  // Ensure value of variable is preserved after rename.
271  if (vv==nullptr)
273  first->second->m_scope=minsky::scope(group.lock(),m_name);
274  // Ensure variable names are updated correctly everywhere they appear.
275  else
276  {
277  auto iter=minsky().variableValues.emplace(valueId,VariableValuePtr(type(),*vv)).first->second;
278  iter->name=m_name;
279  iter->m_scope=minsky::scope(group.lock(),m_name);
280  }
281 
282  }
283 }
284 
285 string VariableBase::init() const
286 {
287  auto value=minsky().variableValues.find(valueId());
288  if (value!=minsky().variableValues.end()) {
289  // set initial value of int var to init value of input to second port. for ticket 1137
290  if (type()==integral)
291  {
292  // find attached integral
293  auto i=dynamic_cast<IntOp*>(controller.lock().get());
294  if (!i && !m_ports[1]->wires().empty())
295  i=dynamic_cast<IntOp*>(&(m_ports[1]->wires()[0]->from()->item()));
296  if (i && i->portsSize()>2 && !i->ports(2).lock()->wires().empty())
297  if (auto lhsVar=i->ports(2).lock()->wires()[0]->from()->item().variableCast())
298  if (auto vv=vValue())
299  if (auto lhsVv=lhsVar->vValue())
300  // Since integral takes initial value from second port, the intVar should have the same intial value.
301  if (vv->init()!=lhsVv->init()) vv->init(lhsVv->init());
302  }
303  return value->second->init();
304  }
305  return "0";
306 }
307 
308 string VariableBase::init(const string& x)
309 {
310  ensureValueExists(nullptr,"");
311  if (isValueId(valueId()))
312  {
314  val.init(x);
315  // for constant types, we may as well set the current value. See ticket #433. Also ignore errors (for now), as they will reappear at reset time.
316  try
317  {
318  if (type()==constant || type()==parameter ||
319  (type()==flow && !cminsky().definingVar(valueId())))
320  {
322  cachedNameRender.reset(); // for constants
323  }
324  }
325  catch (...)
326  {}
328  }
329  return x;
330 }
331 
332 double VariableBase::value() const
333 {
334  auto& vvs=minsky::cminsky().variableValues;
335  auto v=vvs.find(valueId());
336  if (v==vvs.end() || !v->second) return 0;
337  return v->second->value();
338 }
339 
340 double VariableBase::value(const double& x)
341 {
342  if (!m_name.empty() && isValueId(valueId()))
343  {
344  (*minsky().variableValues[valueId()])[0]=x;
345  if (type()==constant)
346  cachedNameRender.reset();
347  }
348  return x;
349 }
350 
351 static string emptyString;
352 
353 const std::string& VariableBase::detailedText() const
354 {
355  if (auto vv=vValue())
356  return vv->detailedText;
357  return emptyString ;
358 }
359 
360 const std::string& VariableBase::detailedText(const std::string& x)
361 {
362  if (auto vv=vValue())
363  return vv->detailedText=x;
364  return emptyString ;
365 }
366 
367 const std::string& VariableBase::tooltip() const
368 {
369  if (auto vv=vValue())
370  return vv->tooltip;
371  return emptyString ;
372 }
373 
374 const std::string& VariableBase::tooltip(const std::string& x)
375 {
376  if (auto vv=vValue())
377  return vv->tooltip=x;
378  return emptyString ;
379 }
380 
383 
384 Units VariableBase::units(bool check) const
385 {
387  // we allow possible traversing twice, to allow
388  // stock variable to break the cycle
389  if (unitsCtr-stockVarsPassed>=1)
390  {
391  if (check)
392  throw_error("Cycle detected on wiring network");
393  else
394  return {};
395  }
396 
397  auto it=minsky().variableValues.find(valueId());
398  if (it!=minsky().variableValues.end())
399  {
400  auto& vv=it->second;
401  if (vv->unitsCached) return vv->units;
402 
403 
404  const IncrDecrCounter ucIdc(unitsCtr);
405  const IncrDecrCounter vpIdc(varsPassed);
406  // use a unique ptr here to only increment counter inside a stockVar
407  unique_ptr<IncrDecrCounter> svp;
408 
409  if (isStock()) // we use user defined units
410  {
411  if (unitsCtr==1)
412  {
413  svp.reset(new IncrDecrCounter(stockVarsPassed));
414  // check that input units match output units
415  Units units;
416  if (auto i=dynamic_cast<IntOp*>(controller.lock().get()))
417  units=i->units(check);
418  else if (auto g=dynamic_cast<GodleyIcon*>(controller.lock().get()))
419  units=g->stockVarUnits(name(),check);
420  else // I/O variable
421  units=vv->units;
422  if (check && units.str()!=vv->units.str())
423  {
424  if (auto i=controller.lock())
425  i->throw_error("Inconsistent units "+units.str()+"≠"+vv->units.str());
426  }
427 
428  if (check && controller.lock())
429  {
430  const FlowCoef fc(init());
431  if (!fc.name.empty())
432  {
433  // extract the valueid corresponding to the initialisation variable
434  auto vid=minsky::valueId(vv->m_scope.lock(), fc.name);
435  // find the first variable matching vid, and check that the units match
436  if (auto initVar=cminsky().model->findAny
437  (&GroupItems::items, [&vid](const ItemPtr& i){
438  if (auto v=i->variableCast())
439  return v->valueId()==vid;
440  return false;}))
441  if (units!=initVar->units(check))
442  throw_error("Inconsistent units in initial conditions");
443  }
444  }
445  }
446  }
447  else
448  // updates units in the process
449  if (m_ports.size()>1 && !m_ports[1]->wires().empty())
450  {
451  assert(m_ports[1]->wires()[0]->from());
452  vv->units=m_ports[1]->wires()[0]->from()->item().units(check);
453  }
454  else if (auto v=cminsky().definingVar(valueId()))
455  vv->units=v->units(check);
456 
457  vv->units.normalise();
458  vv->unitsCached=true;
459  return vv->units;
460  }
461  return Units();
462 }
463 
464 void VariableBase::setUnits(const string& x) const
465 {
466  if (isValueId(valueId()))
467  minsky().variableValues[valueId()]->units=Units(x);
468 }
469 
470 
471 
472 void VariableBase::exportAsCSV(const std::string& filename, bool tabular) const
473 {
474  auto value=minsky().variableValues.find(valueId());
475  if (value!=minsky().variableValues.end())
476  value->second->exportAsCSV(filename, name(), tabular);
477 }
478 
479 void VariableBase::importFromCSV(const vector<string>& filenames, const DataSpecSchema& spec) const
480 {
481  if (auto v=vValue()) {
482  v->csvDialog.spec=spec;
483  if (!filenames.empty())
484  v->csvDialog.url=filenames[0];
485  loadValueFromCSVFile(*v, filenames, v->csvDialog.spec);
487  if (!v->hypercube().dimsAreDistinct())
488  throw_error("Axes of imported data should all have distinct names");
489  }
490 }
491 
493 {
494  if (auto vv=vValue())
495  vv->csvDialog.destroyFrame();
496 }
497 
499 {
500  selection.ensureItemInserted(controller.lock());
501 }
502 
503 
504 namespace minsky
505 {
506  template <> size_t Variable<VariableBase::undefined>::numPorts() const {return 0;}
507  template <> size_t Variable<VariableBase::constant>::numPorts() const {return 1;}
508  template <> size_t Variable<VariableBase::parameter>::numPorts() const {return 1;}
509  template <> size_t Variable<VariableBase::flow>::numPorts() const {return 2;}
510  template <> size_t Variable<VariableBase::stock>::numPorts() const {return 1;}
511  template <> size_t Variable<VariableBase::tempFlow>::numPorts() const {return 2;}
512  template <> size_t Variable<VariableBase::integral>::numPorts() const {return 2;}
513 }
514 
515 
517 {
518  const VariablePtr tmp(*this);
519  if (tmp && tmp->type()!=type)
520  {
522  static_cast<VariableBase&>(*get()) = *tmp;
523  for (size_t i=0; i<get()->portsSize() && i< tmp->portsSize(); ++i)
524  for (auto w: tmp->ports(i).lock()->wires())
525  {
526  if (get()->ports(i).lock()->input())
527  w->moveToPorts(w->from(), get()->ports(i).lock());
528  else
529  w->moveToPorts(get()->ports(i).lock(), w->to());
530  }
531  get()->ensureValueExists(nullptr,"");
532  }
533 }
534 
536 {
537  auto g=group.lock();
538  //toplevel i/o items always visible
539  if ((!g || !g->group.lock()) && g==controller.lock()) return true;
540  if (controller.lock()) return false;
541  return Item::visible();
542 }
543 
545 {
546  auto vv=vValue();
547  return enableSlider() &&
548  ((!vv && type()==parameter) ||
549  (vv && vv->size()==1 &&
550  (type()==parameter ||
551  // !inputWired() short circuits the more expensive definingVar operation.
552  (type()==flow && !inputWired() && !cminsky().definingVar(valueId())))));
553 }
554 
556 {
557  if (auto vv=vValue())
558  vv->adjustSliderBounds();
559 }
560 
561 bool VariableBase::onKeyPress(int keySym, const std::string&,int)
562 {
563  switch (keySym)
564  {
565  case 0xff52: case 0xff53: //Right, Up
566  if (auto vv=vValue()) vv->incrSlider(1);
567  if (!minsky().running) minsky().requestReset();
568  return true;
569  case 0xff51: case 0xff54: //Left, Down
570  if (auto vv=vValue()) vv->incrSlider(-1);
571  if (!minsky().running) minsky().requestReset();
572  return true;
573  default:
574  return false;
575  }
576 }
577 
578 std::string VariableBase::definition() const
579 {
581  ostringstream o;
582 
583  auto varDAG=system.getNodeFromVar(*this);
584 
586  {
587  if (varDAG && varDAG->rhs && varDAG->type!=VariableType::constant && varDAG->type!=VariableType::integral)
588  o << varDAG->rhs->latex();
589  else return system.getDefFromIntVar(*this).str();
590  }
591 
592  return o.str();
593 }
594 
596 {
597  if (enabled)
598  {
599  miniPlot=make_shared<ecolab::Plot>();
600  miniPlot->plotType=ecolab::Plot::PlotType::bar;
601  }
602  else
603  miniPlot=nullptr;
604  return enabled;
605 }
606 
608 {
609  if (miniPlotEnabled())
610  miniPlot->clear();
611 }
612 
613 bool VariableBase::onMouseMotion(float x, float y)
614 {
615  if (auto vv=vValue())
616  {
617  const RenderVariable rv(*this);
618  const double rw=fabs(zoomFactor()*(rv.width()<iWidth()? 0.5*iWidth() : rv.width())*cos(rotation()*M_PI/180));
619  const double sliderPos=(x-this->x())* (vv->sliderMax-vv->sliderMin)/rw+0.5*(vv->sliderMin+vv->sliderMax);
620  const double sliderHatch=sliderPos-fmod(sliderPos,vv->sliderStep); // matches slider's hatch marks to sliderStep value. for ticket 1258
621  vv->sliderSet(sliderHatch);
622  }
623  // push History to prevent an unnecessary reset when
624  // adjusting the slider whilst paused. See ticket #812
625  minsky().pushHistory();
626  if (minsky().reset_flag())
627  minsky().requestReset();
628  minsky().evalEquations();
629  return true;
630 }
631 
633 {
634  if (auto vv=vValue())
635  return vv->sliderMin;
636  return 0;
637 }
638 
639 
640 double VariableBase::sliderMin(double x) const
641 {
642  if (auto vv=vValue())
643  return vv->sliderMin=x;
644  return 0;
645 }
646 
648 {
649  if (auto vv=vValue())
650  return vv->sliderMax;
651  return 0;
652 }
653 
654 double VariableBase::sliderMax(double x) const
655 {
656  if (auto vv=vValue())
657  return vv->sliderMax=x;
658  return 0;
659 }
660 
662 {
663  if (auto vv=vValue())
664  return vv->sliderStep;
665  return 0;
666 }
667 
668 double VariableBase::sliderStep(double x) const
669 {
670  if (auto vv=vValue())
671  return vv->sliderStep=x;
672  return 0;
673 }
674 
676 {
677  if (auto vv=vValue())
678  return vv->sliderStepRel;
679  return false;
680 }
681 
682 bool VariableBase::sliderStepRel(bool x) const
683  {
684  if (auto vv=vValue())
685  return vv->sliderStepRel=x;
686  return false;
687 }
688 
690 {
691  if (auto vv=vValue())
692  return vv->enableSlider;
693  return false;
694 }
695 
697 {
698  if (auto vv=vValue())
699  return vv->enableSlider=x;
700  return false;
701 }
702 
703 
704 void VariableBase::draw(cairo_t *cairo) const
705 {
706  auto [angle,flipped]=rotationAsRadians();
707  const float z=zoomFactor();
708 
709  // grab a thread local copy of the renderer caches, as MacOSX does
710  // rendering on a different thread, and this avoids a race condition
711  // when the cache is invalidated
712  auto l_cachedNameRender=cachedNameRender;
713  if (!l_cachedNameRender || cairo!=cachedNameRender->cairoContext())
714  {
715  l_cachedNameRender=cachedNameRender=std::make_shared<RenderVariable>(*this,cairo);
716  l_cachedNameRender->setFontSize(12.0);
717  }
718 
719  // if rotation is in 1st or 3rd quadrant, rotate as
720  // normal, otherwise flip the text so it reads L->R
721  const Rotate r(rotation() + (flipped? 180:0),0,0);
722  l_cachedNameRender->angle=angle+(flipped? M_PI:0);
723 
724  // parameters of icon in userspace (unscaled) coordinates
725  const double w=std::max(l_cachedNameRender->width(), 0.5f*iWidth());
726  const double h=std::max(l_cachedNameRender->height(), 0.5f*iHeight());
727  const double hoffs=l_cachedNameRender->top();
728 
729  unique_ptr<cairo::Path> clipPath;
730  {
731  const CairoSave cs(cairo);
732  cairo_scale(cairo, z,z);
733  cairo_move_to(cairo,r.x(-w+1,-h-hoffs+2), r.y(-w+1,-h-hoffs+2));
734  {
735  const CairoSave cs(cairo);
736  if (local())
737  cairo_set_source_rgb(cairo,0,0,1);
738  l_cachedNameRender->show();
739  }
740 
741  auto vv=vValue();
742  if (miniPlot && vv && vv->size()==1)
743  try
744  {
745  if (cachedTime!=cminsky().t)
746  {
747  cachedTime=cminsky().t;
748  miniPlot->addPt(0,cachedTime,vv->value());
749  miniPlot->setMinMax();
750  }
751  const CairoSave cs(cairo);
752  cairo_translate(cairo,-w,-h);
753  miniPlot->draw(cairo,2*w,2*h);
754  }
755  catch (...) {} // ignore errors in obtaining values
756 
757  // For feature 47
758  try
759  {
760  if (type()!=constant && !ioVar() && vv && vv->size()==1 && vv->idxInRange())
761  {
762  auto l_cachedMantissa=cachedMantissa;
763  auto l_cachedExponent=cachedExponent;
764  if (!l_cachedMantissa || l_cachedMantissa->cairoContext()!=cairo)
765  {
766  l_cachedMantissa=cachedMantissa=make_shared<Pango>(cairo);
767  l_cachedMantissa->setFontSize(6.0);
768  l_cachedExponent=cachedExponent=make_shared<Pango>(cairo);
769  l_cachedExponent->setFontSize(6.0);
770  cachedValue=nan("");
771  }
772 
773  auto val=engExp();
774  if (value()!=cachedValue)
775  {
776  cachedValue=value();
777  if (!isnan(value())) {
778  if (sliderVisible())
779  l_cachedMantissa->setMarkup
780  (mantissa(val,
781  int(1+
782  (vv->sliderStepRel?
783  -log10(vv->maxSliderSteps()):
784  log10(vv->value()/vv->maxSliderSteps())
785  ))));
786  else
787  l_cachedMantissa->setMarkup(mantissa(val));
788  }
789  else if (isinf(value())) { // Display non-zero divide by zero as infinity. For ticket 1155
790  if (signbit(value())) l_cachedMantissa->setMarkup("-∞");
791  else l_cachedMantissa->setMarkup("∞");
792  }
793  else // Display all other NaN cases as ???. For ticket 1155
794  l_cachedMantissa->setMarkup("???");
795  l_cachedExponent->setMarkup(expMultiplier(val.engExp));
796  }
797  l_cachedMantissa->angle=angle+(flipped? M_PI:0);
798 
799  cairo_move_to(cairo,r.x(w-l_cachedMantissa->width()-2,-h-hoffs+2),
800  r.y(w-l_cachedMantissa->width()-2,-h-hoffs+2));
801  l_cachedMantissa->show();
802 
803  if (val.engExp!=0 && !isnan(value())) // Avoid large exponential number in variable value display. For ticket 1155
804  {
805  cairo_move_to(cairo,r.x(w-l_cachedExponent->width()-2,0),r.y(w-l_cachedExponent->width()-2,0));
806  l_cachedExponent->show();
807  }
808  }
809  }
810  catch (...) {} // ignore errors in obtaining values
811 
812  {
813  const cairo::CairoSave cs(cairo);
814  cairo_rotate(cairo, angle);
815  // constants and parameters should be rendered in blue, all others in red
816  switch (type())
817  {
818  case constant: case parameter:
819  cairo_set_source_rgb(cairo,0,0,1);
820  break;
821  default:
822  cairo_set_source_rgb(cairo,1,0,0);
823  break;
824  }
825  cairo_move_to(cairo,-w,-h);
826  if (lhs())
827  cairo_line_to(cairo,-w+2,0);
828  cairo_line_to(cairo,-w,h);
829  cairo_line_to(cairo,w,h);
830  cairo_line_to(cairo,w+2,0);
831  cairo_line_to(cairo,w,-h);
832  cairo_close_path(cairo);
833  clipPath.reset(new cairo::Path(cairo));
834  cairo_stroke(cairo);
835  if (sliderVisible())
836  {
837  // draw slider
838  const CairoSave cs(cairo);
839  cairo_set_source_rgb(cairo,0,0,0);
840  try
841  {
842  cairo_arc(cairo,(flipped?-1.0:1.0)*l_cachedNameRender->handlePos(), (flipped? h: -h), sliderHandleRadius, 0, 2*M_PI);
843  }
844  catch (const error&) {} // handlePos() may throw.
845  cairo_fill(cairo);
846  }
847  }// undo rotation
848 
849  const double x0=z*w, y0=0, x1=-z*w+2, y1=0;
850  const double sa=sin(angle), ca=cos(angle);
851  if (!m_ports.empty())
852  m_ports[0]->moveTo(x()+(x0*ca-y0*sa),
853  y()+(y0*ca+x0*sa));
854  if (m_ports.size()>1)
855  m_ports[1]->moveTo(x()+(x1*ca-y1*sa),
856  y()+(y1*ca+x1*sa));
857  }
858 
859  auto g=group.lock();
860  if (mouseFocus || (ioVar() && g && g->mouseFocus))
861  {
862  const cairo::CairoSave cs(cairo);
863  drawPorts(cairo);
864  displayTooltip(cairo,tooltip());
866  }
867 
868  cairo_new_path(cairo);
869  clipPath->appendToCurrent(cairo);
870  // Rescale size of variable attached to intop. For ticket 94
871  cairo_clip(cairo);
872  if (selected) drawSelected(cairo);
873 }
874 
876 {
877  const float invZ=1/zoomFactor();
878  moveTo(0.5*(b.x0+b.x1), 0.5*(b.y0+b.y1));
879  iWidth(abs(b.x1-b.x0)*invZ);
880  iHeight(abs(b.y1-b.y0)*invZ);
881 }
882 
884 {
885  retype(minsky::cminsky().variableValues[get()->valueId()]->type());
886 }
887 
888 int VarConstant::nextId=0;
889 
#define M_PI
some useful geometry types, defined from boost::geometry
Definition: geometry.h:29
std::string expMultiplier(int exp)
represents items that have been selected
Definition: selection.h:32
bool onKeyPress(int, const std::string &, int) override
respond to key press events
Definition: variable.cc:561
bool miniPlotEnabled() const
Definition: variable.h:242
void drawPorts(cairo_t *cairo) const
Definition: item.cc:294
Units units() const
Definition: variable.h:195
Expr sin(const Expr &x)
Definition: expr.h:131
classdesc::Exclude< std::shared_ptr< ecolab::Pango > > cachedExponent
Definition: variable.h:79
Expr cos(const Expr &x)
Definition: expr.h:137
void requestReset()
Definition: minsky.cc:467
std::string str() const
Definition: variableType.cc:33
virtual void displayTooltip(cairo_t *, const std::string &) const
display tooltip text, eg on mouseover
Definition: item.cc:398
virtual std::size_t numPorts() const =0
void destroyFrame() override
clean up popup window structures on window close
Definition: variable.cc:492
void resetValue(VariableValue &) const
reset a give variable value to it&#39;s initial condition, in this context
virtual float x() const
Definition: item.cc:107
std::string valueIdInCurrentScope(const std::string &nm) const
returns valueId for nm. If nm is not qualified, this variable&#39;s scope is used
Definition: variable.cc:191
static int stockVarsPassed
for detecting reentrancy in units()
Definition: variable.h:105
bool pushHistory()
push current model state onto history if it differs from previous
Definition: minsky.cc:1265
reset
Definition: minsky.tcl:1325
VariableValues variableValues
Definition: minsky.h:200
virtual bool visible() const
whether this item is visible on the canvas.
Definition: item.cc:250
virtual float y() const
Definition: item.cc:114
void makeConsistentWithValue()
make variable&#39;s type consistent with the type of the valueId
Definition: variable.cc:883
float iHeight() const
Definition: item.h:224
exception-safe increment/decrement of a counter in a block
Definition: variable.h:56
string quoteLaTeX(const std::string &x)
Definition: variable.cc:216
bool onResizeHandles
set to true to indicate mouse is ovcaler resize handles
Definition: item.h:175
std::string name
Definition: flowCoef.h:30
represents rectangular region of a lasso operation
Definition: lasso.h:28
STL namespace.
virtual double value() const override
< set the initial value for this variable
Definition: variable.cc:332
float y(float x, float y) const
Definition: geometry.h:60
size_t scope(const string &name)
extract scope from a qualified variable name
Definition: valueId.cc:83
std::vector< std::string > accessibleVars() const
return a list of existing variables this could be connected to
Definition: variable.cc:89
void retype(VariableBase::Type type)
changes type of variable to type
Definition: variable.cc:516
std::shared_ptr< Item > ItemPtr
Definition: item.h:57
constexpr float sliderHandleRadius
Definition: slider.h:24
std::size_t numPorts() const override
std::pair< double, bool > rotationAsRadians() const
return the rotation as radians, and whether rotation should have additional straight angle added for ...
Definition: item.cc:92
string valueId(const string &name)
construct a valueId from fully qualified name @ name should not be canonicalised
Definition: valueId.cc:75
double sliderMax() const
slider parameters
Definition: variable.cc:647
double handlePos() const
Definition: cairoItems.cc:97
bool inputWired() const
Definition: variable.cc:82
rotate (x,y) by rot (in degrees) around the origin (x0, y0) can be used for rotating multiple points ...
Definition: geometry.h:44
const std::string & tooltip() const override
Definition: variable.cc:367
a shared_ptr that default constructs a default target, and is always valid
const std::string & init() const
Model model
Definition: canvas.h:103
virtual Type type() const =0
BoundingBox bb
canvas bounding box.
Definition: item.h:195
void exportAsCSV(const std::string &filename, bool tabular) const
export this variable as a CSV file
Definition: variable.cc:472
virtual float zoomFactor() const
Definition: item.cc:121
bool flipped(double rotation)
returns if the angle (in degrees) is in the second or third quadrant
Definition: geometry.h:102
void resize(const LassoBox &b) override
resize this item on the canvas
Definition: variable.cc:875
void update(const Item &x)
Definition: item.cc:46
static VariableBase * create(Type type)
Definition: variable.cc:116
int unitsCtr
for detecting reentrancy in units()
Definition: variable.h:104
struct TCLcmd::trap::init_t init
static string emptyString
Definition: variable.cc:351
bool sliderVisible() const
returns true if slider is to be drawn
Definition: variable.cc:544
CLASSDESC_ACCESS_EXPLICIT_INSTANTIATION(minsky::VariablePtr)
virtual ClickType::Type clickType(float x, float y) const
returns the clicktype given a mouse click at x, y.
Definition: item.cc:275
float zoomFactor() const override
Definition: variable.cc:150
bool visible() const override
whether this item is visible on the canvas.
Definition: variable.cc:535
static void drawSelected(cairo_t *cairo)
Definition: item.cc:308
Creation and access to the minskyTCL_obj object, which has code to record whenever Minsky&#39;s state cha...
Definition: constMap.h:22
classdesc::Exclude< std::weak_ptr< Item > > controller
reference to a controlling item - eg GodleyIcon, IntOp or a Group if an IOVar.
Definition: variable.h:129
float iWidth() const
Definition: item.h:217
void insertControlled(Selection &selection) override
Definition: variable.cc:498
bool ioVar() const override
indicates this is a group I/O variable
Definition: variable.cc:258
static int varsPassed
for caching units calculation factory method
Definition: variable.h:116
std::vector< std::string > dimLabels() const
labels along each axis
Definition: variable.cc:179
virtual std::string valueId() const
string used to link to the VariableValue associated with this
Definition: variable.cc:186
represents the units (in sense of dimensional analysis) of a variable.
Definition: units.h:34
Canvas canvas
Definition: minsky.h:256
void adjustSliderBounds()
Definition: variable.cc:555
void ensureValueExists(VariableValue *vv, const std::string &name) const
ensure an associated variableValue exists
Definition: variable.cc:262
static std::string typeName(int t)
Definition: variableType.cc:30
VariablePtr definingVar(const std::string &valueId) const
returns reference to variable defining (ie input wired) for valueId
Definition: minsky.cc:205
std::string m_canonicalName
latex processed and active stripped version of name
Definition: variable.h:102
std::string definition() const
formula defining this variable
Definition: variable.cc:578
const Minsky & cminsky()
const version to help in const correctness
Definition: minsky.h:549
EngNotation engExp() const
return formatted mantissa and exponent in engineering format
Definition: variable.h:227
const std::string & detailedText() const override
Definition: variable.cc:353
void evalEquations(double result[], double, const double vars[])
Definition: rungeKutta.cc:248
void throw_error(const std::string &) const
mark item on canvas, then throw
Definition: item.cc:86
void importFromCSV(const std::vector< std::string > &filenames, const DataSpecSchema &spec) const
import CSV files, using spec
Definition: variable.cc:479
void loadValueFromCSVFile(VariableValue &v, const vector< string > &filenames, const DataSpec &spec)
load a variableValue from a list of files according to data spec
Definition: CSVParser.cc:1058
ItemPortVector m_ports
Definition: item.h:156
string canonicalName(const string &name)
convert a raw name into a canonical name - this is not idempotent.
Definition: valueId.cc:63
VariableDAGPtr getNodeFromVar(const VariableBase &v)
Definition: equations.cc:882
bool selected
true if selected for cut, copy or group operation
Definition: noteBase.h:32
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
bool isStock() const
Definition: variable.h:206
bool isValueId(const string &name)
check that name is a valid valueId (useful for assertions)
Definition: valueId.cc:33
std::string init() const
the initial value of this variable
Definition: variable.cc:285
ClickType::Type clickType(float x, float y) const override
returns the clicktype given a mouse click at x, y.
Definition: variable.cc:97
virtual std::string name() const
variable displayed name
Definition: variable.cc:201
bool enableSlider() const
slider parameters
Definition: variable.cc:689
float x0
Definition: lasso.h:30
void populateMissingDimensionsFromVariable(const VariableValue &, bool &incompatibleMessageDisplayed)
populate missing dimensions from a variableValue
Definition: minsky.cc:516
bool mouseFocus
true if target of a mouseover
Definition: noteBase.h:31
std::string mantissa(const EngNotation &e, int digits=3) const
Definition: variable.h:229
bool sliderStepRel() const
slider parameters
Definition: variable.cc:675
classdesc::Exclude< std::shared_ptr< ecolab::Pango > > cachedMantissa
Definition: variable.h:78
float y1
Definition: lasso.h:30
std::string m_name
Definition: variable.h:101
float x1
Definition: lasso.h:30
classdesc::Exclude< std::shared_ptr< RenderVariable > > cachedNameRender
cached Pango objects
Definition: variable.h:77
double t
time
Definition: rungeKutta.h:57
void convertVarType(const std::string &name, VariableType::Type type)
Converts variable(s) named by name into a variable of type type.
Definition: minsky.cc:1457
void moveTo(float x, float y)
Definition: item.cc:256
void updateBoundingBox() override
Definition: item.h:201
bool local() const
Definition: variable.cc:196
GroupPtr model
Definition: minsky.h:255
virtual std::weak_ptr< Port > ports(std::size_t i) const
callback to be run when item deleted from group
Definition: item.h:180
double sliderStep() const
slider parameters
Definition: variable.cc:661
float y0
Definition: lasso.h:30
Definition: group.tcl:84
void draw(cairo_t *) const override
Definition: variable.cc:704
Minsky & minsky()
global minsky object
Definition: minskyTCL.cc:51
ostringstream getDefFromIntVar(const VariableBase &v)
Definition: equations.cc:891
float x(float x, float y) const
Definition: geometry.h:59
bool lhs() const
variable is on left hand side of flow calculation
Definition: variable.h:202
std::shared_ptr< VariableValue > vValue() const
variableValue associated with this. nullptr if not associated with a variableValue ...
Definition: variable.cc:161
classdesc::Exclude< std::shared_ptr< ecolab::Plot > > miniPlot
miniature plot feature
Definition: variable.h:110
void drawResizeHandles(cairo_t *cairo) const override
Definition: item.cc:355
static int nextId
Definition: variable.h:301
double sliderMin() const
slider parameters
Definition: variable.cc:632
void retype(VariableType::Type type)
attempt to replace this variable with variable of type.
Definition: variable.cc:132
double rotation() const
Definition: item.h:211
float height() const
half height of unrotated image
Definition: cairoItems.h:47
void setUnits(const std::string &) const
Definition: variable.cc:464
float width() const
half width of unrotated image
Definition: cairoItems.h:45
bool onMouseMotion(float x, float y) override
respond to mouse motion events with button pressed
Definition: variable.cc:613