use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::vector::Vector2F;
use std::mem;
pub trait OutlineSink {
fn move_to(&mut self, to: Vector2F);
fn line_to(&mut self, to: Vector2F);
fn quadratic_curve_to(&mut self, ctrl: Vector2F, to: Vector2F);
fn cubic_curve_to(&mut self, ctrl: LineSegment2F, to: Vector2F);
fn close(&mut self);
}
#[derive(Clone, PartialEq, Debug)]
pub struct Outline {
pub contours: Vec<Contour>,
}
#[derive(Clone, PartialEq, Debug)]
pub struct Contour {
pub positions: Vec<Vector2F>,
pub flags: Vec<PointFlags>,
}
bitflags! {
#[derive(Clone, Debug, PartialEq)]
pub struct PointFlags: u8 {
const CONTROL_POINT_0 = 0x01;
const CONTROL_POINT_1 = 0x02;
}
}
#[derive(Clone, Debug)]
pub struct OutlineBuilder {
outline: Outline,
current_contour: Contour,
}
impl Default for Outline {
fn default() -> Self {
Self::new()
}
}
impl Outline {
#[inline]
pub fn new() -> Outline {
Outline { contours: vec![] }
}
pub fn copy_to<S>(&self, sink: &mut S)
where
S: OutlineSink,
{
for contour in &self.contours {
contour.copy_to(sink);
}
}
}
impl Default for Contour {
fn default() -> Self {
Self::new()
}
}
impl Contour {
#[inline]
pub fn new() -> Contour {
Contour {
positions: vec![],
flags: vec![],
}
}
#[inline]
pub fn push(&mut self, position: Vector2F, flags: PointFlags) {
self.positions.push(position);
self.flags.push(flags);
}
pub fn copy_to<S>(&self, sink: &mut S)
where
S: OutlineSink,
{
debug_assert_eq!(self.positions.len(), self.flags.len());
if self.positions.is_empty() {
return;
}
sink.move_to(self.positions[0]);
let mut iter = self.positions[1..].iter().zip(self.flags[1..].iter());
while let Some((&position_0, flags_0)) = iter.next() {
if flags_0.is_empty() {
sink.line_to(position_0);
continue;
}
let (&position_1, flags_1) = iter.next().expect("Invalid outline!");
if flags_1.is_empty() {
sink.quadratic_curve_to(position_0, position_1);
continue;
}
let (&position_2, flags_2) = iter.next().expect("Invalid outline!");
debug_assert!(flags_2.is_empty());
sink.cubic_curve_to(LineSegment2F::new(position_0, position_1), position_2);
}
sink.close();
}
}
impl Default for OutlineBuilder {
fn default() -> Self {
Self::new()
}
}
impl OutlineBuilder {
#[inline]
pub fn new() -> OutlineBuilder {
OutlineBuilder {
outline: Outline::new(),
current_contour: Contour::new(),
}
}
#[inline]
pub fn into_outline(self) -> Outline {
self.outline
}
#[inline]
pub fn take_outline(&mut self) -> Outline {
assert!(self.current_contour.positions.is_empty());
self.current_contour = Contour::new();
mem::replace(&mut self.outline, Outline::new())
}
}
impl OutlineSink for OutlineBuilder {
#[inline]
fn move_to(&mut self, to: Vector2F) {
self.current_contour.push(to, PointFlags::empty());
}
#[inline]
fn line_to(&mut self, to: Vector2F) {
self.current_contour.push(to, PointFlags::empty());
}
#[inline]
fn quadratic_curve_to(&mut self, ctrl: Vector2F, to: Vector2F) {
self.current_contour.push(ctrl, PointFlags::CONTROL_POINT_0);
self.current_contour.push(to, PointFlags::empty());
}
#[inline]
fn cubic_curve_to(&mut self, ctrl: LineSegment2F, to: Vector2F) {
self.current_contour
.push(ctrl.from(), PointFlags::CONTROL_POINT_0);
self.current_contour
.push(ctrl.to(), PointFlags::CONTROL_POINT_1);
self.current_contour.push(to, PointFlags::empty());
}
#[inline]
fn close(&mut self) {
self.outline
.contours
.push(mem::replace(&mut self.current_contour, Contour::new()));
}
}