1use std::sync::Arc;
4
5use emath::{pos2, Align2, Pos2, Rangef, Rect, TSTransform, Vec2};
6
7use crate::{
8 stroke::PathStroke,
9 text::{FontId, Fonts, Galley},
10 Color32, CornerRadius, Mesh, Stroke, StrokeKind, TextureId,
11};
12
13use super::{
14 CircleShape, CubicBezierShape, EllipseShape, PaintCallback, PathShape, QuadraticBezierShape,
15 RectShape, TextShape,
16};
17
18#[must_use = "Add a Shape to a Painter"]
26#[derive(Clone, Debug, PartialEq)]
27pub enum Shape {
28 Noop,
30
31 Vec(Vec<Shape>),
34
35 Circle(CircleShape),
37
38 Ellipse(EllipseShape),
40
41 LineSegment { points: [Pos2; 2], stroke: Stroke },
43
44 Path(PathShape),
47
48 Rect(RectShape),
50
51 Text(TextShape),
55
56 Mesh(Arc<Mesh>),
62
63 QuadraticBezier(QuadraticBezierShape),
65
66 CubicBezier(CubicBezierShape),
68
69 Callback(PaintCallback),
71}
72
73#[test]
74fn shape_size() {
75 assert_eq!(
76 std::mem::size_of::<Shape>(), 64,
77 "Shape changed size! If it shrank - good! Update this test. If it grew - bad! Try to find a way to avoid it."
78 );
79 assert!(
80 std::mem::size_of::<Shape>() <= 64,
81 "Shape is getting way too big!"
82 );
83}
84
85#[test]
86fn shape_impl_send_sync() {
87 fn assert_send_sync<T: Send + Sync>() {}
88 assert_send_sync::<Shape>();
89}
90
91impl From<Vec<Self>> for Shape {
92 #[inline(always)]
93 fn from(shapes: Vec<Self>) -> Self {
94 Self::Vec(shapes)
95 }
96}
97
98impl From<Mesh> for Shape {
99 #[inline(always)]
100 fn from(mesh: Mesh) -> Self {
101 Self::Mesh(mesh.into())
102 }
103}
104
105impl From<Arc<Mesh>> for Shape {
106 #[inline(always)]
107 fn from(mesh: Arc<Mesh>) -> Self {
108 Self::Mesh(mesh)
109 }
110}
111
112impl Shape {
114 #[inline]
117 pub fn line_segment(points: [Pos2; 2], stroke: impl Into<Stroke>) -> Self {
118 Self::LineSegment {
119 points,
120 stroke: stroke.into(),
121 }
122 }
123
124 pub fn hline(x: impl Into<Rangef>, y: f32, stroke: impl Into<Stroke>) -> Self {
126 let x = x.into();
127 Self::LineSegment {
128 points: [pos2(x.min, y), pos2(x.max, y)],
129 stroke: stroke.into(),
130 }
131 }
132
133 pub fn vline(x: f32, y: impl Into<Rangef>, stroke: impl Into<Stroke>) -> Self {
135 let y = y.into();
136 Self::LineSegment {
137 points: [pos2(x, y.min), pos2(x, y.max)],
138 stroke: stroke.into(),
139 }
140 }
141
142 #[inline]
146 pub fn line(points: Vec<Pos2>, stroke: impl Into<PathStroke>) -> Self {
147 Self::Path(PathShape::line(points, stroke))
148 }
149
150 #[inline]
152 pub fn closed_line(points: Vec<Pos2>, stroke: impl Into<PathStroke>) -> Self {
153 Self::Path(PathShape::closed_line(points, stroke))
154 }
155
156 pub fn dotted_line(
158 path: &[Pos2],
159 color: impl Into<Color32>,
160 spacing: f32,
161 radius: f32,
162 ) -> Vec<Self> {
163 let mut shapes = Vec::new();
164 points_from_line(path, spacing, radius, color.into(), &mut shapes);
165 shapes
166 }
167
168 pub fn dashed_line(
170 path: &[Pos2],
171 stroke: impl Into<Stroke>,
172 dash_length: f32,
173 gap_length: f32,
174 ) -> Vec<Self> {
175 let mut shapes = Vec::new();
176 dashes_from_line(
177 path,
178 stroke.into(),
179 &[dash_length],
180 &[gap_length],
181 &mut shapes,
182 0.,
183 );
184 shapes
185 }
186
187 pub fn dashed_line_with_offset(
189 path: &[Pos2],
190 stroke: impl Into<Stroke>,
191 dash_lengths: &[f32],
192 gap_lengths: &[f32],
193 dash_offset: f32,
194 ) -> Vec<Self> {
195 let mut shapes = Vec::new();
196 dashes_from_line(
197 path,
198 stroke.into(),
199 dash_lengths,
200 gap_lengths,
201 &mut shapes,
202 dash_offset,
203 );
204 shapes
205 }
206
207 pub fn dashed_line_many(
210 points: &[Pos2],
211 stroke: impl Into<Stroke>,
212 dash_length: f32,
213 gap_length: f32,
214 shapes: &mut Vec<Self>,
215 ) {
216 dashes_from_line(
217 points,
218 stroke.into(),
219 &[dash_length],
220 &[gap_length],
221 shapes,
222 0.,
223 );
224 }
225
226 pub fn dashed_line_many_with_offset(
229 points: &[Pos2],
230 stroke: impl Into<Stroke>,
231 dash_lengths: &[f32],
232 gap_lengths: &[f32],
233 dash_offset: f32,
234 shapes: &mut Vec<Self>,
235 ) {
236 dashes_from_line(
237 points,
238 stroke.into(),
239 dash_lengths,
240 gap_lengths,
241 shapes,
242 dash_offset,
243 );
244 }
245
246 #[inline]
250 pub fn convex_polygon(
251 points: Vec<Pos2>,
252 fill: impl Into<Color32>,
253 stroke: impl Into<PathStroke>,
254 ) -> Self {
255 Self::Path(PathShape::convex_polygon(points, fill, stroke))
256 }
257
258 #[inline]
259 pub fn circle_filled(center: Pos2, radius: f32, fill_color: impl Into<Color32>) -> Self {
260 Self::Circle(CircleShape::filled(center, radius, fill_color))
261 }
262
263 #[inline]
264 pub fn circle_stroke(center: Pos2, radius: f32, stroke: impl Into<Stroke>) -> Self {
265 Self::Circle(CircleShape::stroke(center, radius, stroke))
266 }
267
268 #[inline]
269 pub fn ellipse_filled(center: Pos2, radius: Vec2, fill_color: impl Into<Color32>) -> Self {
270 Self::Ellipse(EllipseShape::filled(center, radius, fill_color))
271 }
272
273 #[inline]
274 pub fn ellipse_stroke(center: Pos2, radius: Vec2, stroke: impl Into<Stroke>) -> Self {
275 Self::Ellipse(EllipseShape::stroke(center, radius, stroke))
276 }
277
278 #[inline]
280 pub fn rect_filled(
281 rect: Rect,
282 corner_radius: impl Into<CornerRadius>,
283 fill_color: impl Into<Color32>,
284 ) -> Self {
285 Self::Rect(RectShape::filled(rect, corner_radius, fill_color))
286 }
287
288 #[inline]
290 pub fn rect_stroke(
291 rect: Rect,
292 corner_radius: impl Into<CornerRadius>,
293 stroke: impl Into<Stroke>,
294 stroke_kind: StrokeKind,
295 ) -> Self {
296 Self::Rect(RectShape::stroke(rect, corner_radius, stroke, stroke_kind))
297 }
298
299 #[allow(clippy::needless_pass_by_value)]
300 pub fn text(
301 fonts: &Fonts,
302 pos: Pos2,
303 anchor: Align2,
304 text: impl ToString,
305 font_id: FontId,
306 color: Color32,
307 ) -> Self {
308 let galley = fonts.layout_no_wrap(text.to_string(), font_id, color);
309 let rect = anchor.anchor_size(pos, galley.size());
310 Self::galley(rect.min, galley, color)
311 }
312
313 #[inline]
317 pub fn galley(pos: Pos2, galley: Arc<Galley>, fallback_color: Color32) -> Self {
318 TextShape::new(pos, galley, fallback_color).into()
319 }
320
321 #[inline]
323 pub fn galley_with_override_text_color(
324 pos: Pos2,
325 galley: Arc<Galley>,
326 text_color: Color32,
327 ) -> Self {
328 TextShape::new(pos, galley, text_color)
329 .with_override_text_color(text_color)
330 .into()
331 }
332
333 #[inline]
334 #[deprecated = "Use `Shape::galley` or `Shape::galley_with_override_text_color` instead"]
335 pub fn galley_with_color(pos: Pos2, galley: Arc<Galley>, text_color: Color32) -> Self {
336 Self::galley_with_override_text_color(pos, galley, text_color)
337 }
338
339 #[inline]
340 pub fn mesh(mesh: impl Into<Arc<Mesh>>) -> Self {
341 let mesh = mesh.into();
342 debug_assert!(mesh.is_valid());
343 Self::Mesh(mesh)
344 }
345
346 pub fn image(texture_id: TextureId, rect: Rect, uv: Rect, tint: Color32) -> Self {
353 let mut mesh = Mesh::with_texture(texture_id);
354 mesh.add_rect_with_uv(rect, uv, tint);
355 Self::mesh(mesh)
356 }
357
358 pub fn visual_bounding_rect(&self) -> Rect {
360 match self {
361 Self::Noop => Rect::NOTHING,
362 Self::Vec(shapes) => {
363 let mut rect = Rect::NOTHING;
364 for shape in shapes {
365 rect = rect.union(shape.visual_bounding_rect());
366 }
367 rect
368 }
369 Self::Circle(circle_shape) => circle_shape.visual_bounding_rect(),
370 Self::Ellipse(ellipse_shape) => ellipse_shape.visual_bounding_rect(),
371 Self::LineSegment { points, stroke } => {
372 if stroke.is_empty() {
373 Rect::NOTHING
374 } else {
375 Rect::from_two_pos(points[0], points[1]).expand(stroke.width / 2.0)
376 }
377 }
378 Self::Path(path_shape) => path_shape.visual_bounding_rect(),
379 Self::Rect(rect_shape) => rect_shape.visual_bounding_rect(),
380 Self::Text(text_shape) => text_shape.visual_bounding_rect(),
381 Self::Mesh(mesh) => mesh.calc_bounds(),
382 Self::QuadraticBezier(bezier) => bezier.visual_bounding_rect(),
383 Self::CubicBezier(bezier) => bezier.visual_bounding_rect(),
384 Self::Callback(custom) => custom.rect,
385 }
386 }
387}
388
389impl Shape {
391 #[inline(always)]
392 pub fn texture_id(&self) -> crate::TextureId {
393 if let Self::Mesh(mesh) = self {
394 mesh.texture_id
395 } else if let Self::Rect(rect_shape) = self {
396 rect_shape.fill_texture_id()
397 } else {
398 crate::TextureId::default()
399 }
400 }
401
402 #[inline(always)]
406 pub fn scale(&mut self, factor: f32) {
407 self.transform(TSTransform::from_scaling(factor));
408 }
409
410 #[inline(always)]
414 pub fn translate(&mut self, delta: Vec2) {
415 self.transform(TSTransform::from_translation(delta));
416 }
417
418 pub fn transform(&mut self, transform: TSTransform) {
423 match self {
424 Self::Noop => {}
425 Self::Vec(shapes) => {
426 for shape in shapes {
427 shape.transform(transform);
428 }
429 }
430 Self::Circle(circle_shape) => {
431 circle_shape.center = transform * circle_shape.center;
432 circle_shape.radius *= transform.scaling;
433 circle_shape.stroke.width *= transform.scaling;
434 }
435 Self::Ellipse(ellipse_shape) => {
436 ellipse_shape.center = transform * ellipse_shape.center;
437 ellipse_shape.radius *= transform.scaling;
438 ellipse_shape.stroke.width *= transform.scaling;
439 }
440 Self::LineSegment { points, stroke } => {
441 for p in points {
442 *p = transform * *p;
443 }
444 stroke.width *= transform.scaling;
445 }
446 Self::Path(path_shape) => {
447 for p in &mut path_shape.points {
448 *p = transform * *p;
449 }
450 path_shape.stroke.width *= transform.scaling;
451 }
452 Self::Rect(rect_shape) => {
453 rect_shape.rect = transform * rect_shape.rect;
454 rect_shape.corner_radius *= transform.scaling;
455 rect_shape.stroke.width *= transform.scaling;
456 rect_shape.blur_width *= transform.scaling;
457 }
458 Self::Text(text_shape) => {
459 text_shape.pos = transform * text_shape.pos;
460
461 let galley = Arc::make_mut(&mut text_shape.galley);
463 for row in &mut galley.rows {
464 row.visuals.mesh_bounds = transform.scaling * row.visuals.mesh_bounds;
465 for v in &mut row.visuals.mesh.vertices {
466 v.pos = Pos2::new(transform.scaling * v.pos.x, transform.scaling * v.pos.y);
467 }
468 }
469
470 galley.mesh_bounds = transform.scaling * galley.mesh_bounds;
471 galley.rect = transform.scaling * galley.rect;
472 }
473 Self::Mesh(mesh) => {
474 Arc::make_mut(mesh).transform(transform);
475 }
476 Self::QuadraticBezier(bezier) => {
477 for p in &mut bezier.points {
478 *p = transform * *p;
479 }
480 bezier.stroke.width *= transform.scaling;
481 }
482 Self::CubicBezier(bezier) => {
483 for p in &mut bezier.points {
484 *p = transform * *p;
485 }
486 bezier.stroke.width *= transform.scaling;
487 }
488 Self::Callback(shape) => {
489 shape.rect = transform * shape.rect;
490 }
491 }
492 }
493}
494
495fn points_from_line(
499 path: &[Pos2],
500 spacing: f32,
501 radius: f32,
502 color: Color32,
503 shapes: &mut Vec<Shape>,
504) {
505 let mut position_on_segment = 0.0;
506 for window in path.windows(2) {
507 let (start, end) = (window[0], window[1]);
508 let vector = end - start;
509 let segment_length = vector.length();
510 while position_on_segment < segment_length {
511 let new_point = start + vector * (position_on_segment / segment_length);
512 shapes.push(Shape::circle_filled(new_point, radius, color));
513 position_on_segment += spacing;
514 }
515 position_on_segment -= segment_length;
516 }
517}
518
519fn dashes_from_line(
521 path: &[Pos2],
522 stroke: Stroke,
523 dash_lengths: &[f32],
524 gap_lengths: &[f32],
525 shapes: &mut Vec<Shape>,
526 dash_offset: f32,
527) {
528 assert_eq!(dash_lengths.len(), gap_lengths.len());
529 let mut position_on_segment = dash_offset;
530 let mut drawing_dash = false;
531 let mut step = 0;
532 let steps = dash_lengths.len();
533 for window in path.windows(2) {
534 let (start, end) = (window[0], window[1]);
535 let vector = end - start;
536 let segment_length = vector.length();
537
538 let mut start_point = start;
539 while position_on_segment < segment_length {
540 let new_point = start + vector * (position_on_segment / segment_length);
541 if drawing_dash {
542 shapes.push(Shape::line_segment([start_point, new_point], stroke));
544 position_on_segment += gap_lengths[step];
545 step += 1;
547 if step >= steps {
548 step = 0;
549 }
550 } else {
551 start_point = new_point;
553 position_on_segment += dash_lengths[step];
554 }
555 drawing_dash = !drawing_dash;
556 }
557
558 if drawing_dash {
560 shapes.push(Shape::line_segment([start_point, end], stroke));
561 }
562
563 position_on_segment -= segment_length;
564 }
565}