Commit 904b358
Svg transform and a fix for a crash bug in ClickMap
Changed files (13)
modifiedlibbirdfont/BirdFontFile.valaaddedlibbirdfont/ClickMap.valamodifiedlibbirdfont/EditPoint.valamodifiedlibbirdfont/EditPointHandle.valamodifiedlibbirdfont/FastPath.valamodifiedlibbirdfont/OpenFontFormat/GlyfData.valamodifiedlibbirdfont/OpenFontFormat/GlyfTable.valamodifiedlibbirdfont/Path.valamodifiedlibbirdfont/PenTool.valamodifiedlibbirdfont/SearchPaths.valamodifiedlibbirdfont/Svg.valamodifiedlibbirdfont/SvgParser.valamodifiedlibbirdfont/TestCases.vala
Diff
@@ -472,8 +472,8 @@ class BirdFontFile : GLib.Object { EditPointHandle h = start.get_right_handle (); string x0, y0, x1, y1; - x0 = round (h.x ());- y0 = round (h.y ());+ x0 = round (h.x);+ y0 = round (h.y); x1 = round (end.x); y1 = round (end.y); @@ -485,10 +485,10 @@ class BirdFontFile : GLib.Object { EditPointHandle h2 = end.get_left_handle (); string x0, y0, x1, y1, x2, y2; - x0 = round (h1.x ());- y0 = round (h1.y ());- x1 = round (h2.x ());- y1 = round (h2.y ());+ x0 = round (h1.x);+ y0 = round (h1.y);+ x1 = round (h2.x);+ y1 = round (h2.y); x2 = round (end.x); y2 = round (end.y); @@ -500,10 +500,10 @@ class BirdFontFile : GLib.Object { EditPointHandle h2 = end.get_left_handle (); string x0, y0, x1, y1, x2, y2; - x0 = round (h1.x ());- y0 = round (h1.y ());- x1 = round (h2.x ());- y1 = round (h2.y ());+ x0 = round (h1.x);+ y0 = round (h1.y);+ x1 = round (h2.x);+ y1 = round (h2.y); x2 = round (end.x); y2 = round (end.y); new file mode 100644@@ -0,0 +1,189 @@+/*+ Copyright (C) 2014 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 + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version.++ This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details.+*/++namespace BirdFont {++// This is a refcounted container in order to avoid double free +// corruption in lambda functions.+public class ClickMap : GLib.Object {+ char* map;+ int width;+ int margin;+ int map_size;++ public ClickMap (int width) {+ this.width = width;+ margin = 10;+ map_size = width + margin;+ map = malloc (map_size * map_size);+ }+ + ~ClickMap () {+ delete map;+ }+ + public char get_value (int x, int y) {+ + if (unlikely (!(0 <= x < map_size && 0 <= y < map_size))) {+ warning ("Array index out of bounds.");+ return '\0';+ }+ + return map[y * width + x];+ }+ + public void set_value (int x, int y, char val) {+ if (unlikely (!(0 <= x < map_size && 0 <= y < map_size))) {+ warning ("Array index out of bounds.");+ return;+ }+ + map[y * width + x] = val;+ }++ private void map_outline (Path path) {+ path.all_of_path ((cx, cy, ct) => {+ int px = (int) (width * ((cx - path.xmin) / (path.xmax - path.xmin)));+ int py = (int) (width * ((cy - path.ymin) / (path.ymax - path.ymin)));+ + set_value (px, py, '#');+ + return true;+ }, 2 * width);+ }++ private void close_outline (Path path) {+ // first to last point in case the path is open+ Path.all_of (path.get_last_point (), path.get_first_point (), (cx, cy, ct) => {+ int px = (int) (width * ((cx - path.xmin) / (path.xmax - path.xmin)));+ int py = (int) (width * ((cy - path.ymin) / (path.ymax - path.ymin)));+ + set_value (px, py, '#');+ + return true;+ }, 2 * width);+ }+ + public void create_click_map (Path path) {+ // Clear the map+ for (int i = 0; i < map_size; i++) {+ for (int j = 0; j < map_size; j++) {+ set_value (i, j, '\0');+ }+ }+ + // Create outline+ map_outline (path);+ close_outline (path);+ + // Fill the map+ for (int j = 0; j < width; j++) {+ for (int k = 0; k < width; k++) {+ if (get_value (k, j) == '#') {+ + k++;+ while (k < width && get_value (k, j) == '#') {+ k++;+ }++ while (k < width && get_value (k, j) == '\0') {+ set_value (k, j, 'o');+ k++;+ }++ k++;+ while (k < width && get_value (k, j) == '#') {+ k++;+ }+ }+ }+ }++ // Remove fill from the out side+ for (int k = 0; k < width; k++) {+ + if (get_value (k, 0) == 'o') {+ set_value (k, 0, '\0'); + }+ + for (int l = width - 1; l >= 0; l--) {+ if (get_value (k, l) != '#') {+ if (get_value (k, l + 1) == '\0') {+ set_value (k, l, '\0');+ }+ + if (get_value (k, l + 1) == 'o') {+ set_value (k, l, 'o');+ }+ }+ }+ } ++ for (int k = 0; k < width; k++) {+ if (get_value (0, k) == 'o') {+ set_value (0, k,'\0'); + }+ + for (int l = width - 1; l >= 0; l--) {+ if (get_value (l, k) != '#') {+ if (get_value (l + 1, k) == '\0') {+ set_value (l, k, '\0');+ }+ + if (get_value (l + 1, k) == 'o') {+ set_value (l, k, 'o');+ }+ }+ }+ }++ for (int k = width - 1; k > 0; k--) {+ if (get_value (k, 0) == 'o') {+ set_value (k, 0,'\0'); + }+ + for (int l = width - 1; l >= 0; l--) {+ if (get_value (k, l) != '#') { + if (get_value (k, l + 1) == 'o') {+ set_value (k, l, 'o');+ }+ }+ }+ }+ }+ + public void print () {+ char c;+ for (int i = width - 1; i >= 0; i--) {+ for (int j = 0; j < width; j++) {+ c = get_value (j, i);+ if (c == '\0') {+ stdout.printf (" ");+ } else if (c == '#') {+ stdout.printf ("#");+ } else if (c == 'o') {+ stdout.printf ("o");+ } else if (c == 'X') {+ stdout.printf ("X");+ } else {+ stdout.printf ("?");+ }+ }+ + stdout.printf ("\n");+ }+ }+}++}@@ -49,7 +49,34 @@ public class EditPoint { public bool tie_handles = false; public bool reflective_handles = false;- ++ /** Set new position for control point without moving handles. */+ public double independent_x {+ get { + return x;+ }+ + set { + double d = value - x;+ x = value;+ right_handle.x -= d;+ left_handle.x -= d;+ }+ }++ public double independent_y {+ get {+ return y; + }+ + set { + double d = value - y;+ y = value;+ right_handle.y -= d;+ left_handle.y -= d;+ }+ }+ public EditPoint (double nx = 0, double ny = 0, PointType nt = PointType.NONE) { x = nx; y = ny;@@ -93,10 +120,10 @@ public class EditPoint { public bool equals (EditPoint e) { return e.x == x && e.y == y - && get_right_handle ().x () == e.get_right_handle ().x ()- && get_right_handle ().y () == e.get_right_handle ().y ()- && get_left_handle ().x () == e.get_left_handle ().x ()- && get_left_handle ().y () == e.get_left_handle ().y ();+ && get_right_handle ().x == e.get_right_handle ().x+ && get_right_handle ().y == e.get_right_handle ().y+ && get_left_handle ().x == e.get_left_handle ().x+ && get_left_handle ().y == e.get_left_handle ().y; } /** Make handles symmetrical. */@@ -118,8 +145,8 @@ public class EditPoint { px = get_next ().get_next ().x; py = get_next ().get_next ().y; - dr = Math.sqrt (Math.pow (px - right_handle.x (), 2) + Math.pow (py - right_handle.y (), 2));- dl = Math.sqrt (Math.pow (px - left_handle.x (), 2) + Math.pow (py - left_handle.y (), 2));+ dr = Math.sqrt (Math.pow (px - right_handle.x, 2) + Math.pow (py - right_handle.y, 2));+ dl = Math.sqrt (Math.pow (px - left_handle.x, 2) + Math.pow (py - left_handle.y, 2)); // flip handles if (dl < dr) {@@ -283,8 +310,8 @@ public class EditPoint { eh = right_handle; - a = left_handle.x () - right_handle.x ();- b = left_handle.y () - right_handle.y ();+ a = left_handle.x - right_handle.x;+ b = left_handle.y - right_handle.y; c = a * a + b * b; if (c == 0) {@@ -293,7 +320,7 @@ public class EditPoint { length = sqrt (fabs (c)); - if (right_handle.y () < left_handle.y ()) {+ if (right_handle.y < left_handle.y) { angle = acos (a / length) + PI; } else { angle = -acos (a / length) + PI;@@ -311,7 +338,7 @@ public class EditPoint { left_handle.angle = angle - PI; set_tie_handle (true);- eh.move_to_coordinate (right_handle.x (), right_handle.y ());+ eh.move_to_coordinate (right_handle.x, right_handle.y); } public EditPoint copy () {@@ -387,7 +414,7 @@ public class EditPoint { n = get_next (); n.set_tie_handle (false); n.set_reflective_handles (false);- n.left_handle.move_to_coordinate_internal (right_handle.x (), right_handle.y ());+ n.left_handle.move_to_coordinate_internal (right_handle.x, right_handle.y); } } @@ -396,7 +423,7 @@ public class EditPoint { p = get_prev (); p.set_tie_handle (false); p.set_reflective_handles (false);- p.right_handle.move_to_coordinate (left_handle.x (), left_handle.y ());+ p.right_handle.move_to_coordinate (left_handle.x, left_handle.y); } } }@@ -28,6 +28,36 @@ public class EditPointHandle { public double angle; + public double x {+ get { + double r = px (); + + if (unlikely (r <= -100000)) {+ print_position ();+ move_to (0, 0);+ }+ + return r;+ }+ + set { move_to_coordinate_internal (value, py ()); }+ }++ public double y {+ get {+ double r = py ();+ + if (unlikely (r <= -100000)) {+ print_position ();+ move_to (0, 0);+ }+ + return r; + }+ + set { move_to_coordinate_internal (px (), value); }+ }+ public EditPointHandle.empty() { this.parent = none; this.angle = 0;@@ -80,28 +110,6 @@ public class EditPointHandle { public void set_point_type (PointType point_type) { type = point_type; }-- public double x () {- double r = px ();- - if (unlikely (r <= -100000)) {- print_position ();- move_to (0, 0);- }- - return r;- }- - public double y () {- double r = py ();- - if (unlikely (r <= -100000)) {- print_position ();- move_to (0, 0);- }- - return r;- } double px () { assert ((EditPoint?) parent != null);@@ -127,8 +135,8 @@ public class EditPointHandle { } p = (!) visual_handle;- p.x = x ();- p.y = y ();+ p.x = x;+ p.y = y; return p; }@@ -80,10 +80,10 @@ public class FastPath : GLib.Object { public void set_editpoint (int index, EditPoint ep) { points[NUMBERS_PER_POINT * index + POS_X] = ep.x; points[NUMBERS_PER_POINT * index + POS_Y] = ep.y;- points[NUMBERS_PER_POINT * index + LEFT_HANDLE_POS_X] = ep.get_left_handle ().x ();- points[NUMBERS_PER_POINT * index + LEFT_HANDLE_POS_Y] = ep.get_left_handle ().y ();- points[NUMBERS_PER_POINT * index + RIGHT_HANDLE_POS_X] = ep.get_right_handle ().x ();- points[NUMBERS_PER_POINT * index + RIGHT_HANDLE_POS_Y] = ep.get_right_handle ().y ();+ points[NUMBERS_PER_POINT * index + LEFT_HANDLE_POS_X] = ep.get_left_handle ().x;+ points[NUMBERS_PER_POINT * index + LEFT_HANDLE_POS_Y] = ep.get_left_handle ().y;+ points[NUMBERS_PER_POINT * index + RIGHT_HANDLE_POS_X] = ep.get_right_handle ().x;+ points[NUMBERS_PER_POINT * index + RIGHT_HANDLE_POS_Y] = ep.get_right_handle ().y; } public double get_x (int index) {@@ -201,10 +201,10 @@ class GlyfData : GLib.Object { type = e.get_right_handle ().type; // off curve- x = rint (e.get_right_handle ().x () * UNITS - prev - glyph.left_limit * UNITS);+ x = rint (e.get_right_handle ().x * UNITS - prev - glyph.left_limit * UNITS); coordinate_x.append ((int16) x); - prev = rint (e.get_right_handle ().x () * UNITS - glyph.left_limit * UNITS);+ prev = rint (e.get_right_handle ().x * UNITS - glyph.left_limit * UNITS); } } }@@ -225,10 +225,10 @@ class GlyfData : GLib.Object { type = e.get_right_handle ().type; // off curve- y = rint (e.get_right_handle ().y () * UNITS - prev - font.base_line * UNITS);+ y = rint (e.get_right_handle ().y * UNITS - prev - font.base_line * UNITS); coordinate_y.append ((int16) y); - prev = rint (e.get_right_handle ().y () * UNITS - font.base_line * UNITS);+ prev = rint (e.get_right_handle ().y * UNITS - font.base_line * UNITS); } } }@@ -707,8 +707,8 @@ public class GlyfTable : Table { } else { if (prev_is_curve) {- x = x - (x - edit_point.right_handle.x ()) / 2;- y = y - (y - edit_point.right_handle.y ()) / 2;+ x = x - (x - edit_point.right_handle.x) / 2;+ y = y - (y - edit_point.right_handle.y) / 2; edit_point = new EditPoint (); edit_point.set_position (x, y);@@ -734,8 +734,8 @@ public class GlyfTable : Table { x = xcoordinates[first_point] * 1000.0 / units_per_em; // in proportion to em width y = ycoordinates[first_point] * 1000.0 / units_per_em; - x = x - (x - edit_point.right_handle.x ()) / 2;- y = y - (y - edit_point.right_handle.y ()) / 2;+ x = x - (x - edit_point.right_handle.x) / 2;+ y = y - (y - edit_point.right_handle.y) / 2; edit_point = new EditPoint (); edit_point.set_position (x, y);@@ -306,8 +306,8 @@ public class Path { EditPoint middle; double x, y; - x = e.get_right_handle ().x () + (en.get_left_handle ().x () - e.get_right_handle ().x ()) / 2;- y = e.get_right_handle ().y () + (en.get_left_handle ().y () - e.get_right_handle ().y ()) / 2;+ x = e.get_right_handle ().x + (en.get_left_handle ().x - e.get_right_handle ().x) / 2;+ y = e.get_right_handle ().y + (en.get_left_handle ().y - e.get_right_handle ().y) / 2; middle = new EditPoint (x, y, PointType.DOUBLE_CURVE); middle.right_handle = en.get_left_handle ().copy ();@@ -351,11 +351,11 @@ public class Path { xa = center_x + e.x; ya = center_y - e.y; - xb = center_x + e.get_right_handle ().x ();- yb = center_y - e.get_right_handle ().y ();+ xb = center_x + e.get_right_handle ().x;+ yb = center_y - e.get_right_handle ().y; - xc = center_x + en.get_left_handle ().x ();- yc = center_y - en.get_left_handle ().y ();+ xc = center_x + en.get_left_handle ().x;+ yc = center_y - en.get_left_handle ().y; xd = center_x + en.x; yd = center_y - en.y; @@ -366,11 +366,11 @@ public class Path { xa = + e.x; ya = - e.y; - xb = + e.get_right_handle ().x ();- yb = - e.get_right_handle ().y ();+ xb = + e.get_right_handle ().x;+ yb = - e.get_right_handle ().y; - xc = + en.get_left_handle ().x ();- yc = - en.get_left_handle ().y ();+ xc = + en.get_left_handle ().x;+ yc = - en.get_left_handle ().y; xd = + en.x; yd = - en.y; @@ -436,12 +436,12 @@ public class Path { if (!(is_open () && e == points.get (points.size - 1))) { draw_line (handle_right, e, cr, 0.15);- draw_image (cr, img_right, e.get_right_handle ().x (), e.get_right_handle ().y ());+ draw_image (cr, img_right, e.get_right_handle ().x, e.get_right_handle ().y); } if (!(is_open () && e == points.get (0))) { draw_line (handle_left, e, cr, 0.15);- draw_image (cr, img_left, e.get_left_handle ().x (), e.get_left_handle ().y ());+ draw_image (cr, img_left, e.get_left_handle ().x, e.get_left_handle ().y); } } }@@ -617,6 +617,23 @@ public class Path { ymin *= ratio; ymax *= ratio; }++ public void scale (double scale_x, double scale_y) { + foreach (EditPoint p in points) {+ p.right_handle.length *= scale_x * scale_y;+ p.left_handle.length *= scale_x * scale_y;+ }+ + foreach (EditPoint p in points) {+ p.x *= scale_x;+ p.y *= scale_y;+ }+ + xmin *= scale_x; + xmax *= scale_x;+ ymin *= scale_y;+ ymax *= scale_y;+ } public Path copy () { Path new_path = new Path ();@@ -666,30 +683,30 @@ public class Path { public static double get_length_from (EditPoint a, EditPoint b) { double x, y; - x = Math.fabs (a.x - a.get_right_handle ().x ());- x += Math.fabs (a.get_right_handle ().x () - b.get_left_handle ().x ());- x += Math.fabs (b.get_left_handle ().x () - b.x);+ x = Math.fabs (a.x - a.get_right_handle ().x);+ x += Math.fabs (a.get_right_handle ().x - b.get_left_handle ().x );+ x += Math.fabs (b.get_left_handle ().x - b.x); - y = Math.fabs (a.y - a.get_right_handle ().y ());- y += Math.fabs (a.get_right_handle ().y () - b.get_left_handle ().y ());- y += Math.fabs (b.get_left_handle ().y () - b.y);+ y = Math.fabs (a.y - a.get_right_handle ().y);+ y += Math.fabs (a.get_right_handle ().y - b.get_left_handle ().y);+ y += Math.fabs (b.get_left_handle ().y - b.y); return Math.fabs (Math.sqrt (x * x + y * y)); } - + /** Variable precision */ public bool is_over_coordinate_var (double x, double y) { PathList pathlist;- bool result = false;+ bool in_path = false; int width;- char[,] click_map;+ ClickMap click_map; int px, py; int click_x, click_y; if (points.size < 2) { return false; }- + if (stroke > 0) { pathlist = StrokeTool.get_stroke (this, stroke); @@ -706,96 +723,25 @@ public class Path { return false; } + // generate a rasterized image of the object- width = 512;- click_map = new char[width + 1, width + 1];+ width = 80;+ click_map = new ClickMap (width); px = 0; py = 0;- all_of_path ((cx, cy, ct) => {- px = (int) (width * ((cx - xmin) / (xmax - xmin)));- py = (int) (width * ((cy - ymin) / (ymax - ymin)));- click_map[px, py] = '#';- return true;- }, 2 * width); - // first to last point in case the path is open- all_of (get_last_point (), get_first_point (), (cx, cy, ct) => {- px = (int) (width * ((cx - xmin) / (xmax - xmin)));- py = (int) (width * ((cy - ymin) / (ymax - ymin)));- click_map[px, py] = '#';- return true;- }, 2 * width);- - // Fill the map- for (int j = 0; j < width; j++) {- for (int k = 0; k < width; k++) {- if (click_map[k, j] == '#') {- - k++;- while (k < width && click_map[k, j] == '#') {- k++;- }-- while (k < width && click_map[k, j] == '\0') {- click_map[k, j] = 'o';- k++;- }-- k++;- while (k < width && click_map[k, j] == '#') {- k++;- }- }- }- }-- // remove fill from the out side- for (int k = 0; k < width; k++) {- - if (click_map[k, 0] == 'o') {- click_map[k, 0] = '\0'; - }- - for (int l = width - 1; l >= 0; l--) {- if (click_map[k, l] != '#') {- if (click_map[k, l + 1] == '\0') {- click_map[k, l] = '\0'; - }- - if (click_map[k, l + 1] == 'o') {- click_map[k, l] = 'o'; - }- }- }- } -- for (int k = 0; k < width; k++) {- - if (click_map[0, k] == 'o') {- click_map[0, k] = '\0'; - }- - for (int l = width - 1; l >= 0; l--) {- if (click_map[l, k] != '#') {- if (click_map[l + 1, k] == '\0') {- click_map[l, k] = '\0'; - }- - if (click_map[l + 1, k] == 'o') {- click_map[l, k] = 'o'; - }- }- }- }+ click_map.create_click_map (this); click_x = (int) (width * ((x - xmin) / (xmax - xmin))); click_y = (int) (width * ((y - ymin) / (ymax - ymin))); - result = (click_map[click_x, click_y] != '\0');+ in_path = click_map.get_value (click_x, click_y) != '\0'; - click_map[click_x, click_y] = 'X';+ click_map.set_value (click_x, click_y, 'X');+ + click_map.print (); - return result;+ return in_path; } public bool is_over_boundry (double x, double y) {@@ -951,20 +897,20 @@ public class Path { } private void update_region_boundaries_for_handle (EditPointHandle h) {- if (h.x () > xmax) {- xmax = h.x ();+ if (h.x > xmax) {+ xmax = h.x; } - if (h.x () < xmin) {- xmin = h.x ();+ if (h.x < xmin) {+ xmin = h.x; } - if (h.y () > ymax) {- ymax = h.y ();+ if (h.y > ymax) {+ ymax = h.y; } - if (h.y () < ymin) {- ymin = h.y ();+ if (h.y < ymin) {+ ymin = h.y; } } @@ -1068,11 +1014,11 @@ public class Path { first.get_right_handle ().type = PointType.QUADRATIC; // half way between handles- x = first.get_right_handle ().x () + (next.get_left_handle ().x () - first.get_right_handle ().x ()) / 2;- y = first.get_right_handle ().y () + (next.get_left_handle ().y () - first.get_right_handle ().y ()) / 2;+ x = first.get_right_handle ().x + (next.get_left_handle ().x - first.get_right_handle ().x) / 2;+ y = first.get_right_handle ().y + (next.get_left_handle ().y - first.get_right_handle ().y) / 2; hidden = new EditPoint (x, y, PointType.QUADRATIC);- hidden.right_handle.move_to_coordinate_internal (next.get_left_handle ().x(), next.get_left_handle ().y());+ hidden.right_handle.move_to_coordinate_internal (next.get_left_handle ().x, next.get_left_handle ().y); hidden.get_right_handle ().type = PointType.QUADRATIC; hidden.get_left_handle ().type = PointType.QUADRATIC;@@ -1148,7 +1094,7 @@ public class Path { foreach (EditPoint ep in quadratic_path.points) { if (ep.type == PointType.QUADRATIC) {- ep.get_left_handle ().move_to_coordinate (ep.get_prev ().get_right_handle ().x (), ep.get_prev ().get_right_handle ().y ());+ ep.get_left_handle ().move_to_coordinate (ep.get_prev ().get_right_handle ().x, ep.get_prev ().get_right_handle ().y); } } @@ -1230,8 +1176,8 @@ public class Path { prev.get_right_handle ().type = PointType.QUADRATIC; prev.get_right_handle ().move_delta (0.000001, 0.000001); - x = prev.get_right_handle ().x ();- y = prev.get_right_handle ().y ();+ x = prev.get_right_handle ().x;+ y = prev.get_right_handle ().y; ep.get_left_handle ().move_to_coordinate (ep.x - (ep.x - prev.x) / 2, ep.y - (ep.y - prev.y) / 2); @@ -1295,8 +1241,8 @@ public class Path { }); if (right == PointType.DOUBLE_CURVE || left == PointType.DOUBLE_CURVE) {- double_bezier_vector (position, start.x, start.get_right_handle ().x (), stop.get_left_handle ().x (), stop.x, out x0, out x1);- double_bezier_vector (position, start.y, start.get_right_handle ().y (), stop.get_left_handle ().y (), stop.y, out y0, out y1);+ double_bezier_vector (position, start.x, start.get_right_handle ().x, stop.get_left_handle ().x, stop.x, out x0, out x1);+ double_bezier_vector (position, start.y, start.get_right_handle ().x, stop.get_left_handle ().y, stop.y, out y0, out y1); ep.get_left_handle ().set_point_type (PointType.DOUBLE_CURVE); ep.get_right_handle ().set_point_type (PointType.DOUBLE_CURVE);@@ -1306,8 +1252,8 @@ public class Path { ep.type = PointType.DOUBLE_CURVE; } else if (right == PointType.QUADRATIC) { - x0 = quadratic_bezier_vector (1 - position, stop.x, start.get_right_handle ().x (), start.x);- y0 = quadratic_bezier_vector (1 - position, stop.y, start.get_right_handle ().y (), start.y);+ x0 = quadratic_bezier_vector (1 - position, stop.x, start.get_right_handle ().x, start.x);+ y0 = quadratic_bezier_vector (1 - position, stop.y, start.get_right_handle ().x, start.y); ep.get_right_handle ().move_to_coordinate (x0, y0); ep.get_left_handle ().set_point_type (PointType.QUADRATIC); @@ -1317,8 +1263,8 @@ public class Path { ep.type = PointType.QUADRATIC; } else if (right == PointType.CUBIC || left == PointType.CUBIC) {- bezier_vector (position, start.x, start.get_right_handle ().x (), stop.get_left_handle ().x (), stop.x, out x0, out x1);- bezier_vector (position, start.y, start.get_right_handle ().y (), stop.get_left_handle ().y (), stop.y, out y0, out y1);+ bezier_vector (position, start.x, start.get_right_handle ().x, stop.get_left_handle ().x, stop.x, out x0, out x1);+ bezier_vector (position, start.y, start.get_right_handle ().x, stop.get_left_handle ().y, stop.y, out y0, out y1); ep.get_left_handle ().set_point_type (PointType.CUBIC); ep.get_left_handle ().move_to_coordinate (x0, y0);@@ -1350,8 +1296,8 @@ public class Path { if (right == PointType.QUADRATIC) { // update connected handle if (ep.prev != null) { ep.get_left_handle ().move_to_coordinate_internal (- ep.get_prev ().right_handle.x (), - ep.get_prev ().right_handle.y ());+ ep.get_prev ().right_handle.x, + ep.get_prev ().right_handle.y); } else { warning ("ep.prev is null for quadratic point");@@ -1468,8 +1414,8 @@ public class Path { next = (!) next_point; // // FIXME: delete- bezier_vector (step, previous.x, previous.get_right_handle ().x (), next.get_left_handle ().x (), next.x, out handle_x0, out handle_x1);- bezier_vector (step, previous.y, previous.get_right_handle ().y (), next.get_left_handle ().y (), next.y, out handle_y0, out handle_y1);+ bezier_vector (step, previous.x, previous.get_right_handle ().x, next.get_left_handle ().x, next.x, out handle_x0, out handle_x1);+ bezier_vector (step, previous.y, previous.get_right_handle ().y, next.get_left_handle ().y, next.y, out handle_y0, out handle_y1); edit_point.prev = previous_point; edit_point.next = next_point;@@ -1486,21 +1432,21 @@ public class Path { } if (right == PointType.DOUBLE_CURVE || left == PointType.DOUBLE_CURVE) {- 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);+ 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); } else if (right == PointType.QUADRATIC && left == PointType.QUADRATIC) {- all_of_quadratic_curve (start.x, start.y, start.get_right_handle ().x (), start.get_right_handle ().y (), stop.x, stop.y, iter, steps);+ all_of_quadratic_curve (start.x, start.y, start.get_right_handle ().x, start.get_right_handle ().y, stop.x, stop.y, iter, steps); } else if (right == PointType.CUBIC && left == PointType.CUBIC) {- 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);+ 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); } else { warning (@"Mixed point types in segment $(start.x),$(start.y) to $(stop.x),$(stop.y)");- all_of_quadratic_curve (start.x, start.y, start.get_right_handle ().x (), start.get_right_handle ().y (), stop.x, stop.y, iter, steps);+ all_of_quadratic_curve (start.x, start.y, start.get_right_handle ().x, start.get_right_handle ().x, stop.x, stop.y, iter, steps); } } public static void get_point_for_step (EditPoint start, EditPoint stop, double step, out double x, out double y) { // FIXME: Types- x = bezier_path (step, start.x, start.get_right_handle ().x (), stop.get_left_handle ().x (), stop.x);- y = bezier_path (step, start.y, start.get_right_handle ().y (), stop.get_left_handle ().y (), stop.y); + x = bezier_path (step, start.x, start.get_right_handle ().x, stop.get_left_handle ().x, stop.x);+ y = bezier_path (step, start.y, start.get_right_handle ().y, stop.get_left_handle ().y, stop.y); } private static void all_of_double (double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, RasterIterator iter, double steps = 400) {@@ -1956,10 +1902,10 @@ public class Path { hl = e.get_left_handle (); hr = e.get_right_handle (); - lx = hl.x ();- ly = hl.y ();- rx = hr.x ();- ry = hr.y ();+ lx = hl.x;+ ly = hl.y;+ rx = hr.x;+ ry = hr.y; e.y *= -1; @@ -1977,10 +1923,10 @@ public class Path { hl = e.get_left_handle (); hr = e.get_right_handle (); - lx = hl.x ();- ly = hl.y ();- rx = hr.x ();- ry = hr.y ();+ lx = hl.x;+ ly = hl.y;+ rx = hr.x;+ ry = hr.y; e.x *= -1; @@ -2041,7 +1987,7 @@ public class Path { } public static void find_intersection_handle (EditPointHandle h1, EditPointHandle h2, out double point_x, out double point_y) {- 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);+ 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); } public void add_extrema () {@@ -394,11 +394,11 @@ public class PenTool : Tool { glyph.redraw_area (0, 0, glyph.allocation.width, glyph.allocation.height); if (GridTool.is_visible ()) {- last_point_x = Glyph.precise_reverse_path_coordinate_x (selected_handle.x ());- last_point_y = Glyph.precise_reverse_path_coordinate_y (selected_handle.y ());+ last_point_x = Glyph.precise_reverse_path_coordinate_x (selected_handle.x);+ last_point_y = Glyph.precise_reverse_path_coordinate_y (selected_handle.y); } else if (GridTool.has_ttf_grid ()) {- last_point_x = Glyph.precise_reverse_path_coordinate_x (selected_handle.x ());- last_point_y = Glyph.precise_reverse_path_coordinate_y (selected_handle.y ());+ last_point_x = Glyph.precise_reverse_path_coordinate_x (selected_handle.x);+ last_point_y = Glyph.precise_reverse_path_coordinate_y (selected_handle.y); } else { last_point_x = x; last_point_y = y;@@ -730,8 +730,8 @@ public class PenTool : Tool { // TODO: set point type path.points.get (0).left_handle.move_to_coordinate (- path.points.get (path.points.size - 1).left_handle.x (),- path.points.get (path.points.size - 1).left_handle.y ());+ path.points.get (path.points.size - 1).left_handle.x,+ path.points.get (path.points.size - 1).left_handle.y); path.points.get (0).left_handle.type = path.points.get (path.points.size - 1).left_handle.type;@@ -803,7 +803,7 @@ public class PenTool : Tool { last_rh = last_point.get_right_handle (); fist_rh = first_point.get_right_handle ();- last_rh.move_to_coordinate_internal (last_rh. x (), last_rh.y ());+ last_rh.move_to_coordinate_internal (last_rh. x, last_rh.y); union.append_path (second_path); glyph.add_path (union);@@ -146,6 +146,10 @@ public class SearchPaths { } f = (!) File.new_for_path ("/usr/share/unicode/ucd/NamesList.txt");+ if (f.query_exists ()) {+ return f;+ }+ warning ("ucd not found"); return f;@@ -148,8 +148,8 @@ class Svg { EditPoint middle; double x, y; - x = start.get_right_handle ().x () + (end.get_left_handle ().x () - start.get_right_handle ().x ()) / 2;- y = start.get_right_handle ().y () + (end.get_left_handle ().y () - start.get_right_handle ().y ()) / 2;+ x = start.get_right_handle ().x + (end.get_left_handle ().x - start.get_right_handle ().x) / 2;+ y = start.get_right_handle ().y + (end.get_left_handle ().y - start.get_right_handle ().y) / 2; middle = new EditPoint (x, y, PointType.QUADRATIC); middle.right_handle = end.get_left_handle ().copy ();@@ -59,12 +59,12 @@ public class SvgParser { TextReader tr; foreach (string l in lines) {- if (l.index_of ("<!--") > -1 && l.index_of ("Illustrator") > -1) {+ if (l.index_of ("Illustrator") > -1 || l.index_of ("illustrator") > -1) { parser.set_format (SvgFormat.ILLUSTRATOR); has_format = true; } - if (l.index_of ("<!--") > -1 && l.index_of ("Inkscape") > -1) {+ if (l.index_of ("Inkscape") > -1 || l.index_of ("inkscape") > -1) { parser.set_format (SvgFormat.INKSCAPE); has_format = true; } @@ -82,7 +82,7 @@ public class SvgParser { xml_document = sb.str; if (!has_format) {- warning ("No format identifier found.");+ stderr.printf ("No format identifier found.\n"); } Parser.init ();@@ -149,23 +149,189 @@ public class SvgParser { } private void parse_layer (Xml.Node* node, PathList pl) {+ string attr_name = "";+ string attr_content;+ PathList layer = new PathList ();+ return_if_fail (node != null);- + for (Xml.Node* iter = node->children; iter != null; iter = iter->next) { if (iter->name == "path") {- parse_path (iter, pl);+ parse_path (iter, layer); } if (iter->name == "g") {- parse_layer (iter, pl);+ parse_layer (iter, layer); } if (iter->name == "polygon") {- parse_polygon (iter, pl);+ parse_polygon (iter, layer); } }++ for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {+ attr_name = prop->name;+ attr_content = prop->children->content;+ + if (attr_name == "transform") {+ transform (attr_content, layer);+ }+ }+ + pl.append (layer); } + private void transform (string transform_functions, PathList pl) {+ string data = transform_functions.dup ();+ string[] functions;+ + // use only a single space as separator+ while (data.index_of (" ") > -1) {+ data = data.replace (" ", " ");+ }+ + return_if_fail (data.index_of (")") > -1);+ + // add separator+ data = data.replace (") ", "|");+ data = data.replace (")", "|"); + functions = data.split ("|");+ + for (int i = 0; i < functions.length; i++) {+ if (functions[i].has_prefix ("translate")) {+ translate (functions[i], pl);+ }+ + if (functions[i].has_prefix ("scale")) {+ scale (functions[i], pl);+ }++ if (functions[i].has_prefix ("matrix")) {+ matrix (functions[i], pl);+ }+ // TODO: rotate etc.+ }+ }++ public void apply_matrix (Path path, double a, double b, double c, + double d, double e, double f){+ + double dx, dy;+ Font font = BirdFont.get_current_font ();+ Glyph glyph = MainWindow.get_current_glyph ();+ + foreach (EditPoint ep in path.points) {+ apply_matrix_on_handle (ep.get_right_handle (), a, b, c, d, e, f);+ apply_matrix_on_handle (ep.get_left_handle (), a, b, c, d, e, f);++ ep.independent_y = font.top_position - ep.independent_y;+ ep.independent_x -= glyph.left_limit;+ + dx = a * ep.independent_x + c * ep.independent_y + e;+ dy = b * ep.independent_x + d * ep.independent_y + f;+ + ep.independent_x = dx;+ ep.independent_y = dy;+ + ep.independent_y = font.top_position - ep.independent_y;+ ep.independent_x += glyph.left_limit;+ }+ }++ public void apply_matrix_on_handle (EditPointHandle h, + double a, double b, double c, + double d, double e, double f){+ + double dx, dy;+ Font font = BirdFont.get_current_font ();+ Glyph glyph = MainWindow.get_current_glyph ();++ h.y = font.top_position - h.y;+ h.x -= glyph.left_limit;+ + dx = a * h.x + c * h.y + e;+ dy = b * h.x + d * h.y + f;+ + h.x = dx;+ h.y = dy;+ + h.y = font.top_position - h.y;+ h.x += glyph.left_limit;+ }+++ private void matrix (string function, PathList pl) {+ string parameters = get_transform_parameters (function);+ string[] p = parameters.split (" ");++ if (p.length != 6) {+ warning ("Expecting six parameters for matrix transformation.");+ return;+ }++ foreach (Path path in pl.paths) {+ apply_matrix (path, parse_double (p[0]), parse_double (p[1]), + parse_double (p[2]), parse_double (p[3]), + parse_double (p[4]), parse_double (p[5]));+ }+ }+ + private void scale (string function, PathList pl) {+ string parameters = get_transform_parameters (function);+ string[] p = parameters.split (" ");+ double x, y;+ + x = 1;+ y = 1;+ + if (p.length > 0) {+ x = parse_double (p[0]);+ }+ + if (p.length > 1) {+ y = parse_double (p[1]);+ }+ + foreach (Path path in pl.paths) {+ path.scale (-x, y);+ }+ }+ + private void translate (string function, PathList pl) {+ string parameters = get_transform_parameters (function);+ string[] p = parameters.split (" ");+ double x, y;+ + x = 0;+ y = 0;+ + if (p.length > 0) {+ x = parse_double (p[0]);+ }+ + if (p.length > 1) {+ y = parse_double (p[1]);+ }+ + foreach (Path path in pl.paths) {+ path.move (-x, y);+ }+ }++ private string get_transform_parameters (string function) {+ int i;+ string param = "";+ + i = function.index_of ("(");+ return_val_if_fail (i != -1, param);+ param = function.substring (i);++ param = param.replace ("(", "");+ param = param.replace (",", " ");+ + return param; + }+ private void parse_polygon (Xml.Node* node, PathList pl) { string attr_name = ""; string attr_content;@@ -827,7 +993,6 @@ public class SvgParser { // move all points if (svg_glyph) {- // move only y b[i].x0 += glyph.left_limit; b[i].y0 += font.base_line; b[i].x1 += glyph.left_limit;@@ -46,7 +46,8 @@ class TestCases { add (test_extrema, "Extrema"); add (test_codepages, "Codepages"); add (test_double_quadratic, "Double quadratic");- + add (test_raster_path, "Raster path");+ add_bechmark (benchmark_stroke, "Stroke"); } @@ -1243,6 +1244,30 @@ class TestCases { private static void test_boundaries () { Preferences.draw_boundaries = true; }+ + private static void test_raster_path () {+ Glyph g = MainWindow.get_current_glyph ();+ PathList pl = new PathList ();+ Path path;+ + test_open_next_glyph ();+ test_inkscape_import (); // draw somee thing+ + foreach (Path p in g.path_list) {+ path = new Path ();+ p.all_of_path ((cx, cy, ct) => {+ path.add (cx, cy);+ return true;+ });+ pl.add (path);+ }+ + test_open_next_glyph ();+ + foreach (Path p in pl.paths) {+ g.add_path (p);+ }+ } } }