#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