~raph/interp-toy

ref: 4cfe4c880ba40b18c5887781a1cce2da9f2400e9 interp-toy/src/interp_pane.rs -rw-r--r-- 4.6 KiB
4cfe4c88Raph Levien Add "merge" command 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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use std::ops::Deref;
use std::sync::Arc;

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,
};

use crate::AppState;

use crate::InterpPt;

#[derive(Default)]
pub struct InterpPane {
    drag_ix: Option<usize>,
}

enum PtState {
    /// Point is interpolated and can't be dragged.
    Interpolated,
    /// Point doesn't have a master at the current params, but params are at a master.
    CanAddMaster,
    /// Point has a master at the current params.
    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,
        paint_ctx: &mut PaintCtx,
        _base_state: &BaseState,
        data: &AppState,
        _env: &Env,
    ) {
        let width = data.shared.width;
        let weight = data.shared.weight;
        let pt_state = if data.is_at_master() {
            PtState::CanAddMaster
        } else {
            PtState::Interpolated
        };
        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::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 = pts[i];
            let radius = if is_selected { 3.0 } else { 2.0 };
            let circle = Circle::new(interp, radius);
            paint_ctx.fill(circle, &fg_color);
        }
    }

    fn layout(
        &mut self,
        _layout_ctx: &mut LayoutCtx,
        bc: &BoxConstraints,
        _data: &AppState,
        _env: &Env,
    ) -> Size {
        bc.constrain((100.0, 100.0))
    }

    fn event(&mut self, event: &Event, ctx: &mut EventCtx, data: &mut AppState, _env: &Env) {
        let width = data.shared.width;
        let weight = data.shared.weight;
        match event {
            Event::MouseDown(e) => {
                println!("mouse down {:?}!", e);
                let pos = e.pos;
                let mut pts = data.pts.deref().clone();
                for (i, pt) in pts.iter().enumerate() {
                    let interp = pt.eval(width, weight, data.interp_type);
                    if interp.distance(pos) < 5.0 {
                        self.drag_ix = Some(i);
                        data.sel = Some(i);
                        return;
                    }
                }
                self.drag_ix = Some(pts.len());
                data.sel = self.drag_ix;
                let pt = InterpPt::new(pos, width, weight);
                pts.push(pt);
                data.pts = Arc::new(pts);
                ctx.invalidate();
            }
            Event::MouseUp(_e) => {
                self.drag_ix = None;
            }
            Event::MouseMoved(e) => {
                if let Some(drag_ix) = self.drag_ix {
                    let mut pts = data.pts.deref().clone();
                    pts[drag_ix].update(e.pos, data.shared.width, data.shared.weight);
                    data.pts = Arc::new(pts);
                    ctx.invalidate();
                }
            }
            _ => (),
        }
    }

    fn update(
        &mut self,
        _ctx: &mut UpdateCtx,
        _old_data: Option<&AppState>,
        _data: &AppState,
        _env: &Env,
    ) {
    }
}