Blob Blame Raw
#ifndef ACTIVECURVE_H
#define ACTIVECURVE_H


#include <vector>

#include "curve.h"
#include "activearea.h"

class ActiveCurvePoint {
public:
	ActivePoint::Handle p, t0, t1;
	
	ActiveCurvePoint(const Vector &p, const Vector &t0, const Vector &t1):
		p(new ActivePoint(p)),
		t0(new ActivePoint(p + t0, Color::yellow())),
		t1(new ActivePoint(p + t1, Color::yellow()))
		{ }
	explicit ActiveCurvePoint(const Vector &p = Vector(), const Vector &t = Vector()):
		ActiveCurvePoint(p, t, t) { }

	const Vector& get_p() const { return p->position; }
	Vector get_t0() const { return t0->position - get_p(); }
	Vector get_t1() const { return t1->position - get_p(); }
	Vector get_pp0() const { return get_t0()/3 + get_p(); }
	Vector get_pp1() const { return get_t1()/3 + get_p(); }
	
	void add_to(ActiveArea &area) const {
		area.add_point(p);
		area.add_point(t0);
		area.add_point(t1);
	}
	void remove_from(ActiveArea &area) const {
		area.remove_point(p);
		area.remove_point(t0);
		area.remove_point(t1);
	}
};

class ActiveCurve: public std::vector<ActiveCurvePoint> {
public:
	template<typename T>
	class SegIterBase {
	public:
		typedef T Type;
	private:
		Type begin, end, last, i0, i1;
		bool loop, valid;
	protected:
		SegIterBase():
			begin(), end(), i0(), i1(), loop(), valid() { }
		SegIterBase(const Type &begin, const Type &end, bool loop):
			begin(begin), end(end), last(end), i0(begin), i1(begin), loop(loop), valid(true)
		{
			--last; ++i1;
			if (i0 == end) i1 = end;
			if (loop && i1 == end) i1 = begin;
		}
	public:
		operator bool () const
			{ return valid && i0 != end && i1 != end; }
		bool is_first() const
			{ assert(*this); return i0 == begin; }
		bool is_last() const
			{ assert(*this); return i1 == (loop ? begin : last); }
	
		bool next() {
			assert(*this);
			++i0; ++i1;
			if (loop && i1 == end) i1 = begin;
			return *this;
		}
		
		const ActiveCurvePoint& point0() const { assert(*this); return *i0; }
		const ActiveCurvePoint& point1() const { assert(*this); return *i1; }
		const Vector& p0() const { return point0().get_p(); }
		const Vector& p1() const { return point1().get_p(); }
	};

	class SegIter: public SegIterBase<const_iterator> {
	public:
		SegIter() { }
		explicit SegIter(const ActiveCurve &curve):
			SegIterBase(curve.begin(), curve.end(), curve.loop) { }
		Vector t0()  const { return point0().get_t1(); }
		Vector t1()  const { return point1().get_t0(); }
		Vector pp0() const { return point0().get_pp1(); }
		Vector pp1() const { return point1().get_pp0(); }
		Hermite hermite() const { return Hermite(p0(), p1(), t0(), -t1()); }
		Bezier  bezier()  const { return Bezier (p0(), p1(), pp0(), pp1()); }
	};

	class SegRIter: public SegIterBase<const_reverse_iterator> {
	public:
		SegRIter() { }
		explicit SegRIter(const ActiveCurve &curve):
			SegIterBase(curve.rbegin(), curve.rend(), curve.loop) { }
		Vector t0()  const { return point0().get_t0(); }
		Vector t1()  const { return point1().get_t1(); }
		Vector pp0() const { return point0().get_pp0(); }
		Vector pp1() const { return point1().get_pp1(); }
		Hermite hermite() const { return Hermite(p0(), p1(), t0(), -t1()); }
		Bezier  bezier()  const { return Bezier (p0(), p1(), pp0(), pp1()); }
	};

	bool loop;
	Color color;
	Color tangent_color;
	
	ActiveCurve(): loop(), color(Color::black()), tangent_color(Color::gray()) { }
	
	void set_tail_tangents_visible(bool x)
		{ if (!empty()) front().t0->visible = back().t1->visible = x; }
	void set_tail_tangents_visible()
		{ set_tail_tangents_visible(loop); }
	
	void put(const Context &context, bool jump = false) const {
		for(SegIter i(*this); i; i.next())
			context.hermite( i.hermite(), jump && i.is_first() );
		if (loop && jump) context->close_path();
	}

	void put_tangents(const Context &context) const {
		for(SegIter i(*this); i; i.next()) {
			context.move_to(i.p0());
			context.line_to(i.p0() + i.t0());
			context.move_to(i.p1());
			context.line_to(i.p1() + i.t1());
		}
	}
	
	void on_point_move(const ActivePoint::Handle &point, const Vector &oldposition, const Vector &/*newposition*/) {
		for(const_iterator i = begin(); i != end(); ++i) {
			if (i->p == point) {
				Vector d = i->p->position - oldposition;
				i->t0->position += d;
				i->t1->position += d;
				break;
			}
		}
	}

	void draw(const Context &context) const {
		context->save();
		
		context.set_line_width_px(1);
		
		context.set_source_rgba(color);
		put_tangents(context);
		context->stroke();

		context.set_source_rgba(tangent_color);
		put(context, true);
		context->stroke();
		
		context->restore();
	}
};


#endif