Clone in a terminal:
Points.vala
/*
	Copyright (C) 2016 Johan Mattsson

	This library is free software; you can redistribute it and/or modify 
	it under the terms of the GNU Lesser General Public License as 
	published by the Free Software Foundation; either version 3 of the 
	License, or (at your option) any later version.

	This library is distributed in the hope that it will be useful, but 
	WITHOUT ANY WARRANTY; without even the implied warranty of 
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
	Lesser General Public License for more details.
*/

using Cairo;
using Math;

namespace SvgBird {

public class Points : GLib.Object {
	public Doubles point_data = new Doubles.for_capacity (100);
	public bool closed;
	public delegate bool LineIterator (double start_x, double start_y, double end_x, double end_y, double step, int point_index);
	
	public int size {
		get {
			return point_data.size;
		}
	}
	
	public void set_type (int index, uint32 p) {
		point_data.set_type (index, p);
	}
	
	public void set_double (int index, double p) {
		point_data.set_double (index, p);
	}

	public void insert_type (int index, uint32 t) {
		point_data.insert_type (index, t);
	}
	
	public void insert (int index, double p) {
		point_data.insert (index, p);
	}
	
	public void add (double p) {
		point_data.add (p);
	}
	
	public void add_type (uint32 type) {
		point_data.add_type (type);
	}

	public int add_cubic (double handle_x, double handle_y,
		double next_handle_x, double next_handle_y,
		double x, double y) {

		return insert_cubic (size, handle_x, handle_y,
			next_handle_x, next_handle_y, x, y);
	}

	public int add_line (double x, double y) {
		return insert_line (size, x, y);
	}

	public int insert_cubic (int position, double handle_x, double handle_y,
		double next_handle_x, double next_handle_y,
		double x, double y) {
		
		int index;
		
		if (size == 0) {
			insert_line (0, x, y);
		}
		
		index = position;
		insert_type (index, POINT_CUBIC);
		insert (index + 1, handle_x);
		insert (index + 2, handle_y);
		insert (index + 3, next_handle_x);
		insert (index + 4, next_handle_y);
		insert (index + 5, x);
		insert (index + 6, y);
		insert (index + 7, 0);

		return index;
	}

	public int insert_line (int position, double x, double y) {
		int index = position;
		
		insert_type (index, POINT_LINE);
		insert (index + 1, x);
		insert (index + 2, y);
		insert (index + 3, 0);
		insert (index + 4, 0);
		insert (index + 5, 0);
		insert (index + 6, 0);
		insert (index + 7, 0);

		return index;
	}
	
	public Points copy () {
		Points p = new Points ();
		p.point_data = point_data.copy ();
		p.closed = closed;
		return p;
	}

	public double get_double (int index) {
		return point_data.get_double (index);
	}

	public uint32 get_point_type (int index) {
		return point_data.get_point_type (index);
	}
	
	public void set_point_type (int index, uint32 type) {
		point_data.set_point_type (index, type);
	}

	public void all (LineIterator iter) {
		double previous_x;
		double previous_y;

		PointValue* points = point_data.data;
		int size = point_data.size;
		
		return_if_fail (size % 8 == 0);
	
		SvgPath.get_start (this, out previous_x, out previous_y);

		for (int i = 8; i < size; i += 8) {
			switch (points[i].type) {
			case POINT_ARC:		
				double rx = points[i + 1].value;
				double ry = points[i + 2].value;
				double rotation = points[i + 3].value;
				double large_arc = points[i + 4].value;
				double sweep = points[i + 5].value;
				double dest_x = points[i + 6].value;
				double dest_y = points[i + 7].value;

				double angle_start, angle_extent, cx, cy;
				
				get_arc_arguments (previous_x, previous_y, rx, ry,
							rotation, large_arc > 0, sweep > 0, dest_x, dest_y,
							out angle_start, out angle_extent,
							out cx, out cy);
							
				const int steps = 50;
				for (int step = 0; step < steps; step++) {
					double angle = angle_start + step * (angle_extent / steps);
					double next_x = cx + cos (angle);
					double next_y = cy + sin (angle);
					
					iter (previous_x, previous_y, next_x, next_y, step / steps, i);
							
					previous_x = next_x;
					previous_y = next_y;
				}
				break;
			case POINT_CUBIC:
				all_lines (previous_x, previous_y,
					points[i + 1].value, points[i + 2].value,
					points[i + 3].value, points[i + 4].value,
					points[i + 5].value, points[i + 6].value,
					iter,
					i);

				previous_x = points[i + 5].value;
				previous_y = points[i + 6].value;
				break;
			case POINT_LINE:
				double x = points[i + 1].value;
				double y = points[i + 2].value;
				
				iter (previous_x, previous_y, x, y, 1, i);
				
				previous_x = x;
				previous_y = y;
				break;
			}
		}
	}
			
	private static bool all_lines (double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3,
			LineIterator iter, int index = 0, double steps = 400) {
		double px = x1;
		double py = y1;
		
		double t;
		
		double previous_x = px;
		double previous_y = py;
		
		for (int i = 0; i < steps; i++) {
			t = i / steps;
			
			px = bezier_path (t, x0, x1, x2, x3);
			py = bezier_path (t, y0, y1, y2, y3);
						
			if (!iter (previous_x, previous_y, px, py, t, index)) {
				return false;
			}
			
			previous_x = px;
			previous_y = py;
		}
		
		return true;
	}

	public static double bezier_path (double step, double p0, double p1, double p2, double p3) {
		double q0, q1, q2;
		double r0, r1;

		q0 = step * (p1 - p0) + p0;
		q1 = step * (p2 - p1) + p1;
		q2 = step * (p3 - p2) + p2;

		r0 = step * (q1 - q0) + q0;
		r1 = step * (q2 - q1) + q1;

		return step * (r1 - r0) + r0;
	}

	public static void bezier_vector (double step, double p0, double p1, double p2, double p3, out double a0, out double a1) {
		double q0, q1, q2;

		q0 = step * (p1 - p0) + p0;
		q1 = step * (p2 - p1) + p1;
		q2 = step * (p3 - p2) + p2;

		a0 = step * (q1 - q0) + q0;
		a1 = step * (q2 - q1) + q1;
	}
}

}

Contact

Email: johan.mattsson.m@gmail.com
Mastodon: typo.social/@birdfont
GitHub: johanmattssonm
LinkedIn: johan-mattsson-2ab324112