37 using ecolab::cairo::CairoSave;
41 vector<float> Wire::coords()
const 44 assert(from() && to());
45 assert(m_coords.size() % 2 == 0);
52 (
sqr(
f->x()-t->x())+
sqr(
f->y()-t->y()));
54 for (
size_t i=0; m_coords.size()>1 && i<m_coords.size()-1; i+=2)
56 c.push_back(
f->x() + d*m_coords[i]);
57 c.push_back(
f->y() + d*m_coords[i+1]);
65 vector<float> Wire::coords(
const vector<float>& coords)
71 assert(coords.size() % 2 == 0);
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)
78 m_coords[i-2] = (coords[i]-coords[0])*d;
79 m_coords[i-1] = (coords[i+1]-coords[1])*d;
82 return this->coords();
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)
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");
93 m_from.lock()->m_wires.push_back(
this);
94 m_to.lock()->m_wires.push_back(
this);
100 toPort->eraseWire(
this);
101 if (
auto fromPort=
from())
102 fromPort->eraseWire(
this);
108 auto fgroup=
f->item().group.lock(), tgroup=t->item().group.lock();
110 (!fgroup || fgroup->displayContents() ||
111 !tgroup || tgroup->displayContents());
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());
122 from->m_wires.push_back(
this);
123 to->m_wires.push_back(
this);
133 [&](
Wires& wires, Wires::iterator i) {
152 vector<pair<float,float>> points(coords.size()-1);
154 for (
size_t i = 0; i < points.size(); i++) {
156 points[i].first = coords[i];
157 points[i].second = coords[i+1];
160 points[i].first = 0.5*(coords[i-1]+coords[i+1]);
161 points[i].second = 0.5*(coords[i]+coords[i+2]);
170 vector<pair<float,float>> points(coords.size()/2);
172 for (
size_t i = 0; i < coords.size(); i++)
174 points[i/2].first = coords[i];
175 points[i/2].second = coords[i+1];
183 vector<vector<float>> result(length,vector<float>(length));
185 for (
int i = 0; i < length; i++)
186 for (
int j = 0; j < length; j++) {
189 if (j > 0 && i == j-1 && i < length-1) result[i][j] = 1.0;
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;
198 if (j < length-2) result[i][j] = 1.0;
199 else if (j == length-2) result[i][j] = 2.0;
201 else result[i][j] = 0.0;
210 assert(knots.size() > 2);
212 vector<pair<float,float>> result(n);
214 result[0].first = knots[0].first+2.0*knots[1].first;
215 result[0].second = knots[0].second+2.0*knots[1].second;
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));
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;
283 vector<pair<float,float>>
computeControlPoints(vector<vector<float>> triDiag,
const vector<pair<float,float>>& knots, vector<pair<float,float>> target) {
285 assert(knots.size() > 2);
287 const size_t n = knots.size() - 1;
289 vector<pair<float,float>> result(2*n);
292 vector<pair<float,float>> newTarget(n);
295 vector<vector<float>> newTriDiag(n,vector<float>(n));
298 newTriDiag[0][1] = triDiag[0][1]/ triDiag[0][0];
300 newTarget[0].first = target[0].first*(1.0 / triDiag[0][0]);
301 newTarget[0].second = target[0].second*(1.0 / triDiag[0][0]);
304 for (
size_t i = 1; i < n-1; i++)
305 for (
size_t j = 0; j < n; j++)
307 newTriDiag[i][j] = triDiag[i][j] / (triDiag[i][i] - triDiag[i][j-2] * newTriDiag[i-1][j-1]);
309 for (
size_t i = 1; i < n; i++)
310 for (
size_t j = 0; j < n; j++) {
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);
320 result[n-1].first = newTarget[n-1].first;
321 result[n-1].second = newTarget[n-1].second;
323 for (
int i = n-2; i >= 0; i--)
324 for (
int j = n-1; j >= 0; j--) {
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);
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);
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));
350 cairo_path_data_t *data;
352 path = cairo_copy_path_flat(cairo);
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:
359 case CAIRO_PATH_LINE_TO:
360 cairoCoords.push_back(make_pair(data[1].point.x,data[1].point.y));
362 case CAIRO_PATH_CURVE_TO:
363 case CAIRO_PATH_CLOSE_PATH:
367 cairo_path_destroy (path);
375 float angle, lastx, lasty;
412 const int n = points.size()-1;
417 vector<vector<float>> A(n,vector<float>(n));
425 cairo_set_tolerance (cairo, 0.01);
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);
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);
458 const cairo::CairoSave cs(cairo);
459 cairo_set_source_rgb(cairo,0,0,1);
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)
472 const double midx=
cairoCoords[(i+1)*numCairoCoords/(2*numSmallHandles)].first;
473 const double midy=
cairoCoords[(i+1)*numCairoCoords/(2*numSmallHandles)].second;
483 const cairo::CairoSave cs(cairo);
485 ecolab::Pango pango(cairo);
486 pango.setMarkup(toolTipText);
488 auto dd=div(
coords.size(),4);
490 if (dd.quot&1) dd.quot+=dd.rem-1;
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);
513 if (
auto tg=
to()->item().group.lock())
514 if (fg!=tg && !
from()->item().ioVar() && !
to()->item().ioVar())
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())
522 assert(fg->outVariables.back()->portsSize()>1);
523 fg->addWire(
new Wire(
from(),fg->outVariables.back()->ports(1)));
527 i=find_if(tg->wires.begin(), tg->wires.end(), cmp);
528 if (i==tg->wires.end())
531 assert(tg->inVariables.back()->portsSize()>1);
532 tg->addWire(
new Wire(tg->inVariables.back()->ports(0),
to()));
546 f->item().throw_error(
"wiring loop detected");
548 return f->item().units(check);
557 bool segNear(
float x0,
float y0,
float x1,
float y1,
float x,
float y)
564 inline float d2(
float x0,
float y0,
float x1,
float y1)
565 {
return sqr(x1-x0)+
sqr(y1-y0);}
575 return segNear(c[0],c[1],c[2],c[3],x,y);
582 float closestD=
d2(p[0].first,p[0].second,x,y);
583 for (
size_t i=0; i<p.size(); i++)
585 const float d=
d2(p[i].first,p[i].second,x,y);
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));
604 float closestD=
d2(c[0],c[1],x,y);
605 for (
size_t i=2; i<c.size()-1; i+=2)
607 const float d=
d2(c[i],c[i+1],x,y);
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)
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)
644 assert(n<c.size()-1);
645 vector<float> h{x,y};
646 c.insert(c.begin()+n,h.begin(), h.end());
655 float closestD=
d2(c[0],c[1],x,y);
656 for (
auto i=c.begin()+2; i<c.end()-1; i+=2)
658 const float d=
d2(*i,*(i+1),x,y);
675 assert(position<c.size()-2);
#define M_PI
some useful geometry types, defined from boost::geometry
bool visible() const
whether this wire is visible or not
std::vector< WirePtr > Wires
std::string latexToPango(const char *s)
CLASSDESC_ACCESS_EXPLICIT_INSTANTIATION(minsky::Wire)
bool segNear(float x0, float y0, float x1, float y1, float x, float y)
std::shared_ptr< Port > from() const
vector< vector< float > > constructTriDiag(int length)
std::vector< std::pair< float, float > > cairoCoords
contains all the internal cairo coordinates used to draw a wire
std::shared_ptr< Port > to() const
static constexpr float handleRadius
exception-safe increment/decrement of a counter in a block
std::shared_ptr< Wire > WirePtr
void deleteHandle(float x, float y)
int unitsCtr
for detecting wiring loops in units()
bool recursiveDo(M GroupItems::*map, O op) const
Perform action heirarchically on elements of map map. If op returns true, the operation terminates...
void insertHandle(unsigned position, float x, float y)
vector< pair< float, float > > constructTargetVector(int n, const vector< pair< float, float >> &knots)
virtual std::string const & tooltip() const
void storeCairoCoords(cairo_t *cairo) const
stash all the internal cairo coordinates along a wire
std::vector< float > coords() const
display coordinates
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...
std::weak_ptr< Port > m_to
vector< pair< float, float > > toCoordPair(vector< float > coords)
void split()
splits wires crossing group boundaries
represents the units (in sense of dimensional analysis) of a variable.
bool near(float x, float y) const
returns true if coordinates are near this wire
std::weak_ptr< Port > m_from
ports this wire connects
void moveIntoGroup(Group &dest)
move this from its group into dest
Units units(bool) const
units (dimensional analysis) of data flowing across wire
vector< pair< float, float > > allHandleCoords(vector< float > coords)
vector< pair< float, float > > computeControlPoints(vector< vector< float >> triDiag, const vector< pair< float, float >> &knots, vector< pair< float, float >> target)
const Group & globalGroup() const
top level group
void moveToPorts(const std::shared_ptr< Port > &from, const std::shared_ptr< Port > &to)
switch ports this wire links to
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 ...
bool mouseFocus
true if target of a mouseover
void draw(cairo_t *cairo, bool reverseArrow=false) const
draw this item into a cairo context
void editHandle(unsigned position, float x, float y)
float d2(float x0, float y0, float x1, float y1)