/***** * guide.h * Andy Hammerlindl 2005/02/23 * *****/ #ifndef GUIDE_H #define GUIDE_H #include #include "knot.h" #include "flatguide.h" #include "settings.h" namespace camp { // Abstract base class for guides. class guide : public gc { protected: public: virtual ~guide() {} // Returns the path that the guide represents. virtual path solve() { return path(); } // Add the information in the guide to the flatguide, so that it can be // solved via the knotlist solving routines. // Returns true if guide has an interior cycle token. virtual void flatten(flatguide&, bool allowsolve=true)=0; virtual bool cyclic() {return false;} virtual void print(ostream& out) const { out << "nullpath"; } // Needed so that multiguide can know where to put in ".." symbols. virtual side printLocation() const { return END; } }; inline ostream& operator<< (ostream& out, const guide& g) { g.print(out); return out; } // Draws dots between two printings of guides, if their locations are such that // the dots are necessary. inline void adjustLocation(ostream& out, side l1, side l2) { if (l1 == END) out << endl; if ((l1 == END || l1 == OUT) && (l2 == IN || l2 == END)) out << ".."; } // A guide representing a pair. class pairguide : public guide { pair z; public: void flatten(flatguide& g, bool=true) { g.add(z); } pairguide(pair z) : z(z) {} path solve() { return path(z); } void print(ostream& out) const { out << z; } side printLocation() const { return END; } }; // A guide representing a path. class pathguide : public guide { path p; public: void flatten(flatguide& g, bool allowsolve=true) { g.add(p,allowsolve); } pathguide(path p) : p(p) {} path solve() { return p; } bool cyclic() {return p.cyclic();} void print(ostream& out) const { out << p; } side printLocation() const { return END; } }; // Tension expressions are evaluated to this class before being cast to a guide, // so that they can be cast to other types (such as guide3) instead. class tensionSpecifier : public gc { double out,in; bool atleast; public: tensionSpecifier(double val, bool atleast=false) : out(val), in(val), atleast(atleast) {} tensionSpecifier(double out, double in, bool atleast=false) : out(out), in(in), atleast(atleast) {} double getOut() const { return out; } double getIn() const { return in; } bool getAtleast() const { return atleast; } }; // A guide giving tension information (as part of a join). class tensionguide : public guide { tension tout,tin; public: void flatten(flatguide& g, bool=true) { g.setTension(tin,IN); g.setTension(tout,OUT); } tensionguide(tensionSpecifier spec) : tout(spec.getOut(), spec.getAtleast()), tin(spec.getIn(), spec.getAtleast()) {} void print(ostream& out) const { out << (tout.atleast ? ".. tension atleast " : ".. tension ") << tout.val << " and " << tin.val << " .."; } side printLocation() const { return JOIN; } }; // Similar to tensionSpecifier, curl expression are evaluated to this type // before being cast to guides. class curlSpecifier : public gc { double value; side s; public: curlSpecifier(double value, side s) : value(value), s(s) {} double getValue() const { return value; } side getSide() const { return s; } }; // A guide giving a specifier. class specguide : public guide { spec *p; side s; public: void flatten(flatguide& g, bool=true) { g.setSpec(p,s); } specguide(spec *p, side s) : p(p), s(s) {} specguide(curlSpecifier spec) : p(new curlSpec(spec.getValue())), s(spec.getSide()) {} void print(ostream& out) const { out << *p; } side printLocation() const { return s; } }; // A guide for explicit control points between two knots. This could be done // with two specguides, instead, but this prints nicer, and is easier to encode. class controlguide : public guide { pair zout, zin; public: void flatten(flatguide& g, bool=true) { g.setSpec(new controlSpec(zout), OUT); g.setSpec(new controlSpec(zin), IN); } controlguide(pair zout,pair zin) : zout(zout),zin(zin) {} controlguide(pair z) : zout(z),zin(z) {} void print(ostream& out) const { out << ".. controls " << zout << " and " << zin << " .."; } side printLocation() const { return JOIN; } }; // A guide that is a sequence of other guides. This is used, for instance is // joins, where we have the left and right guide, and possibly specifiers and // tensions in between. typedef mem::vector guidevector; // A multiguide represents a guide given by the first "length" items of // the vector pointed to by "base". // The constructor, if given another multiguide as a first argument, // will try to avoid allocating a new "base" array. class multiguide : public guide { guidevector *base; size_t length; guide *subguide(size_t i) const { assert(i < length); assert(length <= base->size()); return (*base)[i]; } public: multiguide(guidevector& v); void flatten(flatguide&, bool=true); bool cyclic() { size_t n=length; if(n < 1) return false; return subguide(n-1)->cyclic(); } path solve() { if (settings::verbose>3) { cerr << "solving guide:\n"; print(cerr); cerr << "\n\n"; } flatguide g; this->flatten(g); path p=g.solve(false); if (settings::verbose>3) cerr << "solved as:\n" << p << "\n\n"; return p; } void print(ostream& out) const; side printLocation() const { int n = length; return subguide(n-1)->printLocation(); } }; struct cycleToken : public gc {}; // A guide representing the cycle token. class cycletokguide : public guide { public: void flatten(flatguide& g, bool allowsolve=true) { // If cycles occur in the midst of a guide, the guide up to that point // should be solved as a path. Any subsequent guide will work with that // path locked in place. if(allowsolve) g.solve(true); else g.close(); } bool cyclic() {return true;} path solve() { // Just a cycle on it's own makes an empty guide. return path(); } void print(ostream& out) const { out << "cycle"; } side printLocation() const { return END; } }; } // namespace camp GC_DECLARE_PTRFREE(camp::pairguide); GC_DECLARE_PTRFREE(camp::tensionSpecifier); GC_DECLARE_PTRFREE(camp::tensionguide); GC_DECLARE_PTRFREE(camp::curlSpecifier); GC_DECLARE_PTRFREE(camp::controlguide); GC_DECLARE_PTRFREE(camp::cycleToken); GC_DECLARE_PTRFREE(camp::cycletokguide); #endif // GUIDE_H