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