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;