1use std::{any::Any, sync::Arc};
4
5use crate::{
6 stroke::PathStroke,
7 text::{FontId, Fonts, Galley},
8 Color32, Mesh, Stroke, TextureId,
9};
10use emath::{pos2, Align2, Pos2, Rangef, Rect, TSTransform, Vec2};
11
12pub use crate::{CubicBezierShape, QuadraticBezierShape};
13
14#[must_use = "Add a Shape to a Painter"]
22#[derive(Clone, Debug, PartialEq)]
23pub enum Shape {
24 Noop,
26
27 Vec(Vec<Shape>),
30
31 Circle(CircleShape),
33
34 Ellipse(EllipseShape),
36
37 LineSegment {
39 points: [Pos2; 2],
40 stroke: PathStroke,
41 },
42
43 Path(PathShape),
46
47 Rect(RectShape),
49
50 Text(TextShape),
54
55 Mesh(Mesh),
59
60 QuadraticBezier(QuadraticBezierShape),
62
63 CubicBezier(CubicBezierShape),
65
66 Callback(PaintCallback),
68}
69
70#[test]
71fn shape_impl_send_sync() {
72 fn assert_send_sync<T: Send + Sync>() {}
73 assert_send_sync::<Shape>();
74}
75
76impl From<Vec<Self>> for Shape {
77 #[inline(always)]
78 fn from(shapes: Vec<Self>) -> Self {
79 Self::Vec(shapes)
80 }
81}
82
83impl From<Mesh> for Shape {
84 #[inline(always)]
85 fn from(mesh: Mesh) -> Self {
86 Self::Mesh(mesh)
87 }
88}
89
90impl Shape {
92 #[inline]
95 pub fn line_segment(points: [Pos2; 2], stroke: impl Into<PathStroke>) -> Self {
96 Self::LineSegment {
97 points,
98 stroke: stroke.into(),
99 }
100 }
101
102 pub fn hline(x: impl Into<Rangef>, y: f32, stroke: impl Into<PathStroke>) -> Self {
104 let x = x.into();
105 Self::LineSegment {
106 points: [pos2(x.min, y), pos2(x.max, y)],
107 stroke: stroke.into(),
108 }
109 }
110
111 pub fn vline(x: f32, y: impl Into<Rangef>, stroke: impl Into<PathStroke>) -> Self {
113 let y = y.into();
114 Self::LineSegment {
115 points: [pos2(x, y.min), pos2(x, y.max)],
116 stroke: stroke.into(),
117 }
118 }
119
120 #[inline]
124 pub fn line(points: Vec<Pos2>, stroke: impl Into<PathStroke>) -> Self {
125 Self::Path(PathShape::line(points, stroke))
126 }
127
128 #[inline]
130 pub fn closed_line(points: Vec<Pos2>, stroke: impl Into<PathStroke>) -> Self {
131 Self::Path(PathShape::closed_line(points, stroke))
132 }
133
134 pub fn dotted_line(
136 path: &[Pos2],
137 color: impl Into<Color32>,
138 spacing: f32,
139 radius: f32,
140 ) -> Vec<Self> {
141 let mut shapes = Vec::new();
142 points_from_line(path, spacing, radius, color.into(), &mut shapes);
143 shapes
144 }
145
146 pub fn dashed_line(
148 path: &[Pos2],
149 stroke: impl Into<Stroke>,
150 dash_length: f32,
151 gap_length: f32,
152 ) -> Vec<Self> {
153 let mut shapes = Vec::new();
154 dashes_from_line(
155 path,
156 stroke.into(),
157 &[dash_length],
158 &[gap_length],
159 &mut shapes,
160 0.,
161 );
162 shapes
163 }
164
165 pub fn dashed_line_with_offset(
167 path: &[Pos2],
168 stroke: impl Into<Stroke>,
169 dash_lengths: &[f32],
170 gap_lengths: &[f32],
171 dash_offset: f32,
172 ) -> Vec<Self> {
173 let mut shapes = Vec::new();
174 dashes_from_line(
175 path,
176 stroke.into(),
177 dash_lengths,
178 gap_lengths,
179 &mut shapes,
180 dash_offset,
181 );
182 shapes
183 }
184
185 pub fn dashed_line_many(
188 points: &[Pos2],
189 stroke: impl Into<Stroke>,
190 dash_length: f32,
191 gap_length: f32,
192 shapes: &mut Vec<Self>,
193 ) {
194 dashes_from_line(
195 points,
196 stroke.into(),
197 &[dash_length],
198 &[gap_length],
199 shapes,
200 0.,
201 );
202 }
203
204 pub fn dashed_line_many_with_offset(
207 points: &[Pos2],
208 stroke: impl Into<Stroke>,
209 dash_lengths: &[f32],
210 gap_lengths: &[f32],
211 dash_offset: f32,
212 shapes: &mut Vec<Self>,
213 ) {
214 dashes_from_line(
215 points,
216 stroke.into(),
217 dash_lengths,
218 gap_lengths,
219 shapes,
220 dash_offset,
221 );
222 }
223
224 #[inline]
228 pub fn convex_polygon(
229 points: Vec<Pos2>,
230 fill: impl Into<Color32>,
231 stroke: impl Into<PathStroke>,
232 ) -> Self {
233 Self::Path(PathShape::convex_polygon(points, fill, stroke))
234 }
235
236 #[inline]
237 pub fn circle_filled(center: Pos2, radius: f32, fill_color: impl Into<Color32>) -> Self {
238 Self::Circle(CircleShape::filled(center, radius, fill_color))
239 }
240
241 #[inline]
242 pub fn circle_stroke(center: Pos2, radius: f32, stroke: impl Into<Stroke>) -> Self {
243 Self::Circle(CircleShape::stroke(center, radius, stroke))
244 }
245
246 #[inline]
247 pub fn ellipse_filled(center: Pos2, radius: Vec2, fill_color: impl Into<Color32>) -> Self {
248 Self::Ellipse(EllipseShape::filled(center, radius, fill_color))
249 }
250
251 #[inline]
252 pub fn ellipse_stroke(center: Pos2, radius: Vec2, stroke: impl Into<Stroke>) -> Self {
253 Self::Ellipse(EllipseShape::stroke(center, radius, stroke))
254 }
255
256 #[inline]
257 pub fn rect_filled(
258 rect: Rect,
259 rounding: impl Into<Rounding>,
260 fill_color: impl Into<Color32>,
261 ) -> Self {
262 Self::Rect(RectShape::filled(rect, rounding, fill_color))
263 }
264
265 #[inline]
266 pub fn rect_stroke(
267 rect: Rect,
268 rounding: impl Into<Rounding>,
269 stroke: impl Into<Stroke>,
270 ) -> Self {
271 Self::Rect(RectShape::stroke(rect, rounding, stroke))
272 }
273
274 #[allow(clippy::needless_pass_by_value)]
275 pub fn text(
276 fonts: &Fonts,
277 pos: Pos2,
278 anchor: Align2,
279 text: impl ToString,
280 font_id: FontId,
281 color: Color32,
282 ) -> Self {
283 let galley = fonts.layout_no_wrap(text.to_string(), font_id, color);
284 let rect = anchor.anchor_size(pos, galley.size());
285 Self::galley(rect.min, galley, color)
286 }
287
288 #[inline]
292 pub fn galley(pos: Pos2, galley: Arc<Galley>, fallback_color: Color32) -> Self {
293 TextShape::new(pos, galley, fallback_color).into()
294 }
295
296 #[inline]
298 pub fn galley_with_override_text_color(
299 pos: Pos2,
300 galley: Arc<Galley>,
301 text_color: Color32,
302 ) -> Self {
303 TextShape::new(pos, galley, text_color)
304 .with_override_text_color(text_color)
305 .into()
306 }
307
308 #[inline]
309 #[deprecated = "Use `Shape::galley` or `Shape::galley_with_override_text_color` instead"]
310 pub fn galley_with_color(pos: Pos2, galley: Arc<Galley>, text_color: Color32) -> Self {
311 Self::galley_with_override_text_color(pos, galley, text_color)
312 }
313
314 #[inline]
315 pub fn mesh(mesh: Mesh) -> Self {
316 debug_assert!(mesh.is_valid());
317 Self::Mesh(mesh)
318 }
319
320 pub fn image(texture_id: TextureId, rect: Rect, uv: Rect, tint: Color32) -> Self {
327 let mut mesh = Mesh::with_texture(texture_id);
328 mesh.add_rect_with_uv(rect, uv, tint);
329 Self::mesh(mesh)
330 }
331
332 pub fn visual_bounding_rect(&self) -> Rect {
334 match self {
335 Self::Noop => Rect::NOTHING,
336 Self::Vec(shapes) => {
337 let mut rect = Rect::NOTHING;
338 for shape in shapes {
339 rect = rect.union(shape.visual_bounding_rect());
340 }
341 rect
342 }
343 Self::Circle(circle_shape) => circle_shape.visual_bounding_rect(),
344 Self::Ellipse(ellipse_shape) => ellipse_shape.visual_bounding_rect(),
345 Self::LineSegment { points, stroke } => {
346 if stroke.is_empty() {
347 Rect::NOTHING
348 } else {
349 Rect::from_two_pos(points[0], points[1]).expand(stroke.width / 2.0)
350 }
351 }
352 Self::Path(path_shape) => path_shape.visual_bounding_rect(),
353 Self::Rect(rect_shape) => rect_shape.visual_bounding_rect(),
354 Self::Text(text_shape) => text_shape.visual_bounding_rect(),
355 Self::Mesh(mesh) => mesh.calc_bounds(),
356 Self::QuadraticBezier(bezier) => bezier.visual_bounding_rect(),
357 Self::CubicBezier(bezier) => bezier.visual_bounding_rect(),
358 Self::Callback(custom) => custom.rect,
359 }
360 }
361}
362
363impl Shape {
365 #[inline(always)]
366 pub fn texture_id(&self) -> super::TextureId {
367 if let Self::Mesh(mesh) = self {
368 mesh.texture_id
369 } else if let Self::Rect(rect_shape) = self {
370 rect_shape.fill_texture_id
371 } else {
372 super::TextureId::default()
373 }
374 }
375
376 #[inline(always)]
380 pub fn scale(&mut self, factor: f32) {
381 self.transform(TSTransform::from_scaling(factor));
382 }
383
384 #[inline(always)]
388 pub fn translate(&mut self, delta: Vec2) {
389 self.transform(TSTransform::from_translation(delta));
390 }
391
392 pub fn transform(&mut self, transform: TSTransform) {
397 match self {
398 Self::Noop => {}
399 Self::Vec(shapes) => {
400 for shape in shapes {
401 shape.transform(transform);
402 }
403 }
404 Self::Circle(circle_shape) => {
405 circle_shape.center = transform * circle_shape.center;
406 circle_shape.radius *= transform.scaling;
407 circle_shape.stroke.width *= transform.scaling;
408 }
409 Self::Ellipse(ellipse_shape) => {
410 ellipse_shape.center = transform * ellipse_shape.center;
411 ellipse_shape.radius *= transform.scaling;
412 ellipse_shape.stroke.width *= transform.scaling;
413 }
414 Self::LineSegment { points, stroke } => {
415 for p in points {
416 *p = transform * *p;
417 }
418 stroke.width *= transform.scaling;
419 }
420 Self::Path(path_shape) => {
421 for p in &mut path_shape.points {
422 *p = transform * *p;
423 }
424 path_shape.stroke.width *= transform.scaling;
425 }
426 Self::Rect(rect_shape) => {
427 rect_shape.rect = transform * rect_shape.rect;
428 rect_shape.stroke.width *= transform.scaling;
429 rect_shape.rounding *= transform.scaling;
430 }
431 Self::Text(text_shape) => {
432 text_shape.pos = transform * text_shape.pos;
433
434 let galley = Arc::make_mut(&mut text_shape.galley);
436 for row in &mut galley.rows {
437 row.visuals.mesh_bounds = transform.scaling * row.visuals.mesh_bounds;
438 for v in &mut row.visuals.mesh.vertices {
439 v.pos = Pos2::new(transform.scaling * v.pos.x, transform.scaling * v.pos.y);
440 }
441 }
442
443 galley.mesh_bounds = transform.scaling * galley.mesh_bounds;
444 galley.rect = transform.scaling * galley.rect;
445 }
446 Self::Mesh(mesh) => {
447 mesh.transform(transform);
448 }
449 Self::QuadraticBezier(bezier_shape) => {
450 bezier_shape.points[0] = transform * bezier_shape.points[0];
451 bezier_shape.points[1] = transform * bezier_shape.points[1];
452 bezier_shape.points[2] = transform * bezier_shape.points[2];
453 bezier_shape.stroke.width *= transform.scaling;
454 }
455 Self::CubicBezier(cubic_curve) => {
456 for p in &mut cubic_curve.points {
457 *p = transform * *p;
458 }
459 cubic_curve.stroke.width *= transform.scaling;
460 }
461 Self::Callback(shape) => {
462 shape.rect = transform * shape.rect;
463 }
464 }
465 }
466}
467
468#[derive(Copy, Clone, Debug, PartialEq)]
472#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
473pub struct CircleShape {
474 pub center: Pos2,
475 pub radius: f32,
476 pub fill: Color32,
477 pub stroke: Stroke,
478}
479
480impl CircleShape {
481 #[inline]
482 pub fn filled(center: Pos2, radius: f32, fill_color: impl Into<Color32>) -> Self {
483 Self {
484 center,
485 radius,
486 fill: fill_color.into(),
487 stroke: Default::default(),
488 }
489 }
490
491 #[inline]
492 pub fn stroke(center: Pos2, radius: f32, stroke: impl Into<Stroke>) -> Self {
493 Self {
494 center,
495 radius,
496 fill: Default::default(),
497 stroke: stroke.into(),
498 }
499 }
500
501 pub fn visual_bounding_rect(&self) -> Rect {
503 if self.fill == Color32::TRANSPARENT && self.stroke.is_empty() {
504 Rect::NOTHING
505 } else {
506 Rect::from_center_size(
507 self.center,
508 Vec2::splat(self.radius * 2.0 + self.stroke.width),
509 )
510 }
511 }
512}
513
514impl From<CircleShape> for Shape {
515 #[inline(always)]
516 fn from(shape: CircleShape) -> Self {
517 Self::Circle(shape)
518 }
519}
520
521#[derive(Copy, Clone, Debug, PartialEq)]
525#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
526pub struct EllipseShape {
527 pub center: Pos2,
528
529 pub radius: Vec2,
531 pub fill: Color32,
532 pub stroke: Stroke,
533}
534
535impl EllipseShape {
536 #[inline]
537 pub fn filled(center: Pos2, radius: Vec2, fill_color: impl Into<Color32>) -> Self {
538 Self {
539 center,
540 radius,
541 fill: fill_color.into(),
542 stroke: Default::default(),
543 }
544 }
545
546 #[inline]
547 pub fn stroke(center: Pos2, radius: Vec2, stroke: impl Into<Stroke>) -> Self {
548 Self {
549 center,
550 radius,
551 fill: Default::default(),
552 stroke: stroke.into(),
553 }
554 }
555
556 pub fn visual_bounding_rect(&self) -> Rect {
558 if self.fill == Color32::TRANSPARENT && self.stroke.is_empty() {
559 Rect::NOTHING
560 } else {
561 Rect::from_center_size(
562 self.center,
563 self.radius * 2.0 + Vec2::splat(self.stroke.width),
564 )
565 }
566 }
567}
568
569impl From<EllipseShape> for Shape {
570 #[inline(always)]
571 fn from(shape: EllipseShape) -> Self {
572 Self::Ellipse(shape)
573 }
574}
575
576#[derive(Clone, Debug, PartialEq)]
580#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
581pub struct PathShape {
582 pub points: Vec<Pos2>,
584
585 pub closed: bool,
588
589 pub fill: Color32,
591
592 pub stroke: PathStroke,
594 }
597
598impl PathShape {
599 #[inline]
603 pub fn line(points: Vec<Pos2>, stroke: impl Into<PathStroke>) -> Self {
604 Self {
605 points,
606 closed: false,
607 fill: Default::default(),
608 stroke: stroke.into(),
609 }
610 }
611
612 #[inline]
614 pub fn closed_line(points: Vec<Pos2>, stroke: impl Into<PathStroke>) -> Self {
615 Self {
616 points,
617 closed: true,
618 fill: Default::default(),
619 stroke: stroke.into(),
620 }
621 }
622
623 #[inline]
627 pub fn convex_polygon(
628 points: Vec<Pos2>,
629 fill: impl Into<Color32>,
630 stroke: impl Into<PathStroke>,
631 ) -> Self {
632 Self {
633 points,
634 closed: true,
635 fill: fill.into(),
636 stroke: stroke.into(),
637 }
638 }
639
640 #[inline]
642 pub fn visual_bounding_rect(&self) -> Rect {
643 if self.fill == Color32::TRANSPARENT && self.stroke.is_empty() {
644 Rect::NOTHING
645 } else {
646 Rect::from_points(&self.points).expand(self.stroke.width / 2.0)
647 }
648 }
649}
650
651impl From<PathShape> for Shape {
652 #[inline(always)]
653 fn from(shape: PathShape) -> Self {
654 Self::Path(shape)
655 }
656}
657
658#[derive(Copy, Clone, Debug, PartialEq)]
662#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
663pub struct RectShape {
664 pub rect: Rect,
665
666 pub rounding: Rounding,
668
669 pub fill: Color32,
671
672 pub stroke: Stroke,
674
675 pub blur_width: f32,
682
683 pub fill_texture_id: TextureId,
687
688 pub uv: Rect,
695}
696
697impl RectShape {
698 #[inline]
699 pub fn new(
700 rect: Rect,
701 rounding: impl Into<Rounding>,
702 fill_color: impl Into<Color32>,
703 stroke: impl Into<Stroke>,
704 ) -> Self {
705 Self {
706 rect,
707 rounding: rounding.into(),
708 fill: fill_color.into(),
709 stroke: stroke.into(),
710 blur_width: 0.0,
711 fill_texture_id: Default::default(),
712 uv: Rect::ZERO,
713 }
714 }
715
716 #[inline]
717 pub fn filled(
718 rect: Rect,
719 rounding: impl Into<Rounding>,
720 fill_color: impl Into<Color32>,
721 ) -> Self {
722 Self {
723 rect,
724 rounding: rounding.into(),
725 fill: fill_color.into(),
726 stroke: Default::default(),
727 blur_width: 0.0,
728 fill_texture_id: Default::default(),
729 uv: Rect::ZERO,
730 }
731 }
732
733 #[inline]
734 pub fn stroke(rect: Rect, rounding: impl Into<Rounding>, stroke: impl Into<Stroke>) -> Self {
735 Self {
736 rect,
737 rounding: rounding.into(),
738 fill: Default::default(),
739 stroke: stroke.into(),
740 blur_width: 0.0,
741 fill_texture_id: Default::default(),
742 uv: Rect::ZERO,
743 }
744 }
745
746 #[inline]
753 pub fn with_blur_width(mut self, blur_width: f32) -> Self {
754 self.blur_width = blur_width;
755 self
756 }
757
758 #[inline]
760 pub fn visual_bounding_rect(&self) -> Rect {
761 if self.fill == Color32::TRANSPARENT && self.stroke.is_empty() {
762 Rect::NOTHING
763 } else {
764 self.rect
765 .expand((self.stroke.width + self.blur_width) / 2.0)
766 }
767 }
768}
769
770impl From<RectShape> for Shape {
771 #[inline(always)]
772 fn from(shape: RectShape) -> Self {
773 Self::Rect(shape)
774 }
775}
776
777#[derive(Copy, Clone, Debug, PartialEq)]
778#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
779pub struct Rounding {
781 pub nw: f32,
783
784 pub ne: f32,
786
787 pub sw: f32,
789
790 pub se: f32,
792}
793
794impl Default for Rounding {
795 #[inline]
796 fn default() -> Self {
797 Self::ZERO
798 }
799}
800
801impl From<f32> for Rounding {
802 #[inline]
803 fn from(radius: f32) -> Self {
804 Self {
805 nw: radius,
806 ne: radius,
807 sw: radius,
808 se: radius,
809 }
810 }
811}
812
813impl Rounding {
814 pub const ZERO: Self = Self {
816 nw: 0.0,
817 ne: 0.0,
818 sw: 0.0,
819 se: 0.0,
820 };
821
822 #[inline]
823 pub const fn same(radius: f32) -> Self {
824 Self {
825 nw: radius,
826 ne: radius,
827 sw: radius,
828 se: radius,
829 }
830 }
831
832 #[inline]
834 pub fn is_same(&self) -> bool {
835 self.nw == self.ne && self.nw == self.sw && self.nw == self.se
836 }
837
838 #[inline]
840 pub fn at_least(&self, min: f32) -> Self {
841 Self {
842 nw: self.nw.max(min),
843 ne: self.ne.max(min),
844 sw: self.sw.max(min),
845 se: self.se.max(min),
846 }
847 }
848
849 #[inline]
851 pub fn at_most(&self, max: f32) -> Self {
852 Self {
853 nw: self.nw.min(max),
854 ne: self.ne.min(max),
855 sw: self.sw.min(max),
856 se: self.se.min(max),
857 }
858 }
859}
860
861impl std::ops::Add for Rounding {
862 type Output = Self;
863 #[inline]
864 fn add(self, rhs: Self) -> Self {
865 Self {
866 nw: self.nw + rhs.nw,
867 ne: self.ne + rhs.ne,
868 sw: self.sw + rhs.sw,
869 se: self.se + rhs.se,
870 }
871 }
872}
873
874impl std::ops::AddAssign for Rounding {
875 #[inline]
876 fn add_assign(&mut self, rhs: Self) {
877 *self = Self {
878 nw: self.nw + rhs.nw,
879 ne: self.ne + rhs.ne,
880 sw: self.sw + rhs.sw,
881 se: self.se + rhs.se,
882 };
883 }
884}
885
886impl std::ops::AddAssign<f32> for Rounding {
887 #[inline]
888 fn add_assign(&mut self, rhs: f32) {
889 *self = Self {
890 nw: self.nw + rhs,
891 ne: self.ne + rhs,
892 sw: self.sw + rhs,
893 se: self.se + rhs,
894 };
895 }
896}
897
898impl std::ops::Sub for Rounding {
899 type Output = Self;
900 #[inline]
901 fn sub(self, rhs: Self) -> Self {
902 Self {
903 nw: self.nw - rhs.nw,
904 ne: self.ne - rhs.ne,
905 sw: self.sw - rhs.sw,
906 se: self.se - rhs.se,
907 }
908 }
909}
910
911impl std::ops::SubAssign for Rounding {
912 #[inline]
913 fn sub_assign(&mut self, rhs: Self) {
914 *self = Self {
915 nw: self.nw - rhs.nw,
916 ne: self.ne - rhs.ne,
917 sw: self.sw - rhs.sw,
918 se: self.se - rhs.se,
919 };
920 }
921}
922
923impl std::ops::SubAssign<f32> for Rounding {
924 #[inline]
925 fn sub_assign(&mut self, rhs: f32) {
926 *self = Self {
927 nw: self.nw - rhs,
928 ne: self.ne - rhs,
929 sw: self.sw - rhs,
930 se: self.se - rhs,
931 };
932 }
933}
934
935impl std::ops::Div<f32> for Rounding {
936 type Output = Self;
937 #[inline]
938 fn div(self, rhs: f32) -> Self {
939 Self {
940 nw: self.nw / rhs,
941 ne: self.ne / rhs,
942 sw: self.sw / rhs,
943 se: self.se / rhs,
944 }
945 }
946}
947
948impl std::ops::DivAssign<f32> for Rounding {
949 #[inline]
950 fn div_assign(&mut self, rhs: f32) {
951 *self = Self {
952 nw: self.nw / rhs,
953 ne: self.ne / rhs,
954 sw: self.sw / rhs,
955 se: self.se / rhs,
956 };
957 }
958}
959
960impl std::ops::Mul<f32> for Rounding {
961 type Output = Self;
962 #[inline]
963 fn mul(self, rhs: f32) -> Self {
964 Self {
965 nw: self.nw * rhs,
966 ne: self.ne * rhs,
967 sw: self.sw * rhs,
968 se: self.se * rhs,
969 }
970 }
971}
972
973impl std::ops::MulAssign<f32> for Rounding {
974 #[inline]
975 fn mul_assign(&mut self, rhs: f32) {
976 *self = Self {
977 nw: self.nw * rhs,
978 ne: self.ne * rhs,
979 sw: self.sw * rhs,
980 se: self.se * rhs,
981 };
982 }
983}
984
985#[derive(Clone, Debug, PartialEq)]
991#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
992pub struct TextShape {
993 pub pos: Pos2,
995
996 pub galley: Arc<Galley>,
998
999 pub underline: Stroke,
1002
1003 pub fallback_color: Color32,
1006
1007 pub override_text_color: Option<Color32>,
1012
1013 pub opacity_factor: f32,
1016
1017 pub angle: f32,
1020}
1021
1022impl TextShape {
1023 #[inline]
1027 pub fn new(pos: Pos2, galley: Arc<Galley>, fallback_color: Color32) -> Self {
1028 Self {
1029 pos,
1030 galley,
1031 underline: Stroke::NONE,
1032 fallback_color,
1033 override_text_color: None,
1034 opacity_factor: 1.0,
1035 angle: 0.0,
1036 }
1037 }
1038
1039 #[inline]
1041 pub fn visual_bounding_rect(&self) -> Rect {
1042 self.galley.mesh_bounds.translate(self.pos.to_vec2())
1043 }
1044
1045 #[inline]
1046 pub fn with_underline(mut self, underline: Stroke) -> Self {
1047 self.underline = underline;
1048 self
1049 }
1050
1051 #[inline]
1053 pub fn with_override_text_color(mut self, override_text_color: Color32) -> Self {
1054 self.override_text_color = Some(override_text_color);
1055 self
1056 }
1057
1058 #[inline]
1061 pub fn with_angle(mut self, angle: f32) -> Self {
1062 self.angle = angle;
1063 self
1064 }
1065
1066 #[inline]
1068 pub fn with_opacity_factor(mut self, opacity_factor: f32) -> Self {
1069 self.opacity_factor = opacity_factor;
1070 self
1071 }
1072}
1073
1074impl From<TextShape> for Shape {
1075 #[inline(always)]
1076 fn from(shape: TextShape) -> Self {
1077 Self::Text(shape)
1078 }
1079}
1080
1081fn points_from_line(
1085 path: &[Pos2],
1086 spacing: f32,
1087 radius: f32,
1088 color: Color32,
1089 shapes: &mut Vec<Shape>,
1090) {
1091 let mut position_on_segment = 0.0;
1092 path.windows(2).for_each(|window| {
1093 let (start, end) = (window[0], window[1]);
1094 let vector = end - start;
1095 let segment_length = vector.length();
1096 while position_on_segment < segment_length {
1097 let new_point = start + vector * (position_on_segment / segment_length);
1098 shapes.push(Shape::circle_filled(new_point, radius, color));
1099 position_on_segment += spacing;
1100 }
1101 position_on_segment -= segment_length;
1102 });
1103}
1104
1105fn dashes_from_line(
1107 path: &[Pos2],
1108 stroke: Stroke,
1109 dash_lengths: &[f32],
1110 gap_lengths: &[f32],
1111 shapes: &mut Vec<Shape>,
1112 dash_offset: f32,
1113) {
1114 assert_eq!(dash_lengths.len(), gap_lengths.len());
1115 let mut position_on_segment = dash_offset;
1116 let mut drawing_dash = false;
1117 let mut step = 0;
1118 let steps = dash_lengths.len();
1119 path.windows(2).for_each(|window| {
1120 let (start, end) = (window[0], window[1]);
1121 let vector = end - start;
1122 let segment_length = vector.length();
1123
1124 let mut start_point = start;
1125 while position_on_segment < segment_length {
1126 let new_point = start + vector * (position_on_segment / segment_length);
1127 if drawing_dash {
1128 shapes.push(Shape::line_segment([start_point, new_point], stroke));
1130 position_on_segment += gap_lengths[step];
1131 step += 1;
1133 if step >= steps {
1134 step = 0;
1135 }
1136 } else {
1137 start_point = new_point;
1139 position_on_segment += dash_lengths[step];
1140 }
1141 drawing_dash = !drawing_dash;
1142 }
1143
1144 if drawing_dash {
1146 shapes.push(Shape::line_segment([start_point, end], stroke));
1147 }
1148
1149 position_on_segment -= segment_length;
1150 });
1151}
1152
1153pub struct PaintCallbackInfo {
1157 pub viewport: Rect,
1166
1167 pub clip_rect: Rect,
1169
1170 pub pixels_per_point: f32,
1172
1173 pub screen_size_px: [u32; 2],
1175}
1176
1177pub struct ViewportInPixels {
1179 pub left_px: i32,
1181
1182 pub top_px: i32,
1184
1185 pub from_bottom_px: i32,
1189
1190 pub width_px: i32,
1192
1193 pub height_px: i32,
1195}
1196
1197impl ViewportInPixels {
1198 fn from_points(rect: &Rect, pixels_per_point: f32, screen_size_px: [u32; 2]) -> Self {
1199 let left_px = (pixels_per_point * rect.min.x).round() as i32; let top_px = (pixels_per_point * rect.min.y).round() as i32; let right_px = (pixels_per_point * rect.max.x).round() as i32; let bottom_px = (pixels_per_point * rect.max.y).round() as i32; let screen_width = screen_size_px[0] as i32;
1210 let screen_height = screen_size_px[1] as i32;
1211 let left_px = left_px.clamp(0, screen_width);
1212 let right_px = right_px.clamp(left_px, screen_width);
1213 let top_px = top_px.clamp(0, screen_height);
1214 let bottom_px = bottom_px.clamp(top_px, screen_height);
1215
1216 let width_px = right_px - left_px;
1217 let height_px = bottom_px - top_px;
1218
1219 Self {
1220 left_px,
1221 top_px,
1222 from_bottom_px: screen_height - height_px - top_px,
1223 width_px,
1224 height_px,
1225 }
1226 }
1227}
1228
1229#[test]
1230fn test_viewport_rounding() {
1231 for i in 0..=10_000 {
1232 let x = i as f32 / 97.0;
1234 let left = Rect::from_min_max(pos2(0.0, 0.0), pos2(100.0, 100.0)).with_max_x(x);
1235 let right = Rect::from_min_max(pos2(0.0, 0.0), pos2(100.0, 100.0)).with_min_x(x);
1236
1237 for pixels_per_point in [0.618, 1.0, std::f32::consts::PI] {
1238 let left = ViewportInPixels::from_points(&left, pixels_per_point, [100, 100]);
1239 let right = ViewportInPixels::from_points(&right, pixels_per_point, [100, 100]);
1240 assert_eq!(left.left_px + left.width_px, right.left_px);
1241 }
1242 }
1243}
1244
1245impl PaintCallbackInfo {
1246 pub fn viewport_in_pixels(&self) -> ViewportInPixels {
1248 ViewportInPixels::from_points(&self.viewport, self.pixels_per_point, self.screen_size_px)
1249 }
1250
1251 pub fn clip_rect_in_pixels(&self) -> ViewportInPixels {
1253 ViewportInPixels::from_points(&self.clip_rect, self.pixels_per_point, self.screen_size_px)
1254 }
1255}
1256
1257#[derive(Clone)]
1261pub struct PaintCallback {
1262 pub rect: Rect,
1266
1267 pub callback: Arc<dyn Any + Send + Sync>,
1284}
1285
1286impl std::fmt::Debug for PaintCallback {
1287 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1288 f.debug_struct("CustomShape")
1289 .field("rect", &self.rect)
1290 .finish_non_exhaustive()
1291 }
1292}
1293
1294impl std::cmp::PartialEq for PaintCallback {
1295 fn eq(&self, other: &Self) -> bool {
1296 self.rect.eq(&other.rect) && Arc::ptr_eq(&self.callback, &other.callback)
1297 }
1298}
1299
1300impl From<PaintCallback> for Shape {
1301 #[inline(always)]
1302 fn from(shape: PaintCallback) -> Self {
1303 Self::Callback(shape)
1304 }
1305}