The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

CircleTool.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) 2012 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 Math; 16 using Cairo; 17 18 namespace BirdFont { 19 20 public class CircleTool : Tool { 21 22 Path circle = new Path (); 23 24 double press_x = -1; 25 double press_y = -1; 26 27 double last_x = -1; 28 double last_y = -1; 29 30 double last_radius = 2; 31 32 bool move_circle = false; 33 bool resize_circle = false; 34 35 public CircleTool (string n) { 36 base (n, t_("Circle")); 37 38 press_action.connect((self, b, x, y) => { 39 press (b, x, y); 40 }); 41 42 release_action.connect((self, b, x, y) => { 43 press_x = -1; 44 press_y = -1; 45 circle = new Path (); 46 }); 47 48 move_action.connect ((self, x, y) => { 49 move (x, y); 50 }); 51 } 52 53 void move (double x, double y) { 54 double dx = last_x - x; 55 double dy = last_y - y; 56 double p = PenTool.precision; 57 double ratio, diameter, radius, cx, cy, nx, ny; 58 double xmin, xmax, ymin; 59 60 if (move_circle) { 61 circle.move (Glyph.ivz () * -dx * p, Glyph.ivz () * dy * p); 62 circle.reset_stroke (); 63 } 64 65 if (resize_circle) { 66 get_boundaries (out xmin, out xmax, out ymin); 67 68 diameter = xmax - xmin; 69 cx = xmin + diameter / 2; 70 cy = ymin + diameter / 2; 71 72 radius = Path.distance_pixels (press_x, press_y, x, y); 73 ratio = 2 * radius / diameter; 74 75 if (diameter * ratio > 0.5) { 76 circle.resize (ratio, ratio); 77 } 78 79 get_boundaries (out xmin, out xmax, out ymin); 80 81 diameter = xmax - xmin; 82 nx = xmin + diameter / 2; 83 ny = ymin + diameter / 2; 84 85 circle.move (cx - nx, cy - ny); 86 87 last_radius = radius; 88 circle.reset_stroke (); 89 circle.update_region_boundaries (); 90 } 91 92 last_x = x; 93 last_y = y; 94 95 GlyphCanvas.redraw (); 96 } 97 98 void get_boundaries (out double xmin, out double xmax, out double ymin) { 99 xmin = Glyph.CANVAS_MAX; 100 xmax = Glyph.CANVAS_MIN; 101 ymin = Glyph.CANVAS_MAX; 102 foreach (EditPoint p in circle.points) { 103 if (p.x < xmin) { 104 xmin = p.x; 105 } 106 107 if (p.x > xmax) { 108 xmax = p.x; 109 } 110 111 if (p.y < ymin) { 112 ymin = p.y; 113 } 114 } 115 } 116 117 public static Path create_circle (double x, double y, 118 double r, PointType pt) { 119 120 double px, py; 121 Path path = new Path (); 122 double steps = (pt == PointType.QUADRATIC) ? PI / 8 : PI / 4; 123 124 for (double angle = 0; angle < 2 * PI; angle += steps) { 125 px = r * cos (angle) + x; 126 py = r * sin (angle) + y; 127 path.add (px, py); 128 } 129 130 path.init_point_type (pt); 131 path.close (); 132 path.recalculate_linear_handles (); 133 134 for (int i = 0; i < 3; i++) { 135 foreach (EditPoint ep in path.points) { 136 ep.set_tie_handle (true); 137 ep.process_tied_handle (); 138 } 139 } 140 141 return path; 142 } 143 144 public static Path create_ellipse (double x, double y, 145 double rx, double ry, PointType pt) { 146 147 double px, py; 148 Path path = new Path (); 149 double steps = (pt == PointType.QUADRATIC) ? PI / 8 : PI / 4; 150 151 for (double angle = 0; angle < 2 * PI; angle += steps) { 152 px = rx * cos (angle) + x; 153 py = ry * sin (angle) + y; 154 path.add (px, py); 155 } 156 157 path.init_point_type (pt); 158 path.close (); 159 path.recalculate_linear_handles (); 160 161 for (int i = 0; i < 3; i++) { 162 foreach (EditPoint ep in path.points) { 163 ep.set_tie_handle (true); 164 ep.process_tied_handle (); 165 } 166 } 167 168 return path; 169 } 170 171 void press (int button, double x, double y) { 172 Glyph glyph = MainWindow.get_current_glyph (); 173 Path path = new Path (); 174 175 press_x = x; 176 press_y = y; 177 178 move_circle = (button == 3); 179 resize_circle = (button == 1); 180 181 if (!move_circle && !resize_circle) { 182 return; 183 } 184 185 glyph.store_undo_state (); 186 187 double px = Glyph.path_coordinate_x (x); 188 double py = Glyph.path_coordinate_y (y); 189 190 path = create_circle (px, py, 2, DrawingTools.point_type); 191 192 if (StrokeTool.add_stroke) { 193 path.stroke = StrokeTool.stroke_width; 194 path.line_cap = StrokeTool.line_cap; 195 } 196 197 glyph.add_path (path); 198 199 if (!PenTool.is_counter_path (circle)) { 200 path.reverse (); 201 } 202 203 circle = path; 204 205 GlyphCanvas.redraw (); 206 } 207 208 } 209 210 } 211