173
public void draw_boundaries (Context cr, WidgetAllocation allocation, double view_zoom) {
172
public void draw_boundaries (Context cr) {
174
173
double x = Glyph.reverse_path_coordinate_x (xmin);
175
174
double y = Glyph.reverse_path_coordinate_y (ymin);
176
175
double x2 = Glyph.reverse_path_coordinate_x (xmax);
240
public void fill_path (Context cr, WidgetAllocation allocation, double view_zoom) {
239
/** Add all control points for a path to the cairo context.
240
* Call Context.new_path (); before this method and Context.fill ()
243
public void draw_path (Context cr, Color? color = null) {
241
244
unowned List<EditPoint> ep = points;
243
245
unowned EditPoint? n = null;
244
246
unowned EditPoint en;
245
247
unowned EditPoint em;
250
double center_x, center_y;
253
if (points.length () == 0){
257
g = MainWindow.get_current_glyph ();
259
center_x = g.allocation.width / 2.0;
260
center_y = g.allocation.height / 2.0;
262
ex = center_x + points.first ().data.x;
263
ey = center_y - points.first ().data.y;
249
268
foreach (EditPoint e in ep) {
266
285
cr.close_path ();
268
if (is_clockwise ()) {
269
cr.set_source_rgba (80/255.0, 95/255.0, 137/255.0, 0.5);
289
cr.set_source_rgba (c.r, c.g, c.b, c.a);
271
cr.set_source_rgba (144/255.0, 145/255.0, 236/255.0, 0.5);
291
if (is_clockwise ()) {
292
cr.set_source_rgba (80/255.0, 95/255.0, 137/255.0, 0.5);
294
cr.set_source_rgba (144/255.0, 145/255.0, 236/255.0, 0.5);
277
299
private void draw_next (EditPoint e, EditPoint en, Context cr) {
313
335
cr.set_source_rgba (line_color_r, line_color_g, line_color_b, line_color_a);
314
336
cr.set_line_width (stroke_width / g.view_zoom);
316
cr.line_to (xa, ya); // this point makes sense only if it is the first or last position, the other points are meaningless don't export them
338
cr.line_to (xa, ya); // this point makes sense only if it is in the first or last position
318
340
if (t == PointType.QUADRATIC || t == PointType.LINE_QUADRATIC || t == PointType.DOUBLE_CURVE || u == PointType.QUADRATIC || u == PointType.LINE_QUADRATIC || u == PointType.DOUBLE_CURVE) {
319
341
cr.curve_to ((xa + 2 * xb) / 3, (ya + 2 * yb) / 3, (xd + 2 * xb) / 3, (yd + 2 * yb) / 3, xd, yd);
1067
foreach (EditPoint p in points) {
1068
update_region_boundaries_for_point (p);
1091
paths = StrokeTool.get_stroke (this, stroke);
1093
paths = new PathList ();
1097
foreach (Path path in paths.paths) {
1098
foreach (EditPoint p in path.points) {
1099
update_region_boundaries_for_point (p);
1125
1157
/** Add the extra point between line handles for double curve. */
1126
void add_hidden_double_points () requires (points.length () > 1) {
1158
public void add_hidden_double_points () requires (points.length () > 1) {
1127
1159
EditPoint hidden;
1128
1160
unowned List<EditPoint> first = points.last ();
1129
1161
PointType left;
1130
1162
PointType right;
1133
for (unowned List<EditPoint> next = points.first (); !is_null (next); next = next.next ) {
1165
for (unowned List<EditPoint> next = points.first (); !is_null (next); next = next.next) {
1134
1166
left = first.data.get_right_handle ().type;
1135
1167
right = next.data.get_left_handle ().type;
1136
1168
if (right == PointType.DOUBLE_CURVE || left == PointType.DOUBLE_CURVE) {
1141
1173
y = first.data.get_right_handle ().y () + (next.data.get_left_handle ().y () - first.data.get_right_handle ().y ()) / 2;
1143
1175
hidden = new EditPoint (x, y, PointType.QUADRATIC);
1144
hidden.right_handle = next.data.get_left_handle ().copy ();
1176
hidden.right_handle.move_to_coordinate_internal (next.data.get_left_handle ().x(), next.data.get_left_handle ().y());
1145
1177
hidden.get_right_handle ().type = PointType.QUADRATIC;
1179
hidden.get_left_handle ().type = PointType.QUADRATIC;
1146
1180
hidden.type = PointType.QUADRATIC;
1148
1182
first.data.get_right_handle ().type = PointType.QUADRATIC;
1149
1183
first.data.type = PointType.QUADRATIC;
1185
next.data.get_left_handle ().type = PointType.QUADRATIC;
1186
next.data.type = PointType.QUADRATIC;
1151
1188
add_point_after (hidden, first);
1189
if (points.last ().data.get_right_handle ().type == PointType.CUBIC
1190
|| points.first ().data.get_left_handle ().type == PointType.CUBIC) {
1226
if (!is_open () && (points.last ().data.get_right_handle ().type == PointType.CUBIC
1227
|| points.first ().data.get_left_handle ().type == PointType.CUBIC)) {
1191
1228
add_quadratic_points (points.last ().data, points.first ().data);
1193
1230
quadratic_path.add_point (points.last ().data.copy ());
1201
1238
quadratic_path.add_hidden_double_points ();
1203
quadratic_path.close ();
1204
1240
quadratic_path.create_list ();
1205
1241
process_quadratic_handles ();
1207
quadratic_path.close ();
1208
1243
quadratic_path.create_list ();
1209
1244
process_cubic_handles ();
1211
quadratic_path.close ();
1246
foreach (EditPoint ep in quadratic_path.points) {
1247
if (ep.type == PointType.QUADRATIC) {
1248
ep.get_left_handle ().move_to_coordinate (ep.get_prev ().data.get_right_handle ().x (), ep.get_prev ().data.get_right_handle ().y ());
1253
quadratic_path.close ();
1255
quadratic_path.reopen ();
1213
1258
return quadratic_path;
1231
1276
// create quadratic paths
1232
1277
all_of (start, stop, (x, y, step) => {
1233
double prev_step = 0;
1234
EditPoint e = new EditPoint ();
1236
1280
if (step == 1) {
1240
1284
e = new EditPoint (x, y, PointType.QUADRATIC);
1241
1285
added_points++;
1246
quadratic_path.add_point (e);
1289
e = quadratic_path.add_point (e);
1247
1290
prev = quadratic_path.points.last ().data;
1249
prev.type = PointType.LINE_QUADRATIC;
1250
prev.get_left_handle ().type = PointType.LINE_QUADRATIC;
1251
prev.get_right_handle ().type = PointType.LINE_QUADRATIC;
1252
1292
prev.recalculate_linear_handles ();
1254
1294
new_quadratic_points.append (prev);
1355
if (right == PointType.LINE_QUADRATIC && left == PointType.LINE_QUADRATIC) {
1356
ep.get_right_handle ().set_point_type (PointType.LINE_QUADRATIC);
1357
ep.get_left_handle ().set_point_type (PointType.LINE_QUADRATIC);
1358
ep.type = PointType.QUADRATIC;
1359
} else if (right == PointType.LINE_CUBIC && left == PointType.LINE_CUBIC) {
1360
ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);
1361
ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);
1362
ep.type = PointType.LINE_CUBIC;
1363
} else if (right == PointType.LINE_DOUBLE_CURVE && left == PointType.LINE_DOUBLE_CURVE) {
1364
ep.get_right_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE);
1365
ep.get_left_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE);
1366
ep.type = PointType.DOUBLE_CURVE;
1367
} else if (right == PointType.DOUBLE_CURVE || left == PointType.DOUBLE_CURVE) {
1396
if (right == PointType.DOUBLE_CURVE || left == PointType.DOUBLE_CURVE) {
1368
1397
double_bezier_vector (position, start.x, start.get_right_handle ().x (), stop.get_left_handle ().x (), stop.x, out x0, out x1);
1369
1398
double_bezier_vector (position, start.y, start.get_right_handle ().y (), stop.get_left_handle ().y (), stop.y, out y0, out y1);
1371
ep.get_left_handle ().move_to_coordinate (x1, y1);
1372
ep.get_right_handle ().move_to_coordinate (x0, y0);
1374
1400
ep.get_left_handle ().set_point_type (PointType.DOUBLE_CURVE);
1375
1401
ep.get_right_handle ().set_point_type (PointType.DOUBLE_CURVE);
1403
ep.get_left_handle ().move_to_coordinate (x0, y0); // FIXME: SWAPPED?
1404
ep.get_right_handle ().move_to_coordinate (x1, y1);
1376
1406
ep.type = PointType.DOUBLE_CURVE;
1377
1407
} else if (right == PointType.QUADRATIC) {
1378
1408
x0 = quadratic_bezier_vector (1 - position, stop.x, start.get_right_handle ().x (), start.x);
1385
1415
ep.get_left_handle ().move_to_coordinate_internal (0, 0);
1387
1417
ep.type = PointType.QUADRATIC;
1418
} else if (right == PointType.CUBIC || left == PointType.CUBIC) {
1389
1419
bezier_vector (position, start.x, start.get_right_handle ().x (), stop.get_left_handle ().x (), stop.x, out x0, out x1);
1390
1420
bezier_vector (position, start.y, start.get_right_handle ().y (), stop.get_left_handle ().y (), stop.y, out y0, out y1);
1396
1426
ep.get_right_handle ().move_to_coordinate (x1, y1);
1398
1428
ep.type = PointType.LINE_CUBIC;
1429
} else if (right == PointType.LINE_QUADRATIC && left == PointType.LINE_QUADRATIC) {
1430
ep.get_right_handle ().set_point_type (PointType.LINE_QUADRATIC);
1431
ep.get_left_handle ().set_point_type (PointType.LINE_QUADRATIC);
1432
ep.type = PointType.QUADRATIC;
1433
} else if (right == PointType.LINE_CUBIC && left == PointType.LINE_CUBIC) {
1434
ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);
1435
ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);
1436
ep.type = PointType.LINE_CUBIC;
1437
} else if (right == PointType.LINE_DOUBLE_CURVE && left == PointType.LINE_DOUBLE_CURVE) {
1438
ep.get_right_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE);
1439
ep.get_left_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE);
1440
ep.type = PointType.DOUBLE_CURVE;
1401
1443
ep.get_left_handle ().parent = ep;
1402
1444
ep.get_right_handle ().parent = ep;
1537
1579
public static void all_of (EditPoint start, EditPoint stop, RasterIterator iter, int steps = -1) {
1538
PointType right = start.get_right_handle ().type;
1539
PointType left = stop.get_left_handle ().type;
1580
PointType right = PenTool.to_curve (start.get_right_handle ().type);
1581
PointType left = PenTool.to_curve (stop.get_left_handle ().type);
1541
1583
if (steps == -1) {
1542
1584
steps = (int) (10 * get_length_from (start, stop));
1545
1587
if (right == PointType.DOUBLE_CURVE || left == PointType.DOUBLE_CURVE) {
1546
1588
all_of_double (start.x, start.y, start.get_right_handle ().x (), start.get_right_handle ().y (), stop.get_left_handle ().x (), stop.get_left_handle ().y (), stop.x, stop.y, iter, steps);
1547
} else if (right == PointType.QUADRATIC) {
1589
} else if (right == PointType.QUADRATIC && left == PointType.QUADRATIC) {
1548
1590
all_of_quadratic_curve (start.x, start.y, start.get_right_handle ().x (), start.get_right_handle ().y (), stop.x, stop.y, iter, steps);
1591
} else if (right == PointType.CUBIC && left == PointType.CUBIC) {
1550
1592
all_of_curve (start.x, start.y, start.get_right_handle ().x (), start.get_right_handle ().y (), stop.get_left_handle ().x (), stop.get_left_handle ().y (), stop.x, stop.y, iter, steps);
1594
warning (@"Mixed point types in segment $(start.x),$(start.y) to $(stop.x),$(stop.y)");
1595
all_of_quadratic_curve (start.x, start.y, start.get_right_handle ().x (), start.get_right_handle ().y (), stop.x, stop.y, iter, steps);
1702
1747
public static double double_bezier_path (double step, double p0, double p1, double p2, double p3) {
1703
1748
double middle = p1 + (p2 - p1) / 2;
1751
// FIXME: return the middle point
1705
1755
if (step < 0.5) {
1706
1756
return quadratic_bezier_path (2 * step, p0, p1, middle);
1712
1762
public static void double_bezier_vector (double step, double p0, double p1, double p2, double p3, out double a0, out double a1) {
1713
1763
double b0, b1, c0, c1, d0, d1;
1765
if (unlikely (step <= 0 || step >= 1)) {
1766
warning (@"Bad step: $step");
1771
b0 = double_bezier_path (step + 0.00001, p0, p1, p2, p3);
1772
c0 = double_bezier_path (step + 0.00002, p0, p1, p2, p3);
1774
b1 = double_bezier_path (step - 0.00001, p0, p1, p2, p3);
1775
c1 = double_bezier_path (step - 0.00002, p0, p1, p2, p3);
1778
d0 = b0 + (b0 - c0) * 25000 * (step);
1779
d1 = b1 + (b1 - c1) * 25000 * (1 - step);
1716
1784
b0 = double_bezier_path (step - 0.00001, p0, p1, p2, p3);
1717
1785
c0 = double_bezier_path (step - 0.00002, p0, p1, p2, p3);
2278
2349
path.points.first ().data.right_handle.type =
2279
2350
points.last ().data.right_handle.type;
2281
2352
path.points.first ().data.recalculate_linear_handles ();
2282
2353
points.last ().data.recalculate_linear_handles ();
2284
// FIXME: path.points.remove_link (path.points.first ());
2286
2355
// copy remaining points
2287
2356
foreach (EditPoint p in path.points) {
2288
2357
add_point (p.copy ());
2403
2473
get_first_point ().get_left_handle ().convert_to_line ();
2404
2474
get_last_point ().get_right_handle ().convert_to_line ();
2477
public void print_all_types () {
2478
print (@"Control points:\n");
2479
foreach (EditPoint ep in points) {
2480
print (@"$(ep.type) L: $(ep.get_left_handle ().type) R: L: $(ep.get_right_handle ().type)\n");
2484
/** Find the point where two lines intersect. */
2485
public static void find_intersection (double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4,
2486
out double point_x, out double point_y) {
2487
point_x = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4));
2488
point_y = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4));
2491
public static void find_intersection_handle (EditPointHandle h1, EditPointHandle h2, out double point_x, out double point_y) {
2492
find_intersection (h1.parent.x, h1.parent.y, h1.x (), h1.y (), h2.parent.x, h2.parent.y, h2.x (), h2.y (), out point_x, out point_y);
2495
public void add_extrema () {
2496
double x0, y0, x1, y1, x2, y2, x3, y3;
2497
double minx, maxx, miny, maxy;
2499
if (unlikely (points.length () < 2)) {
2500
warning (@"Missing points, $(points.length ()) points in path.");
2518
all_of_path ((x, y) => {
2546
insert_new_point_on_path_at (x0 - 1, y0);
2547
insert_new_point_on_path_at (x1 + 1, y1);
2548
insert_new_point_on_path_at (x2, y2 - 1);
2549
insert_new_point_on_path_at (x3, y3 + 1);
2552
void insert_new_point_on_path_at (double x, double y) {
2553
EditPoint ep = new EditPoint ();
2556
get_closest_point_on_path (ep, x, y);
2558
exists = ep.get_prev ().data.x == ep.x && ep.get_prev ().data.y == ep.y;
2559
exists |= ep.get_next ().data.x == ep.x && ep.get_next ().data.y == ep.y;
2562
insert_new_point_on_path (ep);