~raph/interp-toy

ref: 03b54c0609d69ef0e5f65382c7412bb28aecfa78 interp-toy/glyphstool/src/inco_fix.rs -rw-r--r-- 3.9 KiB
03b54c06Raph Levien Add "inco_fix" script 2 years ago
                                                                                
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
113
114
115
116
117
118
119
120
121
122
//! A bit of scripting to automate a bunch of Inconsolata vf work.
//!
//! Note that this is a submodule of main, rather than in the lib, as it is not
//! generally useful. But it's very likely that logic in here can be adapted into
//! a more general tool.

use std::collections::HashMap;

use kurbo::Affine;

use glyphstool::{Font, Glyph, Layer, Node, Path};

#[derive(Default)]
struct LayerMap {
    params_to_id: HashMap<(i64, i64), String>,
    id_to_params: HashMap<String, (i64, i64)>,
}

impl LayerMap {
    fn add(&mut self, wght: i64, wdth: i64, id: &str) {
        self.params_to_id.insert((wght, wdth), id.to_string());
        self.id_to_params.insert(id.to_string(), (wght, wdth));
    }

    fn get_id(&self, wght: i64, wdth: i64) -> &str {
        &self.params_to_id[&(wght, wdth)]
    }

    fn get_params(&self, id: &str) -> Option<(i64, i64)> {
        self.id_to_params.get(id).copied()
    }
}

fn affine_stretch(stretch: f64) -> Affine {
    Affine::new([stretch, 0., 0., 1., 0., 0.])
}

fn fix_path(path0: &Path, path1: &Path, t: f64, stretch: f64) -> Path {
    let a = affine_stretch(stretch);
    let nodes = path0
        .nodes
        .iter()
        .zip(path1.nodes.iter())
        .map(|(n0, n1)| Node {
            pt: a * n0.pt.lerp(n1.pt, t).round(),
            node_type: n0.node_type,
        })
        .collect();
    Path {
        closed: path0.closed,
        nodes,
    }
}

fn fix_glyph(glyph: &mut Glyph, layers: &LayerMap) {
    let paths0 = glyph
        .get_layer(layers.get_id(400, 100))
        .unwrap()
        .paths
        .clone();
    // This is actually the 700 from the master, but is stored in 900.
    let paths1 = glyph
        .get_layer(layers.get_id(900, 100))
        .unwrap()
        .paths
        .clone();
    println!("processing glyph {}", glyph.glyphname);
    for layer in &mut glyph.layers {
        if let Some((wght, wdth)) = layers.get_params(&layer.layer_id) {
            let t = (wght as f64 - 400.0) / 300.0;
            let stretch = wdth as f64 / 100.0;
            println!("  touching layer {}, t = {}", layer.layer_id, t);
            if let Some(ref p0) = paths0 {
                let paths = p0
                    .iter()
                    .zip(paths1.as_ref().unwrap().iter())
                    .map(|(p0, p1)| fix_path(p0, p1, t, stretch))
                    .collect();
                layer.paths = Some(paths);
            }
            layer.width = wdth as f64 * 5.0;

            // Possibly TODO: lerp the affine from the masters, rather than
            // doing the processing in-place. Not clear whether it makes much
            // difference.
            let a = affine_stretch(stretch);
            let a_inv = affine_stretch(stretch.recip());

            if let Some(ref mut anchors) = layer.anchors {
                for anchor in anchors {
                    anchor.position = (a * anchor.position).round();
                }
            }

            if let Some(ref mut components) = layer.components {
                for component in components {
                    if let Some(ref mut transform) = component.transform {
                        // TODO: round the translation component
                        *transform = a * *transform * a_inv;
                    }
                }
            }
        }
    }
}

pub fn inco_fix(font: &mut Font) {
    let mut layers = LayerMap::default();
    for master in &font.font_master {
        let wght = master.weight_value;
        let wdth = master.width_value.unwrap_or(100);
        println!("{}: wght {}, wdth {}", master.id, wght, wdth);
        layers.add(wght, wdth, &master.id);
    }
    let layer_400_narrow_id = layers.get_id(400, 50);
    for glyph in &mut font.glyphs {
        let narrow = glyph.get_layer(layer_400_narrow_id).unwrap();
        if narrow.width != 250. && !glyph.glyphname.starts_with("_corner") {
            fix_glyph(glyph, &layers);
        }
    }
}