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

Add a Draw::line method along with custom line builders to Drawing #98

Merged
merged 1 commit into from
Apr 1, 2018
Merged
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
7 changes: 7 additions & 0 deletions examples/simple_draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ fn view(app: &App, frame: Frame) -> Frame {
.radius(app.window.width * 0.125 * t.sin())
.color(RED);

// Draw a line!
draw.line()
.start(win.top_left() * t.sin())
.end(win.bottom_right() * t.cos())
.thickness(win.h() / (50.0 * t.sin()))
.color(DARK_BLUE);

// Draw a quad that follows the inverse of the ellipse.
draw.quad().x_y(-app.mouse.x, app.mouse.y).color(DARK_GREEN);

Expand Down
8 changes: 8 additions & 0 deletions src/draw/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,9 @@ where
Primitive::Ellipse(prim) => {
into_drawn(draw, node_index, prim)
},
Primitive::Line(prim) => {
into_drawn(draw, node_index, prim)
},
Primitive::Quad(prim) => {
into_drawn(draw, node_index, prim)
}
Expand Down Expand Up @@ -452,6 +455,11 @@ where
self.a(Default::default())
}

/// Begin drawing a **Line**.
pub fn line(&self) -> Drawing<properties::Line<S>, S> {
self.a(Default::default())
}

