#ifndef PUTABLE #define PUTABLE #include #include #include "xyz.h" #include "transformation.h" #include "picture.h" using namespace std; #define K 1 class node { const node *const _next; node& operator=(const node& another); //deliberately undefined public: /* Slow start, slow end, fastest in middle. Derivative of this function is the parabola y = x * x, upside down, with vertex at (.5, .25). */ static double f(double x) {return x * x * (3 - 2 * x);} protected: const node *next() const {return _next;} /* Return begin when frameno == 0, end when frameno == nframes - 1. */ static double interpolate(double begin, double end, unsigned frameno, unsigned nframes) { if (frameno >= (nframes == 0 ? 1 : nframes)) { cerr << "node::interpolate(" << frameno << ", " << nframes << ")\n"; exit(EXIT_FAILURE); } #if 0 //linear interpolation const unsigned nf = nframes - 1; return (begin * (nf - frameno) + end * frameno) / nf; #endif const double x = nframes == 1 ? .5 : static_cast(frameno) / (nframes - 1); return begin * f(1 - x) + end * f(x); } void check(unsigned i, unsigned frameno, unsigned nframes) const { if (i > npoints()) { cerr << "node::check, i == " << i << ", npoints() == " << npoints() << "\n"; exit(EXIT_FAILURE); } if (frameno >= (nframes == 0 ? 1 : nframes)) { cerr << "node::check, frameno == " << frameno << ", nframes == " << nframes << "\n"; exit(EXIT_FAILURE); } } public: node(const node *initial_next): _next(initial_next) {} node(const node& another) : _next(another.next() == 0 ? 0 : another.next()->clone()) {} virtual ~node() { if (_next) { delete _next; } } virtual node *clone() const {return new node(*this);} virtual unsigned npoints() const {return 0;} virtual xyz point(unsigned i, unsigned frameno, unsigned nframes) const{ cerr << "node::point(" << i << ", " << frameno << ", " << nframes << ")\n"; exit(EXIT_FAILURE); } }; class putable { const node *next; public: putable(): next(0) {} putable(const putable& another) : next(another.next == 0 ? 0 : another.next->clone()) {} virtual ~putable() { if (next) { delete next; } } putable& operator=(const putable& another) { if (next) { delete next; } next = another.next->clone(); return *this; } virtual putable *clone() const {return new putable(*this);} unsigned npoints() const {next == 0 ? 0 : next->npoints();} virtual xyz point(unsigned i, unsigned frameno, unsigned nframes) const{ if (next == 0) { cerr << "putable::point(" << i << ", " << frameno << ", :" << nframes << ") next == 0\n"; exit(EXIT_FAILURE); } check(i); return next->point(i, frameno, nframes); } void check(unsigned i) const { if (i > npoints()) { cerr << "putable::check(" << i << "), npoints == " << npoints() << "\n"; exit(EXIT_FAILURE); } } putable& line(); putable& square(); putable& filled_square(); putable& circle(); putable& semicircle(); putable& arc(); putable& sphere(); putable& putable_set(const xyz* begin, const xyz* end); putable& scale(double d); putable& scale(double d1, double d2); putable& scale(const xyz& point); putable& scale(const xyz& p1, const xyz& p2); putable& translate(const xyz& point); putable& translate(const xyz& p1, const xyz& p2); putable& xrot(double d); putable& yrot(double d); putable& zrot(double d); putable& xrot(double d1, double d2); putable& yrot(double d1, double d2); putable& zrot(double d1, double d2); putable& rot(const xyz& initial_direction, const xyz& initial_viewpoint, double d); putable& rot(const xyz& initial_direction, const xyz& initial_viewpoint, double d1, double d2); putable& push_back(const putable& p); }; #if K class line: public node { public: line(const node *initial_next): node(initial_next) { if (next()) { cerr << "turning existing putable into a line\n"; exit(EXIT_FAILURE); } } line *clone() const {return new line(*this);} unsigned npoints() const {return 5 * picture::xmax;} xyz point(unsigned i, unsigned frameno, unsigned nframes) const { check(i, frameno, nframes); return static_cast(i) / (npoints() - 1); } }; //Filled unit square (1 x 1), centered at origin class filled_square: public node { static const unsigned n = 2 * picture::width; public: filled_square(const node *initial_next): node(initial_next) { if (next()) { cerr << "turning existing putable into a " "filled square\n"; exit(EXIT_FAILURE); } } filled_square *clone() const {return new filled_square(*this);} unsigned npoints() const {return n * n;} xyz point(unsigned i, unsigned frameno, unsigned nframes) const { check(i, frameno, nframes); const unsigned quotient = i / n; const unsigned remainder = i % n; return xyz( static_cast(quotient) / (n - 1) - .5, static_cast(remainder) / (n - 1) - .5); } }; //Unit square (1 x 1), centered at origin class square: public node { public: square(const node *initial_next): node(initial_next) { if (next()) { cerr << "turning existing putable into a square\n"; exit(EXIT_FAILURE); } } square *clone() const {return new square(*this);} unsigned npoints() const {return 16 * picture::xmax;} xyz point(unsigned i, unsigned frameno, unsigned nframes) const { check(i, frameno, nframes); xyz point; switch (4 * i / npoints()) { case 0: point = xyz(4.0 * i / npoints(), 0); break; case 1: point = xyz(1, 4.0 * i / npoints() - 1); break; case 2: point = xyz(3 - 4.0 * i / npoints(), 1); break; case 3: point = xyz(0, 4 - 4.0 * i / npoints()); break; default: cerr << "square::point(" << i << ", " << frameno << ", " << nframes << ")\n"; exit(EXIT_FAILURE); } return point - xyz(.5, .5); } }; class circle: public node { public: circle(const node *initial_next): node(initial_next) { if (next()) { cerr << "turning existing putable into a circle\n"; exit(EXIT_FAILURE); } } circle *clone() const {return new circle(*this);} unsigned npoints() const {return 10 * picture::xmax;} xyz point(unsigned i, unsigned frameno, unsigned nframes) const { check(i, frameno, nframes); return rtp(1, 2 * pi * i / (npoints() - 1)); } }; class semicircle: public node { static const unsigned n = picture::xmax; public: semicircle(const node *initial_next): node(initial_next) { if (next()) { cerr << "turning existing putable into a circle\n"; exit(EXIT_FAILURE); } } semicircle *clone() const {return new semicircle(*this);} unsigned npoints() const {return n * n;} xyz point(unsigned i, unsigned frameno, unsigned nframes) const { check(i, frameno, nframes); const unsigned quotient = i / n; //0 to n-1 inclusive const unsigned remainder = i % n; //0 to n-1 inclusive const double r = static_cast(quotient) / (n - 1); const double theta = pi * remainder / (n - 1); return rtp(r, theta); } }; class arc: public node { //90 degree arc of radius 1 in 1st quadrant public: arc(const node *initial_next): node(initial_next) { if (next()) { cerr << "turning existing putable into an arc\n"; exit(EXIT_FAILURE); } } arc *clone() const {return new arc(*this);} unsigned npoints() const {return 10 * picture::xmax / 4;} xyz point(unsigned i, unsigned frameno, unsigned nframes) const { check(i, frameno, nframes); return rtp(1, pi * i / (2 * (npoints() - 1))); } }; class sphere: public node { static const unsigned n = 4 * picture::xmax; public: sphere(const node *initial_next): node(initial_next) { if (next()) { cerr << "turning existing putable into a sphere\n"; exit(EXIT_FAILURE); } } sphere *clone() const {return new sphere(*this);} unsigned npoints() const {return n * n;} xyz point(unsigned i, unsigned frameno, unsigned nframes) const { check(i, frameno, nframes); const unsigned quotient = i / n; const unsigned remainder = i % n; const double theta = 2 * pi * remainder / (n - 1); const double phi = pi * quotient / (n - 1); return rtp(1, theta, phi); } }; class putable_set: public node { vector v; public: putable_set(const node *initial_next): node(initial_next) { if (next()) { cerr << "turning existing putable into a sphere\n"; exit(EXIT_FAILURE); } } putable_set(const node *initial_next, const xyz *begin, const xyz *end) : node(initial_next), v(begin, end) {} putable_set *clone() const {return new putable_set(*this);} unsigned npoints() const {return v.size();} xyz point(unsigned i, unsigned frameno, unsigned nframes) const { check(i, frameno, nframes); return v[i]; } }; class scale: public node { const xyz begin; const xyz end; public: scale(const node *initial_next, const xyz& initial_begin) : node(initial_next), begin(initial_begin), end(initial_begin) { if (next() == 0) { cerr << "can't scale(" << begin << ") nothing\n"; exit(EXIT_FAILURE); } } scale(const node *initial_next, const xyz& initial_begin, const xyz& initial_end) : node(initial_next), begin(initial_begin), end(initial_end) { if (next() == 0) { cerr << "can't scale(" << begin << ", " << end << ") nothing\n"; exit(EXIT_FAILURE); } } scale *clone() const {return new scale(*this);} unsigned npoints() const {return next()->npoints();} xyz point(unsigned i, unsigned frameno, unsigned nframes) const { check(i, frameno, nframes); xyz point(next()->point(i, frameno, nframes)); point.x *= interpolate(begin.x, end.x, frameno, nframes); point.y *= interpolate(begin.y, end.y, frameno, nframes); point.z *= interpolate(begin.z, end.z, frameno, nframes); return point; } }; class translate: public node { const xyz begin; const xyz end; public: translate(const node *initial_next, const xyz& initial_begin) : node(initial_next), begin(initial_begin), end(initial_begin) { if (next() == 0) { cerr << "can't translate(" << begin << ") nothing\n"; exit(EXIT_FAILURE); } } translate(const node *initial_put, const xyz& initial_begin, const xyz& initial_end) : node(initial_put), begin(initial_begin), end(initial_end) { if (next() == 0) { cerr << "can't translate(" << begin << ", " << end << ") nothing\n"; exit(EXIT_FAILURE); } } translate *clone() const {return new translate(*this);} unsigned npoints() const {return next()->npoints();} xyz point(unsigned i, unsigned frameno, unsigned nframes) const { check(i, frameno, nframes); xyz point(next()->point(i, frameno, nframes)); return point + xyz( interpolate(begin.x, end.x, frameno, nframes), interpolate(begin.y, end.y, frameno, nframes), interpolate(begin.z, end.z, frameno, nframes) ); } }; template class axis_rotate: public node { const double begin; const double end; public: axis_rotate(const node *initial_next, double initial_begin) : node(initial_next), begin(initial_begin), end(initial_begin) { if (next() == 0) { cerr << "can't axis_rotate(" << begin << ") nothing\n"; exit(EXIT_FAILURE); } } axis_rotate(const node *initial_next, double initial_begin, double initial_end) : node(initial_next), begin(initial_begin), end(initial_end) { if (next() == 0) { cerr << "can't axis_rotate(" << begin << ", " << end << ") nothing\n"; exit(EXIT_FAILURE); } } axis_rotate *clone() const {return new axis_rotate(*this);} unsigned npoints() const {return next()->npoints();} xyz point(unsigned i, unsigned frameno, unsigned nframes) const { check(i, frameno, nframes); xyz point(next()->point(i, frameno, nframes)); return (point.*AXIS)(interpolate(begin, end, frameno, nframes)); } }; typedef axis_rotate<&xyz::xrot> xrot; typedef axis_rotate<&xyz::yrot> yrot; typedef axis_rotate<&xyz::zrot> zrot; class rot: public node { const xyz direction; const xyz viewpoint; const double begin; const double end; public: rot(const node *initial_next, const xyz& initial_direction, const xyz& initial_viewpoint, double initial_begin) : node(initial_next), direction(initial_direction), viewpoint(initial_viewpoint), begin(initial_begin), end(initial_begin) { if (next() == 0) { cerr << "can't rot(" << direction << ", " << viewpoint << ", " << begin << ") nothing\n"; exit(EXIT_FAILURE); } } rot(const node *initial_next, const xyz& initial_direction, const xyz& initial_viewpoint, double initial_begin, double initial_end) : node(initial_next), direction(initial_direction), viewpoint(initial_viewpoint), begin(initial_begin), end(initial_end) { if (next() == 0) { cerr << "can't rot(" << direction << ", " << viewpoint << ", " << begin << ", " << end << ") nothing\n"; exit(EXIT_FAILURE); } } rot *clone() const {return new rot(*this);} unsigned npoints() const {return next()->npoints();} xyz point(unsigned i, unsigned frameno, unsigned nframes) const { check(i, frameno, nframes); xyz point(next()->point(i, frameno, nframes)); return point.rot(direction, viewpoint, interpolate(begin, end, frameno, nframes)); } }; class push_back: public node { const putable *const pushed; public: push_back(const node *initial_next, const putable& initial_pushed) : node(initial_next), pushed(initial_pushed.clone()) {} push_back(const push_back& another) : node(another), pushed(another.pushed->clone()) {} push_back *clone() const {return new push_back(*this);} unsigned npoints() const { return (next() == 0 ? 0 : next()->npoints()) + pushed->npoints(); } xyz point(unsigned i, unsigned frameno, unsigned nframes) const { check(i, frameno, nframes); const unsigned n = next() == 0 ? 0 : next()->npoints(); return i < n ? next()->point(i, frameno, nframes) : pushed->point(i - n, frameno, nframes); } }; inline putable& putable::line() { next = new ::line(next); return *this; } inline putable& putable::filled_square() { next = new ::filled_square(next); return *this; } inline putable& putable::square() { next = new ::square(next); return *this; } inline putable& putable::circle() { next = new ::circle(next); return *this; } inline putable& putable::semicircle() { next = new ::semicircle(next); return *this; } inline putable& putable::arc() { next = new ::arc(next); return *this; } inline putable& putable::sphere() { next = new ::sphere(next); return *this; } inline putable& putable::putable_set(const xyz* begin, const xyz* end) { next = new ::putable_set(next, begin, end); return *this; } inline putable& putable::scale(double d) { scale(xyz(d, d, d)); return *this; } inline putable& putable::scale(double d1, double d2) { scale(xyz(d1, d1, d1), xyz(d2, d2, d2)); return *this; } inline putable& putable::scale(const xyz& point) { next = new ::scale(next, point); return *this; } inline putable& putable::scale(const xyz& p1, const xyz& p2) { next = new ::scale(next, p1, p2); return *this; } inline putable& putable::translate(const xyz& point) { next = new ::translate(next, point); return *this; } inline putable& putable::translate(const xyz& p1, const xyz& p2) { next = new ::translate(next, p1, p2); return *this; } inline putable& putable::xrot(double d) { next = new ::xrot(next, d); return *this; } inline putable& putable::xrot(double d1, double d2) { next = new ::xrot(next, d1, d2); return *this; } inline putable& putable::yrot(double d) { next = new ::yrot(next, d); return *this; } inline putable& putable::yrot(double d1, double d2) { next = new ::yrot(next, d1, d2); return *this; } inline putable& putable::zrot(double d) { next = new ::zrot(next, d); return *this; } inline putable& putable::zrot(double d1, double d2) { next = new ::zrot(next, d1, d2); return *this; } inline putable& putable::rot( const xyz& initial_direction, const xyz& initial_viewpoint, double d) { next = new ::rot(next, initial_direction, initial_viewpoint, d); return *this; } inline putable& putable::rot( const xyz& initial_direction, const xyz& initial_viewpoint, double d1, double d2) { next = new ::rot(next, initial_direction, initial_viewpoint, d1, d2); return *this; } inline putable& putable::push_back(const putable& p) { next = new ::push_back(next, p); return *this; } #endif #if !K class line: public putable { public: line *clone() const {return new line(*this);} unsigned npoints() const {return 4 * picture::xmax;} xyz basepoint(unsigned i, unsigned frameno, unsigned nframes) const { check(i); return static_cast(i) / (npoints() - 1); } }; //Unit square, centered at origin class square: public putable { public: square *clone() const {return new square(*this);} unsigned npoints() const {return 16 * picture::xmax;} xyz basepoint(unsigned i, unsigned frameno, unsigned nframes) const { check(i); xyz point; switch (4 * i / npoints()) { case 0: point = xyz(4.0 * i / npoints(), 0); break; case 1: point = xyz(1, 4.0 * i / npoints() - 1); break; case 2: point = xyz(3 - 4.0 * i / npoints(), 1); break; case 3: point = xyz(0, 4 - 4.0 * i / npoints()); break; default: cerr << "square::basepoint(" << i << ", " << frameno << ", " << nframes << ")\n"; exit(EXIT_FAILURE); } return point - xyz(.5, .5); } }; class circle: public putable { public: circle *clone() const {return new circle(*this);} unsigned npoints() const {return 10 * picture::xmax;} xyz basepoint(unsigned i, unsigned frameno, unsigned nframes) const { check(i); return rtp(1, 2 * pi * i / (npoints() - 1)); } }; class semicircle: public putable { static const unsigned n = picture::xmax; public: semicircle *clone() const {return new semicircle(*this);} unsigned npoints() const {return n * n;} xyz basepoint(unsigned i, unsigned frameno, unsigned nframes) const { check(i); const unsigned quotient = i / n; //0 to n-1 inclusive const unsigned remainder = i % n; //0 to n-1 inclusive const double r = static_cast(quotient) / (n - 1); const double theta = pi * remainder / (n - 1); return rtp(r, theta); } }; class sphere: public putable { static const unsigned n = picture::xmax; public: sphere *clone() const {return new sphere(*this);} unsigned npoints() const {return n * n;} xyz basepoint(unsigned i, unsigned frameno, unsigned nframes) const { check(i); const unsigned quotient = i / n; const unsigned remainder = i % n; const double theta = 2 * pi * remainder / (n - 1); const double phi = pi * quotient / (n - 1); return rtp(1, theta, phi); } }; class putable_vector: public putable { vector v; public: putable_vector() {} ~putable_vector() { for (vector::const_iterator it = v.begin(); it != v.end(); ++it) { delete *it; } } putable_vector& operator=(const putable_vector& another) { if (this != &another) { cerr << "enter putable_vector operator=, size == " << another.v.size() << "\n"; for (vector::const_iterator it = v.begin(); it != v.end(); ++it) { delete *it; } v.clear(); for (vector::const_iterator it = another.v.begin(); it != another.v.end(); ++it){ v.push_back((*it)->clone()); } cerr << "exit putable_vector operator=\n"; } return *this; } putable_vector *clone() const {return new putable_vector(*this);} putable_vector(const putable_vector& another): putable(another) { cerr << "enter putable_vector copy constructor, size == " << another.v.size() << "\n"; for (vector::const_iterator it = another.v.begin(); it != another.v.end(); ++it) { v.push_back((*it)->clone()); } cerr << "exit putable_vector copy constructor\n"; } putable_vector(const putable& p) { push_back(p); } putable_vector(const putable& p1, const putable& p2) { push_back(p1); push_back(p2); } putable_vector& push_back(const putable& p) { v.push_back(p.clone()); return *this; } unsigned npoints() const { unsigned sum = 0; for (vector::const_iterator it = v.begin(); it != v.end(); ++it) { sum += (*it)->npoints(); } //cerr << "putable_vector::npoints() == " << sum << ".\n"; return sum; } xyz basepoint(unsigned i, unsigned frameno, unsigned nframes) const { for (vector::const_iterator it = v.begin(); it != v.end(); ++it) { const unsigned n = (*it)->npoints(); if (i < n) { return (*it)->point(i, frameno, nframes); } i -= n; } } }; class putable_set: public putable { vector v; public: putable_set(const xyz *begin, const xyz *end): v(begin, end) {} putable_set *clone() const {return new putable_set(*this);} unsigned npoints() const {return v.size();} xyz basepoint(unsigned i, unsigned frameno, unsigned nframes) const { return v[i]; } }; #endif template class animator { public: virtual ~animator() {} virtual T operator()(int frameno, int nframes) const = 0; }; template class constant_class: public animator { const T value; public: constant_class(const T& initial_value): value(initial_value) {} T operator()(int frameno, int nframes) const { /* cerr << "constant::class::operator() returns " << value << "\n"; */ return value; } }; template inline constant_class *constant(const T& value) { return new constant_class(value); } template class interpolate_class: public animator { const T begin; const T end; public: interpolate_class(const T& initial_begin, const T& initial_end) : begin(initial_begin), end(initial_end) {} T operator()(int frameno, int nframes) const { return begin + (end - begin) * node::f( static_cast(frameno) / (nframes - 1) ); #if 0 return begin * (nframes - 1 - frameno) / (nframes - 1) + end * frameno / (nframes - 1); #endif } }; template inline interpolate_class *interpolate(const T& begin, const T& end) { return new interpolate_class(begin, end); } //Interpolate by rotating around the X axis. class xarc_class: public animator { const xyz begin; const xyz end; public: xarc_class(const xyz& initial_begin, const xyz& initial_end) : begin(initial_begin), end(initial_end) {} xyz operator()(int frameno, int nframes) const { if (frameno == 0) { return begin; } if (frameno == nframes - 1) { return end; } double begin_theta; if (begin.z == 0 && begin.y == 0) { begin_theta = 0; } else if ((begin_theta = atan2(begin.z, begin.y)) < 0) { begin_theta += 2 * pi; } double end_theta; if (end.z == 0 && end.y == 0) { end_theta = 0; } else if ((end_theta = atan2(end.z, end.y)) < 0) { end_theta += 2 * pi; } const double theta = end_theta - begin_theta; return xyz(begin).xrot(theta * node::f(static_cast(frameno) / (nframes - 1))); } }; inline xarc_class *xarc(const xyz& begin, const xyz& end) { return new xarc_class(begin, end); } #if 0 class translate: public putable { const animator& offset; const putable& put; public: translate(const animator *initial_offset, const putable *initial_put) : offset(*initial_offset), put(*initial_put) {} int npoints() const {return put.npoints();} xyz point(int frameno, int nframes, int i) const { return put.point(frameno, nframes, i) + offset(frameno, nframes); } }; class scale: public putable { const animator& factor; const putable& put; public: scale(const animator *initial_factor, const putable *initial_put) : factor(*initial_factor), put(*initial_put) {} int npoints() const { return put.npoints(); } xyz point(int frameno, int nframes, int i) const { /* cerr << "scale(" << typeid(factor).name() << ", " << typeid(put).name() << ")::point(" << frameno << ", " << nframes << ", " << i << ") == " << put.point(frameno, nframes, i) << ",\n" << "factor(" << frameno << ", " << nframes << ") == "; cerr << factor(frameno, nframes); */ return put.point(frameno, nframes, i) * factor(frameno, nframes); } }; //Rotate around X axis. class xrot: public putable { const animator& theta; const putable& put; public: xrot(const animator *initial_theta, const putable *initial_put) : theta(*initial_theta), put(*initial_put) {} int npoints() const {return put.npoints();} xyz point(int frameno, int nframes, int i) const { return put.point(frameno, nframes, i) .xrot(theta(frameno, nframes)); } }; //Rotate around Y axis. class yrot: public putable { const animator& theta; const putable& put; public: yrot(const animator *initial_theta, const putable *initial_put) : theta(*initial_theta), put(*initial_put) {} int npoints() const {return put.npoints();} xyz point(int frameno, int nframes, int i) const { return put.point(frameno, nframes, i) .yrot(theta(frameno, nframes)); } }; //Rotate around Z axis. class zrot: public putable { const animator& theta; const putable& put; public: zrot(const animator *initial_theta, const putable *initial_put) : theta(*initial_theta), put(*initial_put) {} int npoints() const {return put.npoints();} xyz point(int frameno, int nframes, int i) const { return put.point(frameno, nframes, i) .zrot(theta(frameno, nframes)); } }; //Rotate around the line connecting direction and viewpoint. class rot: public putable { const xyz direction; const xyz viewpoint; const animator& theta; const putable& put; public: rot(const xyz& initial_direction, const xyz& initial_viewpoint, const animator *initial_theta, const putable *initial_put) : direction(initial_direction), viewpoint(initial_viewpoint), theta(*initial_theta), put(*initial_put) {} int npoints() const {return put.npoints();} xyz point(int frameno, int nframes, int i) const { return put.point(frameno, nframes, i) .rot(direction, viewpoint, theta(frameno, nframes)); } }; #endif #endif