EditPointHandle.vala
/*
Copyright (C) 2012 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 Math;
namespace BirdFont {
public class EditPointHandle : GLib.Object {
public double length;
public unowned EditPoint parent;
public PointType type;
EditPoint? visual_handle = null;
static EditPoint none = new EditPoint ();
public bool active;
public bool selected;
public double angle;
public double x {
get {
double r = px ();
if (unlikely (r <= -100000)) {
print_position ();
move_to (0, 0);
}
return r;
}
set {
move_to_coordinate_internal (value, py ());
if (parent.tie_handles) {
parent.process_tied_handle ();
}
if (parent.reflective_point) {
process_symmetrical_handle ();
}
process_connected_handle ();
}
}
public double y {
get {
double r = py ();
if (unlikely (r <= -100000)) {
print_position ();
move_to (0, 0);
}
return r;
}
set {
move_to_coordinate_internal (px (), value);
if (parent.tie_handles) {
parent.process_tied_handle ();
}
if (parent.reflective_point) {
process_symmetrical_handle ();
}
process_connected_handle ();
}
}
public double independent_x {
get {
double r = px ();
if (unlikely (r <= -100000)) {
print_position ();
move_to (0, 0);
}
return r;
}
set {
move_to_coordinate_internal (value, py ());
}
}
public double independent_y {
get {
double r = py ();
if (unlikely (r <= -100000)) {
print_position ();
move_to (0, 0);
}
return r;
}
set {
move_to_coordinate_internal (px (), value);
}
}
public EditPointHandle.empty() {
this.parent = none;
this.angle = 0;
this.length = 10;
this.type = PointType.NONE;
this.active = false;
this.selected = false;
}
public EditPointHandle (EditPoint parent, double angle, double length) {
this.parent = parent;
this.angle = angle;
this.length = length;
this.type = PointType.LINE_CUBIC;
this.active = false;
this.selected = false;
}
public EditPointHandle copy () {
EditPointHandle n = new EditPointHandle.empty ();
n.angle = angle;
n.length = length;
n.parent = parent;
n.type = type;
n.active = active;
n.selected = selected;
return n;
}
public EditPoint get_parent () {
return parent;
}
public void convert_to_line () {
parent.tie_handles = false;
parent.reflective_point = false;
switch (type) {
case PointType.QUADRATIC:
type = PointType.LINE_QUADRATIC;
break;
case PointType.DOUBLE_CURVE:
type = PointType.LINE_DOUBLE_CURVE;
break;
case PointType.CUBIC:
type = PointType.LINE_CUBIC;
break;
default:
break;
}
}
public bool is_curve () {
switch (type) {
case PointType.LINE_QUADRATIC:
return false;
case PointType.LINE_DOUBLE_CURVE:
return false;
case PointType.LINE_CUBIC:
return false;
}
return true;
}
public void convert_to_curve () {
switch (type) {
case PointType.LINE_QUADRATIC:
type = PointType.QUADRATIC;
break;
case PointType.LINE_DOUBLE_CURVE:
type = PointType.DOUBLE_CURVE;
break;
case PointType.LINE_CUBIC:
type = PointType.CUBIC;
break;
case PointType.QUADRATIC:
break;
case PointType.DOUBLE_CURVE:
break;
case PointType.CUBIC:
break;
default:
warning (@"Type $type");
break;
}
}
public void set_point_type (PointType point_type) {
type = point_type;
}
double px () {
assert ((EditPoint?) parent != null);
return cos (angle) * length + parent.x;
}
double py () {
assert ((EditPoint?) parent != null);
return sin (angle) * length + parent.y;
}
internal void print_position () {
warning (@"\nEdit point handle at position $(px ()),$(py ()) is not valid.\n"
+ @"Type: $(parent.type), "
+ @"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 bool is_left_handle () {
return parent.get_left_handle () == this;
}
public void move_to_coordinate_delta (double dx, double dy) {
move_to_coordinate_internal (px () + dx, py () + dy);
}
public void move_to_coordinate (double x, double y) {
move_to_coordinate_internal (x, y);
if (parent.tie_handles) {
tie_handle ();
}
if (parent.reflective_point) {
tie_handle ();
process_symmetrical_handle ();
}
process_connected_handle ();
}
public void move_to_coordinate_internal (double x, double y) {
double a, b, c;
a = parent.x - x;
b = parent.y - y;
c = a * a + b * b;
if (unlikely(c == 0)) {
angle = 0; // FIXME: this should be a different point type without line handles
length = 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;
}
}
public void process_connected_handle () {
EditPointHandle h;
if (unlikely (type == PointType.NONE)) {
warning ("Invalid type.");
}
if (type == PointType.QUADRATIC) {
if (!is_left_handle ()) {
if (parent.next != null) {
h = parent.get_next ().get_left_handle ();
h.parent.set_reflective_handles (false);
h.parent.set_tie_handle (false);
h.type = PointType.QUADRATIC;
h.move_to_coordinate_internal (x, y);
}
} else {
if (parent.prev != null) {
h = parent.get_prev ().get_right_handle ();
h.parent.set_reflective_handles (false);
h.parent.set_tie_handle (false);
h.type = PointType.QUADRATIC;
h.move_to_coordinate_internal (x, y);
}
}
}
}
public void process_symmetrical_handle () {
if (is_left_handle ()) {
parent.get_right_handle ().length = length;
parent.get_right_handle ().process_connected_handle ();
} else {
parent.get_left_handle ().length = length;
parent.get_left_handle ().process_connected_handle ();
}
process_connected_handle ();
}
public void tie_handle () {
if (is_left_handle ()) {
parent.get_right_handle ().angle = angle - PI;
parent.get_right_handle ().process_connected_handle ();
} else {
parent.get_left_handle ().angle = angle - PI;
parent.get_left_handle ().process_connected_handle ();
}
process_connected_handle ();
}
public void move_delta_coordinate (double dx, double dy) {
double px = px () + dx;
double py = py () + dy;
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);
}
public bool is_line () {
return PenTool.is_line (type);
}
public static double average_angle (double angle1, double angle2) {
EditPointHandle handle = new EditPointHandle (new EditPoint (0, 0, PointType.CUBIC), 0, 1);
double x = (cos (angle1) + cos (angle2)) / 2;
double y = (sin (angle1) + sin (angle2)) / 2;
handle.move_to_coordinate (x, y);
if (fabs (x) < 0.001 && fabs (y) < 0.001) {
return (angle1 + PI / 2) % (2 * PI);
}
return handle.angle;
}
}
}