.
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 if (quadratic_path.points.size <= 1) {
73 }
74
75 foreach (EditPoint ep in quadratic_path.points) {
76 ep.set_tie_handle (false);
77 ep.set_reflective_handles (false);
78 }
79
80 size = quadratic_path.points.size;
81 segment_start = quadratic_path.get_first_point ();
82 for (int i = 0; i < size; i++) {
83 segment_stop = (i == size -1)
84 ? quadratic_path.get_first_point ()
85 : segment_start.get_next ();
86
87 quadratic_segment_start = segment_start.copy ();
88 quadratic_segment_stop = segment_stop.copy ();
89
90 PenTool.convert_point_segment_type (quadratic_segment_start, quadratic_segment_stop, PointType.DOUBLE_CURVE);
91
92 distance = 0;
93 e = new EditPoint ();
94 if (segment_start.get_right_handle ().is_line ()
95 && segment_stop.get_left_handle ().is_line ()) {
96 // skipping line
97 } else if (points_in_segment >= 10) {
98 warning ("Too many points.");
99 } else {
100 find_largest_distance (segment_start, segment_stop,
101 quadratic_segment_start, quadratic_segment_stop,
102 out distance, out e, out step);
103 }
104
105 if (distance > 0.2) { // range 0.1 - 0.4,
106 quadratic_path.insert_new_point_on_path (e);
107 points_in_segment++;
108 size += 2; // the new point + segment start
109 } else {
110 points_in_segment = 0;
111 segment_start = segment_stop;
112 }
113 }
114 }
115
116 // TODO: Optimize
117 public static void find_largest_distance (EditPoint a0, EditPoint a1, EditPoint b0, EditPoint b1,
118 out double distance, out EditPoint new_point, out double step) {
119 double max_d;
120 double min_d;
121 int steps = (int) (1.6 * Path.get_length_from (a0, a1));
122 double x_out, y_out;
123 double step_out;
124
125 x_out = 0;
126 y_out = 0;
127 step_out = 0;
128 step = 0;
129 distance = 0;
130
131 new_point = new EditPoint ();
132 new_point.prev = a0;
133 new_point.next = a1;
134 new_point.get_right_handle ().type = PointType.CUBIC;
135 new_point.get_left_handle ().type = PointType.CUBIC;
136
137 steps = 20; // FIXME: adjust to length
138
139 if (a0.get_right_handle ().type == PointType.QUADRATIC
140 || a1.get_left_handle ().type == PointType.QUADRATIC
141 || a0.get_right_handle ().type == PointType.LINE_QUADRATIC
142 || a1.get_left_handle ().type == PointType.LINE_QUADRATIC) {
143 return;
144 }
145
146 max_d = Glyph.CANVAS_MIN;
147 min_d = Glyph.CANVAS_MAX;
148 Path.all_of (a0, a1, (xa, ya, ta) => {
149
150 min_d = double.MAX;
151 Path.all_of (b0, b1, (xb, yb, tb) => {
152 double d = Path.distance (xa, xb, ya, yb);
153
154 if (d < min_d) {
155 min_d = d;
156 }
157
158 return true;
159 }, steps);
160
161 if (min_d > max_d) {
162 max_d = min_d;
163 x_out = xa;
164 y_out = ya;
165 step_out = ta;
166 }
167
168 return true;
169 }, steps);
170
171 distance = max_d;
172 new_point.x = x_out;
173 new_point.y = y_out;
174 step = step_out;
175 }
176 }
177
178 }
179