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