Commit 7bf31d8
Select SVG objects
Changed files (6)
modifiedlibsvgbird/Circle.valamodifiedlibsvgbird/Ellipse.valamodifiedlibsvgbird/Object.valamodifiedlibsvgbird/Rectangle.valamodifiedlibsvgbird/SvgDrawing.valamodifiedlibsvgbird/SvgPath.vala
Diff
@@ -26,7 +26,9 @@ public class Circle : Object { } public override bool is_over (double x, double y) {- return false;+ double dx = x - cx;+ double dy = y - cy;+ return Math.sqrt (dx * dx + dy * dy) <= r; } public override void draw_outline (Context cr) {@@ -28,7 +28,9 @@ public class Ellipse : Object { } public override bool is_over (double x, double y) {- return false;+ double point_x = x - cx;+ double point_y = y - cy;+ return (point_x * point_x) / (rx * rx) + (point_y * point_y) / (ry * ry) <= 1; } public override void draw_outline (Context cr) {@@ -93,7 +93,10 @@ public abstract class Object : GLib.Object { public Object.create_copy (Object o) { } - public abstract bool is_over (double x, double y);+ public virtual bool is_over (double x, double y) {+ return left <= x <= right && top <= y <= bottom; + }+ public abstract void draw_outline (Context cr); public abstract Object copy ();@@ -128,6 +131,10 @@ public abstract class Object : GLib.Object { return "Object"; } + public bool is_over_boundaries (double x, double y) {+ return top <= y <= bottom && left <= x <= right;+ }+ public void paint (Context cr) { Color fill, stroke; bool need_fill = (style.fill_gradient != null || style.fill != null);@@ -214,6 +221,8 @@ public abstract class Object : GLib.Object { double x0, y0, x1, y1; bool has_stroke = style.has_stroke (); + apply_transform (context);+ if (has_stroke) { context.set_line_width (style.stroke_width); } else {@@ -223,15 +232,18 @@ public abstract class Object : GLib.Object { draw_outline (context); context.save ();- - context.set_matrix (Matrix.identity ());- + if (has_stroke) { context.stroke_extents (out x0, out y0, out x1, out y1); } else { context.fill_extents (out x0, out y0, out x1, out y1); }- + + Matrix matrix = context.get_matrix ();+ matrix.transform_point (ref x0, ref y0);+ matrix.transform_point (ref x1, ref y1);++ context.fill (); context.restore (); left = x0;@@ -32,7 +32,7 @@ public class Rectangle : Object { } public override bool is_over (double x, double y) {- return false;+ return this.x <= x <= this.x + width && this.y <= y <= this.y + height; } public override void draw_outline (Context cr) {@@ -55,8 +55,8 @@ public class SvgDrawing : Object { public override void apply_transform (Context cr) { apply_view_box (cr);- root_layer.apply_transform (cr); base.apply_transform (cr);+ root_layer.apply_transform (cr); } public void draw (Context cr) {@@ -13,6 +13,7 @@ */ using Cairo;+using Math; namespace SvgBird { @@ -22,6 +23,8 @@ public class SvgPath : Object { double pen_position_x = 0; double pen_position_y = 0; + public delegate bool LineIterator (double x, double y, double step); + public SvgPath () { } @@ -32,14 +35,153 @@ public class SvgPath : Object { points.add (point_data.copy ()); } }++ public override bool is_over (double point_x, double point_y) {+ bool inside = false;+ + double x = point_x;+ double y = point_y;+ + Matrix matrix = view_matrix;+ matrix.invert ();+ matrix.transform_point (ref x, ref y);+ + if (is_over_boundaries (point_x, point_y)) {+ foreach (Points p in points) {+ if (is_over_points (p, point_x, point_y)) {+ inside = !inside;+ }+ }+ }+ + return inside;+ } - public override bool is_over (double x, double y) {- return false;+ public bool is_over_points (Points p, double point_x, double point_y) {+ double previous_x;+ double previous_y;+ bool inside = false;++ PointValue* points = p.point_data.data;+ int size = p.point_data.size;+ + return_if_fail (size % 8 == 0);+ + get_start (p, out previous_x, out previous_y);+ + for (int i = 0; i < size; i += 8) {+ switch (points[i].type) {+ case POINT_ARC: + double rx = points[i + 1].value;+ double ry = points[i + 2].value;+ double rotation = points[i + 3].value;+ double large_arc = points[i + 4].value;+ double sweep = points[i + 5].value;+ double dest_x = points[i + 6].value;+ double dest_y = points[i + 7].value;++ double angle_start, angle_extent, cx, cy;+ + get_arc_arguments (pen_position_x, pen_position_y, rx, ry,+ rotation, large_arc > 0, sweep > 0, dest_x, dest_y,+ out angle_start, out angle_extent,+ out cx, out cy);+ const int steps = 50;+ for (int step = 0; step < steps; step++) {+ double angle = angle_start + step * (angle_extent / steps);+ double next_x = cx + cos (angle);+ double next_y = cy + sin (angle);+ is_inside (ref inside, + point_x, point_y,+ previous_x, previous_y, + next_x, next_y);+ + previous_x = next_x;+ previous_y = next_y;+ }+ break;+ case POINT_CUBIC:+ all_lines (previous_x, previous_y,+ points[i + 1].value, points[i + 2].value,+ points[i + 3].value, points[i + 4].value,+ points[i + 5].value, points[i + 6].value,+ (x, y, t) => {+ is_inside (ref inside, + point_x, point_y,+ previous_x, previous_y, + x, y);+ + previous_x = x;+ previous_y = y;+ return true;+ });+ + previous_x = points[i + 5].value;+ previous_y = points[i + 6].value;+ break;+ case POINT_LINE:+ is_inside (ref inside, point_x, point_y, previous_x, previous_y, + points[i + 1].value, points[i + 2].value);+ previous_x = points[i + 1].value;+ previous_y = points[i + 2].value;+ break;+ }+ }+ + return inside; }++ static void is_inside (ref bool inside, double point_x, double point_y, + double prev_x, double prev_y, double next_x, double next_y) {+ if ((next_y > point_y) != (prev_y > point_y) + && point_x < (prev_x - next_x) * (point_y - next_y) / (prev_y - next_y) + next_x) {+ inside = !inside;+ print (@"flip\n");+ }+ }+ + private static bool all_lines (double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3,+ LineIterator iter, double steps = 400) {+ double px = x1;+ double py = y1;+ + double t;+ + for (int i = 0; i < steps; i++) {+ t = i / steps; + px = bezier_path (t, x0, x1, x2, x3);+ py = bezier_path (t, y0, y1, y2, y3);+ + if (!iter (px, py, t)) {+ return false;+ }+ }+ + return true;+ }++ public static double bezier_path (double step, double p0, double p1, double p2, double p3) {+ double q0, q1, q2;+ double r0, r1;++ q0 = step * (p1 - p0) + p0;+ q1 = step * (p2 - p1) + p1;+ q2 = step * (p3 - p2) + p2;++ r0 = step * (q1 - q0) + q0;+ r1 = step * (q2 - q1) + q1;++ return step * (r1 - r0) + r0;+ }+ public override void draw_outline (Context cr) { foreach (Points p in points) {- move_to_start (cr, p);+ double x, y;+ + get_start (p, out x, out y);+ cr.move_to (x, y);+ draw_points (cr, p); if (p.closed) {@@ -48,22 +190,28 @@ public class SvgPath : Object { } } - public void move_to_start (Context cr, Points path) {+ public void get_start (Points path, out double x, out double y) { int size = path.point_data.size; + x = 0;+ y = 0;+ // points are padded up to 8 units return_if_fail (size % 8 == 0); return_if_fail (size >= 8); switch (path.get_point_type (0)) {- case POINT_ARC: - cr.move_to (path.get_double (6), path.get_double (7));+ case POINT_ARC:+ x = path.get_double (6);+ y = path.get_double (7); break; case POINT_CUBIC:- cr.move_to (path.get_double (5), path.get_double (6));+ x = path.get_double (5);+ y = path.get_double (6); break; case POINT_LINE:- cr.move_to (path.get_double (1), path.get_double (2));+ x = path.get_double (1);+ y = path.get_double (2); break; default: warning (@"Unknown type $(path.get_point_type (0))");@@ -102,7 +250,7 @@ public class SvgPath : Object { pen_position_y = points[i + 2].value; break; }- } + } } void draw_arc (Context cr,