interpolatable/
problems.rs

1use serde::Serialize;
2
3use crate::Glyph;
4
5#[derive(Debug, Serialize)]
6/// An interpolation problem between two masters.
7pub struct Problem {
8    /// The name of the first master.
9    pub master_1_name: String,
10    /// The name of the second master.
11    pub master_2_name: String,
12    /// The index of the first master.
13    pub master_1_index: usize,
14    /// The index of the second master.
15    pub master_2_index: usize,
16    /// Describes the problem in detail.
17    #[serde(flatten)]
18    pub details: ProblemDetails,
19    /// The tolerance for the problem, if applicable.
20    pub tolerance: Option<f64>,
21    /// The index of the contour in the glyph, if applicable.
22    pub contour: Option<usize>,
23    /// The index of the node in the contour, if applicable.
24    pub node: Option<usize>,
25}
26
27#[derive(Debug, Serialize)]
28#[serde(tag = "type")]
29/// The particular problem found
30pub enum ProblemDetails {
31    /// The number of paths in the two masters is different.
32    PathCount {
33        /// The number of paths in the first master.
34        count_1: usize,
35        /// The number of paths in the second master.
36        count_2: usize,
37    },
38    /// The number of nodes in the two masters is different.
39    NodeCount {
40        /// The number of nodes in the first master.
41        count_1: usize,
42        /// The number of nodes in the second master.
43        count_2: usize,
44    },
45    /// The nodes in the two masters are incompatible.
46    NodeIncompatibility {
47        /// Whether the node in the first master is a control point.
48        is_control_1: bool,
49        /// Whether the node in the second master is a control point.
50        is_control_2: bool,
51    },
52    /// The order of the contours in the two masters is different.
53    ContourOrder {
54        /// The order of the contours in the first master.
55        order_1: Vec<usize>,
56        /// The order of the contours in the second master.
57        order_2: Vec<usize>,
58    },
59    /// The start point of the contour in the two masters is different.
60    WrongStartPoint {
61        /// What the start point in the second master should be.
62        proposed_point: usize,
63        /// Whether the contour in the second master is reversed.
64        reverse: bool,
65    },
66    /// The contour in the second master overweight compared to the first master.
67    Overweight {
68        /// The perceptual weight in the first master.
69        value_1: f64,
70        /// The perceptual weight in the second master.
71        value_2: f64,
72    },
73    /// The contour in the second master underweight compared to the first master.
74    Underweight {
75        /// The perceptual weight in the first master.
76        value_1: f64,
77        /// The perceptual weight in the second master.
78        value_2: f64,
79    },
80    /// The contour in the second master has a kink compared to the first master.
81    Kink,
82}
83
84impl Problem {
85    pub(crate) fn path_count(g1: &Glyph, g2: &Glyph, count_1: usize, count_2: usize) -> Problem {
86        Problem {
87            master_1_name: g1.master_name.to_string(),
88            master_2_name: g2.master_name.to_string(),
89            master_1_index: g1.master_index,
90            master_2_index: g2.master_index,
91            tolerance: None,
92            contour: None,
93            node: None,
94            details: ProblemDetails::PathCount { count_1, count_2 },
95        }
96    }
97
98    pub(crate) fn node_count(
99        g1: &Glyph,
100        g2: &Glyph,
101        path_index: usize,
102        count_1: usize,
103        count_2: usize,
104    ) -> Problem {
105        Problem {
106            master_1_name: g1.master_name.to_string(),
107            master_2_name: g2.master_name.to_string(),
108            master_1_index: g1.master_index,
109            master_2_index: g2.master_index,
110            tolerance: None,
111            contour: Some(path_index),
112            node: None,
113            details: ProblemDetails::NodeCount { count_1, count_2 },
114        }
115    }
116
117    pub(crate) fn node_incompatibility(
118        g1: &Glyph,
119        g2: &Glyph,
120        contour: usize,
121        node: usize,
122        is_control_1: bool,
123        is_control_2: bool,
124    ) -> Problem {
125        Problem {
126            master_1_name: g1.master_name.to_string(),
127            master_2_name: g2.master_name.to_string(),
128            master_1_index: g1.master_index,
129            master_2_index: g2.master_index,
130            contour: Some(contour),
131            node: Some(node),
132            tolerance: None,
133            details: ProblemDetails::NodeIncompatibility {
134                is_control_1,
135                is_control_2,
136            },
137        }
138    }
139
140    pub(crate) fn contour_order(
141        g1: &Glyph,
142        g2: &Glyph,
143        tolerance: f64,
144        order_1: Vec<usize>,
145        order_2: Vec<usize>,
146    ) -> Problem {
147        Problem {
148            master_1_name: g1.master_name.to_string(),
149            master_2_name: g2.master_name.to_string(),
150            master_1_index: g1.master_index,
151            master_2_index: g2.master_index,
152            tolerance: Some(tolerance),
153            contour: None,
154            node: None,
155            details: ProblemDetails::ContourOrder { order_1, order_2 },
156        }
157    }
158
159    pub(crate) fn wrong_start_point(
160        g1: &Glyph,
161        g2: &Glyph,
162        tolerance: f64,
163        contour: usize,
164        proposed_point: usize,
165        reverse: bool,
166    ) -> Problem {
167        Problem {
168            master_1_name: g1.master_name.to_string(),
169            master_2_name: g2.master_name.to_string(),
170            master_1_index: g1.master_index,
171            master_2_index: g2.master_index,
172            tolerance: Some(tolerance),
173            contour: Some(contour),
174            node: None,
175            details: ProblemDetails::WrongStartPoint {
176                proposed_point,
177                reverse,
178            },
179        }
180    }
181
182    pub(crate) fn overweight(
183        g1: &Glyph,
184        g2: &Glyph,
185        contour: usize,
186        tolerance: f64,
187        value_1: f64,
188        value_2: f64,
189    ) -> Problem {
190        Problem {
191            master_1_name: g1.master_name.to_string(),
192            master_2_name: g2.master_name.to_string(),
193            master_1_index: g1.master_index,
194            master_2_index: g2.master_index,
195            contour: Some(contour),
196            tolerance: Some(tolerance),
197            node: None,
198            details: ProblemDetails::Overweight { value_1, value_2 },
199        }
200    }
201
202    pub(crate) fn underweight(
203        g1: &Glyph,
204        g2: &Glyph,
205        contour: usize,
206        tolerance: f64,
207        value_1: f64,
208        value_2: f64,
209    ) -> Problem {
210        Problem {
211            master_1_name: g1.master_name.to_string(),
212            master_2_name: g2.master_name.to_string(),
213            master_1_index: g1.master_index,
214            master_2_index: g2.master_index,
215            contour: Some(contour),
216            tolerance: Some(tolerance),
217            details: ProblemDetails::Underweight { value_1, value_2 },
218            node: None,
219        }
220    }
221
222    pub(crate) fn kink(
223        g1: &Glyph,
224        g2: &Glyph,
225        contour: usize,
226        node: usize,
227        tolerance: f64,
228    ) -> Problem {
229        Problem {
230            master_1_name: g1.master_name.to_string(),
231            master_2_name: g2.master_name.to_string(),
232            master_1_index: g1.master_index,
233            master_2_index: g2.master_index,
234            contour: Some(contour),
235            node: Some(node),
236            tolerance: Some(tolerance),
237            details: ProblemDetails::Kink,
238        }
239    }
240
241    /// Returns the type of problem as a string.
242    pub fn problem_type(&self) -> String {
243        match self.details {
244            ProblemDetails::PathCount { .. } => "PathCount".to_string(),
245            ProblemDetails::NodeCount { .. } => "NodeCount".to_string(),
246            ProblemDetails::NodeIncompatibility { .. } => "NodeIncompatibility".to_string(),
247            ProblemDetails::ContourOrder { .. } => "ContourOrder".to_string(),
248            ProblemDetails::WrongStartPoint { .. } => "WrongStartPoint".to_string(),
249            ProblemDetails::Overweight { .. } => "Overweight".to_string(),
250            ProblemDetails::Underweight { .. } => "Underweight".to_string(),
251            ProblemDetails::Kink => "Kink".to_string(),
252        }
253    }
254}