The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

PointConverter.vala in /libbirdfont

This file is a part of the Birdfont project.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git
Circle boundaries heads/master
1 /* 2 Copyright (C) 2014 2016 Johan Mattsson 3 4 This library is free software; you can redistribute it and/or modify 5 it under the terms of the GNU Lesser General Public License as 6 published by the Free Software Foundation; either version 3 of the 7 License, or (at your option) any later version. 8 9 This library is distributed in the hope that it will be useful, but 10 WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 Lesser General Public License for more details. 13 */ 14 15 using Cairo; 16 using Math; 17 18 namespace BirdFont { 19 20 /** A class for converting control points. */ 21 public class PointConverter { 22 23 Path original_path; 24 Path quadratic_path; 25 26 public PointConverter (Path path) { 27 original_path = path; 28 } 29 30 public Path get_quadratic_path () { 31 bool add_more_points = false; 32 33 quadratic_path = original_path.copy (); 34 35 estimated_cubic_path (); 36 37 if (add_more_points) { 38 warning ("Too many points in segment."); 39 } 40 41 if (quadratic_path.points.size < 2) { 42 return new Path (); 43 } 44 45 foreach (EditPoint e in quadratic_path.points) { 46 if (e.get_right_handle ().type == PointType.CUBIC) { 47 PenTool.convert_point_segment_type (e, e.get_next (), PointType.DOUBLE_CURVE); 48 } 49 } 50 51 52 53 if (!original_path.is_open ()) { 54 if (quadratic_path.get_last_point ().get_right_handle ().type == PointType.CUBIC) { 55 PenTool.convert_point_segment_type (quadratic_path.get_last_point (), 56 quadratic_path.get_first_point (), 57 PointType.DOUBLE_CURVE); 58 } 59 } 60 61 quadratic_path.add_hidden_double_points (); 62 63 return quadratic_path; 64 } 65 66 public void estimated_cubic_path () { 67 EditPoint segment_start; 68 EditPoint segment_stop; 69 EditPoint quadratic_segment_start; 70 EditPoint quadratic_segment_stop; 71 EditPoint e; 72 double distance, step; 73 int points_in_segment = 0; 74 int size; 75 76 foreach (EditPoint ep in quadratic_path.points) { 77 ep.set_tie_handle (false); 78 ep.set_reflective_handles (false); 79 } 80 81 size = quadratic_path.points.size; 82 segment_start = quadratic_path.get_first_point (); 83 84 if (original_path.is_open ()) { 85 size--; 86 } 87 88 for (int i = 0; i < size; i++) { 89 segment_stop = (i == size -1) 90 ? quadratic_path.get_first_point () 91 : segment_start.get_next (); 92 93 quadratic_segment_start = segment_start.copy (); 94 quadratic_segment_stop = segment_stop.copy (); 95 96 PenTool.convert_point_segment_type (quadratic_segment_start, quadratic_segment_stop, PointType.DOUBLE_CURVE); 97 98 // add new points to estimate path 99 distance = 0; 100 e = new EditPoint (); 101 if (segment_start.get_right_handle ().is_line () 102 && segment_stop.get_left_handle ().is_line ()) { 103 // skipping line 104 } else if (points_in_segment >= 10) { 105 warning ("Too many points."); 106 } else { 107 find_largest_distance (segment_start, segment_stop, 108 quadratic_segment_start, quadratic_segment_stop, 109 out distance, out e, out step); 110 } 111 112 if (distance > 0.2) { // range 0.1 - 0.4, 113 quadratic_path.insert_new_point_on_path (e); 114 points_in_segment++; 115 size += 2; // the new point + segment start 116 } else { 117 points_in_segment = 0; 118 segment_start = segment_stop; 119 } 120 } 121 } 122 123 // TODO: Optimize 124 public static void find_largest_distance (EditPoint a0, EditPoint a1, EditPoint b0, EditPoint b1, 125 out double distance, out EditPoint new_point, out double step) { 126 double max_d; 127 double min_d; 128 int steps; 129 double x_out, y_out; 130 double step_out; 131 132 x_out = 0; 133 y_out = 0; 134 step_out = 0; 135 step = 0; 136 distance = 0; 137 138 new_point = new EditPoint (); 139 new_point.prev = a0; 140 new_point.next = a1; 141 new_point.get_right_handle ().type = PointType.CUBIC; 142 new_point.get_left_handle ().type = PointType.CUBIC; 143 144 steps = 20; // FIXME: adjust to length 145 146 if (a0.get_right_handle ().type == PointType.QUADRATIC 147 || a1.get_left_handle ().type == PointType.QUADRATIC 148 || a0.get_right_handle ().type == PointType.LINE_QUADRATIC 149 || a1.get_left_handle ().type == PointType.LINE_QUADRATIC) { 150 return; 151 } 152 153 max_d = Glyph.CANVAS_MIN; 154 min_d = Glyph.CANVAS_MAX; 155 Path.all_of (a0, a1, (xa, ya, ta) => { 156 157 min_d = double.MAX; 158 Path.all_of (b0, b1, (xb, yb, tb) => { 159 double d = Path.distance (xa, xb, ya, yb); 160 161 if (d < min_d) { 162 min_d = d; 163 } 164 165 return true; 166 }, steps); 167 168 if (min_d > max_d) { 169 max_d = min_d; 170 x_out = xa; 171 y_out = ya; 172 step_out = ta; 173 } 174 175 return true; 176 }, steps); 177 178 distance = max_d; 179 new_point.x = x_out; 180 new_point.y = y_out; 181 step = step_out; 182 } 183 } 184 185 } 186