From 2e87b305d0683e7b19d84135b53acbe933af8b54 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Sat, 3 Aug 2019 23:38:43 +0200 Subject: [PATCH] [WIP] lyon 2D tessellation integration - polyline improvements This is a working WIP that replaces the old polyline implementation with lyon's polyline tessellation. The following commits will continue to replace the tessellation approaches of all 2D shapes in favour of lyon tessellation. Closes #185. Closes #281. Closes #335. --- Cargo.toml | 1 + examples/simple_polyline.rs | 4 +- src/draw/mesh/intermediary.rs | 152 ++++++++ src/draw/mesh/mod.rs | 1 + src/draw/mesh/vertex.rs | 3 +- src/draw/mod.rs | 60 +-- src/draw/properties/primitive/mesh.rs | 4 +- src/draw/properties/primitive/polyline.rs | 433 ++++++++++++++++++---- src/lib.rs | 1 + 9 files changed, 533 insertions(+), 126 deletions(-) create mode 100644 src/draw/mesh/intermediary.rs diff --git a/Cargo.toml b/Cargo.toml index 924e6000a..fc86a37ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ conrod_vulkano = "0.66" daggy = "0.6" find_folder = "0.3" image = "0.21" +lyon = "0.14" noise = "0.5" palette = "0.4" pennereq = "0.3" diff --git a/examples/simple_polyline.rs b/examples/simple_polyline.rs index 9e373f8fb..2fdd4cbcf 100644 --- a/examples/simple_polyline.rs +++ b/examples/simple_polyline.rs @@ -16,7 +16,7 @@ fn view(app: &App, frame: &Frame) { // Decide on a number of points and a thickness. let n_points = 10; - let half_thickness = 4.0; + let thickness = 8.0; let hz = ((app.mouse.x + win.right()) / win.w()).powi(4) * 1000.0; let vertices = (0..n_points) // A sine wave mapped to the range of the window. @@ -39,7 +39,7 @@ fn view(app: &App, frame: &Frame) { }); // Draw the polyline. - draw.polyline().vertices(half_thickness, vertices); + draw.polyline().thickness(thickness).vertices(vertices); // Write the result of our drawing to the window's frame. draw.to_frame(app, &frame).unwrap(); diff --git a/src/draw/mesh/intermediary.rs b/src/draw/mesh/intermediary.rs new file mode 100644 index 000000000..563fefc26 --- /dev/null +++ b/src/draw/mesh/intermediary.rs @@ -0,0 +1,152 @@ +use crate::draw::mesh; +use crate::geom; +use crate::mesh::vertex::{WithColor, WithTexCoords}; +use lyon::tessellation::geometry_builder::{self, GeometryBuilder, GeometryBuilderError, VertexId}; +use std::ops; + +/// 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 IntermediaryVertexData { + pub(crate) points: Vec>, + pub(crate) colors: Vec, + pub(crate) tex_coords: Vec>, +} + +/// An intermediary mesh to which drawings-in-progress may store vertex data and indices until they +/// are submitted to the **Draw**'s inner mesh. +#[derive(Clone, Debug)] +pub struct IntermediaryMesh { + pub(crate) vertex_data: IntermediaryVertexData, + pub(crate) indices: Vec, +} + +/// A set of ranges into the **IntermediaryVertexData**. +/// +/// This allows polygons, polylines, etc to track which slices of data are associated with their +/// own instance. +#[derive(Clone, Debug)] +pub struct IntermediaryVertexDataRanges { + pub points: ops::Range, + pub colors: ops::Range, + pub tex_coords: ops::Range, +} + +/// A `lyon::GeometryBuilder` around the `IntermediaryMesh` type. +#[derive(Debug)] +pub struct IntermediaryMeshBuilder<'a, S = geom::scalar::Default> { + pub(crate) mesh: &'a mut IntermediaryMesh, + pub(crate) vertex_data_ranges: IntermediaryVertexDataRanges, + pub(crate) index_range: ops::Range, +} + +impl IntermediaryMesh { + /// Produce a lyon-compatible `GeometryBuilder` for extending the `IntermediaryMesh`. + pub fn builder(&mut self) -> IntermediaryMeshBuilder { + let vertex_data_ranges = Default::default(); + let index_range = 0..0; + let mut builder = IntermediaryMeshBuilder { + mesh: self, + vertex_data_ranges, + index_range, + }; + builder.update_ranges_start(); + builder + } +} + +impl<'a, S> IntermediaryMeshBuilder<'a, S> { + fn update_ranges_start(&mut self) { + self.vertex_data_ranges.points.start = self.mesh.vertex_data.points.len(); + self.vertex_data_ranges.colors.start = self.mesh.vertex_data.colors.len(); + self.vertex_data_ranges.tex_coords.start = self.mesh.vertex_data.tex_coords.len(); + self.index_range.start = self.mesh.indices.len(); + } + + fn update_ranges_end(&mut self) { + self.vertex_data_ranges.points.end = self.mesh.vertex_data.points.len(); + self.vertex_data_ranges.colors.end = self.mesh.vertex_data.colors.len(); + self.vertex_data_ranges.tex_coords.end = self.mesh.vertex_data.tex_coords.len(); + self.index_range.end = self.mesh.indices.len(); + } + + pub fn vertex_data_ranges(&self) -> IntermediaryVertexDataRanges { + self.vertex_data_ranges.clone() + } + + pub fn index_range(&self) -> ops::Range { + self.index_range.clone() + } +} + +impl<'a, S> GeometryBuilder> for IntermediaryMeshBuilder<'a, S> { + fn begin_geometry(&mut self) { + self.update_ranges_start(); + self.vertex_data_ranges.points.end = self.vertex_data_ranges.points.start; + } + + fn end_geometry(&mut self) -> geometry_builder::Count { + self.update_ranges_end(); + let vertices = self.vertex_data_ranges.points.len() as u32; + let indices = self.index_range.len() as u32; + geometry_builder::Count { vertices, indices } + } + + fn add_vertex(&mut self, v: mesh::Vertex) -> Result { + let id = self.vertex_data_ranges.points.end as u32; + if id >= std::u32::MAX { + return Err(GeometryBuilderError::TooManyVertices); + } + let WithTexCoords { + tex_coords, + vertex: WithColor { + color, + vertex: point, + }, + } = v; + self.mesh.vertex_data.points.push(point); + self.mesh.vertex_data.colors.push(color); + self.mesh.vertex_data.tex_coords.push(tex_coords); + self.vertex_data_ranges.points.end += 1; + Ok(VertexId(id)) + } + + fn add_triangle(&mut self, a: VertexId, b: VertexId, c: VertexId) { + self.mesh.indices.push(a.to_usize()); + self.mesh.indices.push(b.to_usize()); + self.mesh.indices.push(c.to_usize()); + } + + fn abort_geometry(&mut self) { + self.update_ranges_end(); + } +} + +impl Default for IntermediaryVertexData { + fn default() -> Self { + IntermediaryVertexData { + points: Default::default(), + colors: Default::default(), + tex_coords: Default::default(), + } + } +} + +impl Default for IntermediaryMesh { + fn default() -> Self { + IntermediaryMesh { + vertex_data: Default::default(), + indices: Default::default(), + } + } +} + +impl Default for IntermediaryVertexDataRanges { + fn default() -> Self { + IntermediaryVertexDataRanges { + points: 0..0, + colors: 0..0, + tex_coords: 0..0, + } + } +} diff --git a/src/draw/mesh/mod.rs b/src/draw/mesh/mod.rs index 85693cd5d..bd762fe4b 100644 --- a/src/draw/mesh/mod.rs +++ b/src/draw/mesh/mod.rs @@ -5,6 +5,7 @@ use crate::math::{BaseFloat, BaseNum}; use crate::mesh::{self, MeshPoints, WithColors, WithIndices, WithTexCoords}; use std::ops::{Deref, DerefMut}; +pub mod intermediary; pub mod vertex; pub use self::vertex::Vertex; diff --git a/src/draw/mesh/vertex.rs b/src/draw/mesh/vertex.rs index 3d9818459..4a4a07525 100644 --- a/src/draw/mesh/vertex.rs +++ b/src/draw/mesh/vertex.rs @@ -11,7 +11,8 @@ pub type Normal = Vector3; pub type ColoredPoint = WithColor, Color>; /// The vertex type produced by the **draw::Mesh**'s inner **MeshType**. -pub type Vertex = WithTexCoords, Color>, TexCoords>; +pub type Vertex = + WithTexCoords, Color>, TexCoords>; /// Types that can be converted directly into a **draw::mesh::Vertex**. pub trait IntoVertex { diff --git a/src/draw/mod.rs b/src/draw/mod.rs index 80e97dadf..7b099f188 100644 --- a/src/draw/mod.rs +++ b/src/draw/mod.rs @@ -11,6 +11,9 @@ use std::ops; pub use self::background::Background; pub use self::drawing::Drawing; +pub use self::mesh::intermediary::{ + IntermediaryMesh, IntermediaryMeshBuilder, IntermediaryVertexData, IntermediaryVertexDataRanges, +}; pub use self::mesh::Mesh; use self::properties::spatial::orientation::{self, Orientation}; use self::properties::spatial::position::{self, Position}; @@ -90,63 +93,6 @@ 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 IntermediaryVertexData { - pub(crate) points: Vec>, - pub(crate) colors: Vec, - pub(crate) tex_coords: Vec>, -} - -/// An intermediary mesh to which drawings-in-progress may store vertex data and indices until they -/// are submitted to the **Draw**'s inner mesh. -#[derive(Clone, Debug)] -pub struct IntermediaryMesh { - pub(crate) vertex_data: IntermediaryVertexData, - pub(crate) indices: Vec, -} - -/// A set of ranges into the **IntermediaryVertexData**. -/// -/// This allows polygons, polylines, etc to track which slices of data are associated with their -/// own instance. -#[derive(Clone, Debug)] -pub struct IntermediaryVertexDataRanges { - pub points: ops::Range, - pub colors: ops::Range, - pub tex_coords: ops::Range, -} - -impl Default for IntermediaryVertexData { - fn default() -> Self { - IntermediaryVertexData { - points: Default::default(), - colors: Default::default(), - tex_coords: Default::default(), - } - } -} - -impl Default for IntermediaryMesh { - fn default() -> Self { - IntermediaryMesh { - vertex_data: Default::default(), - indices: Default::default(), - } - } -} - -impl Default for IntermediaryVertexDataRanges { - fn default() -> Self { - IntermediaryVertexDataRanges { - 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 { diff --git a/src/draw/properties/primitive/mesh.rs b/src/draw/properties/primitive/mesh.rs index d53930da3..b2d9451d9 100644 --- a/src/draw/properties/primitive/mesh.rs +++ b/src/draw/properties/primitive/mesh.rs @@ -31,7 +31,7 @@ impl Vertexless { /// Describe the mesh with a sequence of triangles. /// /// Each triangle may be composed of any vertex type that may be converted directly into the - /// `draw;;mesh::vertex` type. + /// `draw::mesh::vertex` type. pub fn tris(self, mesh: &mut draw::IntermediaryMesh, tris: I) -> Mesh where S: BaseFloat, @@ -75,7 +75,7 @@ impl Vertexless { /// /// Each trio of `indices` describes a single triangle of `vertices`. /// - /// Each vertex may be any type that may be converted directly into the `draw;;mesh::vertex` + /// Each vertex may be any type that may be converted directly into the `draw::mesh::vertex` /// type. pub fn indexed( self, diff --git a/src/draw/properties/primitive/polyline.rs b/src/draw/properties/primitive/polyline.rs index d618a87c4..b703d991f 100644 --- a/src/draw/properties/primitive/polyline.rs +++ b/src/draw/properties/primitive/polyline.rs @@ -1,16 +1,22 @@ -use crate::draw::mesh::vertex::{IntoPoint, IntoVertex}; +use crate::color::conv::IntoLinSrgba; +use crate::draw::mesh::vertex::IntoVertex; use crate::draw::properties::spatial::{self, orientation, position}; use crate::draw::properties::{Draw, Drawn, IntoDrawn, Primitive, SetOrientation, SetPosition}; use crate::draw::{self, Drawing}; -use crate::geom::line::join::miter; -use crate::geom::{self, pt2, Point2}; +use crate::geom::{self, pt2}; use crate::math::BaseFloat; -use crate::mesh::vertex::{WithColor, WithTexCoords}; +use lyon::tessellation::geometry_builder::{self, GeometryBuilder, GeometryBuilderError, VertexId}; +use lyon::tessellation::{LineCap, LineJoin, StrokeOptions, StrokeVertex}; +use std::cell::Cell; use std::iter; +use std::ops; /// A polyline prior to being initialised. #[derive(Clone, Debug, Default)] -pub struct Vertexless; +pub struct Vertexless { + opts: StrokeOptions, + close: bool, +} /// Properties related to drawing a **Polyline**. #[derive(Clone, Debug)] @@ -18,14 +24,169 @@ pub struct Polyline { position: position::Properties, orientation: orientation::Properties, vertex_data_ranges: draw::IntermediaryVertexDataRanges, + index_range: ops::Range, +} + +struct PolylineGeometryBuilder<'a, 'mesh, S = geom::scalar::Default> { + builder: &'a mut draw::IntermediaryMeshBuilder<'mesh, S>, + color: &'a Cell, } impl Vertexless { + /// The start line cap as specified by the SVG spec. + pub fn start_cap(mut self, cap: LineCap) -> Self { + self.opts.start_cap = cap; + self + } + + /// The end line cap as specified by the SVG spec. + pub fn end_cap(mut self, cap: LineCap) -> Self { + self.opts.end_cap = cap; + self + } + + /// The start and end line cap as specified by the SVG spec. + pub fn caps(self, cap: LineCap) -> Self { + self.start_cap(cap).end_cap(cap) + } + + /// The stroke for each sub-path does not extend beyond its two endpoints. A zero length + /// sub-path will therefore not have any stroke. + pub fn start_cap_butt(self) -> Self { + self.start_cap(LineCap::Butt) + } + + /// At the end of each sub-path, the shape representing the stroke will be extended by a + /// rectangle with the same width as the stroke width and whose length is half of the stroke + /// width. If a sub-path has zero length, then the resulting effect is that the stroke for that + /// sub-path consists solely of a square with side length equal to the stroke width, centered + /// at the sub-path's point. + pub fn start_cap_square(self) -> Self { + self.start_cap(LineCap::Square) + } + + /// At each end of each sub-path, the shape representing the stroke will be extended by a half + /// circle with a radius equal to the stroke width. If a sub-path has zero length, then the + /// resulting effect is that the stroke for that sub-path consists solely of a full circle + /// centered at the sub-path's point. + pub fn start_cap_round(self) -> Self { + self.start_cap(LineCap::Round) + } + + /// The stroke for each sub-path does not extend beyond its two endpoints. A zero length + /// sub-path will therefore not have any stroke. + pub fn end_cap_butt(self) -> Self { + self.end_cap(LineCap::Butt) + } + + /// At the end of each sub-path, the shape representing the stroke will be extended by a + /// rectangle with the same width as the stroke width and whose length is half of the stroke + /// width. If a sub-path has zero length, then the resulting effect is that the stroke for that + /// sub-path consists solely of a square with side length equal to the stroke width, centered + /// at the sub-path's point. + pub fn end_cap_square(self) -> Self { + self.end_cap(LineCap::Square) + } + + /// At each end of each sub-path, the shape representing the stroke will be extended by a half + /// circle with a radius equal to the stroke width. If a sub-path has zero length, then the + /// resulting effect is that the stroke for that sub-path consists solely of a full circle + /// centered at the sub-path's point. + pub fn end_cap_round(self) -> Self { + self.end_cap(LineCap::Round) + } + + /// The stroke for each sub-path does not extend beyond its two endpoints. A zero length + /// sub-path will therefore not have any stroke. + pub fn caps_butt(self) -> Self { + self.caps(LineCap::Butt) + } + + /// At the end of each sub-path, the shape representing the stroke will be extended by a + /// rectangle with the same width as the stroke width and whose length is half of the stroke + /// width. If a sub-path has zero length, then the resulting effect is that the stroke for that + /// sub-path consists solely of a square with side length equal to the stroke width, centered + /// at the sub-path's point. + pub fn caps_square(self) -> Self { + self.caps(LineCap::Square) + } + + /// At each end of each sub-path, the shape representing the stroke will be extended by a half + /// circle with a radius equal to the stroke width. If a sub-path has zero length, then the + /// resulting effect is that the stroke for that sub-path consists solely of a full circle + /// centered at the sub-path's point. + pub fn caps_round(self) -> Self { + self.caps(LineCap::Round) + } + + /// Draw a line between the end point and the start point, closing the polyline. + /// + /// Default value is `false`. + pub fn close(mut self) -> Self { + self.close = true; + self + } + + /// The way in which lines are joined at the vertices, matching the SVG spec. + /// + /// Default value is `MiterClip`. + pub fn join(mut self, join: LineJoin) -> Self { + self.opts.line_join = join; + self + } + + /// A sharp corner is to be used to join path segments. + pub fn join_miter(self) -> Self { + self.join(LineJoin::Miter) + } + + /// Same as a `join_miter`, but if the miter limit is exceeded, the miter is clipped at a miter + /// length equal to the miter limit value multiplied by the stroke width. + pub fn join_miter_clip(self) -> Self { + self.join(LineJoin::MiterClip) + } + + /// A round corner is to be used to join path segments. + pub fn join_round(self) -> Self { + self.join(LineJoin::Round) + } + + /// A bevelled corner is to be used to join path segments. The bevel shape is a triangle that + /// fills the area between the two stroked segments. + pub fn join_bevel(self) -> Self { + self.join(LineJoin::Bevel) + } + + /// The total thickness (aka width) of the line. + pub fn thickness(mut self, thickness: f32) -> Self { + self.opts.line_width = thickness; + self + } + + /// Describes the limit before miter lines will clip, as described in the SVG spec. + /// + /// Must be greater than or equal to `1.0`. + pub fn miter_limit(mut self, limit: f32) -> Self { + self.opts.miter_limit = limit; + self + } + + /// Maximum allowed distance to the path when building an approximation. + pub fn tolerance(mut self, tolerance: f32) -> Self { + self.opts.tolerance = tolerance; + self + } + + /// Specify the full set of stroke options for the polyline tessellation. + pub fn stroke_options(mut self, opts: StrokeOptions) -> Self { + self.opts = opts; + self + } + /// Draw a polyline whose path is defined by the given list of vertices. pub(crate) fn vertices( self, mesh: &mut draw::IntermediaryMesh, - half_thickness: S, vertices: I, ) -> Polyline where @@ -33,58 +194,27 @@ impl Vertexless { I: IntoIterator, I::Item: IntoVertex, { - let mut vertex_data_ranges = draw::IntermediaryVertexDataRanges::default(); - vertex_data_ranges.points.start = mesh.vertex_data.points.len(); - vertex_data_ranges.colors.start = mesh.vertex_data.colors.len(); - vertex_data_ranges.tex_coords.start = mesh.vertex_data.tex_coords.len(); - - fn v_to_pt2(v: draw::mesh::Vertex) -> Point2 - where - S: BaseFloat, - { - pt2(v.x, v.y) + let color = Cell::new(crate::color::WHITE.into_lin_srgba()); + let color = &color; + let v_iter = vertices.into_iter().map(IntoVertex::into_vertex).map(|v| { + color.set(v.color); + let p: geom::Point3 = v.cast().expect("failed to cast point"); + lyon::tessellation::math::Point::new(p.x, p.y) + }); + let mut builder = mesh.builder(); + let res = lyon::tessellation::basic_shapes::stroke_polyline( + v_iter, + self.close, + &self.opts, + &mut PolylineGeometryBuilder { + builder: &mut builder, + color, + }, + ); + if let Err(err) = res { + eprintln!("failed to tessellate polyline: {:?}", err); } - - // For each vertex in the given sequence, generate the miter point pairs. Colour and - // texture each pair of points by using the colour and texture coords of the original - // vertex. - let mut v_iter = vertices.into_iter().map(IntoVertex::into_vertex); - let mut a = None; - let mut v = v_iter.next(); - let mut b = v.clone().map(v_to_pt2); - loop { - let next_v = v_iter.next(); - let next = next_v.clone().map(v_to_pt2); - let [l, r] = match miter::next_pair(half_thickness, &mut a, &mut b, next) { - None => break, - Some(pair) => pair, - }; - - // `v` should always be `Some` if `Some` next miter pair was yielded. - let WithTexCoords { - tex_coords, - vertex: WithColor { color, .. }, - } = v.clone().expect("no vertex for the next miter pair"); - - // A function for pushing the left and right miter points. - let mut push_point = |point| { - mesh.vertex_data.points.push(point); - mesh.vertex_data.colors.push(color); - mesh.vertex_data.tex_coords.push(tex_coords); - }; - push_point(l.into_point()); - push_point(r.into_point()); - - // Update the vertex used for texturing and colouring the next miter pair. - if next_v.is_some() { - v = next_v; - } - } - - vertex_data_ranges.points.end = mesh.vertex_data.points.len(); - vertex_data_ranges.colors.end = mesh.vertex_data.colors.len(); - vertex_data_ranges.tex_coords.end = mesh.vertex_data.tex_coords.len(); - Polyline::new(vertex_data_ranges) + Polyline::new(builder.vertex_data_ranges(), builder.index_range()) } } @@ -93,13 +223,17 @@ where S: BaseFloat, { // Initialise a new `Polyline` with its ranges into the intermediary mesh, ready for drawing. - fn new(vertex_data_ranges: draw::IntermediaryVertexDataRanges) -> Self { + fn new( + vertex_data_ranges: draw::IntermediaryVertexDataRanges, + index_range: ops::Range, + ) -> Self { let orientation = Default::default(); let position = Default::default(); Polyline { orientation, position, vertex_data_ranges, + index_range, } } } @@ -108,14 +242,186 @@ impl<'a, S> Drawing<'a, Vertexless, S> where S: BaseFloat, { + /// The start line cap as specified by the SVG spec. + pub fn start_cap(self, cap: LineCap) -> Self { + self.map_ty(|ty| ty.start_cap(cap)) + } + + /// The end line cap as specified by the SVG spec. + pub fn end_cap(self, cap: LineCap) -> Self { + self.map_ty(|ty| ty.end_cap(cap)) + } + + /// The start and end line cap as specified by the SVG spec. + pub fn caps(self, cap: LineCap) -> Self { + self.map_ty(|ty| ty.caps(cap)) + } + + /// The stroke for each sub-path does not extend beyond its two endpoints. A zero length + /// sub-path will therefore not have any stroke. + pub fn start_cap_butt(self) -> Self { + self.map_ty(|ty| ty.start_cap_butt()) + } + + /// At the end of each sub-path, the shape representing the stroke will be extended by a + /// rectangle with the same width as the stroke width and whose length is half of the stroke + /// width. If a sub-path has zero length, then the resulting effect is that the stroke for that + /// sub-path consists solely of a square with side length equal to the stroke width, centered + /// at the sub-path's point. + pub fn start_cap_square(self) -> Self { + self.map_ty(|ty| ty.start_cap_square()) + } + + /// At each end of each sub-path, the shape representing the stroke will be extended by a half + /// circle with a radius equal to the stroke width. If a sub-path has zero length, then the + /// resulting effect is that the stroke for that sub-path consists solely of a full circle + /// centered at the sub-path's point. + pub fn start_cap_round(self) -> Self { + self.map_ty(|ty| ty.start_cap_round()) + } + + /// The stroke for each sub-path does not extend beyond its two endpoints. A zero length + /// sub-path will therefore not have any stroke. + pub fn end_cap_butt(self) -> Self { + self.map_ty(|ty| ty.end_cap_butt()) + } + + /// At the end of each sub-path, the shape representing the stroke will be extended by a + /// rectangle with the same width as the stroke width and whose length is half of the stroke + /// width. If a sub-path has zero length, then the resulting effect is that the stroke for that + /// sub-path consists solely of a square with side length equal to the stroke width, centered + /// at the sub-path's point. + pub fn end_cap_square(self) -> Self { + self.map_ty(|ty| ty.end_cap_square()) + } + + /// At each end of each sub-path, the shape representing the stroke will be extended by a half + /// circle with a radius equal to the stroke width. If a sub-path has zero length, then the + /// resulting effect is that the stroke for that sub-path consists solely of a full circle + /// centered at the sub-path's point. + pub fn end_cap_round(self) -> Self { + self.map_ty(|ty| ty.end_cap_round()) + } + + /// The stroke for each sub-path does not extend beyond its two endpoints. A zero length + /// sub-path will therefore not have any stroke. + pub fn caps_butt(self) -> Self { + self.map_ty(|ty| ty.caps_butt()) + } + + /// At the end of each sub-path, the shape representing the stroke will be extended by a + /// rectangle with the same width as the stroke width and whose length is half of the stroke + /// width. If a sub-path has zero length, then the resulting effect is that the stroke for that + /// sub-path consists solely of a square with side length equal to the stroke width, centered + /// at the sub-path's point. + pub fn caps_square(self) -> Self { + self.map_ty(|ty| ty.caps_square()) + } + + /// At each end of each sub-path, the shape representing the stroke will be extended by a half + /// circle with a radius equal to the stroke width. If a sub-path has zero length, then the + /// resulting effect is that the stroke for that sub-path consists solely of a full circle + /// centered at the sub-path's point. + pub fn caps_round(self) -> Self { + self.map_ty(|ty| ty.caps_round()) + } + + /// Draw a line between the end point and the start point, closing the polyline. + /// + /// Default value is `false`. + pub fn close(self) -> Self { + self.map_ty(|ty| ty.close()) + } + + /// The way in which lines are joined at the vertices, matching the SVG spec. + /// + /// Default value is `MiterClip`. + pub fn join(self, join: LineJoin) -> Self { + self.map_ty(|ty| ty.join(join)) + } + + /// A sharp corner is to be used to join path segments. + pub fn join_miter(self) -> Self { + self.map_ty(|ty| ty.join_miter()) + } + + /// Same as a `join_miter`, but if the miter limit is exceeded, the miter is clipped at a miter + /// length equal to the miter limit value multiplied by the stroke width. + pub fn join_miter_clip(self) -> Self { + self.map_ty(|ty| ty.join_miter_clip()) + } + + /// A round corner is to be used to join path segments. + pub fn join_round(self) -> Self { + self.map_ty(|ty| ty.join_round()) + } + + /// A bevelled corner is to be used to join path segments. The bevel shape is a triangle that + /// fills the area between the two stroked segments. + pub fn join_bevel(self) -> Self { + self.map_ty(|ty| ty.join_bevel()) + } + + /// The total thickness (aka width) of the line. + pub fn thickness(self, thickness: f32) -> Self { + self.map_ty(|ty| ty.thickness(thickness)) + } + + /// Describes the limit before miter lines will clip, as described in the SVG spec. + /// + /// Must be greater than or equal to `1.0`. + pub fn miter_limit(self, limit: f32) -> Self { + self.map_ty(|ty| ty.miter_limit(limit)) + } + + /// Maximum allowed distance to the path when building an approximation. + pub fn tolerance(self, tolerance: f32) -> Self { + self.map_ty(|ty| ty.tolerance(tolerance)) + } + + /// Specify the full set of stroke options for the polyline tessellation. + pub fn stroke_options(self, opts: StrokeOptions) -> Self { + self.map_ty(|ty| ty.stroke_options(opts)) + } + /// Describe the polyline with some "half_thickness" of the line and the given sequence of /// vertices. - pub fn vertices(self, half_thickness: S, vertices: I) -> Drawing<'a, Polyline, S> + pub fn vertices(self, vertices: I) -> Drawing<'a, Polyline, S> where I: IntoIterator, I::Item: IntoVertex, { - self.map_ty_with_vertices(|ty, mesh| ty.vertices(mesh, half_thickness, vertices)) + self.map_ty_with_vertices(|ty, mesh| ty.vertices(mesh, vertices)) + } +} + +impl<'a, 'mesh, S> GeometryBuilder for PolylineGeometryBuilder<'a, 'mesh, S> +where + S: BaseFloat, +{ + fn begin_geometry(&mut self) { + self.builder.begin_geometry(); + } + + fn end_geometry(&mut self) -> geometry_builder::Count { + self.builder.end_geometry() + } + + fn add_vertex(&mut self, v: StrokeVertex) -> Result { + let color = self.color.get(); + let point = pt2(v.position.x, v.position.y) + .cast() + .expect("failed to cast point"); + let v: draw::mesh::Vertex = (point, color).into_vertex(); + self.builder.add_vertex(v) + } + + fn add_triangle(&mut self, a: VertexId, b: VertexId, c: VertexId) { + self.builder.add_triangle(a, b, c); + } + + fn abort_geometry(&mut self) { + self.builder.abort_geometry(); } } @@ -138,12 +444,13 @@ where S: BaseFloat, { type Vertices = draw::properties::VerticesFromRanges; - type Indices = geom::tri::FlattenIndices; + type Indices = draw::properties::IndicesFromRange; fn into_drawn(self, _draw: Draw) -> Drawn { let Polyline { orientation, position, vertex_data_ranges, + index_range, } = self; let dimensions = spatial::dimension::Properties::default(); let spatial = spatial::Properties { @@ -151,13 +458,11 @@ where orientation, position, }; - let n_points = (vertex_data_ranges.points.end - vertex_data_ranges.points.start) / 2; let vertices = draw::properties::VerticesFromRanges { ranges: vertex_data_ranges, fill_color: None, }; - let index_tris = miter::TriangleIndices::new(n_points); - let indices = geom::tri::flatten_index_tris(index_tris); + let indices = draw::properties::IndicesFromRange { range: index_range }; (spatial, vertices, indices) } } diff --git a/src/lib.rs b/src/lib.rs index 3a61143cc..7012cf669 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,6 +18,7 @@ pub use conrod_vulkano; pub use conrod_winit; pub use daggy; pub use find_folder; +pub use lyon; use serde_derive; pub use vulkano; pub use vulkano_shaders;