Skip to content

Commit aaccf5b

Browse files
committed
Better document newish traits
1 parent 2615fb8 commit aaccf5b

4 files changed

Lines changed: 90 additions & 43 deletions

File tree

src/outline/contour.rs

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub enum End {
1616
Tail,
1717
}
1818

19+
/// Generic prev/next given an index for [`Vec<T>`].
1920
pub trait GenericPrevNext {
2021
type Error;
2122
/// Return the previous and next index, given an index.
@@ -50,42 +51,37 @@ pub trait GenericPrevNext {
5051
}
5152
}
5253

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

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

6468
fn idx_sane(&self, idx: usize) -> Result<(), GlifParserError> {
6569
if idx >= self.len() {
66-
return Err(GlifParserError::PointIdxOutOfBounds { idx, len: self.len() })
70+
return Err(GlifParserError::PointIdxOutOfBounds { idx, len: self.len() });
6771
} else if self.len() == 1 {
68-
return Err(GlifParserError::ContourLenOneUnexpected)
72+
return Err(GlifParserError::ContourLenOneUnexpected);
6973
} else if self.len() == 0 {
70-
return Err(GlifParserError::ContourLenZeroUnexpected)
74+
return Err(GlifParserError::ContourLenZeroUnexpected);
7175
}
7276
Ok(())
7377
}
7478

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

78-
let prev = if idx == 0 {
79-
self.len() - 1
80-
} else {
81-
idx - 1
82-
};
82+
let prev = if idx == 0 { self.len() - 1 } else { idx - 1 };
8383

84-
let next = if idx == self.len() - 1 {
85-
0
86-
} else {
87-
idx + 1
88-
};
84+
let next = if idx == self.len() - 1 { 0 } else { idx + 1 };
8985

9086
Ok((prev, next))
9187
}
@@ -102,6 +98,7 @@ impl<T> GenericPrevNext for Vec<T> {
10298
}
10399
}
104100

