Blame c++/vector/bendarea.cpp

145120
145120
#include <vector></vector>
145120
145120
#include "random.h"
145120
#include "matrix.h"
145120
145120
#include "bendarea.h"
145120
145120
145120
namespace {
145120
	enum BendMode {
145120
		BendNone,
145120
		BendRound,
145120
		BendCorner
145120
	};
145120
	
145120
	class ContourChunk {
145120
	public:
145120
		Vector p1, t0, t1;
145120
		explicit ContourChunk(const Vector &p1 = Vector(), const Vector &t0 = Vector(), const Vector &t1 = Vector()):
145120
			p1(p1), t0(t0), t1(t1) { }
145120
	};
145120
145120
	class BendPoint {
145120
	public:
145120
		Vector p;
145120
		Vector t0, t1;
145120
		bool e0, e1;
145120
		Real l;
145120
		Real length;
145120
		BendMode mode;
145120
		BendPoint(): e0(), e1(), l(), length(), mode(BendNone) { }
145120
	};
145120
	
145120
	class Intersection {
145120
	public:
145120
		Real l;
145120
		BendPoint point;
145120
		explicit Intersection(const Real &l = Real(), const BendPoint &point = BendPoint()):
145120
			l(l), point(point) { }
145120
		bool operator<(const Intersection &other) const { return l < other.l; }
145120
	};
145120
	
145120
	typedef std::vector<intersection> IntersectionList;</intersection>
145120
	
145120
	class Contour: public std::vector<contourchunk> {</contourchunk>
145120
	public:
145120
		void hermite_to(const Vector &p1, const Vector &t0, const Vector &t1)
145120
			{ push_back( ContourChunk(p1, t0, t1) ); }
145120
		void line_to(const Vector &p1) {
145120
			if (empty()) {
145120
				hermite_to(p1, Vector(), Vector());
145120
			} else
145120
			if (p1 != back().p1) {
145120
				Vector t = p1 - back().p1;
145120
				hermite_to(p1, t, t);
145120
			}
145120
		}
145120
145120
		void line(const Vector &p0, const Vector &p1)
145120
			{ line_to(p0); line_to(p1); }
145120
		void hermite(const Vector &p0, const Vector &p1, const Vector &t0, const Vector &t1)
145120
			{ line_to(p0); hermite_to(p1, t0, t1); }
145120
		void hermite(const Hermite &h)
145120
			{ hermite(h.p0, h.p1, h.t0, h.t1); }
145120
		
145120
		void put(const Context &context, bool jump = false) const {
145120
			if (empty()) return;
145120
			const_iterator i = begin();
145120
			Hermite h(i->p1, i->p1);
145120
			for(++i; i != end(); ++i) {
145120
				h.p0 = h.p1;
145120
				h.p1 = i->p1;
145120
				h.t0 = i->t0;
145120
				h.t1 = i->t1;
145120
				context.hermite(h, jump);
145120
				jump = false;
145120
			}
145120
		}
145120
	};
145120
	
145120
	class BendContour: public std::vector<bendpoint> {</bendpoint>
145120
	public:
145120
		const_iterator find(const Real &length) const {
145120
			if (empty()) return end();
145120
			const_iterator a = begin(), b = end() - 1, c;
145120
			if (!less(a->length, length)) return a;
145120
			if (!less(length, b->length)) return b;
145120
			while( (c = a + (b - a)/2) != a ) {
145120
				if (equal(length, c->length)) return c;
145120
				(length < c->length ? b : a) = c;
145120
			}
145120
			return c;
145120
		}
145120
		
145120
		const BendPoint* find_exact(const Real &length) const {
145120
			const_iterator i = find(length);
145120
			return i == end() || !equal(length, i->length) ? nullptr : &*i;
145120
		}
145120
145120
		BendPoint interpolate(const Real &length) const {
145120
			const_iterator i = find(length);
145120
			if (i == end()) return BendPoint();
145120
			
145120
			if (equal(length, i->length)) return *i;
145120
			
145120
			if (length < i->length) {
145120
				// extarpolation before
145120
				Real dlen = length - i->length;
145120
				BendPoint s;
145120
				s.p = i->p + i->t0.norm()*dlen;
145120
				s.t0 = s.t1 = i->t0.norm();
145120
				s.l = i->l + dlen;
145120
				s.length = length;
145120
				s.e0 = s.e1 = i->e0;
145120
				return s;
145120
			}
145120
			
145120
			const_iterator j = i + 1;
145120
			if (j == end()) {
145120
				// extarpolation after
145120
				Real dlen = length - i->length;
145120
				BendPoint s;
145120
				s.p = i->p + i->t1.norm()*dlen;
145120
				s.t0 = s.t1 = i->t1.norm();
145120
				s.l = i->l + dlen;
145120
				s.length = length;
145120
				s.e0 = s.e1 = i->e1;
145120
				return s;
145120
			}
145120
			
145120
			// interpolation
145120
			Real l = (length - i->length)/(j->length - i->length);
145120
			Real dl = j->l - i->l;
145120
			Hermite h(i->p, j->p, i->t1*dl, j->t0*dl);
145120
			
145120
			BendPoint s;
145120
			s.p = h.p(l);
145120
			s.t0 = s.t1 = h.t(l);
145120
			s.l = i->l + l*(j->l - i->l);
145120
			s.length = length;
145120
			s.e0 = s.e1 = (i->e1 && j->e0);
145120
			
145120
			return s;
145120
		}
145120
		
145120
		void half_corner(Contour &dst, const BendPoint &point, const Real &radius, bool flip, bool out) const {
145120
			if (point.mode == BendNone || equal(radius, 0))
145120
				return;
145120
145120
			const Vector &t0 = flip ? point.t1 : point.t0;
145120
			const Vector &t1 = flip ? point.t0 : point.t1;
145120
			const Vector p0 = t0.perp().norm() * radius;
145120
			const Vector p1 = t1.perp().norm() * radius;
145120
			const Vector &p = out ? p1 : p0;
145120
			const Vector ¢er = point.p;
145120
			const Real rmod = fabs(radius);
145120
			
145120
			Real d = dot( t0, t1.perp() );
145120
			if (!equal(0, d) && (d*radius < 0) == flip) {
145120
				if (point.mode == BendRound) {
145120
					const Vector pp = (p0 + p1).norm()*rmod;
145120
					const Vector ppp = (p + pp).norm()*rmod;
145120
					Real k = (ppp - p).len()/rmod;
145120
					if (dot(p, ppp.perp()) < 0) k = -k;
145120
					if (out) {
145120
						dst.line_to(center + pp);
145120
						dst.hermite_to(center + ppp, pp.perp()*k, ppp.perp()*k);
145120
						dst.hermite_to(center + p, ppp.perp()*k, p.perp()*k);
145120
					} else {
145120
						k = -k;
145120
						dst.line_to(center + p);
145120
						dst.hermite_to(center + ppp, p.perp()*k, ppp.perp()*k);
145120
						dst.hermite_to(center + pp, ppp.perp()*k, pp.perp()*k);
145120
					}
145120
				} else
145120
				if (point.mode == BendCorner) {
145120
					Vector pp = p0 + t0*(dot(p1 - p0, t1.perp())/d);
145120
					if (out) dst.line(center + pp, center + p);
145120
						else dst.line(center + p, center + pp);
145120
				}
145120
			} else {
145120
				Vector pp = (p0 + p1)*0.5;
145120
				if (out) dst.line(center + pp, center + p);
145120
				    else dst.line(center + p, center + pp);
145120
			}
145120
		}
145120
145120
		void bend(Contour &dst, const Contour &src, const Matrix &matrix = Matrix()) const {
145120
			if (empty() || src.empty())
145120
				dst.insert(dst.end(), src.begin(), src.end());
145120
145120
			Contour::const_iterator i = src.begin();
145120
			Vector p0 = matrix.transform(i->p1);
145120
			size_t dst_size = dst.size();
145120
			
145120
			IntersectionList intersections;
145120
			while(++i != src.end()) {
145120
				Hermite h(p0, matrix.transform(i->p1), matrix.transform(i->t0, false), matrix.transform(i->t1, false));
145120
				p0 = h.p1;
145120
				
145120
				Real bends[2];
145120
				int bends_count = h.bends_x(bends);
145120
				Range r(h.p0.x);
145120
				r.expand(h.p1.x);
145120
				for(Real *i = bends, *end = i + bends_count; i != end; ++i)
145120
					r.expand( Hermite::p(*i, h.p0.x, h.p1.x, h.t0.x, h.t1.x) );
145120
				bool b0 = bends_count > 0, b1 = bends_count > 1;
145120
				
145120
				for(const_iterator bi = find(r.a0); bi != end() && !less(r.a1, bi->length); ++bi) {
145120
					Real roots[3];
145120
					int count = h.intersections_x(roots, bi->length);
145120
					for(Real *i = roots, *end = i + count; i != end; ++i) {
145120
						if (b0 && equal(*i, bends[0])) b0 = false;
145120
						if (b1 && equal(*i, bends[1])) b1 = false;
145120
						intersections.push_back( Intersection(*i, *bi) );
145120
					}
145120
				}
145120
				if (b0) intersections.push_back( Intersection(bends[0], interpolate( h.p(bends[0]).x )) );
145120
				if (b1) intersections.push_back( Intersection(bends[1], interpolate( h.p(bends[1]).x )) );
145120
				sort(intersections.begin(), intersections.end());
145120
				
145120
				Intersection prev(0, interpolate(h.p0.x));
145120
				intersections.push_back( Intersection(1, interpolate(h.p1.x)) );
145120
				
145120
				for(IntersectionList::const_iterator bi = intersections.begin(); bi != intersections.end(); ++bi) {
145120
					const Intersection &next = *bi;
145120
					if (equal(prev.l, next.l)) continue;
145120
145120
					bool flip = next.point.l < prev.point.l;
145120
					Real dl = next.point.l - prev.point.l;
145120
					bool e0 = flip ? prev.point.e0 : prev.point.e1;
145120
					bool e1 = flip ? next.point.e1 : next.point.e0;
145120
					if (e0 && e1) {
145120
						Vector bt0 = flip ? prev.point.t0 : prev.point.t1;
145120
						Vector bt1 = flip ? next.point.t1 : next.point.t0;
145120
						Hermite bh(prev.point.p, next.point.p, bt0*dl, bt1*dl);
145120
						
145120
						Hermite src_h(h.sub(prev.l, next.l));
145120
						Vector x0 = bt0.norm(), y0 = x0.perp();
145120
						Vector x1 = bt1.norm(), y1 = x1.perp();
145120
						Hermite dst_h(
145120
							prev.point.p + y0*src_h.p0.y,
145120
							next.point.p + y1*src_h.p1.y,
145120
							x0*src_h.t0.x + y0*src_h.t0.y,
145120
							x1*src_h.t1.x + y1*src_h.t1.y );
145120
						Real k = (src_h.p1 - src_h.p0).lensqr();
145120
						if (!equal(k, 0)) {
145120
							k = sqrt( (dst_h.p1 - dst_h.p0).lensqr()/k );
145120
							dst_h.t0 *= k;
145120
							dst_h.t1 *= k;
145120
						}
145120
						
145120
						half_corner(dst, prev.point, src_h.p0.y, flip, true);
145120
						dst.hermite(dst_h);
145120
						half_corner(dst, next.point, src_h.p1.y, flip, false);
145120
					}
145120
					
145120
					prev = next;
145120
				}
145120
				
145120
				intersections.clear();
145120
			}
145120
			
145120
			if (dst_size < dst.size())
145120
				dst.line_to(dst[dst_size].p1);
145120
		}
145120
		
145120
		void draw(const Context &context) const {
145120
			if (empty()) return;
145120
			
145120
			context->save();
145120
			
145120
			context.set_line_width_px(10);
145120
			context.set_source_rgba(Color::blue().mulalpha(0.1));
145120
145120
			for(const_iterator i = begin(); i != end(); ++i) {
145120
				context.move_to(i->p + i->t0);
145120
				context.line_to(i->p);
145120
				context.line_to(i->p + i->t1);
145120
			}
145120
			context->stroke();
145120
145120
			context.set_source_rgba(Color::blue().mulalpha(0.25));
145120
			for(const_iterator j = begin(), i = j++; j != end(); i = j++) {
145120
				Real dl = j->l - i->l;
145120
				context.hermite(Hermite(i->p, j->p, i->t1*dl, j->t0*dl), true);
145120
			}
145120
			context->stroke();
145120
145120
			context->restore();
145120
		}
145120
	};
145120
	
145120
	void build_contour(Contour &dst, const ActiveCurve &curve) {
145120
		size_t dst_size = dst.size();
145120
		for(ActiveCurve::SegIter i(curve); i; i.next())
145120
			dst.hermite( i.hermite() );
145120
		if (dst_size < dst.size())
145120
			dst.line_to( dst[dst_size].p1 );
145120
	}
145120
145120
	void build_bend(BendContour &dst, const ActiveCurve &curve, bool round, int segments) {
145120
		const Real dl = Real(1)/segments;
145120
		size_t dst_size = dst.size();
145120
		BendPoint bp;
145120
		BendMode mode = round ? BendRound : BendCorner;
145120
		
145120
		bp.p = curve.front().get_p();
145120
		bp.e0 = bp.e1 = true;
145120
		bp.l = 0;
145120
		bp.length = 0;
145120
		bp.mode = BendNone;
145120
		dst.push_back( bp );
145120
		
145120
		int index = 0;
145120
		for(ActiveCurve::SegIter i(curve); i; i.next()) {
145120
			Hermite h = i.hermite();
145120
			
145120
			dst.back().p = h.p0;
145120
			dst.back().t1 = h.t0;
145120
			
145120
			Real l0 = index;
145120
			Real l = dl;
145120
			bp.mode = BendNone;
145120
			for(int k = 2; k < segments; ++k, l += dl) {
145120
				Vector p = h.p(l);
145120
				bp.l = l0 + l;
145120
				bp.length += (p - bp.p).len();
145120
				bp.p = p;
145120
				bp.t0 = bp.t1 = h.t(l);
145120
				dst.push_back( bp );
145120
			}
145120
			
145120
			bp.l = ++index;
145120
			bp.length += (h.p1 - bp.p).len();
145120
			bp.p = h.p1;
145120
			bp.t0 = h.t1;
145120
			bp.mode = mode;
145120
			dst.push_back( bp );
145120
		}
145120
		
145120
		BendPoint &first = dst[dst_size];
145120
		BendPoint &last = dst.back();
145120
		if (curve.loop) {
145120
			first.mode = mode;
145120
			first.t0 = last.t0;
145120
			last.t1 = first.t1;
145120
			first.e0 = last.e1 = false;
145120
		} else {
145120
			first.t0 = first.t1;
145120
			last.t1 = last.t0;
145120
			last.mode = BendNone;
145120
		}
145120
	}
145120
}
145120
145120
145120
145120
145120
BendArea::BendArea():
145120
	p0(new ActivePoint(Vector(100, 100), Color::blue())),
