1 /*****
2  * flatguide.h
3  * Andy Hammerlindl 2005/02/23
4  *
5  * The data structure that builds up a knotlist.  This is done by calling in
6  * order the methods to set knots, specifiers, and tensions.
7  * Used by the guide solving routines.
8  *
9  * NOTE: figure out how nullpath{}..a should be handled.
10  *****/
11 
12 #ifndef FLATGUIDE_H
13 #define FLATGUIDE_H
14 
15 #include "knot.h"
16 #include "guideflags.h"
17 
18 namespace camp {
19 
20 class flatguide
21 {
22   // A cached solution of the path.  When traversing through a tree of guides,
23   // if a cycle tag is encountered, then the path is solved up to that point.
24   // If the guide continues from there (which rarely occurs in practice), all of
25   // the control points solved are added as control specifiers, and then solved
26   // into a path again.  In the (usual) case that a cycle ends a path, the
27   // cached path avoids this second pass.
28   bool solved;
29 
30   // Used by reverse(guide) to indicate the presence of an unresolved
31   // interior cycle.
32   bool precycle;
33 
34   path p;
35 
36   cvector<knot> nodes;
37 
38   // Information before the first knot.  For a non-cyclic guide, this is
39   // ignored.  For a cyclic guide, it may be useful, but I can't determine a
40   // sensible way to use it yet.
41   tension tout;
42   spec *out;
43 
44   // Information for the next knot to come.
45   tension tin;
46   spec *in;
47 
48   static spec open;
49 
tref(side s)50   tension& tref(side s)
51   {
52     switch (s) {
53       case OUT:
54         return nodes.empty() ? tout : nodes.back().tout;
55       case IN:
56       default:
57         return tin;
58     }
59   }
60 
61   // Returns a reference to a spec* so that it may be assigned.
sref(side s)62   spec*& sref(side s)
63   {
64     switch (s) {
65       case OUT:
66         return nodes.empty() ? out : nodes.back().out;
67       case IN:
68       default:
69         return in;
70     }
71   }
72 
73   void addPre(path& p, Int j);
74   void addPoint(path& p, Int j);
75   void addPost(path& p, Int j);
76 
clearNodes()77   void clearNodes() {
78     nodes.clear();
79     in=&open;
80     tin=tension();
81   }
clearPath()82   void clearPath() {
83     p=path();
84     solved=false;
85   }
86 
87   void uncheckedAdd(path p, bool allowsolve=true);
88 
89   // Sets solved to false, indicating that the path has been updated since last
90   // being solved.  Also, copies a solved path back in as knots and control
91   // specifiers, as it will have to be solved again.
update()92   void update() {
93     if (solved) {
94       solved=false;
95       clearNodes();
96       add(p);
97       clearPath();
98     }
99   }
100 
101 public:
flatguide()102   flatguide()
103     : solved(true), precycle(false), p(), out(&open), in(&open) {}
104 
size()105   Int size() const {
106     return (Int) nodes.size();
107   }
108 
Nodes(Int i)109   knot Nodes(Int i) const {
110     return nodes[i];
111   }
112 
setTension(tension t,side s)113   void setTension(tension t, side s) {
114     update();
115     tref(s)=t;
116   }
setSpec(spec * p,side s)117   void setSpec(spec *p, side s) {
118     assert(p);
119     update();
120     spec *&ref=sref(s);
121     // Control specifiers trump normal direction specifiers.
122     if (!ref || !ref->controlled() || p->controlled())
123       ref=p;
124   }
125 
add(pair z)126   void add(pair z) {
127     update();
128     // Push the pair onto the vector as a knot, using the current in-specifier
129     // and in-tension for the in side for the knot. Use default values for the
130     // out side, as those will be set after the point is added.
131     nodes.push_back(knot(z,in,&open,tin,tension()));
132 
133     // Reset the in-spec and in-tension to defaults;
134     tin=tension();
135     in=&open;
136   }
137 
138   // Reverts to an empty state.
139   void add(path p, bool allowsolve=true) {
140     update();
141     uncheckedAdd(p,allowsolve);
142   }
143 
clear()144   void clear() {
145     clearNodes();
146     clearPath();
147   }
148 
close()149   void close() {
150     if(!nodes.empty()) {
151       nodes.front().in=in;
152       nodes.front().tin=tin;
153     }
154   }
155 
resolvecycle()156   void resolvecycle() {
157     if(!nodes.empty())
158       nodes.push_back(nodes.front());
159   }
160 
precyclic(bool b)161   void precyclic(bool b) {
162     precycle=b;
163   }
164 
precyclic()165   bool precyclic() {
166     return precycle;
167   }
168 
169   // Once all information has been added, release the flat result.
170   simpleknotlist list(bool cycles=false) {
171     if(cycles && !nodes.empty()) close();
172     return simpleknotlist(nodes,cycles);
173   }
174 
175   // Yield a path from the guide as represented here.
176   path solve(bool cycles=false) {
177     if (solved)
178       return p;
179     else {
180       simpleknotlist l=list(cycles);
181       p=camp::solve(l);
182       solved=true;
183       return p;
184     }
185   }
186 };
187 
188 } // namespace camp
189 
190 #endif // FLATGUIDE_H
191