/// Begin drawing a **Quad**.
pub fn quad(&self) -> Drawing<properties::Quad<S>, S> {
self.a(Default::default())
Expand Down
2 changes: 1 addition & 1 deletion src/draw/properties/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub mod spatial;
use self::spatial::dimension;

pub use self::color::{IntoRgba, SetColor};
pub use self::primitive::{Ellipse, Quad, Primitive, Rect, Tri};
pub use self::primitive::{Ellipse, Line, Quad, Primitive, Rect, Tri};
pub use self::spatial::dimension::SetDimensions;
pub use self::spatial::position::SetPosition;

Expand Down
216 changes: 216 additions & 0 deletions src/draw/properties/primitive/line.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
use draw::{self, Drawing};
use draw::properties::{spatial, ColorScalar, Draw, Drawn, IntoDrawn, Primitive, Rgba, SetColor, SetDimensions, SetPosition};
use draw::properties::spatial::{dimension, position};
use geom;
use math::{BaseFloat, ElementWise, Point2, Vector2};
use std::{iter, slice};

/// Properties related to drawing a **Line**.
#[derive(Clone, Debug)]
pub struct Line<S = geom::DefaultScalar> {
line: geom::Line<S>,
spatial: spatial::Properties<S>,
color: Option<Rgba>,
}
// Line-specific methods.

impl<S> Line<S>
where
S: BaseFloat,
{
/// Create a new **Line**. from its geometric parts.
pub fn new(start: Point2<S>, end: Point2<S>, half_thickness: S) -> Self {
let color = Default::default();
let spatial = Default::default();
let line = geom::Line::new(start, end, half_thickness);
Line { line, spatial, color }
}

/// Specify the thickness of the **Line**.
pub fn thickness(self, thickness: S) -> Self {
self.half_thickness(thickness / (S::one() + S::one()))
}

/// Specify half the thickness of the **Line**.
///
/// As the half-thickness is used more commonly within **Line** geometric calculations, this
/// can be *slightly* more efficient than the full `thickness` method.
pub fn half_thickness(mut self, half_thickness: S) -> Self {
self.line.half_thickness = half_thickness;
self
}

/// Specify the `start` point for the line.
pub fn start(mut self, start: Point2<S>) -> Self {
self.line.start = start;
self
}

/// Specify the `end` point for the line.
pub fn end(mut self, end: Point2<S>) -> Self {
self.line.end = end;
self
}

/// Use the given four points as the vertices (corners) of the quad.
pub fn points(self, start: Point2<S>, end: Point2<S>) -> Self {
self.start(start).end(end)
}
}

// Trait implementations.

impl<S> IntoDrawn<S> for Line<S>
where
S: BaseFloat,
{
type Vertices = draw::mesh::vertex::IterFromPoint2s<geom::line::Vertices<S>, S>;
type Indices = iter::Cloned<slice::Iter<'static, usize>>;
fn into_drawn(self, draw: Draw<S>) -> Drawn<S, Self::Vertices, Self::Indices> {
let Line {
line,
spatial,
color,
} = self;

// If dimensions were specified, scale the points to those dimensions.
let (maybe_x, maybe_y, maybe_z) = spatial.dimensions.to_scalars(&draw);
assert!(
maybe_z.is_none(),
"z dimension support for ellipse is unimplemented"
);

let mut quad = line.quad_corners();
if maybe_x.is_some() || maybe_y.is_some() {
let rect = line.bounding_rect();
let centroid = line.centroid();
let x_scale = maybe_x.map(|x| x / rect.w()).unwrap_or_else(S::one);
let y_scale = maybe_y.map(|y| y / rect.h()).unwrap_or_else(S::one);
let scale = Vector2 { x: x_scale, y: y_scale };
let (a, b, c, d) = quad.into();
let translate = |v: Point2<S>| centroid + ((v - centroid).mul_element_wise(scale));
let new_a = translate(a);
let new_b = translate(b);
let new_c = translate(c);
let new_d = translate(d);
quad = geom::Quad([new_a, new_b, new_c, new_d]);
}

// The color.
let color = color
.or_else(|| {
draw.theme(|theme| {
theme
.color
.primitive
.get(&draw::theme::Primitive::Line)
.map(|&c| c)
})
})
.unwrap_or(draw.theme(|t| t.color.default));

let points = quad.vertices();
let vertices = draw::mesh::vertex::IterFromPoint2s::new(points, color);
let indices = geom::quad::TRIANGLE_INDICES.iter().cloned();

(spatial, vertices, indices)
}
}

impl<S> From<geom::Line<S>> for Line<S>
where
S: BaseFloat,
{
fn from(line: geom::Line<S>) -> Self {
let spatial = <_>::default();
let color = <_>::default();
Line { line, spatial, color }
}
}

impl<S> Default for Line<S>
where
S: BaseFloat,
{
fn default() -> Self {
// Create a quad pointing towards 0.0 radians.
let zero = S::zero();
let fifty = S::from(50.0).unwrap();
let half_thickness = S::one();
let left = -fifty;
let right = fifty;
let a = Point2 { x: left, y: zero };
let b = Point2 { x: right, y: zero };
Line::new(a, b, half_thickness)
}
}

impl<S> SetPosition<S> for Line<S> {
fn properties(&mut self) -> &mut position::Properties<S> {
SetPosition::properties(&mut self.spatial)
}
}

impl<S> SetDimensions<S> for Line<S> {
fn properties(&mut self) -> &mut dimension::Properties<S> {
SetDimensions::properties(&mut self.spatial)
}
}

impl<S> SetColor<ColorScalar> for Line<S> {
fn rgba_mut(&mut self) -> &mut Option<Rgba> {
SetColor::rgba_mut(&mut self.color)
}
}

// Primitive conversions.

impl<S> From<Line<S>> for Primitive<S> {
fn from(prim: Line<S>) -> Self {
Primitive::Line(prim)
}
}

impl<S> Into<Option<Line<S>>> for Primitive<S> {
fn into(self) -> Option<Line<S>> {
match self {
Primitive::Line(prim) => Some(prim),
_ => None,
}
}
}

// Drawing methods.

impl<'a, S> Drawing<'a, Line<S>, S>
where
S: BaseFloat,
{
/// Specify the thickness of the **Line**.
pub fn thickness(self, thickness: S) -> Self {
self.map_ty(|ty| ty.thickness(thickness))
}

/// Specify half the thickness of the **Line**.
///
/// As the half-thickness is used more commonly within **Line** geometric calculations, this
/// can be *slightly* more efficient than the full `thickness` method.
pub fn half_thickness(self, half_thickness: S) -> Self {
self.map_ty(|ty| ty.half_thickness(half_thickness))
}

/// Specify the `start` point for the line.
pub fn start(self, start: Point2<S>) -> Self {
self.map_ty(|ty| ty.start(start))
}

/// Specify the `end` point for the line.
pub fn end(self, end: Point2<S>) -> Self {
self.map_ty(|ty| ty.end(end))
}

/// Use the given four points as the vertices (corners) of the quad.
pub fn points(self, start: Point2<S>, end: Point2<S>) -> Self {
self.map_ty(|ty| ty.points(start, end))
}
}
3 changes: 3 additions & 0 deletions src/draw/properties/primitive/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use geom;

pub mod ellipse;
pub mod line;
pub mod quad;
pub mod rect;
pub mod tri;

pub use self::ellipse::Ellipse;
pub use self::line::Line;
pub use self::quad::Quad;
pub use self::rect::Rect;
pub use self::tri::Tri;
Expand All @@ -18,6 +20,7 @@ pub use self::tri::Tri;
#[derive(Clone, Debug)]
pub enum Primitive<S = geom::DefaultScalar> {
Ellipse(Ellipse<S>),
Line(Line<S>),
Quad(Quad<S>),
Rect(Rect<S>),
Tri(Tri<S>),
Expand Down
18 changes: 9 additions & 9 deletions src/geom/line.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use geom::{quad, tri, vertex, DefaultScalar};
use geom::{quad, tri, vertex, DefaultScalar, Rect};
use math::{two, vec2, BaseFloat, EuclideanSpace, InnerSpace, Point2};

/// The quad used to describe a line.
Expand Down Expand Up @@ -34,12 +34,12 @@ where
pub fn centroid(&self) -> Point2<S> {
EuclideanSpace::midpoint(self.start, self.end)
}
}

impl<S> Line<S>
where
S: BaseFloat,
{
/// The bounding **Rect** of the **Line** including thickness and line caps.
pub fn bounding_rect(&self) -> Rect<S> {
self.quad_corners().bounding_rect()
}

/// The four corners of the rectangle describing the line.
pub fn quad_corners(&self) -> Quad<S> {
quad_corners(self.start, self.end, self.half_thickness)
Expand Down Expand Up @@ -93,9 +93,9 @@ where
let n = normal.normalize_to(half_thickness);
let neg_n = n * neg_1;
let r1 = a + neg_n;
let r2 = a + n;
let r3 = b + neg_n;
let r4 = b + n;
let r2 = b + neg_n;
let r3 = b + n;
let r4 = a + n;
Quad::from([r1, r2, r3, r4])
}

Expand Down
2 changes: 1 addition & 1 deletion src/geom/quad.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ where
triangles_iter(self)
}

/// The bounding `Rect` of the triangle.
/// The bounding `Rect` of the quad.
pub fn bounding_rect(self) -> Rect<V::Scalar>
where
V: Vertex2d,
Expand Down