.
1 /*
2 Copyright (C) 2014 Johan Mattsson
3
4 This library is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 3 of the
7 License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13 */
14
15 using Cairo;
16 using Math;
17
18 namespace BirdFont {
19
20 /** A class for converting control points. */
21 public class PointConverter {
22
23 Path original_path;
24 Path quadratic_path;
25
26 public PointConverter (Path path) {
27 original_path = path;
28 }
29
30 public Path get_quadratic_path () {
31 bool add_more_points = false;
32
33 quadratic_path = original_path.copy ();
34
35 estimated_cubic_path ();
36
37 if (add_more_points) {
38 warning ("Too many points in segment.");
39 }
40
41 if (quadratic_path.points.size < 2) {
42 return new Path ();
43 }
44
45 foreach (EditPoint e in quadratic_path.points) {
46 if (e.get_right_handle ().type == PointType.CUBIC) {
47 PenTool.convert_point_segment_type (e, e.get_next (), PointType.DOUBLE_CURVE);
48 }
49 }
50
51 if (quadratic_path.get_last_point ().get_right_handle ().type == PointType.CUBIC) {
52 PenTool.convert_point_segment_type (quadratic_path.get_last_point (),
53 quadratic_path.get_first_point (),
54 PointType.DOUBLE_CURVE);
55 }
56
57 quadratic_path.add_hidden_double_points ();
58
59 return quadratic_path;
60 }
61
62 public void estimated_cubic_path () {
63 EditPoint segment_start;
64 EditPoint segment_stop;
65 EditPoint quadratic_segment_start;
66 EditPoint quadratic_segment_stop;
67 EditPoint e;
68 double distance, step;
69 int points_in_segment = 0;
70 int size;
71
72 foreach (EditPoint ep in quadratic_path.points) {
73 ep.set_tie_handle (false);
74 ep.set_reflective_handles (false);
75 }
76
77 size = quadratic_path.points.size;
78 segment_start = quadratic_path.get_first_point ();
79 for (int i = 0; i < size; i++) {
80 segment_stop = (i == size -1)
81 ? quadratic_path.get_first_point ()
82 : segment_start.get_next ();
83
84 quadratic_segment_start = segment_start.copy ();
85 quadratic_segment_stop = segment_stop.copy ();
86
87 PenTool.convert_point_segment_type (quadratic_segment_start, quadratic_segment_stop, PointType.DOUBLE_CURVE);
88
89 distance = 0;
90 e = new EditPoint ();
91 if (segment_start.get_right_handle ().is_line ()
92 && segment_stop.get_left_handle ().is_line ()) {
93 // skipping line
94 } else if (points_in_segment >= 10) {
95 warning ("Too many points.");
96 } else {
97 find_largest_distance (segment_start, segment_stop,
98 quadratic_segment_start, quadratic_segment_stop,
99 out distance, out e, out step);
100 }
101
102 if (distance > 0.2) { // range 0.1 - 0.4,
103 quadratic_path.insert_new_point_on_path (e);
104 points_in_segment++;
105 size += 2; // the new point + segment start
106 } else {
107 points_in_segment = 0;
108 segment_start = segment_stop;
109 }
110 }
111 }
112
113 // TODO: Optimize
114 public static void find_largest_distance (EditPoint a0, EditPoint a1, EditPoint b0, EditPoint b1,
115 out double distance, out EditPoint new_point, out double step) {
116 double max_d;
117 double min_d;
118 int steps = (int) (1.6 * Path.get_length_from (a0, a1));
119 double x_out, y_out;
120 double step_out;
121
122 x_out = 0;
123 y_out = 0;
124 step_out = 0;
125 step = 0;
126 distance = 0;
127
128 new_point = new EditPoint ();
129 new_point.prev = a0;
130 new_point.next = a1;
131 new_point.get_right_handle ().type = PointType.CUBIC;
132 new_point.get_left_handle ().type = PointType.CUBIC;
133
134 steps = 20; // FIXME: adjust to length
135
136 if (a0.get_right_handle ().type == PointType.QUADRATIC
137 || a1.get_left_handle ().type == PointType.QUADRATIC
138 || a0.get_right_handle ().type == PointType.LINE_QUADRATIC
139 || a1.get_left_handle ().type == PointType.LINE_QUADRATIC) {
140 return;
141 }
142
143 max_d = Glyph.CANVAS_MIN;
144 min_d = Glyph.CANVAS_MAX;
145 Path.all_of (a0, a1, (xa, ya, ta) => {
146
147 min_d = double.MAX;
148 Path.all_of (b0, b1, (xb, yb, tb) => {
149 double d = Path.distance (xa, xb, ya, yb);
150
151 if (d < min_d) {
152 min_d = d;
153 }
154
155 return true;
156 }, steps);
157
158 if (min_d > max_d) {
159 max_d = min_d;
160 x_out = xa;
161 y_out = ya;
162 step_out = ta;
163 }
164
165 return true;
166 }, steps);
167
168 distance = max_d;
169 new_point.x = x_out;
170 new_point.y = y_out;
171 step = step_out;
172 }
173 }
174
175 }
176