The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

Object.vala in /libsvgbird

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) 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 SvgBird { 19 20 public abstract class Object : GLib.Object { 21 public bool visible = true; 22 public SvgStyle style = new SvgStyle (); 23 public SvgTransforms transforms = new SvgTransforms (); 24 public ClipPath? clip_path = null; 25 public string id = ""; 26 public string css_class = ""; 27 28 /** Path boundaries */ 29 public virtual double left { get; set; } 30 public virtual double right { get; set; } 31 public virtual double top { get; set; } 32 public virtual double bottom { get; set; } 33 34 public virtual double boundaries_x0 { get; set; } 35 public virtual double boundaries_y0 { get; set; } 36 public virtual double boundaries_x1 { get; set; } 37 public virtual double boundaries_y1 { get; set; } 38 39 public virtual double boundaries_height { 40 get { 41 return bottom - top; 42 } 43 } 44 45 public virtual double boundaries_width { 46 get { 47 return right - left; 48 } 49 } 50 51 /** Cartesian coordinates for the old BirdFont system. */ 52 public double xmax { 53 get { 54 return right; 55 } 56 57 set { 58 right = value; 59 } 60 } 61 62 public double xmin { 63 get { 64 return left; 65 } 66 67 set { 68 left = value; 69 } 70 } 71 72 public double ymin { 73 get { 74 return -top - boundaries_height; 75 } 76 77 set { 78 top = boundaries_height - value; 79 } 80 } 81 82 public double ymax { 83 get { 84 return -top; 85 } 86 87 set { 88 top = -value; 89 } 90 } 91 92 public const double CANVAS_MAX = 1000000; 93 public const double CANVAS_MIN = -1000000; 94 95 public Matrix view_matrix = Matrix.identity (); 96 public Matrix parent_matrix = Matrix.identity (); 97 98 public Object () { 99 } 100 101 public Object.create_copy (Object o) { 102 } 103 104 public virtual bool is_over (double x, double y) { 105 return left <= x <= right && top <= y <= bottom; 106 } 107 108 public void to_object_view (ref double x, ref double y) { 109 Matrix m = view_matrix; 110 m.invert (); 111 m.transform_point (ref x, ref y); 112 } 113 114 public void to_parent_distance (ref double x, ref double y) { 115 Matrix m = parent_matrix; 116 m.invert (); 117 m.transform_distance (ref x, ref y); 118 } 119 120 public void to_parent_view (ref double x, ref double y) { 121 Matrix m = parent_matrix; 122 m.invert (); 123 m.transform_point (ref x, ref y); 124 } 125 126 public void from_object_view (ref double x, ref double y) { 127 Matrix m = view_matrix; 128 m.transform_point (ref x, ref y); 129 } 130 131 public void from_object_distance (ref double x, ref double y) { 132 Matrix m = view_matrix; 133 m.transform_distance (ref x, ref y); 134 } 135 136 public void to_object_distance (ref double x, ref double y) { 137 Matrix m = view_matrix; 138 m.invert (); 139 m.transform_distance (ref x, ref y); 140 } 141 142 public abstract void draw_outline (Context cr); 143 144 public abstract Object copy (); 145 public abstract bool is_empty (); 146 147 public virtual void move (double dx, double dy) { 148 left += dx; 149 right += dx; 150 top += dy; 151 bottom += dy; 152 153 to_object_distance (ref dx, ref dy); 154 transforms.translate (dx, dy); 155 156 update_view_matrix (); 157 } 158 159 public void update_view_matrix () { 160 Matrix v = parent_matrix; 161 Matrix m = transforms.get_matrix (); 162 m.multiply (m, v); 163 view_matrix = m; 164 } 165 166 public Matrix get_parent_matrix () { 167 Matrix matrix = view_matrix; 168 Matrix object_matrix = transforms.get_matrix (); 169 object_matrix.invert (); 170 matrix.multiply (matrix, object_matrix); 171 return matrix; 172 } 173 174 public virtual void move_bounding_box (double dx, double dy) { 175 top += dy; 176 bottom += dy; 177 left += dx; 178 right += dx; 179 } 180 181 public static void copy_attributes (Object from, Object to) { 182 to.left = from.left; 183 to.right = from.right; 184 to.top = from.top; 185 to.bottom = from.bottom; 186 187 to.visible = from.visible; 188 to.style = from.style.copy (); 189 to.transforms = from.transforms.copy (); 190 191 if (from.clip_path != null) { 192 to.clip_path = ((!) from.clip_path).copy (); 193 } 194 } 195 196 public virtual string to_string () { 197 return "Object"; 198 } 199 200 public bool is_over_boundaries (double x, double y) { 201 return top <= y <= bottom && left <= x <= right; 202 } 203 204 public void paint (Context cr) { 205 Color fill, stroke; 206 bool need_fill = (style.fill_gradient != null || style.fill != null); 207 bool need_stroke = (style.stroke_gradient != null || style.stroke != null); 208 209 cr.set_line_width (style.stroke_width); 210 211 if (style.fill_gradient != null) { 212 apply_gradient (cr, (!) style.fill_gradient); 213 } else if (style.fill != null) { 214 fill = (!) style.fill; 215 cr.set_source_rgba (fill.r, fill.g, fill.b, fill.a); 216 } 217 218 if (need_fill) { 219 if (need_stroke) { 220 cr.fill_preserve (); 221 } else { 222 cr.fill (); 223 } 224 } 225 226 if (style.stroke_gradient != null) { 227 apply_gradient (cr, (!) style.stroke_gradient); 228 } else if (style.stroke != null) { 229 stroke = (!) style.stroke; 230 cr.set_source_rgba (stroke.r, stroke.g, stroke.b, stroke.a); 231 } 232 233 if (need_stroke) { 234 cr.stroke (); 235 } 236 } 237 238 public void apply_gradient (Context cr, Gradient? gradient) { 239 Cairo.Pattern pattern; 240 Gradient g; 241 LinearGradient linear; 242 RadialGradient radial; 243 244 if (gradient != null) { 245 g = (!) gradient; 246 247 if (g is LinearGradient) { 248 linear = (LinearGradient) g; 249 pattern = new Cairo.Pattern.linear (linear.x1, linear.y1, linear.x2, linear.y2); 250 } else if (g is RadialGradient) { 251 radial = (RadialGradient) g; 252 pattern = new Cairo.Pattern.radial (radial.cx, radial.cy, 0, radial.cx, radial.cy, radial.r); 253 } else { 254 warning ("Unknown gradient."); 255 pattern = new Cairo.Pattern.linear (0, 0, 0, 0); 256 } 257 258 Matrix gradient_matrix = g.get_matrix (); 259 gradient_matrix.invert (); 260 261 pattern.set_matrix (gradient_matrix); 262 263 g.parent_matrix = view_matrix; 264 g.view_matrix = gradient_matrix; 265 266 foreach (Stop s in g.stops) { 267 Color c = s.color; 268 pattern.add_color_stop_rgba (s.offset, c.r, c.g, c.b, c.a); 269 } 270 271 if (likely (pattern.status () == 0)) { 272 cr.set_source (pattern); 273 } else { 274 warning ("Invalid pattern."); 275 } 276 } 277 } 278 279 public virtual void apply_transform (Context cr) { 280 Matrix view_matrix = cr.get_matrix (); 281 Matrix object_matrix = transforms.get_matrix (); 282 283 object_matrix.multiply (object_matrix, view_matrix); 284 cr.set_matrix (object_matrix); 285 } 286 287 /** @return true if the object has an area. */ 288 public virtual bool update_boundaries (Context context) { 289 double x0, y0, x1, y1; 290 bool has_stroke = style.has_stroke (); 291 292 context.save (); 293 294 parent_matrix = copy_matrix (context.get_matrix ()); 295 apply_transform (context); 296 297 if (style.fill_gradient != null) { 298 apply_gradient (context, (!) style.fill_gradient); 299 } 300 301 if (style.stroke_gradient != null) { 302 apply_gradient (context, (!) style.stroke_gradient); 303 } 304 305 if (has_stroke) { 306 context.set_line_width (style.stroke_width); 307 } else { 308 context.set_line_width (0); 309 } 310 311 view_matrix = copy_matrix (context.get_matrix ()); 312 context.set_matrix (Matrix.identity ()); 313 draw_outline (context); 314 315 if (has_stroke) { 316 context.stroke_extents (out x0, out y0, out x1, out y1); 317 } else { 318 context.fill_extents (out x0, out y0, out x1, out y1); 319 } 320 321 boundaries_x0 = x0; 322 boundaries_y0 = y0; 323 boundaries_x1 = x1; 324 boundaries_y1 = y1; 325 326 double point_x0 = x0; 327 double point_y0 = y0; 328 double point_x1 = x1; 329 double point_y1 = y0; 330 double point_x2 = x1; 331 double point_y2 = y1; 332 double point_x3 = x0; 333 double point_y3 = y1; 334 335 view_matrix.transform_point (ref point_x0, ref point_y0); 336 view_matrix.transform_point (ref point_x1, ref point_y1); 337 view_matrix.transform_point (ref point_x2, ref point_y2); 338 view_matrix.transform_point (ref point_x3, ref point_y3); 339 340 context.fill (); 341 context.restore (); 342 343 left = min (point_x0, point_x1, point_x2, point_x3); 344 top = min (point_y0, point_y1, point_y2, point_y3); 345 right = max (point_x0, point_x1, point_x2, point_x3); 346 bottom = max (point_y0, point_y1, point_y2, point_y3); 347 348 return boundaries_width != 0; 349 } 350 351 static double min (double x0, double x1, double x2, double x3) { 352 double min = x0; 353 354 if (x1 < min) { 355 min = x1; 356 } 357 358 if (x2 < min) { 359 min = x2; 360 } 361 362 if (x3 < min) { 363 min = x3; 364 } 365 366 return min; 367 } 368 369 static double max (double x0, double x1, double x2, double x3) { 370 double max = x0; 371 372 if (x1 > max) { 373 max = x1; 374 } 375 376 if (x2 > max) { 377 max = x2; 378 } 379 380 if (x3 > max) { 381 max = x3; 382 } 383 384 return max; 385 } 386 387 public Matrix get_view_matrix () { 388 return view_matrix; 389 } 390 391 public virtual bool update_boundaries_for_object () { 392 ImageSurface surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, 1, 1); 393 Context context = new Cairo.Context (surface); 394 context.set_matrix (parent_matrix); 395 return update_boundaries (context); 396 } 397 398 public Matrix copy_matrix (Matrix m) { 399 return Matrix (m.xx, m.yx, m.xy, m.yy, m.x0, m.y0); 400 } 401 } 402 403 } 404