Skip to content

Commit

Permalink
Better document newish traits
Browse files Browse the repository at this point in the history
  • Loading branch information
ctrlcctrlv committed Dec 19, 2022
1 parent 2615fb8 commit aaccf5b
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 43 deletions.
62 changes: 36 additions & 26 deletions src/outline/contour.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub enum End {
Tail,
}

/// Generic prev/next given an index for [`Vec<T>`].
pub trait GenericPrevNext {
type Error;
/// Return the previous and next index, given an index.
Expand Down Expand Up @@ -50,42 +51,37 @@ pub trait GenericPrevNext {
}
}

/// Return the previous and next index, given an index. As opposed to ``GenericPrevNext``, this
/// always considers a contour's open/closed state (`assert!(self[0].ptype == PointType::Move)`).
pub trait PrevNext {
type Error;
/// Return the previous and next index, given an index. As opposed to ``GenericPrevNext``, this always
/// considers a contour's open/closed state (`assert!(self[0].ptype == PointType::Move)`).
fn contour_prev_next(&self, idx: usize) -> Result<(Option<usize>, Option<usize>), GlifParserError>;
fn contour_prev_next_handles(&self, idx: usize) -> Result<((Handle, Handle), (Handle, Handle)), GlifParserError>;
fn contour_prev_next_handles(
&self,
idx: usize,
) -> Result<((Handle, Handle), (Handle, Handle)), GlifParserError>;
}

impl<T> GenericPrevNext for Vec<T> {
type Error = GlifParserError;

fn idx_sane(&self, idx: usize) -> Result<(), GlifParserError> {
if idx >= self.len() {
return Err(GlifParserError::PointIdxOutOfBounds { idx, len: self.len() })
return Err(GlifParserError::PointIdxOutOfBounds { idx, len: self.len() });
} else if self.len() == 1 {
return Err(GlifParserError::ContourLenOneUnexpected)
return Err(GlifParserError::ContourLenOneUnexpected);
} else if self.len() == 0 {
return Err(GlifParserError::ContourLenZeroUnexpected)
return Err(GlifParserError::ContourLenZeroUnexpected);
}
Ok(())
}

fn prev_next(&self, idx: usize) -> Result<(usize, usize), GlifParserError> {
self.idx_sane(idx)?;

let prev = if idx == 0 {
self.len() - 1
} else {
idx - 1
};
let prev = if idx == 0 { self.len() - 1 } else { idx - 1 };

let next = if idx == self.len() - 1 {
0
} else {
idx + 1
};
let next = if idx == self.len() - 1 { 0 } else { idx + 1 };

Ok((prev, next))
}
Expand All @@ -102,6 +98,7 @@ impl<T> GenericPrevNext for Vec<T> {
}
}

