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

Implement draw.polygon() API #148

Merged
merged 10 commits into from
Jun 10, 2018
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
- Add better `panic!` message to `map_range` if cast fails.
- Add many items to prelude (audio, io, math, osc, ui, window).
- Change event positioning types to use DefaultScalar.
- Implement `draw.polygon()`
- Update internal `IntoDrawn` API to support a dynamic number of arbitrary
vertices.
- Update `Drawing` API to allow builders to produce new `Drawing` types.

# Version 0.6.0 (2017-06-07)

Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ path = "examples/simple_audio.rs"
name = "simple_draw"
path = "examples/simple_draw.rs"
[[example]]
name = "simple_polygon"
path = "examples/simple_polygon.rs"
[[example]]
name = "simple_ui"
path = "examples/simple_ui.rs"
[[example]]
Expand Down
60 changes: 60 additions & 0 deletions examples/simple_polygon.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
extern crate nannou;

use nannou::prelude::*;

fn main() {
nannou::view(view);
}

fn view(app: &App, frame: Frame) -> Frame {
// Begin drawing
let win = app.window_rect();
let t = app.time;
let draw = app.draw();

// Clear the background to blue.
draw.background().color(BLACK);

// Create an `ngon` of points.
let n_points = 5;
let radius = win.w().min(win.h()) * 0.25;
let points = (0..n_points)
.map(|i| {
let fract = i as f32 / n_points as f32;
let phase = fract;
let x = radius * (TAU * phase).cos();
let y = radius * (TAU * phase).sin();
pt3(x, y, 0.0)
});
draw.polygon()
.points(points)
.x(-win.w() * 0.25)
.color(WHITE)
.rotate(-t * 0.1);

// Do the same, but give each point a unique colour.
let n_points = 7;
let colored_points = (0..n_points)
.map(|i| {
let fract = i as f32 / n_points as f32;
let phase = fract;
let x = radius * (TAU * phase).cos();
let y = radius * (TAU * phase).sin();
let r = fract;
let g = 1.0 - fract;
let b = (0.5 + fract) % 1.0;
let a = 1.0;
let color = Rgba::new(r, g, b, a);
(pt3(x, y, 0.0), color)
});
draw.polygon()
.colored_points(colored_points)
.x(win.w() * 0.25)
.rotate(t * 0.2);

// Write the result of our drawing to the window's OpenGL frame.
draw.to_frame(app, &frame).unwrap();

// Return the drawn frame.
frame
}
64 changes: 57 additions & 7 deletions src/draw/drawing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ where
// **Drawing** may yet describe further positioning, orientation or scaling and in turn using
// the index to refer to a node before these properties are set may yield unexpected behaviour.
index: node::Index,
// Whether or not the **Drawing** should attempt to finish the drawing on drop.
finish_on_drop: bool,
// The node type currently being drawn.
_ty: PhantomData<T>,
}
Expand All @@ -41,7 +43,8 @@ where
S: BaseFloat,
{
let _ty = PhantomData;
Drawing { draw, index, _ty }
let finish_on_drop = true;
Drawing { draw, index, finish_on_drop, _ty }
}

impl<'a, T, S> Drop for Drawing<'a, T, S>
Expand All @@ -50,8 +53,11 @@ where
S: BaseFloat,
{
fn drop(&mut self) {
self.finish_inner()
.expect("the drawing contained a relative edge that would have caused a cycle within the geometry graph");
if self.finish_on_drop {
self.finish_inner()
.expect("the drawing contained a relative edge that would have \
caused a cycle within the geometry graph");
}
}
}

Expand Down Expand Up @@ -93,28 +99,53 @@ where
// Map the given function onto the primitive stored within **Draw** at `index`.
//
// The functionn is only applied if the node has not yet been **Drawn**.
fn map_primitive<F>(self, map: F) -> Self
fn map_primitive<F, T2>(mut self, map: F) -> Drawing<'a, T2, S>
where
F: FnOnce(draw::properties::Primitive<S>) -> draw::properties::Primitive<S>,
T2: IntoDrawn<S> + Into<Primitive<S>>,
{
if let Ok(mut state) = self.draw.state.try_borrow_mut() {
if let Some(mut primitive) = state.drawing.remove(&self.index) {
primitive = map(primitive);
state.drawing.insert(self.index, primitive);
}
}
self
self.finish_on_drop = false;
let Drawing { draw, index, .. } = self;
Drawing { draw, index, finish_on_drop: true, _ty: PhantomData }
}

// The same as `map_primitive` but also passes a mutable reference to the vertex data to the
// map function. This is useful for types that may have an unknown number of arbitrary
// vertices.
fn map_primitive_with_vertices<F, T2>(mut self, map: F) -> Drawing<'a, T2, S>
where
F: FnOnce(draw::properties::Primitive<S>, &mut draw::GeomVertexData<S>) -> draw::properties::Primitive<S>,
T2: IntoDrawn<S> + Into<Primitive<S>>,
{
if let Ok(mut state) = self.draw.state.try_borrow_mut() {
if let Some(mut primitive) = state.drawing.remove(&self.index) {
{
let mut geom_vertex_data = state.geom_vertex_data.borrow_mut();
primitive = map(primitive, &mut *geom_vertex_data);
}
state.drawing.insert(self.index, primitive);
}
}
self.finish_on_drop = false;
let Drawing { draw, index, .. } = self;
Drawing { draw, index, finish_on_drop: true, _ty: PhantomData }
}

