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}