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

Revisions

View the latest version of libsvgbird/Object.vala.
Click to object matrix transformation
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_height { 35 get { 36 return bottom - top; 37 } 38 } 39 40 public virtual double boundaries_width { 41 get { 42 return right - left; 43 } 44 } 45 46 /** Cartesian coordinates for the old BirdFont system. */ 47 public double xmax { 48 get { 49 return right; 50 } 51 52 set { 53 right = value; 54 } 55 } 56 57 public double xmin { 58 get { 59 return left; 60 } 61 62 set { 63 left = value; 64 } 65 } 66 67 public double ymin { 68 get { 69 return -top - boundaries_height; 70 } 71 72 set { 73 top = boundaries_height - value; 74 } 75 } 76 77 public double ymax { 78 get { 79 return -top; 80 } 81 82 set { 83 top = -value; 84 } 85 } 86 87 public const double CANVAS_MAX = 100000; 88 public const double CANVAS_MIN = -100000; 89 90 public Object () { 91 } 92 93 public Object.create_copy (Object o) { 94 } 95 96 public virtual bool is_over (double x, double y) { 97 return left <= x <= right && top <= y <= bottom; 98 } 99 100 public void to_object_view (ref double x, ref double y) { 101 Matrix m = view_matrix; 102 m.invert (); 103 m.transform_point (ref x, ref y); 104 } 105 106 public abstract void draw_outline (Context cr); 107 108 public abstract Object copy (); 109 public abstract bool is_empty (); 110 public abstract void move (double dx, double dy); 111 112 public Matrix view_matrix = Matrix.identity (); 113 114 public virtual void move_bounding_box (double dx, double dy) { 115 top += dy; 116 bottom += dy; 117 left += dx; 118 right += dx; 119 } 120 121 public static void copy_attributes (Object from, Object to) { 122 to.left = from.left; 123 to.right = from.right; 124 to.top = from.top; 125 to.bottom = from.bottom; 126 127 to.visible = from.visible; 128 to.style = from.style.copy (); 129 to.transforms = from.transforms.copy (); 130 131 if (from.clip_path != null) { 132 to.clip_path = ((!) from.clip_path).copy (); 133 } 134 } 135 136 public virtual string to_string () { 137 return "Object"; 138 } 139 140 public bool is_over_boundaries (double x, double y) { 141 return top <= y <= bottom && left <= x <= right; 142 } 143 144 public void paint (Context cr) { 145 Color fill, stroke; 146 bool need_fill = (style.fill_gradient != null || style.fill != null); 147 bool need_stroke = (style.stroke_gradient != null || style.stroke != null); 148 149 cr.set_line_width (style.stroke_width); 150 151 if (style.fill_gradient != null) { 152 apply_gradient (cr, (!) style.fill_gradient); 153 } else if (style.fill != null) { 154 fill = (!) style.fill; 155 cr.set_source_rgba (fill.r, fill.g, fill.b, fill.a); 156 } 157 158 if (need_fill) { 159 if (need_stroke) { 160 cr.fill_preserve (); 161 } else { 162 cr.fill (); 163 } 164 } 165 166 if (style.stroke_gradient != null) { 167 apply_gradient (cr, (!) style.stroke_gradient); 168 } else if (style.stroke != null) { 169 stroke = (!) style.stroke; 170 cr.set_source_rgba (stroke.r, stroke.g, stroke.b, stroke.a); 171 } 172 173 if (need_stroke) { 174 cr.stroke (); 175 } 176 } 177 178 public void apply_gradient (Context cr, Gradient? gradient) { 179 Cairo.Pattern pattern; 180 Gradient g; 181 LinearGradient linear; 182 RadialGradient radial; 183 184 if (gradient != null) { 185 g = (!) gradient; 186 187 if (g is LinearGradient) { 188 linear = (LinearGradient) g; 189 pattern = new Cairo.Pattern.linear (linear.x1, linear.y1, linear.x2, linear.y2); 190 } else if (g is RadialGradient) { 191 radial = (RadialGradient) g; 192 pattern = new Cairo.Pattern.radial (radial.cx, radial.cy, 0, radial.cx, radial.cy, radial.r); 193 } else { 194 warning ("Unknown gradient."); 195 pattern = new Cairo.Pattern.linear (0, 0, 0, 0); 196 } 197 198 Matrix gradient_matrix = g.get_matrix (); 199 gradient_matrix.invert (); 200 Matrix object_matrix = transforms.get_matrix (); 201 object_matrix.invert (); 202 gradient_matrix.multiply (gradient_matrix, object_matrix); 203 204 pattern.set_matrix (gradient_matrix); 205 206 g.view_matrix = gradient_matrix; 207 208 foreach (Stop s in g.stops) { 209 Color c = s.color; 210 pattern.add_color_stop_rgba (s.offset, c.r, c.g, c.b, c.a); 211 } 212 213 cr.set_source (pattern); 214 } 215 } 216 217 public virtual void apply_transform (Context cr) { 218 Matrix view_matrix = cr.get_matrix (); 219 Matrix object_matrix = transforms.get_matrix (); 220 221 object_matrix.multiply (object_matrix, view_matrix); 222 cr.set_matrix (object_matrix); 223 } 224 225 /** @return true if the object has an area. */ 226 public virtual bool update_boundaries (Context context) { 227 double x0, y0, x1, y1; 228 bool has_stroke = style.has_stroke (); 229 230 apply_transform (context); 231 232 if (has_stroke) { 233 context.set_line_width (style.stroke_width); 234 } else { 235 context.set_line_width (0); 236 } 237 238 draw_outline (context); 239 240 context.save (); 241 242 if (has_stroke) { 243 context.stroke_extents (out x0, out y0, out x1, out y1); 244 } else { 245 context.fill_extents (out x0, out y0, out x1, out y1); 246 } 247 248 Matrix matrix = context.get_matrix (); 249 matrix.transform_point (ref x0, ref y0); 250 matrix.transform_point (ref x1, ref y1); 251 252 context.fill (); 253 context.restore (); 254 255 left = x0; 256 top = y0; 257 right = x1; 258 bottom = y1; 259 260 return boundaries_width != 0; 261 } 262 263 public Matrix get_view_matrix () { 264 return view_matrix; 265 } 266 267 public virtual bool update_boundaries_for_object () { 268 ImageSurface surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, 1, 1); 269 Context context = new Cairo.Context (surface); 270 return update_boundaries (context); 271 } 272 273 } 274 275 } 276