/// Is contour open or closed?
pub trait State {
fn is_open(&self) -> bool;
fn is_closed(&self) -> bool {
Expand All @@ -113,18 +110,20 @@ impl<PD: PointData> State for Contour<PD> {
fn is_open(&self) -> bool {
match self.len() {
0 | 1 => true,
_ => self[0].ptype == PointType::Move
_ => self[0].ptype == PointType::Move,
}
}
}

/// Error will always be GlifParserError::PointIdxOutOfBounds
impl<PD: PointData> PrevNext for Contour<PD> {
type Error = GlifParserError;
/// Error will always be GlifParserError::PointIdxOutOfBounds
fn contour_prev_next(&self, idx: usize) -> Result<(Option<usize>, Option<usize>), GlifParserError> {
let (prev, next) = self.prev_next(idx)?;
if self.is_open() && self.idx_at_start_or_end(idx)? {
let end = self.idx_which_end(idx)?.expect("self.idx_at_start_or_end true but self.idx_which_end returned None???");
let end = self
.idx_which_end(idx)?
.expect("self.idx_at_start_or_end true but self.idx_which_end returned None???");
match end {
End::Head => Ok((None, Some(next))),
End::Tail => Ok((Some(prev), None)),
Expand All @@ -133,14 +132,22 @@ impl<PD: PointData> PrevNext for Contour<PD> {
Ok((Some(self.prev(idx)?), Some(self.next(idx)?)))
}
}
fn contour_prev_next_handles(&self, idx: usize) -> Result<((Handle, Handle), (Handle, Handle)), GlifParserError> {
fn contour_prev_next_handles(
&self,
idx: usize,
) -> Result<((Handle, Handle), (Handle, Handle)), GlifParserError> {
let (prev, next) = self.contour_prev_next(idx)?;
let prev = prev.map(|idx| (self[idx].a, self[idx].b)).unwrap_or((Handle::Colocated, Handle::Colocated));
let next = next.map(|idx| (self[idx].a, self[idx].b)).unwrap_or((Handle::Colocated, Handle::Colocated));
let prev = prev
.map(|idx| (self[idx].a, self[idx].b))
.unwrap_or((Handle::Colocated, Handle::Colocated));
let next = next
.map(|idx| (self[idx].a, self[idx].b))
.unwrap_or((Handle::Colocated, Handle::Colocated));
Ok((prev, next))
}
}

/// This validates the UFO `.glif` point `smooth` attribute.
pub trait CheckSmooth {
fn is_point_smooth_within(&self, idx: usize, within: f32) -> Result<bool, GlifParserError>;
fn is_point_smooth(&self, idx: usize) -> Result<bool, GlifParserError> {
Expand All @@ -161,21 +168,24 @@ impl<PD: PointData> CheckSmooth for Contour<PD> {
if let Some(prev) = prev {
(p.a, self[prev].b)
} else {
return Ok(false)
return Ok(false);
}
}
Some(End::Tail) => {
if let Some(next) = next {
(self[next].a, p.b)
} else {
return Ok(false)
return Ok(false);
}
}
};
let kp0 = kurbo::Point::new(p.x as f64, p.y as f64);
let (kp1, kp2) = match (a, b) {
(Handle::At(x1, y1), Handle::At(x2, y2)) => (kurbo::Point::new(x1 as f64, y1 as f64), kurbo::Point::new(x2 as f64, y2 as f64)),
_ => return Ok(false)
(Handle::At(x1, y1), Handle::At(x2, y2)) => (
kurbo::Point::new(x1 as f64, y1 as f64),
kurbo::Point::new(x2 as f64, y2 as f64),
),
_ => return Ok(false),
};
let line = kurbo::Line::new(kp1, kp2);
let nearest = line.nearest(kp0, 0.000001);
Expand Down
24 changes: 20 additions & 4 deletions src/outline/conv/penops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ use PointType::*;

use float_cmp::ApproxEq;

/// Representation of glyph data as pen operations, rather than as [`Point`]'s.
#[derive(Debug, Clone, PartialEq, IsVariant, Unwrap)]
pub enum PenOperations {
/// Should always be used even if first point at `(0. , 0.)`.
MoveTo(GlifPoint),
LineTo(GlifPoint),
QuadTo(GlifPoint, GlifPoint),
Expand Down Expand Up @@ -145,14 +147,26 @@ impl<PD: PointData> IntoPenOperations for Contour<PD> {
}
}

pub type PenOperationsPath = Vec<Vec<PenOperations>>;
/// A list of pen operations that should contain one and only one contour.
pub type PenOperationsContour = Vec<Vec<PenOperations>>;
/// A list of lists of pen operations creating an outline of [`Vec::len()`] contours.
pub type PenOperationsPath = Vec<PenOperationsContour>;

/// Split a long vec of pen operations into constitutent contours.
pub trait SplitPenOperations {
fn split_pen_operations(self) -> PenOperationsPath;
fn has_n_contours(&self, n: usize) -> bool {
self.split_pen_operations().len() == n
}
}

impl IsValid for PenOperationsContour {
fn is_valid(&self) -> bool {
self.has_n_contours(1)
}
}

impl SplitPenOperations for Vec<PenOperations> {
/// Split a long vec of pen operations into constitutent contours.
impl SplitPenOperations for PenOperationsContour {
fn split_pen_operations(self) -> PenOperationsPath {
let mut koutline = vec![];
let mut kcontour = vec![];
Expand Down Expand Up @@ -204,7 +218,9 @@ impl<PD: PointData> ToOutline<PD> for PenOperationsPath {
let mut next_points: Vec<GlifPoint>;
for (i, el) in skc.iter().enumerate() {
let points: Vec<GlifPoint> = el.clone().into();
if points.len() == 0 { continue }
if points.len() == 0 {
continue;
}
if i != skc_len - 1 {
next_points = skc[i + 1].clone().into();
} else {
Expand Down
34 changes: 26 additions & 8 deletions src/outline/kurbo.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
/// Kurbo module — warning: only guaranteed to round trip closed contours!
use kurbo::{BezPath, PathEl, PathEl::*};

use super::{conv::{PenOperations, SplitPenOperations as _}, Contour, Outline, ToOutline as _};
use super::{
conv::{PenOperations, SplitPenOperations as _},
Contour, Outline, ToOutline as _,
};
use crate::error::GlifParserError;
use crate::point::PointLike as _;
use crate::point::{GlifPoint, PointData, PointType, PointType::*};
Expand Down Expand Up @@ -59,21 +62,35 @@ impl Into<PathEl> for PenOperations {
impl From<PathEl> for PenOperations {
fn from(el: PathEl) -> PenOperations {
match el {
PathEl::MoveTo(gp) => PenOperations::MoveTo(GlifPoint::from_x_y_type((gp.x as f32, gp.y as f32), PointType::Move)),
PathEl::LineTo(gp) => PenOperations::LineTo(GlifPoint::from_x_y_type((gp.x as f32, gp.y as f32), PointType::Line)),
PathEl::QuadTo(gpa, gp) => PenOperations::QuadTo(GlifPoint::from_x_y_type((gp.x as f32, gp.y as f32), PointType::OffCurve), GlifPoint::from_x_y_type((gpa.x as f32, gpa.y as f32), PointType::QCurve)),
PathEl::CurveTo(gpa, gp2b, gp) => {
PenOperations::CurveTo(GlifPoint::from_x_y_type((gp.x as f32, gp.y as f32), PointType::OffCurve), GlifPoint::from_x_y_type((gp2b.x as f32, gp2b.y as f32), PointType::OffCurve), GlifPoint::from_x_y_type((gpa.x as f32, gpa.y as f32), PointType::Curve))
}
PathEl::MoveTo(gp) => PenOperations::MoveTo(GlifPoint::from_x_y_type(
(gp.x as f32, gp.y as f32),
PointType::Move,
)),
PathEl::LineTo(gp) => PenOperations::LineTo(GlifPoint::from_x_y_type(
(gp.x as f32, gp.y as f32),
PointType::Line,
)),
PathEl::QuadTo(gpa, gp) => PenOperations::QuadTo(
GlifPoint::from_x_y_type((gp.x as f32, gp.y as f32), PointType::OffCurve),
GlifPoint::from_x_y_type((gpa.x as f32, gpa.y as f32), PointType::QCurve),
),
PathEl::CurveTo(gpa, gp2b, gp) => PenOperations::CurveTo(
GlifPoint::from_x_y_type((gp.x as f32, gp.y as f32), PointType::OffCurve),
GlifPoint::from_x_y_type((gp2b.x as f32, gp2b.y as f32), PointType::OffCurve),
GlifPoint::from_x_y_type((gpa.x as f32, gpa.y as f32), PointType::Curve),
),
PathEl::ClosePath => PenOperations::Close,
}
}
}

/// Type (most useful on [`Outline`]) to [`kurbo::BezPath`]
pub trait IntoKurbo: Sized {
/// Implemented via [`crate::outline::IntoPenOperations`].
fn into_kurbo(self) -> Result<BezPath, GlifParserError> {
Ok(BezPath::from_vec(self.into_kurbo_vec()?))
}
/// In case you want to use [`PenOperations`].
fn into_kurbo_vec(self) -> Result<Vec<PathEl>, GlifParserError>;
}

Expand Down Expand Up @@ -104,6 +121,7 @@ impl<PD: PointData> IntoKurbo for Contour<PD> {
}
}

/// [`kurbo::BezPath`] to type (most useful for [`Outline`])
pub trait FromKurbo {
fn from_kurbo(kpath: &BezPath) -> Self;
}
Expand All @@ -130,7 +148,7 @@ impl IntoKurboPointsVec for PathEl {

impl<PD: PointData> FromKurbo for Outline<PD> {
fn from_kurbo(kpath: &BezPath) -> Self {
let gpself: Vec<PenOperations> = kpath.iter().map(|pe|pe.into()).collect();
let gpself: Vec<PenOperations> = kpath.iter().map(|pe| pe.into()).collect();
let koutline = gpself.split_pen_operations();
koutline.to_outline()
}
Expand Down
13 changes: 8 additions & 5 deletions src/outline/refigure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ use std::collections::VecDeque;

use integer_or_float::IntegerOrFloat::*;

/// Only knocks out _exactly equal_ floats. For colocated within see
/// [`MFEKmath::Fixup::assert_colocated_within`](
/// https://docs.rs/MFEKmath/latest/MFEKmath/trait.Fixup.html#tymethod.assert_colocated_within ).
pub trait RefigurePointTypes<PD: PointData> {
fn refigure_point_types(&mut self);
}
Expand All @@ -22,7 +25,6 @@ impl<PD: PointData> RefigurePointTypes<PD> for Outline<PD> {
impl<PD: PointData> RefigurePointTypes<PD> for Contour<PD> {
fn refigure_point_types(&mut self) {
for i in 0..self.len() {
// Only knocks out exactly equal floats. For colocated within see MFEKmath
if let Handle::At(ax, ay) = self[i].a {
if ax == self[i].x && ay == self[i].y {
self[i].a = Handle::Colocated;
Expand Down Expand Up @@ -72,10 +74,11 @@ impl<PD: PointData> PointTypeForIdx for Contour<PD> {
}
}

/// This trait is primarily intended for easing .glif equality testing internally by our test
/// This trait is primarily intended for easing `.glif` equality testing internally by our test
/// suite. It therefore doesn't do any of the fancy things it could like change point types and
/// assert handles as colocated. Consider MFEKmath::Refigure, RefigurePointTypes, etc., and not
/// this (or perhaps together?).
/// assert handles as colocated. Consider [`MFEKmath::Fixup`](
/// https://docs.rs/MFEKmath/latest/MFEKmath/trait.Fixup.html ), [`RefigurePointTypes`], etc., not
/// this. (…Or perhaps together?)
pub trait RoundToInt {
fn round_to_int(&mut self);
}
Expand Down Expand Up @@ -103,7 +106,7 @@ macro_rules! impl_rti {
}
}
}
}
};
}

impl_rti!(Vec);
Expand Down

0 comments on commit aaccf5b

Please sign in to comment.