Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rounded corners on rect Issue #849, absolute values on height and width #887

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion examples/draw/draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,19 @@ fn view(app: &App, frame: Frame) {
.rotate(t);

// Draw a rect that follows a different inverse of the ellipse.
draw.rect()
if app.mouse.y > 0.0 {
draw.rect()
.x_y(app.mouse.y, app.mouse.x)
.w(app.mouse.x * 0.25)
.corner_radius(app.mouse.x * 0.05)
.hsv(t, 1.0, 1.0);
} else {
draw.rect()
.x_y(app.mouse.y, app.mouse.x)
.w(app.mouse.x * 0.25)
.hsv(t, 1.0, 1.0);
}


// Write the result of our drawing to the window's frame.
draw.to_frame(app, &frame).unwrap();
Expand Down
79 changes: 71 additions & 8 deletions nannou/src/draw/primitive/rect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@ use crate::draw::properties::{
use crate::draw::{self, Drawing};
use crate::geom;
use crate::glam::Vec2;
use lyon::math::Point;
use lyon::tessellation::StrokeOptions;
use nannou_core::geom::point;
use nannou_core::prelude::{Vec2Rotate, abs};
use lyon::path::builder::SvgPathBuilder;

/// Properties related to drawing a **Rect**.
#[derive(Clone, Debug)]
pub struct Rect {
dimensions: dimension::Properties,
polygon: PolygonInit,
corner_radius: Option<f32>,
}

/// The drawing context for a Rect.
Expand All @@ -30,8 +35,15 @@ impl Rect {
{
self.stroke_color(color)
}

pub fn corner_radius(mut self, radius: f32) -> Self {
self.corner_radius = Some(abs(radius));
self
}

}


impl<'a> DrawingRect<'a> {
/// Stroke the outline with the given color.
pub fn stroke<C>(self, color: C) -> Self
Expand All @@ -40,6 +52,12 @@ impl<'a> DrawingRect<'a> {
{
self.map_ty(|ty| ty.stroke(color))
}


pub fn corner_radius(self, radius: f32) -> Self {
self.map_ty(|ty| ty.corner_radius(radius))
}

}

impl draw::renderer::RenderPrimitive for Rect {
Expand All @@ -51,26 +69,69 @@ impl draw::renderer::RenderPrimitive for Rect {
let Rect {
polygon,
dimensions,
corner_radius,
} = self;



// If dimensions were specified, scale the points to those dimensions.
let (maybe_x, maybe_y, maybe_z) = (dimensions.x, dimensions.y, dimensions.z);
assert!(
maybe_z.is_none(),
"z dimension support for rect is unimplemented"
);

let w = maybe_x.unwrap_or(100.0);
let h = maybe_y.unwrap_or(100.0);

let rect = geom::Rect::from_wh([w, h].into());
let points = rect.corners().vertices().map(Vec2::from);
polygon::render_points_themed(
polygon.opts,
points,
ctxt,
&draw::theme::Primitive::Rect,
mesh,
);

match corner_radius {
Some(radius) => {
let mut builder = lyon::path::Path::svg_builder();

// unit vector parallel to segment, used for positioning arc points
let mut segment_vector = Vec2::from([0.0, 1.0]);
let starting_point = rect.bottom_left() + segment_vector * radius;
// start at lower left corner.
builder.move_to (Point::new(starting_point.x, starting_point.y));
let mut rotation = lyon::geom::Angle::radians(0.0);

for vertex in rect.corners().vertices().map(Vec2::from) {
let arc_start = vertex - segment_vector * radius;
builder.line_to(Point::new(arc_start.x, arc_start.y));
// rotate segment vector by 90 degrees to the right
segment_vector = segment_vector.rotate(-std::f32::consts::PI/2.0);
let arc_end = vertex + segment_vector * radius;
builder.arc_to(
lyon::math::Vector::from([radius, radius]),
rotation,
lyon::geom::ArcFlags { large_arc: false, sweep: false },
Point::new(arc_end.x, arc_end.y)
);
rotation += lyon::geom::Angle::frac_pi_2();

}
let path = builder.build();
polygon::render_events_themed(
polygon.opts,
|| (&path).into_iter(),
ctxt,
&draw::theme::Primitive::Ellipse,
mesh,
);
}
None => {
let points = rect.corners().vertices().map(Vec2::from);
polygon::render_points_themed(
polygon.opts,
points.into_iter(),
ctxt,
&draw::theme::Primitive::Rect,
mesh,
);
}
}
draw::renderer::PrimitiveRender::default()
}
}
Expand All @@ -89,6 +150,7 @@ impl Default for Rect {
Rect {
dimensions,
polygon,
corner_radius: None,
}
}
}
Expand Down Expand Up @@ -129,6 +191,7 @@ impl SetPolygon for Rect {
}
}


// Primitive conversions.

impl From<Rect> for Primitive {
Expand Down
7 changes: 5 additions & 2 deletions nannou/src/draw/properties/spatial/dimension.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use nannou_core::prelude::abs;

use crate::glam::{Vec2, Vec3};


/// Dimension properties for **Drawing** a **Primitive**.
#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub struct Properties {
Expand All @@ -15,13 +18,13 @@ pub trait SetDimensions: Sized {

/// Set the absolute width for the primitive.
fn width(mut self, w: f32) -> Self {
self.properties().x = Some(w);
self.properties().x = Some(abs(w));
self
}

/// Set the absolute height for the primitive.
fn height(mut self, h: f32) -> Self {
self.properties().y = Some(h);
self.properties().y = Some(abs(h));
self
}

Expand Down