1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use super::*;
use kurbo::Point as KurboPoint;

pub trait IsValid: Debug {
    /// You'll need to redefine this if you want to use it on PointData.
    fn is_valid(&self) -> bool { true }
    fn expect_valid(&self) {
        if !self.is_valid() {
            panic!("Failed validity check! {:?}", self)
        }
    }
}

pub trait PointLike: IsValid {
    fn x(&self) -> IntegerOrFloat;
    fn y(&self) -> IntegerOrFloat;
    fn set_x(&mut self, x: IntegerOrFloat);
    fn set_y(&mut self, y: IntegerOrFloat);
    fn x32(&self) -> f32 { f32::from(self.x()) }
    fn y32(&self) -> f32 { f32::from(self.y()) }
    fn x64(&self) -> f64 { f64::from(self.x()) }
    fn y64(&self) -> f64 { f64::from(self.y()) }
    fn as_kpoint(&self) -> KurboPoint {
        KurboPoint::new(self.x64(), self.y64())
    }
}

impl<PD: PointData> IsValid for Point<PD> {
    /// `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<PD>, but removing that's TODO. This API will change when that does
    /// and should be considered unstable/testing.
    fn is_valid(&self) -> bool {
        if let Some(pd) = self.data.as_ref() {
            if !pd.is_valid() {
                return false;
            }
        }

        if self.ptype == PointType::Undefined {
            return false;
        }
        if self.x.is_nan() || 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_nan() || hy.is_subnormal() {
                    return false;
                }
            }
        }
        true
    }
}

impl<PD: PointData> PointLike for Point<PD> {
    fn x(&self) -> IntegerOrFloat {
        IntegerOrFloat::from(self.x)
    }
    fn y(&self) -> IntegerOrFloat {
        IntegerOrFloat::from(self.y)
    }
    fn set_x(&mut self, x: IntegerOrFloat) {
        self.x = f32::from(x);
    }
    fn set_y(&mut self, y: IntegerOrFloat) {
        self.y = f32::from(y);
    }
}

impl IsValid for GlifPoint {
    fn is_valid(&self) -> bool {
        if self.ptype == PointType::Undefined {
            return false;
        }
        if self
            .x
            .holding_float()
            .map(|x| x.is_subnormal() || x.is_nan())
            .unwrap_or(false)
        {
            return false;
        }
        if self
            .y
            .holding_float()
            .map(|y| y.is_subnormal() || y.is_nan())
            .unwrap_or(false)
        {
            return false;
        }
        true
    }
}

impl PointLike for GlifPoint {
    fn x(&self) -> IntegerOrFloat {
        self.x
    }
    fn y(&self) -> IntegerOrFloat {
        self.y
    }
    fn set_x(&mut self, x: IntegerOrFloat) {
        self.x = x;
    }
    fn set_y(&mut self, y: IntegerOrFloat) {
        self.y = y;
    }
}