121 lines
3.5 KiB
Rust
121 lines
3.5 KiB
Rust
|
// pathfinder/geometry/src/dash.rs
|
||
|
//
|
||
|
// Copyright © 2019 The Pathfinder Project Developers.
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||
|
// option. This file may not be copied, modified, or distributed
|
||
|
// except according to those terms.
|
||
|
|
||
|
//! Line dashing support.
|
||
|
|
||
|
use crate::outline::{Contour, Outline, PushSegmentFlags};
|
||
|
use std::mem;
|
||
|
|
||
|
pub struct OutlineDash<'a> {
|
||
|
input: &'a Outline,
|
||
|
output: Outline,
|
||
|
state: DashState<'a>,
|
||
|
}
|
||
|
|
||
|
impl<'a> OutlineDash<'a> {
|
||
|
#[inline]
|
||
|
pub fn new(input: &'a Outline, dashes: &'a [f32]) -> OutlineDash<'a> {
|
||
|
OutlineDash { input, output: Outline::new(), state: DashState::new(dashes) }
|
||
|
}
|
||
|
|
||
|
pub fn dash(&mut self) {
|
||
|
for contour in &self.input.contours {
|
||
|
ContourDash::new(contour, &mut self.output, &mut self.state).dash()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn into_outline(mut self) -> Outline {
|
||
|
if self.state.is_on() {
|
||
|
self.output.push_contour(self.state.output);
|
||
|
}
|
||
|
self.output
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct ContourDash<'a, 'b, 'c> {
|
||
|
input: &'a Contour,
|
||
|
output: &'b mut Outline,
|
||
|
state: &'c mut DashState<'a>,
|
||
|
}
|
||
|
|
||
|
impl<'a, 'b, 'c> ContourDash<'a, 'b, 'c> {
|
||
|
fn new(input: &'a Contour, output: &'b mut Outline, state: &'c mut DashState<'a>)
|
||
|
-> ContourDash<'a, 'b, 'c> {
|
||
|
ContourDash { input, output, state }
|
||
|
}
|
||
|
|
||
|
fn dash(&mut self) {
|
||
|
let (mut iterator, mut queued_segment) = (self.input.iter(), None);
|
||
|
loop {
|
||
|
if queued_segment.is_none() {
|
||
|
match iterator.next() {
|
||
|
None => break,
|
||
|
Some(segment) => queued_segment = Some(segment),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
let mut current_segment = queued_segment.take().unwrap();
|
||
|
let mut distance = self.state.distance_left;
|
||
|
|
||
|
let t = current_segment.time_for_distance(distance);
|
||
|
if t < 1.0 {
|
||
|
let (prev_segment, next_segment) = current_segment.split(t);
|
||
|
current_segment = prev_segment;
|
||
|
queued_segment = Some(next_segment);
|
||
|
} else {
|
||
|
distance = current_segment.arc_length();
|
||
|
}
|
||
|
|
||
|
if self.state.is_on() {
|
||
|
self.state.output.push_segment(¤t_segment, PushSegmentFlags::empty());
|
||
|
}
|
||
|
|
||
|
self.state.distance_left -= distance;
|
||
|
if self.state.distance_left < EPSILON {
|
||
|
if self.state.is_on() {
|
||
|
self.output.push_contour(mem::replace(&mut self.state.output, Contour::new()));
|
||
|
}
|
||
|
|
||
|
self.state.current_dash_index += 1;
|
||
|
if self.state.current_dash_index == self.state.dashes.len() {
|
||
|
self.state.current_dash_index = 0;
|
||
|
}
|
||
|
|
||
|
self.state.distance_left = self.state.dashes[self.state.current_dash_index];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const EPSILON: f32 = 0.0001;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct DashState<'a> {
|
||
|
output: Contour,
|
||
|
dashes: &'a [f32],
|
||
|
current_dash_index: usize,
|
||
|
distance_left: f32,
|
||
|
}
|
||
|
|
||
|
impl<'a> DashState<'a> {
|
||
|
fn new(dashes: &'a [f32]) -> DashState<'a> {
|
||
|
DashState {
|
||
|
output: Contour::new(),
|
||
|
dashes,
|
||
|
current_dash_index: 0,
|
||
|
distance_left: dashes[0],
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[inline]
|
||
|
fn is_on(&self) -> bool {
|
||
|
self.current_dash_index % 2 == 0
|
||
|
}
|
||
|
}
|