fj_text/
lib.rs

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
use anyhow::{anyhow, Result};
use bezier_rs::{Bezier, TValueType};
use fj::{
    core::{
        objects::Region,
        operations::{build::BuildRegion, update::UpdateRegion},
        Core,
    },
    math::Winding,
};
use font::{glyph::Segment, Font, Glyph, Offset, Read};

const DEFAULT_RESOLUTION: usize = 5;

pub struct GlyphRegionBuilder {
    glyph: Glyph,
}

impl GlyphRegionBuilder {
    pub fn try_new<T: Read>(font: &mut Font<T>, character: char) -> Result<Self> {
        let glyph = font.glyph(character)?;
        match glyph {
            Some(glyph) => Ok(Self { glyph }),
            None => Err(anyhow!("Character not in font: {}", character)),
        }
    }

    pub fn build(self, core: &mut Core) -> Vec<Region> {
        let mut point_lists = vec![];
        let mut a = Offset::default();
        for contour in self.glyph.iter() {
            a += contour.offset;
            let mut beziers = vec![];
            for segment in contour.iter() {
                match *segment {
                    Segment::Linear(mut b) => {
                        b += a;
                        beziers.push(Bezier::from_linear_coordinates(
                            a.0 as f64, a.1 as f64, b.0 as f64, b.1 as f64,
                        ));
                        a = b;
                    }
                    Segment::Quadratic(mut b, mut c) => {
                        b += a;
                        c += b;
                        beziers.push(Bezier::from_quadratic_coordinates(
                            a.0 as f64, a.1 as f64, b.0 as f64, b.1 as f64, c.0 as f64, c.1 as f64,
                        ));
                        a = c;
                    }
                    Segment::Cubic(mut b, mut c, mut d) => {
                        b += a;
                        c += b;
                        d += c;
                        beziers.push(Bezier::from_cubic_coordinates(
                            a.0 as f64, a.1 as f64, b.0 as f64, b.1 as f64, c.0 as f64, c.1 as f64,
                            d.0 as f64, d.1 as f64,
                        ));
                        a = d;
                    }
                }
            }
            let mut res = vec![];
            for bezier in beziers.iter() {
                let x = bezier
                    .compute_lookup_table(Some(DEFAULT_RESOLUTION), Some(TValueType::Euclidean));
                for p in x {
                    if !res.contains(&[p.x, p.y]) {
                        res.push([p.x, p.y]);
                    }
                }
            }
            point_lists.push(res);
        }
        let mut max = f64::MIN;
        for point_list in &point_lists {
            for point in point_list {
                if point[1] > max {
                    max = point[1]
                }
            }
        }
        let point_lists: Vec<Vec<[f64; 2]>> = point_lists
            .iter()
            .map(|point_list| {
                point_list
                    .iter()
                    .map(|point| [point[0] / max, point[1] / max])
                    .collect()
            })
            .collect();
        let mut polygons: Vec<Region> = vec![];
        for mut region_points in point_lists {
            region_points.reverse();
            let region = Region::polygon(region_points, core);
            if region.exterior().winding(&core.layers.geometry) == Winding::Cw {
                let last_polygon = polygons.remove(0);
                let new_region = last_polygon.add_interiors([region.exterior().clone()], core);
                polygons.insert(0, new_region);
            } else {
                polygons.push(region);
            }
        }
        polygons
    }
}