summaryrefslogtreecommitdiff
path: root/graphics/asymptote/guide.h
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /graphics/asymptote/guide.h
Initial commit
Diffstat (limited to 'graphics/asymptote/guide.h')
-rw-r--r--graphics/asymptote/guide.h319
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