101+
/// Is contour open or closed?
105102
pub trait State {
106103
fn is_open(&self) -> bool;
107104
fn is_closed(&self) -> bool {
@@ -113,18 +110,20 @@ impl<PD: PointData> State for Contour<PD> {
113110
fn is_open(&self) -> bool {
114111
match self.len() {
115112
0 | 1 => true,
116-
_ => self[0].ptype == PointType::Move
113+
_ => self[0].ptype == PointType::Move,
117114
}
118115
}
119116
}
120117

118+
/// Error will always be GlifParserError::PointIdxOutOfBounds
121119
impl<PD: PointData> PrevNext for Contour<PD> {
122120
type Error = GlifParserError;
123-
/// Error will always be GlifParserError::PointIdxOutOfBounds
124121
fn contour_prev_next(&self, idx: usize) -> Result<(Option<usize>, Option<usize>), GlifParserError> {
125122
let (prev, next) = self.prev_next(idx)?;
126123
if self.is_open() && self.idx_at_start_or_end(idx)? {
127-
let end = self.idx_which_end(idx)?.expect("self.idx_at_start_or_end true but self.idx_which_end returned None???");
124+
let end = self
125+
.idx_which_end(idx)?
126+
.expect("self.idx_at_start_or_end true but self.idx_which_end returned None???");
128127
match end {
129128
End::Head => Ok((None, Some(next))),
130129
End::Tail => Ok((Some(prev), None)),
@@ -133,14 +132,22 @@ impl<PD: PointData> PrevNext for Contour<PD> {
133132
Ok((Some(self.prev(idx)?), Some(self.next(idx)?)))
134133
}
135134
}
136-
fn contour_prev_next_handles(&self, idx: usize) -> Result<((Handle, Handle), (Handle, Handle)), GlifParserError> {
135+
fn contour_prev_next_handles(
136+
&self,
137+
idx: usize,
138+
) -> Result<((Handle, Handle), (Handle, Handle)), GlifParserError> {
137139
let (prev, next) = self.contour_prev_next(idx)?;
138-
let prev = prev.map(|idx| (self[idx].a, self[idx].b)).unwrap_or((Handle::Colocated, Handle::Colocated));
139-
let next = next.map(|idx| (self[idx].a, self[idx].b)).unwrap_or((Handle::Colocated, Handle::Colocated));
140+
let prev = prev
141+
.map(|idx| (self[idx].a, self[idx].b))
142+
.unwrap_or((Handle::Colocated, Handle::Colocated));
143+
let next = next
144+
.map(|idx| (self[idx].a, self[idx].b))
145+
.unwrap_or((Handle::Colocated, Handle::Colocated));
140146
Ok((prev, next))
141147
}
142148
}
143149

150+
/// This validates the UFO `.glif` point `smooth` attribute.
144151
pub trait CheckSmooth {
145152
fn is_point_smooth_within(&self, idx: usize, within: f32) -> Result<bool, GlifParserError>;
146153
fn is_point_smooth(&self, idx: usize) -> Result<bool, GlifParserError> {
@@ -161,21 +168,24 @@ impl<PD: PointData> CheckSmooth for Contour<PD> {
161168
if let Some(prev) = prev {
162169
(p.a, self[prev].b)
163170
} else {
164-
return Ok(false)
171+
return Ok(false);
165172
}
166173
}
167174
Some(End::Tail) => {
168175
if let Some(next) = next {
169176
(self[next].a, p.b)
170177
} else {
171-
return Ok(false)
178+
return Ok(false);
172179
}
173180
}
174181
};
175182
let kp0 = kurbo::Point::new(p.x as f64, p.y as f64);
176183
let (kp1, kp2) = match (a, b) {
177-
(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)),
178-
_ => return Ok(false)
184+
(Handle::At(x1, y1), Handle::At(x2, y2)) => (
185+
kurbo::Point::new(x1 as f64, y1 as f64),
186+
kurbo::Point::new(x2 as f64, y2 as f64),
187+
),
188+
_ => return Ok(false),
179189
};
180190
let line = kurbo::Line::new(kp1, kp2);
181191
let nearest = line.nearest(kp0, 0.000001);

src/outline/conv/penops.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ use PointType::*;
99

1010
use float_cmp::ApproxEq;
1111

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

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

155+
/// Split a long vec of pen operations into constitutent contours.
150156
pub trait SplitPenOperations {
151157
fn split_pen_operations(self) -> PenOperationsPath;
158+
fn has_n_contours(&self, n: usize) -> bool {
159+
self.split_pen_operations().len() == n
160+
}
161+
}
162+
163+
impl IsValid for PenOperationsContour {
164+
fn is_valid(&self) -> bool {
165+
self.has_n_contours(1)
166+
}
152167
}
153168

154-
impl SplitPenOperations for Vec<PenOperations> {
155-
/// Split a long vec of pen operations into constitutent contours.
169+
impl SplitPenOperations for PenOperationsContour {
156170
fn split_pen_operations(self) -> PenOperationsPath {
157171
let mut koutline = vec![];
158172
let mut kcontour = vec![];
@@ -204,7 +218,9 @@ impl<PD: PointData> ToOutline<PD> for PenOperationsPath {
204218
let mut next_points: Vec<GlifPoint>;
205219
for (i, el) in skc.iter().enumerate() {
206220
let points: Vec<GlifPoint> = el.clone().into();
207-
if points.len() == 0 { continue }
221+
if points.len() == 0 {
222+
continue;
223+
}
208224
if i != skc_len - 1 {
209225
next_points = skc[i + 1].clone().into();
210226
} else {

src/outline/kurbo.rs

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
/// Kurbo module — warning: only guaranteed to round trip closed contours!
22
use kurbo::{BezPath, PathEl, PathEl::*};
33

4-
use super::{conv::{PenOperations, SplitPenOperations as _}, Contour, Outline, ToOutline as _};
4+
use super::{
5+
conv::{PenOperations, SplitPenOperations as _},
6+
Contour, Outline, ToOutline as _,
7+
};
58
use crate::error::GlifParserError;
69
use crate::point::PointLike as _;
710
use crate::point::{GlifPoint, PointData, PointType, PointType::*};
@@ -59,21 +62,35 @@ impl Into<PathEl> for PenOperations {
5962
impl From<PathEl> for PenOperations {
6063
fn from(el: PathEl) -> PenOperations {
6164
match el {
62-
PathEl::MoveTo(gp) => PenOperations::MoveTo(GlifPoint::from_x_y_type((gp.x as f32, gp.y as f32), PointType::Move)),
63-
PathEl::LineTo(gp) => PenOperations::LineTo(GlifPoint::from_x_y_type((gp.x as f32, gp.y as f32), PointType::Line)),
64-
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)),
65-
PathEl::CurveTo(gpa, gp2b, gp) => {
66-
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))
67-
}
65+
PathEl::MoveTo(gp) => PenOperations::MoveTo(GlifPoint::from_x_y_type(
66+
(gp.x as f32, gp.y as f32),
67+
PointType::Move,
68+
)),
69+
PathEl::LineTo(gp) => PenOperations::LineTo(GlifPoint::from_x_y_type(
70+
(gp.x as f32, gp.y as f32),
71+
PointType::Line,
72+
)),
73+
PathEl::QuadTo(gpa, gp) => PenOperations::QuadTo(
74+
GlifPoint::from_x_y_type((gp.x as f32, gp.y as f32), PointType::OffCurve),
75+
GlifPoint::from_x_y_type((gpa.x as f32, gpa.y as f32), PointType::QCurve),
76+
),
77+
PathEl::CurveTo(gpa, gp2b, gp) => PenOperations::CurveTo(
78+
GlifPoint::from_x_y_type((gp.x as f32, gp.y as f32), PointType::OffCurve),
79+
GlifPoint::from_x_y_type((gp2b.x as f32, gp2b.y as f32), PointType::OffCurve),
80+
GlifPoint::from_x_y_type((gpa.x as f32, gpa.y as f32), PointType::Curve),
81+
),
6882
PathEl::ClosePath => PenOperations::Close,
6983
}
7084
}
7185
}
7286

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

@@ -104,6 +121,7 @@ impl<PD: PointData> IntoKurbo for Contour<PD> {
104121
}
105122
}
106123

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

131149
impl<PD: PointData> FromKurbo for Outline<PD> {
132150
fn from_kurbo(kpath: &BezPath) -> Self {
133-
let gpself: Vec<PenOperations> = kpath.iter().map(|pe|pe.into()).collect();
151+
let gpself: Vec<PenOperations> = kpath.iter().map(|pe| pe.into()).collect();
134152
let koutline = gpself.split_pen_operations();
135153
koutline.to_outline()
136154
}

src/outline/refigure.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ use std::collections::VecDeque;
77

88
use integer_or_float::IntegerOrFloat::*;
99

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

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

109112
impl_rti!(Vec);

0 commit comments

Comments
 (0)