1use anyhow::{anyhow, Result};
2use bezier_rs::{Bezier, TValueType};
3use fj::{
4 core::{
5 objects::Region,
6 operations::{build::BuildRegion, update::UpdateRegion},
7 Core,
8 },
9 math::Winding,
10};
11use font::{glyph::Segment, Font, Glyph, Offset, Read};
12
13const DEFAULT_RESOLUTION: usize = 5;
14
15pub struct GlyphRegionBuilder {
16 glyph: Glyph,
17}
18
19impl GlyphRegionBuilder {
20 pub fn try_new<T: Read>(font: &mut Font<T>, character: char) -> Result<Self> {
21 let glyph = font.glyph(character)?;
22 match glyph {
23 Some(glyph) => Ok(Self { glyph }),
24 None => Err(anyhow!("Character not in font: {}", character)),
25 }
26 }
27
28 pub fn build(self, core: &mut Core) -> Vec<Region> {
29 let mut point_lists = vec![];
30 let mut a = Offset::default();
31 for contour in self.glyph.iter() {
32 a += contour.offset;
33 let mut beziers = vec![];
34 for segment in contour.iter() {
35 match *segment {
36 Segment::Linear(mut b) => {
37 b += a;
38 beziers.push(Bezier::from_linear_coordinates(
39 a.0 as f64, a.1 as f64, b.0 as f64, b.1 as f64,
40 ));
41 a = b;
42 }
43 Segment::Quadratic(mut b, mut c) => {
44 b += a;
45 c += b;
46 beziers.push(Bezier::from_quadratic_coordinates(
47 a.0 as f64, a.1 as f64, b.0 as f64, b.1 as f64, c.0 as f64, c.1 as f64,
48 ));
49 a = c;
50 }
51 Segment::Cubic(mut b, mut c, mut d) => {
52 b += a;
53 c += b;
54 d += c;
55 beziers.push(Bezier::from_cubic_coordinates(
56 a.0 as f64, a.1 as f64, b.0 as f64, b.1 as f64, c.0 as f64, c.1 as f64,
57 d.0 as f64, d.1 as f64,
58 ));
59 a = d;
60 }
61 }
62 }
63 let mut res = vec![];
64 for bezier in beziers.iter() {
65 let x = bezier
66 .compute_lookup_table(Some(DEFAULT_RESOLUTION), Some(TValueType::Euclidean));
67 for p in x {
68 if !res.contains(&[p.x, p.y]) {
69 res.push([p.x, p.y]);
70 }
71 }
72 }
73 point_lists.push(res);
74 }
75 let mut max = f64::MIN;
76 for point_list in &point_lists {
77 for point in point_list {
78 if point[1] > max {
79 max = point[1]
80 }
81 }
82 }
83 let point_lists: Vec<Vec<[f64; 2]>> = point_lists
84 .iter()
85 .map(|point_list| {
86 point_list
87 .iter()
88 .map(|point| [point[0] / max, point[1] / max])
89 .collect()
90 })
91 .collect();
92 let mut polygons: Vec<Region> = vec![];
93 for mut region_points in point_lists {
94 region_points.reverse();
95 let region = Region::polygon(region_points, core);
96 if region.exterior().winding(&core.layers.geometry) == Winding::Cw {
97 let last_polygon = polygons.remove(0);
98 let new_region = last_polygon.add_interiors([region.exterior().clone()], core);
99 polygons.insert(0, new_region);
100 } else {
101 polygons.push(region);
102 }
103 }
104 polygons
105 }
106}