Blob Blame Raw
#ifndef CURVE_H
#define CURVE_H


#include <cassert>

#include "vector.h"


class Bezier;

class Hermite {
public:
	Vector p0, p1, t0, t1;

	explicit Hermite(
		const Vector &p0 = Vector(),
		const Vector &p1 = Vector(),
		const Vector &t0 = Vector(),
		const Vector &t1 = Vector()
	):
		p0(p0), p1(p1), t0(t0), t1(t1) { }

	bool operator< (const Hermite &a) const {
		return p0 < a.p0 ? true : a.p0 < p0 ? false
			 : p1 < a.p1 ? true : a.p1 < p1 ? false
			 : t0 < a.t0 ? true : a.t0 < t0 ? false
			 : t1 < a.t1;
	}
	bool operator>(const Hermite &a) const { return a < *this; }
	bool operator==(const Hermite &a) const { return p0 == a.p0 && p1 == a.p1 && t0 == a.t0 && t1 == a.t1; }
	bool operator!=(const Hermite &a) const { return !(*this == a); }

	Vector p(const Real &l) const
		{ return p(l, p0, p1, t0, t1); }
	Vector t(const Real &l) const
		{ return t(l, p0, p1, t0, t1); }

	Vector operator() (const Real &l) const  { return p(l); }
	
	Vector d0(const Real &dl = differential)
		{ return (p(dl) - p0).norm(); }
	Vector d1(const Real &dl = differential)
		{ return (p1 - p(1 - dl)).norm(); }
	
	Vector pp0() const { return p0 + t0/3; }
	Vector pp1() const { return p1 - t1/3; }
	
	Hermite flip() const
		{ return Hermite(p1, p0, -t1, -t0); }
	
	Hermite sub(const Real &a, const Real &b) const {
		Real k = b - a;
		return Hermite(p(a), p(b), t(a)*k, t(b)*k);
	}
	
	void split(const Real &l, Hermite *a, Hermite *b) const {
		if (!a && !b) return;
		assert(a != b);
		Vector point = p(l);
		Vector tangent = t(l);
		Hermite h = *this;
		if (a) {
			a->p0 = h.p0;
			a->t0 = h.t0*l;
			a->p1 = point;
			a->t1 = tangent*l;
		}
		if (b && b != a) {
			Real ll = 1 - l;
			b->p0 = point;
			b->t0 = tangent*ll;
			b->p1 = h.p1;
			b->t1 = h.t1*ll;
		}
	}

	Rect bounds() const
		{ return Rect(p0).expand(p1).expand(pp0()).expand(pp1()); }

	int bends_x(Real *l) const
		{ return bends(l, p0.x, p1.x, t0.x, t1.x); }
	int bends_y(Real *l) const
		{ return bends(l, p0.y, p1.y, t0.y, t1.y); }

	Range bounds_accurate_x() const
		{ return bounds_accurate(p0.x, p1.x, t0.x, t1.x); }
	Range bounds_accurate_y() const
		{ return bounds_accurate(p0.y, p1.y, t0.y, t1.y); }
	Rect bounds_accurate() const
		{ return Rect(bounds_accurate_x(), bounds_accurate_y()); }

	int intersections_x(Real *l, const Real &x) const
		{ return intersections(l, x, p0.x, p1.x, t0.x, t1.x); }
	int intersections_y(Real *l, const Real &y) const
		{ return intersections(l, y, p0.y, p1.y, t0.y, t1.y); }
	
	inline Bezier get_bezier() const;
	
	static Hermite linear(const Vector &p, const Vector &t)
		{ return Hermite(p, p + t, t, t);  }
	static Hermite point(const Vector &p)
		{ return Hermite(p, p);  }

	static Real p(const Real &l, const Real &p0, const Real &p1, const Real &t0, const Real &t1) {
		return p0*((( 2*l - 3)*l    )*l + 1)
			 + p1*(((-2*l + 3)*l    )*l    )
			 + t0*(((   l - 2)*l + 1)*l    )
			 + t1*(((   l - 1)*l    )*l    );
	}

