Skip to content

Commit

Permalink
Merge pull request #100 from hecrj/feature/mesh-with-custom-tolerance
Browse files Browse the repository at this point in the history
Mesh with custom tolerance
  • Loading branch information
hecrj authored Sep 25, 2019
2 parents 7197ea3 + d190b1e commit 27d05c5
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 16 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
performing a slow task.
- `ui::Image`, a simple widget to display a `graphics::Image` in your user
interface.
- `Mesh::new_with_tolerance`, which allows to control the tolerance of line
segment approximations. [#100]

### Changed
- `Mesh::stroke` now takes an `f32` as `line_width` instead of a `u16`.
Expand All @@ -37,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- any button click [#67]
- wheel movements [#67]
- the cursor leaving/entering the game window [#67]
- The `mesh` example now has a slider to control the tolerance. [#100]

### Fixed
- Hang when `Game::TICKS_PER_SECOND` is set as `0`. [#99]
Expand All @@ -48,6 +51,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[#78]: https://github.com/hecrj/coffee/pull/78
[#79]: https://github.com/hecrj/coffee/pull/79
[#99]: https://github.com/hecrj/coffee/pull/99
[#100]: https://github.com/hecrj/coffee/pull/100


## [0.3.2] - 2019-09-01
Expand Down
29 changes: 26 additions & 3 deletions examples/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ fn main() -> Result<()> {
struct Example {
shape: ShapeOption,
mode: ModeOption,
tolerance: f32,
stroke_width: u16,
radius: f32,
vertical_radius: f32,
color: Color,
polyline_points: Vec<Point>,

tolerance_slider: slider::State,
stroke_width_slider: slider::State,
radius_slider: slider::State,
vertical_radius_slider: slider::State,
Expand Down Expand Up @@ -58,12 +60,14 @@ impl Game for Example {
Task::succeed(move || Example {
shape: ShapeOption::Rectangle,
mode: ModeOption::Fill,
tolerance: 0.1,
color: Color::WHITE,
radius: 100.0,
vertical_radius: 50.0,
stroke_width: 2,
polyline_points: Vec::new(),

tolerance_slider: slider::State::new(),
stroke_width_slider: slider::State::new(),
radius_slider: slider::State::new(),
vertical_radius_slider: slider::State::new(),
Expand All @@ -89,7 +93,7 @@ impl Game for Example {
a: 1.0,
});

let mut mesh = Mesh::new();
let mut mesh = Mesh::new_with_tolerance(self.tolerance);

let shape = match self.shape {
ShapeOption::Rectangle => Shape::Rectangle(Rectangle {
Expand Down Expand Up @@ -138,6 +142,9 @@ impl UserInterface for Example {
Message::ModeSelected(mode) => {
self.mode = mode;
}
Message::ToleranceChanged(tolerance) => {
self.tolerance = tolerance;
}
Message::StrokeWidthChanged(stroke_width) => {
self.stroke_width = stroke_width;
}
Expand Down Expand Up @@ -206,8 +213,9 @@ impl UserInterface for Example {
}
}

controls =
controls.push(color_sliders(&mut self.color_sliders, self.color));
controls = controls
.push(color_sliders(&mut self.color_sliders, self.color))
.push(tolerance_slider(&mut self.tolerance_slider, self.tolerance));

Column::new()
.width(window.width() as u32)
Expand All @@ -225,6 +233,7 @@ impl UserInterface for Example {
enum Message {
ShapeSelected(ShapeOption),
ModeSelected(ModeOption),
ToleranceChanged(f32),
StrokeWidthChanged(u16),
RadiusChanged(f32),
VerticalRadiusChanged(f32),
Expand Down Expand Up @@ -256,6 +265,20 @@ fn shape_selector(current: ShapeOption) -> Element<'static, Message> {
.into()
}

fn tolerance_slider(
state: &mut slider::State,
tolerance: f32,
) -> Element<Message> {
slider_with_label(
"Tolerance:",
state,
0.001..=20.0,
tolerance,
&format!("{:.*}", 3, tolerance),
Message::ToleranceChanged,
)
}

fn mode_selector(current: ModeOption) -> Element<'static, Message> {
let options = [ModeOption::Fill, ModeOption::Stroke].iter().cloned().fold(
Row::new().padding(10).spacing(10),
Expand Down
59 changes: 46 additions & 13 deletions src/graphics/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,46 @@ use crate::graphics::{gpu, Color, Rectangle, Shape, Target};
use lyon_tessellation as lyon;

/// A set of shapes that can be drawn.
///
/// # Tolerance
/// When shapes contain curves or arcs, they will be approximated using line
/// segments. The `tolerance` parameter controls this approximation by
/// establishing the maximum distance between a curve and its line segments.
///
/// The lower the tolerance provided, the better a [`Mesh`] will approximate
/// a [`Shape`]. However, a lower tolerance can have a noticeable performance
/// impact. Use it wisely!
///
/// [`Mesh`]: struct.Mesh.html
/// [`Shape`]: enum.Shape.html
#[derive(Debug, Clone)]
pub struct Mesh {
tolerance: f32,
buffers: lyon::VertexBuffers<gpu::Vertex, u32>,
}

impl Mesh {
/// Creates a new empty [`Mesh`].
/// Creates a new empty [`Mesh`] with a default tolerance of `0.1`.
///
/// [`Mesh`]: struct.Mesh.html
pub fn new() -> Mesh {
Mesh {
tolerance: 0.1,
buffers: lyon::VertexBuffers::new(),
}
}

/// Creates a new empty [`Mesh`] with the given tolerance.
///
/// Providing a lower tolerance here can allow you to zoom in your [`Mesh`]
/// using a [`Transformation`] and still observe smooth curves. See
/// [Tolerance](#tolerance).
///
/// [`Mesh`]: struct.Mesh.html
/// [`Transformation`]: struct.Transformation.html
pub fn new_with_tolerance(tolerance: f32) -> Mesh {
Mesh {
tolerance,
buffers: lyon::VertexBuffers::new(),
}
}
Expand Down Expand Up @@ -45,7 +74,7 @@ impl Mesh {
}) => {
let _ = lyon::basic_shapes::fill_rectangle(
&lyon::math::rect(x, y, width, height),
&Self::fill_options(),
&Self::fill_options(self.tolerance),
&mut builder,
)
.expect("Fill rectangle");
Expand All @@ -54,7 +83,7 @@ impl Mesh {
let _ = lyon::basic_shapes::fill_circle(
lyon::math::point(center.x, center.y),
radius,
&Self::fill_options(),
&Self::fill_options(self.tolerance),
&mut builder,
)
.expect("Fill circle");
Expand All @@ -69,7 +98,7 @@ impl Mesh {
lyon::math::point(center.x, center.y),
lyon::math::vector(horizontal_radius, vertical_radius),
lyon::math::Angle::radians(rotation),
&Self::fill_options(),
&Self::fill_options(self.tolerance),
&mut builder,
)
.expect("Fill ellipse");
Expand All @@ -80,7 +109,7 @@ impl Mesh {
.iter()
.map(|point| lyon::math::point(point.x, point.y)),
&mut lyon::FillTessellator::new(),
&Self::fill_options(),
&Self::fill_options(self.tolerance),
&mut builder,
)
.expect("Fill polyline");
Expand Down Expand Up @@ -108,7 +137,7 @@ impl Mesh {
}) => {
let _ = lyon::basic_shapes::stroke_rectangle(
&lyon::math::rect(x, y, rect_width, height),
&Self::stroke_options(width),
&Self::stroke_options(self.tolerance, width),
&mut builder,
)
.expect("Stroke rectangle");
Expand All @@ -117,7 +146,7 @@ impl Mesh {
let _ = lyon::basic_shapes::stroke_circle(
lyon::math::point(center.x, center.y),
radius,
&Self::stroke_options(width),
&Self::stroke_options(self.tolerance, width),
&mut builder,
)
.expect("Stroke circle");
Expand All @@ -132,7 +161,7 @@ impl Mesh {
lyon::math::point(center.x, center.y),
lyon::math::vector(horizontal_radius, vertical_radius),
lyon::math::Angle::radians(rotation),
&Self::stroke_options(width),
&Self::stroke_options(self.tolerance, width),
&mut builder,
)
.expect("Stroke ellipse");
Expand All @@ -143,7 +172,7 @@ impl Mesh {
.iter()
.map(|point| lyon::math::point(point.x, point.y)),
false,
&Self::stroke_options(width),
&Self::stroke_options(self.tolerance, width),
&mut builder,
)
.expect("Stroke polyline");
Expand All @@ -159,12 +188,16 @@ impl Mesh {
target.draw_triangles(&self.buffers.vertices, &self.buffers.indices);
}

fn fill_options() -> lyon::FillOptions {
lyon::FillOptions::DEFAULT.with_normals(false)
fn fill_options(tolerance: f32) -> lyon::FillOptions {
lyon::FillOptions::DEFAULT
.with_tolerance(tolerance)
.with_normals(false)
}

fn stroke_options(width: f32) -> lyon::StrokeOptions {
lyon::StrokeOptions::DEFAULT.with_line_width(width)
fn stroke_options(tolerance: f32, width: f32) -> lyon::StrokeOptions {
lyon::StrokeOptions::DEFAULT
.with_tolerance(tolerance)
.with_line_width(width)
}
}

Expand Down

0 comments on commit 27d05c5

Please sign in to comment.