diff --git a/CHANGELOG.md b/CHANGELOG.md index e3b078380..1b72f0fc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/Cargo.toml b/Cargo.toml index 1205404f4..3daf15a3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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]] diff --git a/examples/simple_polygon.rs b/examples/simple_polygon.rs new file mode 100644 index 000000000..b65857c6f --- /dev/null +++ b/examples/simple_polygon.rs @@ -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 +} diff --git a/src/draw/drawing.rs b/src/draw/drawing.rs index 8c149c2e8..7d4ffc36c 100644 --- a/src/draw/drawing.rs +++ b/src/draw/drawing.rs @@ -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, } @@ -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> @@ -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"); + } } } @@ -93,9 +99,10 @@ 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(self, map: F) -> Self + fn map_primitive(mut self, map: F) -> Drawing<'a, T2, S> where F: FnOnce(draw::properties::Primitive) -> draw::properties::Primitive, + T2: IntoDrawn + Into>, { if let Ok(mut state) = self.draw.state.try_borrow_mut() { if let Some(mut primitive) = state.drawing.remove(&self.index) { @@ -103,7 +110,31 @@ where 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(mut self, map: F) -> Drawing<'a, T2, S> + where + F: FnOnce(draw::properties::Primitive, &mut draw::GeomVertexData) -> draw::properties::Primitive, + T2: IntoDrawn + Into>, + { + 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**. @@ -111,10 +142,10 @@ where /// 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(self, map: F) -> Self + pub fn map_ty(self, map: F) -> Drawing<'a, T2, S> where F: FnOnce(T) -> T2, - T2: Into>, + T2: IntoDrawn + Into>, Primitive: Into>, { self.map_primitive(|prim| { @@ -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(self, map: F) -> Drawing<'a, T2, S> + where + F: FnOnce(T, &mut draw::GeomVertexData) -> T2, + T2: IntoDrawn + Into>, + Primitive: Into>, + { + self.map_primitive_with_vertices(|prim, v_data| { + let maybe_ty: Option = prim.into(); + let ty = maybe_ty.expect("expected `T` but primitive contained different type"); + let ty2 = map(ty, v_data); + ty2.into() + }) + } } // SetColor implementations. diff --git a/src/draw/mesh/vertex.rs b/src/draw/mesh/vertex.rs index 5dbc4f579..84438473d 100644 --- a/src/draw/mesh/vertex.rs +++ b/src/draw/mesh/vertex.rs @@ -8,6 +8,7 @@ pub type Point = Point3; pub type Color = color::Rgba; pub type TexCoords = Point2; pub type Normal = Vector3; +pub type ColoredPoint = WithColor, Color>; /// Types that can be converted into a `draw::mesh::vertex::Point`. pub trait IntoPoint { @@ -166,6 +167,20 @@ pub struct IterFromPoints { _scalar: PhantomData, } +/// 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 { + points: I, + default_color: Color, + _scalar: PhantomData, +} + impl IterFromPoints { /// Produce an iterator that converts an iterator yielding points to an iterator yielding /// **Vertex**s. @@ -188,37 +203,6 @@ impl IterFromPoints { } } -impl Iterator for IterFromPoints -where - I: Iterator>, - S: BaseFloat, -{ - type Item = Vertex; - fn next(&mut self) -> Option { - 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 { - points: I, - default_color: Color, - _scalar: PhantomData, -} - impl IterFromPoint2s { /// A type that converts an iterator yielding 2D points to an iterator yielding **Vertex**s. /// @@ -242,6 +226,23 @@ impl IterFromPoint2s { } } +impl Iterator for IterFromPoints +where + I: Iterator>, + S: BaseFloat, +{ + type Item = Vertex; + fn next(&mut self) -> Option { + 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 Iterator for IterFromPoint2s where I: Iterator>, diff --git a/src/draw/mod.rs b/src/draw/mod.rs index 53319cd6a..7064b5424 100644 --- a/src/draw/mod.rs +++ b/src/draw/mod.rs @@ -67,6 +67,8 @@ where geom_graph: geom::Graph, /// For performing a depth-first search over the geometry graph. geom_graph_dfs: RefCell>, + /// Buffers of vertex data that may be re-used for polylines, polygons, etc between view calls. + geom_vertex_data: RefCell>, /// The mesh containing vertices for all drawn shapes, etc. mesh: Mesh, /// The map from node indices to their vertex and index ranges within the mesh. @@ -81,6 +83,46 @@ where background_color: Option, } +/// A set of intermediary buffers for collecting geometry point data for geometry types that may +/// produce a dynamic number of vertices that may or not also contain colour or texture data. +#[derive(Clone, Debug)] +pub struct GeomVertexData { + pub(crate) points: Vec>, + pub(crate) colors: Vec, + pub(crate) tex_coords: Vec>, +} + +/// A set of ranges into the **GeomVertexData**. +/// +/// This allows polygons, polylines, etc to track which slices of data are associated with their +/// own instance. +#[derive(Clone, Debug)] +pub(crate) struct GeomVertexDataRanges { + pub points: ops::Range, + pub colors: ops::Range, + pub tex_coords: ops::Range, +} + +impl Default for GeomVertexData { + fn default() -> Self { + GeomVertexData { + points: Default::default(), + colors: Default::default(), + tex_coords: Default::default(), + } + } +} + +impl Default for GeomVertexDataRanges { + fn default() -> Self { + GeomVertexDataRanges { + points: 0..0, + colors: 0..0, + tex_coords: 0..0, + } + } +} + /// The vertex and index ranges into a mesh for a particular node. #[derive(Clone, Debug)] struct Ranges { @@ -259,7 +301,17 @@ where let vertices_start_index = draw.mesh.raw_vertex_count(); let indices_start_index = draw.mesh.indices().len(); let indices = indices.into_iter().map(|i| vertices_start_index + i); - draw.mesh.extend(vertices, indices); + + { + let State { + ref mut mesh, + ref mut geom_vertex_data, + .. + } = *draw; + let data = &mut *geom_vertex_data.borrow_mut(); + let vertices = properties::Vertices::into_iter(vertices, data); + mesh.extend(vertices, indices); + } // Update the **Draw**'s range map. let vertices_end_index = draw.mesh.raw_vertex_count(); @@ -327,6 +379,15 @@ where Primitive::Line(prim) => { into_drawn(draw, node_index, prim) }, + Primitive::PolygonPointless(_) => { + Ok(()) + }, + Primitive::PolygonFill(prim) => { + into_drawn(draw, node_index, prim) + }, + Primitive::PolygonColorPerVertex(prim) => { + into_drawn(draw, node_index, prim) + }, Primitive::Quad(prim) => { into_drawn(draw, node_index, prim) } @@ -357,6 +418,15 @@ where }) } +impl GeomVertexData { + /// Clears all buffers. + pub fn reset(&mut self) { + self.points.clear(); + self.colors.clear(); + self.tex_coords.clear(); + } +} + impl State where S: BaseFloat, @@ -367,6 +437,7 @@ where self.geom_graph_dfs.borrow_mut().reset(&self.geom_graph); self.drawing.clear(); self.ranges.clear(); + self.geom_vertex_data.borrow_mut().reset(); self.mesh.clear(); self.background_color = None; self.last_node_drawn = None; @@ -524,6 +595,11 @@ where self.a(Default::default()) } + /// Begin drawing a **Polygon**. + pub fn polygon(&self) -> Drawing { + self.a(Default::default()) + } + /// Produce the transformed mesh vertices for the node at the given index. /// /// Returns **None** if there is no node for the given index. @@ -678,6 +754,7 @@ where let geom_graph = Default::default(); let geom_graph_dfs = RefCell::new(geom::graph::node::Dfs::new(&geom_graph)); let drawing = Default::default(); + let geom_vertex_data = RefCell::new(Default::default()); let mesh = Default::default(); let ranges = Default::default(); let theme = Default::default(); @@ -686,6 +763,7 @@ where State { geom_graph, geom_graph_dfs, + geom_vertex_data, mesh, drawing, ranges, diff --git a/src/draw/properties/mod.rs b/src/draw/properties/mod.rs index 2094b1380..59626696b 100644 --- a/src/draw/properties/mod.rs +++ b/src/draw/properties/mod.rs @@ -119,6 +119,21 @@ where } } +/// Similar to the `Iterator` trait, but provides access to the **GeomVertexData** on each call to +/// the **next** method. +pub trait Vertices: Sized { + /// Return the next **Vertex** within the sequence. + fn next(&mut self, data: &mut draw::GeomVertexData) -> Option>; + + /// Converts `self` and the given `data` into an iterator yielding vertices. + fn into_iter(self, data: &mut draw::GeomVertexData) -> IterVertices { + IterVertices { + vertices: self, + data, + } + } +} + /// Types that can be **Drawn** into a parent **Draw** geometry graph and mesh. pub trait IntoDrawn where @@ -128,13 +143,20 @@ where /// /// The position of each yielded vertex should be relative to `0, 0, 0` as all displacement, /// scaling and rotation transformations will be performed via the geometry graph. - type Vertices: IntoIterator>; + type Vertices: Vertices; /// The iterator type yielding all vertex indices, describing edges of the drawing. type Indices: IntoIterator; /// Consume `self` and return its **Drawn** form. fn into_drawn(self, Draw) -> Drawn; } +/// An iterator adaptor around a type implementing the **Vertices** trait and the +/// **GeomVertexData** necessary for producing vertices. +pub struct IterVertices<'a, V, S: 'a> { + vertices: V, + data: &'a mut draw::GeomVertexData, +} + // Implement a method to simplify retrieving the dimensions of a type from `dimension::Properties`. impl dimension::Properties @@ -156,3 +178,26 @@ where (x, y, z) } } + +impl Vertices for I +where + I: Iterator>, +{ + fn next(&mut self, _data: &mut draw::GeomVertexData) -> Option> { + self.next() + } +} + +impl<'a, V, S> Iterator for IterVertices<'a, V, S> +where + V: Vertices, +{ + type Item = draw::mesh::Vertex; + fn next(&mut self) -> Option { + let IterVertices { + ref mut vertices, + ref mut data, + } = *self; + Vertices::next(vertices, *data) + } +} diff --git a/src/draw/properties/primitive/mod.rs b/src/draw/properties/primitive/mod.rs index e76dfc38e..33608206e 100644 --- a/src/draw/properties/primitive/mod.rs +++ b/src/draw/properties/primitive/mod.rs @@ -2,12 +2,14 @@ use geom; pub mod ellipse; pub mod line; +pub mod polygon; pub mod quad; pub mod rect; pub mod tri; pub use self::ellipse::Ellipse; pub use self::line::Line; +pub use self::polygon::Polygon; pub use self::quad::Quad; pub use self::rect::Rect; pub use self::tri::Tri; @@ -21,6 +23,11 @@ pub use self::tri::Tri; pub enum Primitive { Ellipse(Ellipse), Line(Line), + + PolygonPointless(polygon::Pointless), + PolygonFill(Polygon), + PolygonColorPerVertex(Polygon), + Quad(Quad), Rect(Rect), Tri(Tri), diff --git a/src/draw/properties/primitive/polygon.rs b/src/draw/properties/primitive/polygon.rs new file mode 100644 index 000000000..c82736e19 --- /dev/null +++ b/src/draw/properties/primitive/polygon.rs @@ -0,0 +1,309 @@ +use draw::{self, mesh, Drawing}; +use draw::properties::{ColorScalar, Draw, Drawn, IntoDrawn, Primitive, Rgba, SetColor, SetOrientation, SetPosition}; +use draw::properties::spatial::{self, orientation, position}; +use geom; +use math::BaseFloat; +use std::iter; + +/// A polygon prior to being initialised. +#[derive(Clone, Debug, Default)] +pub struct Pointless; + +/// Properties related to drawing a **Polygon**. +#[derive(Clone, Debug)] +pub struct Polygon { + position: position::Properties, + orientation: orientation::Properties, + color: C, + ranges: draw::GeomVertexDataRanges, +} + +/// Color all vertices of the polygon with a single color. +#[derive(Clone, Debug)] +pub struct Fill(Option); + +/// Color each vertex individually. +#[derive(Clone, Debug)] +pub struct PerVertex; + +/// The vertices type yielded for drawing into the mesh. +pub struct Vertices { + ranges: draw::GeomVertexDataRanges, + fill_color: Option, +} + +impl Pointless { + /// Draw a filled, convex polygon whose edges are defined by the given list of vertices. + pub(crate) fn points( + self, + vertex_data: &mut draw::GeomVertexData, + points: P, + ) -> Polygon + where + P: IntoIterator, + P::Item: Into>, + S: BaseFloat, + { + let mut ranges = draw::GeomVertexDataRanges::default(); + ranges.points.start = vertex_data.points.len(); + vertex_data.points.extend(points.into_iter().map(Into::into)); + ranges.points.end = vertex_data.points.len(); + let color = Fill(None); + Polygon::new(color, ranges) + } + + /// Draw a convex polygon whose edges and vertex colours are described by the given sequence of + /// vertices. + pub(crate) fn colored_points( + self, + vertex_data: &mut draw::GeomVertexData, + points: P, + ) -> Polygon + where + P: IntoIterator, + P::Item: Into<::mesh::vertex::WithColor, mesh::vertex::Color>>, + S: BaseFloat, + { + let mut ranges = draw::GeomVertexDataRanges::default(); + ranges.points.start = vertex_data.points.len(); + ranges.colors.start = vertex_data.colors.len(); + for v in points.into_iter().map(Into::into) { + vertex_data.points.push(v.vertex); + vertex_data.colors.push(v.color); + } + ranges.points.end = vertex_data.points.len(); + ranges.colors.end = vertex_data.colors.len(); + let color = PerVertex; + Polygon::new(color, ranges) + } +} + +impl Polygon +where + S: BaseFloat, +{ + // Initialise a new `Polygon` with no points, ready for drawing. + // + // The given `GeomVertexData` is use used to fill points. + fn new(color: C, ranges: draw::GeomVertexDataRanges) -> Self { + let orientation = Default::default(); + let position = Default::default(); + Polygon { + orientation, + position, + color, + ranges, + } + } +} + +impl<'a, S> Drawing<'a, Pointless, S> +where + S: BaseFloat, +{ + /// Describe the polygon's edges with the given list of consecutive vertices that join them. + pub fn points

