/*****
* 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 practice), 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;
// Used by reverse(guide) to indicate the presence of an unresolved
// interior cycle.
bool precycle;
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);
// 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();
}
}
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, bool allowsolve=true) {
update();
uncheckedAdd(p,allowsolve);
}
// Once all information has been added, release the flat result.
simpleknotlist list(bool cycles=false) {
if(cycles && !nodes.empty()) close();
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;
}
}
};