// pathfinder/geometry/src/dash.rs // // Copyright © 2019 The Pathfinder Project Developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , 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 } }