The Birdfont Source Code


All Repositories / birdfont.git / commitdiff – RSS feed

Parse arc instructions in SVG paths

These changes was commited to the Birdfont repository Wed, 13 Jan 2016 22:50:56 +0000.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git
[Wed, 13 Jan 2016 22:50:56 +0000]

Updated Files

libbirdfont/EmbeddedSvg.vala
libbirdfont/MoveTool.vala
libbirdfont/OverViewItem.vala
libbirdfont/SvgParser.vala
libsvgbird/BezierPoints.vala
libsvgbird/Doubles.vala
libsvgbird/Ellipse.vala
libsvgbird/Points.vala
libsvgbird/Polygon.vala
libsvgbird/Polyline.vala
libsvgbird/SvgArc.vala
libsvgbird/SvgFile.vala
libsvgbird/SvgPath.vala
libsvgbird/SvgStyle.vala
--- a/libbirdfont/EmbeddedSvg.vala +++ b/libbirdfont/EmbeddedSvg.vala @@ -71,11 +71,7 @@ drawing.update_region_boundaries (); } - // FIXME: handle this in SVG library instead public override bool is_over (double x, double y) { - print (@" $(this.x) <= $(x) <= $(this.x) + $(drawing.width)"); - print (@" $(this.y) <= $(y) <= $(this.y) + $(drawing.height)"); - return (this.x <= x <= this.x + drawing.width) && (this.y - drawing.height <= y <= this.y); }
--- a/libbirdfont/MoveTool.vala +++ b/libbirdfont/MoveTool.vala @@ -492,8 +492,6 @@ double w = Math.fabs (selection_x - last_x); double h = Math.fabs (selection_y - last_y); - - Glyph glyph = MainWindow.get_current_glyph (); cr.save (); Theme.color (cr, "Foreground 1");
--- a/libbirdfont/OverViewItem.vala +++ b/libbirdfont/OverViewItem.vala @@ -99,7 +99,6 @@ } Glyph g; - Font font; double gx, gy; double x1, x2, y1, y2; double scale_box;
--- a/libbirdfont/SvgParser.vala +++ b/libbirdfont/SvgParser.vala @@ -1016,6 +1016,36 @@ } } + public void get_bezier_points (string svg_data, out BezierPoints[] instructions, out int points, bool svg_glyph) { + SvgFile.get_bezier_points (svg_data, out instructions, out points, svg_glyph); + Gee.ArrayList<BezierPoints> bezier_points = new Gee.ArrayList<BezierPoints> (); + BezierPoints[] arc_data = new BezierPoints[8]; + + for (int i = 0; i < points; i++) { + if (instructions[i].type == 'A') { + int arc_index = 0; + + add_arc_points (arc_data, ref arc_index, + instructions[i].x0, instructions[i].y0, + instructions[i].rx, instructions[i].ry, + instructions[i].angle, + instructions[i].large_arc, + instructions[i].sweep, + instructions[i].x1, instructions[i].y1); + + for (int j = 0; j < arc_index; j++) { + bezier_points.add (instructions[j]); + } + } + + bezier_points.add (instructions[i]); + } + + instructions = new BezierPoints[bezier_points.size]; + for (int i = 0; i < bezier_points.size; i++) { + instructions[i] = bezier_points.get (i); + } + } /** * @param d svg data * @param glyph use lines from this glyph but don't add the generated paths @@ -1517,7 +1547,6 @@ public static EmbeddedSvg parse_embedded_svg_file (string path) { string xml_data; - SvgFile svg_file = new SvgFile (); try { FileUtils.get_contents (path, out xml_data); @@ -1532,7 +1561,6 @@ public static EmbeddedSvg parse_embedded_svg_data (string xml_data) { XmlTree tree = new XmlTree (xml_data); - XmlElement tag = tree.get_root (); SvgDrawing drawing = new SvgDrawing (); SvgFile svg_file = new SvgFile (); @@ -1543,7 +1571,56 @@ return svg; } + /** Convert an SVG arc instruction to a Beziér path. */ + public static void add_arc_points (BezierPoints[] bezier_points, ref int bi, + double x0, double y0, double rx, double ry, double angle, + bool largeArcFlag, bool sweepFlag, double x, double y) { + + double angleStart, angleExtent; + double s, step, theta; + double cx, cy; + + cx = 0; + cy = 0; + + // Approximate the path with Beziér points + SvgBird.get_arc_arguments (x0, y0, rx, ry, angle, largeArcFlag, sweepFlag, x, y, + out angleStart, out angleExtent, out cx, out cx); + + s = (angleExtent > 0) ? 1 : -1; + step = fabs (angleExtent) / (2 * fabs (angleExtent)); + + theta = PI - angleStart - angleExtent; + + bezier_points[bi].type = 'C'; + bezier_points[bi].svg_type = 'a'; + + bezier_points[bi].x0 = cx + rx * cos (theta); + bezier_points[bi].y0 = cy + ry * sin (theta); + + bi++; + + for (double a = 0; a < fabs (angleExtent); a += step) { + theta = PI - angleStart - angleExtent + s * a; + + return_if_fail (0 <= bi < bezier_points.length); + + bezier_points[bi].type = 'S'; + bezier_points[bi].svg_type = 'a'; + + bezier_points[bi].x0 = cx + rx * cos (theta); + bezier_points[bi].y0 = cy + ry * sin (theta); + + bezier_points[bi].x1 = cx + rx * cos (theta + 1 * step / 4); + bezier_points[bi].y1 = cy + ry * sin (theta + 1 * step / 4); + + bezier_points[bi].x2 = cx + rx * cos (theta + 2 * step / 4); + bezier_points[bi].y2 = cy + ry * sin (theta + 2 * step / 4); + + bi++; + } + } } }
--- a/libsvgbird/BezierPoints.vala +++ b/libsvgbird/BezierPoints.vala @@ -1,5 +1,5 @@ /* - Copyright (C) 2014 Johan Mattsson + Copyright (C) 2014 2016 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 @@ -24,11 +24,23 @@ public double y1 = 0; public double x2 = 0; public double y2 = 0; - + + // arc arguments + public double rx = 0; + public double ry = 0; + public double angle = 0; + public bool large_arc = false; + public bool sweep = false; + // the arc instructions begins at x0, y0 and ends at x1, x1 + public string to_string () { + if (svg_type == 'A' || svg_type == 'a') { + return @"SVG type:$((!) svg_type.to_string ()) $x0,$y0 $x1,$y1 rx=$rx, ry=$ry, angle=$angle, large_arc=$large_arc, sweep=$sweep)"; + } + return @"$((!)type.to_string ()) $x0,$y0 $x1,$y1 $x2,$y2 SVG:$((!)svg_type.to_string ())"; } } }
--- a/libsvgbird/Doubles.vala +++ b/libsvgbird/Doubles.vala @@ -17,12 +17,12 @@ namespace SvgBird { public class Doubles : GLib.Object { - public double* data; + public PointValue* data; public int size = 0; int capacity = 10; public Doubles () { - data = new double[capacity]; + data = new PointValue[capacity]; } ~Doubles () { @@ -31,21 +31,34 @@ } public Doubles.for_capacity (int capacity) { - data = new double[capacity]; + data = new PointValue[capacity]; this.capacity = capacity; } - - public void add(double d) { + + void increase_capacity () { + int new_capacity = 2 * capacity; + PointValue* new_data = new PointValue[new_capacity]; + Posix.memcpy (new_data, data, sizeof (PointValue) * size); + delete data; + data = new_data; + capacity = new_capacity; + } + + public void add_type (uchar type) { + if (size >= capacity) { + increase_capacity (); + } + + data[size].type = type; + size++; + } + + public void add (double d) { if (size >= capacity) { - int new_capacity = 2 * capacity; - double* new_data = new double[new_capacity]; - Posix.memcpy (new_data, data, sizeof (double) * size); - delete data; - data = new_data; - capacity = new_capacity; + increase_capacity (); } - data[size] = d; + data[size].value = d; size++; } @@ -55,7 +68,7 @@ d.data = new double[capacity]; d.capacity = capacity; d.size = size; - Posix.memcpy (d.data, data, sizeof (double) * size); + Posix.memcpy (d.data, data, sizeof (PointValue) * size); return d; } @@ -70,10 +83,10 @@ return 0; } - return data[index]; + return data[index].value; } } }
--- a/libsvgbird/Ellipse.vala +++ b/libsvgbird/Ellipse.vala @@ -13,6 +13,7 @@ */ using Cairo; + using Math; namespace SvgBird { @@ -40,10 +41,10 @@ public override void draw (Context cr) { cr.save (); - cr.translate (cx + rx, cy + ry); + cr.translate (cx, cy); cr.scale (rx, ry); - cr.arc (0, 0, 1, 0, 2 * Math.PI); - cr.restore (); + cr.arc (0, 0, 1, 0, 2 * PI); + cr.restore (); cr.save (); apply_transform (cr);
--- a/libsvgbird/Points.vala +++ b/libsvgbird/Points.vala @@ -24,9 +24,13 @@ public void add (double p) { point_data.add (p); + } + + public void add_type (uchar type) { + point_data.add_type (type); } } }
--- a/libsvgbird/Polygon.vala +++ b/libsvgbird/Polygon.vala @@ -33,10 +33,10 @@ apply_transform (cr); if (points.size > 2) { - cr.move_to (points.data[0], points.data[1]); + cr.move_to (points.data[0].value, points.data[1].value); for (int i = 2; i < points.size - 1; i += 2) { - cr.line_to (points.data[i], points.data[i + 1]); + cr.line_to (points.data[i].value, points.data[i + 1].value); } cr.close_path ();
--- a/libsvgbird/Polyline.vala +++ b/libsvgbird/Polyline.vala @@ -33,10 +33,10 @@ apply_transform (cr); if (points.size > 2) { - cr.move_to (points.data[0], points.data[1]); + cr.move_to (points.data[0].value, points.data[1].value); for (int i = 2; i < points.size - 1; i += 2) { - cr.line_to (points.data[i], points.data[i + 1]); + cr.line_to (points.data[i].value, points.data[i + 1].value); } }
--- a/libsvgbird/SvgArc.vala +++ b/libsvgbird/SvgArc.vala @@ -41,27 +41,30 @@ namespace SvgBird { - /** Convert an SVG arc instruction to a Beziér path. */ - static void add_arc_points (BezierPoints[] bezier_points, ref int bi, double x0, double y0, double rx, double ry, double angle, bool largeArcFlag, bool sweepFlag, double x, double y) { + public static void get_arc_arguments (double x0, double y0, + double rx, double ry, double angle, bool largeArcFlag, + bool sweepFlag, double x, double y, + out double angle_start, out double angle_extent, + out double center_x, out double center_y) { // // Elliptical arc implementation based on the SVG specification notes // + double cx, cy; double dx2, dy2, cosAngle, sinAngle; double x1, y1, Prx, Pry, Px1, Py1, radiiCheck; double sign, sq, coef, cx1, cy1; - double sx2, sy2, cx, cy; + double sx2, sy2; double ux, uy, vx, vy, p, n; double angleStart, angleExtent; - double s, step, theta; // Compute the half distance between the current and the final point dx2 = (x0 - x) / 2.0; dy2 = (y0 - y) / 2.0; // Convert angle from degrees to radians - angle = 2 * PI * ((angle % 360.0) / 360.0); + angle = angle % (2 * Math.PI); cosAngle = cos (angle); sinAngle = sin (angle); @@ -71,7 +74,7 @@ // x1 = cosAngle * dx2 + sinAngle * dy2; y1 = -sinAngle * dx2 + cosAngle * dy2; - + // Ensure radii are large enough rx = fabs(rx); ry = fabs(ry); @@ -80,7 +83,6 @@ Px1 = x1 * x1; Py1 = y1 * y1; - // Check that radii are large enough radiiCheck = Px1 / Prx + Py1 / Pry; @@ -107,13 +109,13 @@ sx2 = (x0 + x) / 2.0; sy2 = (y0 + y) / 2.0; - cx = sx2 - (cosAngle * cx1 - sinAngle * cy1); - cy = sy2 - (sinAngle * cx1 + cosAngle * cy1); + cx = sx2 + (cosAngle * cx1 - sinAngle * cy1); // FIXME: + delete this + cy = sy2 + (sinAngle * cx1 + cosAngle * cy1); // // Step 4 : Compute the angleStart (angle1) and the angleExtent (dangle) // - + ux = (x1 - cx1) / rx; uy = (y1 - cy1) / ry; vx = (-x1 - cx1) / rx; @@ -132,51 +134,27 @@ angleExtent = sign * Math.acos(p / n); if(!sweepFlag && angleExtent > 0) { - angleExtent -= 2 *PI; + angleExtent -= 2 * PI; } else if (sweepFlag && angleExtent < 0) { - angleExtent += 2 *PI; + angleExtent += 2 * PI; } - angleExtent %= 2 * PI; - angleStart %= 2 * PI; + + //center_x = cx - rx; + //center_y = cy - ry; - angleExtent *= -1; - angleStart *= -1; - - // Approximate the path with Beziér points - s = (angleExtent > 0) ? 1 : -1; - step = fabs (angleExtent) / (2 * fabs (angleExtent)); + center_x = cx; + center_y = cy; + + angleExtent = -angleExtent; + angleStart = -angleStart; - theta = PI - angleStart - angleExtent; - - bezier_points[bi].type = 'L'; - bezier_points[bi].svg_type = 'a'; + angle_start = angleStart; + angle_extent = angleExtent; - bezier_points[bi].x0 = cx + rx * cos (theta); - bezier_points[bi].y0 = cy + ry * sin (theta); - - bi++; - - for (double a = 0; a < fabs (angleExtent); a += step) { - theta = PI - angleStart - angleExtent + s * a; + print (@"angleStart: $angle_start angleExtent $angle_extent\n"); + } - return_if_fail (0 <= bi < bezier_points.length); - bezier_points[bi].type = 'S'; - bezier_points[bi].svg_type = 'a'; - - bezier_points[bi].x0 = cx + rx * cos (theta); - bezier_points[bi].y0 = cy + ry * sin (theta); - - bezier_points[bi].x1 = cx + rx * cos (theta + 1 * step / 4); - bezier_points[bi].y1 = cy + ry * sin (theta + 1 * step / 4); - - bezier_points[bi].x2 = cx + rx * cos (theta + 2 * step / 4); - bezier_points[bi].y2 = cy + ry * sin (theta + 2 * step / 4); - - bi++; - } } - } -
--- a/libsvgbird/SvgFile.vala +++ b/libsvgbird/SvgFile.vala @@ -14,6 +14,7 @@ using B; using Cairo; + using Math; namespace SvgBird { @@ -635,6 +636,8 @@ int points_size; get_bezier_points (data, out bezier_points, out points_size, true); + + // instructions are padded for (int i = 0; i < points_size; i++) { // FIXME: add more types @@ -642,19 +645,45 @@ points.x = bezier_points[i].x0; points.y = bezier_points[i].y0; } else if (bezier_points[i].type == 'C') { + points.add_type (CUBIC); points.add (bezier_points[i].x0); points.add (bezier_points[i].y0); points.add (bezier_points[i].x1); points.add (bezier_points[i].y1); points.add (bezier_points[i].x2); points.add (bezier_points[i].y2); + points.add (0); } else if (bezier_points[i].type == 'L') { + points.add_type (CUBIC); // FIXME: use cairo line points.add (bezier_points[i].x0); points.add (bezier_points[i].y0); points.add (bezier_points[i].x0); points.add (bezier_points[i].y0); points.add (bezier_points[i].x0); points.add (bezier_points[i].y0); + points.add (0); + } else if (bezier_points[i].type == 'A') { + BezierPoints b = bezier_points[i]; + double angle_start; + double angle_extent; + double center_x; + double center_y; + //double rotation = Math.PI * (b.angle / 180.0); + double rotation = b.angle; + + get_arc_arguments (b.x0, b.y0, b.rx, b.ry, + b.angle, b.large_arc, b.sweep, b.x1, b.y1, + out angle_start, out angle_extent, + out center_x, out center_y); + + points.add_type (ARC); + points.add (center_x); + points.add (center_y); + points.add (b.rx); + points.add (b.ry); + points.add (angle_start); + points.add (angle_extent); + points.add (rotation); } else if (bezier_points[i].type == 'z') { points.closed = true; path_data.add (points); @@ -686,7 +715,7 @@ return double.parse ((!) s); } - + // FIXME: rename to instructions public static void get_bezier_points (string point_data, out BezierPoints[] bezier_points, out int points, bool svg_glyph) { double px = 0; double py = 0; @@ -1157,7 +1186,7 @@ arc_rx = parse_double (c[++i]); arc_ry = parse_double (c[++i]); - arc_rotation = parse_double (c[++i]); + arc_rotation = PI * (parse_double (c[++i]) / 180.0); large_arc = parse_int (c[++i]); arc_sweep = parse_int (c[++i]); @@ -1172,7 +1201,20 @@ arc_dest_x = cx; arc_dest_y = cy; - add_arc_points (bezier_points, ref bi, px, py, arc_rx, arc_ry, arc_rotation, large_arc == 1, arc_sweep == 1, cx, cy); + bezier_points[bi].type = 'A'; + bezier_points[bi].svg_type = 'a'; + bezier_points[bi].x0 = px; + bezier_points[bi].y0 = py; + bezier_points[bi].x1 = cx; + bezier_points[bi].y1 = cy; + bezier_points[bi].rx = arc_rx; + bezier_points[bi].ry = arc_ry; + bezier_points[bi].angle = arc_rotation; + bezier_points[bi].large_arc = large_arc == 1; + bezier_points[bi].sweep = arc_sweep == 1; + bi++; + + // FIXME: Delete add_arc_points (bezier_points, ref bi, px, py, arc_rx, arc_ry, arc_rotation, large_arc == 1, arc_sweep == 1, cx, cy); px = cx; py = cy; @@ -1182,7 +1224,7 @@ arc_rx = parse_double (c[++i]); arc_ry = parse_double (c[++i]); - arc_rotation = parse_double (c[++i]); + arc_rotation = PI * (parse_double (c[++i]) / 180.0); large_arc = parse_int (c[++i]); arc_sweep = parse_int (c[++i]); @@ -1197,12 +1239,21 @@ arc_dest_x = cx; arc_dest_y = cy; - add_arc_points (bezier_points, ref bi, px, py, arc_rx, arc_ry, arc_rotation, large_arc == 1, arc_sweep == 1, cx, cy); - + bezier_points[bi].type = 'A'; + bezier_points[bi].svg_type = 'A'; + bezier_points[bi].x0 = px; + bezier_points[bi].y0 = py; + bezier_points[bi].x1 = cx; + bezier_points[bi].y1 = cy; + bezier_points[bi].rx = arc_rx; + bezier_points[bi].ry = arc_ry; + bezier_points[bi].angle = arc_rotation; + bezier_points[bi].large_arc = large_arc == 1; + bezier_points[bi].sweep = arc_sweep == 1; + bi++; + px = cx; py = cy; - - } } else if (c[i] == "z") { bezier_points[bi].type = 'z';
--- a/libsvgbird/SvgPath.vala +++ b/libsvgbird/SvgPath.vala @@ -48,16 +48,47 @@ cr.restore (); } - public void draw_points (Context cr, Points points) { - Doubles p = points.point_data; + public void draw_points (Context cr, Points path) { + PointValue* points = path.point_data.data; + int size = path.point_data.size; - return_if_fail (p.size % 6 == 0); + return_if_fail (size % 8 == 0); - for (int i = 0; i < p.size; i += 6) { - cr.curve_to (p.data[i], p.data[i + 1], - p.data[i + 2], p.data[i + 3], - p.data[i + 4], p.data[i + 5]); + for (int i = 0; i < size; i += 8) { + switch (points[i].type) { + case ARC: + draw_arc (cr, points[i + 1].value, points[i + 2].value, + points[i + 3].value, points[i + 4].value, + points[i + 5].value, points[i + 6].value, + points[i + 7].value); + break; + case CUBIC: + cr.curve_to (points[i + 1].value, points[i + 2].value, + points[i + 3].value, points[i + 4].value, + points[i + 5].value, points[i + 6].value); + break; + } } + } + + static void draw_arc (Context cr, + double x, double y, + double rx, double ry, + double angle_start, double angle_extent, + double rotation) { + + cr.save (); + cr.translate (x, y); + cr.rotate (rotation); + cr.scale (rx, ry); + + if (angle_extent > 0) { + cr.arc_negative (0, 0, 1, -angle_start, -angle_start - angle_extent); + } else { + cr.arc (0, 0, 1, -angle_start, -angle_start - angle_extent); + } + + cr.restore (); } public override void move (double dx, double dy) {
--- a/libsvgbird/SvgStyle.vala +++ b/libsvgbird/SvgStyle.vala @@ -1,5 +1,5 @@ /* - Copyright (C) 2015 Johan Mattsson + Copyright (C) 2015 2016 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 @@ -70,7 +70,6 @@ public void inherit (SvgStyle inherited) { foreach (string key in inherited.style.keys) { - print (@"inherit $key: " + inherited.style.get (key) + "\n"); style.set (key, inherited.style.get (key)); } }