37 using ecolab::cairo::CairoSave;
41 WireAccessor::WireAccessor():
ecolab::TCLAccessor<
Wire,
std::vector<float>>
42 (
"coords", (Getter)&
Wire::coords, (Setter)&
Wire::coords) {}
56 (
sqr(
f->x()-t->x())+
sqr(
f->y()-t->y()));
75 assert(
coords.size() % 2 == 0);
80 for (
size_t i=2; i<
coords.size()-3; i+=2)
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)
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");
97 m_from.lock()->m_wires.push_back(
this);
98 m_to.lock()->m_wires.push_back(
this);
103 if (
auto toPort=
to())
104 toPort->eraseWire(
this);
105 if (
auto fromPort=
from())
106 fromPort->eraseWire(
this);
112 auto fgroup=
f->item().group.lock(), tgroup=t->item().group.lock();
114 (!fgroup || fgroup->displayContents() ||
115 !tgroup || tgroup->displayContents());
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());
126 from->m_wires.push_back(
this);
127 to->m_wires.push_back(
this);
137 [&](
Wires& wires, Wires::iterator i) {
156 vector<pair<float,float>> points(coords.size()-1);
158 for (
size_t i = 0; i < points.size(); i++) {
160 points[i].first = coords[i];
161 points[i].second = coords[i+1];
164 points[i].first = 0.5*(coords[i-1]+coords[i+1]);
165 points[i].second = 0.5*(coords[i]+coords[i+2]);
174 vector<pair<float,float>> points(coords.size()/2);
176 for (
size_t i = 0; i < coords.size(); i++)
178 points[i/2].first = coords[i];
179 points[i/2].second = coords[i+1];
187 vector<vector<float>> result(length,vector<float>(length));
189 for (
int i = 0; i < length; i++)
190 for (
int j = 0; j < length; j++) {
193 if (j > 0 && i == j-1 && i < length-1) result[i][j] = 1.0;
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;
202 if (j < length-2) result[i][j] = 1.0;
203 else if (j == length-2) result[i][j] = 2.0;
205 else result[i][j] = 0.0;
214 assert(knots.size() > 2);
216 vector<pair<float,float>> result(n);
218 result[0].first = knots[0].first+2.0*knots[1].first;
219 result[0].second = knots[0].second+2.0*knots[1].second;
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));
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;
287 vector<pair<float,float>>
computeControlPoints(vector<vector<float>> triDiag,
const vector<pair<float,float>>& knots, vector<pair<float,float>> target) {
289 assert(knots.size() > 2);
291 const size_t n = knots.size() - 1;
293 vector<pair<float,float>> result(2*n);
296 vector<pair<float,float>> newTarget(n);
299 vector<vector<float>> newTriDiag(n,vector<float>(n));
302 newTriDiag[0][1] = triDiag[0][1]/ triDiag[0][0];
304 newTarget[0].first = target[0].first*(1.0 / triDiag[0][0]);
305 newTarget[0].second = target[0].second*(1.0 / triDiag[0][0]);
308 for (
size_t i = 1; i < n-1; i++)
309 for (
size_t j = 0; j < n; j++)
311 newTriDiag[i][j] = triDiag[i][j] / (triDiag[i][i] - triDiag[i][j-2] * newTriDiag[i-1][j-1]);
313 for (
size_t i = 1; i < n; i++)
314 for (
size_t j = 0; j < n; j++) {
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);
324 result[n-1].first = newTarget[n-1].first;
325 result[n-1].second = newTarget[n-1].second;
327 for (
int i = n-2; i >= 0; i--)
328 for (
int j = n-1; j >= 0; j--) {
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);
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);
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));
354 cairo_path_data_t *data;
356 path = cairo_copy_path_flat(cairo);
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:
363 case CAIRO_PATH_LINE_TO:
364 cairoCoords.push_back(make_pair(data[1].point.x,data[1].point.y));
366 case CAIRO_PATH_CURVE_TO:
367 case CAIRO_PATH_CLOSE_PATH:
371 cairo_path_destroy (path);
379 float angle, lastx, lasty;
416 const int n = points.size()-1;
421 vector<vector<float>> A(n,vector<float>(n));
429 cairo_set_tolerance (cairo, 0.01);
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);
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);
462 const cairo::CairoSave cs(cairo);
463 cairo_set_source_rgb(cairo,0,0,1);
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)
476 const double midx=
cairoCoords[(i+1)*numCairoCoords/(2*numSmallHandles)].first;
477 const double midy=
cairoCoords[(i+1)*numCairoCoords/(2*numSmallHandles)].second;
487 const cairo::CairoSave cs(cairo);
489 ecolab::Pango pango(cairo);
490 pango.setMarkup(toolTipText);
492 auto dd=div(
coords.size(),4);
494 if (dd.quot&1) dd.quot+=dd.rem-1;
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);
517 if (
auto tg=
to()->item().group.lock())
518 if (fg!=tg && !
from()->item().ioVar() && !
to()->item().ioVar())
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())
526 assert(fg->outVariables.back()->portsSize()>1);
527 fg->addWire(
new Wire(
from(),fg->outVariables.back()->ports(1)));
531 i=find_if(tg->wires.begin(), tg->wires.end(), cmp);
532 if (i==tg->wires.end())
535 assert(tg->inVariables.back()->portsSize()>1);
536 tg->addWire(
new Wire(tg->inVariables.back()->ports(0),
to()));
550 f->item().throw_error(
"wiring loop detected");
552 return f->item().units(check);
561 bool segNear(
float x0,
float y0,
float x1,
float y1,
float x,
float y)
568 inline float d2(
float x0,
float y0,
float x1,
float y1)
569 {
return sqr(x1-x0)+
sqr(y1-y0);}
579 return segNear(c[0],c[1],c[2],c[3],x,y);
586 float closestD=
d2(p[0].first,p[0].second,x,y);
587 for (
size_t i=0; i<p.size(); i++)
589 const float d=
d2(p[i].first,p[i].second,x,y);
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));
608 float closestD=
d2(c[0],c[1],x,y);
609 for (
size_t i=2; i<c.size()-1; i+=2)
611 const float d=
d2(c[i],c[i+1],x,y);
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)
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)
648 assert(n<c.size()-1);
649 vector<float> h{x,y};
650 c.insert(c.begin()+n,h.begin(), h.end());
659 float closestD=
d2(c[0],c[1],x,y);
660 for (
auto i=c.begin()+2; i<c.end()-1; i+=2)
662 const float d=
d2(*i,*(i+1),x,y);
679 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::vector< float > m_coords
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...
Creation and access to the minskyTCL_obj object, which has code to record whenever Minsky's state cha...
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)