	static Real t(const Real &l, const Real &p0, const Real &p1, const Real &t0, const Real &t1) {
		return p0*(( 6*l - 6)*l    )
			 + p1*((-6*l + 6)*l    )
			 + t0*(( 3*l - 4)*l + 1)
			 + t1*(( 3*l - 2)*l    );
	}

	static Vector p(const Real &l, const Vector &p0, const Vector &p1, const Vector &t0, const Vector &t1)
		{ return Vector( p(l, p0.x, p1.x, t0.x, t1.x), p(l, p0.y, p1.y, t0.y, t1.y) ); }
	static Vector t(const Real &l, const Vector &p0, const Vector &p1, const Vector &t0, const Vector &t1)
		{ return Vector( t(l, p0.x, p1.x, t0.x, t1.x), t(l, p0.y, p1.y, t0.y, t1.y) ); }
	
	static int bends(Real *l, const Real &p0, const Real &p1, const Real &t0, const Real &t1);
	static int intersections(Real *l, const Real &p, const Real &p0, const Real &p1, const Real &t0, const Real &t1);
	static Range bounds_accurate(const Real &p0, const Real &p1, const Real &t0, const Real &t1);
};


class Bezier {
public:
	Vector p0, pp0, pp1, p1;

	Bezier(const Vector &p0, const Vector &p1, const Vector &pp0, const Vector &pp1):
		p0(p0), pp0(pp0), pp1(pp1), p1(p1) { }
	Bezier(const Vector &p0, const Vector &p1, const Vector &pp):
		Bezier(p0, p1, pp, pp) { }
	Bezier(const Vector &p0, const Vector &p1):
		Bezier(p0, p1, (p1 - p0)/3 + p0, (p0 - p1)/3 + p1) { }
	explicit Bezier(const Vector &p = Vector()):
		Bezier(p, p, p, p) { }

	bool operator< (const Bezier &a) const {
		return p0  < a.p0  ? true : a.p0  < p0  ? false
			 : p1  < a.p1  ? true : a.p1  < p1  ? false
			 : pp0 < a.pp0 ? true : a.pp0 < pp0 ? false
			 : pp1 < a.pp1;
	}
	bool operator>(const Bezier &a) const { return a < *this; }
	bool operator==(const Bezier &a) const { return p0 == a.p0 && p1 == a.p1 && pp0 == a.pp0 && pp1 == a.pp1; }
	bool operator!=(const Bezier &a) const { return !(*this == a); }

	Vector p(const Real &l) const
		{ return p(l, p0, p1, pp0, pp1); }
	Vector t(const Real &l) const
		{ return t(l, p0, p1, pp0, pp1); }

	Vector operator() (const Real &l) const  { return p(l); }
	
	Vector d0(const Real &dl = differential)
		{ return (p(dl) - p0).norm(); }
	Vector d1(const Real &dl = differential)
		{ return (p1 - p(1 - dl)).norm(); }
	
	Vector t0() const { return 3*(pp0 - p0); }
	Vector t1() const { return 3*(p1 - pp1); }
	
	void split(const Real &l, Bezier *a, Bezier *b) const {
		if (!a && !b) return;
		assert(a != b);
		Real ll = 1 - l;
		Vector p0 = this->p0;
		Vector p1 = this->p1;
		Vector a0 =  p0*ll + pp0*l;
		Vector a1 = pp0*ll + pp1*l;
		Vector a2 = pp1*ll +  p1*l;
		Vector b0 =  a0*ll +  a1*l;
		Vector b1 =  a1*ll +  a2*l;
		Vector c  =  b0*ll +  b1*l;
		if (a) {
			a->p0  = b->p0;
			a->pp0 = a0;
			a->pp1 = b0;
			a->p1  = c;
		}
		if (b && b != a) {
			b->p0  = c;
			b->pp0 = b1;
			b->pp1 = a2;
			b->p1  = b->p1;
		}
	}
	
