diff options
Diffstat (limited to 'graphics/asymptote/guide.h')
-rw-r--r-- | graphics/asymptote/guide.h | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/graphics/asymptote/guide.h b/graphics/asymptote/guide.h new file mode 100644 index 0000000000..a25e7ffa9e --- /dev/null +++ b/graphics/asymptote/guide.h @@ -0,0 +1,319 @@ +/***** + * guide.h + * Andy Hammerlindl 2005/02/23 + * + *****/ + +#ifndef GUIDE_H +#define GUIDE_H + +#include <iostream> +#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<guide *> 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 |