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

Add line dash API #1225

Merged
merged 6 commits into from
Feb 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions examples/solar_system/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ impl<Message> canvas::Program<Message> for State {
Stroke {
width: 1.0,
color: Color::from_rgba8(0, 153, 255, 0.1),
line_dash: canvas::LineDash {
offset: 0,
segments: &[3.0, 6.0],
},
..Stroke::default()
},
);
Expand Down
2 changes: 1 addition & 1 deletion graphics/src/widget/canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub use frame::Frame;
pub use geometry::Geometry;
pub use path::Path;
pub use program::Program;
pub use stroke::{LineCap, LineJoin, Stroke};
pub use stroke::{LineCap, LineDash, LineJoin, Stroke};
pub use text::Text;

/// A widget capable of drawing 2D graphics.
Expand Down
11 changes: 10 additions & 1 deletion graphics/src/widget/canvas/frame.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use std::borrow::Cow;

use iced_native::{Point, Rectangle, Size, Vector};

use crate::{
canvas::path,
canvas::{Fill, Geometry, Path, Stroke, Text},
triangle, Primitive,
};
Expand Down Expand Up @@ -150,7 +153,7 @@ impl Frame {

/// Draws the stroke of the given [`Path`] on the [`Frame`] with the
/// provided style.
pub fn stroke(&mut self, path: &Path, stroke: impl Into<Stroke>) {
pub fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>) {
let stroke = stroke.into();

let mut buffers = tessellation::BuffersBuilder::new(
Expand All @@ -164,6 +167,12 @@ impl Frame {
options.end_cap = stroke.line_cap.into();
options.line_join = stroke.line_join.into();

let path = if stroke.line_dash.segments.is_empty() {
Cow::Borrowed(path)
} else {
Cow::Owned(path::dashed(path, stroke.line_dash))
};

let result = if self.transforms.current.is_identity {
self.stroke_tessellator.tessellate_path(
path.raw(),
Expand Down
44 changes: 44 additions & 0 deletions graphics/src/widget/canvas/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ mod builder;
pub use arc::Arc;
pub use builder::Builder;

use crate::canvas::LineDash;

use iced_native::{Point, Size};
use lyon::algorithms::walk::{walk_along_path, RepeatedPattern};
use lyon::path::iterator::PathIterator;

/// An immutable set of points that may or may not be connected.
///
Expand Down Expand Up @@ -66,3 +70,43 @@ impl Path {
}
}
}

pub(super) fn dashed(path: &Path, line_dash: LineDash<'_>) -> Path {
Path::new(|builder| {
let segments_odd = (line_dash.segments.len() % 2 == 1).then(|| {
[&line_dash.segments[..], &line_dash.segments[..]].concat()
});

let mut draw_line = false;

walk_along_path(
path.raw().iter().flattened(0.01),
0.0,
&mut RepeatedPattern {
callback: |position: lyon::algorithms::math::Point,
_tangent,
_distance| {
let point = Point {
x: position.x,
y: position.y,
};

if draw_line {
builder.line_to(point);
} else {
builder.move_to(point);
}

draw_line = !draw_line;

true
},
index: line_dash.offset,
intervals: segments_odd
.as_ref()
.map(Vec::as_slice)
.unwrap_or(line_dash.segments),
},
);
})
}
29 changes: 21 additions & 8 deletions graphics/src/widget/canvas/stroke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use iced_native::Color;

/// The style of a stroke.
#[derive(Debug, Clone, Copy)]
pub struct Stroke {
pub struct Stroke<'a> {
/// The color of the stroke.
pub color: Color,
/// The distance between the two edges of the stroke.
Expand All @@ -12,37 +12,40 @@ pub struct Stroke {
/// The shape to be used at the corners of paths or basic shapes when they
/// are stroked.
pub line_join: LineJoin,
/// The dash pattern used when stroking the line.
pub line_dash: LineDash<'a>,
}

impl Stroke {
impl<'a> Stroke<'a> {
/// Sets the color of the [`Stroke`].
pub fn with_color(self, color: Color) -> Stroke {
pub fn with_color(self, color: Color) -> Self {
Stroke { color, ..self }
}

/// Sets the width of the [`Stroke`].
pub fn with_width(self, width: f32) -> Stroke {
pub fn with_width(self, width: f32) -> Self {
Stroke { width, ..self }
}

/// Sets the [`LineCap`] of the [`Stroke`].
pub fn with_line_cap(self, line_cap: LineCap) -> Stroke {
pub fn with_line_cap(self, line_cap: LineCap) -> Self {
Stroke { line_cap, ..self }
}

/// Sets the [`LineJoin`] of the [`Stroke`].
pub fn with_line_join(self, line_join: LineJoin) -> Stroke {
pub fn with_line_join(self, line_join: LineJoin) -> Self {
Stroke { line_join, ..self }
}
}

impl Default for Stroke {
fn default() -> Stroke {
impl<'a> Default for Stroke<'a> {
fn default() -> Self {
Stroke {
color: Color::BLACK,
width: 1.0,
line_cap: LineCap::default(),
line_join: LineJoin::default(),
line_dash: LineDash::default(),
}
}
}
Expand Down Expand Up @@ -103,3 +106,13 @@ impl From<LineJoin> for lyon::tessellation::LineJoin {
}
}
}

/// The dash pattern used when stroking the line.
#[derive(Debug, Clone, Copy, Default)]
pub struct LineDash<'a> {
/// The alternating lengths of lines and gaps which describe the pattern.
pub segments: &'a [f32],

/// The offset of [`LineDash::segments`] to start the pattern.
pub offset: usize,
}