Clone in a terminal:
EditPointHandle.vala
/*
    Copyright (C) 2012 Johan Mattsson

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

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

using Math;

namespace Supplement {
		
public class EditPointHandle  {
	
	public double angle;
	public double length;
	
	public EditPoint parent;
	
	public PointType type;
	
	EditPoint? visual_handle = null;

	static EditPoint none = new EditPoint ();

	public bool active;

	public EditPointHandle.empty() {
		this.parent = none;
		this.angle = 0;
		this.length = 10;
		this.type = PointType.LINE;
		this.active = false;
	}
	
	public EditPointHandle (EditPoint parent, double angle, double length) {
		this.parent = parent;
		this.angle = angle;
		this.length = length;
		this.type = PointType.LINE;
		this.active = false;
	}

	public EditPointHandle copy () {
		EditPointHandle n = new EditPointHandle.empty ();
		n.angle = angle;
		n.length = length;
		n.parent = parent;
		n.type = type;
		n.active = active;
		return n;
	}

	public void set_point_type (PointType point_type) {
		type = point_type;
	}

	public double x () {
		double r = px ();
		
		if (unlikely (r <= -100000)) {
			print_position ();
			move_to (0, 0);
		}
	
		return r;
	}
	
	public double y () {
		double r = py ();
		
		if (unlikely (r <= -100000)) {
			print_position ();
			move_to (0, 0);
		}
		
		return r;
	}
	
	double px () {
		assert ((EditPoint?) parent != null);
		return cos (angle) * length + parent.x;
	}

	double py () {
		assert ((EditPoint?) parent != null);
		return sin (angle) * length + parent.y;
	}
		
	void print_position () {
			warning (@"\nEdit point handle at position $(px ()),$(py ()) is not valid.\n"
				+ @"Type: $(parent.type), "
				+ @"Index: $(parent.get_index ()) of $(parent.get_list ().length ())\n"
				+ @"Angle: $angle, Length: $length.");	
	}
	
	public EditPoint get_point () {
		EditPoint p;
		
		if (visual_handle == null) {
			visual_handle = new EditPoint (0, 0);
		}
	
		p = (!) visual_handle;
		p.x = x ();
		p.y = y ();
		
		return p;
	}
	
	public void move_to_coordinate (double x, double y) {
		double a, b, c;

		a = parent.x - x;
		b = parent.y - y;
		c = a * a + b * b;
		
		if (c == 0) {
			return;
		}
		
		length = sqrt (fabs (c));	
		
		if (c < 0) length = -length;
	
		if (y < parent.y) {
			angle = acos (a / length) + PI;
		} else {
			angle = -acos (a / length) + PI;
		}
		
		if (parent.tie_handles) {
			tie_handle ();
		}
	}

	private void tie_handle () {
		if (this == parent.get_left_handle ()) {
			parent.get_right_handle ().angle = angle - PI;
		} else {
			parent.get_left_handle ().angle = angle - PI;
		}
	}
	
	public void move_delta (double dx, double dy) {
		double px = px () + dx * Glyph.ivz ();
		double py = py () - dy * Glyph.ivz ();
		
		move_to_coordinate (px, py);
	}
	
	public void move_to (double x, double y) {
		EditPoint.to_coordinate (ref x, ref y);
		move_to_coordinate (x, y);
	}
}

}

Contact

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