1use std::f64::consts::SQRT_2;
2
3use kurbo::{CubicBez, ParamCurveExtrema};
4use typst_library::diag::{bail, SourceResult};
5use typst_library::engine::Engine;
6use typst_library::foundations::{Content, Packed, Resolve, Smart, StyleChain};
7use typst_library::introspection::Locator;
8use typst_library::layout::{
9 Abs, Axes, Corner, Corners, Frame, FrameItem, Length, Point, Ratio, Region, Rel,
10 Sides, Size,
11};
12use typst_library::visualize::{
13 CircleElem, CloseMode, Curve, CurveComponent, CurveElem, EllipseElem, FillRule,
14 FixedStroke, Geometry, LineElem, Paint, PathElem, PathVertex, PolygonElem, RectElem,
15 Shape, SquareElem, Stroke,
16};
17use typst_syntax::Span;
18use typst_utils::{Get, Numeric};
19
20#[typst_macros::time(span = elem.span())]
22pub fn layout_line(
23 elem: &Packed<LineElem>,
24 _: &mut Engine,
25 _: Locator,
26 styles: StyleChain,
27 region: Region,
28) -> SourceResult<Frame> {
29 let resolve = |axes: Axes<Rel<Abs>>| axes.zip_map(region.size, Rel::relative_to);
30 let start = resolve(elem.start(styles));
31 let delta = elem.end(styles).map(|end| resolve(end) - start).unwrap_or_else(|| {
32 let length = elem.length(styles);
33 let angle = elem.angle(styles);
34 let x = angle.cos() * length;
35 let y = angle.sin() * length;
36 resolve(Axes::new(x, y))
37 });
38
39 let stroke = elem.stroke(styles).unwrap_or_default();
40 let size = start.max(start + delta).max(Size::zero());
41
42 if !size.is_finite() {
43 bail!(elem.span(), "cannot create line with infinite length");
44 }
45
46 let mut frame = Frame::soft(size);
47 let shape = Geometry::Line(delta.to_point()).stroked(stroke);
48 frame.push(start.to_point(), FrameItem::Shape(shape, elem.span()));
49 Ok(frame)
50}
51
52#[typst_macros::time(span = elem.span())]
54pub fn layout_path(
55 elem: &Packed<PathElem>,
56 _: &mut Engine,
57 _: Locator,
58 styles: StyleChain,
59 region: Region,
60) -> SourceResult<Frame> {
61 let resolve = |axes: Axes<Rel<Length>>| {
62 axes.resolve(styles).zip_map(region.size, Rel::relative_to).to_point()
63 };
64
65 let vertices = &elem.vertices;
66 let points: Vec<Point> = vertices.iter().map(|c| resolve(c.vertex())).collect();
67
68 let mut size = Size::zero();
69 if points.is_empty() {
70 return Ok(Frame::soft(size));
71 }
72
73 let mut curve = Curve::new();
76 curve.move_(points[0]);
77
78 let mut add_cubic = |from_point: Point,
79 to_point: Point,
80 from: PathVertex,
81 to: PathVertex| {
82 let from_control_point = resolve(from.control_point_from()) + from_point;
83 let to_control_point = resolve(to.control_point_to()) + to_point;
84 curve.cubic(from_control_point, to_control_point, to_point);
85
86 let p0 = kurbo::Point::new(from_point.x.to_raw(), from_point.y.to_raw());
87 let p1 = kurbo::Point::new(
88 from_control_point.x.to_raw(),
89 from_control_point.y.to_raw(),
90 );
91 let p2 =
92 kurbo::Point::new(to_control_point.x.to_raw(), to_control_point.y.to_raw());
93 let p3 = kurbo::Point::new(to_point.x.to_raw(), to_point.y.to_raw());
94 let extrema = kurbo::CubicBez::new(p0, p1, p2, p3).bounding_box();
95 size.x.set_max(Abs::raw(extrema.x1));
96 size.y.set_max(Abs::raw(extrema.y1));
97 };
98
99 for (vertex_window, point_window) in vertices.windows(2).zip(points.windows(2)) {
100 let from = vertex_window[0];
101 let to = vertex_window[1];
102 let from_point = point_window[0];
103 let to_point = point_window[1];
104
105 add_cubic(from_point, to_point, from, to);
106 }
107
108 if elem.closed(styles) {
109 let from = *vertices.last().unwrap(); let to = vertices[0];
111 let from_point = *points.last().unwrap();
112 let to_point = points[0];
113
114 add_cubic(from_point, to_point, from, to);
115 curve.close();
116 }
117
118 if !size.is_finite() {
119 bail!(elem.span(), "cannot create path with infinite length");
120 }
121
122 let fill = elem.fill(styles);
124 let fill_rule = elem.fill_rule(styles);
125 let stroke = match elem.stroke(styles) {
126 Smart::Auto if fill.is_none() => Some(FixedStroke::default()),
127 Smart::Auto => None,
128 Smart::Custom(stroke) => stroke.map(Stroke::unwrap_or_default),
129 };
130
131 let mut frame = Frame::soft(size);
132 let shape = Shape {
133 geometry: Geometry::Curve(curve),
134 stroke,
135 fill,
136 fill_rule,
137 };
138 frame.push(Point::zero(), FrameItem::Shape(shape, elem.span()));
139 Ok(frame)
140}
141
142#[typst_macros::time(span = elem.span())]
144pub fn layout_curve(
145 elem: &Packed<CurveElem>,
146 _: &mut Engine,
147 _: Locator,
148 styles: StyleChain,
149 region: Region,
150) -> SourceResult<Frame> {
151 let mut builder = CurveBuilder::new(region, styles);
152
153 for item in &elem.components {
154 match item {
155 CurveComponent::Move(element) => {
156 let relative = element.relative(styles);
157 let point = builder.resolve_point(element.start, relative);
158 builder.move_(point);
159 }
160
161 CurveComponent::Line(element) => {
162 let relative = element.relative(styles);
163 let point = builder.resolve_point(element.end, relative);
164 builder.line(point);
165 }
166
167 CurveComponent::Quad(element) => {
168 let relative = element.relative(styles);
169 let end = builder.resolve_point(element.end, relative);
170 let control = match element.control {
171 Smart::Auto => {
172 control_c2q(builder.last_point, builder.last_control_from)
173 }
174 Smart::Custom(Some(p)) => builder.resolve_point(p, relative),
175 Smart::Custom(None) => end,
176 };
177 builder.quad(control, end);
178 }
179
180 CurveComponent::Cubic(element) => {
181 let relative = element.relative(styles);
182 let end = builder.resolve_point(element.end, relative);
183 let c1 = match element.control_start {
184 Some(Smart::Custom(p)) => builder.resolve_point(p, relative),
185 Some(Smart::Auto) => builder.last_control_from,
186 None => builder.last_point,
187 };
188 let c2 = match element.control_end {
189 Some(p) => builder.resolve_point(p, relative),
190 None => end,
191 };
192 builder.cubic(c1, c2, end);
193 }
194
195 CurveComponent::Close(element) => {
196 builder.close(element.mode(styles));
197 }
198 }
199 }
200
201 let (curve, size) = builder.finish();
202 if curve.is_empty() {
203 return Ok(Frame::soft(size));
204 }
205
206 if !size.is_finite() {
207 bail!(elem.span(), "cannot create curve with infinite size");
208 }
209
210 let fill = elem.fill(styles);
212 let fill_rule = elem.fill_rule(styles);
213 let stroke = match elem.stroke(styles) {
214 Smart::Auto if fill.is_none() => Some(FixedStroke::default()),
215 Smart::Auto => None,
216 Smart::Custom(stroke) => stroke.map(Stroke::unwrap_or_default),
217 };
218
219 let mut frame = Frame::soft(size);
220 let shape = Shape {
221 geometry: Geometry::Curve(curve),
222 stroke,
223 fill,
224 fill_rule,
225 };
226 frame.push(Point::zero(), FrameItem::Shape(shape, elem.span()));
227 Ok(frame)
228}
229
230struct CurveBuilder<'a> {
232 curve: Curve,
234 size: Size,
236 region: Region,
238 styles: StyleChain<'a>,
240 start_point: Point,
242 start_control_into: Point,
244 last_point: Point,
246 last_control_from: Point,
248 is_started: bool,
251 is_empty: bool,
253}
254
255impl<'a> CurveBuilder<'a> {
256 fn new(region: Region, styles: StyleChain<'a>) -> Self {
258 Self {
259 curve: Curve::new(),
260 size: Size::zero(),
261 region,
262 styles,
263 start_point: Point::zero(),
264 start_control_into: Point::zero(),
265 last_point: Point::zero(),
266 last_control_from: Point::zero(),
267 is_started: false,
268 is_empty: true,
269 }
270 }
271
272 fn finish(self) -> (Curve, Size) {
274 (self.curve, self.size)
275 }
276
277 fn move_(&mut self, point: Point) {
279 self.expand_bounds(point);
282 self.start_point = point;
283 self.start_control_into = point;
284 self.last_point = point;
285 self.last_control_from = point;
286 self.is_started = true;
287 }
288
289 fn line(&mut self, point: Point) {
291 if self.is_empty {
292 self.start_component();
293 self.start_control_into = self.start_point;
294 }
295 self.curve.line(point);
296 self.expand_bounds(point);
297 self.last_point = point;
298 self.last_control_from = point;
299 }
300
301 fn quad(&mut self, control: Point, end: Point) {
303 let c1 = control_q2c(self.last_point, control);
304 let c2 = control_q2c(end, control);
305 self.cubic(c1, c2, end);
306 }
307
308 fn cubic(&mut self, c1: Point, c2: Point, end: Point) {
310 if self.is_empty {
311 self.start_component();
312 self.start_control_into = mirror_c(self.start_point, c1);
313 }
314 self.curve.cubic(c1, c2, end);
315
316 let p0 = point_to_kurbo(self.last_point);
317 let p1 = point_to_kurbo(c1);
318 let p2 = point_to_kurbo(c2);
319 let p3 = point_to_kurbo(end);
320 let extrema = CubicBez::new(p0, p1, p2, p3).bounding_box();
321 self.size.x.set_max(Abs::raw(extrema.x1));
322 self.size.y.set_max(Abs::raw(extrema.y1));
323
324 self.last_point = end;
325 self.last_control_from = mirror_c(end, c2);
326 }
327
328 fn close(&mut self, mode: CloseMode) {
330 if self.is_started && !self.is_empty {
331 if mode == CloseMode::Smooth {
332 self.cubic(
333 self.last_control_from,
334 self.start_control_into,
335 self.start_point,
336 );
337 }
338 self.curve.close();
339 self.last_point = self.start_point;
340 self.last_control_from = self.start_point;
341 }
342 self.is_started = false;
343 self.is_empty = true;
344 }
345
346 fn start_component(&mut self) {
348 self.curve.move_(self.start_point);
349 self.is_empty = false;
350 self.is_started = true;
351 }
352
353 fn expand_bounds(&mut self, point: Point) {
355 self.size.x.set_max(point.x);
356 self.size.y.set_max(point.y);
357 }
358
359 fn resolve_point(&self, point: Axes<Rel>, relative: bool) -> Point {
361 let mut p = point
362 .resolve(self.styles)
363 .zip_map(self.region.size, Rel::relative_to)
364 .to_point();
365 if relative {
366 p += self.last_point;
367 }
368 p
369 }
370}
371
372fn control_c2q(p: Point, c: Point) -> Point {
374 1.5 * c - 0.5 * p
375}
376
377fn control_q2c(p: Point, c: Point) -> Point {
379 (p + 2.0 * c) / 3.0
380}
381
382fn mirror_c(p: Point, c: Point) -> Point {
384 2.0 * p - c
385}
386
387fn point_to_kurbo(point: Point) -> kurbo::Point {
389 kurbo::Point::new(point.x.to_raw(), point.y.to_raw())
390}
391
392#[typst_macros::time(span = elem.span())]
394pub fn layout_polygon(
395 elem: &Packed<PolygonElem>,
396 _: &mut Engine,
397 _: Locator,
398 styles: StyleChain,
399 region: Region,
400) -> SourceResult<Frame> {
401 let points: Vec<Point> = elem
402 .vertices
403 .iter()
404 .map(|c| c.resolve(styles).zip_map(region.size, Rel::relative_to).to_point())
405 .collect();
406
407 let size = points.iter().fold(Point::zero(), |max, c| c.max(max)).to_size();
408 if !size.is_finite() {
409 bail!(elem.span(), "cannot create polygon with infinite size");
410 }
411
412 let mut frame = Frame::hard(size);
413
414 if points.is_empty() {
416 return Ok(frame);
417 }
418
419 let fill = elem.fill(styles);
421 let fill_rule = elem.fill_rule(styles);
422 let stroke = match elem.stroke(styles) {
423 Smart::Auto if fill.is_none() => Some(FixedStroke::default()),
424 Smart::Auto => None,
425 Smart::Custom(stroke) => stroke.map(Stroke::unwrap_or_default),
426 };
427
428 let mut curve = Curve::new();
430 curve.move_(points[0]);
431 for &point in &points[1..] {
432 curve.line(point);
433 }
434 curve.close();
435
436 let shape = Shape {
437 geometry: Geometry::Curve(curve),
438 stroke,
439 fill,
440 fill_rule,
441 };
442 frame.push(Point::zero(), FrameItem::Shape(shape, elem.span()));
443 Ok(frame)
444}
445
446#[typst_macros::time(span = elem.span())]
448pub fn layout_rect(
449 elem: &Packed<RectElem>,
450 engine: &mut Engine,
451 locator: Locator,
452 styles: StyleChain,
453 region: Region,
454) -> SourceResult<Frame> {
455 layout_shape(
456 engine,
457 locator,
458 styles,
459 region,
460 ShapeKind::Rect,
461 elem.body(styles),
462 elem.fill(styles),
463 elem.stroke(styles),
464 elem.inset(styles),
465 elem.outset(styles),
466 elem.radius(styles),
467 elem.span(),
468 )
469}
470
471#[typst_macros::time(span = elem.span())]
473pub fn layout_square(
474 elem: &Packed<SquareElem>,
475 engine: &mut Engine,
476 locator: Locator,
477 styles: StyleChain,
478 region: Region,
479) -> SourceResult<Frame> {
480 layout_shape(
481 engine,
482 locator,
483 styles,
484 region,
485 ShapeKind::Square,
486 elem.body(styles),
487 elem.fill(styles),
488 elem.stroke(styles),
489 elem.inset(styles),
490 elem.outset(styles),
491 elem.radius(styles),
492 elem.span(),
493 )
494}
495
496#[typst_macros::time(span = elem.span())]
498pub fn layout_ellipse(
499 elem: &Packed<EllipseElem>,
500 engine: &mut Engine,
501 locator: Locator,
502 styles: StyleChain,
503 region: Region,
504) -> SourceResult<Frame> {
505 layout_shape(
506 engine,
507 locator,
508 styles,
509 region,
510 ShapeKind::Ellipse,
511 elem.body(styles),
512 elem.fill(styles),
513 elem.stroke(styles).map(|s| Sides::splat(Some(s))),
514 elem.inset(styles),
515 elem.outset(styles),
516 Corners::splat(None),
517 elem.span(),
518 )
519}
520
521#[typst_macros::time(span = elem.span())]
523pub fn layout_circle(
524 elem: &Packed<CircleElem>,
525 engine: &mut Engine,
526 locator: Locator,
527 styles: StyleChain,
528 region: Region,
529) -> SourceResult<Frame> {
530 layout_shape(
531 engine,
532 locator,
533 styles,
534 region,
535 ShapeKind::Circle,
536 elem.body(styles),
537 elem.fill(styles),
538 elem.stroke(styles).map(|s| Sides::splat(Some(s))),
539 elem.inset(styles),
540 elem.outset(styles),
541 Corners::splat(None),
542 elem.span(),
543 )
544}
545
546#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
548enum ShapeKind {
549 Square,
551 Rect,
553 Circle,
555 Ellipse,
557}
558
559impl ShapeKind {
560 fn is_round(self) -> bool {
562 matches!(self, Self::Circle | Self::Ellipse)
563 }
564
565 fn is_quadratic(self) -> bool {
567 matches!(self, Self::Square | Self::Circle)
568 }
569}
570
571#[allow(clippy::too_many_arguments)]
573fn layout_shape(
574 engine: &mut Engine,
575 locator: Locator,
576 styles: StyleChain,
577 region: Region,
578 kind: ShapeKind,
579 body: &Option<Content>,
580 fill: Option<Paint>,
581 stroke: Smart<Sides<Option<Option<Stroke<Abs>>>>>,
582 inset: Sides<Option<Rel<Abs>>>,
583 outset: Sides<Option<Rel<Abs>>>,
584 radius: Corners<Option<Rel<Abs>>>,
585 span: Span,
586) -> SourceResult<Frame> {
587 let mut frame;
588 if let Some(child) = body {
589 let mut inset = inset.unwrap_or_default();
590 if kind.is_round() {
591 inset = inset.map(|v| v + Ratio::new(0.5 - SQRT_2 / 4.0));
593 }
594 let has_inset = !inset.is_zero();
595
596 let mut pod = region;
598 if has_inset {
599 pod.size = crate::pad::shrink(region.size, &inset);
600 }
601
602 if kind.is_quadratic() {
606 let length = match quadratic_size(pod) {
607 Some(length) => length,
608 None => {
609 crate::layout_frame(engine, child, locator.relayout(), styles, pod)?
611 .size()
612 .max_by_side()
613 .min(pod.size.min_by_side())
614 }
615 };
616
617 pod = Region::new(Size::splat(length), Axes::splat(true));
618 }
619
620 frame = crate::layout_frame(engine, child, locator, styles, pod)?;
622
623 if has_inset {
625 crate::pad::grow(&mut frame, &inset);
626 }
627 } else {
628 let default = Size::new(Abs::pt(45.0), Abs::pt(30.0)).min(region.size);
631
632 let size = if kind.is_quadratic() {
633 Size::splat(match quadratic_size(region) {
634 Some(length) => length,
635 None => default.min_by_side(),
636 })
637 } else {
638 region.expand.select(region.size, default)
642 };
643
644 frame = Frame::soft(size);
645 }
646
647 let stroke = match stroke {
649 Smart::Auto if fill.is_none() => Sides::splat(Some(FixedStroke::default())),
650 Smart::Auto => Sides::splat(None),
651 Smart::Custom(strokes) => {
652 strokes.unwrap_or_default().map(|s| s.map(Stroke::unwrap_or_default))
653 }
654 };
655
656 if fill.is_some() || stroke.iter().any(Option::is_some) {
658 if kind.is_round() {
659 let outset = outset.unwrap_or_default().relative_to(frame.size());
660 let size = frame.size() + outset.sum_by_axis();
661 let pos = Point::new(-outset.left, -outset.top);
662 let shape = Shape {
663 geometry: Geometry::Curve(Curve::ellipse(size)),
664 fill,
665 stroke: stroke.left,
666 fill_rule: FillRule::default(),
667 };
668 frame.prepend(pos, FrameItem::Shape(shape, span));
669 } else {
670 fill_and_stroke(
671 &mut frame,
672 fill,
673 &stroke,
674 &outset.unwrap_or_default(),
675 &radius.unwrap_or_default(),
676 span,
677 );
678 }
679 }
680
681 Ok(frame)
682}
683
684fn quadratic_size(region: Region) -> Option<Abs> {
689 if region.expand.x && region.expand.y {
690 Some(region.size.x.min(region.size.y))
693 } else if region.expand.x {
694 Some(region.size.x)
695 } else if region.expand.y {
696 Some(region.size.y)
697 } else {
698 None
699 }
700}
701
702pub fn clip_rect(
704 size: Size,
705 radius: &Corners<Rel<Abs>>,
706 stroke: &Sides<Option<FixedStroke>>,
707 outset: &Sides<Rel<Abs>>,
708) -> Curve {
709 let outset = outset.relative_to(size);
710 let size = size + outset.sum_by_axis();
711
712 let stroke_widths = stroke
713 .as_ref()
714 .map(|s| s.as_ref().map_or(Abs::zero(), |s| s.thickness / 2.0));
715
716 let max_radius = (size.x.min(size.y)) / 2.0
717 + stroke_widths.iter().cloned().min().unwrap_or(Abs::zero());
718
719 let radius = radius.map(|side| side.relative_to(max_radius * 2.0).min(max_radius));
720 let corners = corners_control_points(size, &radius, stroke, &stroke_widths);
721
722 let mut curve = Curve::new();
723 if corners.top_left.arc_inner() {
724 curve.arc_move(
725 corners.top_left.start_inner(),
726 corners.top_left.center_inner(),
727 corners.top_left.end_inner(),
728 );
729 } else {
730 curve.move_(corners.top_left.center_inner());
731 }
732 for corner in [&corners.top_right, &corners.bottom_right, &corners.bottom_left] {
733 if corner.arc_inner() {
734 curve.arc_line(
735 corner.start_inner(),
736 corner.center_inner(),
737 corner.end_inner(),
738 )
739 } else {
740 curve.line(corner.center_inner());
741 }
742 }
743 curve.close();
744 curve.translate(Point::new(-outset.left, -outset.top));
745 curve
746}
747
748pub fn fill_and_stroke(
750 frame: &mut Frame,
751 fill: Option<Paint>,
752 stroke: &Sides<Option<FixedStroke>>,
753 outset: &Sides<Rel<Abs>>,
754 radius: &Corners<Rel<Abs>>,
755 span: Span,
756) {
757 let outset = outset.relative_to(frame.size());
758 let size = frame.size() + outset.sum_by_axis();
759 let pos = Point::new(-outset.left, -outset.top);
760 frame.prepend_multiple(
761 styled_rect(size, radius, fill, stroke)
762 .into_iter()
763 .map(|x| (pos, FrameItem::Shape(x, span))),
764 );
765}
766
767pub fn styled_rect(
772 size: Size,
773 radius: &Corners<Rel<Abs>>,
774 fill: Option<Paint>,
775 stroke: &Sides<Option<FixedStroke>>,
776) -> Vec<Shape> {
777 if stroke.is_uniform() && radius.iter().cloned().all(Rel::is_zero) {
778 simple_rect(size, fill, stroke.top.clone())
779 } else {
780 segmented_rect(size, radius, fill, stroke)
781 }
782}
783
784fn simple_rect(
786 size: Size,
787 fill: Option<Paint>,
788 stroke: Option<FixedStroke>,
789) -> Vec<Shape> {
790 vec![Shape {
791 geometry: Geometry::Rect(size),
792 fill,
793 stroke,
794 fill_rule: FillRule::default(),
795 }]
796}
797
798fn corners_control_points(
799 size: Size,
800 radius: &Corners<Abs>,
801 strokes: &Sides<Option<FixedStroke>>,
802 stroke_widths: &Sides<Abs>,
803) -> Corners<ControlPoints> {
804 Corners {
805 top_left: Corner::TopLeft,
806 top_right: Corner::TopRight,
807 bottom_right: Corner::BottomRight,
808 bottom_left: Corner::BottomLeft,
809 }
810 .map(|corner| ControlPoints {
811 radius: radius.get(corner),
812 stroke_before: stroke_widths.get(corner.side_ccw()),
813 stroke_after: stroke_widths.get(corner.side_cw()),
814 corner,
815 size,
816 same: match (
817 strokes.get_ref(corner.side_ccw()),
818 strokes.get_ref(corner.side_cw()),
819 ) {
820 (Some(a), Some(b)) => a.paint == b.paint && a.dash == b.dash,
821 (None, None) => true,
822 _ => false,
823 },
824 })
825}
826
827fn segmented_rect(
829 size: Size,
830 radius: &Corners<Rel<Abs>>,
831 fill: Option<Paint>,
832 strokes: &Sides<Option<FixedStroke>>,
833) -> Vec<Shape> {
834 let mut res = vec![];
835 let stroke_widths = strokes
836 .as_ref()
837 .map(|s| s.as_ref().map_or(Abs::zero(), |s| s.thickness / 2.0));
838
839 let max_radius = (size.x.min(size.y)) / 2.0
840 + stroke_widths.iter().cloned().min().unwrap_or(Abs::zero());
841
842 let radius = radius.map(|side| side.relative_to(max_radius * 2.0).min(max_radius));
843 let corners = corners_control_points(size, &radius, strokes, &stroke_widths);
844
845 let mut stroke_insert = 0;
847
848 if let Some(fill) = fill {
850 let mut curve = Curve::new();
851 let c = corners.get_ref(Corner::TopLeft);
852 if c.arc() {
853 curve.arc_move(c.start(), c.center(), c.end());
854 } else {
855 curve.move_(c.center());
856 };
857
858 for corner in [Corner::TopRight, Corner::BottomRight, Corner::BottomLeft] {
859 let c = corners.get_ref(corner);
860 if c.arc() {
861 curve.arc_line(c.start(), c.center(), c.end());
862 } else {
863 curve.line(c.center());
864 }
865 }
866 curve.close();
867 res.push(Shape {
868 geometry: Geometry::Curve(curve),
869 fill: Some(fill),
870 fill_rule: FillRule::default(),
871 stroke: None,
872 });
873 stroke_insert += 1;
874 }
875
876 let current = corners.iter().find(|c| !c.same).map(|c| c.corner);
877 if let Some(mut current) = current {
878 let mut last = current;
881 for _ in 0..4 {
882 current = current.next_cw();
883 if corners.get_ref(current).same {
884 continue;
885 }
886 let start = last;
888 let end = current;
889 last = current;
890 let Some(stroke) = strokes.get_ref(start.side_cw()) else { continue };
891 let (shape, ontop) = segment(start, end, &corners, stroke);
892 if ontop {
893 res.push(shape);
894 } else {
895 res.insert(stroke_insert, shape);
896 stroke_insert += 1;
897 }
898 }
899 } else if let Some(stroke) = &strokes.top {
900 let (shape, _) = segment(Corner::TopLeft, Corner::TopLeft, &corners, stroke);
902 res.push(shape);
903 }
904 res
905}
906
907fn curve_segment(
908 start: Corner,
909 end: Corner,
910 corners: &Corners<ControlPoints>,
911 curve: &mut Curve,
912) {
913 let c = corners.get_ref(start);
915 if start == end || !c.arc() {
916 curve.move_(c.end());
917 } else {
918 curve.arc_move(c.mid(), c.center(), c.end());
919 }
920
921 let mut current = start.next_cw();
923 while current != end {
924 let c = corners.get_ref(current);
925 if c.arc() {
926 curve.arc_line(c.start(), c.center(), c.end());
927 } else {
928 curve.line(c.end());
929 }
930 current = current.next_cw();
931 }
932
933 let c = corners.get_ref(end);
935 if !c.arc() {
936 curve.line(c.start());
937 } else if start == end {
938 curve.arc_line(c.start(), c.center(), c.end());
939 } else {
940 curve.arc_line(c.start(), c.center(), c.mid());
941 }
942}
943
944fn segment(
946 start: Corner,
947 end: Corner,
948 corners: &Corners<ControlPoints>,
949 stroke: &FixedStroke,
950) -> (Shape, bool) {
951 fn fill_corner(corner: &ControlPoints) -> bool {
952 corner.stroke_before != corner.stroke_after
953 || corner.radius() < corner.stroke_before
954 }
955
956 fn fill_corners(
957 start: Corner,
958 end: Corner,
959 corners: &Corners<ControlPoints>,
960 ) -> bool {
961 if fill_corner(corners.get_ref(start)) {
962 return true;
963 }
964 if fill_corner(corners.get_ref(end)) {
965 return true;
966 }
967 let mut current = start.next_cw();
968 while current != end {
969 if fill_corner(corners.get_ref(current)) {
970 return true;
971 }
972 current = current.next_cw();
973 }
974 false
975 }
976
977 let solid = stroke.dash.as_ref().map(|dash| dash.array.is_empty()).unwrap_or(true);
978
979 let use_fill = solid && fill_corners(start, end, corners);
980 let shape = if use_fill {
981 fill_segment(start, end, corners, stroke)
982 } else {
983 stroke_segment(start, end, corners, stroke.clone())
984 };
985
986 (shape, use_fill)
987}
988
989fn stroke_segment(
991 start: Corner,
992 end: Corner,
993 corners: &Corners<ControlPoints>,
994 stroke: FixedStroke,
995) -> Shape {
996 let mut curve = Curve::new();
998 curve_segment(start, end, corners, &mut curve);
999
1000 Shape {
1001 geometry: Geometry::Curve(curve),
1002 stroke: Some(stroke),
1003 fill: None,
1004 fill_rule: FillRule::default(),
1005 }
1006}
1007
1008fn fill_segment(
1010 start: Corner,
1011 end: Corner,
1012 corners: &Corners<ControlPoints>,
1013 stroke: &FixedStroke,
1014) -> Shape {
1015 let mut curve = Curve::new();
1016
1017 if start == end {
1022 let c = corners.get_ref(start);
1023 curve.move_(c.end_inner());
1024 curve.line(c.end_outer());
1025 } else {
1026 let c = corners.get_ref(start);
1027
1028 if c.arc_inner() {
1029 curve.arc_move(c.end_inner(), c.center_inner(), c.mid_inner());
1030 } else {
1031 curve.move_(c.end_inner());
1032 }
1033
1034 if c.arc_outer() {
1035 curve.arc_line(c.mid_outer(), c.center_outer(), c.end_outer());
1036 } else {
1037 curve.line(c.outer());
1038 curve.line(c.end_outer());
1039 }
1040 }
1041
1042 let mut current = start.next_cw();
1044 while current != end {
1045 let c = corners.get_ref(current);
1046 if c.arc_outer() {
1047 curve.arc_line(c.start_outer(), c.center_outer(), c.end_outer());
1048 } else {
1049 curve.line(c.outer());
1050 }
1051 current = current.next_cw();
1052 }
1053
1054 if start == end {
1059 let c = corners.get_ref(end);
1060 if c.arc_outer() {
1061 curve.arc_line(c.start_outer(), c.center_outer(), c.end_outer());
1062 } else {
1063 curve.line(c.outer());
1064 curve.line(c.end_outer());
1065 }
1066 if c.arc_inner() {
1067 curve.arc_line(c.end_inner(), c.center_inner(), c.start_inner());
1068 } else {
1069 curve.line(c.center_inner());
1070 }
1071 } else {
1072 let c = corners.get_ref(end);
1073 if c.arc_outer() {
1074 curve.arc_line(c.start_outer(), c.center_outer(), c.mid_outer());
1075 } else {
1076 curve.line(c.outer());
1077 }
1078 if c.arc_inner() {
1079 curve.arc_line(c.mid_inner(), c.center_inner(), c.start_inner());
1080 } else {
1081 curve.line(c.center_inner());
1082 }
1083 }
1084
1085 let mut current = end.next_ccw();
1087 while current != start {
1088 let c = corners.get_ref(current);
1089 if c.arc_inner() {
1090 curve.arc_line(c.end_inner(), c.center_inner(), c.start_inner());
1091 } else {
1092 curve.line(c.center_inner());
1093 }
1094 current = current.next_ccw();
1095 }
1096
1097 curve.close();
1098
1099 Shape {
1100 geometry: Geometry::Curve(curve),
1101 stroke: None,
1102 fill: Some(stroke.paint.clone()),
1103 fill_rule: FillRule::default(),
1104 }
1105}
1106
1107struct ControlPoints {
1127 radius: Abs,
1128 stroke_after: Abs,
1129 stroke_before: Abs,
1130 corner: Corner,
1131 size: Size,
1132 same: bool,
1133}
1134
1135impl ControlPoints {
1136 fn rotate(&self, point: Point) -> Point {
1138 match self.corner {
1139 Corner::TopLeft => point,
1140 Corner::TopRight => Point { x: self.size.x - point.y, y: point.x },
1141 Corner::BottomRight => {
1142 Point { x: self.size.x - point.x, y: self.size.y - point.y }
1143 }
1144 Corner::BottomLeft => Point { x: point.y, y: self.size.y - point.x },
1145 }
1146 }
1147
1148 pub fn outer(&self) -> Point {
1150 self.rotate(Point { x: -self.stroke_before, y: -self.stroke_after })
1151 }
1152
1153 pub fn center_outer(&self) -> Point {
1155 let r = self.radius_outer();
1156 self.rotate(Point {
1157 x: r - self.stroke_before,
1158 y: r - self.stroke_after,
1159 })
1160 }
1161
1162 pub fn center(&self) -> Point {
1164 let r = self.radius();
1165 self.rotate(Point { x: r, y: r })
1166 }
1167
1168 pub fn center_inner(&self) -> Point {
1170 let r = self.radius_inner();
1171
1172 self.rotate(Point {
1173 x: self.stroke_before + r,
1174 y: self.stroke_after + r,
1175 })
1176 }
1177
1178 pub fn radius_outer(&self) -> Abs {
1180 self.radius
1181 }
1182
1183 pub fn radius(&self) -> Abs {
1185 (self.radius - self.stroke_before.min(self.stroke_after)).max(Abs::zero())
1186 }
1187
1188 pub fn radius_inner(&self) -> Abs {
1190 (self.radius - 2.0 * self.stroke_before.max(self.stroke_after)).max(Abs::zero())
1191 }
1192
1193 pub fn mid_outer(&self) -> Point {
1195 let c_i = self.center_inner();
1196 let c_o = self.center_outer();
1197 let o = self.outer();
1198 let r = self.radius_outer();
1199
1200 let a = (o.x - c_i.x).to_raw().powi(2) + (o.y - c_i.y).to_raw().powi(2);
1203 let b = 2.0 * (o.x - c_i.x).to_raw() * (c_i.x - c_o.x).to_raw()
1204 + 2.0 * (o.y - c_i.y).to_raw() * (c_i.y - c_o.y).to_raw();
1205 let c = (c_i.x - c_o.x).to_raw().powi(2) + (c_i.y - c_o.y).to_raw().powi(2)
1206 - r.to_raw().powi(2);
1207 let t = (-b + (b * b - 4.0 * a * c).max(0.0).sqrt()) / (2.0 * a);
1208 c_i + t * (o - c_i)
1209 }
1210
1211 pub fn mid(&self) -> Point {
1213 let center = self.center_outer();
1214 let outer = self.outer();
1215 let diff = outer - center;
1216 center + diff / diff.hypot().to_raw() * self.radius().to_raw()
1217 }
1218
1219 pub fn mid_inner(&self) -> Point {
1221 let center = self.center_inner();
1222 let outer = self.outer();
1223 let diff = outer - center;
1224 center + diff / diff.hypot().to_raw() * self.radius_inner().to_raw()
1225 }
1226
1227 pub fn arc_outer(&self) -> bool {
1229 self.radius_outer() > Abs::zero()
1230 }
1231
1232 pub fn arc(&self) -> bool {
1233 self.radius() > Abs::zero()
1234 }
1235
1236 pub fn arc_inner(&self) -> bool {
1238 self.radius_inner() > Abs::zero()
1239 }
1240
1241 pub fn start_outer(&self) -> Point {
1243 self.rotate(Point {
1244 x: -self.stroke_before,
1245 y: self.radius_outer() - self.stroke_after,
1246 })
1247 }
1248
1249 pub fn start(&self) -> Point {
1251 self.rotate(Point::with_y(self.radius()))
1252 }
1253
1254 pub fn start_inner(&self) -> Point {
1256 self.rotate(Point {
1257 x: self.stroke_before,
1258 y: self.stroke_after + self.radius_inner(),
1259 })
1260 }
1261
1262 pub fn end_outer(&self) -> Point {
1264 self.rotate(Point {
1265 x: self.radius_outer() - self.stroke_before,
1266 y: -self.stroke_after,
1267 })
1268 }
1269
1270 pub fn end(&self) -> Point {
1272 self.rotate(Point::with_x(self.radius()))
1273 }
1274
1275 pub fn end_inner(&self) -> Point {
1277 self.rotate(Point {
1278 x: self.stroke_before + self.radius_inner(),
1279 y: self.stroke_after,
1280 })
1281 }
1282}
1283
1284trait CurveExt {
1286 fn arc(&mut self, start: Point, center: Point, end: Point);
1287 fn arc_move(&mut self, start: Point, center: Point, end: Point);
1288 fn arc_line(&mut self, start: Point, center: Point, end: Point);
1289}
1290
1291impl CurveExt for Curve {
1292 fn arc(&mut self, start: Point, center: Point, end: Point) {
1293 let arc = bezier_arc_control(start, center, end);
1294 self.cubic(arc[0], arc[1], end);
1295 }
1296
1297 fn arc_move(&mut self, start: Point, center: Point, end: Point) {
1298 self.move_(start);
1299 self.arc(start, center, end);
1300 }
1301
1302 fn arc_line(&mut self, start: Point, center: Point, end: Point) {
1303 self.line(start);
1304 self.arc(start, center, end);
1305 }
1306}
1307
1308fn bezier_arc_control(start: Point, center: Point, end: Point) -> [Point; 2] {
1312 let a = start - center;
1314 let b = end - center;
1315
1316 let q1 = a.x.to_raw() * a.x.to_raw() + a.y.to_raw() * a.y.to_raw();
1317 let q2 = q1 + a.x.to_raw() * b.x.to_raw() + a.y.to_raw() * b.y.to_raw();
1318 let k2 = (4.0 / 3.0) * ((2.0 * q1 * q2).sqrt() - q2)
1319 / (a.x.to_raw() * b.y.to_raw() - a.y.to_raw() * b.x.to_raw());
1320
1321 let control_1 = Point::new(center.x + a.x - k2 * a.y, center.y + a.y + k2 * a.x);
1322 let control_2 = Point::new(center.x + b.x + k2 * b.y, center.y + b.y - k2 * b.x);
1323
1324 [control_1, control_2]
1325}