pix_engine/shape.rs
1//! Shape methods for drawing.
2//!
3//! Methods for drawing and interacting with shapes such as points, lines, rectangles, etc.
4//!
5//! Provided traits:
6//!
7//! - [Contains]: Defines [`contains`] for shapes containing other shapes.
8//! - [Intersects]: Defines [`intersects`] for shapes intersecting other shapes.
9//!
10//! Provided [`PixState`] methods;
11//!
12//! - [`PixState::point`]: Draw a [Point] to the current canvas.
13//! - [`PixState::line`]: Draw a [Line] to the current canvas.
14//! - [`PixState::triangle`]: Draw a [Triangle][Tri] to the current canvas.
15//! - [`PixState::square`]: Draw a square [Rect] to the current canvas.
16//! - [`PixState::rounded_square`]: Draw a square [Rect] with rounded corners to the current canvas.
17//! - [`PixState::rect`]: Draw a [Rect] to the current canvas.
18//! - [`PixState::rounded_rect`]: Draw a [Rect] with rounded corners to the current canvas.
19//! - [`PixState::quad`]: Draw a [Quad] to the current canvas.
20//! - [`PixState::polygon`]: Draw a polygon defined by a set of [Point]s to the current canvas.
21//! - [`PixState::wireframe`]: Draw a wireframe defined by a set vertexes to the current canvas.
22//! - [`PixState::circle`]: Draw a circle [Ellipse] to the current canvas.
23//! - [`PixState::ellipse`]: Draw an [Ellipse] to the current canvas.
24//! - [`PixState::arc`]: Draw an arc to the current canvas.
25//!
26//! [`contains`]: Contains::contains
27//! [`intersects`]: Intersects::intersects
28
29use crate::{prelude::*, renderer::Rendering};
30use std::iter::Iterator;
31
32#[macro_use]
33pub mod ellipse;
34#[macro_use]
35pub mod line;
36#[macro_use]
37pub mod point;
38#[macro_use]
39pub mod rect;
40#[macro_use]
41pub mod quad;
42#[macro_use]
43pub mod sphere;
44#[macro_use]
45pub mod triangle;
46
47#[doc(inline)]
48pub use ellipse::*;
49#[doc(inline)]
50pub use line::*;
51#[doc(inline)]
52pub use point::*;
53#[doc(inline)]
54pub use quad::*;
55#[doc(inline)]
56pub use rect::*;
57#[doc(inline)]
58pub use sphere::*;
59#[doc(inline)]
60pub use triangle::*;
61
62/// Trait for shape containing operations.
63pub trait Contains<S> {
64 /// Returns whether this shape contains a another shape.
65 fn contains(&self, shape: S) -> bool;
66}
67
68/// Trait for shape intersection operations.
69pub trait Intersects<S> {
70 /// The result of the intersection.
71 type Result;
72
73 /// Returns an intersection result based on the shape or `None` if there is no intersection.
74 fn intersects(&self, shape: S) -> Option<Self::Result>;
75}
76
77impl PixState {
78 /// Draw a [Point] to the current canvas. [`PixState::stroke`] controls whether the point is
79 /// drawn or not. [`PixState::stroke_weight`] and [`PixState::fill`] have no effect.
80 ///
81 /// # Errors
82 ///
83 /// If the renderer fails to draw to the current render target, then an error is returned.
84 ///
85 /// # Example
86 ///
87 /// ```
88 /// # use pix_engine::prelude::*;
89 /// # struct App;
90 /// # impl PixEngine for App {
91 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
92 /// s.stroke(Color::RED);
93 /// s.point(s.mouse_pos())?;
94 /// Ok(())
95 /// }
96 /// # }
97 /// ```
98 pub fn point<P>(&mut self, p: P) -> PixResult<()>
99 where
100 P: Into<Point<i32>>,
101 {
102 if let Some(stroke) = self.settings.stroke {
103 self.renderer.point(p.into(), stroke)?;
104 }
105 Ok(())
106 }
107
108 /// Draw a [Line] to the current canvas. [`PixState::stroke`] controls whether the line is drawn
109 /// or not. [`PixState::stroke_weight`] controls the line thickness. [`PixState::fill`] has no
110 /// effect.
111 ///
112 /// # Errors
113 ///
114 /// If the renderer fails to draw to the current render target, then an error is returned.
115 ///
116 /// # Example
117 ///
118 /// ```
119 /// # use pix_engine::prelude::*;
120 /// # struct App;
121 /// # impl PixEngine for App {
122 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
123 /// s.stroke(Color::RED);
124 /// s.line([s.pmouse_pos(), s.mouse_pos()])?;
125 /// Ok(())
126 /// }
127 /// # }
128 /// ```
129 pub fn line<L>(&mut self, line: L) -> PixResult<()>
130 where
131 L: Into<Line<i32>>,
132 {
133 let s = &self.settings;
134 if let Some(stroke) = s.stroke {
135 self.renderer
136 .line(line.into(), s.smooth, s.stroke_weight as u8, stroke)?;
137 }
138 Ok(())
139 }
140
141 /// Draw a cubic Bezier curve to the current canvas. [`PixState::stroke`] controls whether the
142 /// line is drawn or not. [`PixState::bezier_detail`] controls the resolution of the
143 /// curve. [`PixState::fill`] has no effect.
144 ///
145 /// The first and last points are the anchor points of the curve, while the middle points are
146 /// the control points that "pull" the curve towards them.
147 ///
148 /// # Errors
149 ///
150 /// If the renderer fails to draw to the current render target, then an error is returned.
151 ///
152 /// # Example
153 ///
154 /// ```
155 /// # use pix_engine::prelude::*;
156 /// # struct App;
157 /// # impl PixEngine for App {
158 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
159 /// s.stroke(Color::RED);
160 /// s.bezier([[85, 20], [10, 10], [90, 90], [15, 80]])?;
161 /// Ok(())
162 /// }
163 /// # }
164 /// ```
165 pub fn bezier<P, I>(&mut self, points: I) -> PixResult<()>
166 where
167 P: Into<Point<i32>>,
168 I: IntoIterator<Item = P>,
169 {
170 let s = &self.settings;
171 self.renderer.bezier(
172 points.into_iter().map(Into::into),
173 s.bezier_detail,
174 s.stroke,
175 )
176 }
177
178 /// Draw a [Triangle][Tri] to the current canvas. [`PixState::fill`] and [`PixState::stroke`]
179 /// control whether the triangle is filled or outlined.
180 ///
181 /// # Errors
182 ///
183 /// If the renderer fails to draw to the current render target, then an error is returned.
184 ///
185 /// # Example
186 ///
187 /// ```
188 /// # use pix_engine::prelude::*;
189 /// # struct App;
190 /// # impl PixEngine for App {
191 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
192 /// s.fill(Color::BLACK);
193 /// s.stroke(Color::RED);
194 /// s.triangle(tri!([10, 0], [-5, 5], [5, 5]))?;
195 /// Ok(())
196 /// }
197 /// # }
198 /// ```
199 pub fn triangle<T>(&mut self, tri: T) -> PixResult<()>
200 where
201 T: Into<Tri<i32>>,
202 {
203 let s = &self.settings;
204 self.renderer
205 .triangle(tri.into(), s.smooth, s.fill, s.stroke)
206 }
207
208 /// Draw a square [Rect] to the current canvas. [`PixState::fill`] and [`PixState::stroke`] control
209 /// whether the square is filled or outlined. [`RectMode`] controls how the `(x, y)` position is
210 /// interpreted.
211 ///
212 /// Alias for [`PixState::rect`].
213 ///
214 /// # Errors
215 ///
216 /// If the renderer fails to draw to the current render target, then an error is returned.
217 ///
218 /// # Example
219 ///
220 /// ```
221 /// # use pix_engine::prelude::*;
222 /// # struct App;
223 /// # impl PixEngine for App {
224 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
225 /// s.fill(Color::BLACK);
226 /// s.stroke(Color::RED);
227 /// s.square(square![s.mouse_pos(), 100])?;
228 /// Ok(())
229 /// }
230 /// # }
231 /// ```
232 #[doc(alias = "rect")]
233 pub fn square<R>(&mut self, square: R) -> PixResult<()>
234 where
235 R: Into<Rect<i32>>,
236 {
237 self.rect(square)
238 }
239
240 /// Draw a rounded [Square](Rect) to the current canvas. [`PixState::fill`] and
241 /// [`PixState::stroke`] control whether the square is filled or outlined. [`RectMode`] controls
242 /// how the `(x, y)` position is interpreted.
243 ///
244 /// Alias for [`PixState::rounded_rect`].
245 ///
246 /// # Errors
247 ///
248 /// If the renderer fails to draw to the current render target, then an error is returned.
249 ///
250 /// # Example
251 ///
252 /// ```
253 /// # use pix_engine::prelude::*;
254 /// # struct App;
255 /// # impl PixEngine for App {
256 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
257 /// s.fill(Color::BLACK);
258 /// s.stroke(Color::RED);
259 /// s.rounded_square(square![s.mouse_pos(), 100], 10)?;
260 /// Ok(())
261 /// }
262 /// # }
263 /// ```
264 #[doc(alias = "rounded_rect")]
265 pub fn rounded_square<R>(&mut self, square: R, radius: i32) -> PixResult<()>
266 where
267 R: Into<Rect<i32>>,
268 {
269 self.rounded_rect(square, radius)
270 }
271
272 /// Draw a [Rectangle](Rect) to the current canvas. [`PixState::fill`] and [`PixState::stroke`]
273 /// control whether the rect is filled or outlined. [`RectMode`] controls how the `(x, y)`
274 /// position is interpreted.
275 ///
276 /// # Errors
277 ///
278 /// If the renderer fails to draw to the current render target, then an error is returned.
279 ///
280 /// # Example
281 ///
282 /// ```
283 /// # use pix_engine::prelude::*;
284 /// # struct App;
285 /// # impl PixEngine for App {
286 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
287 /// s.fill(Color::BLACK);
288 /// s.stroke(Color::RED);
289 /// s.rect(rect![s.mouse_pos(), 100, 100])?;
290 /// Ok(())
291 /// }
292 /// # }
293 /// ```
294 pub fn rect<R>(&mut self, rect: R) -> PixResult<()>
295 where
296 R: Into<Rect<i32>>,
297 {
298 let s = &self.settings;
299 let rect = self.get_rect(rect);
300 self.renderer.rect(rect, None, s.fill, s.stroke)
301 }
302
303 /// Draw a rounded [Rectangle](Rect) to the current canvas. [`PixState::fill`] and
304 /// [`PixState::stroke`] control whether the rect is filled or outlined. [`RectMode`] controls how
305 /// the `(x, y)` position is interpreted.
306 ///
307 /// # Errors
308 ///
309 /// If the renderer fails to draw to the current render target, then an error is returned.
310 ///
311 /// # Example
312 ///
313 /// ```
314 /// # use pix_engine::prelude::*;
315 /// # struct App;
316 /// # impl PixEngine for App {
317 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
318 /// s.fill(Color::BLACK);
319 /// s.stroke(Color::RED);
320 /// s.rounded_rect(rect![s.mouse_pos(), 100, 100], 10)?;
321 /// Ok(())
322 /// }
323 /// # }
324 /// ```
325 pub fn rounded_rect<R>(&mut self, rect: R, radius: i32) -> PixResult<()>
326 where
327 R: Into<Rect<i32>>,
328 {
329 let s = &self.settings;
330 let rect = self.get_rect(rect);
331 self.renderer.rect(rect, Some(radius), s.fill, s.stroke)
332 }
333
334 /// Draw a [Quadrilateral](Quad) to the current canvas. [`PixState::fill`] and
335 /// [`PixState::stroke`] control whether the quad is filled or outlined. [`RectMode`] has no
336 /// effect.
337 ///
338 /// # Errors
339 ///
340 /// If the renderer fails to draw to the current render target, then an error is returned.
341 ///
342 /// # Example
343 ///
344 /// ```
345 /// # use pix_engine::prelude::*;
346 /// # struct App;
347 /// # impl PixEngine for App {
348 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
349 /// s.fill(Color::BLACK);
350 /// s.stroke(Color::RED);
351 /// s.quad(quad![10, 20, 30, 10, 20, 25, 15, 15])?;
352 /// Ok(())
353 /// }
354 /// # }
355 /// ```
356 pub fn quad<Q>(&mut self, quad: Q) -> PixResult<()>
357 where
358 Q: Into<Quad<i32>>,
359 {
360 let s = &self.settings;
361 self.renderer.quad(quad.into(), s.smooth, s.fill, s.stroke)
362 }
363
364 /// Draw a polygon to the current canvas. [`PixState::fill`] and [`PixState::stroke`] control
365 /// whether the polygon is filled or outlined. [`RectMode`] has no effect.
366 ///
367 /// # Errors
368 ///
369 /// If the renderer fails to draw to the current render target, then an error is returned.
370 ///
371 /// # Example
372 ///
373 /// ```
374 /// # use pix_engine::prelude::*;
375 /// # struct App;
376 /// # impl PixEngine for App {
377 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
378 /// s.fill(Color::BLACK);
379 /// s.stroke(Color::RED);
380 /// s.polygon([[10, 10], [50, 20], [70, 30], [60, 50], [10, 50]])?;
381 /// Ok(())
382 /// }
383 /// # }
384 /// ```
385 pub fn polygon<P, I>(&mut self, points: I) -> PixResult<()>
386 where
387 P: Into<Point<i32>>,
388 I: IntoIterator<Item = P>,
389 {
390 let s = &self.settings;
391 self.renderer.polygon(
392 points.into_iter().map(Into::into),
393 s.smooth,
394 s.fill,
395 s.stroke,
396 )
397 }
398
399 /// Draw a wireframe to the current canvas, translated to a given [Point] and optionally
400 /// rotated by `angle` and `scaled`. [`PixState::fill`] and [`PixState::stroke`] control whether
401 /// the wireframe is filled or outlined. `angle` can be in either radians or degrees based on
402 /// [`AngleMode`].
403 ///
404 /// # Errors
405 ///
406 /// If the renderer fails to draw to the current render target, then an error is returned.
407 ///
408 /// # Example
409 ///
410 /// ```
411 /// # use pix_engine::prelude::*;
412 /// # struct App;
413 /// # impl PixEngine for App {
414 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
415 /// let wireframe = [
416 /// point!(5.0, 0.0),
417 /// point!(-2.5, -2.5),
418 /// point!(-2.5, 2.5)
419 /// ];
420 /// s.fill(Color::CADET_BLUE);
421 /// s.stroke(Color::BLACK);
422 /// s.angle_mode(AngleMode::Degrees);
423 /// s.wireframe(wireframe, [10, 10], 45.0, 2.0)?;
424 /// Ok(())
425 /// }
426 /// # }
427 /// ```
428 pub fn wireframe<V, P1, P2, A, S>(
429 &mut self,
430 vertexes: V,
431 pos: P2,
432 angle: A,
433 scale: S,
434 ) -> PixResult<()>
435 where
436 V: IntoIterator<Item = P1>,
437 P1: Into<Point<f64>>,
438 P2: Into<Point<i32>>,
439 A: Into<Option<f64>>,
440 S: Into<Option<f64>>,
441 {
442 let s = &self.settings;
443 let pos = pos.into();
444 let mut angle = angle.into().unwrap_or(0.0);
445 if s.angle_mode == AngleMode::Degrees {
446 angle = angle.to_radians();
447 };
448 let scale = scale.into().unwrap_or(1.0);
449 let (sin, cos) = angle.sin_cos();
450 let (px, py) = (f64::from(pos.x()), f64::from(pos.y()));
451 let vs = vertexes.into_iter().map(|v| {
452 let v = v.into();
453 // rotation * scale + translation
454 let x = (v.x() * cos - v.y() * sin).mul_add(scale, px).round() as i32;
455 let y = (v.x().mul_add(sin, v.y() * cos)).mul_add(scale, py).round() as i32;
456 point![x, y]
457 });
458 self.polygon(vs)
459 }
460
461 /// Draw a circle [Ellipse] to the current canvas. [`PixState::fill`] and [`PixState::stroke`]
462 /// control whether the circle is filled or outlined. [`EllipseMode`] controls how the `(x, y)`
463 /// position is interpreted.
464 ///
465 /// Alias for [`PixState::ellipse`].
466 ///
467 /// # Errors
468 ///
469 /// If the renderer fails to draw to the current render target, then an error is returned.
470 ///
471 /// # Example
472 ///
473 /// ```
474 /// # use pix_engine::prelude::*;
475 /// # struct App;
476 /// # impl PixEngine for App {
477 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
478 /// s.fill(Color::BLACK);
479 /// s.stroke(Color::RED);
480 /// s.circle(circle![s.mouse_pos(), 100])?;
481 /// Ok(())
482 /// }
483 /// # }
484 /// ```
485 #[doc(alias = "ellipse")]
486 pub fn circle<C>(&mut self, circle: C) -> PixResult<()>
487 where
488 C: Into<Ellipse<i32>>,
489 {
490 self.ellipse(circle)
491 }
492
493 /// Draw a [Ellipse] to the current canvas. [`PixState::fill`] and [`PixState::stroke`] control
494 /// whether the ellipse is filled or outlined. [`EllipseMode`] controls how the `(x, y)` position
495 /// is interpreted.
496 ///
497 /// # Errors
498 ///
499 /// If the renderer fails to draw to the current render target, then an error is returned.
500 ///
501 /// # Example
502 ///
503 /// ```
504 /// # use pix_engine::prelude::*;
505 /// # struct App;
506 /// # impl PixEngine for App {
507 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
508 /// s.fill(Color::BLACK);
509 /// s.stroke(Color::RED);
510 /// s.ellipse(ellipse![s.mouse_pos(), 100, 100])?;
511 /// Ok(())
512 /// }
513 /// # }
514 /// ```
515 pub fn ellipse<E>(&mut self, ellipse: E) -> PixResult<()>
516 where
517 E: Into<Ellipse<i32>>,
518 {
519 let s = &self.settings;
520 let ellipse = self.get_ellipse(ellipse);
521 self.renderer.ellipse(ellipse, s.smooth, s.fill, s.stroke)
522 }
523
524 /// Draw an arc of a given `radius` and length defined by `start` and `end` to the current
525 /// canvas. [`PixState::fill`] and [`PixState::stroke`] control whether the pie is filled or
526 /// outlined. [`ArcMode`] changes whether the arc is drawn as an open segment or a pie shape.
527 ///
528 /// # Errors
529 ///
530 /// If the renderer fails to draw to the current render target, then an error is returned.
531 ///
532 /// # Example
533 ///
534 /// ```
535 /// # use pix_engine::prelude::*;
536 /// # struct App;
537 /// # impl PixEngine for App {
538 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
539 /// s.fill(Color::BLACK);
540 /// s.stroke(Color::RED);
541 /// s.arc_mode(ArcMode::Pie);
542 /// s.arc(s.mouse_pos(), 20, 0, 180)?;
543 /// Ok(())
544 /// }
545 /// # }
546 /// ```
547 pub fn arc<P>(&mut self, p: P, radius: i32, start: i32, end: i32) -> PixResult<()>
548 where
549 P: Into<Point<i32>>,
550 {
551 let s = &self.settings;
552 let p = p.into();
553 self.renderer
554 .arc(p, radius, start, end, s.arc_mode, s.fill, s.stroke)
555 }
556}