The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

SvgPath.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 class SvgPath : Object { 21 public Gee.ArrayList<Points> points = new Gee.ArrayList<Points> (); 22 23 double pen_position_x = 0; 24 double pen_position_y = 0; 25 26 public SvgPath () { 27 } 28 29 public SvgPath.create_copy (SvgPath p) { 30 Object.copy_attributes (p, this); 31 32 foreach (Points point_data in p.points) { 33 points.add (point_data.copy ()); 34 } 35 } 36 37 public override bool is_over (double point_x, double point_y) { 38 bool inside = false; 39 40 if (is_over_boundaries (point_x, point_y)) { 41 to_object_view (ref point_x, ref point_y); 42 43 foreach (Points p in points) { 44 if (is_over_points (p, point_x, point_y)) { 45 inside = !inside; 46 } 47 } 48 } 49 50 return inside; 51 } 52 53 public static bool is_over_points (Points p, double point_x, double point_y) { 54 bool inside = false; 55 56 p.all ((start_x, start_y, end_x, end_y) => { 57 is_inside (ref inside, point_x, point_y, start_x, start_y, end_x, end_y); 58 return true; 59 }); 60 61 return inside; 62 } 63 64 static void is_inside (ref bool inside, double point_x, double point_y, 65 double prev_x, double prev_y, double next_x, double next_y) { 66 if ((next_y > point_y) != (prev_y > point_y) 67 && point_x < (prev_x - next_x) * (point_y - next_y) / (prev_y - next_y) + next_x) { 68 inside = !inside; 69 } 70 } 71 72 public override void draw_outline (Context cr) { 73 foreach (Points p in points) { 74 double x, y; 75 76 get_start (p, out x, out y); 77 cr.move_to (x, y); 78 79 draw_points (cr, p); 80 81 if (p.closed) { 82 cr.close_path (); 83 } 84 } 85 } 86 87 public static void get_start (Points path, out double x, out double y) { 88 int size = path.point_data.size; 89 90 x = 0; 91 y = 0; 92 93 // points are padded up to 8 units 94 return_if_fail (size % 8 == 0); 95 return_if_fail (size >= 8); 96 97 switch (path.get_point_type (0) & POINT_TYPE) { 98 case POINT_ARC: 99 x = path.get_double (6); 100 y = path.get_double (7); 101 break; 102 case POINT_CUBIC: 103 x = path.get_double (5); 104 y = path.get_double (6); 105 break; 106 case POINT_LINE: 107 x = path.get_double (1); 108 y = path.get_double (2); 109 break; 110 default: 111 warning (@"Unknown type $(path.get_point_type (0))"); 112 break; 113 } 114 } 115 116 public void draw_points (Context cr, Points path) { 117 PointValue* points = path.point_data.data; 118 int size = path.point_data.size; 119 120 return_if_fail (size % 8 == 0); 121 122 for (int i = 0; i < size; i += 8) { 123 switch (points[i].type & POINT_TYPE) { 124 case POINT_ARC: 125 draw_arc (cr, points[i + 1].value, points[i + 2].value, 126 points[i + 3].value, points[i + 4].value, 127 points[i + 5].value, points[i + 6].value, 128 points[i + 7].value); 129 130 pen_position_x = points[i + 6].value; 131 pen_position_y = points[i + 7].value; 132 break; 133 case POINT_CUBIC: 134 cr.curve_to (points[i + 1].value, points[i + 2].value, 135 points[i + 3].value, points[i + 4].value, 136 points[i + 5].value, points[i + 6].value); 137 138 pen_position_x = points[i + 5].value; 139 pen_position_y = points[i + 6].value; 140 break; 141 case POINT_LINE: 142 cr.line_to (points[i + 1].value, points[i + 2].value); 143 pen_position_x = points[i + 1].value; 144 pen_position_y = points[i + 2].value; 145 break; 146 } 147 } 148 } 149 150 void draw_arc (Context cr, 151 double rx, double ry, double rotation, 152 double large_arc, double sweep, 153 double x, double y) { 154 155 double angle_start, angle_extent, cx, cy; 156 157 get_arc_arguments (pen_position_x, pen_position_y, rx, ry, 158 rotation, large_arc > 0, sweep > 0, x, y, 159 out angle_start, out angle_extent, 160 out cx, out cy); 161 162 cr.save (); 163 164 cr.translate (cx, cy); 165 cr.rotate (rotation); 166 cr.scale (rx, ry); 167 168 double start_x = Math.cos (angle_start); 169 double start_y = Math.sin (angle_start); 170 cr.move_to (start_x, start_y); 171 172 angle_start %= 2 * Math.PI; 173 angle_extent %= 2 * Math.PI; 174 175 if (angle_extent.is_normal () && angle_extent.is_normal ()) { 176 if (angle_extent > 0) { 177 cr.arc_negative (0, 0, 1, -angle_start, -angle_start - angle_extent); 178 } else { 179 cr.arc (0, 0, 1, -angle_start, -angle_start - angle_extent); 180 } 181 } 182 183 cr.restore (); 184 } 185 186 public override bool is_empty () { 187 return false; 188 } 189 190 public override Object copy () { 191 return new SvgPath.create_copy (this); 192 } 193 194 public override string to_string () { 195 return "SvgPath"; 196 } 197 } 198 199 } 200