/// Apply the given function to the type stored within **Draw**.
///
/// The function is only applied if the node has not yet been **Drawn**.
///
/// **Panics** if the primitive does not contain type **T**.
pub fn map_ty<F, T2>(self, map: F) -> Self
pub fn map_ty<F, T2>(self, map: F) -> Drawing<'a, T2, S>
where
F: FnOnce(T) -> T2,
T2: Into<Primitive<S>>,
T2: IntoDrawn<S> + Into<Primitive<S>>,
Primitive<S>: Into<Option<T>>,
{
self.map_primitive(|prim| {
Expand All @@ -124,6 +155,25 @@ where
ty2.into()
})
}

/// Apply the given function to the type stored within **Draw**.
///
/// The function is only applied if the node has not yet been **Drawn**.
///
/// **Panics** if the primitive does not contain type **T**.
pub(crate) fn map_ty_with_vertices<F, T2>(self, map: F) -> Drawing<'a, T2, S>
where
F: FnOnce(T, &mut draw::GeomVertexData<S>) -> T2,
T2: IntoDrawn<S> + Into<Primitive<S>>,
Primitive<S>: Into<Option<T>>,
{
self.map_primitive_with_vertices(|prim, v_data| {
let maybe_ty: Option<T> = prim.into();
let ty = maybe_ty.expect("expected `T` but primitive contained different type");
let ty2 = map(ty, v_data);
ty2.into()
})
}
}

// SetColor implementations.
Expand Down
63 changes: 32 additions & 31 deletions src/draw/mesh/vertex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub type Point<S> = Point3<S>;
pub type Color = color::Rgba;
pub type TexCoords<S> = Point2<S>;
pub type Normal<S> = Vector3<S>;
pub type ColoredPoint<S> = WithColor<Point<S>, Color>;

/// Types that can be converted into a `draw::mesh::vertex::Point`.
pub trait IntoPoint<S> {
Expand Down Expand Up @@ -166,6 +167,20 @@ pub struct IterFromPoints<I, S = geom::DefaultScalar> {
_scalar: PhantomData<S>,
}

/// A type that converts an iterator yielding 2D points to an iterator yielding **Vertex**s.
///
/// The `z` position for each vertex will be `0.0`.
///
/// The given `default_color` is used to color every vertex.
///
/// The default value of `(0.0, 0.0)` is used for tex_coords.
#[derive(Clone, Debug)]
pub struct IterFromPoint2s<I, S = geom::DefaultScalar> {
points: I,
default_color: Color,
_scalar: PhantomData<S>,
}

impl<I, S> IterFromPoints<I, S> {
/// Produce an iterator that converts an iterator yielding points to an iterator yielding
/// **Vertex**s.
Expand All @@ -188,37 +203,6 @@ impl<I, S> IterFromPoints<I, S> {
}
}

impl<I, S> Iterator for IterFromPoints<I, S>
where
I: Iterator<Item = Point<S>>,
S: BaseFloat,
{
type Item = Vertex<S>;
fn next(&mut self) -> Option<Self::Item> {
self.points.next().map(|vertex| {
let color = self.default_color;
let vertex = WithColor { vertex, color };
let tex_coords = default_tex_coords();
let vertex = WithTexCoords { vertex, tex_coords };
vertex
})
}
}

/// A type that converts an iterator yielding 2D points to an iterator yielding **Vertex**s.
///
/// The `z` position for each vertex will be `0.0`.
///
/// The given `default_color` is used to color every vertex.
///
/// The default value of `(0.0, 0.0)` is used for tex_coords.
#[derive(Clone, Debug)]
pub struct IterFromPoint2s<I, S = geom::DefaultScalar> {
points: I,
default_color: Color,
_scalar: PhantomData<S>,
}

impl<I, S> IterFromPoint2s<I, S> {
/// A type that converts an iterator yielding 2D points to an iterator yielding **Vertex**s.
///
Expand All @@ -242,6 +226,23 @@ impl<I, S> IterFromPoint2s<I, S> {
}
}

impl<I, S> Iterator for IterFromPoints<I, S>
where
I: Iterator<Item = Point<S>>,
S: BaseFloat,
{
type Item = Vertex<S>;
fn next(&mut self) -> Option<Self::Item> {
self.points.next().map(|vertex| {
let color = self.default_color;
let vertex = WithColor { vertex, color };
let tex_coords = default_tex_coords();
let vertex = WithTexCoords { vertex, tex_coords };
vertex
})
}
}

impl<I, S> Iterator for IterFromPoint2s<I, S>
where
I: Iterator<Item = Point2<S>>,
Expand Down
Loading