Minsky
wire.cc
Go to the documentation of this file.
1 /*
2  @copyright Steve Keen 2015
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 "cairoItems.h"
21 #include "wire.h"
22 #include "geometry.h"
23 #include "port.h"
24 #include "group.h"
25 #include "selection.h"
26 #include "lasso.h"
27 #include "operation.h"
28 #include "pango.h"
29 #include "plotWidget.h"
30 #include "SVGItem.h"
31 #include "wire.rcd"
32 #include "minsky_epilogue.h"
33 #include <random>
34 #include <iterator>
35 
36 using namespace std;
37 using ecolab::cairo::CairoSave;
38 
39 namespace minsky
40 {
41  WireAccessor::WireAccessor(): ecolab::TCLAccessor<Wire, std::vector<float>>
42  ("coords", (Getter)&Wire::coords, (Setter)&Wire::coords) {}
43 
44 
45  vector<float> Wire::coords() const
46  {
47  vector<float> c;
48  assert(from() && to());
49  assert(m_coords.size() % 2 == 0);
50  if (auto f=from())
51  if (auto t=to())
52  {
53  c.push_back(f->x());
54  c.push_back(f->y());
55  const float d=sqrt
56  (sqr(f->x()-t->x())+sqr(f->y()-t->y()));
57 
58  for (size_t i=0; m_coords.size()>1 && i<m_coords.size()-1; i+=2)
59  {
60  c.push_back(f->x() + d*m_coords[i]);
61  c.push_back(f->y() + d*m_coords[i+1]);
62  }
63  c.push_back(t->x());
64  c.push_back(t->y());
65  }
66  return c;
67  }
68 
69  vector<float> Wire::coords(const vector<float>& coords)
70  {
71  if (coords.size()<6)
72  m_coords.clear();
73  else
74  {
75  assert(coords.size() % 2 == 0);
76 
77  const float d=1/sqrt
78  (sqr(coords[coords.size()-2]-coords[0])+sqr(coords[coords.size()-1]-coords[1]));
79  m_coords.resize(coords.size()-4);
80  for (size_t i=2; i<coords.size()-3; i+=2)
81  {
82  m_coords[i-2] = (coords[i]-coords[0])*d;
83  m_coords[i-1] = (coords[i+1]-coords[1])*d;
84  }
85  }
86  return this->coords();
87  }
88 
89 
90  Wire::Wire(const weak_ptr<Port>& from, const weak_ptr<Port>& to,
91  const vector<float>& a_coords):
92  m_from(from), m_to(to)
93  {
94  if (!from.lock() || !to.lock()) throw error("wiring defunct ports");
95  if (from.lock()->input() || !to.lock()->input()) throw error("invalid ports for wire");
96  coords(a_coords);
97  m_from.lock()->m_wires.push_back(this);
98  m_to.lock()->m_wires.push_back(this);
99  }
100 
102  {
103  if (auto toPort=to())
104  toPort->eraseWire(this);
105  if (auto fromPort=from())
106  fromPort->eraseWire(this);
107  }
108 
109  bool Wire::visible() const
110  {
111  auto f=from(), t=to();
112  auto fgroup=f->item().group.lock(), tgroup=t->item().group.lock();
113  return f && t &&
114  (!fgroup || fgroup->displayContents() ||
115  !tgroup || tgroup->displayContents());
116  }
117 
118  void Wire::moveToPorts(const shared_ptr<Port>& from, const shared_ptr<Port>& to)
119  {
120  if (auto f=this->from())
121  f->m_wires.erase(remove(f->m_wires.begin(), f->m_wires.end(), this), f->m_wires.end());
122  if (auto t=this->to())
123  t->m_wires.erase(remove(t->m_wires.begin(), t->m_wires.end(), this), t->m_wires.end());
124  m_from=from;
125  m_to=to;
126  from->m_wires.push_back(this);
127  to->m_wires.push_back(this);
128  }
129 
130 
132  {
133  WirePtr wp;
134  // one hit find and remove wire from its map, saving the wire
135  dest.globalGroup().recursiveDo
136  (&Group::wires,
137  [&](Wires& wires, Wires::iterator i) {
138  if (i->get()==this)
139  {
140  wp=*i;
141  wires.erase(i);
142  return true;
143  }
144  return false;
145  });
146  if (wp)
147  dest.addWire(wp);
148  }
149 
150 namespace
151 {
152 
153  // For ticket 1095. Returns coordinate pairs for both moving and "to be inserted" handles on a curved wire
154  vector<pair<float,float>> allHandleCoords(vector<float> coords) {
155 
156  vector<pair<float,float>> points(coords.size()-1);
157 
158  for (size_t i = 0; i < points.size(); i++) {
159  if (i%2 == 0) {
160  points[i].first = coords[i];
161  points[i].second = coords[i+1];
162  }
163  else {
164  points[i].first = 0.5*(coords[i-1]+coords[i+1]);
165  points[i].second = 0.5*(coords[i]+coords[i+2]);
166  }
167  }
168 
169  return points;
170  }
171 
172  // For ticket 991/1092. Returns coordinate pairs for moving handles on a curved wire
173  vector<pair<float,float>> toCoordPair(vector<float> coords) {
174  vector<pair<float,float>> points(coords.size()/2);
175 
176  for (size_t i = 0; i < coords.size(); i++)
177  if (i%2 == 0) {
178  points[i/2].first = coords[i];
179  points[i/2].second = coords[i+1];
180  }
181  return points;
182  }
183 
184 // For ticket 991. Construct tridoagonal matrix A which relates control points c and knots k (curved wire handles): Ac = k.
185  vector<vector<float>> constructTriDiag(int length)
186  {
187  vector<vector<float>> result(length,vector<float>(length));
188 
189  for (int i = 0; i < length; i++) // rows
190  for (int j = 0; j < length; j++) { // columns
191 
192  //construct upper diagonal
193  if (j > 0 && i == j-1 && i < length-1) result[i][j] = 1.0;
194  //construct main diagonal
195  else if (i == j) {
196  if (i == 0) result[i][j] = 2.0;
197  else if (i < length-1) result[i][j] = 4.0;
198  else if (i == length-1) result[i][j] = 7.0;
199  }
200  //construct lower diagonal
201  else if (i == j+1) {
202  if (j < length-2) result[i][j] = 1.0;
203  else if (j == length-2) result[i][j] = 2.0;
204  }
205  else result[i][j] = 0.0;
206  }
207 
208  return result;
209  }
210 
211 // For ticket 991. Vector of input knots k (all the handles on a curved wire).
212  vector<pair<float,float>> constructTargetVector(int n, const vector<pair<float,float>>& knots) {
213 
214  assert(knots.size() > 2);
215 
216  vector<pair<float,float>> result(n);
217 
218  result[0].first = knots[0].first+2.0*knots[1].first;
219  result[0].second = knots[0].second+2.0*knots[1].second;
220 
221  for (int i = 1; i < n - 1; i++) {
222  result[i].first = 2.0*(2.0*knots[i].first+(knots[i+1].first));
223  result[i].second = 2.0*(2.0*knots[i].second+(knots[i+1].second));
224  }
225 
226  result[result.size() - 1].first = 8.0*knots[n-1].first+knots[n].first;
227  result[result.size() - 1].second = 8.0*knots[n-1].second+knots[n].second;
228 
229  return result;
230  }
231 
232  // For ticket 991. Applies Thomas' algorithm to the matrix equation Ac = k.
287  vector<pair<float,float>> computeControlPoints(vector<vector<float>> triDiag, const vector<pair<float,float>>& knots, vector<pair<float,float>> target) {
288 
289  assert(knots.size() > 2);
290 
291  const size_t n = knots.size() - 1;
292 
293  vector<pair<float,float>> result(2*n);
294 
295  // Vector of knots k' after Thomas' algorithm is applied to the initial system Ac = k
296  vector<pair<float,float>> newTarget(n);
297 
298  // Matrix A' after Thomas' algorithm is applied to the initial system Ac = k
299  vector<vector<float>> newTriDiag(n,vector<float>(n));
300 
301  // forward sweep for control points c_i,0:
302  newTriDiag[0][1] = triDiag[0][1]/ triDiag[0][0];
303 
304  newTarget[0].first = target[0].first*(1.0 / triDiag[0][0]);
305  newTarget[0].second = target[0].second*(1.0 / triDiag[0][0]);
306 
307 
308  for (size_t i = 1; i < n-1; i++)
309  for (size_t j = 0; j < n; j++)
310  if (i == j-1)
311  newTriDiag[i][j] = triDiag[i][j] / (triDiag[i][i] - triDiag[i][j-2] * newTriDiag[i-1][j-1]);
312 
313  for (size_t i = 1; i < n; i++)
314  for (size_t j = 0; j < n; j++) {
315  if (i == j + 1)
316  {
317  const float targetScale = 1.0/(triDiag[i][i] - triDiag[i][j] * newTriDiag[i-1][j+1]);
318  newTarget[i].first = (target[i].first-(newTarget[i-1].first*(triDiag[i][j])))*(targetScale);
319  newTarget[i].second = (target[i].second-(newTarget[i-1].second*(triDiag[i][j])))*(targetScale);
320  }
321  }
322 
323  // backward sweep for control points c_i,0:
324  result[n-1].first = newTarget[n-1].first;
325  result[n-1].second = newTarget[n-1].second;
326 
327  for (int i = n-2; i >= 0; i--)
328  for (int j = n-1; j >= 0; j--) {
329  if (i == j-1)
330  {
331  result[i].first = newTarget[i].first-(newTriDiag[i][j]*result[i+1].first);
332  result[i].second = newTarget[i].second-(newTriDiag[i][j]*result[i+1].second);
333  }
334  }
335 
336  // calculate remaining control points c_i,1 directly:
337  for (size_t i = 0; i < n-1; i++) {
338  result[n+i].first = 2.0*knots[i+1].first-(result[i+1].first);
339  result[n+i].second = 2.0*knots[i+1].second-(result[i+1].second);
340  }
341 
342  result[2*n-1].first = 0.5*(knots[n].first+(result[n-1].first));
343  result[2*n-1].second = 0.5*(knots[n].second+(result[n-1].second));
344 
345  return result;
346  }
347 
348 }
349 
350  void Wire::storeCairoCoords(cairo_t* cairo) const
351  {
352  cairoCoords.clear();
353  cairo_path_t *path;
354  cairo_path_data_t *data;
355 
356  path = cairo_copy_path_flat(cairo);
357 
358  for (int j=0; j < path->num_data; j += path->data[j].header.length) {
359  data = &path->data[j];
360  switch (data->header.type) {
361  case CAIRO_PATH_MOVE_TO:
362  break;
363  case CAIRO_PATH_LINE_TO:
364  cairoCoords.push_back(make_pair(data[1].point.x,data[1].point.y));
365  break;
366  case CAIRO_PATH_CURVE_TO:
367  case CAIRO_PATH_CLOSE_PATH:
368  break;
369  }
370  }
371  cairo_path_destroy (path);
372  }
373 
374  void Wire::draw(cairo_t* cairo, bool reverseArrow) const
375  {
376  auto coords=this->coords();
377  if (coords.size()<4 || !visible()) return;
378 
379  float angle, lastx, lasty;
380  if (coords.size()==4)
381  {
382  cairo_move_to(cairo,coords[0],coords[1]);
383  cairo_line_to(cairo, coords[2], coords[3]);
384  if (reverseArrow)
385  {
386  angle=atan2(coords[3]-coords[1], coords[2]-coords[0])+M_PI;
387  lastx=coords[0]; lasty=coords[1];
388  }
389  else
390  {
391  angle=atan2(coords[3]-coords[1], coords[2]-coords[0]) + (reverseArrow? M_PI:0);
392  lastx=coords[2]; lasty=coords[3];
393  }
394  }
395  else
396  {
397  cairo_move_to(cairo, coords[0], coords[1]);
398 
413  // For ticket 991/1092. Convert to coordinate pairs.
414  vector<pair<float,float>> points = toCoordPair(coords);
415 
416  const int n = points.size()-1;
417 
418  // Initial vector of knots in the matrix equation Ac = k
419  const vector<pair<float,float>> target = constructTargetVector(n, points);
420 
421  vector<vector<float>> A(n,vector<float>(n));
422  // Initial matrix A which relates control points c_i to knots k_i by matching first and second derivatives of cubic Bezier curves at common points (knots) between curves.
423  A = constructTriDiag(n);
424 
425  // For ticket 991. Apply Thomas' algorithm to matrix equation Ac=k
426  vector<pair<float,float>> controlPoints = computeControlPoints(A, points, target);
427 
428  // Decrease tolerance a bit, since it's going to be magnified
429  cairo_set_tolerance (cairo, 0.01);
430 
431  for (int i = 0; i < n; i++) {
432  cairo_curve_to(cairo, controlPoints[i].first,controlPoints[i].second,controlPoints[n+i].first,controlPoints[n+i].second,points[i+1].first,points[i+1].second);
433  }
434 
435  // Stash the internal cairo coordinates used to draw curved wires. for ticket 1079.
436  if (coords.size()>4) storeCairoCoords(cairo);
437 
438  cairo_stroke(cairo);
439  angle=atan2(coords[coords.size()-1]-coords[coords.size()-3],
440  coords[coords.size()-2]-coords[coords.size()-4]);
441  lastx=coords[coords.size()-2]; lasty=coords[coords.size()-1];
442  }
443  cairo_stroke(cairo);
444 
445  // draw arrow
446  {
447  const CairoSave cs(cairo);
448  auto lw=cairo_get_line_width(cairo);
449  cairo_translate(cairo, lastx, lasty);
450  cairo_rotate(cairo,angle);
451  cairo_move_to(cairo,0,0);
452  cairo_line_to(cairo,-5*lw,-3*lw);
453  cairo_line_to(cairo,-3*lw,0);
454  cairo_line_to(cairo,-5*lw,3*lw);
455  cairo_close_path(cairo);
456  cairo_fill(cairo);
457  }
458 
459  // draw handles
460  if (mouseFocus)
461  {
462  const cairo::CairoSave cs(cairo);
463  cairo_set_source_rgb(cairo,0,0,1);
464  if (cairoCoords.empty() || coords.size()==4)
465  {
466  const double midx=0.5*(coords[0]+coords[2]);
467  const double midy=0.5*(coords[1]+coords[3]);
468  cairo_arc(cairo,midx,midy,1.5*handleRadius, 0, 2*M_PI);
469  cairo_fill(cairo);
470  }
471  else
472  {
473  const size_t numSmallHandles=0.5*(coords.size()-4)+1, numCairoCoords=cairoCoords.size()-1;
474  for (size_t i=0; i<coords.size()-3; i+=2)
475  {
476  const double midx=cairoCoords[(i+1)*numCairoCoords/(2*numSmallHandles)].first;
477  const double midy=cairoCoords[(i+1)*numCairoCoords/(2*numSmallHandles)].second;
478  cairo_arc(cairo,midx,midy,handleRadius, 0, 2*M_PI);
479  if (i>0) // draw existing interior gripping handle
480  cairo_arc(cairo,coords[i],coords[i+1],1.5*handleRadius, 0, 2*M_PI);
481  cairo_fill(cairo);
482  }
483  }
484  }
485  if (mouseFocus && !tooltip().empty())
486  {
487  const cairo::CairoSave cs(cairo);
488  const string toolTipText=latexToPango(tooltip());
489  ecolab::Pango pango(cairo);
490  pango.setMarkup(toolTipText);
491  // place tooltip on centre dot if an odd number of control points, or halfway between otherwise
492  auto dd=div(coords.size(),4);
493  assert(dd.rem%2==0);
494  if (dd.quot&1) dd.quot+=dd.rem-1; // adjust so the dd.quot points to an x coordinate
495  double midx=coords[dd.quot], midy=coords[dd.quot+1];
496  if (dd.rem==0)
497  {
498  midx=0.5*(coords[dd.quot]+coords[dd.quot+2]);
499  midy=0.5*(coords[dd.quot+1]+coords[dd.quot+3]);
500  }
501  cairo_translate(cairo,midx,midy);
502  cairo_rectangle(cairo,0,0,pango.width(),pango.height());
503  cairo_set_source_rgb(cairo,1,1,1);
504  cairo_fill_preserve(cairo);
505  cairo_set_source_rgb(cairo,0,0,0);
506  cairo_set_line_width(cairo,1);
507  cairo_set_dash(cairo,nullptr,0,0);
508  pango.show();
509  cairo_stroke(cairo);
510  }
511  }
512 
513  void Wire::split()
514  {
515  // add I/O variables if this wire crosses a group boundary
516  if (auto fg=from()->item().group.lock())
517  if (auto tg=to()->item().group.lock())
518  if (fg!=tg && !from()->item().ioVar() && !to()->item().ioVar()) // crosses boundary
519  {
520  // check if this wire is in from group
521  auto cmp=[&](const WirePtr& w) {return w.get()==this;};
522  auto i=find_if(fg->wires.begin(), fg->wires.end(), cmp);
523  if (i==fg->wires.end())
524  {
525  fg->addOutputVar();
526  assert(fg->outVariables.back()->portsSize()>1);
527  fg->addWire(new Wire(from(),fg->outVariables.back()->ports(1)));
528  moveToPorts(fg->outVariables.back()->ports(0).lock(), to());
529  }
530  // check if this wire is in to group
531  i=find_if(tg->wires.begin(), tg->wires.end(), cmp);
532  if (i==tg->wires.end())
533  {
534  tg->addInputVar();
535  assert(tg->inVariables.back()->portsSize()>1);
536  tg->addWire(new Wire(tg->inVariables.back()->ports(0),to()));
537  moveToPorts(from(), tg->inVariables.back()->ports(1).lock());
538  }
539  }
540  }
541 
542  Units Wire::units(bool check) const
543  {
544 
545  if (auto f=from())
546  {
547  // we allow possible traversing twice over a wire, to allow an
548  // integral to break the cycle
549  if (unitsCtr>2)
550  f->item().throw_error("wiring loop detected");
551  const IncrDecrCounter idc(unitsCtr);
552  return f->item().units(check);
553  }
554  return {};
555  }
556 
557  namespace
558  {
559 
560  // returns true if x,y lies close to the line segment (x0,y0)-(x1,y1)
561  bool segNear(float x0, float y0, float x1, float y1, float x, float y)
562  {
563  const float d=sqrt(sqr(x1-x0)+sqr(y1-y0));
564  const float d1=sqrt(sqr(x-x0)+sqr(y-y0)), d2=sqrt(sqr(x-x1)+sqr(y-y1));
565  return d1+d2<=d+5;
566  }
567 
568  inline float d2(float x0, float y0, float x1, float y1)
569  {return sqr(x1-x0)+sqr(y1-y0);}
570 
571  }
572 
573 #undef near
574  bool Wire::near(float x, float y) const
575  {
576  auto c=coords();
577  assert(c.size()>=4);
578  if (c.size()==4)
579  return segNear(c[0],c[1],c[2],c[3],x,y);
580 
581  // fixes for tickets 991/1095
582  vector<pair<float,float>> p=allHandleCoords(c);
583  if (!cairoCoords.empty()) p=cairoCoords;
584 
585  unsigned k=0; // nearest index
586  float closestD=d2(p[0].first,p[0].second,x,y);
587  for (size_t i=0; i<p.size(); i++)
588  {
589  const float d=d2(p[i].first,p[i].second,x,y);
590  if (d<=closestD)
591  {
592  closestD=d;
593  k=i;
594  }
595  }
596 
597  // Check for proximity to line segments about index k
598  if (k>0 && k<p.size()-1)
599  return (segNear(p[k-1].first,p[k-1].second,p[k].first,p[k].second,x,y) || segNear(p[k].first,p[k].second,p[k+1].first,p[k+1].second,x,y));
600  return false;
601  }
602 
603  unsigned Wire::nearestHandle(float x, float y)
604  {
605  auto c=coords();
606 
607  unsigned n=0; // nearest index
608  float closestD=d2(c[0],c[1],x,y);
609  for (size_t i=2; i<c.size()-1; i+=2)
610  {
611  const float d=d2(c[i],c[i+1],x,y);
612  if (d<=closestD)
613  {
614  closestD=d;
615  n=i;
616  }
617  }
618 
619  // now work out if we need to insert a midpoint handle
620  if (n>0)
621  {
622  const float mx=0.5*(c[n]+c[n-2]), my=0.5*(c[n+1]+c[n-1]);
623  const float d=d2(mx,my,x,y);
624  if (n==c.size()-2 || d<closestD)
625  {
626  insertHandle((n>>1)-1, mx, my);
627  return (n>>1)-1;
628  }
629  }
630  if (n<c.size()-3)
631  {
632  const float mx=0.5*(c[n+2]+c[n]), my=0.5*(c[n+3]+c[n+1]);
633  const float d=d2(mx,my,x,y);
634  if (n==0 || d<closestD)
635  {
636  insertHandle(n>>1, mx, my);
637  return (n>>1);
638  }
639  }
640  return (n>>1)-1;
641  }
642 
643  void Wire::insertHandle(unsigned n, float x, float y)
644  {
645  n++;
646  n*=2;
647  auto c=coords();
648  assert(n<c.size()-1);
649  vector<float> h{x,y};
650  c.insert(c.begin()+n,h.begin(), h.end());
651  coords(c);
652  }
653 
654  // For ticket 1092. Reinstate delete handle user interaction
655  void Wire::deleteHandle(float x, float y)
656  {
657  auto c=coords();
658  auto n=c.begin(); // nearest index
659  float closestD=d2(c[0],c[1],x,y);
660  for (auto i=c.begin()+2; i<c.end()-1; i+=2)
661  {
662  const float d=d2(*i,*(i+1),x,y);
663  if (d<closestD)
664  {
665  closestD=d;
666  n=i;
667  }
668  }
669  assert(n<c.end()-1);
670  c.erase(n, n+2);
671  coords(c);
672  }
673 
674  void Wire::editHandle(unsigned position, float x, float y)
675  {
676  position++;
677  position*=2;
678  auto c=coords();
679  assert(position<c.size()-2);
680  c[position]=x;
681  c[position+1]=y;
682  coords(c);
683  }
684 
685 
686 }
#define M_PI
some useful geometry types, defined from boost::geometry
Definition: geometry.h:29
bool visible() const
whether this wire is visible or not
Definition: wire.cc:109
function f
Definition: canvas.m:1
std::vector< WirePtr > Wires
Definition: wire.h:103
std::string latexToPango(const char *s)
Definition: latexMarkup.h:30
CLASSDESC_ACCESS_EXPLICIT_INSTANTIATION(minsky::Wire)
bool segNear(float x0, float y0, float x1, float y1, float x, float y)
Definition: wire.cc:561
std::shared_ptr< Port > from() const
Definition: wire.h:63
Expr sqrt(const Expr &x)
Definition: expr.h:154
vector< vector< float > > constructTriDiag(int length)
Definition: wire.cc:185
std::vector< std::pair< float, float > > cairoCoords
contains all the internal cairo coordinates used to draw a wire
Definition: wire.h:55
std::shared_ptr< Port > to() const
Definition: wire.h:64
static constexpr float handleRadius
Definition: wire.h:53
exception-safe increment/decrement of a counter in a block
Definition: variable.h:56
STL namespace.
std::vector< float > m_coords
Definition: wire.h:49
Wire()
Definition: wire.h:58
std::shared_ptr< Wire > WirePtr
Definition: wire.h:102
void deleteHandle(float x, float y)
Definition: wire.cc:655
int unitsCtr
for detecting wiring loops in units()
Definition: wire.h:54
bool recursiveDo(M GroupItems::*map, O op) const
Perform action heirarchically on elements of map map. If op returns true, the operation terminates...
Definition: group.h:111
void insertHandle(unsigned position, float x, float y)
Definition: wire.cc:643
vector< pair< float, float > > constructTargetVector(int n, const vector< pair< float, float >> &knots)
Definition: wire.cc:212
virtual std::string const & tooltip() const
Definition: noteBase.h:36
void storeCairoCoords(cairo_t *cairo) const
stash all the internal cairo coordinates along a wire
Definition: wire.cc:350
std::vector< float > coords() const
display coordinates
Definition: wire.cc:45
unsigned nearestHandle(float x, float y)
returns the index into the coordinate list if x,y is close to it. Otherwise inserts midpoints and ret...
Definition: wire.cc:603
Creation and access to the minskyTCL_obj object, which has code to record whenever Minsky&#39;s state cha...
Definition: constMap.h:22
std::weak_ptr< Port > m_to
Definition: wire.h:51
vector< pair< float, float > > toCoordPair(vector< float > coords)
Definition: wire.cc:173
void split()
splits wires crossing group boundaries
Definition: wire.cc:513
represents the units (in sense of dimensional analysis) of a variable.
Definition: units.h:34
bool near(float x, float y) const
returns true if coordinates are near this wire
Definition: wire.cc:574
std::weak_ptr< Port > m_from
ports this wire connects
Definition: wire.h:51
void moveIntoGroup(Group &dest)
move this from its group into dest
Definition: wire.cc:131
Units units(bool) const
units (dimensional analysis) of data flowing across wire
Definition: wire.cc:542
T sqr(T x)
Definition: geometry.h:36
vector< pair< float, float > > allHandleCoords(vector< float > coords)
Definition: wire.cc:154
vector< pair< float, float > > computeControlPoints(vector< vector< float >> triDiag, const vector< pair< float, float >> &knots, vector< pair< float, float >> target)
Definition: wire.cc:287
const Group & globalGroup() const
top level group
Definition: group.cc:739
void moveToPorts(const std::shared_ptr< Port > &from, const std::shared_ptr< Port > &to)
switch ports this wire links to
Definition: wire.cc:118
WirePtr addWire(const Item &from, const Item &to, unsigned toPortIdx, const std::vector< float > &coords)
add a wire from item from, to item to, connecting to the toIdx port of to, with coordinates ...
Definition: group.h:63
bool mouseFocus
true if target of a mouseover
Definition: noteBase.h:31
void draw(cairo_t *cairo, bool reverseArrow=false) const
draw this item into a cairo context
Definition: wire.cc:374
Definition: group.tcl:84
void editHandle(unsigned position, float x, float y)
Definition: wire.cc:674
float d2(float x0, float y0, float x1, float y1)
Definition: wire.cc:568