Skip to content
10 changes: 6 additions & 4 deletions assets/shapes/annulus.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

#import smud

// Parametrized annulus (ring) SDF
// params.x contains the outer radius
// params.y contains the inner radius
// Annulus (ring) SDF using bounds for outer radius
// Outer radius is computed as min(bounds.x, bounds.y)
// params.x contains the inner radius
fn sdf(input: smud::SdfInput) -> f32 {
return smud::sd_annulus(input.pos, input.params.x, input.params.y);
let outer_radius = min(input.bounds.x, input.bounds.y);
let inner_radius = input.params.x;
return smud::sd_annulus(input.pos, outer_radius, inner_radius);
}
10 changes: 6 additions & 4 deletions assets/shapes/capsule.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

#import smud

// Parametrized capsule SDF
// params.x contains the radius
// params.y contains the half_length
// Capsule SDF using bounds
// Radius is min(bounds.x, bounds.y) to ensure half_length remains positive
// Half-length of the line segment is bounds.y - radius
fn sdf(input: smud::SdfInput) -> f32 {
return smud::sd_capsule(input.pos, input.params.x, input.params.y);
let radius = min(input.bounds.x, input.bounds.y);
let half_length = input.bounds.y - radius;
return smud::sd_capsule(input.pos, radius, half_length);
}
7 changes: 4 additions & 3 deletions assets/shapes/circle.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

#import smud

// Parametrized circle SDF
// params.x contains the radius
// Circle SDF using bounds
// Radius is computed as min(bounds.x, bounds.y)
fn sdf(input: smud::SdfInput) -> f32 {
return smud::sd_circle(input.pos, input.params.x);
let radius = min(input.bounds.x, input.bounds.y);
return smud::sd_circle(input.pos, radius);
}
13 changes: 7 additions & 6 deletions assets/shapes/circular_sector.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

#import smud

// Parametrized circular sector (pie slice) SDF
// params.x contains the radius
// params.y contains sin(half_angle)
// params.z contains cos(half_angle)
// Circular sector (pie slice) SDF using bounds for radius
// Radius is computed as min(bounds.x, bounds.y)
// params.x contains sin(half_angle)
// params.y contains cos(half_angle)
fn sdf(input: smud::SdfInput) -> f32 {
let c = vec2<f32>(input.params.y, input.params.z);
return smud::sd_pie(input.pos, c, input.params.x);
let radius = min(input.bounds.x, input.bounds.y);
let c = vec2<f32>(input.params.x, input.params.y);
return smud::sd_pie(input.pos, c, radius);
}
9 changes: 4 additions & 5 deletions assets/shapes/ellipse.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@

#import smud

