Minsky
equationDisplayRender.cc
Go to the documentation of this file.
1 /*
2  @copyright Steve Keen 2014
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 "cairoItems.h"
20 #include "dataOp.h"
21 #include "equations.h"
22 #include "latexMarkup.h"
23 #include "selection.h"
24 #include "lasso.h"
25 #include "userFunction.h"
26 #include <pango.h>
27 #include "minsky_epilogue.h"
28 using namespace ecolab;
29 using ecolab::cairo::Surface;
30 using ecolab::cairo::CairoSave;
31 
32 namespace MathDAG
33 {
34  namespace
35  {
36 
37  enum class Anchor {n,e,w,s,ne,nw,se,sw};
38 
39  // move pen to the anchor point of \a object
40  template <class T>
41  void moveToAnchor(cairo_t* cairo, const T& object, Anchor anchor)
42  {
43  switch (anchor)
44  {
45  case Anchor::n:
46  cairo_rel_move_to(cairo, -0.5*object.width(), 0); break;
47  case Anchor::s:
48  cairo_rel_move_to(cairo, -0.5*object.width(), -object.height()); break;
49  case Anchor::e:
50  cairo_rel_move_to(cairo, object.width(), -0.5*object.height()); break;
51  case Anchor::w:
52  cairo_rel_move_to(cairo, 0, -0.5*object.height()); break;
53  case Anchor::ne:
54  cairo_rel_move_to(cairo, -object.width(), 0); break;
55  case Anchor::nw:
56  break;
57  case Anchor::se:
58  cairo_rel_move_to(cairo, -object.width(), -object.height()); break;
59  case Anchor::sw:
60  cairo_rel_move_to(cairo, 0, -object.height()); break;
61  }
62  }
63 
64  // renders latex string to \a cairo, anchored at \a anchor
65  // move current point to RHS of text, and returns the height
66  double print(cairo_t* cairo, const string& text, Anchor anchor)
67  {
68  Pango pango(cairo);
69  {
70  const CairoSave cs(cairo);
71  pango.setMarkup(text);
72  moveToAnchor(cairo, pango, anchor);
73  pango.show();
74  }
75  cairo_rel_move_to(cairo, pango.width(), 0);
76  return pango.height();
77  }
78 
79  struct RecordingSurface: public Surface
80  {
81  RecordingSurface(): Surface(cairo_recording_surface_create
82  (CAIRO_CONTENT_COLOR, nullptr))
83  {cairo_move_to(cairo(),0,0);}
84  };
85 
86  // renders \a x a function taking a Surface, with big enough brackets around them
87  template <class X>
88  double parenthesise(Surface& s, X x, const string& left="(", const string& right=")")
89  {
90  double xx,yy;
91  cairo_get_current_point(s.cairo(),&xx,&yy);
92 
94  x(r);
95  Pango pango(s.cairo());
96  const double oldFs=pango.getFontSize();
97  pango.setFontSize(r.height());
98  pango.setMarkup(left);
99  cairo_rel_move_to(s.cairo(),0,-(r.height()-oldFs));
100  pango.show();
101  cairo_rel_move_to(s.cairo(),0,(r.height()-oldFs));
102  cairo_rel_move_to(s.cairo(),pango.width(),0);
103  x(s);
104 
105  xx+=pango.width()+r.width();
106  pango.setMarkup(right);
107  cairo_move_to(s.cairo(),xx,yy-r.height()+oldFs);
108  pango.show();
109  cairo_move_to(s.cairo(),xx+pango.width(),yy);
110  return r.height();
111  }
112 
113  // render list of arguments separated by \a op, or \a empty if arglist is empty
114  void naryRender(Surface& dest, const vector<WeakNodePtr>& arglist, int BODMASlevel, const char* op, const char *empty)
115  {
116  if (arglist.empty())
117  print(dest.cairo(),empty,Anchor::nw);
118  else
119  for (size_t j=0; j<arglist.size(); j++)
120  {
121  if (arglist[j]->BODMASlevel()>BODMASlevel)
122  parenthesise(dest, [&](Surface& dest) {arglist[j]->render(dest);});
123  else
124  arglist[j]->render(dest);
125  if (j!=arglist.size()-1)
126  print(dest.cairo(),op,Anchor::nw);
127  }
128  }
129 
130  void variableRender(Surface& surf, const VariableDAG& v)
131  {
132  print(surf.cairo(), latexToPango(v.name)+" = ", Anchor::nw);
133  if (v.rhs)
134  v.rhs->render(surf);
135  else
136  print(surf.cairo(), latexToPango(latexInit(v.init)), Anchor::nw);
137  }
138 
139  }
140 
141  void SystemOfEquations::renderEquations(Surface& dest, double height) const
142  {
143  double x, y; // starting position of current line
144  cairo_get_current_point(dest.cairo(),&x,&y);
145  const double origin=-y;
146  Pango den(dest.cairo());
147  den.setMarkup("dt");
148 
149  const double fontHeight=30;
150  const int baseEqn=origin/fontHeight;
151  int eqnNo=0;
152 
153  for (const VariableDAG* i: variables)
154  {
155  if (dynamic_cast<const IntegralInputVariableDAG*>(i)) continue;
156  if (!i || i->type==VariableType::constant) continue;
157  if (eqnNo++ < baseEqn) continue;
158  RecordingSurface line;
159  variableRender(line,*i);
160  cairo_move_to(dest.cairo(), x, y-line.top());
161  variableRender(dest,*i);
162  y+=line.height()+4;
163  cairo_move_to(dest.cairo(), x, y);
164  if (y>height) return;
165  }
166 
167  for (const VariableDAG* i: integrationVariables)
168  {
169  if (eqnNo++ < baseEqn) continue;
170  // initial conditions
171  cairo_move_to(dest.cairo(), x, y); // needed to define a current point on the equations tab. for ticket 1256
172  y+=print(dest.cairo(), latexToPango(mathrm(i->name))+"(0) = "+
173  latexToPango(latexInit(i->init)),Anchor::nw);
174 
175  // differential equation
176  Pango num(dest.cairo());
177  num.setMarkup("d"+latexToPango(mathrm(i->name)));
178  double lineSpacing=num.height()+den.height()+2;
179 
180  const VariableDAGPtr input=expressionCache.getIntegralInput(i->valueId);
181  if (input && input->rhs)
182  { // adjust linespacing to allow enough height for RHS
183  RecordingSurface rhs;
184  input->rhs->render(rhs);
185  lineSpacing = max(rhs.height(), lineSpacing);
186  }
187 
188  // vertical location of the = sign
189  const double eqY=y+max(num.height(), 0.5*lineSpacing);
190 
191  cairo_move_to(dest.cairo(), x, eqY-num.height());
192  num.show();
193  cairo_move_to(dest.cairo(), x, eqY);
194  const double solidusLength = max(num.width(),den.width());
195  cairo_rel_line_to(dest.cairo(), solidusLength, 0);
196  cairo_stroke(dest.cairo());
197  cairo_move_to(dest.cairo(), x+solidusLength, eqY);
198  // display RHS here
199  if (input && input->rhs)
200  {
201  print(dest.cairo()," = ", Anchor::w);
202  input->rhs->render(dest);
203  }
204  else
205  print(dest.cairo()," = 0", Anchor::w);
206  cairo_move_to(dest.cairo(), x+0.5*(num.width()-den.width()), eqY);
207  den.show();
208  cairo_move_to(dest.cairo(), x, y+=lineSpacing);// move to next line
209 
210  if (y>height) return;
211  }
212  }
213 
214  void ConstantDAG::render(ecolab::cairo::Surface& surf) const
215  {
216  print(surf.cairo(), latexToPango(value),Anchor::nw);
217  }
218 
219  void VariableDAG::render(ecolab::cairo::Surface& surf) const
220  {
221  print(surf.cairo(), latexToPango(mathrm(name)), Anchor::nw);
222  }
223 
224  void LockDAG::render(ecolab::cairo::Surface& surf) const
225  {
226  print(surf.cairo(), "locked", Anchor::nw);
227  }
228 
229 
230  template <>
232  {
233  assert(!"constant deprecated");
234  }
235 
236  template <>
238  {
239  for (size_t i=0; i<arguments.size(); ++i)
240  {
241  naryRender(surf, arguments[i], BODMASlevel()," + ","0");
242  if (i!=arguments.size()-1) print(surf.cairo()," + ",Anchor::nw);
243  }
244  }
245 
246  template <>
248  {
249  if (!arguments.empty())
250  naryRender(surf, arguments[0], BODMASlevel(), " + ","0");
251  if (arguments.size()>1)
252  {
253  print(surf.cairo()," - ",Anchor::nw);
254  if (arguments[1].size()>1 ||
255  (!arguments[1].empty() && BODMASlevel() == arguments[1][0]->BODMASlevel()))
256  parenthesise(surf, [&](Surface& s){naryRender(s,arguments[1], BODMASlevel(), " + ","0");});
257  else
258  naryRender(surf, arguments[1], BODMASlevel(), " + ","0");
259  }
260  }
261 
262  template <>
264  {
265  for (size_t i=0; i<arguments.size(); ++i)
266  {
267  naryRender(surf,arguments[i], BODMASlevel(), " × ","1");
268  if (i!=arguments.size()-1) print(surf.cairo()," × ",Anchor::nw);
269  }
270  }
271 
272  template <>
274  {
275  RecordingSurface num, den;
276  if (!arguments.empty())
277  naryRender(num, arguments[0], BODMASlevel()," × ","1");
278 
279  if (arguments.size()>1)
280  {
281  naryRender(den, arguments[1], BODMASlevel()," × ","1");
282  if (!arguments[1].empty())
283  {
284  double x, y; // starting position of current line
285  cairo_get_current_point(surf.cairo(),&x,&y);
286 
287  const double solidusLength = std::max(num.width(),den.width());
288 
289  cairo_move_to(surf.cairo(), x+0.5*(solidusLength-num.width()), y-num.height());
290  naryRender(surf, arguments[0], BODMASlevel()," × ","1");
291  cairo_move_to(surf.cairo(), x+0.5*(solidusLength-den.width()), y+0.5*num.height()+5);
292  naryRender(surf, arguments[1], BODMASlevel()," × ","1");
293 
294  cairo_move_to(surf.cairo(), x, y+0.5*num.height()+5);
295  cairo_rel_line_to(surf.cairo(), solidusLength, 0);
296  {
297  const CairoSave cs(surf.cairo());
298  cairo_set_line_width(surf.cairo(),1);
299  cairo_stroke(surf.cairo());
300  }
301  cairo_move_to(surf.cairo(),x+solidusLength,y);
302  }
303  else // denominator =1, so just render the numerator
304  naryRender(surf, arguments[0], BODMASlevel()," × ","1");
305  }
306  else // denominator =1, so just render the numerator
307  naryRender(surf, arguments[0], BODMASlevel()," × ","1");
308  }
309 
310  template <>
311  void OperationDAG<OperationType::log>::render(Surface& surf) const
312  {
313  if (arguments.empty() || arguments[0].empty() || !arguments[0][0])
314  print(surf.cairo(),"<i>0</i>",Anchor::nw);
315  else
316  {
317  if (arguments.size()>1 && !arguments[1].empty() && arguments[1][0])
318  {
319  const double h=print(surf.cairo(),"log",Anchor::nw);
320  cairo_rel_move_to(surf.cairo(), 0, 0.5*h);
321  arguments[1][0]->render(surf);
322  cairo_rel_move_to(surf.cairo(), 0, -0.5*h);
323  }
324  else
325  print(surf.cairo(),"ln",Anchor::nw);
326  arguments[0][0]->render(surf);
327  }
328  }
329 
330  template <>
332  {
333  print(surf.cairo(),"<i>"+dynamic_cast<UserFunction&>(*state).description()+"</i>",Anchor::nw);
334  if (arguments.empty() || arguments[0].empty() || !arguments[0][0])
335  print(surf.cairo(),"(0,0)",Anchor::nw);
336  else
337  parenthesise(surf, [&](Surface& surf){
338  arguments[0][0]->render(surf);
339  print(surf.cairo(),",",Anchor::nw);
340  if (arguments.size()>1 && !arguments[1].empty() && arguments[1][0])
341  arguments[1][0]->render(surf);
342  else
343  print(surf.cairo(),"0",Anchor::nw);
344  });
345  }
346 
347  template <>
348  void OperationDAG<OperationType::pow>::render(Surface& surf) const
349  {
350  if (arguments.empty() || arguments[0].empty() || !arguments[0][0])
351  print(surf.cairo(),"<i>0</i>",Anchor::nw);
352  else
353  {
354  if (arguments.size()>1 && !arguments[1].empty() && arguments[1][0])
355  {
356  if (arguments[0][0]->BODMASlevel()>BODMASlevel())
357  parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});
358  else
359  arguments[0][0]->render(surf);
360 
361  RecordingSurface base;
362  arguments[0][0]->render(base);
363  cairo_rel_move_to(surf.cairo(), 0, -0.5*base.height());
364  arguments[1][0]->render(surf);
365  cairo_rel_move_to(surf.cairo(), 0, 0.5*base.height());
366  }
367  else
368  print(surf.cairo(),"<i>1</i>",Anchor::nw);
369  }
370 
371  }
372 
373 
374  template <>
375  void OperationDAG<OperationType::time>::render(Surface& surf) const
376  {print(surf.cairo(),"<i>t</i>",Anchor::nw);}
377  template <>
379  {print(surf.cairo(),"<i>e</i>",Anchor::nw);}
380  template <>
381  void OperationDAG<OperationType::pi>::render(Surface& surf) const
382  {print(surf.cairo(),"<i>π</i>",Anchor::nw);}
383  template <>
384  void OperationDAG<OperationType::zero>::render(Surface& surf) const
385  {print(surf.cairo(),"<i>0</i>",Anchor::nw);}
386  template <>
387  void OperationDAG<OperationType::one>::render(Surface& surf) const
388  {print(surf.cairo(),"<i>1</i>",Anchor::nw);}
389  template <>
390  void OperationDAG<OperationType::inf>::render(Surface& surf) const
391  {print(surf.cairo(),"<i>∞</i>",Anchor::nw);}
392  template <>
394  {
395  double xx,yy;
396  cairo_get_current_point(s.cairo(),&xx,&yy);
397 
398  RecordingSurface r;
399  arguments[0][0]->render(r);
400 
401  Pango pango(s.cairo());
402  const double oldFs=pango.getFontSize();
403  pango.setFontSize(r.height());
404  cairo_rel_move_to(s.cairo(),0,-(r.height()-oldFs));
405  pango.show();
406  cairo_rel_move_to(s.cairo(),0,(r.height()-oldFs));
407  cairo_rel_move_to(s.cairo(),pango.width(),0);
408  arguments[0][0]->render(s);
409  xx+=pango.width()+r.width();
410  pango.setMarkup("%");
411  cairo_move_to(s.cairo(),xx,yy-r.height()+oldFs);
412  pango.show();
413  cairo_move_to(s.cairo(),xx+pango.width(),yy);
414  }
415  template <>
417  {print(surf.cairo(),"=",Anchor::nw);}
418  template <>
420  {throw error("should not be rendering integration operations");}
421 
422 // if (IntOp* i=dynamic_cast<IntOp*>(state.get()))
423 // if (VariablePtr v=i->intVar)
424 // print(surf.cairo(), latexToPango(mathrm(v->name())),Anchor::nw);
425 // }
426  template <>
428  {throw error("should not be rendering differentiation operations");}
429  template <>
430  void OperationDAG<OperationType::sqrt>::render(Surface& surf) const
431  {
432  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
433  {
434  RecordingSurface arg;
435  arguments[0][0]->render(arg);
436 
437  // display a scaled surd.
438  Pango pango(surf.cairo());
439  {
440  const CairoSave cs(surf.cairo());
441  const double oldFs=pango.getFontSize();
442  pango.setFontSize(arg.height());
443  pango.setMarkup("√");
444  cairo_rel_move_to(surf.cairo(),0,oldFs-arg.height());
445  pango.show();
446  }
447  cairo_rel_move_to(surf.cairo(),pango.width(),0);
448  // draw an overbar
449  double x,y;
450  cairo_get_current_point(surf.cairo(),&x,&y);
451  {
452  const CairoSave cs(surf.cairo());
453  cairo_rel_line_to(surf.cairo(),arg.width(),0);
454  cairo_set_line_width(surf.cairo(),1);
455  cairo_stroke(surf.cairo());
456  }
457 
458  cairo_move_to(surf.cairo(),x,y-arg.top()+2);
459  arguments[0][0]->render(surf);
460  }
461  }
462  template <>
463  void OperationDAG<OperationType::data>::render(Surface& surf) const
464  {
465  if (auto d=dynamic_cast<const minsky::DataOp*>(state.get()))
466  print(surf.cairo(),latexToPango(mathrm(d->description())),Anchor::nw);
467  else
468  print(surf.cairo(),latexToPango(mathrm("\\uplus")),Anchor::nw);
469  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
470  {parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});}
471  }
472  template <>
474  {
475  if (auto d=dynamic_cast<const minsky::DataOp*>(state.get()))
476  print(surf.cairo(),latexToPango(mathrm(d->description())),Anchor::nw);
477  else
478  print(surf.cairo(),latexToPango(mathrm("\\uplus")),Anchor::nw);
479  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
480  {parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});}
481  }
482  template <>
483  void OperationDAG<OperationType::exp>::render(Surface& surf) const
484  {
485  print(surf.cairo(),"exp",Anchor::nw);
486  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
487  {parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});}
488  }
489  template <>
490  void OperationDAG<OperationType::ln>::render(Surface& surf) const
491  {
492  print(surf.cairo(),"ln",Anchor::nw);
493  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
494  {parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});}
495  }
496  template <>
498  {
499  print(surf.cairo(),"sin",Anchor::nw);
500  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
501  {parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});}
502  }
503  template <>
505  {
506  print(surf.cairo(),"cos",Anchor::nw);
507  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
508  {parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});}
509  }
510  template <>
512  {
513  print(surf.cairo(),"tan",Anchor::nw);
514  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
515  {parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});}
516  }
517  template <>
519  {
520  print(surf.cairo(),"sin<sup>-1</sup>",Anchor::nw);
521  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
522  {parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});}
523  }
524  template <>
526  {
527  print(surf.cairo(),"cos<small><sup>-1</sup></small>",Anchor::nw);
528  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
529  {parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});}
530  }
531  template <>
533  {
534  print(surf.cairo(),"tan<small><sup>-1</sup></small>",Anchor::nw);
535  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
536  {parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});}
537  }
538  template <>
540  {
541  print(surf.cairo(),"sinh",Anchor::nw);
542  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
543  {parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});}
544  }
545  template <>
547  {
548  print(surf.cairo(),"cosh",Anchor::nw);
549  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
550  {parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});}
551  }
552  template <>
554  {
555  print(surf.cairo(),"tanh",Anchor::nw);
556  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
557  {parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});}
558  }
559 
560  template <>
562  {
563  double xx,yy;
564  cairo_get_current_point(s.cairo(),&xx,&yy);
565 
566  RecordingSurface r;
567  arguments[0][0]->render(r);
568 
569  Pango pango(s.cairo());
570  const double oldFs=pango.getFontSize();
571  pango.setFontSize(r.height());
572  pango.setMarkup("|");
573  cairo_rel_move_to(s.cairo(),0,-(r.height()-oldFs));
574  pango.show();
575  cairo_rel_move_to(s.cairo(),0,(r.height()-oldFs));
576  cairo_rel_move_to(s.cairo(),pango.width(),0);
577  arguments[0][0]->render(s);
578  xx+=pango.width()+r.width();
579  pango.setMarkup("|");
580  cairo_move_to(s.cairo(),xx,yy-r.height()+oldFs);
581  pango.show();
582  cairo_move_to(s.cairo(),xx+pango.width(),yy);
583  }
584 
585  template <>
587  {
588  double xx,yy;
589  cairo_get_current_point(s.cairo(),&xx,&yy);
590 
591  RecordingSurface r;
592  arguments[0][0]->render(r);
593 
594  Pango pango(s.cairo());
595  const double oldFs=pango.getFontSize();
596  pango.setFontSize(r.height());
597  pango.setMarkup("⌊");
598  cairo_rel_move_to(s.cairo(),0,-(r.height()-oldFs));
599  pango.show();
600  cairo_rel_move_to(s.cairo(),0,(r.height()-oldFs));
601  cairo_rel_move_to(s.cairo(),pango.width(),0);
602  arguments[0][0]->render(s);
603  xx+=pango.width()+r.width();
604  pango.setMarkup("⌋");
605  cairo_move_to(s.cairo(),xx,yy-r.height()+oldFs);
606  pango.show();
607  cairo_move_to(s.cairo(),xx+pango.width(),yy);
608  }
609 
610  template <>
612  {
613  print(surf.cairo(),"frac",Anchor::nw);
614  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
615  {parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});}
616  }
617 
618  template <>
620  {
621  print(surf.cairo(),"Γ",Anchor::nw);
622  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
623  {parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});}
624  }
625 
626  template <>
628  {
629  print(surf.cairo(),"ψ",Anchor::nw);
630  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
631  {
632  if (arguments.size()>1 && !arguments[1].empty() && arguments[1][0])
633  {
634  RecordingSurface base;
635  arguments[1][0]->render(base);
636  cairo_rel_move_to(surf.cairo(), 0, -0.5*base.height());
637  parenthesise(surf, [&](Surface& surf){arguments[1][0]->render(surf);});
638  arguments[0][0]->render(base);
639  cairo_rel_move_to(surf.cairo(), 0, 0.5*base.height());
640  parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});
641  }
642  else
643  parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});
644  }
645  }
646 
647  template <>
649  {
650  double xx,yy;
651  cairo_get_current_point(s.cairo(),&xx,&yy);
652 
653  RecordingSurface r;
654  arguments[0][0]->render(r);
655 
656  Pango pango(s.cairo());
657  const double oldFs=pango.getFontSize();
658  pango.setFontSize(r.height());
659  cairo_rel_move_to(s.cairo(),0,-(r.height()-oldFs));
660  pango.show();
661  cairo_rel_move_to(s.cairo(),0,(r.height()-oldFs));
662  cairo_rel_move_to(s.cairo(),pango.width(),0);
663  arguments[0][0]->render(s);
664  xx+=pango.width()+r.width();
665  pango.setMarkup("!");
666  cairo_move_to(s.cairo(),xx,yy-r.height()+oldFs);
667  pango.show();
668  cairo_move_to(s.cairo(),xx+pango.width(),yy);
669  }
670 
671  template <>
673  {
674  print(surf.cairo(),"∑<sub>i</sub>",Anchor::nw);
675  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
676  {
677  const double h=parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});
678  cairo_rel_move_to(surf.cairo(), 0, 0.5*h);
679  print(surf.cairo(),"<sub>i</sub>",Anchor::nw);
680  cairo_rel_move_to(surf.cairo(), 0, -0.5*h);
681  }
682  }
683 
684  template <>
686  {
687  print(surf.cairo(),"∏<sub>i</sub>",Anchor::nw);
688  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
689  {
690  parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});
691  print(surf.cairo(),"<sub>i</sub>",Anchor::nw);
692  }
693  }
694 
695  template <>
697  {
698  print(surf.cairo(),"min<sub>i</sub>",Anchor::nw);
699  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
700  {
701  parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});
702  print(surf.cairo(),"<sub>i</sub>",Anchor::nw);
703  }
704  }
705 
706  template <>
708  {
709  print(surf.cairo(),"min<sub>i</sub>",Anchor::nw);
710  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
711  {
712  parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});
713  print(surf.cairo(),"<sub>i</sub>",Anchor::nw);
714  }
715  }
716 
717  template <>
719  {
720  print(surf.cairo(),"indexOf",Anchor::nw);
721  parenthesise(surf, [&](Surface& surf){OperationDAG<OperationType::infimum>().render(surf);});
722  }
723 
724  template <>
726  {
727  print(surf.cairo(),"indexOf",Anchor::nw);
728  parenthesise(surf, [&](Surface& surf){OperationDAG<OperationType::supremum>().render(surf);});
729  }
730 
731  template <>
733  {
734  print(surf.cairo(),"Θ(max<sub>i</sub>",Anchor::nw);
735  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
736  {
737  parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});
738  print(surf.cairo(),"<sub>i</sub>&gt;0.5)",Anchor::nw);
739  }
740  }
741 
742  template <>
744  {
745  print(surf.cairo(),"∏<sub>i</sub>Θ(",Anchor::nw);
746  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
747  {
748  parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});
749  print(surf.cairo(),"<sub>i</sub>&gt;0.5)",Anchor::nw);
750  }
751  }
752 
753  template <>
755  {
756  print(surf.cairo(),"∑<sub>j=0</sub><sup>i</sup>",Anchor::nw);
757  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
758  {
759  parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});
760  print(surf.cairo(),"<sub>i</sub>",Anchor::nw);
761  }
762  }
763 
764  template <>
766  {
767  print(surf.cairo(),"∏<sub>j=0</sub><sup>i</sup>",Anchor::nw);
768  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
769  {
770  parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});
771  print(surf.cairo(),"<sub>i</sub>",Anchor::nw);
772  }
773  }
774 
775  template <>
777  {
778  print(surf.cairo(),"Δ⁻ᵢ",Anchor::nw);
779  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
780  {
781  parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});
782  print(surf.cairo(),"<sub>i</sub>",Anchor::nw);
783  }
784  }
785 
786  template <>
788  {
789  print(surf.cairo(),"Δ⁺ᵢ",Anchor::nw);
790  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
791  {
792  parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});
793  print(surf.cairo(),"<sub>i</sub>",Anchor::nw);
794  }
795  }
796 
797  template <>
799  {
800  print(surf.cairo(),"index",Anchor::nw);
801  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
802  {
803  parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});
804  }
805  }
806 
807  template <>
808  void OperationDAG<OperationType::lt>::render(Surface& surf) const
809  {
810  if (!arguments.empty())
811  {
812  if (!arguments[1].empty() && arguments[1][0])
813  if (!arguments[0].empty() && arguments[0][0])
814  {
815  print(surf.cairo(),"θ",Anchor::nw);
816  parenthesise(surf, [&](Surface& surf){
817  arguments[1][0]->render(surf);
818  print(surf.cairo()," - ",Anchor::nw);
819  if (arguments[0][0]->BODMASlevel()>1)
820  parenthesise(surf, [&](Surface& surf){
821  arguments[0][0]->render(surf);
822  });
823  else
824  arguments[0][0]->render(surf);
825  });
826  }
827  else
828  {
829  print(surf.cairo(),"θ",Anchor::nw);
830  parenthesise(surf, [&](Surface& surf){arguments[1][0]->render(surf);});
831  }
832  else
833  if (!arguments[0].empty() && arguments[0][0])
834  {
835  print(surf.cairo(),"θ",Anchor::nw);
836  parenthesise(surf, [&](Surface& surf){
837  print(surf.cairo()," - ",Anchor::nw);
838  if (arguments[0][0]->BODMASlevel()>1)
839  parenthesise(surf, [&](Surface& surf){
840  arguments[0][0]->render(surf);
841  });
842  else
843  arguments[0][0]->render(surf);
844  });
845  }
846  else
847  print(surf.cairo(),"0",Anchor::nw);
848  }
849  else
850  print(surf.cairo(),"0",Anchor::nw);
851  }
852 
853  template <>
854  void OperationDAG<OperationType::eq>::render(Surface& surf) const
855  {
856  if (!arguments.empty())
857  {
858  if (!arguments[1].empty() && arguments[1][0])
859  if (!arguments[0].empty() && arguments[0][0])
860  {
861  print(surf.cairo(),"δ",Anchor::nw);
862  parenthesise(surf, [&](Surface& surf){
863  arguments[0][0]->render(surf);
864  print(surf.cairo()," - ",Anchor::nw);
865  arguments[1][0]->render(surf);
866  });
867  }
868  else
869  {
870  print(surf.cairo(),"δ",Anchor::nw);
871  parenthesise(surf, [&](Surface& surf){arguments[1][0]->render(surf);});
872  }
873  else
874  if (!arguments[0].empty() && arguments[0][0])
875  {
876  print(surf.cairo(),"δ",Anchor::nw);
877  parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});
878  }
879  else
880  print(surf.cairo(),"1",Anchor::nw);
881  }
882  else
883  print(surf.cairo(),"1",Anchor::nw);
884  }
885 
886  template <>
887  void OperationDAG<OperationType::le>::render(Surface& surf) const
888  {
889  if ((!arguments.empty() && !arguments[0].empty() && arguments[0][0]) ||
890  (arguments.size()>1 && !arguments[1].empty() && arguments[1][0]))
891  {
894  lt.render(surf);
895  print(surf.cairo(),"+",Anchor::nw);
896  eq.render(surf);
897  }
898  else
899  print(surf.cairo(),"1",Anchor::nw);
900  }
901 
902 
903  template <>
905  {
906  if (!arguments.empty())
907  {
908  print(surf.cairo(),"min",Anchor::nw);
909  parenthesise(surf, [&](Surface& surf){
910  naryRender(surf,arguments[0],BODMASlevel(),",","∞");
911  if (!arguments[1].empty())
912  {
913  print(surf.cairo(),",",Anchor::nw);
914  naryRender(surf,arguments[1],BODMASlevel(),",","∞");
915  }
916  });
917  }
918  else
919  print(surf.cairo(),"∞",Anchor::nw);
920  }
921 
922  template <>
924  {
925  if (!arguments.empty())
926  {
927  print(surf.cairo(),"max",Anchor::nw);
928  parenthesise(surf, [&](Surface& surf){
929  naryRender(surf,arguments[0],BODMASlevel(),",","-∞");
930  if (!arguments[1].empty())
931  {
932  print(surf.cairo(),",",Anchor::nw);
933  naryRender(surf,arguments[1],BODMASlevel(),",","-∞");
934  }
935  });
936  }
937  else
938  print(surf.cairo(),"-∞",Anchor::nw);
939  }
940 
941  template <>
943  {
944  if (!arguments.empty())
945  {
946  naryRender(surf,arguments[0],BODMASlevel(),"∧","1");
947  if (!arguments[1].empty())
948  {
949  print(surf.cairo(),"∧",Anchor::nw);
950  naryRender(surf,arguments[0],BODMASlevel(),"∧","1");
951  }
952  }
953  else
954  print(surf.cairo(),"1",Anchor::nw);
955  }
956 
957 
958  template <>
960  {
961  if (!arguments.empty())
962  {
963  naryRender(surf,arguments[0],BODMASlevel(),"∨","0");
964  if (!arguments[1].empty())
965  {
966  print(surf.cairo(),"∨",Anchor::nw);
967  naryRender(surf,arguments[0],BODMASlevel(),"∨","0");
968  }
969  }
970  else
971  print(surf.cairo(),"0",Anchor::nw);
972  }
973 
974  template <>
976  {
977  if (!arguments.empty())
978  {
979  if (!arguments[0].empty() && arguments[0][0])
980  {
981  print(surf.cairo(),"¬",Anchor::nw);
982  parenthesise(surf, [&](Surface& surf){
983  arguments[0][0]->render(surf);
984  });
985  }
986  else
987  print(surf.cairo(),"0",Anchor::nw);
988  }
989  }
990 
991  template <>
993  {
994  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0] &&
995  arguments.size()>1 && !arguments[1].empty() && arguments[1][0])
996  {
997  print(surf.cairo(),"cov",Anchor::nw);
998  parenthesise(surf, [&](Surface& surf){
999  arguments[0][0]->render(surf);
1000  print(surf.cairo(),",",Anchor::nw);
1001  arguments[0][1]->render(surf);
1002  });
1003  }
1004  else
1005  print(surf.cairo(),"0",Anchor::nw);
1006  }
1007 
1008  template <>
1010  {
1011  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0] &&
1012  arguments.size()>1 && !arguments[1].empty() && arguments[1][0])
1013  {
1014  print(surf.cairo(),"ρ",Anchor::nw);
1015  parenthesise(surf, [&](Surface& surf){
1016  arguments[0][0]->render(surf);
1017  print(surf.cairo(),",",Anchor::nw);
1018  arguments[0][1]->render(surf);
1019  });
1020  }
1021  else
1022  print(surf.cairo(),"0",Anchor::nw);
1023  }
1024 
1025  template <>
1027  {
1028  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0] &&
1029  arguments.size()>1 && !arguments[1].empty() && arguments[1][0])
1030  {
1031  print(surf.cairo(),"linReg",Anchor::nw);
1032  parenthesise(surf, [&](Surface& surf){
1033  arguments[0][0]->render(surf);
1034  print(surf.cairo(),",",Anchor::nw);
1035  arguments[0][1]->render(surf);
1036  });
1037  }
1038  else
1039  print(surf.cairo(),"0",Anchor::nw);
1040  }
1041 
1042  template <>
1044  {
1045  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
1046  {
1047  print(surf.cairo(),"dim",Anchor::nw);
1048  parenthesise(surf, [&](Surface& surf){
1049  arguments[0][0]->render(surf);
1050  print(surf.cairo(),",i",Anchor::nw); // TODO - can we extract the actual argument here?
1051  });
1052  }
1053  else
1054  print(surf.cairo(),"0",Anchor::nw);
1055  }
1056 
1057  template <>
1059  {
1060  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
1061  {
1062  print(surf.cairo(),"shape",Anchor::nw);
1063  parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});
1064  }
1065  else
1066  print(surf.cairo(),"0",Anchor::nw);
1067  }
1068 
1069  template <>
1071  {
1072  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
1073  {
1074  parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);}, "&lt;","&gt;");
1075  }
1076  else
1077  print(surf.cairo(),"0",Anchor::nw);
1078  }
1079 
1080  template <>
1082  {
1083  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
1084  {
1085  print(surf.cairo(),"median",Anchor::nw);
1086  parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});
1087  }
1088  else
1089  print(surf.cairo(),"0",Anchor::nw);
1090  }
1091 
1092  template <>
1094  {
1095  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
1096  {
1097  print(surf.cairo(),"σ",Anchor::nw);
1098  parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});
1099  }
1100  else
1101  print(surf.cairo(),"0",Anchor::nw);
1102  }
1103 
1104  template <>
1106  {
1107  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
1108  {
1109  parenthesise(surf, [&](Surface& surf){
1110  print(surf.cairo(),"Δ",Anchor::nw);
1111  parenthesise(surf,[&](Surface& surf){arguments[0][0]->render(surf);});
1112 
1113  RecordingSurface base;
1114  arguments[0][0]->render(base);
1115  cairo_rel_move_to(surf.cairo(), 0, -0.5*base.height());
1116  print(surf.cairo(),"k",Anchor::nw); // TODO can we extract the actual exponent here?
1117  cairo_rel_move_to(surf.cairo(), 0, 0.5*base.height());
1118  },"&lt;","&gt;");
1119  }
1120  else
1121  print(surf.cairo(),"0",Anchor::nw);
1122  }
1123 
1124  template <>
1126  {
1127  if (!arguments.empty() && !arguments[0].empty() && arguments[0][0])
1128  {
1129  print(surf.cairo(),"histogram",Anchor::nw);
1130  parenthesise(surf, [&](Surface& surf){arguments[0][0]->render(surf);});
1131  }
1132  else
1133  print(surf.cairo(),"0",Anchor::nw);
1134  }
1135 
1136 
1137 
1138  template <>
1140  {
1141  if (arguments.size()<2 || arguments[0].empty() || arguments[1].empty())
1142  throw error("incorrectly wired");
1143  parenthesise(surf, [&](Surface& surf){
1144  arguments[0][0]->render(surf);
1145  });
1146  print(surf.cairo(),"·",Anchor::nw);
1147  parenthesise(surf, [&](Surface& surf){
1148  arguments[1][0]->render(surf);
1149  });
1150  }
1151 
1152  template <>
1154  {
1155  if (arguments.size()<2 || arguments[0].empty() || arguments[1].empty())
1156  throw error("incorrectly wired");
1157  parenthesise(surf, [&](Surface& surf){
1158  arguments[0][0]->render(surf);
1159  });
1160  print(surf.cairo(),"⊗",Anchor::nw);
1161  parenthesise(surf, [&](Surface& surf){
1162  arguments[1][0]->render(surf);
1163  });
1164  }
1165 
1166  template <>
1168  {
1169  if (arguments.size()<2 || arguments[0].empty() || arguments[1].empty())
1170  throw error("incorrectly wired");
1171  parenthesise(surf, [&](Surface& surf){
1172  arguments[0][0]->render(surf);});
1173  parenthesise(surf, [&](Surface& surf){
1174  arguments[1][0]->render(surf);},"[","]");
1175  }
1176 
1177  template <>
1179  {
1180  if (arguments.size()<2 || arguments[0].empty() || arguments[1].empty())
1181  throw error("incorrectly wired");
1182  print(surf.cairo(),"meld",Anchor::nw);
1183  parenthesise(surf, [&](Surface& surf){
1184  for (size_t i=0; i<arguments.size(); ++i)
1185  {
1186  naryRender(surf, arguments[i], BODMASlevel(),"","");
1187  if (i!=arguments.size()-1) print(surf.cairo(),",",Anchor::nw);
1188  }
1189  });
1190  }
1191 
1192  template <>
1194  {
1195  if (arguments.size()<2 || arguments[0].empty() || arguments[1].empty())
1196  throw error("incorrectly wired");
1197  print(surf.cairo(),"merge",Anchor::nw);
1198  parenthesise(surf, [&](Surface& surf){
1199  for (size_t i=0; i<arguments.size(); ++i)
1200  {
1201  naryRender(surf, arguments[i], BODMASlevel(),"","");
1202  if (i!=arguments.size()-1) print(surf.cairo(),",",Anchor::nw);
1203  }
1204  });
1205  }
1206 
1207  template <>
1209  {
1210  if (arguments.empty() || arguments[0].empty())
1211  throw error("incorrectly wired");
1212  print(surf.cairo(),"slice",Anchor::nw);
1213  parenthesise(surf, [&](Surface& surf){
1214  arguments[0][0]->render(surf);
1215  print(surf.cairo(),",",Anchor::nw);
1216  string slice="0";
1217  if (state)
1218  if (auto o=state->operationCast())
1219  slice=to_string(o->arg);
1220  print(surf.cairo(),slice,Anchor::nw);
1221  });
1222  }
1223 
1224 
1225 
1226 }
std::string description(const std::string &) override
name of the associated data operation
void variableRender(Surface &surf, const VariableDAG &v)
std::string latexToPango(const char *s)
Definition: latexMarkup.h:30
double parenthesise(Surface &s, X x, const string &left="(", const string &right=")")
Definition: input.py:1
void naryRender(Surface &dest, const vector< WeakNodePtr > &arglist, int BODMASlevel, const char *op, const char *empty)
string latexInit(const string &)
convert an initialisation string into a matlab expression
Definition: node_latex.cc:73
virtual void render(ecolab::cairo::Surface &surf) const =0
renders a visual representation of this node to surf graphic extends right from the current pen posit...
void moveToAnchor(cairo_t *cairo, const T &object, Anchor anchor)
double print(cairo_t *cairo, const string &text, Anchor anchor)
vector< vector< WeakNodePtr > > arguments
Definition: equations.h:219
string to_string(CONST84 char *x)
Definition: minskyTCLObj.h:33
shared_ptr< VariableDAG > VariableDAGPtr
Definition: equations.h:208
void render(ecolab::cairo::Surface &surf) const override
renders a visual representation of this node to surf graphic extends right from the current pen posit...
string mathrm(const string &nm)
wraps in if nm has more than one letter - and also takes into account LaTeX subscript/superscripts ...
Definition: node_latex.cc:41
WeakNodePtr rhs
Definition: equations.h:178