1 | use std::{cmp::Ordering, f32::consts::PI, ops::Range}; |
2 | |
3 | use bitflags::bitflags; |
4 | |
5 | use crate::{ |
6 | geometry::{Bounds, Position, Transform2D, Vector}, |
7 | renderer::Vertex, |
8 | FillRule, LineCap, LineJoin, Solidity, |
9 | }; |
10 | |
11 | use super::Verb; |
12 | |
13 | bitflags! { |
14 | #[derive (Default, Clone, Copy, Debug, PartialEq, Eq, Hash)] |
15 | pub struct PointFlags: u8 { |
16 | const CORNER = 0x01; |
17 | const LEFT = 0x02; |
18 | const BEVEL = 0x04; |
19 | const INNERBEVEL = 0x08; |
20 | } |
21 | } |
22 | |
23 | #[derive (Copy, Clone, Debug, Default)] |
24 | pub struct Point { |
25 | pos: Position, |
26 | dpos: Vector, |
27 | len: f32, |
28 | dmpos: Vector, |
29 | flags: PointFlags, |
30 | } |
31 | |
32 | impl Point { |
33 | pub fn new(x: f32, y: f32, flags: PointFlags) -> Self { |
34 | Self { |
35 | pos: Position { x, y }, |
36 | flags, |
37 | ..Default::default() |
38 | } |
39 | } |
40 | |
41 | pub fn is_left(p0: &Self, p1: &Self, x: f32, y: f32) -> f32 { |
42 | let pos: Position = Position { x, y }; |
43 | (p1.pos - p0.pos).dot((pos - p0.pos).orthogonal()) |
44 | } |
45 | |
46 | pub fn approx_eq(&self, other: &Self, tolerance: f32) -> bool { |
47 | (other.pos - self.pos).mag2() < tolerance * tolerance |
48 | } |
49 | } |
50 | |
51 | #[derive (Copy, Clone, Debug, Eq, PartialEq, Default)] |
52 | pub enum Convexity { |
53 | Concave, |
54 | Convex, |
55 | #[default] |
56 | Unknown, |
57 | } |
58 | |
59 | #[derive (Clone, Debug)] |
60 | pub struct Contour { |
61 | point_range: Range<usize>, |
62 | closed: bool, |
63 | bevel: usize, |
64 | solidity: Option<Solidity>, |
65 | pub(crate) fill: Vec<Vertex>, |
66 | pub(crate) stroke: Vec<Vertex>, |
67 | pub(crate) convexity: Convexity, |
68 | } |
69 | |
70 | impl Default for Contour { |
71 | fn default() -> Self { |
72 | Self { |
73 | point_range: 0..0, |
74 | closed: Default::default(), |
75 | bevel: Default::default(), |
76 | solidity: None, |
77 | fill: Vec::new(), |
78 | stroke: Vec::new(), |
79 | convexity: Default::default(), |
80 | } |
81 | } |
82 | } |
83 | |
84 | impl Contour { |
85 | fn point_pairs<'a>(&self, points: &'a [Point]) -> impl Iterator<Item = (&'a Point, &'a Point)> { |
86 | PointPairsIter { |
87 | curr: 0, |
88 | points: &points[self.point_range.clone()], |
89 | } |
90 | } |
91 | |
92 | fn polygon_area(points: &[Point]) -> f32 { |
93 | let mut area: f32 = 0.0; |
94 | |
95 | for (p0: &Point, p1: &Point) in (PointPairsIter { curr: 0, points }) { |
96 | area += (p1.pos.x - p0.pos.x) * (p1.pos.y + p0.pos.y); |
97 | } |
98 | |
99 | area * 0.5 |
100 | } |
101 | |
102 | fn point_count(&self) -> usize { |
103 | self.point_range.end - self.point_range.start |
104 | } |
105 | } |
106 | |
107 | struct PointPairsIter<'a> { |
108 | curr: usize, |
109 | points: &'a [Point], |
110 | } |
111 | |
112 | impl<'a> Iterator for PointPairsIter<'a> { |
113 | type Item = (&'a Point, &'a Point); |
114 | |
115 | fn next(&mut self) -> Option<Self::Item> { |
116 | let curr: Option<&Point> = self.points.get(self.curr); |
117 | |
118 | let prev: Option<&Point> = if self.curr == 0 { |
119 | self.points.last() |
120 | } else { |
121 | self.points.get(self.curr - 1) |
122 | }; |
123 | |
124 | self.curr += 1; |
125 | |
126 | curr.and_then(|some_curr: &Point| prev.map(|some_prev: &Point| (some_prev, some_curr))) |
127 | } |
128 | } |
129 | |
130 | #[derive (Clone, Debug, Default)] |
131 | pub struct PathCache { |
132 | pub(crate) contours: Vec<Contour>, |
133 | pub(crate) bounds: Bounds, |
134 | points: Vec<Point>, |
135 | } |
136 | |
137 | impl PathCache { |
138 | pub fn new(verbs: impl Iterator<Item = Verb>, transform: &Transform2D, tess_tol: f32, dist_tol: f32) -> Self { |
139 | let mut cache = Self::default(); |
140 | |
141 | // Convert path verbs to a set of contours |
142 | for verb in verbs { |
143 | match verb { |
144 | Verb::MoveTo(x, y) => { |
145 | cache.add_contour(); |
146 | let (x, y) = transform.transform_point(x, y); |
147 | cache.add_point(x, y, PointFlags::CORNER, dist_tol); |
148 | } |
149 | Verb::LineTo(x, y) => { |
150 | let (x, y) = transform.transform_point(x, y); |
151 | cache.add_point(x, y, PointFlags::CORNER, dist_tol); |
152 | } |
153 | Verb::BezierTo(c1x, c1y, c2x, c2y, x, y) => { |
154 | if let Some(last) = cache.points.last().copied() { |
155 | let (c1x, c1y) = transform.transform_point(c1x, c1y); |
156 | let (c2x, c2y) = transform.transform_point(c2x, c2y); |
157 | let (x, y) = transform.transform_point(x, y); |
158 | |
159 | cache.tesselate_bezier( |
160 | last.pos.x, |
161 | last.pos.y, |
162 | c1x, |
163 | c1y, |
164 | c2x, |
165 | c2y, |
166 | x, |
167 | y, |
168 | 0, |
169 | PointFlags::CORNER, |
170 | tess_tol, |
171 | dist_tol, |
172 | ); |
173 | |
174 | // cache.tesselate_bezier_afd( |
175 | // last.pos.x, |
176 | // last.pos.y, |
177 | // c1x, |
178 | // c1y, |
179 | // c2x, |
180 | // c2y, |
181 | // x, |
182 | // y, |
183 | // PointFlags::CORNER, |
184 | // tess_tol, |
185 | // dist_tol, |
186 | // ); |
187 | } |
188 | } |
189 | Verb::Close => { |
190 | if let Some(contour) = cache.contours.last_mut() { |
191 | contour.closed = true; |
192 | } |
193 | } |
194 | Verb::Solid => { |
195 | if let Some(contour) = cache.contours.last_mut() { |
196 | contour.solidity = Some(Solidity::Solid); |
197 | } |
198 | } |
199 | Verb::Hole => { |
200 | if let Some(contour) = cache.contours.last_mut() { |
201 | contour.solidity = Some(Solidity::Hole); |
202 | } |
203 | } |
204 | } |
205 | } |
206 | |
207 | let all_points = &mut cache.points; |
208 | let bounds = &mut cache.bounds; |
209 | |
210 | cache.contours.retain_mut(|contour| { |
211 | let mut points = &mut all_points[contour.point_range.clone()]; |
212 | |
213 | // If the first and last points are the same, remove the last, mark as closed contour. |
214 | if let (Some(p0), Some(p1)) = (points.last(), points.first()) { |
215 | if p0.approx_eq(p1, dist_tol) { |
216 | contour.point_range.end -= 1; |
217 | contour.closed = true; |
218 | points = &mut all_points[contour.point_range.clone()]; |
219 | } |
220 | } |
221 | |
222 | if points.len() < 2 { |
223 | return false; |
224 | } |
225 | |
226 | // Enforce solidity by reversing the winding. |
227 | if let Some(solidity) = contour.solidity { |
228 | let area = Contour::polygon_area(points); |
229 | |
230 | if solidity == Solidity::Solid && area < 0.0 { |
231 | points.reverse(); |
232 | } |
233 | |
234 | if solidity == Solidity::Hole && area > 0.0 { |
235 | points.reverse(); |
236 | } |
237 | } |
238 | |
239 | for i in 0..contour.point_count() { |
240 | let p1 = points.get(i).copied().unwrap(); |
241 | |
242 | let p0 = if i == 0 { |
243 | points.last_mut().unwrap() |
244 | } else { |
245 | points.get_mut(i - 1).unwrap() |
246 | }; |
247 | |
248 | p0.dpos = p1.pos - p0.pos; |
249 | p0.len = p0.dpos.normalize(); |
250 | |
251 | bounds.minx = bounds.minx.min(p0.pos.x); |
252 | bounds.miny = bounds.miny.min(p0.pos.y); |
253 | bounds.maxx = bounds.maxx.max(p0.pos.x); |
254 | bounds.maxy = bounds.maxy.max(p0.pos.y); |
255 | } |
256 | |
257 | true |
258 | }); |
259 | |
260 | cache |
261 | } |
262 | |
263 | fn add_contour(&mut self) { |
264 | let mut contour = Contour::default(); |
265 | |
266 | contour.point_range.start = self.points.len(); |
267 | contour.point_range.end = self.points.len(); |
268 | |
269 | self.contours.push(contour); |
270 | } |
271 | |
272 | fn add_point(&mut self, x: f32, y: f32, flags: PointFlags, dist_tol: f32) { |
273 | if let Some(contour) = self.contours.last_mut() { |
274 | let new_point = Point::new(x, y, flags); |
275 | |
276 | // If last point equals this new point just OR the flags and ignore the new point |
277 | if let Some(last_point) = self.points.get_mut(contour.point_range.end) { |
278 | if last_point.approx_eq(&new_point, dist_tol) { |
279 | last_point.flags |= new_point.flags; |
280 | return; |
281 | } |
282 | } |
283 | |
284 | self.points.push(new_point); |
285 | contour.point_range.end += 1; |
286 | } |
287 | } |
288 | |
289 | #[allow (clippy::too_many_arguments)] |
290 | fn tesselate_bezier( |
291 | &mut self, |
292 | x1: f32, |
293 | y1: f32, |
294 | x2: f32, |
295 | y2: f32, |
296 | x3: f32, |
297 | y3: f32, |
298 | x4: f32, |
299 | y4: f32, |
300 | level: usize, |
301 | flags: PointFlags, |
302 | tess_tol: f32, |
303 | dist_tol: f32, |
304 | ) { |
305 | if level > 10 { |
306 | return; |
307 | } |
308 | |
309 | let x12 = (x1 + x2) * 0.5; |
310 | let y12 = (y1 + y2) * 0.5; |
311 | let x23 = (x2 + x3) * 0.5; |
312 | let y23 = (y2 + y3) * 0.5; |
313 | let x34 = (x3 + x4) * 0.5; |
314 | let y34 = (y3 + y4) * 0.5; |
315 | let x123 = (x12 + x23) * 0.5; |
316 | let y123 = (y12 + y23) * 0.5; |
317 | |
318 | let dx = x4 - x1; |
319 | let dy = y4 - y1; |
320 | let d2 = ((x2 - x4) * dy - (y2 - y4) * dx).abs(); |
321 | let d3 = ((x3 - x4) * dy - (y3 - y4) * dx).abs(); |
322 | |
323 | if (d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy) { |
324 | self.add_point(x4, y4, flags, dist_tol); |
325 | return; |
326 | } |
327 | |
328 | let x234 = (x23 + x34) * 0.5; |
329 | let y234 = (y23 + y34) * 0.5; |
330 | let x1234 = (x123 + x234) * 0.5; |
331 | let y1234 = (y123 + y234) * 0.5; |
332 | |
333 | self.tesselate_bezier( |
334 | x1, |
335 | y1, |
336 | x12, |
337 | y12, |
338 | x123, |
339 | y123, |
340 | x1234, |
341 | y1234, |
342 | level + 1, |
343 | PointFlags::empty(), |
344 | tess_tol, |
345 | dist_tol, |
346 | ); |
347 | self.tesselate_bezier( |
348 | x1234, |
349 | y1234, |
350 | x234, |
351 | y234, |
352 | x34, |
353 | y34, |
354 | x4, |
355 | y4, |
356 | level + 1, |
357 | flags, |
358 | tess_tol, |
359 | dist_tol, |
360 | ); |
361 | } |
362 | |
363 | // fn tesselate_bezier_afd( |
364 | // &mut self, |
365 | // x1: f32, |
366 | // y1: f32, |
367 | // x2: f32, |
368 | // y2: f32, |
369 | // x3: f32, |
370 | // y3: f32, |
371 | // x4: f32, |
372 | // y4: f32, |
373 | // flags: PointFlags, |
374 | // tess_tol: f32, |
375 | // dist_tol: f32, |
376 | // ) { |
377 | // let ax = -x1 + 3.*x2 - 3.*x3 + x4; |
378 | // let ay = -y1 + 3.*y2 - 3.*y3 + y4; |
379 | // let bx = 3.*x1 - 6.*x2 + 3.*x3; |
380 | // let by = 3.*y1 - 6.*y2 + 3.*y3; |
381 | // let cx = -3.*x1 + 3.*x2; |
382 | // let cy = -3.*y1 + 3.*y2; |
383 | |
384 | // // Transform to forward difference basis (stepsize 1) |
385 | // let mut px = x1; |
386 | // let mut py = y1; |
387 | // let mut dx = ax + bx + cx; |
388 | // let mut dy = ay + by + cy; |
389 | // let mut ddx = 6.*ax + 2.*bx; |
390 | // let mut ddy = 6.*ay + 2.*by; |
391 | // let mut dddx = 6.*ax; |
392 | // let mut dddy = 6.*ay; |
393 | |
394 | // //printf("dx: %f, dy: %f\n", dx, dy); |
395 | // //printf("ddx: %f, ddy: %f\n", ddx, ddy); |
396 | // //printf("dddx: %f, dddy: %f\n", dddx, dddy); |
397 | |
398 | // const AFD_ONE: i32 = 1<<10; |
399 | |
400 | // let mut t = 0; |
401 | // let mut dt = AFD_ONE; |
402 | |
403 | // let tol = tess_tol * 4.0; |
404 | |
405 | // while t < AFD_ONE { |
406 | |
407 | // // Flatness measure. |
408 | // let mut d = ddx*ddx + ddy*ddy + dddx*dddx + dddy*dddy; |
409 | |
410 | // // Go to higher resolution if we're moving a lot |
411 | // // or overshooting the end. |
412 | // while (d > tol && dt > 1) || (t+dt > AFD_ONE) { |
413 | |
414 | // // Apply L to the curve. Increase curve resolution. |
415 | // dx = 0.5 * dx - (1.0/8.0)*ddx + (1.0/16.0)*dddx; |
416 | // dy = 0.5 * dy - (1.0/8.0)*ddy + (1.0/16.0)*dddy; |
417 | // ddx = (1.0/4.0) * ddx - (1.0/8.0) * dddx; |
418 | // ddy = (1.0/4.0) * ddy - (1.0/8.0) * dddy; |
419 | // dddx = (1.0/8.0) * dddx; |
420 | // dddy = (1.0/8.0) * dddy; |
421 | |
422 | // // Half the stepsize. |
423 | // dt >>= 1; |
424 | |
425 | // // Recompute d |
426 | // d = ddx*ddx + ddy*ddy + dddx*dddx + dddy*dddy; |
427 | |
428 | // } |
429 | |
430 | // // Go to lower resolution if we're really flat |
431 | // // and we aren't going to overshoot the end. |
432 | // // XXX: tol/32 is just a guess for when we are too flat. |
433 | // while (d > 0.0 && d < tol/32.0 && dt < AFD_ONE) && (t+2*dt <= AFD_ONE) { |
434 | |
435 | // // printf("down\n"); |
436 | |
437 | // // Apply L^(-1) to the curve. Decrease curve resolution. |
438 | // dx = 2. * dx + ddx; |
439 | // dy = 2. * dy + ddy; |
440 | // ddx = 4. * ddx + 4. * dddx; |
441 | // ddy = 4. * ddy + 4. * dddy; |
442 | // dddx = 8. * dddx; |
443 | // dddy = 8. * dddy; |
444 | |
445 | // // Double the stepsize. |
446 | // dt <<= 1; |
447 | |
448 | // // Recompute d |
449 | // d = ddx*ddx + ddy*ddy + dddx*dddx + dddy*dddy; |
450 | |
451 | // } |
452 | |
453 | // // Forward differencing. |
454 | // px += dx; |
455 | // py += dy; |
456 | // dx += ddx; |
457 | // dy += ddy; |
458 | // ddx += dddx; |
459 | // ddy += dddy; |
460 | |
461 | // // Output a point. |
462 | // self.add_point(px, py, flags, dist_tol); |
463 | |
464 | // // Advance along the curve. |
465 | // t += dt; |
466 | |
467 | // // Ensure we don't overshoot. |
468 | // debug_assert!(t <= AFD_ONE); |
469 | |
470 | // } |
471 | // } |
472 | |
473 | pub fn contains_point(&self, x: f32, y: f32, fill_rule: FillRule) -> bool { |
474 | // Early out if point is outside the bounding rectangle |
475 | if !self.bounds.contains(x, y) { |
476 | return false; |
477 | } |
478 | |
479 | if fill_rule == FillRule::EvenOdd { |
480 | for contour in &self.contours { |
481 | let mut crossing = false; |
482 | |
483 | for (p0, p1) in contour.point_pairs(&self.points) { |
484 | if (p1.pos.y > y) != (p0.pos.y > y) |
485 | && (x < (p0.pos.x - p1.pos.x) * (y - p1.pos.y) / (p0.pos.y - p1.pos.y) + p1.pos.x) |
486 | { |
487 | crossing = !crossing; |
488 | } |
489 | } |
490 | |
491 | if crossing { |
492 | return true; |
493 | } |
494 | } |
495 | } else { |
496 | // NonZero |
497 | for contour in &self.contours { |
498 | let mut winding_number: i32 = 0; |
499 | |
500 | for (p0, p1) in contour.point_pairs(&self.points) { |
501 | if p0.pos.y <= y { |
502 | if p1.pos.y > y && Point::is_left(p0, p1, x, y) > 0.0 { |
503 | winding_number = winding_number.wrapping_add(1); |
504 | } |
505 | } else if p1.pos.y <= y && Point::is_left(p0, p1, x, y) < 0.0 { |
506 | winding_number = winding_number.wrapping_sub(1); |
507 | } |
508 | } |
509 | |
510 | if winding_number != 0 { |
511 | return true; |
512 | } |
513 | } |
514 | } |
515 | |
516 | false |
517 | } |
518 | |
519 | pub(crate) fn expand_fill(&mut self, fringe_width: f32, line_join: LineJoin, miter_limit: f32) { |
520 | let has_fringe = fringe_width > 0.0; |
521 | |
522 | self.calculate_joins(fringe_width, line_join, miter_limit); |
523 | |
524 | // Calculate max vertex usage. |
525 | for contour in &mut self.contours { |
526 | let point_count = contour.point_count(); |
527 | let mut vertex_count = point_count + contour.bevel + 1; |
528 | |
529 | if has_fringe { |
530 | vertex_count += (point_count + contour.bevel * 5 + 1) * 2; |
531 | contour.stroke.reserve(vertex_count); |
532 | } |
533 | |
534 | contour.fill.reserve(vertex_count); |
535 | } |
536 | |
537 | let convex = self.contours.len() == 1 && self.contours[0].convexity == Convexity::Convex; |
538 | |
539 | for contour in &mut self.contours { |
540 | contour.stroke.clear(); |
541 | contour.fill.clear(); |
542 | |
543 | let triangle_count = (contour.fill.capacity() - 2) * 3; |
544 | let mut triangle_fan_fill = Vec::with_capacity(triangle_count); |
545 | |
546 | // TODO: woff = 0.0 produces no artifaacts for small sizes |
547 | let woff = 0.5 * fringe_width; |
548 | //let woff = 0.0; // Makes everything thicker |
549 | |
550 | if has_fringe { |
551 | for (p0, p1) in contour.point_pairs(&self.points) { |
552 | if p1.flags.contains(PointFlags::BEVEL) { |
553 | if p1.flags.contains(PointFlags::LEFT) { |
554 | let lpos = p1.pos + p1.dmpos * woff; |
555 | triangle_fan_fill.push(Vertex::pos(lpos, 0.5, 1.0)); |
556 | } else { |
557 | let lpos0 = p1.pos + p0.dpos.orthogonal() * woff; |
558 | let lpos1 = p1.pos + p1.dpos.orthogonal() * woff; |
559 | triangle_fan_fill.push(Vertex::pos(lpos0, 0.5, 1.0)); |
560 | triangle_fan_fill.push(Vertex::pos(lpos1, 0.5, 1.0)); |
561 | } |
562 | } else { |
563 | triangle_fan_fill.push(Vertex::pos(p1.pos + p1.dmpos * woff, 0.5, 1.0)); |
564 | } |
565 | } |
566 | } else { |
567 | let points = &self.points[contour.point_range.clone()]; |
568 | |
569 | for point in points { |
570 | triangle_fan_fill.push(Vertex::pos(point.pos, 0.5, 1.0)); |
571 | } |
572 | } |
573 | |
574 | // convert fill triangle fan to triangles, to eliminate requirement for GL_TRIANGLE_FAN |
575 | // from the renderer. |
576 | if triangle_fan_fill.len() > 2 { |
577 | let center = triangle_fan_fill[0]; |
578 | let tail = &triangle_fan_fill[1..]; |
579 | contour.fill = tail |
580 | .windows(2) |
581 | .flat_map(|vertices| IntoIterator::into_iter([center, vertices[0], vertices[1]])) |
582 | .collect(); |
583 | } |
584 | |
585 | if has_fringe { |
586 | let rw = fringe_width - woff; |
587 | let ru = 1.0; |
588 | |
589 | let (lw, lu) = if convex { |
590 | // Create only half a fringe for convex shapes so that |
591 | // the shape can be rendered without stenciling. |
592 | (woff, 0.5) |
593 | } else { |
594 | (fringe_width + woff, 0.0) |
595 | }; |
596 | |
597 | for (p0, p1) in contour.point_pairs(&self.points) { |
598 | if p1.flags.contains(PointFlags::BEVEL | PointFlags::INNERBEVEL) { |
599 | bevel_join(&mut contour.stroke, p0, p1, lw, rw, lu, ru); |
600 | } else { |
601 | contour.stroke.push(Vertex::pos(p1.pos + p1.dmpos * lw, lu, 1.0)); |
602 | contour.stroke.push(Vertex::pos(p1.pos - p1.dmpos * rw, ru, 1.0)); |
603 | } |
604 | } |
605 | |
606 | // Loop it |
607 | let p0 = contour.stroke[0]; |
608 | let p1 = contour.stroke[1]; |
609 | contour.stroke.push(Vertex::new(p0.x, p0.y, lu, 1.0)); |
610 | contour.stroke.push(Vertex::new(p1.x, p1.y, ru, 1.0)); |
611 | } |
612 | } |
613 | } |
614 | |
615 | #[allow (clippy::too_many_arguments)] |
616 | pub(crate) fn expand_stroke( |
617 | &mut self, |
618 | stroke_width: f32, |
619 | fringe_width: f32, |
620 | line_cap_start: LineCap, |
621 | line_cap_end: LineCap, |
622 | line_join: LineJoin, |
623 | miter_limit: f32, |
624 | tess_tol: f32, |
625 | ) { |
626 | let ncap = curve_divisions(stroke_width, PI, tess_tol); |
627 | |
628 | let stroke_width = stroke_width + (fringe_width * 0.5); |
629 | |
630 | // Disable the gradient used for antialiasing when antialiasing is not enabled. |
631 | let (u0, u1) = if fringe_width == 0.0 { (0.5, 0.5) } else { (0.0, 1.0) }; |
632 | |
633 | self.calculate_joins(stroke_width, line_join, miter_limit); |
634 | |
635 | for contour in &mut self.contours { |
636 | contour.stroke.clear(); |
637 | |
638 | for (i, (p0, p1)) in contour.point_pairs(&self.points).enumerate() { |
639 | // Add start cap |
640 | if !contour.closed && i == 1 { |
641 | match line_cap_start { |
642 | LineCap::Butt => butt_cap_start( |
643 | &mut contour.stroke, |
644 | p0, |
645 | p0, |
646 | stroke_width, |
647 | -fringe_width * 0.5, |
648 | fringe_width, |
649 | u0, |
650 | u1, |
651 | ), |
652 | LineCap::Square => butt_cap_start( |
653 | &mut contour.stroke, |
654 | p0, |
655 | p0, |
656 | stroke_width, |
657 | stroke_width - fringe_width, |
658 | fringe_width, |
659 | u0, |
660 | u1, |
661 | ), |
662 | LineCap::Round => { |
663 | round_cap_start(&mut contour.stroke, p0, p0, stroke_width, ncap as usize, u0, u1) |
664 | } |
665 | } |
666 | } |
667 | |
668 | if (i > 0 && i < contour.point_count() - 1) || contour.closed { |
669 | if p1.flags.contains(PointFlags::BEVEL) || p1.flags.contains(PointFlags::INNERBEVEL) { |
670 | if line_join == LineJoin::Round { |
671 | round_join( |
672 | &mut contour.stroke, |
673 | p0, |
674 | p1, |
675 | stroke_width, |
676 | stroke_width, |
677 | u0, |
678 | u1, |
679 | ncap as usize, |
680 | ); |
681 | } else { |
682 | bevel_join(&mut contour.stroke, p0, p1, stroke_width, stroke_width, u0, u1); |
683 | } |
684 | } else { |
685 | contour |
686 | .stroke |
687 | .push(Vertex::pos(p1.pos + p1.dmpos * stroke_width, u0, 1.0)); |
688 | contour |
689 | .stroke |
690 | .push(Vertex::pos(p1.pos - p1.dmpos * stroke_width, u1, 1.0)); |
691 | } |
692 | } |
693 | |
694 | // Add end cap |
695 | if !contour.closed && i == contour.point_count() - 1 { |
696 | match line_cap_end { |
697 | LineCap::Butt => butt_cap_end( |
698 | &mut contour.stroke, |
699 | p1, |
700 | p0, |
701 | stroke_width, |
702 | -fringe_width * 0.5, |
703 | fringe_width, |
704 | u0, |
705 | u1, |
706 | ), |
707 | LineCap::Square => butt_cap_end( |
708 | &mut contour.stroke, |
709 | p1, |
710 | p0, |
711 | stroke_width, |
712 | stroke_width - fringe_width, |
713 | fringe_width, |
714 | u0, |
715 | u1, |
716 | ), |
717 | LineCap::Round => { |
718 | round_cap_end(&mut contour.stroke, p1, p0, stroke_width, ncap as usize, u0, u1) |
719 | } |
720 | } |
721 | } |
722 | } |
723 | |
724 | if contour.closed { |
725 | contour |
726 | .stroke |
727 | .push(Vertex::new(contour.stroke[0].x, contour.stroke[0].y, u0, 1.0)); |
728 | contour |
729 | .stroke |
730 | .push(Vertex::new(contour.stroke[1].x, contour.stroke[1].y, u1, 1.0)); |
731 | } |
732 | } |
733 | } |
734 | |
735 | fn calculate_joins(&mut self, stroke_width: f32, line_join: LineJoin, miter_limit: f32) { |
736 | let inv_stroke_width = if stroke_width > 0.0 { 1.0 / stroke_width } else { 0.0 }; |
737 | |
738 | for contour in &mut self.contours { |
739 | let points = &mut self.points[contour.point_range.clone()]; |
740 | let mut nleft = 0; |
741 | |
742 | contour.bevel = 0; |
743 | |
744 | let mut x_sign = 0; |
745 | let mut y_sign = 0; |
746 | let mut x_first_sign = 0; // Sign of first nonzero edge vector x |
747 | let mut y_first_sign = 0; // Sign of first nonzero edge vector y |
748 | let mut x_flips = 0; // Number of sign changes in x |
749 | let mut y_flips = 0; // Number of sign changes in y |
750 | |
751 | for i in 0..points.len() { |
752 | let p0 = if i == 0 { |
753 | points.last().copied().unwrap() |
754 | } else { |
755 | points.get(i - 1).copied().unwrap() |
756 | }; |
757 | |
758 | let p1 = points.get_mut(i).unwrap(); |
759 | |
760 | let dlpos0 = p0.dpos.orthogonal(); |
761 | let dlpos1 = p1.dpos.orthogonal(); |
762 | |
763 | // Calculate extrusions |
764 | p1.dmpos = (dlpos0 + dlpos1) * 0.5; |
765 | let dmr2 = p1.dmpos.mag2(); |
766 | |
767 | if dmr2 > 0.000_001 { |
768 | let scale = (1.0 / dmr2).min(600.0); |
769 | |
770 | p1.dmpos *= scale; |
771 | } |
772 | |
773 | // Clear flags, but keep the corner. |
774 | p1.flags = if p1.flags.contains(PointFlags::CORNER) { |
775 | PointFlags::CORNER |
776 | } else { |
777 | PointFlags::empty() |
778 | }; |
779 | |
780 | // Keep track of left turns. |
781 | let cross = p0.dpos.cross(p1.dpos); |
782 | |
783 | if cross > 0.0 { |
784 | nleft += 1; |
785 | p1.flags |= PointFlags::LEFT; |
786 | } |
787 | |
788 | // Determine sign for convexity |
789 | match p1.dpos.x.partial_cmp(&0.0) { |
790 | Some(Ordering::Greater) => { |
791 | match x_sign.cmp(&0) { |
792 | Ordering::Equal => x_first_sign = 1, |
793 | Ordering::Less => x_flips += 1, |
794 | Ordering::Greater => (), |
795 | } |
796 | |
797 | x_sign = 1; |
798 | } |
799 | Some(Ordering::Less) => { |
800 | match x_sign.cmp(&0) { |
801 | Ordering::Equal => x_first_sign = -1, |
802 | Ordering::Greater => x_flips += 1, |
803 | Ordering::Less => (), |
804 | } |
805 | |
806 | x_sign = -1; |
807 | } |
808 | _ => (), |
809 | } |
810 | |
811 | match p1.dpos.y.partial_cmp(&0.0) { |
812 | Some(Ordering::Greater) => { |
813 | match y_sign.cmp(&0) { |
814 | Ordering::Equal => y_first_sign = 1, |
815 | Ordering::Less => y_flips += 1, |
816 | Ordering::Greater => (), |
817 | } |
818 | |
819 | y_sign = 1; |
820 | } |
821 | Some(Ordering::Less) => { |
822 | match y_sign.cmp(&0) { |
823 | Ordering::Equal => y_first_sign = -1, |
824 | Ordering::Greater => y_flips += 1, |
825 | Ordering::Less => (), |
826 | } |
827 | |
828 | y_sign = -1; |
829 | } |
830 | _ => (), |
831 | } |
832 | |
833 | // Calculate if we should use bevel or miter for inner join. |
834 | let limit = (p0.len.min(p1.len) * inv_stroke_width).max(1.01); |
835 | |
836 | if (dmr2 * limit * limit) < 1.0 { |
837 | p1.flags |= PointFlags::INNERBEVEL; |
838 | } |
839 | |
840 | #[allow (clippy::collapsible_if)] |
841 | // Check to see if the corner needs to be beveled. |
842 | if p1.flags.contains(PointFlags::CORNER) { |
843 | if (dmr2 * miter_limit * miter_limit) < 1.0 |
844 | || line_join == LineJoin::Bevel |
845 | || line_join == LineJoin::Round |
846 | { |
847 | p1.flags |= PointFlags::BEVEL; |
848 | } |
849 | } |
850 | |
851 | if p1.flags.contains(PointFlags::BEVEL | PointFlags::INNERBEVEL) { |
852 | contour.bevel += 1; |
853 | } |
854 | } |
855 | |
856 | if x_sign != 0 && x_first_sign != 0 && x_sign != x_first_sign { |
857 | x_flips += 1; |
858 | } |
859 | |
860 | if y_sign != 0 && y_first_sign != 0 && y_sign != y_first_sign { |
861 | y_flips += 1; |
862 | } |
863 | |
864 | let convex = x_flips == 2 && y_flips == 2; |
865 | |
866 | contour.convexity = if nleft == points.len() && convex { |
867 | Convexity::Convex |
868 | } else { |
869 | Convexity::Concave |
870 | }; |
871 | } |
872 | } |
873 | |
874 | /// If this path is merely a rectangle, return it |
875 | pub(crate) fn path_fill_is_rect(&self) -> Option<crate::Rect> { |
876 | if self.contours.len() != 1 { |
877 | return None; |
878 | } |
879 | |
880 | let vertices = &self.contours[0].fill; |
881 | if vertices.len() != 6 { |
882 | return None; |
883 | } |
884 | |
885 | let maybe_t1_top_left = vertices[0]; |
886 | let maybe_t1_bottom_left = vertices[1]; |
887 | let maybe_t1_bottom_right = vertices[2]; |
888 | let maybe_t2_top_left = vertices[3]; |
889 | let maybe_t2_bottom_right = vertices[4]; |
890 | let maybe_t2_top_right = vertices[5]; |
891 | |
892 | if maybe_t1_top_left == maybe_t2_top_left |
893 | && maybe_t1_bottom_right == maybe_t2_bottom_right |
894 | && maybe_t1_top_left.x == maybe_t1_bottom_left.x |
895 | && maybe_t1_top_left.y == maybe_t2_top_right.y |
896 | && maybe_t1_bottom_right.x == maybe_t2_top_right.x |
897 | && maybe_t2_bottom_right.y == maybe_t1_bottom_left.y |
898 | { |
899 | Some(crate::Rect::new( |
900 | maybe_t1_top_left.x, |
901 | maybe_t1_top_left.y, |
902 | maybe_t2_top_right.x - maybe_t1_top_left.x, |
903 | maybe_t1_bottom_left.y - maybe_t1_top_left.y, |
904 | )) |
905 | } else { |
906 | None |
907 | } |
908 | } |
909 | } |
910 | |
911 | fn curve_divisions(radius: f32, arc: f32, tol: f32) -> u32 { |
912 | let da: f32 = (radius / (radius + tol)).acos() * 2.0; |
913 | |
914 | ((arc / da).ceil() as u32).max(2) |
915 | } |
916 | |
917 | #[allow (clippy::too_many_arguments)] |
918 | fn butt_cap_start(verts: &mut Vec<Vertex>, p0: &Point, p1: &Point, w: f32, d: f32, aa: f32, u0: f32, u1: f32) { |
919 | let ppos: Position = p0.pos - p1.dpos * d; |
920 | let dlpos: Vector = p1.dpos.orthogonal(); |
921 | |
922 | verts.push(Vertex::pos(position:ppos + dlpos * w - p1.dpos * aa, u:u0, v:0.0)); |
923 | verts.push(Vertex::pos(position:ppos - dlpos * w - p1.dpos * aa, u:u1, v:0.0)); |
924 | verts.push(Vertex::pos(position:ppos + dlpos * w, u:u0, v:1.0)); |
925 | verts.push(Vertex::pos(position:ppos - dlpos * w, u:u1, v:1.0)); |
926 | } |
927 | |
928 | #[allow (clippy::too_many_arguments)] |
929 | fn butt_cap_end(verts: &mut Vec<Vertex>, p0: &Point, p1: &Point, w: f32, d: f32, aa: f32, u0: f32, u1: f32) { |
930 | let ppos: Position = p0.pos + p1.dpos * d; |
931 | let dlpos: Vector = p1.dpos.orthogonal(); |
932 | |
933 | verts.push(Vertex::pos(position:ppos + dlpos * w, u:u0, v:1.0)); |
934 | verts.push(Vertex::pos(position:ppos - dlpos * w, u:u1, v:1.0)); |
935 | verts.push(Vertex::pos(position:ppos + dlpos * w + p1.dpos * aa, u:u0, v:0.0)); |
936 | verts.push(Vertex::pos(position:ppos - dlpos * w + p1.dpos * aa, u:u1, v:0.0)); |
937 | } |
938 | |
939 | fn round_cap_start(verts: &mut Vec<Vertex>, p0: &Point, p1: &Point, w: f32, ncap: usize, u0: f32, u1: f32) { |
940 | let ppos: Position = p0.pos; |
941 | let dlpos: Vector = p1.dpos.orthogonal(); |
942 | |
943 | for i: usize in 0..ncap { |
944 | let a: f32 = i as f32 / (ncap as f32 - 1.0) * PI; |
945 | let offset: Vector = Vector::from_angle(a).with_basis(-dlpos, -p1.dpos) * w; |
946 | |
947 | verts.push(Vertex::pos(position:ppos + offset, u:u0, v:1.0)); |
948 | verts.push(Vertex::pos(position:ppos, u:0.5, v:1.0)); |
949 | } |
950 | |
951 | verts.push(Vertex::pos(position:ppos + dlpos * w, u:u0, v:1.0)); |
952 | verts.push(Vertex::pos(position:ppos - dlpos * w, u:u1, v:1.0)); |
953 | } |
954 | |
955 | fn round_cap_end(verts: &mut Vec<Vertex>, p0: &Point, p1: &Point, w: f32, ncap: usize, u0: f32, u1: f32) { |
956 | let ppos: Position = p0.pos; |
957 | let dlpos: Vector = p1.dpos.orthogonal(); |
958 | |
959 | verts.push(Vertex::pos(position:ppos + dlpos * w, u:u0, v:1.0)); |
960 | verts.push(Vertex::pos(position:ppos - dlpos * w, u:u1, v:1.0)); |
961 | |
962 | for i: usize in 0..ncap { |
963 | let a: f32 = i as f32 / (ncap as f32 - 1.0) * PI; |
964 | let offset: Vector = Vector::from_angle(a).with_basis(-dlpos, basis_y:p1.dpos) * w; |
965 | |
966 | verts.push(Vertex::pos(position:ppos, u:0.5, v:1.0)); |
967 | verts.push(Vertex::pos(position:ppos + offset, u:u0, v:1.0)); |
968 | } |
969 | } |
970 | |
971 | fn choose_bevel(bevel: bool, p0: &Point, p1: &Point, w: f32) -> (Position, Position) { |
972 | if bevel { |
973 | (p1.pos + p0.dpos.orthogonal() * w, p1.pos + p1.dpos.orthogonal() * w) |
974 | } else { |
975 | let pos: Position = p1.pos + p1.dmpos * w; |
976 | (pos, pos) |
977 | } |
978 | } |
979 | |
980 | #[allow (clippy::too_many_arguments)] |
981 | fn round_join(verts: &mut Vec<Vertex>, p0: &Point, p1: &Point, lw: f32, rw: f32, lu: f32, ru: f32, ncap: usize) { |
982 | let dlpos0 = p0.dpos.orthogonal(); |
983 | let dlpos1 = p1.dpos.orthogonal(); |
984 | |
985 | let a0; |
986 | let mut a1; |
987 | |
988 | if p1.flags.contains(PointFlags::LEFT) { |
989 | let (lpos0, lpos1) = choose_bevel(p1.flags.contains(PointFlags::INNERBEVEL), p0, p1, lw); |
990 | a0 = (-dlpos0).angle(); |
991 | a1 = (-dlpos1).angle(); |
992 | |
993 | if a1 > a0 { |
994 | a1 -= PI * 2.0; |
995 | } |
996 | |
997 | verts.push(Vertex::pos(lpos0, lu, 1.0)); |
998 | verts.push(Vertex::pos(p1.pos - dlpos0 * rw, ru, 1.0)); |
999 | |
1000 | let n = ((((a0 - a1) / PI) * ncap as f32).ceil() as usize).max(2).min(ncap); |
1001 | |
1002 | for i in 0..n { |
1003 | let u = i as f32 / (n - 1) as f32; |
1004 | let a = a0 + u * (a1 - a0); |
1005 | let rpos = p1.pos + Vector::from_angle(a) * rw; |
1006 | |
1007 | verts.push(Vertex::pos(p1.pos, 0.5, 1.0)); |
1008 | verts.push(Vertex::pos(rpos, ru, 1.0)); |
1009 | } |
1010 | |
1011 | verts.push(Vertex::pos(lpos1, lu, 1.0)); |
1012 | verts.push(Vertex::pos(p1.pos - dlpos1 * rw, ru, 1.0)); |
1013 | } else { |
1014 | let (rpos0, rpos1) = choose_bevel(p1.flags.contains(PointFlags::INNERBEVEL), p0, p1, -rw); |
1015 | a0 = dlpos0.angle(); |
1016 | a1 = dlpos1.angle(); |
1017 | |
1018 | if a1 < a0 { |
1019 | a1 += PI * 2.0; |
1020 | } |
1021 | |
1022 | verts.push(Vertex::pos(p1.pos + dlpos0 * rw, lu, 1.0)); |
1023 | verts.push(Vertex::pos(rpos0, ru, 1.0)); |
1024 | |
1025 | let n = ((((a1 - a0) / PI) * ncap as f32).ceil() as usize).max(2).min(ncap); |
1026 | |
1027 | for i in 0..n { |
1028 | let u = i as f32 / (n - 1) as f32; |
1029 | let a = a0 + u * (a1 - a0); |
1030 | let lpos = p1.pos + Vector::from_angle(a) * lw; |
1031 | |
1032 | verts.push(Vertex::pos(lpos, lu, 1.0)); |
1033 | verts.push(Vertex::pos(p1.pos, 0.5, 1.0)); |
1034 | } |
1035 | |
1036 | verts.push(Vertex::pos(p1.pos + dlpos1 * rw, lu, 1.0)); |
1037 | verts.push(Vertex::pos(rpos1, ru, 1.0)); |
1038 | } |
1039 | } |
1040 | |
1041 | #[allow (clippy::branches_sharing_code)] |
1042 | fn bevel_join(verts: &mut Vec<Vertex>, p0: &Point, p1: &Point, lw: f32, rw: f32, lu: f32, ru: f32) { |
1043 | let dlpos0 = p0.dpos.orthogonal(); |
1044 | let dlpos1 = p1.dpos.orthogonal(); |
1045 | |
1046 | if p1.flags.contains(PointFlags::LEFT) { |
1047 | let (lpos0, lpos1) = choose_bevel(p1.flags.contains(PointFlags::INNERBEVEL), p0, p1, lw); |
1048 | |
1049 | verts.push(Vertex::pos(lpos0, lu, 1.0)); |
1050 | verts.push(Vertex::pos(p1.pos - dlpos0 * rw, ru, 1.0)); |
1051 | |
1052 | if p1.flags.contains(PointFlags::BEVEL) { |
1053 | verts.push(Vertex::pos(lpos0, lu, 1.0)); |
1054 | verts.push(Vertex::pos(p1.pos - dlpos0 * rw, ru, 1.0)); |
1055 | |
1056 | verts.push(Vertex::pos(lpos1, lu, 1.0)); |
1057 | verts.push(Vertex::pos(p1.pos - dlpos1 * rw, ru, 1.0)); |
1058 | } else { |
1059 | let rpos0 = p1.pos - p1.dmpos * rw; |
1060 | |
1061 | verts.push(Vertex::pos(p1.pos, 0.5, 1.0)); |
1062 | verts.push(Vertex::pos(p1.pos - dlpos0 * rw, ru, 1.0)); |
1063 | |
1064 | verts.push(Vertex::pos(rpos0, ru, 1.0)); |
1065 | verts.push(Vertex::pos(rpos0, ru, 1.0)); |
1066 | |
1067 | verts.push(Vertex::pos(p1.pos, 0.5, 1.0)); |
1068 | verts.push(Vertex::pos(p1.pos - dlpos1 * rw, ru, 1.0)); |
1069 | } |
1070 | |
1071 | verts.push(Vertex::pos(lpos1, lu, 1.0)); |
1072 | verts.push(Vertex::pos(p1.pos - dlpos1 * rw, ru, 1.0)); |
1073 | } else { |
1074 | let (rpos0, rpos1) = choose_bevel(p1.flags.contains(PointFlags::INNERBEVEL), p0, p1, -rw); |
1075 | |
1076 | verts.push(Vertex::pos(p1.pos + dlpos0 * lw, lu, 1.0)); |
1077 | verts.push(Vertex::pos(rpos0, ru, 1.0)); |
1078 | |
1079 | if p1.flags.contains(PointFlags::BEVEL) { |
1080 | verts.push(Vertex::pos(p1.pos + dlpos0 * lw, lu, 1.0)); |
1081 | verts.push(Vertex::pos(rpos0, ru, 1.0)); |
1082 | |
1083 | verts.push(Vertex::pos(p1.pos + dlpos1 * lw, lu, 1.0)); |
1084 | verts.push(Vertex::pos(rpos1, ru, 1.0)); |
1085 | } else { |
1086 | let lpos0 = p1.pos + p1.dmpos * lw; |
1087 | |
1088 | verts.push(Vertex::pos(p1.pos + dlpos0 * lw, lu, 1.0)); |
1089 | verts.push(Vertex::pos(p1.pos, 0.5, 1.0)); |
1090 | |
1091 | verts.push(Vertex::pos(lpos0, lu, 1.0)); |
1092 | verts.push(Vertex::pos(lpos0, lu, 1.0)); |
1093 | |
1094 | verts.push(Vertex::pos(p1.pos + dlpos1 * lw, lu, 1.0)); |
1095 | verts.push(Vertex::pos(p1.pos, 0.5, 1.0)); |
1096 | } |
1097 | |
1098 | verts.push(Vertex::pos(p1.pos + dlpos1 * lw, lu, 1.0)); |
1099 | verts.push(Vertex::pos(rpos1, ru, 1.0)); |
1100 | } |
1101 | } |
1102 | |
1103 | #[cfg (test)] |
1104 | mod tests { |
1105 | |
1106 | use super::*; |
1107 | use crate::Path; |
1108 | |
1109 | #[test ] |
1110 | fn self_intersecting_polygon_is_concave() { |
1111 | // star |
1112 | let mut path = Path::new(); |
1113 | path.move_to(50.0, 0.0); |
1114 | path.line_to(21.0, 90.0); |
1115 | path.line_to(98.0, 35.0); |
1116 | path.line_to(2.0, 35.0); |
1117 | path.line_to(79.0, 90.0); |
1118 | path.close(); |
1119 | |
1120 | let transform = Transform2D::identity(); |
1121 | |
1122 | let mut path_cache = PathCache::new(path.verbs(), &transform, 0.25, 0.01); |
1123 | path_cache.expand_fill(1.0, LineJoin::Miter, 10.0); |
1124 | |
1125 | assert_eq!(path_cache.contours[0].convexity, Convexity::Concave); |
1126 | } |
1127 | } |
1128 | |
1129 | /* |
1130 | pub struct MutStridedChunks<'a, T: 'a> { |
1131 | buffer: &'a mut [T], |
1132 | rotated: bool, |
1133 | pos: usize, |
1134 | } |
1135 | |
1136 | impl<'a, T: 'a> MutStridedChunks<'a, T> { |
1137 | pub fn new(buffer: &'a mut [T]) -> Self { |
1138 | buffer.rotate_right(1); |
1139 | Self { |
1140 | buffer: buffer, |
1141 | rotated: false, |
1142 | pos: 0 |
1143 | } |
1144 | } |
1145 | |
1146 | fn next(&mut self) -> Option<&mut [T]> { |
1147 | if self.pos == self.buffer.len() - 1 && !self.rotated { |
1148 | self.buffer.rotate_left(1); |
1149 | self.rotated = true; |
1150 | self.pos -= 1; |
1151 | } |
1152 | |
1153 | let len = self.buffer.len() - self.pos; |
1154 | |
1155 | if 2 <= len { |
1156 | let (start, end) = (self.pos, self.pos + 2); |
1157 | let subslice = &mut self.buffer[start..end]; |
1158 | |
1159 | self.pos += 1; |
1160 | Some(subslice) |
1161 | } else { |
1162 | None |
1163 | } |
1164 | } |
1165 | } |
1166 | |
1167 | fn main() { |
1168 | let mut my_array = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]; |
1169 | |
1170 | let mut iter = MutStridedChunks::new(&mut my_array); |
1171 | |
1172 | while let Some(subslice) = iter.next() { |
1173 | log::info!("{:?}", subslice); |
1174 | } |
1175 | } |
1176 | */ |
1177 | |