Minsky
intOp.cc
Go to the documentation of this file.
1 /*
2  @copyright Steve Keen 2021
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 "cairoItems.h"
22 #include "intOp.h"
23 #include "intOp.rcd"
24 #include "itemT.rcd"
25 #include "minsky_epilogue.h"
26 
27 namespace minsky
28 {
29  IntOpAccessor::IntOpAccessor(): ecolab::TCLAccessor<IntOp, std::string>
30  ("description",(Getter)&IntOp::description,(Setter)&IntOp::description) {}
31 
32  Units IntOp::units(bool check) const {
33  Units r=m_ports[1]->units(check);
34  if (!cminsky().timeUnit.empty())
35  r[cminsky().timeUnit]++;
36  r.normalise();
37  return r;
38  }
39 
40  void IntOp::draw(cairo_t* cairo) const
41  {
42  // if rotation is in 1st or 3rd quadrant, rotate as
43  // normal, otherwise flip the text so it reads L->R
44  auto [angle,textFlipped]=rotationAsRadians();
45  double coupledIntTranslation=0;
46  const float z=zoomFactor();
47 
50 
51  if (fabs(l)<iWidth()*z) l=-iWidth()*z;
52  if (r<iWidth()*z) r=iWidth()*z;
53  if (h<iHeight()*z) h=iHeight()*z;
54 
55  if (coupled() && intVar)
56  {
57  const cairo::CairoSave cs(cairo);
58  auto& iv=*intVar;
59  const RenderVariable rv(iv,cairo);
60  // we need to add some translation if the variable is bound
61  cairo_rotate(cairo,angle);
62  coupledIntTranslation=-0.5*(intVarOffset+2*rv.width()+2+r)*z;
63  if (rv.width()<iv.iWidth()) coupledIntTranslation=-0.5*(intVarOffset+2*iv.iWidth()+2+r)*z;
64  }
65 
66 
67  {
68  const cairo::CairoSave cs(cairo);
69  cairo_rotate(cairo, angle);
70  cairo_scale(cairo,z,z);
71  if (textFlipped) cairo_rotate(cairo, M_PI);
72  const double sf = scaleFactor();
73  cairo_scale(cairo,sf,sf);
74  cairo_move_to(cairo,-7,3.5);
75  cairo_show_text(cairo,"∫dt");
76  }
77  DrawBinOp d(cairo, zoomFactor());
78  d.drawPort([&](){d.drawSymbol("0");}, l,h,rotation());
79  d.drawPort([&](){d.drawSymbol("f");}, l,-h,rotation());
80 
81  cairo_save(cairo);
82  cairo_rotate(cairo, angle);
83 
84  int intVarWidth=0;
85 
86 
87  cairo_move_to(cairo,l,h);
88  cairo_line_to(cairo,l,-h);
89  cairo_line_to(cairo,r,0);
90 
91  cairo_close_path(cairo);
92 
93  cairo_set_source_rgb(cairo,0,0,1);
94  cairo_stroke_preserve(cairo);
95 
96  if (coupled() && intVar)
97  {
98  const float ivo=intVarOffset*z;
99  cairo_new_path(cairo);
100  cairo_move_to(cairo,r,0);
101  cairo_line_to(cairo,r+ivo,0);
102  cairo_set_source_rgb(cairo,0,0,0);
103  cairo_stroke(cairo);
104 
105  // display an integration variable next to it
106  RenderVariable rv(*intVar, cairo);
107  // save the render width for later use in setting the clip
108  intVarWidth=rv.width()*z;
109  if (rv.width()<intVar->iWidth()) intVarWidth=0.5*intVar->iWidth()*z;
110  // set the port location...
111  const Rotate rot(rotation(), x(), y());
112  auto ivp=rot(x()+r+ivo+intVarWidth, y());
113  intVar->moveTo(ivp.x(), ivp.y());
114 
115  cairo_save(cairo);
116  cairo_translate(cairo,r+ivo+intVarWidth,0);
117  // to get text to render correctly, we need to set
118  // the var's rotation, then antirotate it
119  intVar->rotation(rotation());
120  cairo_rotate(cairo, -M_PI*rotation()/180.0);
121  rv.draw();
122  cairo_restore(cairo);
123 
124  // build clip path the hard way grr...
125  cairo_move_to(cairo,l,h);
126  cairo_line_to(cairo,l,-h);
127  cairo_line_to(cairo,r,0);
128  cairo_line_to(cairo,r+ivo,0);
129  float rvw=rv.width()*z, rvh=rv.height()*z;
130  if (rv.width()<intVar->iWidth()) rvw=intVar->iWidth()*z;
131  if (rv.height()<intVar->iHeight()) rvh=intVar->iHeight()*z;
132  cairo_line_to(cairo,r+ivo,-rvh);
133  cairo_line_to(cairo,r+ivo+2*rvw,-rvh);
134  cairo_line_to(cairo,r+ivo+2*rvw+2*z,0);
135  cairo_line_to(cairo,r+ivo+2*rvw,rvh);
136  cairo_line_to(cairo,r+ivo,rvh);
137  cairo_line_to(cairo,r+ivo,0);
138  cairo_line_to(cairo,r,0);
139  cairo_close_path(cairo);
140  }
141 
142  cairo::Path clipPath(cairo);
143 
144  double x0=r, y0=0, x1=l, y1=numPorts() > 2? -h+3: 0,
145  x2=l, y2=numPorts() > 2? h-3: 0;
146 
147  if (textFlipped) swap(y1,y2);
148 
149  // adjust for integration variable
150  if (coupled())
151  x0+=intVarOffset+2*intVarWidth+2;
152 
153  cairo_save(cairo);
154  cairo_identity_matrix(cairo);
155  cairo_translate(cairo, x(), y());
156  cairo_rotate(cairo, angle);
157  cairo_user_to_device(cairo, &x0, &y0);
158  cairo_user_to_device(cairo, &x1, &y1);
159  cairo_user_to_device(cairo, &x2, &y2);
160  cairo_restore(cairo);
161 
162  if (numPorts()>0)
163  m_ports[0]->moveTo(x0, y0);
164  if (numPorts()>1)
165  m_ports[1]->moveTo(x1, y1);
166  if (numPorts()>2)
167  m_ports[2]->moveTo(x2, y2);
168 
169  cairo_translate(cairo,-coupledIntTranslation,0);
170  cairo_restore(cairo); // undo rotation
171  if (mouseFocus)
172  {
173  drawPorts(cairo);
174  displayTooltip(cairo,tooltip());
175  }
176  if (onResizeHandles) drawResizeHandles(cairo);
177 
178  cairo_new_path(cairo);
179  clipPath.appendToCurrent(cairo);
180  cairo_clip(cairo);
181  if (selected) drawSelected(cairo);
182  }
183 
184  void IntOp::resize(const LassoBox& b)
185  {
186  const float invZ=1.0/zoomFactor();
187  this->moveTo(0.5*(b.x0+b.x1), 0.5*(b.y0+b.y1));
188  iWidth(0.5*std::abs(b.x1-b.x0)*invZ);
189  // Ensure int op height and var height similar to make gripping resize handle easier. for ticket 1203.
190  iHeight(0.25*std::abs(b.y1-b.y0)*invZ);
191  intVar->iWidth(0.5*std::abs(b.x1-b.x0)*invZ);
192  intVar->iHeight(0.5*std::abs(b.y1-b.y0)*invZ);
193  bb.update(*this);
194  }
195 
197  {
198  selection.ensureItemInserted(intVar);
199  }
200 
202  {
204  intVar.reset(x.intVar->clone());
205  return *this;
206  }
207 
209  {
210  if (intVar)
211  g.removeItem(*intVar);
212  }
213 
214  string IntOp::description(const string& a_desc)
215  {
216  auto desc=a_desc;
217 
218  // set a default name if none given
219  if (desc.empty())
220  desc=minsky().variableValues.newName(minsky::valueId(group.lock(),"int"));
221 
222  // disallow global integration variables
223  if (desc[0]==':') desc=desc.substr(1);
224 
225  if (intVar && intVar->group.lock() == group.lock() && intVar->name()==desc)
226  return description(); // nothing to do
227 
228  vector<Wire> savedWires;
229  if (intVar && intVar->portsSize()>0)
230  {
231  // save any attached wires for later use
232  for (auto w: intVar->ports(0).lock()->wires())
233  savedWires.push_back(*w);
234  }
235 
236  // if the variable name exists, and already has a connected
237  // input, then it is not a candidate for being an integral
238  // variable, so generate a new name that doesn't currently
239  // exist
240 
241  const string vid=minsky::valueId(group.lock(),desc);
242  auto i=minsky().variableValues.find(vid);
243  if (i!=minsky().variableValues.end())
244  {
245  if (i->second->type()!=VariableType::integral)
246  try
247  {
249  }
250  catch (...)
251  {
252  desc=minsky().variableValues.newName(vid);
253  }
254  else
255  if (minsky().definingVar(vid)) // Also check that integral has input. for ticket 1068.
256  desc=minsky().variableValues.newName(vid);
257  else
258  desc=vid;
259  if (desc[0]==':') desc=desc.substr(1);// disallow global integration variables
260  }
261 
262 
263  ItemPtr oldIvar;
264  if (intVar)
265  oldIvar=minsky().model->removeItem(*intVar);
266 
267  intVar.reset(new Variable<VariableType::integral>(desc));
268  if (auto g=group.lock())
269  intVar->controller=g->findItem(*this); // we're managing our own display
270  // initialise in toggled state
271  m_coupled=true;
272 
273  // recreate any previously attached wires, initially in global group.
274  for (auto& w: savedWires)
275  minsky().model->addWire(new Wire(intVar->ports(0), w.to(), w.coords()));
276 
277  bb.update(*this); // adjust icon bounding box - see ticket #704
278 
279  // this should also adjust the wire's group ownership appropriately
280  if (auto g=group.lock())
281  g->addItem(intVar);
282  return description();
283  }
284 
286  {
287  if (type()!=integrate) return false;
288 
289  assert(intVar);
290 
291  assert(m_ports.size()==3);
292  if (m_coupled)
293  {
294  const WirePtr newWire(new Wire(m_ports[0], intVar->ports(1)));
295  if (auto g=group.lock())
296  g->addWire(newWire);
297  else
298  minsky().model->addWire(newWire);
299  intVar->controller.reset();
300  intVar->rotation(rotation());
301  }
302  else
303  {
304  if (auto g=group.lock())
305  {
306  for (auto w: intVar->ports(1).lock()->wires())
307  g->removeWire(*w);
308  intVar->controller=g->findItem(*this);
309  }
310  intVar->mouseFocus=false; // prevent drawing of variable ports when coupled
311  }
313  bb.update(*this); // adjust bounding box for coupled integral operation - see ticket #1055
314  return coupled();
315  }
316 
317  void IntOp::pack(pack_t& x, const string& d) const
318  {::pack(x,d,*this);}
319 
320  void IntOp::unpack(unpack_t& x, const string& d)
321  {::unpack(x,d,*this);}
322 
323 }
324 
std::string newName(const std::string &name) const
generate a new valueId not otherwise in the system
#define M_PI
some useful geometry types, defined from boost::geometry
Definition: geometry.h:29
represents items that have been selected
Definition: selection.h:32
IntOp & operator=(const IntOp &x)
Definition: intOp.cc:201
void drawPorts(cairo_t *cairo) const
Definition: item.cc:294
virtual void displayTooltip(cairo_t *, const std::string &) const
display tooltip text, eg on mouseover
Definition: item.cc:398
void drawPort(F f, float x, float y, float rotation) const
Definition: operation.h:172
virtual float x() const
Definition: item.cc:107
VariableValues variableValues
Definition: minsky.h:200
virtual float y() const
Definition: item.cc:114
float iHeight() const
Definition: item.h:224
std::string description() const
name of the associated integral variable
Definition: intOp.h:57
bool onResizeHandles
set to true to indicate mouse is ovcaler resize handles
Definition: item.h:175
represents rectangular region of a lasso operation
Definition: lasso.h:28
STL namespace.
std::shared_ptr< Item > ItemPtr
Definition: item.h:57
std::shared_ptr< Wire > WirePtr
Definition: wire.h:102
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
rotate (x,y) by rot (in degrees) around the origin (x0, y0) can be used for rotating multiple points ...
Definition: geometry.h:44
void insertControlled(Selection &selection) override
Definition: intOp.cc:196
BoundingBox bb
canvas bounding box.
Definition: item.h:195
virtual std::string const & tooltip() const
Definition: noteBase.h:36
void resize(const LassoBox &b) override
resize this item on the canvas
Definition: intOp.cc:184
virtual float zoomFactor() const
Definition: item.cc:121
bool coupled() const
Definition: intOp.h:83
void update(const Item &x)
Definition: item.cc:46
static constexpr float l
Definition: operationBase.h:58
void pack(classdesc::pack_t &x, const std::string &d) const override
Definition: intOp.cc:317
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
std::string timeUnit
Definition: simulation.h:35
float iWidth() const
Definition: item.h:217
void drawSymbol(const char *s) const
Definition: operation.h:162
float scaleFactor() const override
factor by which item has been resized
Definition: operation.cc:135
static constexpr float h
Definition: operationBase.h:58
represents the units (in sense of dimensional analysis) of a variable.
Definition: units.h:34
const Minsky & cminsky()
const version to help in const correctness
Definition: minsky.h:549
CLASSDESC_ACCESS_EXPLICIT_INSTANTIATION(minsky::IntOp)
VariablePtr intVar
return reference to integration variable
Definition: intOp.h:73
ItemPortVector m_ports
Definition: item.h:156
void removeControlledItems(minsky::GroupItems &) override
Definition: intOp.cc:208
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
void draw(cairo_t *) const override
draw this item into a cairo context
Definition: intOp.cc:40
bool toggleCoupled()
toggles coupled state of integration variable. Only valid for integrate
Definition: intOp.cc:285
float x0
Definition: lasso.h:30
bool mouseFocus
true if target of a mouseover
Definition: noteBase.h:31
float y1
Definition: lasso.h:30
float x1
Definition: lasso.h:30
void convertVarType(const std::string &name, VariableType::Type type)
Converts variable(s) named by name into a variable of type type.
Definition: minsky.cc:1457
bool m_coupled
Definition: intOp.h:39
void moveTo(float x, float y)
Definition: item.cc:256
void unpack(classdesc::unpack_t &x, const std::string &d) override
Definition: intOp.cc:320
static constexpr float r
Definition: operationBase.h:58
UnitsExpressionWalker timeUnit
GroupPtr model
Definition: minsky.h:255
float y0
Definition: lasso.h:30
static constexpr float intVarOffset
Definition: intOp.h:44
Definition: group.tcl:84
Minsky & minsky()
global minsky object
Definition: minskyTCL.cc:51
helper class to draw port label symbols
Definition: operation.h:120
void drawResizeHandles(cairo_t *cairo) const override
Definition: item.cc:355
void draw()
render the cairo image
Definition: cairoItems.cc:83
Units units(bool) const override
compute the dimensional units
Definition: intOp.cc:32
double rotation() const
Definition: item.h:211
float height() const
half height of unrotated image
Definition: cairoItems.h:47
ItemPtr removeItem(const Item &)
Definition: group.cc:212
float width() const
half width of unrotated image
Definition: cairoItems.h:45