1use bit_vec::BitVec;
2use visioncortex::BinaryImage;
3use stats::ShapeStats;
4use std::cmp::Ordering;
5
6pub trait Trace {
7 fn bits(&self) -> &BitVec;
8
9 fn diff(&self, other: &Self) -> usize {
11 let (mut self_clone, mut other_clone) = (self.bits().clone(), other.bits().clone());
12 self_clone.difference(&other.bits());
13 other_clone.difference(&self.bits());
14 self_clone.or(&other_clone);
15 self_clone.into_iter().filter(|bit| *bit).count()
16 }
17
18
19 fn from_image(image: &BinaryImage, tolerance: f64) -> Self;
20}
21
22#[derive(Debug)]
23pub struct GlyphTrace {
24 pub bits: BitVec,
25}
26
27impl Trace for GlyphTrace {
28 fn bits(&self) -> &BitVec {
29 &self.bits
30 }
31
32 fn from_image(image: &BinaryImage, tolerance: f64) -> Self {
33 let mut layer_traces = vec![];
34 layer_traces.push(LayerTrace::from_image(image, tolerance));
38 Self::from_layer_traces(layer_traces)
39 }
40}
41
42impl GlyphTrace {
43 pub fn from_layer_traces(layer_traces: Vec<LayerTrace>) -> Self {
44 let total_length = layer_traces.len() * LayerTrace::LENGTH;
45 Self {
46 bits: BitVec::from_fn(total_length, |i| {layer_traces[i/LayerTrace::LENGTH].bits.get(i%LayerTrace::LENGTH).unwrap()}),
47 }
48 }
49}
50
51#[derive(Debug)]
52pub struct LayerTrace {
53 pub bits: BitVec,
54}
55
56impl Trace for LayerTrace {
57 fn bits(&self) -> &BitVec {
58 &self.bits
59 }
60
61 #[allow(unused_assignments)]
62 fn from_image(image: &BinaryImage, tolerance: f64) -> Self {
63 let stats = ShapeStats::from_image(image, tolerance);
64 if stats.is_empty() {
65 return Self::default();
66 }
67
68 let mut bits = BitVec::from_elem(Self::LENGTH, true); let mut bit_offset = 0;
71 macro_rules! set_bits {
72 ($fun:ident) => {
73 match stats.$fun() {
74 Ordering::Less => bits.set(bit_offset, false),
75 Ordering::Greater => bits.set(bit_offset+1, false),
76 Ordering::Equal => {},
77 }
78 bit_offset += 2;
79 }
80 }
81
82 set_bits!(vertical_comparison);
83 set_bits!(horizontal_comparison);
84 set_bits!(diagonal_comparison);
85 set_bits!(a_b_comparison);
86 set_bits!(a_c_comparison);
87 set_bits!(a_d_comparison);
88 set_bits!(b_c_comparison);
89 set_bits!(b_d_comparison);
90 set_bits!(c_d_comparison);
91 set_bits!(ef_gh_comparison);
92
93 Self {
94 bits
95 }
96 }
97}
98
99impl Default for LayerTrace {
100 fn default() -> Self {
101 Self {
102 bits: BitVec::from_elem(Self::LENGTH, false),
103 }
104 }
105}
106
107impl LayerTrace {
112 const LENGTH: usize = ShapeStats::NUM_COMPARISONS << 1;
114}
115
116#[cfg(test)]
117mod tests {
118
119 use super::*;
120
121 const T: bool = true;
122 const F: bool = false;
123
124 #[test]
125 fn trace_diff() {
126 let a = &mut LayerTrace::default();
127 let b = &mut LayerTrace::default();
128 assert_eq!(a.diff(b), 0);
129
130 a.bits.set(0, true);
131 assert_eq!(a.diff(b), 1);
132 b.bits.set(0, true);
133 assert_eq!(a.diff(b), 0);
134
135 a.bits.set(2, true);
136 a.bits.set(5, true);
137 b.bits.set(4, true);
138 assert_eq!(a.diff(b), 3);
139
140 b.bits.set(5, true);
141 assert_eq!(a.diff(b), 2);
142 }
143
144 #[test]
145 fn layer_trace_from_image() {
146 let encoding = &LayerTrace::from_image(
148 &BinaryImage::from_string(
149 &("-*\n".to_owned() +
150 "--")
151 ),
152 0.0
153 );
154 assert!(encoding.bits.eq_vec(&[
155 T,F,F,T,F,T,F,T,T,T,T,T,T,F,T,F,T,T
156 ]));
157
158 let encoding = &LayerTrace::from_image(
160 &BinaryImage::from_string(
161 &("*-\n".to_owned() +
162 "-*")
163 ),
164 0.0
165 );
166 assert!(encoding.bits.eq_vec(&[
167 T,T,T,T,T,F,T,F,T,F,T,T,T,T,F,T,F,T
168 ]));
169 }
170
171 #[test]
172 fn layer_trace_empty_image() {
173 let encoding = &LayerTrace::from_image(
175 &BinaryImage::from_string(
176 &("--\n".to_owned() +
177 "--")
178 ),
179 0.0
180 );
181 println!("{:?}", encoding.bits);
182 assert!(!encoding.bits.any());
183 }
184
185 #[test]
186 fn glyph_trace_empty() {
187 let encoding = &GlyphTrace::from_image(
188 &BinaryImage::from_string(
189 &("----\n".to_owned() +
190 "----\n" +
191 "----\n" +
192 "----\n"
193 )
194 ),
195 0.0
196 );
197 assert!(!encoding.bits.any());
198 }
199
200 #[test]
201 fn glyph_trace_typical() {
202 let encoding = &GlyphTrace::from_image(
204 &BinaryImage::from_string(
205 &("*---\n".to_owned() +
206 "--*-\n" +
207 "*--*\n" +
208 "--*-\n"
209 )
210 ),
211 0.0
212 );
213
214 assert!(encoding.bits.eq_vec(&[
215 F,T,F,T,T,F,T,T,T,T,F,T,T,T,F,T,F,T
216 ]));
217 }
218}
219
220mod stats {
221 use std::cmp::Ordering;
222
223 use visioncortex::{BinaryImage, Sampler};
224
225 #[derive(Debug)]
226 pub struct ShapeStats {
227 top_left: usize,
229 top_right: usize,
231 bot_left: usize,
233 bot_right: usize,
235 top: usize,
237 bottom: usize,
239 left: usize,
241 right: usize,
243 tolerance: f64,
244 }
245
246 impl ShapeStats {
247 pub fn from_image(image: &BinaryImage, tolerance: f64) -> Self {
248 let horiz_q1 = image.width;
250 let vert_q1 = image.height;
251 let horiz_mid = image.width << 1;
252 let vert_mid = image.height << 1;
253 let horiz_q3 = horiz_mid + horiz_q1;
254 let vert_q3 = vert_mid + vert_q1;
255
256 let image = &Sampler::resample_image(image, image.width*4, image.height*4);
257 let sampler = Sampler::new(image);
258
259 let top_left = sampler.sample(0, 0, horiz_mid, vert_mid);
260 let top_right = sampler.sample(horiz_mid, 0, sampler.image.width, vert_mid);
261 let bot_left = sampler.sample(0, vert_mid, horiz_mid, sampler.image.height);
262 let bot_right = sampler.sample(horiz_mid,vert_mid, sampler.image.width, sampler.image.height);
263
264 let top = sampler.sample(horiz_q1, 0, horiz_q3, vert_q1);
265 let bottom = sampler.sample(horiz_q1, vert_q3, horiz_q3, sampler.image.height);
266 let left = sampler.sample(0, vert_q1, horiz_q1, vert_q3);
267 let right = sampler.sample(horiz_q3, vert_q1, sampler.image.width, vert_q3);
268
269 Self {
270 top_left,
271 top_right,
272 bot_left,
273 bot_right,
274 top,
275 bottom,
276 left,
277 right,
278 tolerance,
279 }
280 }
281
282 pub fn is_empty(&self) -> bool {
283 self.top_left + self.top_right + self.bot_left + self.bot_right == 0
284 }
285
286 pub const NUM_COMPARISONS: usize = 10;
287
288 pub fn vertical_comparison(&self) -> Ordering {
290 Self::approximate_compare(self.top_left + self.top_right, self.bot_left + self.bot_right, self.tolerance)
291 }
292
293 pub fn horizontal_comparison(&self) -> Ordering {
295 Self::approximate_compare(self.top_left + self.bot_left, self.top_right + self.bot_right, self.tolerance)
296 }
297
298 pub fn diagonal_comparison(&self) -> Ordering {
300 Self::approximate_compare(self.top_left + self.bot_right, self.top_right + self.bot_left, self.tolerance)
301 }
302
303 pub fn a_b_comparison(&self) -> Ordering {
304 Self::approximate_compare(self.top_left, self.top_right, self.tolerance)
305 }
306
307 pub fn c_d_comparison(&self) -> Ordering {
308 Self::approximate_compare(self.bot_left, self.bot_right, self.tolerance)
309 }
310
311 pub fn a_c_comparison(&self) -> Ordering {
312 Self::approximate_compare(self.top_left, self.bot_left, self.tolerance)
313 }
314
315 pub fn b_d_comparison(&self) -> Ordering {
316 Self::approximate_compare(self.top_right, self.bot_right, self.tolerance)
317 }
318
319 pub fn a_d_comparison(&self) -> Ordering {
320 Self::approximate_compare(self.top_left, self.bot_right, self.tolerance)
321 }
322
323 pub fn b_c_comparison(&self) -> Ordering {
324 Self::approximate_compare(self.top_right, self.bot_left, self.tolerance)
325 }
326
327 pub fn ef_gh_comparison(&self) -> Ordering {
328 Self::approximate_compare(self.top + self.bottom, self.left + self.right, self.tolerance)
329 }
330
331 fn approximate_compare(a: usize, b: usize, tolerance: f64) -> Ordering {
333 if a == b {
334 return Ordering::Equal;
335 }
336 let determinant = std::cmp::min(a,b) as f64 / std::cmp::max(a,b) as f64;
337 if determinant > (1.0-tolerance) {
338 return Ordering::Equal;
339 }
340 if a > b {
341 Ordering::Greater
342 } else {
343 Ordering::Less
344 }
345 }
346 }
347}