Skip to content

Commit

Permalink
Remove the need for PathBuilder (#267)
Browse files Browse the repository at this point in the history
* Make `Geometry` generic over the builder

This is quite a rudimentary solution
that involves too many traits.
A minimal solution is needed to simplify the code.

Fix test

* Substitute `PathBuilder` with `ShapePath`

`ShapePath` is a `Geometry` that can be fed to `ShapeBuilder`.
This makes `PathBuilder` redundant.

* Update `CHANGELOG.md`
  • Loading branch information
Nilirad authored Dec 14, 2024
1 parent 7dd478c commit 0dacb38
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 114 deletions.
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## 0.14.0

This version uses Bevy's **Required Components**,
along with other changes,
therefore many items have been changed.
Examples may help migration.

Expand All @@ -12,8 +13,10 @@ Examples may help migration.
- Deprecated `ShapeBundle` in favor of the `Shape` component.
- `prelude` no longer exports `ShapeBundle`.
- Added `ShapeBuilder`: works similarly to `GeometryBuilder`
- Removed `GeometryBuilder` and `ShapePath`.
- `PathBuilder` now allows chaining methods.
- Removed `GeometryBuilder` and `PathBuilder`.
- `ShapePath` now works similarly to `PathBuilder`,
but implements `Geometry`,
so it has to be used with `ShapeBuilder`.

## 0.13.0
- Support for Bevy 0.15.0.
Expand Down
15 changes: 9 additions & 6 deletions examples/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ fn main() {
}

fn setup_system(mut commands: Commands) {
let shape = PathBuilder::new()
.fill(RED)
.stroke((BLACK, 10.0))
let path = ShapePath::new()
.move_to(Vec2::new(0., 0.))
.cubic_bezier_to(
Vec2::new(70., 70.),
Expand All @@ -23,9 +21,14 @@ fn setup_system(mut commands: Commands) {
Vec2::new(-70., 70.),
Vec2::new(0., 0.),
)
.close()
.build();
.close();

commands.spawn((Camera2d, Msaa::Sample4));
commands.spawn((shape, Transform::from_xyz(0., 75., 0.)));
commands.spawn((
ShapeBuilder::with(&path)
.fill(RED)
.stroke((BLACK, 10.0))
.build(),
Transform::from_xyz(0., 75., 0.),
));
}
5 changes: 3 additions & 2 deletions src/entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#![expect(deprecated)]

use bevy::prelude::*;
use lyon_algorithms::path::Builder;
use lyon_tessellation::{self as tess};

use crate::{
Expand Down Expand Up @@ -69,8 +70,8 @@ impl Shape {
}
}

impl Geometry for Shape {
fn add_geometry(&self, b: &mut tess::path::path::Builder) {
impl Geometry<Builder> for Shape {
fn add_geometry(&self, b: &mut Builder) {
b.extend_from_paths(&[self.path.as_slice()]);
}
}
Expand Down
83 changes: 59 additions & 24 deletions src/geometry.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Types for defining and using geometries.
use lyon_algorithms::path::{builder::WithSvg, BuilderImpl};
use lyon_tessellation::path::path::Builder;

use crate::{
Expand Down Expand Up @@ -38,7 +39,7 @@ use crate::{
/// }
///
/// // Finally, implement the `add_geometry` method.
/// impl Geometry for Rectangle {
/// impl Geometry<Builder> for Rectangle {
/// fn add_geometry(&self, b: &mut Builder) {
/// b.add_rectangle(
/// &Box2D::new(Point::zero(), Point::new(self.width, self.height)),
Expand All @@ -47,46 +48,46 @@ use crate::{
/// }
/// }
/// ```
pub trait Geometry {
pub trait Geometry<GenericBuilder> {
/// Adds the geometry of the shape to the given Lyon path `Builder`.
fn add_geometry(&self, b: &mut Builder);
fn add_geometry(&self, b: &mut GenericBuilder);
}

/// Provides basic functionality common
/// to [`ShapeBuilder`] and [`ReadyShapeBuilder`].
pub trait ShapeBuilderBase {
pub trait ShapeBuilderBase<GenericBuilder> {
/// Adds a `Geometry` to the builder.
#[must_use]
fn add(self, shape: &impl Geometry) -> Self;
fn add(self, shape: &impl Geometry<GenericBuilder>) -> Self;

/// Sets the fill mode of the builder.
#[must_use]
fn fill(self, fill: impl Into<Fill>) -> ReadyShapeBuilder;
fn fill(self, fill: impl Into<Fill>) -> ReadyShapeBuilder<GenericBuilder>;

/// Sets the stroke mode of the builder.
#[must_use]
fn stroke(self, stroke: impl Into<Stroke>) -> ReadyShapeBuilder;
fn stroke(self, stroke: impl Into<Stroke>) -> ReadyShapeBuilder<GenericBuilder>;
}

/// Provides methods for building a shape.
#[derive(Default, Clone)]
pub struct ShapeBuilder(Builder);
pub struct ShapeBuilder<GenericBuilder>(GenericBuilder);

impl ShapeBuilderBase for ShapeBuilder {
fn add(mut self, shape: &impl Geometry) -> Self {
impl<GenericBuilder> ShapeBuilderBase<GenericBuilder> for ShapeBuilder<GenericBuilder> {
fn add(mut self, shape: &impl Geometry<GenericBuilder>) -> Self {
shape.add_geometry(&mut self.0);
self
}

fn fill(self, fill: impl Into<Fill>) -> ReadyShapeBuilder {
fn fill(self, fill: impl Into<Fill>) -> ReadyShapeBuilder<GenericBuilder> {
ReadyShapeBuilder {
builder: self.0,
fill: Some(fill.into()),
stroke: None,
}
}

fn stroke(self, stroke: impl Into<Stroke>) -> ReadyShapeBuilder {
fn stroke(self, stroke: impl Into<Stroke>) -> ReadyShapeBuilder<GenericBuilder> {
ReadyShapeBuilder {
builder: self.0,
fill: None,
Expand All @@ -95,16 +96,19 @@ impl ShapeBuilderBase for ShapeBuilder {
}
}

impl ShapeBuilder {
impl<GenericBuilder> ShapeBuilder<GenericBuilder>
where
GenericBuilder: LyonPathBuilderExt,
{
/// Constructs a new `ShapeBuilder`.
#[must_use]
pub fn new() -> Self {
Self(Builder::new())
Self(GenericBuilder::new())
}

/// Constructs a new `ShapeBuilder` with an initial `Geometry`.
#[must_use]
pub fn with(geometry: &impl Geometry) -> Self {
pub fn with(geometry: &impl Geometry<GenericBuilder>) -> Self {
Self::new().add(geometry)
}
}
Expand All @@ -113,36 +117,67 @@ impl ShapeBuilder {
///
/// This struct can only be obtained by using [`ShapeBuilder`].
#[derive(Clone)]
pub struct ReadyShapeBuilder {
pub(crate) builder: Builder,
pub struct ReadyShapeBuilder<GenericBuilder> {
pub(crate) builder: GenericBuilder,
pub(crate) fill: Option<Fill>,
pub(crate) stroke: Option<Stroke>,
}

impl ShapeBuilderBase for ReadyShapeBuilder {
fn add(mut self, shape: &impl Geometry) -> Self {
impl<GenericBuilder> ShapeBuilderBase<GenericBuilder> for ReadyShapeBuilder<GenericBuilder> {
fn add(mut self, shape: &impl Geometry<GenericBuilder>) -> Self {
shape.add_geometry(&mut self.builder);
self
}

fn fill(self, fill: impl Into<Fill>) -> ReadyShapeBuilder {
fn fill(self, fill: impl Into<Fill>) -> Self {
Self {
fill: Some(fill.into()),
..self
}
}

fn stroke(self, stroke: impl Into<Stroke>) -> ReadyShapeBuilder {
fn stroke(self, stroke: impl Into<Stroke>) -> Self {
Self {
stroke: Some(stroke.into()),
..self
}
}
}
impl ReadyShapeBuilder {

/// Generalizes the implementation of the `build` method
/// over multiple Lyon builders.
pub trait ReadyShapeBuilderTrait {
/// Builds a `Shape` according to the builder's settings.
#[allow(clippy::must_use_candidate)]
pub fn build(self) -> Shape {
fn build(self) -> Shape;
}

impl ReadyShapeBuilderTrait for ReadyShapeBuilder<Builder> {
fn build(self) -> Shape {
Shape::new(self.builder.build(), self.fill, self.stroke)
}
}

impl ReadyShapeBuilderTrait for ReadyShapeBuilder<WithSvg<BuilderImpl>> {
fn build(self) -> Shape {
Shape::new(self.builder.build(), self.fill, self.stroke)
}
}

/// Extends `lyon` path builders with analogous methods
/// under the same interface.
pub trait LyonPathBuilderExt {
/// Creates a new path builder.
fn new() -> Self;
}

impl LyonPathBuilderExt for Builder {
fn new() -> Self {
Self::new()
}
}

impl LyonPathBuilderExt for WithSvg<BuilderImpl> {
fn new() -> Self {
Builder::new().with_svg()
}
}
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ pub mod prelude {
pub use crate::{
draw::{Fill, Stroke},
entity::Shape,
geometry::{Geometry, ShapeBuilder, ShapeBuilderBase},
path::PathBuilder,
geometry::{Geometry, ReadyShapeBuilderTrait, ShapeBuilder, ShapeBuilderBase},
path::ShapePath,
plugin::ShapePlugin,
shapes::{self, BorderRadii, RectangleOrigin, RegularPolygon, RegularPolygonFeature},
};
Expand Down
Loading

0 comments on commit 0dacb38

Please sign in to comment.