// Parametrized ellipse SDF
// params.x contains the semi-major axis (half_size.x)
// params.y contains the semi-minor axis (half_size.y)
// Ellipse SDF using bounds
// Uses input.bounds for the semi-major and semi-minor axes (half_size.x, half_size.y)
// If the axes are equal (or nearly equal), delegates to circle SDF since sd_ellipse doesn't handle that case
fn sdf(input: smud::SdfInput) -> f32 {
let a = input.params.x;
let b = input.params.y;
let a = input.bounds.x;
let b = input.bounds.y;

// Use circle SDF when radii are equal (sd_ellipse doesn't handle this case well)
if (abs(a - b) < 1e-6) {
Expand Down
6 changes: 3 additions & 3 deletions assets/shapes/rectangle.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

#import smud

// Parametrized rectangle SDF
// params.xy contains the half-size of the rectangle
// Rectangle SDF using bounds
// Uses input.bounds for the half-size of the rectangle
fn sdf(input: smud::SdfInput) -> f32 {
return smud::sd_box(input.pos, input.params.xy);
return smud::sd_box(input.pos, input.bounds);
}
11 changes: 6 additions & 5 deletions assets/shapes/regular_polygon.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

#import smud

// Parametrized regular polygon SDF
// params.x contains the radius (circumradius)
// params.y contains the number of sides (as float, should be cast to int)
// Regular polygon SDF using bounds for radius
// Radius is computed as min(bounds.x, bounds.y)
// params.x contains the number of sides (as float, should be cast to int)
fn sdf(input: smud::SdfInput) -> f32 {
let sides = i32(input.params.y);
return smud::sd_regular_polygon(input.pos, input.params.x, sides);
let radius = min(input.bounds.x, input.bounds.y);
let sides = i32(input.params.x);
return smud::sd_regular_polygon(input.pos, radius, sides);
}
7 changes: 3 additions & 4 deletions assets/shapes/rhombus.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

#import smud

// Parametrized rhombus SDF
// params.x contains half of the horizontal diagonal
// params.y contains half of the vertical diagonal
// Rhombus SDF using bounds
// Uses input.bounds for the half-diagonals
fn sdf(input: smud::SdfInput) -> f32 {
return smud::sd_rhombus(input.pos, vec2<f32>(input.params.x, input.params.y));
return smud::sd_rhombus(input.pos, input.bounds);
}
159 changes: 111 additions & 48 deletions examples/bevy_primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,111 +6,174 @@ fn main() {
App::new()
.add_plugins((DefaultPlugins, SmudPlugin, SmudPickingPlugin))
.add_systems(Startup, setup)
.add_systems(Update, update_colors_on_hover)
.add_systems(Update, (animate_bounds, update_colors_on_hover))
.run();
}

fn setup(mut commands: Commands) {
// Spawn a camera
commands.spawn((Camera2d, Msaa::Off));

// Row 1: Default bounds (each shape uses its natural size)
commands.spawn((
Transform::from_translation(Vec3::new(-200., 100., 0.)),
SmudShape::from(Rectangle::new(100., 50.)).with_color(css::TOMATO),
Transform::from_translation(Vec3::new(-420., 150., 0.)),
SmudShape::from(Rectangle::new(50., 50.)).with_color(css::TOMATO),
OriginalColor(css::TOMATO.into()),
));

commands.spawn((
Transform::from_translation(Vec3::new(100., 100., 0.)),
SmudShape::from(Circle::new(40.)).with_color(css::CORNFLOWER_BLUE),
Transform::from_translation(Vec3::new(-300., 150., 0.)),
SmudShape::from(Circle::new(25.)).with_color(css::CORNFLOWER_BLUE),
OriginalColor(css::CORNFLOWER_BLUE.into()),
));

// Using struct initialization with spread operator
commands.spawn((
Transform::from_translation(Vec3::new(-200., -100., 0.)),
SmudShape {
color: css::LIMEGREEN.into(),
..SmudShape::from(Rectangle::new(120., 40.))
},
Transform::from_translation(Vec3::new(-180., 150., 0.)),
SmudShape::from(Ellipse::new(30., 30.)).with_color(css::VIOLET),
OriginalColor(css::VIOLET.into()),
));

// Rotated rectangle
commands.spawn((
Transform::from_translation(Vec3::new(100., -100., 0.))
.with_rotation(Quat::from_rotation_z(0.5)),
SmudShape::from(Rectangle::new(100., 50.)).with_color(css::ORANGE),
Transform::from_translation(Vec3::new(-60., 150., 0.)),
SmudShape::from(Annulus::new(15., 30.)).with_color(css::MAGENTA),
OriginalColor(css::MAGENTA.into()),
));

// Scaled rectangle
commands.spawn((
Transform::from_translation(Vec3::new(0., 0., 0.)).with_scale(Vec3::splat(1.5)),
SmudShape::from(Rectangle::new(60., 60.)).with_color(css::HOT_PINK),
Transform::from_translation(Vec3::new(60., 150., 0.)),
SmudShape::from(Capsule2d::new(10., 20.)).with_color(css::LIME),
OriginalColor(css::LIME.into()),
));

commands.spawn((
Transform::from_translation(Vec3::new(250., 0., 0.)),
SmudShape::from(Rectangle::new(50., 100.))
.with_color(css::YELLOW)
.with_fill(SIMPLE_FILL_HANDLE),
OriginalColor(css::YELLOW.into()),
Transform::from_translation(Vec3::new(180., 150., 0.)),
SmudShape::from(Rhombus::new(30., 30.)).with_color(css::GOLD),
OriginalColor(css::GOLD.into()),
));

// Ellipse
commands.spawn((
Transform::from_translation(Vec3::new(-250., 0., 0.)),
SmudShape::from(Ellipse::new(70., 40.)).with_color(css::VIOLET),
OriginalColor(css::VIOLET.into()),
Transform::from_translation(Vec3::new(300., 150., 0.)),
SmudShape::from(CircularSector::from_turns(35., 0.25)).with_color(css::ORANGE_RED),
OriginalColor(css::ORANGE_RED.into()),
));

commands.spawn((
Transform::from_translation(Vec3::new(420., 150., 0.)),
SmudShape::from(RegularPolygon::new(30., 6)).with_color(css::AQUA),
OriginalColor(css::AQUA.into()),
));

// Row 2: Animated bounds (shapes smoothly transition between tall and wide)
commands.spawn((
Transform::from_translation(Vec3::new(-420., -150., 0.)),
SmudShape {
bounds: Rectangle::new(70., 40.),
color: css::TOMATO.into(),
..SmudShape::from(Rectangle::new(50., 50.))
},
OriginalColor(css::TOMATO.into()),
AnimatedBounds,
));

commands.spawn((
Transform::from_translation(Vec3::new(-300., -150., 0.)),
SmudShape {
bounds: Rectangle::new(70., 40.),
color: css::CORNFLOWER_BLUE.into(),
..SmudShape::from(Circle::new(25.))
},
OriginalColor(css::CORNFLOWER_BLUE.into()),
AnimatedBounds,
));

// Rotated ellipse
commands.spawn((
Transform::from_translation(Vec3::new(0., 150., 0.))
.with_rotation(Quat::from_rotation_z(0.8)),
SmudShape::from(Ellipse::new(80., 30.)).with_color(css::TURQUOISE),
OriginalColor(css::TURQUOISE.into()),
Transform::from_translation(Vec3::new(-180., -150., 0.)),
SmudShape {
bounds: Rectangle::new(70., 40.),
color: css::VIOLET.into(),
..SmudShape::from(Ellipse::new(30., 30.))
},
OriginalColor(css::VIOLET.into()),
AnimatedBounds,
));

// Annulus (ring)
commands.spawn((
Transform::from_translation(Vec3::new(0., -150., 0.)),
SmudShape::from(Annulus::new(20., 50.)).with_color(css::MAGENTA),
Transform::from_translation(Vec3::new(-60., -150., 0.)),
SmudShape {
bounds: Rectangle::new(70., 40.),
color: css::MAGENTA.into(),
..SmudShape::from(Annulus::new(15., 30.))
},
OriginalColor(css::MAGENTA.into()),
AnimatedBounds,
));

// Capsule (pill shape)
commands.spawn((
Transform::from_translation(Vec3::new(-350., 0., 0.)),
SmudShape::from(Capsule2d::new(15., 40.)).with_color(css::LIME),
Transform::from_translation(Vec3::new(60., -150., 0.)),
SmudShape {
bounds: Rectangle::new(70., 40.),
color: css::LIME.into(),
..SmudShape::from(Capsule2d::new(10., 20.))
},
OriginalColor(css::LIME.into()),
AnimatedBounds,
));

// Rhombus (diamond)
commands.spawn((
Transform::from_translation(Vec3::new(350., 0., 0.)),
SmudShape::from(Rhombus::new(50., 30.)).with_color(css::GOLD),
Transform::from_translation(Vec3::new(180., -150., 0.)),
SmudShape {
bounds: Rectangle::new(70., 40.),
color: css::GOLD.into(),
..SmudShape::from(Rhombus::new(30., 30.))
},
OriginalColor(css::GOLD.into()),
AnimatedBounds,
));

// Circular sector (pie slice) - 90 degrees (quarter turn)
commands.spawn((
Transform::from_translation(Vec3::new(0., 200., 0.)),
SmudShape::from(CircularSector::from_turns(60., 0.25)).with_color(css::ORANGE_RED),
Transform::from_translation(Vec3::new(300., -150., 0.)),
SmudShape {
bounds: Rectangle::new(70., 40.),
color: css::ORANGE_RED.into(),
..SmudShape::from(CircularSector::from_turns(35., 0.25))
},
OriginalColor(css::ORANGE_RED.into()),
AnimatedBounds,
));

// Regular polygon - hexagon (6 sides)
commands.spawn((
Transform::from_translation(Vec3::new(250., -150., 0.)),
SmudShape::from(RegularPolygon::new(50., 6)).with_color(css::AQUA),
Transform::from_translation(Vec3::new(420., -150., 0.)),
SmudShape {
bounds: Rectangle::new(70., 40.),
color: css::AQUA.into(),
..SmudShape::from(RegularPolygon::new(30., 6))
},
OriginalColor(css::AQUA.into()),
AnimatedBounds,
));
}

// Component to store the original color for restoring after hover
#[derive(Component)]
struct OriginalColor(Color);

// Marker component for shapes with animated bounds
#[derive(Component)]
struct AnimatedBounds;

// System to animate bounds based on time
fn animate_bounds(time: Res<Time>, mut shapes: Query<&mut SmudShape, With<AnimatedBounds>>) {
let speed = 2.0;
let t = time.elapsed_secs() * speed;

// Use sine and cosine to smoothly transition between tall and wide
let width = 55.0 + 25.0 * t.sin(); // Ranges from 30 to 80
let height = 55.0 - 25.0 * t.cos(); // Ranges from 30 to 80, offset from width

for mut shape in &mut shapes {
shape.bounds = Rectangle::new(width, height);
}
}

// System to change colors on hover
fn update_colors_on_hover(
mut shapes: Query<(&mut SmudShape, &OriginalColor, &PickingInteraction)>,
Expand Down
Loading