~raph/interp-toy

622590999ff80e55c6e4fbd362def96489f5b792 — Raph Levien 2 years ago 99dec86
Display filled glyph shape

Get structure from Glyphs font file, draw the interpolated glyph.
5 files changed, 67 insertions(+), 9 deletions(-)

M glyphstool/src/font.rs
M glyphstool/src/lib.rs
M src/app_state.rs
M src/interp_pane.rs
M src/main.rs
M glyphstool/src/font.rs => glyphstool/src/font.rs +1 -1
@@ 52,7 52,7 @@ pub struct Node {
    pub node_type: NodeType,
}

#[derive(Debug)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum NodeType {
    Line,
    OffCurve,

M glyphstool/src/lib.rs => glyphstool/src/lib.rs +1 -1
@@ 6,7 6,7 @@ mod plist;
mod stretch;
mod to_plist;

pub use font::Font;
pub use font::{Font, NodeType};
pub use from_plist::FromPlist;
pub use plist::Plist;
pub use stretch::stretch;

M src/app_state.rs => src/app_state.rs +8 -0
@@ 3,6 3,7 @@ use std::sync::Arc;

use nalgebra::DVector;

use glyphstool::NodeType;
use rbf_interp::{Basis, Scatter};

use druid::kurbo::Point;


@@ 21,6 22,8 @@ pub struct AppState {

    pub masters: Arc<Vec<Master>>,
    pub interp_type: InterpType,

    pub structure: Arc<Vec<Vec<NodeType>>>,
}

#[derive(Clone, Copy, PartialEq)]


@@ 43,6 46,7 @@ impl Data for AppState {
            && self.pts.same(&other.pts)
            && self.masters.same(&other.masters)
            && self.interp_type.same(&other.interp_type)
            && self.structure.same(&other.structure)
    }
}



@@ 214,6 218,10 @@ impl AppState {
    pub fn set_pts(&mut self, pts: Vec<InterpPt>) {
        *Arc::make_mut(&mut self.pts) = pts;
    }

    pub fn set_structure(&mut self, structure: Vec<Vec<NodeType>>) {
        *Arc::make_mut(&mut self.structure) = structure;
    }
}

impl InterpPt {

M src/interp_pane.rs => src/interp_pane.rs +47 -7
@@ 1,7 1,9 @@
use std::ops::Deref;
use std::sync::Arc;

use druid::kurbo::{Circle, Size};
use glyphstool::NodeType;

use druid::kurbo::{BezPath, Circle, Point, Size};
use druid::piet::{Color, RenderContext};
use druid::{
    BaseState, BoxConstraints, Env, Event, EventCtx, LayoutCtx, PaintCtx, UpdateCtx, Widget,


@@ 25,6 27,36 @@ enum PtState {
    IsMaster,
}

fn reconstruct_path(pts: &[Point], structure: &[Vec<NodeType>]) -> BezPath {
    let mut bez_path = BezPath::new();
    let mut j = 0;
    for subpath in structure {
        let mut start_ix = 0;
        while subpath[start_ix] == NodeType::OffCurve {
            start_ix += 1;
        }
        let n = subpath.len();
        bez_path.move_to(pts[j + start_ix]);
        let mut ctrl_pts = Vec::with_capacity(2);
        for i in 0..n {
            let ix = (start_ix + i + 1) % n;
            let node_type = subpath[ix];
            let p = pts[j + ix];
            match node_type {
                NodeType::Line => bez_path.line_to(p),
                NodeType::OffCurve => ctrl_pts.push(p),
                NodeType::Curve | NodeType::CurveSmooth => {
                    bez_path.curve_to(ctrl_pts[0], ctrl_pts[1], p);
                    ctrl_pts.clear();
                }
            }
        }
        bez_path.close_path();
        j += n;
    }
    bez_path
}

impl Widget<AppState> for InterpPane {
    fn paint(
        &mut self,


@@ 40,17 72,25 @@ impl Widget<AppState> for InterpPane {
        } else {
            PtState::Interpolated
        };
        for (i, pt) in data.pts.iter().enumerate() {
        let pts: Vec<_> = data
            .pts
            .iter()
            .map(|pt| pt.eval(width, weight, data.interp_type))
            .collect();
        let fill_color = Color::WHITE;
        let path = reconstruct_path(&pts, &data.structure);
        paint_ctx.fill(path, &fill_color);
        for i in 0..pts.len() {
            let fg_color = match pt_state {
                PtState::CanAddMaster => Color::WHITE,
                PtState::Interpolated => Color::WHITE.with_alpha(0.5),
                PtState::CanAddMaster => Color::rgb8(0x80, 0x80, 0xff),
                PtState::Interpolated => Color::rgb8(0x80, 0x80, 0xff).with_alpha(0.8),
                _ => Color::rgb(0xff, 0, 0),
            };
            let is_selected = data.sel == Some(i);
            let interp = pt.eval(width, weight, data.interp_type);
            let radius = if is_selected { 6.0 } else { 5.0 };
            let interp = pts[i];
            let radius = if is_selected { 3.0 } else { 2.0 };
            let circle = Circle::new(interp, radius);
            paint_ctx.render_ctx.fill(circle, &fg_color);
            paint_ctx.fill(circle, &fg_color);
        }
    }


M src/main.rs => src/main.rs +10 -0
@@ 94,12 94,20 @@ fn set_app_state_for_glyph(app_state: &mut AppState, font: &Font, glyphname: &st
        app_state.add_weight(weight);
    }
    if let Some(glyph) = font.get_glyph(glyphname) {
        let mut structure = Vec::new();
        let mut first_layer = true;
        let mut pts = Vec::new();
        for layer in &glyph.layers {
            if let Some(&weight) = weight_map.get(&layer.layer_id) {
                let mut i = 0;
                for p in layer.paths.as_ref().unwrap() {
                    if first_layer {
                        structure.push(Vec::new());
                    }
                    for n in &p.nodes {
                        if first_layer {
                            structure.last_mut().unwrap().push(n.node_type);
                        }
                        if i == pts.len() {
                            pts.push(InterpPt::default());
                        }


@@ 113,8 121,10 @@ fn set_app_state_for_glyph(app_state: &mut AppState, font: &Font, glyphname: &st
                    }
                }
            }
            first_layer = false;
        }
        app_state.set_pts(pts);
        app_state.set_structure(structure);
    }
}