(self, points: P) -> Drawing<'a, Polygon, S> + where + P: IntoIterator, + P::Item: Into>, + S: BaseFloat, + { + self.map_ty_with_vertices(|ty, data| ty.points(data, points)) + } + + /// Describe the polygon's edges with the given list of consecutive vertices that join them. + /// + /// Each vertex may be colored uniquely. + pub fn colored_points

(self, points: P) -> Drawing<'a, Polygon, S> + where + P: IntoIterator, + P::Item: Into>, + S: BaseFloat, + { + self.map_ty_with_vertices(|ty, data| ty.colored_points(data, points)) + } +} + +impl IntoDrawn for Pointless +where + S: BaseFloat, +{ + type Vertices = iter::Empty>; + type Indices = iter::Empty; + fn into_drawn(self, _draw: Draw) -> Drawn { + let properties = Default::default(); + let vertices = iter::empty(); + let indices = iter::empty(); + (properties, vertices, indices) + } +} + +impl IntoDrawn for Polygon +where + S: BaseFloat, +{ + type Vertices = Vertices; + type Indices = geom::polygon::TriangleIndices; + fn into_drawn(self, draw: Draw) -> Drawn { + let Polygon { + orientation, + position, + color: Fill(color), + ranges, + } = self; + + // If color is not specified within the ranges, determine the fill colour to use. + let fill_color = match ranges.colors.len() { + 0 => { + let color = color + .or_else(|| { + draw.theme(|theme| { + theme + .color + .primitive + .get(&draw::theme::Primitive::Polygon) + .map(|&c| c) + }) + }) + .unwrap_or(draw.theme(|t| t.color.default)); + Some(color) + }, + _ => None, + }; + + let dimensions = spatial::dimension::Properties::default(); + let spatial = spatial::Properties { dimensions, orientation, position }; + let indices = geom::polygon::triangle_indices(ranges.points.len()); + let vertices = Vertices { ranges, fill_color }; + (spatial, vertices, indices) + } +} + +impl IntoDrawn for Polygon +where + S: BaseFloat, +{ + type Vertices = Vertices; + type Indices = geom::polygon::TriangleIndices; + fn into_drawn(self, _draw: Draw) -> Drawn { + let Polygon { + orientation, + position, + ranges, + .. + } = self; + let fill_color = None; + let dimensions = spatial::dimension::Properties::default(); + let spatial = spatial::Properties { dimensions, orientation, position }; + let indices = geom::polygon::triangle_indices(ranges.points.len()); + let vertices = Vertices { ranges, fill_color }; + (spatial, vertices, indices) + } +} + +impl draw::properties::Vertices for Vertices +where + S: BaseFloat, +{ + fn next(&mut self, data: &mut draw::GeomVertexData) -> Option> { + let Vertices { + ref mut ranges, + fill_color, + } = *self; + + let point = ranges.points.next(); + let color = ranges.colors.next(); + let tex_coords = ranges.tex_coords.next(); + + let point = match point { + None => return None, + Some(point_ix) => { + *data.points + .get(point_ix) + .expect("no point for point index in GeomVertexData") + }, + }; + + let color = color + .map(|color_ix| { + *data.colors + .get(color_ix) + .expect("no color for color index in GeomVertexData") + }) + .or(fill_color) + .expect("no color for vertex"); + + let tex_coords = tex_coords + .map(|tex_coords_ix| { + *data.tex_coords + .get(tex_coords_ix) + .expect("no tex_coords for tex_coords index in GeomVertexData") + }) + .unwrap_or_else(draw::mesh::vertex::default_tex_coords); + + Some(draw::mesh::vertex::new(point, color, tex_coords)) + } +} + +impl SetOrientation for Polygon { + fn properties(&mut self) -> &mut orientation::Properties { + SetOrientation::properties(&mut self.orientation) + } +} + +impl SetPosition for Polygon { + fn properties(&mut self) -> &mut position::Properties { + SetPosition::properties(&mut self.position) + } +} + +impl SetColor for Polygon { + fn rgba_mut(&mut self) -> &mut Option { + SetColor::rgba_mut(&mut self.color.0) + } +} + +impl From for Primitive { + fn from(prim: Pointless) -> Self { + Primitive::PolygonPointless(prim) + } +} + +impl From> for Primitive { + fn from(prim: Polygon) -> Self { + Primitive::PolygonFill(prim) + } +} + +impl From> for Primitive { + fn from(prim: Polygon) -> Self { + Primitive::PolygonColorPerVertex(prim) + } +} + +impl Into> for Primitive { + fn into(self) -> Option { + match self { + Primitive::PolygonPointless(prim) => Some(prim), + _ => None, + } + } +} + +impl Into>> for Primitive { + fn into(self) -> Option> { + match self { + Primitive::PolygonFill(prim) => Some(prim), + _ => None, + } + } +} + +impl Into>> for Primitive { + fn into(self) -> Option> { + match self { + Primitive::PolygonColorPerVertex(prim) => Some(prim), + _ => None, + } + } +} diff --git a/src/geom/polygon.rs b/src/geom/polygon.rs index 67009ec0e..62e9b0bc3 100644 --- a/src/geom/polygon.rs +++ b/src/geom/polygon.rs @@ -8,6 +8,13 @@ pub struct Polygon { pub points: I, } +/// An iterator yielding indices into a polygon's vertices required to triangulate the polygon. +#[derive(Clone, Debug)] +pub struct TriangleIndices { + index: usize, + n_points: usize, +} + impl Polygon where I: Iterator, @@ -95,6 +102,24 @@ where }) } +/// An iterator yielding indices into a polygon's vertices required to triangulate the polygon. +pub fn triangle_indices(n_points: usize) -> TriangleIndices { + let index = 0; + TriangleIndices { index, n_points, } +} + +/// Returns `Some` with the touched triangle if the given `Point` is over the polygon described by +/// the given series of points. +/// +/// This uses the `triangles` function internally. +pub fn contains(points: I, point: &I::Item) -> Option> +where + I: IntoIterator, + I::Item: Vertex2d, +{ + triangles(points).and_then(|ts| tri::iter_contains(ts, &point)) +} + impl Iterator for Triangles where I: Iterator, @@ -110,14 +135,18 @@ where } } -/// Returns `Some` with the touched triangle if the given `Point` is over the polygon described by -/// the given series of points. -/// -/// This uses the `triangles` function internally. -pub fn contains(points: I, point: &I::Item) -> Option> -where - I: IntoIterator, - I::Item: Vertex2d, -{ - triangles(points).and_then(|ts| tri::iter_contains(ts, &point)) +impl Iterator for TriangleIndices { + type Item = usize; + fn next(&mut self) -> Option { + let index = self.index; + let tri_index = index / 3; + if self.n_points < tri_index + 3 { + return None; + } + self.index += 1; + match index % 3 { + 0 => Some(0), + remainder => Some(tri_index + remainder), + } + } } diff --git a/src/mesh/vertex.rs b/src/mesh/vertex.rs index 1ef81a7c0..b2552578b 100644 --- a/src/mesh/vertex.rs +++ b/src/mesh/vertex.rs @@ -192,3 +192,42 @@ where self.vertex.point3() } } + +// For converting from a tuples to vertices. + +impl From<(A, C)> for WithColor +where + A: Into, +{ + fn from((vertex, color): (A, C)) -> Self { + let vertex = vertex.into(); + WithColor { vertex, color } + } +} + +impl From<(A, T)> for WithTexCoords +where + A: Into, +{ + fn from((vertex, tex_coords): (A, T)) -> Self { + let vertex = vertex.into(); + WithTexCoords { vertex, tex_coords } + } +} + +impl From<(A, N)> for WithNormal +where + A: Into, +{ + fn from((vertex, normal): (A, N)) -> Self { + let vertex = vertex.into(); + WithNormal { vertex, normal } + } +} + +#[test] +fn test_tuple_conv() { + use color::named::GREEN; + let _: Point2<_> = [0.0, 0.0].into(); + let _: WithColor, _> = ([0.0, 0.0], GREEN).into(); +}