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