1 /*
2 Copyright (C) 2016 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 SvgBird {
19
20 public class Points : GLib.Object {
21 public Doubles point_data = new Doubles.for_capacity (100);
22 public bool closed;
23 public delegate bool LineIterator (double start_x, double start_y, double end_x, double end_y, double step, int point_index);
24
25 public int size {
26 get {
27 return point_data.size;
28 }
29 }
30
31 public void set_type (int index, uint32 p) {
32 point_data.set_type (index, p);
33 }
34
35 public void set_double (int index, double p) {
36 point_data.set_double (index, p);
37 }
38
39 public void insert_type (int index, uint32 t) {
40 point_data.insert_type (index, t);
41 }
42
43 public void insert (int index, double p) {
44 point_data.insert (index, p);
45 }
46
47 public void add (double p) {
48 point_data.add (p);
49 }
50
51 public void add_type (uint32 type) {
52 point_data.add_type (type);
53 }
54
55 public int add_cubic (double handle_x, double handle_y,
56 double next_handle_x, double next_handle_y,
57 double x, double y) {
58
59 return insert_cubic (size, handle_x, handle_y,
60 next_handle_x, next_handle_y, x, y);
61 }
62
63 public int add_line (double x, double y) {
64 return insert_line (size, x, y);
65 }
66
67 public int insert_cubic (int position, double handle_x, double handle_y,
68 double next_handle_x, double next_handle_y,
69 double x, double y) {
70
71 int index;
72
73 if (size == 0) {
74 insert_line (0, x, y);
75 }
76
77 index = position;
78 insert_type (index, POINT_CUBIC);
79 insert (index + 1, handle_x);
80 insert (index + 2, handle_y);
81 insert (index + 3, next_handle_x);
82 insert (index + 4, next_handle_y);
83 insert (index + 5, x);
84 insert (index + 6, y);
85 insert (index + 7, 0);
86
87 return index;
88 }
89
90 public int insert_line (int position, double x, double y) {
91 int index = position;
92
93 insert_type (index, POINT_LINE);
94 insert (index + 1, x);
95 insert (index + 2, y);
96 insert (index + 3, 0);
97 insert (index + 4, 0);
98 insert (index + 5, 0);
99 insert (index + 6, 0);
100 insert (index + 7, 0);
101
102 return index;
103 }
104
105 public Points copy () {
106 Points p = new Points ();
107 p.point_data = point_data.copy ();
108 p.closed = closed;
109 return p;
110 }
111
112 public double get_double (int index) {
113 return point_data.get_double (index);
114 }
115
116 public uint32 get_point_type (int index) {
117 return point_data.get_point_type (index);
118 }
119
120 public void set_point_type (int index, uint32 type) {
121 point_data.set_point_type (index, type);
122 }
123
124 public void all (LineIterator iter) {
125 double previous_x;
126 double previous_y;
127
128 PointValue* points = point_data.data;
129 int size = point_data.size;
130
131 return_if_fail (size % 8 == 0);
132
133 SvgPath.get_start (this, out previous_x, out previous_y);
134
135 for (int i = 8; i < size; i += 8) {
136 switch (points[i].type) {
137 case POINT_ARC:
138 double rx = points[i + 1].value;
139 double ry = points[i + 2].value;
140 double rotation = points[i + 3].value;
141 double large_arc = points[i + 4].value;
142 double sweep = points[i + 5].value;
143 double dest_x = points[i + 6].value;
144 double dest_y = points[i + 7].value;
145
146 double angle_start, angle_extent, cx, cy;
147
148 get_arc_arguments (previous_x, previous_y, rx, ry,
149 rotation, large_arc > 0, sweep > 0, dest_x, dest_y,
150 out angle_start, out angle_extent,
151 out cx, out cy);
152
153 const int steps = 50;
154 for (int step = 0; step < steps; step++) {
155 double angle = angle_start + step * (angle_extent / steps);
156 double next_x = cx + cos (angle);
157 double next_y = cy + sin (angle);
158
159 iter (previous_x, previous_y, next_x, next_y, step / steps, i);
160
161 previous_x = next_x;
162 previous_y = next_y;
163 }
164 break;
165 case POINT_CUBIC:
166 all_lines (previous_x, previous_y,
167 points[i + 1].value, points[i + 2].value,
168 points[i + 3].value, points[i + 4].value,
169 points[i + 5].value, points[i + 6].value,
170 iter,
171 i);
172
173 previous_x = points[i + 5].value;
174 previous_y = points[i + 6].value;
175 break;
176 case POINT_LINE:
177 double x = points[i + 1].value;
178 double y = points[i + 2].value;
179
180 iter (previous_x, previous_y, x, y, 1, i);
181
182 previous_x = x;
183 previous_y = y;
184 break;
185 }
186 }
187 }
188
189 private static bool all_lines (double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3,
190 LineIterator iter, int index = 0, double steps = 400) {
191 double px = x1;
192 double py = y1;
193
194 double t;
195
196 double previous_x = px;
197 double previous_y = py;
198
199 for (int i = 0; i < steps; i++) {
200 t = i / steps;
201
202 px = bezier_path (t, x0, x1, x2, x3);
203 py = bezier_path (t, y0, y1, y2, y3);
204
205 if (!iter (previous_x, previous_y, px, py, t, index)) {
206 return false;
207 }
208
209 previous_x = px;
210 previous_y = py;
211 }
212
213 return true;
214 }
215
216 public static double bezier_path (double step, double p0, double p1, double p2, double p3) {
217 double q0, q1, q2;
218 double r0, r1;
219
220 q0 = step * (p1 - p0) + p0;
221 q1 = step * (p2 - p1) + p1;
222 q2 = step * (p3 - p2) + p2;
223
224 r0 = step * (q1 - q0) + q0;
225 r1 = step * (q2 - q1) + q1;
226
227 return step * (r1 - r0) + r0;
228 }
229
230 public static void bezier_vector (double step, double p0, double p1, double p2, double p3, out double a0, out double a1) {
231 double q0, q1, q2;
232
233 q0 = step * (p1 - p0) + p0;
234 q1 = step * (p2 - p1) + p1;
235 q2 = step * (p3 - p2) + p2;
236
237 a0 = step * (q1 - q0) + q0;
238 a1 = step * (q2 - q1) + q1;
239 }
240 }
241
242 }
243
244