	Bezier flip() const
		{ return Bezier(p1, p0, pp1, pp0); }

	Bezier sub(const Real &a, const Real &b) const {
		if (equal(a, b)) return Bezier( p(0.5*(a + b)) );

		Bezier s;
		if (equal(a, 1)) {
			split(b, nullptr, &s);
			return s.flip();
		}
		
		split(a, nullptr, &s);
		s.split((b - a)/(1 - a), &s, nullptr);
		return s;
	}
	
	Rect bounds() const
		{ return Rect(p0).expand(p1).expand(pp0).expand(pp1); }

	int bends_x(Real *l) const
		{ return bends(l, p0.x, p1.x, pp0.x, pp1.x); }
	int bends_y(Real *l) const
		{ return bends(l, p0.y, p1.y, pp0.y, pp1.y); }

	Range bounds_accurate_x() const
		{ return bounds_accurate(p0.x, p1.x, pp0.x, pp1.x); }
	Range bounds_accurate_y() const
		{ return bounds_accurate(p0.y, p1.y, pp0.y, pp1.y); }
	Rect bounds_accurate() const
		{ return Rect(bounds_accurate_x(), bounds_accurate_y()); }

	int intersections_x(Real *l, const Real &x) const
		{ return intersections(l, x, p0.x, p1.x, pp0.x, pp1.x); }
	int intersections_y(Real *l, const Real &y) const
		{ return intersections(l, y, p0.y, p1.y, pp0.y, pp1.y); }

	inline Hermite get_hermite() const;

	static Bezier linear(const Vector &p0, const Vector &p1)
		{ return Bezier(p0, p1, p0 + (p1 - p0)/3, p1 + (p0 - p1)/3);  }
	static Bezier point(const Vector &p)
		{ return Bezier(p, p);  }

	static Real p(const Real &l, const Real &p0, const Real &p1, const Real &pp0, const Real &pp1) {
		Real ll = 1 - l;
		const Real a0 =  p0*ll + pp0*l;
		const Real a1 = pp0*ll + pp1*l;
		const Real a2 = pp1*ll +  p1*l;
		const Real b0 =  a0*ll +  a1*l;
		const Real b1 =  a1*ll +  a2*l;
		return b0*ll +  b1*l;
	}

	static Real t(const Real &l, const Real &p0, const Real &p1, const Real &pp0, const Real &pp1) {
		Real ll = 1 - l;
		const Real a0 =  p0*ll + pp0*l;
		const Real a1 = pp0*ll + pp1*l;
		const Real a2 = pp1*ll +  p1*l;
		const Real b0 =  a0*ll +  a1*l;
		const Real b1 =  a1*ll +  a2*l;
		return 3*(b1 - b0)*l;
	}

	static Vector p(const Real &l, const Vector &p0, const Vector &p1, const Vector &pp0, const Vector &pp1)
		{ return Vector( p(l, p0.x, p1.x, pp0.x, pp1.x), p(l, p0.y, p1.y, pp0.y, pp1.y) ); }
	static Vector t(const Real &l, const Vector &p0, const Vector &p1, const Vector &pp0, const Vector &pp1)
		{ return Vector( t(l, p0.x, p1.x, pp0.x, pp1.x), t(l, p0.y, p1.y, pp0.y, pp1.y) ); }
	
	static int bends(Real *l, const Real &p0, const Real &p1, const Real &pp0, const Real &pp1);
	static int intersections(Real *l, const Real &p, const Real &p0, const Real &p1, const Real &pp0, const Real &pp1);
	static Range bounds_accurate(const Real &p0, const Real &p1, const Real &pp0, const Real &pp1);
};


inline Bezier Hermite::get_bezier() const { return Bezier(p0, p1, pp0(), pp1()); }
inline Hermite Bezier::get_hermite() const { return Hermite(p0, p1, t0(), t1()); }

#endif