Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
block.cpp
Go to the documentation of this file.
1/*
2 * vim: ts=4 sw=4 et tw=0 wm=0
3 *
4 * libvpsc - A solver for the problem of Variable Placement with
5 * Separation Constraints.
6 *
7 * Copyright (C) 2005-2008 Monash University
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 * See the file LICENSE.LGPL distributed with the library.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 *
19 * Author(s): Tim Dwyer
20*/
21
22/*
23 * @brief A block is a group of variables that must be moved together to improve
24 * the goal function without violating already active constraints.
25 * The variables in a block are spanned by a tree of active constraints.
26 *
27 */
28
29#include "libvpsc/block.h"
30#include "libvpsc/variable.h"
31#include <cassert>
33#include "libvpsc/constraint.h"
34#include "libvpsc/exceptions.h"
35#include "libvpsc/blocks.h"
36
37#ifdef LIBVPSC_LOGGING
38#include <fstream>
39using std::ios;
40using std::ofstream;
41using std::endl;
42#endif
43
44#define __NOTNAN(p) (p)==(p)
45
46namespace vpsc {
48 double ai=scale/v->scale;
49 double bi=v->offset/v->scale;
50 double wi=v->weight;
51 AB+=wi*ai*bi;
52 AD+=wi*ai*v->desiredPosition;
53 A2+=wi*ai*ai;
54 /*
55#ifdef LIBVPSC_LOGGING
56 ofstream f(LOGFILE,ios::app);
57 f << "adding v[" << v->id << "], blockscale=" << scale << ", despos="
58 << v->desiredPosition << ", ai=" << ai << ", bi=" << bi
59 << ", AB=" << AB << ", AD=" << AD << ", A2=" << A2;
60#endif
61*/
62}
64 v->block=this;
65 vars->push_back(v);
66 if(ps.A2==0) ps.scale=v->scale;
67 //weight+= v->weight;
68 //wposn += v->weight * (v->desiredPosition - v->offset);
69 //posn=wposn/weight;
70 ps.addVariable(v);
71 posn=(ps.AD - ps.AB) / ps.A2;
72 COLA_ASSERT(__NOTNAN(posn));
73 /*
74#ifdef LIBVPSC_LOGGING
75 ofstream f(LOGFILE,ios::app);
76 f << ", posn=" << posn << endl;
77#endif
78*/
79}
80Block::Block(Blocks *blocks, Variable* const v)
81 : vars(new std::vector<Variable*>)
82 , posn(0)
83 //, weight(0)
84 //, wposn(0)
85 , deleted(false)
86 , timeStamp(0)
87 , in(nullptr)
88 , out(nullptr)
89 , blocks(blocks)
90{
91 if(v!=nullptr) {
92 v->offset=0;
93 addVariable(v);
94 }
95}
96
98 //wposn=0;
99 ps.AB=ps.AD=ps.A2=0;
100 for (Vit v=vars->begin();v!=vars->end();++v) {
101 //wposn += ((*v)->desiredPosition - (*v)->offset) * (*v)->weight;
102 ps.addVariable(*v);
103 }
104 posn=(ps.AD - ps.AB) / ps.A2;
105 COLA_ASSERT(__NOTNAN(posn));
106#ifdef LIBVPSC_LOGGING
107 ofstream f(LOGFILE,ios::app);
108 f << ", posn=" << posn << endl;
109#endif
110}
112{
113 delete vars;
114 delete in;
115 delete out;
116}
124 delete h;
126 for (Vit i=vars->begin();i!=vars->end();++i) {
127 Variable *v=*i;
128 std::vector<Constraint*> *cs=in?&(v->in):&(v->out);
129 for (Cit j=cs->begin();j!=cs->end();++j) {
130 Constraint *c=*j;
132 if ( ((c->left->block != this) && in) ||
133 ((c->right->block != this) && !in) )
134 {
135 h->insert(c);
136 }
137 }
138 }
139}
141#ifdef LIBVPSC_LOGGING
142 ofstream f(LOGFILE,ios::app);
143 f<<" merging on: "<<*c<<",c->left->offset="<<c->left->offset<<",c->right->offset="<<c->right->offset<<endl;
144#endif
145 double dist = c->right->offset - c->left->offset - c->gap;
146 Block *l=c->left->block;
147 Block *r=c->right->block;
148 if (l->vars->size() < r->vars->size()) {
149 r->merge(l,c,dist);
150 } else {
151 l->merge(r,c,-dist);
152 }
153 Block* mergeBlock=b->deleted?this:b;
154#ifdef LIBVPSC_LOGGING
155 f<<" merged block="<<*mergeBlock<<endl;
156#endif
157 return mergeBlock;
158}
159/*
160 * Merges b into this block across c. Can be either a
161 * right merge or a left merge
162 * @param b block to merge into this
163 * @param c constraint being merged
164 * @param distance separation required to satisfy c
165 */
166void Block::merge(Block *b, Constraint *c, double dist) {
167#ifdef LIBVPSC_LOGGING
168 ofstream f(LOGFILE,ios::app);
169 f<<" merging: "<<*b<<"dist="<<dist<<endl;
170#endif
171 c->active=true;
172 //wposn+=b->wposn-dist*b->weight;
173 //weight+=b->weight;
174 for(Vit i=b->vars->begin();i!=b->vars->end();++i) {
175 Variable *v=*i;
176 //v->block=this;
177 //vars->push_back(v);
178 v->offset+=dist;
179 addVariable(v);
180 }
181#ifdef LIBVPSC_LOGGING
182 for(Vit i=vars->begin();i!=vars->end();++i) {
183 Variable *v=*i;
184 f<<" v["<<v->id<<"]: d="<<v->desiredPosition
185 <<" a="<<v->scale<<" o="<<v->offset
186 <<endl;
187 }
188 f<<" AD="<<ps.AD<<" AB="<<ps.AB<<" A2="<<ps.A2<<endl;
189#endif
190 //posn=wposn/weight;
191 //COLA_ASSERT(wposn==ps.AD - ps.AB);
192 posn=(ps.AD - ps.AB) / ps.A2;
193 COLA_ASSERT(__NOTNAN(posn));
194 b->deleted=true;
195}
196
198#ifdef LIBVPSC_LOGGING
199 ofstream f(LOGFILE,ios::app);
200 f<<" merging constraint heaps... "<<endl;
201#endif
202 // We check the top of the heaps to remove possible internal constraints
205 in->merge(b->in);
206#ifdef LIBVPSC_LOGGING
207 f<<" merged heap: "<<*in<<endl;
208#endif
209}
213 out->merge(b->out);
214}
216 Constraint *v = nullptr;
217 std::vector<Constraint*> outOfDate;
218 while (!in->isEmpty()) {
219 v = in->findMin();
220 Block *lb=v->left->block;
221 Block *rb=v->right->block;
222 // rb may not be this if called between merge and mergeIn
223#ifdef LIBVPSC_LOGGING
224 ofstream f(LOGFILE,ios::app);
225 f<<" checking constraint ... "<<*v;
226 f<<" timestamps: left="<<lb->timeStamp<<" right="<<rb->timeStamp<<" constraint="<<v->timeStamp<<endl;
227#endif
228 if(lb == rb) {
229 // constraint has been merged into the same block
230#ifdef LIBVPSC_LOGGING
231 if(v->slack()<0) {
232 f<<" violated internal constraint found! "<<*v<<endl;
233 f<<" lb="<<*lb<<endl;
234 f<<" rb="<<*rb<<endl;
235 }
236#endif
237 in->deleteMin();
238#ifdef LIBVPSC_LOGGING
239 f<<" ... skipping internal constraint"<<endl;
240#endif
241 } else if(v->timeStamp < lb->timeStamp) {
242 // block at other end of constraint has been moved since this
243 in->deleteMin();
244 outOfDate.push_back(v);
245#ifdef LIBVPSC_LOGGING
246 f<<" reinserting out of date (reinsert later)"<<endl;
247#endif
248 } else {
249 break;
250 }
251 }
252 for(Cit i=outOfDate.begin();i!=outOfDate.end();++i) {
253 v=*i;
254 v->timeStamp=blocks->blockTimeCtr;
255 in->insert(v);
256 }
257 if(in->isEmpty()) {
258 v=nullptr;
259 } else {
260 v=in->findMin();
261 }
262 return v;
263}
265 if(out->isEmpty()) return nullptr;
266 Constraint *v = out->findMin();
267 while (v->left->block == v->right->block) {
268 out->deleteMin();
269 if(out->isEmpty()) return nullptr;
270 v = out->findMin();
271 }
272 return v;
273}
275 in->deleteMin();
276#ifdef LIBVPSC_LOGGING
277 ofstream f(LOGFILE,ios::app);
278 f<<"deleteMinInConstraint... "<<endl;
279 f<<" result: "<<*in<<endl;
280#endif
281}
283 out->deleteMin();
284}
285inline bool Block::canFollowLeft(Constraint const* c, Variable const* last) const {
286 return c->left->block==this && c->active && last!=c->left;
287}
288inline bool Block::canFollowRight(Constraint const* c, Variable const* last) const {
289 return c->right->block==this && c->active && last!=c->right;
290}
291
292// computes the derivative of v and the lagrange multipliers
293// of v's out constraints (as the recursive sum of those below.
294// Does not backtrack over u.
295// also records the constraint with minimum lagrange multiplier
296// in min_lm
297double Block::compute_dfdv(Variable* const v, Variable* const u,
298 Constraint *&min_lm) {
299 double dfdv=v->dfdv();
300 for(Cit it=v->out.begin();it!=v->out.end();++it) {
301 Constraint *c=*it;
302 if(canFollowRight(c,u)) {
303 c->lm=compute_dfdv(c->right,v,min_lm);
304 dfdv+=c->lm*c->left->scale;
305 if(!c->equality&&(min_lm==nullptr||c->lm<min_lm->lm)) min_lm=c;
306 }
307 }
308 for(Cit it=v->in.begin();it!=v->in.end();++it) {
309 Constraint *c=*it;
310 if(canFollowLeft(c,u)) {
311 c->lm=-compute_dfdv(c->left,v,min_lm);
312 dfdv-=c->lm*c->right->scale;
313 if(!c->equality&&(min_lm==nullptr||c->lm<min_lm->lm)) min_lm=c;
314 }
315 }
316 return dfdv/v->scale;
317}
318double Block::compute_dfdv(Variable* const v, Variable* const u) {
319 double dfdv = v->dfdv();
320 for(Cit it = v->out.begin(); it != v->out.end(); ++it) {
321 Constraint *c = *it;
322 if(canFollowRight(c,u)) {
323 c->lm = compute_dfdv(c->right,v);
324 dfdv += c->lm * c->left->scale;
325 }
326 }
327 for(Cit it=v->in.begin();it!=v->in.end();++it) {
328 Constraint *c = *it;
329 if(canFollowLeft(c,u)) {
330 c->lm = - compute_dfdv(c->left,v);
331 dfdv -= c->lm * c->right->scale;
332 }
333 }
334 return dfdv/v->scale;
335}
336
337// The top level v and r are variables between which we want to find the
338// constraint with the smallest lm.
339// Similarly, m is initially nullptr and is only assigned a value if the next
340// variable to be visited is r or if a possible min constraint is returned from
341// a nested call (rather than nullptr).
342// Then, the search for the m with minimum lm occurs as we return from
343// the recursion (checking only constraints traversed left-to-right
344// in order to avoid creating any new violations).
345// We also do not consider equality constraints as potential split points
347 Variable* r,
348 Variable* const v,
349 Variable* const u,
350 Constraint* &m,
351 bool desperation=false
352 )
353{
354 for(Cit it(v->in.begin());it!=v->in.end();++it) {
355 Constraint *c=*it;
356 if(canFollowLeft(c,u)) {
357#ifdef LIBVPSC_LOGGING
358 ofstream f(LOGFILE,ios::app);
359 f<<" left split path: "<<*c<<endl;
360#endif
361 if(c->left==r) {
362 if(desperation&&!c->equality) m=c;
363 return true;
364 } else {
365 if(split_path(r,c->left,v,m)) {
366 if(desperation && !c->equality && (!m||c->lm<m->lm)) {
367 m=c;
368 }
369 return true;
370 }
371 }
372 }
373 }
374 for(Cit it(v->out.begin());it!=v->out.end();++it) {
375 Constraint *c=*it;
376 if(canFollowRight(c,u)) {
377#ifdef LIBVPSC_LOGGING
378 ofstream f(LOGFILE,ios::app);
379 f<<" right split path: "<<*c<<endl;
380#endif
381 if(c->right==r) {
382 if(!c->equality) m=c;
383 return true;
384 } else {
385 if(split_path(r,c->right,v,m)) {
386 if(!c->equality && (!m||c->lm<m->lm))
387 m=c;
388 return true;
389 }
390 }
391 }
392 }
393 return false;
394}
395/*
396Block::Pair Block::compute_dfdv_between(
397 Variable* r, Variable* const v, Variable* const u,
398 const Direction dir = NONE, bool changedDirection = false) {
399 double dfdv=v->weight*(v->position() - v->desiredPosition);
400 Constraint *m=nullptr;
401 for(Cit it(v->in.begin());it!=v->in.end();++it) {
402 Constraint *c=*it;
403 if(canFollowLeft(c,u)) {
404 if(dir==RIGHT) {
405 changedDirection = true;
406 }
407 if(c->left==r) {
408 r=nullptr;
409 if(!c->equality) m=c;
410 }
411 Pair p=compute_dfdv_between(r,c->left,v,
412 LEFT,changedDirection);
413 dfdv -= c->lm = -p.first;
414 if(r && p.second)
415 m = p.second;
416 }
417 }
418 for(Cit it(v->out.begin());it!=v->out.end();++it) {
419 Constraint *c=*it;
420 if(canFollowRight(c,u)) {
421 if(dir==LEFT) {
422 changedDirection = true;
423 }
424 if(c->right==r) {
425 r=nullptr;
426 if(!c->equality) m=c;
427 }
428 Pair p=compute_dfdv_between(r,c->right,v,
429 RIGHT,changedDirection);
430 dfdv += c->lm = p.first;
431 if(r && p.second)
432 m = changedDirection && !c->equality && c->lm < p.second->lm
433 ? c
434 : p.second;
435 }
436 }
437 return Pair(dfdv,m);
438}
439*/
440
441// resets LMs for all active constraints to 0 by
442// traversing active constraint tree starting from v,
443// not back tracking over u
444void Block::reset_active_lm(Variable* const v, Variable* const u) {
445 for(Cit it=v->out.begin();it!=v->out.end();++it) {
446 Constraint *c=*it;
447 if(canFollowRight(c,u)) {
448 c->lm=0;
449 reset_active_lm(c->right,v);
450 }
451 }
452 for(Cit it=v->in.begin();it!=v->in.end();++it) {
453 Constraint *c=*it;
454 if(canFollowLeft(c,u)) {
455 c->lm=0;
456 reset_active_lm(c->left,v);
457 }
458 }
459}
460void Block::list_active(Variable* const v, Variable* const u) {
461 for(Cit it=v->out.begin();it!=v->out.end();++it) {
462 Constraint *c=*it;
463 if(canFollowRight(c,u)) {
464#ifdef LIBVPSC_LOGGING
465 ofstream f(LOGFILE,ios::app);
466 f<<" "<<*c<<endl;
467#endif
468 list_active(c->right,v);
469 }
470 }
471 for(Cit it=v->in.begin();it!=v->in.end();++it) {
472 Constraint *c=*it;
473 if(canFollowLeft(c,u)) {
474#ifdef LIBVPSC_LOGGING
475 ofstream f(LOGFILE,ios::app);
476 f<<" "<<*c<<endl;
477#endif
478 list_active(c->left,v);
479 }
480 }
481}
482/*
483 * finds the constraint with the minimum lagrange multiplier, that is, the constraint
484 * that most wants to split
485 */
487 Constraint *min_lm=nullptr;
488 reset_active_lm(vars->front(),nullptr);
489 compute_dfdv(vars->front(),nullptr,min_lm);
490#ifdef LIBVPSC_LOGGING
491 ofstream f(LOGFILE,ios::app);
492 f<<" langrangians: "<<endl;
493 list_active(vars->front(),nullptr);
494#endif
495 return min_lm;
496}
498 reset_active_lm(vars->front(),nullptr);
499 compute_dfdv(vars->front(),nullptr);
500 Constraint *min_lm=nullptr;
501 split_path(rv,lv,nullptr,min_lm);
502#if 0
503 if(min_lm==nullptr) {
504 split_path(rv,lv,nullptr,min_lm,true);
505 }
506#else
507 if(min_lm==nullptr) {
508#ifdef LIBVPSC_DEBUG
509 fprintf(stderr,"Couldn't find split point!\n");
510#endif
512 getActivePathBetween(e.path,lv,rv,nullptr);
513 throw e;
514 }
515 COLA_ASSERT(min_lm!=nullptr);
516#endif
517 return min_lm;
518}
519
520// populates block b by traversing the active constraint tree adding variables as they're
521// visited. Starts from variable v and does not backtrack over variable u.
523 b->addVariable(v);
524 for (Cit c=v->in.begin();c!=v->in.end();++c) {
525 if (canFollowLeft(*c,u))
526 populateSplitBlock(b, (*c)->left, v);
527 }
528 for (Cit c=v->out.begin();c!=v->out.end();++c) {
529 if (canFollowRight(*c,u))
530 populateSplitBlock(b, (*c)->right, v);
531 }
532}
533/*
534 * Returns the active path between variables u and v... not back tracking over w
535 */
537 Variable const* v, Variable const *w) const {
538 if(u==v) return true;
539 for (Cit_const c=u->in.begin();c!=u->in.end();++c) {
540 if (canFollowLeft(*c,w)) {
541 if(getActivePathBetween(path, (*c)->left, v, u)) {
542 path.push_back(*c);
543 return true;
544 }
545 }
546 }
547 for (Cit_const c=u->out.begin();c!=u->out.end();++c) {
548 if (canFollowRight(*c,w)) {
549 if(getActivePathBetween(path, (*c)->right, v, u)) {
550 path.push_back(*c);
551 return true;
552 }
553 }
554 }
555 return false;
556}
557// Search active constraint tree from u to see if there is a directed path to v.
558// Returns true if path is found with all constraints in path having their visited flag
559// set true.
561 if(u==v) return true;
562 for (Cit_const c=u->out.begin();c!=u->out.end();++c) {
563 if(canFollowRight(*c,nullptr)) {
564 if(isActiveDirectedPathBetween((*c)->right,v)) {
565 return true;
566 }
567 }
568 }
569 return false;
570}
572 Constraints& path, Variable const* u, Variable const* v) const {
573 if(u==v) return true;
574 for (Cit_const c=u->out.begin();c!=u->out.end();++c) {
575 if(canFollowRight(*c,nullptr)) {
576 if(getActiveDirectedPathBetween(path,(*c)->right,v)) {
577 path.push_back(*c);
578 return true;
579 }
580 }
581 }
582 return false;
583}
584/*
585 * Block needs to be split because of a violated constraint between vl and vr.
586 * We need to search the active constraint tree between l and r and find the constraint
587 * with min lagrangrian multiplier and split at that point.
588 * Returns the split constraint
589 */
591 Block* &lb, Block* &rb) {
592#ifdef LIBVPSC_LOGGING
593 ofstream f(LOGFILE,ios::app);
594 f<<" need to split between: "<<*vl<<" and "<<*vr<<endl;
595#endif
597#ifdef LIBVPSC_LOGGING
598 f<<" going to split on: "<<*c<<endl;
599#endif
600 if(c!=nullptr) {
601 split(lb,rb,c);
602 deleted = true;
603 }
604 return c;
605}
606
607/*
608 * Creates two new blocks, l and r, and splits this block across constraint c,
609 * placing the left subtree of constraints (and associated variables) into l
610 * and the right into r.
611 */
613 c->active=false;
614 l=new Block(blocks);
615 populateSplitBlock(l,c->left,c->right);
616 //COLA_ASSERT(l->weight>0);
617 r=new Block(blocks);
618 populateSplitBlock(r,c->right,c->left);
619 //COLA_ASSERT(r->weight>0);
620}
621
622/*
623 * Computes the cost (squared euclidean distance from desired positions) of the
624 * current positions for variables in this block
625 */
626double Block::cost() {
627 double c = 0;
628 for (Vit v=vars->begin();v!=vars->end();++v) {
629 double diff = (*v)->position() - (*v)->desiredPosition;
630 c += (*v)->weight * diff * diff;
631 }
632 return c;
633}
634std::ostream& operator <<(std::ostream &os, const Block& b)
635{
636 os<<"Block(posn="<<b.posn<<"):";
637 for(Block::Vit v=b.vars->begin();v!=b.vars->end();++v) {
638 os<<" "<<**v;
639 }
640 if(b.deleted) {
641 os<<" Deleted!";
642 }
643 return os;
644}
645
646}
647
PairNode< T > * insert(const T &x)
Insert item x into the priority queue, maintaining heap order.
void setUpConstraintHeap(PairingHeap< Constraint *, CompareConstraints > *&h, bool in)
Definition block.cpp:123
void populateSplitBlock(Block *b, Variable *v, Variable const *u)
Definition block.cpp:522
void mergeOut(Block *b)
Definition block.cpp:210
bool canFollowRight(Constraint const *c, Variable const *last) const
Definition block.cpp:288
Constraint * findMinLM()
Definition block.cpp:486
PairingHeap< Constraint *, CompareConstraints > * in
Definition block.h:86
void merge(Block *b, Constraint *c, double dist)
Definition block.cpp:166
void mergeIn(Block *b)
Definition block.cpp:197
double compute_dfdv(Variable *const v, Variable *const u)
Definition block.cpp:318
Block(Blocks *blocks, Variable *const v=nullptr)
Definition block.cpp:80
Constraint * splitBetween(Variable *vl, Variable *vr, Block *&lb, Block *&rb)
Definition block.cpp:590
double cost()
Definition block.cpp:626
Constraint * findMinLMBetween(Variable *const lv, Variable *const rv)
Definition block.cpp:497
void setUpOutConstraints()
Definition block.cpp:120
void reset_active_lm(Variable *const v, Variable *const u)
Definition block.cpp:444
bool isActiveDirectedPathBetween(Variable const *u, Variable const *v) const
Definition block.cpp:560
bool canFollowLeft(Constraint const *c, Variable const *last) const
Definition block.cpp:285
long timeStamp
Definition block.h:85
bool deleted
Definition block.h:84
void deleteMinInConstraint()
Definition block.cpp:274
void split(Block *&l, Block *&r, Constraint *c)
Definition block.cpp:612
Variables::iterator Vit
Definition block.h:55
Constraint * findMinOutConstraint()
Definition block.cpp:264
void updateWeightedPosition()
Definition block.cpp:97
void addVariable(Variable *v)
Definition block.cpp:63
bool getActiveDirectedPathBetween(Constraints &path, Variable const *u, Variable const *v) const
Definition block.cpp:571
Constraint * findMinInConstraint()
Definition block.cpp:215
void deleteMinOutConstraint()
Definition block.cpp:282
double posn
Definition block.h:62
std::vector< Constraint * > Constraints
Definition block.h:54
bool getActivePathBetween(Constraints &path, Variable const *u, Variable const *v, Variable const *w) const
Definition block.cpp:536
bool split_path(Variable *, Variable *const, Variable *const, Constraint *&min_lm, bool desperation)
Definition block.cpp:346
Constraints::iterator Cit
Definition block.h:56
~Block(void)
Definition block.cpp:111
PositionStats ps
Definition block.h:65
Variables * vars
Definition block.h:61
Constraints::const_iterator Cit_const
Definition block.h:57
void list_active(Variable *const v, Variable *const u)
Definition block.cpp:460
void setUpInConstraints()
Definition block.cpp:117
Blocks * blocks
Definition block.h:109
PairingHeap< Constraint *, CompareConstraints > * out
Definition block.h:87
long blockTimeCtr
Definition blocks.h:70
A constraint determines a minimum or exact spacing required between two Variable objects.
Definition constraint.h:45
A variable is comprised of an ideal position, final position and a weight.
Definition variable.h:45
Block * block
Definition variable.h:58
Constraints out
Definition variable.h:62
Constraints in
Definition variable.h:61
const double w
Definition conic-4.cpp:19
double c[8][4]
STL namespace.
libvpsc: Variable Placement with Separation Constraints quadratic program solver library.
void addVariable(Variable *const v)
Definition block.cpp:47
std::vector< Constraint * > path
Definition exceptions.h:28