summaryrefslogtreecommitdiffstats
path: root/graphics
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón <hector0193@gmail.com>2022-07-10 18:40:11 +0200
committerLibravatar GitHub <noreply@github.com>2022-07-10 18:40:11 +0200
commitd1505a98d967f36679f8b7d98347ba03a4c2af1c (patch)
treeb0139b1e87a7f98536c3d83cac1b99eafd0b28aa /graphics
parent9051dd6977d045f991092e247e6bd9da40d2e793 (diff)
parent3a26a8ccd4c406b4b3f0d5c6d2863d0613798f01 (diff)
downloadiced-d1505a98d967f36679f8b7d98347ba03a4c2af1c.tar.gz
iced-d1505a98d967f36679f8b7d98347ba03a4c2af1c.tar.bz2
iced-d1505a98d967f36679f8b7d98347ba03a4c2af1c.zip
Merge pull request #1358 from ThatsNoMoon/fix-arc_to
Fix `arc_to`
Diffstat (limited to 'graphics')
-rw-r--r--graphics/src/widget/canvas/path/builder.rs53
1 files changed, 46 insertions, 7 deletions
diff --git a/graphics/src/widget/canvas/path/builder.rs b/graphics/src/widget/canvas/path/builder.rs
index 05316d8a..88acf544 100644
--- a/graphics/src/widget/canvas/path/builder.rs
+++ b/graphics/src/widget/canvas/path/builder.rs
@@ -42,22 +42,61 @@ impl Builder {
/// Adds a circular arc to the [`Path`] with the given control points and
/// radius.
///
- /// The arc is connected to the previous point by a straight line, if
- /// necessary.
+ /// This essentially draws a straight line segment from the current
+ /// position to `a`, but fits a circular arc of `radius` tangent to that
+ /// segment and tangent to the line between `a` and `b`.
+ ///
+ /// With another `.line_to(b)`, the result will be a path connecting the
+ /// starting point and `b` with straight line segments towards `a` and a
+ /// circular arc smoothing out the corner at `a`.
+ ///
+ /// See [the HTML5 specification of `arcTo`](https://html.spec.whatwg.org/multipage/canvas.html#building-paths:dom-context-2d-arcto)
+ /// for more details and examples.
pub fn arc_to(&mut self, a: Point, b: Point, radius: f32) {
use lyon::{math, path};
- let a = math::Point::new(a.x, a.y);
+ let start = self.raw.current_position();
+ let mid = math::Point::new(a.x, a.y);
+ let end = math::Point::new(b.x, b.y);
+
+ if start == mid || mid == end || radius == 0.0 {
+ let _ = self.raw.line_to(mid);
+ return;
+ }
+
+ let double_area = start.x * (mid.y - end.y)
+ + mid.x * (end.y - start.y)
+ + end.x * (start.y - mid.y);
- if self.raw.current_position() != a {
- let _ = self.raw.line_to(a);
+ if double_area == 0.0 {
+ let _ = self.raw.line_to(mid);
+ return;
}
+ let to_start = (start - mid).normalize();
+ let to_end = (end - mid).normalize();
+
+ let inner_angle = to_start.dot(to_end).acos();
+
+ let origin_angle = inner_angle / 2.0;
+
+ let origin_adjacent = radius / origin_angle.tan();
+
+ let arc_start = mid + to_start * origin_adjacent;
+ let arc_end = mid + to_end * origin_adjacent;
+
+ let sweep = to_start.cross(to_end) < 0.0;
+
+ let _ = self.raw.line_to(arc_start);
+
self.raw.arc_to(
math::Vector::new(radius, radius),
math::Angle::radians(0.0),
- path::ArcFlags::default(),
- math::Point::new(b.x, b.y),
+ path::ArcFlags {
+ large_arc: false,
+ sweep,
+ },
+ arc_end,
);
}