145120
	p1(new ActivePoint(Vector(900, 100), Color::blue()))
145120
{
145120
	set_size_request(1000, 900);
145120
145120
	add_point(p0);
145120
	add_point(p1);
145120
	
145120
	// generate src curve
145120
	Vector b0 = p0->position - Vector(20, 80);
145120
	Vector b1 = p1->position + Vector(20, 0);
145120
	Vector pp(b0.x, b1.y);
145120
	while(true) {
145120
		Vector pt0 = pp + Vector(-50, 0); //rnd::vector(pp.x - 20, b0.y, pp.x + 20, b1.y);
145120
		Vector pt1 = pp + Vector( 50, 0); //rnd::vector(pp.x - 20, b0.y, pp.x + 20, b1.y);
145120
145120
		src_curve.push_back(ActiveCurvePoint(pp, pt0 - pp, pt1 - pp));
145120
		src_curve.back().add_to(*this);
145120
145120
		if (!less(pp.x, b1.x)) break;
145120
		pp = Vector(pp.x + 60, 0.5*(b0.y + b1.y)); //rnd::vector(pp.x + 20, b0.y, pp.x + 100, b1.y);
145120
		if (!less(pp.x, b1.x)) pp = b1;
145120
	}
145120
	src_curve.set_tail_tangents_visible();
145120
145120
	// generate dst curve
145120
	b0.x = p0->position.x;
145120
	b0.y = p0->position.y + 200;
145120
	b1.x = p1->position.x;
145120
	b1.y = 780;
145120
	Vector bt(100, 100);
145120
	dst_curve.loop = true;
145120
	for(int i = 0; i < 5; ++i) {
145120
		dst_curve.push_back(ActiveCurvePoint(rnd::vector(b0, b1), rnd::vector(-bt, bt), rnd::vector(-bt, bt)));
145120
		dst_curve.back().add_to(*this);
145120
	}
145120
	dst_curve.set_tail_tangents_visible();
145120
}
145120
145120
void BendArea::on_point_move(const ActivePoint::Handle &point, const Vector&oldposition, const Vector &newposition) {
145120
	src_curve.on_point_move(point, oldposition, newposition);
145120
	dst_curve.on_point_move(point, oldposition, newposition);
145120
	if (point == p0 || point == p1)
145120
		p0->position.y = p1->position.y = newposition.y;
145120
}
145120
145120
void BendArea::on_draw_content(const Context &context) {
145120
	context->save();
145120
	
145120
	context.set_line_width_px(1);
145120
	context.set_source_rgba(Color::blue());
145120
	context.move_to(p0->position);
145120
	context.line_to(p1->position);
145120
	context->stroke();
145120
	
145120
	if (!src_curve.empty()) {
145120
		context.set_source_rgba(Color::gray().mulalpha(0.5));
145120
		src_curve.put(context);
145120
		context->close_path();
145120
		context->fill();
145120
145120
		if (!dst_curve.empty()) {
145120
			Contour src_contour;
145120
			BendContour bend;
145120
			Contour dst_contour;
145120
			
145120
			build_contour(src_contour, src_curve);
145120
			build_bend(bend, dst_curve, false, 16);
145120
			
145120
			bend.draw(context);
145120
			
145120
			Real dst_length = bend.back().length;
145120
			Real src_length = p1->position.x - p0->position.x;
145120
			if (!equal(src_length, 0) && !equal(dst_length, 0)) {
145120
				Real kx = dst_length/src_length;
145120
				Real ky = 1;
145120
				Matrix m( kx, 0, 0,
145120
						  0, ky, 0,
145120
						  -kx*p0->position.x, -ky*p0->position.y, 1 );
145120
				bend.bend(dst_contour, src_contour, m);
145120
				
145120
				dst_contour.put(context, true);
145120
				context->close_path();
145120
				context->fill_preserve();
145120
				context.set_source_rgba(Color::blue());
145120
				context->stroke();
145120
			}
145120
		}
145120
	}
145120
	
145120
	context->restore();
145120
	
145120
	src_curve.draw(context);
145120
	dst_curve.draw(context);
145120
}
145120