Updated Files
libbirdfont/ClickMap.vala |
libbirdfont/EditPoint.vala |
libbirdfont/Font.vala |
libbirdfont/OpenFontFormat/GlyfData.vala |
libbirdfont/OpenFontFormat/GlyfTable.vala |
libbirdfont/OpenFontFormat/HmtxTable.vala |
libbirdfont/OpenFontFormat/OpenFontFormatReader.vala |
libbirdfont/Path.vala |
libbirdfont/PenTool.vala |
libbirdfont/PointConverter.vala |
libbirdfont/StrokeTool.vala |
scripts/version.py |
--- a/libbirdfont/ClickMap.vala
+++ b/libbirdfont/ClickMap.vala
@@ -17,53 +17,103 @@
namespace BirdFont {
public class ClickMap : GLib.Object {
- ImageSurface map;
- int width;
+ public ImageSurface map;
+ public int width;
+ public double xmax;
+ public double ymax;
+ public double xmin;
+ public double ymin;
+
public ClickMap (int width) {
this.width = width;
map = new Cairo.ImageSurface (Cairo.Format.ARGB32, width, width);
+ xmax = 0;
+ ymax = 0;
+ xmin = 0;
+ ymin = 0;
}
public bool get_value (int x, int y) {
unowned uchar[] d = map.get_data ();
bool transparent;
+
+ Context c;
+
+ c = new Context (map);
+ c.set_fill_rule (Cairo.FillRule.WINDING);
if (unlikely (!(0 <= x < width && 0 <= y < width))) {
- warning ("Array index out of bounds.");
+ warning (@"Array index out of bounds. x: $x y: $y size: $width");
return true;
}
transparent = d[y * map.get_stride () + 4 * x + 3] == 0;
-
+
return !transparent;
}
-
- public void create_click_map (Path path) {
+
+ bool add_point (Context c, double cx, double cy) {
+ int px = (int) (width * ((cx - xmin) / (xmax - xmin)));
+ int py = (int) (width * ((cy - ymin) / (ymax - ymin)));
+ c.line_to (px, py);
+ return true;
+ }
+
+ public void create_click_map (Path path, Path? counter = null) { // FIXME: clean up
+ Path p;
Context c;
-
+
c = new Context (map);
- c.save ();
+ if (counter == null) {
+ xmax = path.xmax;
+ ymax = path.ymax;
+ xmin = path.xmin;
+ ymin = path.ymin;
+ } else {
+ p = (!) counter;
+
+ xmax = fmax (p.xmax, path.xmax);
+ ymax = fmax (p.ymax, path.ymax);
+ xmin = fmin (p.xmin, path.xmin);
+ ymin = fmin (p.ymin, path.ymin);
+ }
+ c.save ();
+ c.set_fill_rule (FillRule.EVEN_ODD);
c.set_source_rgba (0, 0, 0, 1);
c.new_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)));
-
- c.line_to (px, py);
-
+ path.all_of_path ((cx, cy) => {
+ add_point (c, cx, cy);
return true;
}, 2 * width);
+ c.close_path ();
- c.close_path ();
- c.fill ();
+ if (counter != null) {
+ c.new_path ();
+ p = (!) counter;
+ p.all_of_path ((cx, cy) => {
+ add_point (c, cx, cy);
+ return true;
+ }, 2 * width);
+
+ c.close_path ();
+ }
+
+ c.fill ();
c.restore ();
- }
+ }
}
+ static double fmin (double a, double b) {
+ return a < b ? a : b;
+ }
+
+ static double fmax (double a, double b) {
+ return a > b ? a : b;
+ }
+
}
--- a/libbirdfont/EditPoint.vala
+++ b/libbirdfont/EditPoint.vala
@@ -44,8 +44,8 @@
public static uint DELETED = 1 << 2;
public static uint TIE = 1 << 3;
public static uint REFLECTIVE = 1 << 4;
- public static uint CORNER = 1 << 5;
- public static uint INTERSECTION = 1 << 6;
+ public static uint INTERSECTION = 1 << 5;
+ public static uint NEW_CORNER = 1 << 6;
public uint flags = NONE;
@@ -150,6 +150,8 @@
left_handle.y -= d;
}
}
+
+ public Color? color = null;
public EditPoint (double nx = 0, double ny = 0, PointType nt = PointType.NONE) {
x = nx;
@@ -436,7 +438,9 @@
new_point.left_handle.angle = left_handle.angle;
new_point.left_handle.length = left_handle.length;
new_point.left_handle.type = left_handle.type;
-
+
+ new_point.color = color;
+
return new_point;
}
--- a/libbirdfont/Font.vala
+++ b/libbirdfont/Font.vala
@@ -485,18 +485,7 @@
/** Obtain all versions and alterntes for this glyph. */
public GlyphCollection? get_glyph_collection (string glyph) {
- GlyphCollection? gc = get_cached_glyph_collection (glyph);
- Glyph? g;
-
- if (gc == null && otf_font) {
- // load it from otf file if we need to
- g = otf.read_glyph (glyph);
-
- if (g != null) {
- return get_cached_glyph_collection (glyph);
- }
- }
-
+ GlyphCollection? gc = get_cached_glyph_collection (glyph);
return gc;
}
--- a/libbirdfont/OpenFontFormat/GlyfData.vala
+++ b/libbirdfont/OpenFontFormat/GlyfData.vala
@@ -36,6 +36,7 @@
/** Data for one entry in the glyf table. */
public class GlyfData : GLib.Object {
public Gee.ArrayList<Path> paths = new Gee.ArrayList<Path> ();
+ public Gee.ArrayList<EditPoint> points = new Gee.ArrayList<EditPoint> ();
public Gee.ArrayList<uint16> end_points = new Gee.ArrayList<uint16> ();
public Gee.ArrayList<uint8> flags = new Gee.ArrayList<uint8> ();
public Gee.ArrayList<int16> coordinate_x = new Gee.ArrayList<int16> ();
@@ -55,26 +56,48 @@
get { return HeadTable.UNITS; }
}
- public GlyfData (Glyph g) {
+ public GlyfData (Glyph g) {
+ bool process;
+ PathList qp = g.get_quadratic_paths ();
+
glyph = g;
- foreach (Path p in g.get_quadratic_paths ().paths) {
+ foreach (Path p in qp.paths) {
if (p.points.size > 0) {
if (!is_empty (p)) {
// Add points at extrema
p.add_extrema ();
- paths.add (p);
}
}
}
- if (paths.size > 0) {
+ process = true;
+
+ while (process) {
+ points.clear ();
+ paths.clear ();
+ foreach (Path p in qp.paths) {
+ if (!is_empty (p)) {
+ paths.add (p);
+ foreach (EditPoint ep in p.points) {
+ points.add (ep);
+ }
+ }
+ }
+
+ if (paths.size == 0) {
+ break;
+ }
+
process_end_points ();
process_flags ();
process_x ();
- process_y ();
+
+ // error checking is done here
+ process = !process_y ();
+
process_bounding_box ();
- }
+ }
}
bool is_empty (Path p) {
@@ -112,6 +135,7 @@
uint16 last_end_point = 0;
PointType type;
+ end_points.clear ();
end_point = 0;
foreach (Path quadratic in paths) {
@@ -187,6 +211,7 @@
double x;
PointType type;
+ coordinate_x.clear ();
foreach (Path p in paths) {
foreach (EditPoint e in p.points) {
x = rint (e.x * UNITS - prev - glyph.left_limit * UNITS);
@@ -205,16 +230,30 @@
}
}
- void process_y () {
+ bool process_y () {
double prev = 0;
double y;
Font font = OpenFontFormatWriter.get_current_font ();
PointType type;
+ int epi = 0;
+
+ coordinate_y.clear ();
foreach (Path p in paths) {
foreach (EditPoint e in p.points) {
y = rint (e.y * UNITS - prev - font.base_line * UNITS);
coordinate_y.add ((int16) y);
+
+ if ((int16) y == 0 && (int16) coordinate_x.get (coordinate_y.size - 1) == 0) {
+ warning (@"Point on point in TTF. Index $(coordinate_y.size - 1)");
+
+ if (BirdFont.has_argument ("--test")) {
+ print (glyph.get_name () + "\n");
+ print (points.get (epi).to_string ());
+ PenTool.remove_point_simplify (new PointSelection (points.get (epi), p));
+ return false;
+ }
+ }
prev = rint (e.y * UNITS - font.base_line * UNITS);
@@ -223,10 +262,24 @@
// off curve
y = rint (e.get_right_handle ().y * UNITS - prev - font.base_line * UNITS);
coordinate_y.add ((int16) y);
-
+
+ if ((int16) y == 0 && (int16) coordinate_x.get (coordinate_y.size - 1) == 0) {
+ warning (@"Point on point in TTF (off curve) Index: $(coordinate_y.size - 1) ");
+ if (BirdFont.has_argument ("--test")) {
+ print (glyph.get_name () + "\n");
+ print (points.get (epi).to_string ());
+
+ PenTool.remove_point_simplify (new PointSelection (points.get (epi), p));
+ return false;
+ }
+ }
+
prev = rint (e.get_right_handle ().y * UNITS - font.base_line * UNITS);
+ epi++;
}
}
+
+ return true;
}
void process_bounding_box () {
@@ -283,4 +336,5 @@
}
}
+
--- a/libbirdfont/OpenFontFormat/GlyfTable.vala
+++ b/libbirdfont/OpenFontFormat/GlyfTable.vala
@@ -318,20 +318,6 @@
fd.add (0);
}
printd (@"length after padding: $(fd.length ())\n");
- }
-
- public Glyph? read_glyph (string name) throws GLib.Error {
- Glyph? glyph;
- int i;
-
- i = post_table.get_gid (name);
-
- if (i == -1) {
- return null;
- }
-
- glyph = parse_index (i, dis, loca_table, hmtx_table, head_table, post_table);
- return glyph;
}
public new void parse (FontData dis, CmapTable cmap_table, LocaTable loca, HmtxTable hmtx_table, HeadTable head_table, PostTable post_table, KernTable kern_table) throws GLib.Error {
@@ -342,56 +328,6 @@
this.head_table = head_table;
this.kern_table = kern_table;
this.dis = dis;
- }
-
- Glyph parse_index (int index, FontData dis, LocaTable loca, HmtxTable hmtx_table, HeadTable head_table, PostTable post_table) throws GLib.Error {
-
- // FIXME: DELETE parse with freetype
- Glyph glyph = new Glyph ("");
- /*
- double xmin, xmax;
- double units_per_em = head_table.get_units_per_em ();
- unichar character = 0;
- string name;
-
- character = cmap_table.get_char (index);
- name = post_table.get_name (index);
-
- if (name == "") {
- StringBuilder name_c = new StringBuilder ();
- name_c.append_unichar (character);
- name = name_c.str;
- }
-
- printd (@"name: $(name)\n");
-
- if (!loca.is_empty (index)) {
- glyph = parse_next_glyf (dis, character, index, out xmin, out xmax, units_per_em);
-
- glyph.left_limit = xmin - hmtx_table.get_lsb (index);
- glyph.left_limit = 0;
- glyph.right_limit = glyph.left_limit + hmtx_table.get_advance (index);
- } else {
- // add empty glyph
- glyph = new Glyph (name, character);
- glyph.left_limit = -hmtx_table.get_lsb (index);
- glyph.right_limit = hmtx_table.get_advance (index) - hmtx_table.get_lsb (index);
- }
-
- glyph.name = name;
-
- if (character == 0) {
- glyph.set_unassigned (true);
- }
-
- if (character == 0 && name != "") {
- stderr.printf (@"Got null character\n");
- stderr.printf (@"gid: $index\n");
- stderr.printf (@"char: $((uint) character)\n");
- stderr.printf (@"name: $(name)\n");
- }
- */
- return glyph;
}
Glyph parse_next_composite_glyf (FontData dis, unichar character, int pgid) throws Error {
--- a/libbirdfont/OpenFontFormat/HmtxTable.vala
+++ b/libbirdfont/OpenFontFormat/HmtxTable.vala
@@ -173,7 +173,9 @@
font_data = fd;
- warn_if_fail (max_advance != 0);
+ if (max_advance != 0) {
+ warning ("max_advance is zero");
+ }
}
public int16 get_average_width () {
--- a/libbirdfont/OpenFontFormat/OpenFontFormatReader.vala
+++ b/libbirdfont/OpenFontFormat/OpenFontFormatReader.vala
@@ -58,16 +58,6 @@
public void close () {
dis.close ();
- }
-
- public Glyph? read_glyph (string name) {
- try {
- return directory_table.glyf_table.read_glyph (name);
- } catch (GLib.Error e) {
- warning (e.message);
- }
-
- return null;
}
public void parse_index (string file_name) throws Error {
--- a/libbirdfont/Path.vala
+++ b/libbirdfont/Path.vala
@@ -410,8 +410,8 @@
}
public void draw_edit_point_handles (EditPoint e, Context cr) {
- string color_left = "Control Point Handle";
- string color_right = "Control Point Handle";
+ Color color_left = Theme.get_color ("Control Point Handle");
+ Color color_right = Theme.get_color ("Control Point Handle");
EditPoint handle_right = e.get_right_handle ().get_point ();
EditPoint handle_left = e.get_left_handle ().get_point ();
@@ -419,19 +419,19 @@
if (e.type != PointType.HIDDEN) {
if (e.get_right_handle ().selected) {
- color_right = "Selected Control Point Handle";
+ color_right = Theme.get_color ("Selected Control Point Handle");
} else if (e.get_right_handle ().active) {
- color_right = "Active Control Point Handle";
+ color_right = Theme.get_color ("Active Control Point Handle");
} else {
- color_right = "Control Point Handle";
+ color_right = Theme.get_color ("Control Point Handle");
}
if (e.get_left_handle ().selected) {
- color_left = "Selected Control Point Handle";
+ color_left = Theme.get_color ("Selected Control Point Handle");
} else if (e.get_left_handle ().active) {
- color_left = "Active Control Point Handle";
+ color_left = Theme.get_color ("Active Control Point Handle");
} else {
- color_left = "Control Point Handle";
+ color_left = Theme.get_color ("Control Point Handle");
}
if (!hide_end_handle || !(is_open () && e == points.get (points.size - 1))) {
@@ -447,40 +447,76 @@
}
public static void draw_edit_point_center (EditPoint e, Context cr) {
+ Color c;
+
if (e.type != PointType.HIDDEN) {
if (e.type == PointType.CUBIC || e.type == PointType.LINE_CUBIC) {
if (e.is_selected ()) {
if (e.active_point) {
- draw_control_point (cr, e.x, e.y, "Selected Active Cubic Control Point");
+ if (e.color != null) {
+ c = (!) e.color;
+ } else {
+ c = Theme.get_color ("Selected Active Cubic Control Point");
+ }
} else {
- draw_control_point (cr, e.x, e.y, "Selected Cubic Control Point");
+ if (e.color != null) {
+ c = (!) e.color;
+ } else {
+ c = Theme.get_color ("Selected Cubic Control Point");
+ }
}
} else {
if (e.active_point) {
- draw_control_point (cr, e.x, e.y, "Active Cubic Control Point");
+ if (e.color != null) {
+ c = (!) e.color;
+ } else {
+ c = Theme.get_color ("Active Cubic Control Point");
+ }
} else {
- draw_control_point (cr, e.x, e.y, "Cubic Control Point");
+ if (e.color != null) {
+ c = (!) e.color;
+ } else {
+ c = Theme.get_color ("Cubic Control Point");
+ }
}
}
} else {
if (e.is_selected ()) {
if (e.active_point) {
- draw_control_point (cr, e.x, e.y, "Selected Active Quadratic Control Point");
+ if (e.color != null) {
+ c = (!) e.color;
+ } else {
+ c = Theme.get_color ("Selected Active Quadratic Control Point");
+ }
} else {
- draw_control_point (cr, e.x, e.y, "Selected Quadratic Control Point");
+ if (e.color != null) {
+ c = (!) e.color;
+ } else {
+ c = Theme.get_color ("Selected Quadratic Control Point");
+ }
}
} else {
if (e.active_point) {
- draw_control_point (cr, e.x, e.y, "Active Quadratic Control Point");
+ if (e.color != null) {
+ c = (!) e.color;
+ } else {
+ c = Theme.get_color ("Active Quadratic Control Point");
+ }
} else {
- draw_control_point (cr, e.x, e.y, "Quadratic Control Point");
+ if (e.color != null) {
+ c = (!) e.color;
+ } else {
+ c = Theme.get_color ("Quadratic Control Point");
+ }
}
}
}
+
+ draw_control_point (cr, e.x, e.y, c);
}
}
- public static void draw_control_point (Context cr, double x, double y, string color, double size = 3.5) {
+ public static void draw_control_point (Context cr, double x, double y, Color color, double size = 3.5) {
Glyph g = MainWindow.get_current_glyph ();
double ivz = 1 / g.view_zoom;
double width = size * Math.sqrt (stroke_width) * ivz;
@@ -492,7 +528,7 @@
x = xc + x - (width / 2.0) * ivz;
y = yc - y - (width / 2.0) * ivz;
- Theme.color (cr, color);
+ cr.set_source_rgba (color.r, color.g, color.b, color.a);
cr.move_to (x, y);
cr.arc (x, y, width, 0, 2 * Math.PI);
@@ -764,8 +800,7 @@
if (!is_over_boundry (x, y)) {
return false;
}
-
-
+
// generate a rasterized image of the object
width = 160;
click_map = new ClickMap (width);
@@ -1667,7 +1702,7 @@
int i;
int index = 0;
- if (!has_deleted_point ()) {
+ if (!has_deleted_point ()) {
return path_list;
}
@@ -1676,7 +1711,7 @@
return path_list;
}
- // set start position to the point that will be removed
+ // set start position to a point that will be removed
for (i = 0; i < points.size; i++) {
p = points.get (i);
@@ -1959,6 +1994,7 @@
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);
}
+ /** Finx intersection point for two straight lines. */
public static void find_intersection_point (EditPoint p1, EditPoint p2, EditPoint q1, EditPoint q2, out double point_x, out double point_y) {
find_intersection (p1.x, p1.y, p2.x, p2.y, q1.x, q1.y, q2.x, q2.y, out point_x, out point_y);
}
--- a/libbirdfont/PenTool.vala
+++ b/libbirdfont/PenTool.vala
@@ -1209,16 +1209,16 @@
if (active_handle.active) {
Path.draw_control_point (cr, Glyph.path_coordinate_x (begin_action_x),
- Glyph.path_coordinate_y (begin_action_y), "Control Point Handle");
+ Glyph.path_coordinate_y (begin_action_y), Theme.get_color ("Control Point Handle"));
} else if (selected_points.size > 0) {
ps = selected_points.get (selected_points.size - 1);
if (ps.point.type == PointType.CUBIC) {
Path.draw_control_point (cr, Glyph.path_coordinate_x (begin_action_x),
- Glyph.path_coordinate_y (begin_action_y), "Selected Cubic Control Point");
+ Glyph.path_coordinate_y (begin_action_y), Theme.get_color ("Selected Cubic Control Point"));
} else {
Path.draw_control_point (cr, Glyph.path_coordinate_x (begin_action_x),
- Glyph.path_coordinate_y (begin_action_y), "Selected Quadratic Control Point");
+ Glyph.path_coordinate_y (begin_action_y), Theme.get_color ("Selected Quadratic Control Point"));
}
}
}
--- a/libbirdfont/PointConverter.vala
+++ b/libbirdfont/PointConverter.vala
@@ -38,8 +38,6 @@
warning ("Too many points in segment.");
}
- quadratic_path.remove_points_on_points ();
-
if (quadratic_path.points.size < 2) {
return new Path ();
}
--- a/libbirdfont/StrokeTool.vala
+++ b/libbirdfont/StrokeTool.vala
@@ -18,10 +18,17 @@
namespace BirdFont {
public class StrokeTool : Tool {
+
+ static bool stroke_selected = false;
+ static int iterations = 0;
public StrokeTool (string tooltip) {
+ iterations = 10;
select_action.connect((self) => {
+ stroke_selected = true;
+ iterations++;
stroke_selected_paths ();
+ stroke_selected = false;
});
}
@@ -50,29 +57,39 @@
}
public static PathList get_stroke (Path path, double thickness) {
- Path p = path.copy ();
- PathList pl;
+ PathList pl = new PathList ();
+ StrokeParts parts;
+
+ parts = get_parts (path.copy (), thickness, path);
- pl = get_stroke_outline (p, thickness);
+ foreach (Path p in parts.get_all ()) {
+ p.get_first_point ().color = new Color (0, 1, 0, 1);
+ pl.append (get_stroke_outline (p, thickness));
+ }
return pl;
}
public static PathList get_stroke_outline (Path p, double thickness) {
- Path counter, outline, merged;
+ Path counter, outline;
+ Path merged;
PathList paths = new PathList ();
-
+ StrokeParts parts;
+
if (!p.is_open () && p.is_filled ()) {
outline = create_stroke (p, thickness);
outline.close ();
- paths.add (outline);
- outline.update_region_boundaries ();
+
+ parts = remove_intersections (outline, thickness, p);
+
+ foreach (Path sp in parts.get_all ()) {
+ paths.add (sp);
+ sp.update_region_boundaries ();
+ }
} else if (!p.is_open () && !p.is_filled ()) {
outline = create_stroke (p, thickness);
counter = create_stroke (p, -1 * thickness);
- paths.add (outline);
- paths.add (counter);
-
+
if (p.is_clockwise ()) {
outline.force_direction (Direction.CLOCKWISE);
} else {
@@ -85,8 +102,19 @@
counter.force_direction (Direction.CLOCKWISE);
}
- outline.update_region_boundaries ();
- counter.update_region_boundaries ();
+ parts = remove_intersections (outline, thickness, p);
+
+ foreach (Path sp in parts.get_all ()) {
+ paths.add (sp);
+ sp.update_region_boundaries ();
+ }
+
+ parts = remove_intersections (counter, thickness, p);
+
+ foreach (Path sp in parts.get_all ()) {
+ paths.add (sp);
+ sp.update_region_boundaries ();
+ }
} else if (p.is_open ()) {
outline = create_stroke (p, thickness);
counter = create_stroke (p, -1 * thickness);
@@ -97,9 +125,13 @@
} else {
merged.force_direction (Direction.COUNTER_CLOCKWISE);
}
+
+ parts = remove_intersections (merged, thickness, p);
- merged.update_region_boundaries ();
- paths.add (merged);
+ foreach (Path sp in parts.get_all ()) {
+ paths.add (sp);
+ sp.update_region_boundaries ();
+ }
} else {
warning ("Can not create stroke.");
paths.add (p);
@@ -139,10 +171,12 @@
static Path create_stroke (Path p, double thickness) {
Path stroked;
+ Path path;
if (p.points.size >= 2) {
- stroked = p.copy ();
- stroked = generate_stroke (stroked, thickness);
+ path = p.copy ();
+ path.remove_points_on_points ();
+ stroked = generate_stroke (path, thickness);
if (!p.is_open ()) {
stroked.reverse ();
@@ -153,9 +187,7 @@
warning ("One point.");
stroked = new Path ();
}
-
- remove_self_intersections (stroked);
-
+
return stroked;
}
@@ -166,7 +198,7 @@
EditPoint previous;
int i;
- previous = p.get_last_point ().copy ();
+ previous = p.get_first_point ().copy ();
move_segment (start, previous, thickness);
i = 0;
@@ -176,10 +208,12 @@
move_segment (start, end, thickness);
- if (end.get_left_handle ().length > 0 && end.get_right_handle ().length > 0) {
- if (!p.is_open () || (i != 0 && i != p.points.size - 1)) { // FIXME: first point i=0
- add_corner (stroked, previous, start, ep.copy (), thickness);
- }
+ if (start == p.get_last_point ()) {
+ end = p.get_first_point ();
+ }
+
+ if (!p.is_open () || (i != 0 && i != p.points.size - 1)) {
+ add_corner (stroked, previous, start, ep.copy (), thickness);
}
stroked.add_point (start);
@@ -253,10 +287,16 @@
corner = new EditPoint (corner_x, corner_y, previous.type);
corner.convert_to_line ();
+ previous_handle.angle -= PI;
+ next_handle.angle -= PI;
+
distance = Path.distance_to_point (corner, original);
+ ratio = fabs (stroke_width) / distance; // FIXME: cutoff parameter
- ratio = fabs (stroke_width) / distance;
-
+ double r = original.get_right_handle ().angle;
+ double l = original.get_left_handle ().angle;
+ double angle = atan2 (sin (r - l), cos (r - l));
+
if (ratio > 1) {
stroked.add_point (corner);
} else {
@@ -273,74 +313,200 @@
cutoff2.x = next.x + (corner.x - next.x) * ratio;
cutoff2.y = next.y + (corner.y - next.y) * ratio;
+
+ cutoff1 = stroked.add_point (cutoff1);
+ cutoff2 = stroked.add_point (cutoff2);
- stroked.add_point (cutoff1);
- stroked.add_point (cutoff2);
- }
+ cutoff1.recalculate_linear_handles ();
+ cutoff2.recalculate_linear_handles ();
+ }
+ }
- previous_handle.angle -= PI;
- next_handle.angle -= PI;
+ static StrokeParts remove_intersections (Path path, double thickness, Path original) {
+ StrokeParts parts;
+
+ parts = get_parts (path, thickness, original);
+ delete_intersection_parts (original, parts.parts, thickness);
+
+ return parts;
+ }
+
+ static StrokeParts get_parts (Path path, double thickness, Path original) {
+ StrokeParts sp;
+ PathList pl = new PathList ();
+ StrokeParts parts = new StrokeParts ();
+ Path remaining_points = path;
+
+ while (add_self_intersection_points (remaining_points)) {
+ foreach (EditPoint p in remaining_points.points) {
+ if ((p.flags & EditPoint.INTERSECTION) > 0) {
+ p.deleted = true;
+ // FIXME: delete p.color = new Color (1, 1, 0, 1);
+ }
+ }
+
+ remaining_points = get_remaining_points (remaining_points, parts);
+ }
+
+ foreach (Path p in parts.parts.paths) {
+ p.close ();
+ sp = get_parts (p, thickness, original);
+ pl.add (sp.path);
+ pl.append (sp.parts);
+ }
+
+ parts.parts.clear ();
+
+ foreach (Path p in pl.paths) {
+ parts.parts.add (p);
+ }
+
+ parts.path = remaining_points;
+
+ return parts;
}
- static void remove_self_intersections (Path p) {
- bool keep = true;
+ static PathList process_deleted_control_points (Path path) {
+ PathList paths, nl, pl, rl;
- add_self_intersection_points (p);
-
- // FIXME: set start on non intersecting point
- foreach (EditPoint ep in p.points) {
- if ((ep.flags & EditPoint.INTERSECTION) > 0) {
- print (@"Inter $(ep)");
- keep = !keep;
+ paths = new PathList ();
+ rl = new PathList ();
+ pl = new PathList ();
+ nl = new PathList ();
+
+ if (!path.has_deleted_point ()) {
+ return pl;
+ }
+
+ pl.add (path);
+
+ foreach (Path p in pl.paths) {
+ nl = p.process_deleted_points ();
+
+ if (nl.paths.size > 0) {
+ rl.append (nl);
+ rl.paths.remove (p);
}
+ }
+
+ foreach (Path p in rl.paths) {
+ pl = process_deleted_control_points (p);
- if (!keep && (ep.flags & EditPoint.INTERSECTION) == 0) {
- ep.deleted = true;
- ep.type = PointType.CUBIC;
+ if (pl.paths.size > 0) {
+ paths.append (pl);
+ } else {
+ paths.add (p);
}
}
- p.remove_deleted_points ();
+ return paths;
}
- static void add_self_intersection_points (Path path) {
- Gee.ArrayList<EditPoint> n = new Gee.ArrayList<EditPoint> ();
+ static Path get_remaining_points (Path old_path, StrokeParts parts) {
+ Path new_path;
+ PathList pl;
+
+ old_path.close ();
+ pl = process_deleted_control_points (old_path);
+
+ if (pl.paths.size == 0) {
+ return old_path;
+ }
+
+ new_path = new Path ();
+ foreach (Path pn in pl.paths) {
+ if (pn.points.size > new_path.points.size) {
+ new_path = pn;
+ }
+
+ if (stroke_selected) // FIXME: DELETE
+ ((!) BirdFont.get_current_font ().get_glyph ("a")).add_path (pn);
+ }
+ new_path.reopen ();
+
+ foreach (Path pn in pl.paths) {
+ if (pn != new_path) {
+ parts.parts.add (pn);
+ }
+ }
+
+ if (new_path.has_deleted_point ()) {
+ warning ("Points left.");
+ }
+
+ return get_remaining_points (new_path, parts);
+ }
+
+ static bool add_self_intersection_points (Path path) {
+ bool intersection = false;
+
+ path.get_first_point ().color = new Color (0, 1, 0, 1);
+
path.all_segments ((ep1, ep2) => {
double ix, iy;
- EditPoint nep;
- EditPoint nep2;
+
EditPoint p1;
EditPoint p2;
- if (segment_intersects (path, ep1, ep2, out ix, out iy, out p1, out p2)) {
- nep = new EditPoint ();
- nep.prev = ep1;
- nep.next = ep2;
-
- nep.x = ix;
- nep.y = iy;
-
- n.add (nep);
-
- nep2 = new EditPoint ();
- nep2.prev = p1;
- nep2.next = p2;
-
- nep2.x = ix;
- nep2.y = iy;
-
- n.add (nep2);
+ if (segment_intersects (path, ep1, ep2, out ix, out iy, out p1, out p2)) {
+ add_intersection (path, ep1, ep2, ix, iy);
+ add_intersection (path, p1, p2, ix, iy);
+ intersection = true;
+ return false;
}
return true;
});
+ return intersection;
+ }
+
+ static void add_intersection (Path path, EditPoint prev, EditPoint next, double px, double py, Color? c = null) {
+ Gee.ArrayList<EditPoint> n = new Gee.ArrayList<EditPoint> ();
+ EditPoint ep1 = new EditPoint ();
+ EditPoint ep2 = new EditPoint ();
+ EditPoint ep3 = new EditPoint ();
+
+ if (prev == path.get_last_point ()) { // FIXME: double check
+ ep1.prev = null;
+ } else {
+ ep1.prev = prev;
+ }
+
+ ep1.prev = prev;
+ ep1.next = ep2;
+ ep1.flags |= EditPoint.NEW_CORNER;
+ ep1.type = PointType.CUBIC;
+ ep1.x = px;
+ ep1.y = py;
+ ep1.color = c;
+ n.add (ep1);
+
+ ep2.prev = ep1;
+ ep2.next = ep3;
+ ep2.flags |= EditPoint.INTERSECTION;
+ ep2.type = PointType.QUADRATIC;
+ ep2.x = px;
+ ep2.y = py;
+ ep2.color = c;
+ n.add (ep2);
+
+ ep3.prev = ep2;
+ ep3.next = next;
+ ep3.flags |= EditPoint.NEW_CORNER;
+ ep3.type = PointType.CUBIC;
+ ep3.x = px;
+ ep3.y = py;
+ ep3.color = c;
+ n.add (ep3);
+
foreach (EditPoint np in n) {
- path.insert_new_point_on_path (np, -1, true);
- np.type = PointType.QUADRATIC;
- np.flags |= EditPoint.INTERSECTION;
+ np = path.add_point_after (np, np.prev);
+ path.create_list ();
}
+
+ path.recalculate_linear_handles ();
}
static bool segment_intersects (Path path, EditPoint ep, EditPoint next,
@@ -360,32 +526,115 @@
}
// FIXME: last to first
- for (int i = 1; i < path.points.size - 2; i++) {
+ for (int i = 2; i < path.points.size - 2; i++) {
p1 = path.points.get (i - 1);
p2 = path.points.get (i);
Path.find_intersection_point (ep, next, p1, p2, out cross_x, out cross_y);
-
- if ((p1.x < cross_x < p2.x || p1.x > cross_x > p2.x)
- && (p1.y < cross_y < p2.y || p1.y > cross_y > p2.y)
- && (ep.x < cross_x < next.x || ep.x > cross_x > next.x)
- && (ep.y < cross_y < next.y || ep.y > cross_y > next.y)) {
+
+ if (Glyph.CANVAS_MIN < cross_x < Glyph.CANVAS_MAX
+ && Glyph.CANVAS_MIN < cross_y < Glyph.CANVAS_MAX) {
+ // iterate to find intersection.
+
+ if (!((ep.x == cross_x && ep.y == cross_y)
+ || (next.x == cross_x && next.y == cross_y)
+ || (p1.x == cross_x && p1.y == cross_y)
+ || (p2.x == cross_x && p2.y == cross_y))) {
+
+ if (is_line (ep.x, ep.y, cross_x, cross_y, next.x, next.y)
+ && is_line (p1.x, p1.y, cross_x, cross_y, p2.x, p2.y)) {
- // iterate to find intersection.
- ix = cross_x;
- iy = cross_y;
-
- ia = p1;
- ib = p2;
-
- return true;
- }
+ // FIXME: delete ep.color = new Color (1, 0, 0, 1);
+ next.color = new Color (0.5, 0, 0, 1);
+
+ // FIXME: delete p1.color = new Color (0, 0, 1, 1);
+ // FIXME: delete p2.color = new Color (0, 0, 0.5, 1);
+
+ ix = cross_x;
+ iy = cross_y;
+
+ ia = p1;
+ ib = p2;
+
+ return true;
+ }
+ }
+ }
}
return false;
}
- }
+
+ /** @return true if p2 is on the line p1 to p3 */
+ static bool is_line (double x1, double y1, double x2, double y2, double x3, double y3) {
+ double ds = Path.distance (x1, x3, y1, y3);
+ double d1 = Path.distance (x1, x2, y1, y2);
+ double d2 = Path.distance (x2, x3, y2, y3);
+ double p = d1 / ds;
+ double x = fabs ((x3 - x1) * p - (x2 - x1));
+ double y = fabs ((y3 - y1) * p - (y2 - y1));
+ double d = fabs (ds - (d1 + d2));
+
+ return ds > 0.001 && d1 > 0.001 && d2 > 0.001
+ && d < 0.001 && x < 0.001 && y < 0.001
+ && fmin (x1, x3) < x2 && x2 < fmax (x1, x3)
+ && fmin (y1, y3) < y2 && y2 < fmax (y1, y3);
+ }
+ static void delete_intersection_parts (Path original, PathList parts, double stroke_width) {
+ PathList remove = new PathList ();
+
+ foreach (Path p in parts.paths) {
+ if (is_stroke (original, p, stroke_width)) {
+ remove.add (p);
+ }
+ }
+
+ foreach (Path p in remove.paths) {
+ parts.paths.remove (p);
+ }
+ }
+
+ static bool is_stroke (Path original, Path part, double stroke_width) {
+ double stroke_size = fabs (stroke_width);
+ bool stroke = false;
+
+ original.all_of_path ((cx, cy, ct) => {
+ foreach (EditPoint p in part.points) {
+ if (Path.distance (cx, p.x, cy, p.y) < stroke_size - 0.1) {
+ if (48 < p.x < 50) print (@"D: $(Path.distance (cx, p.x, cy, p.y)) < $(stroke_size) \n");
+
+ p.color = new Color (1, 0, 1, 1); // FIXME: DELETE
+ stroke = true;
+ return false;
+ } else {
+ p.color = new Color (1, 1, 1, 1); // FIXME: DELETE
+ }
+ }
+
+ return true;
+ }, 12);
+
+ return stroke;
+ }
+
+ class StrokeParts : GLib.Object {
+ public PathList parts = new PathList ();
+ public Path path = new Path ();
+
+ public Gee.ArrayList<Path> get_all () {
+ Gee.ArrayList<Path> pl = new Gee.ArrayList<Path> ();
+
+ pl.add (path);
+
+ foreach (Path p in parts.paths) {
+ pl.add (p);
+ }
+
+ return pl;
+ }
+ }
}
+ }
--- a/scripts/version.py
+++ b/scripts/version.py
@@ -13,7 +13,7 @@
Lesser General Public License for more details.
"""
- VERSION = '2.4.3'
+ VERSION = '2.4.4'
SO_VERSION_MAJOR = '36'
SO_VERSION_MINOR = '0'
SO_VERSION = SO_VERSION_MAJOR + '.' + SO_VERSION_MINOR