From e25bfa0ec567d996f4a8b1ba770ed2393af17a71 Mon Sep 17 00:00:00 2001 From: Fredrick Brennan Date: Sat, 18 Dec 2021 23:05:41 -0500 Subject: [PATCH] New API: `Point::is_valid` Cf. MFEK/stroke#21 (rationale, we'd catch `NaN` bugs earlier if we refused to write at all) Cf. linebender/norad#198 (similar problem in a competing/featurewise inferior library) --- src/glif/write.rs | 1 + src/point.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/glif/write.rs b/src/glif/write.rs index fba500f1..420efa71 100644 --- a/src/glif/write.rs +++ b/src/glif/write.rs @@ -115,6 +115,7 @@ pub fn write_ufo_glif_data(glif: &Glif) -> Result, Gl } let mut point_node = xmltree::Element::new("point"); + debug_assert!(point.is_valid(None)); point_node.attributes.insert("x".to_owned(), point.x.to_string()); point_node.attributes.insert("y".to_owned(), point.y.to_string()); diff --git a/src/point.rs b/src/point.rs index 0a33e917..f22234a3 100644 --- a/src/point.rs +++ b/src/point.rs @@ -104,6 +104,36 @@ pub struct Point { pub data: Option, } +impl Point { + /// `validate_data` parameter allows you to define an `is_valid` (or whatever) impl on your + /// `PointData` struct's. You can then pass the function while validating the point as e.g. + /// `Some(MyPointData::is_valid)`. It takes an `Option<&PD>` so that you have the choice as to + /// whether it's valid or not for your type not to be defined; `Point.data` should probably not + /// be defined as an Option, but removing that's TODO. This API will change when that does + /// and should be considered unstable/testing. + pub fn is_valid(&self, validate_data: Option)->bool>) -> bool { + if let Some(validate_point_data_function) = validate_data { + if !validate_point_data_function(self.data.as_ref()) { + return false + } + } + if self.ptype == PointType::Undefined { + return false + } + if self.x.is_subnormal() || self.y.is_subnormal() { + return false + } + for handle in [self.handle(WhichHandle::A), self.handle(WhichHandle::B)] { + if let Handle::At(hx, hy) = handle { + if hx.is_subnormal() || hy.is_subnormal() { + return false + } + } + } + true + } +} + /// For use by ``Point::handle_or_colocated`` /// TODO: Replace with Option #[cfg_attr(feature = "glifserde", derive(Serialize, Deserialize))]