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