summaryrefslogtreecommitdiffstats
path: root/examples/lib-dfscq-log/src/labeled_delimiter.rs
blob: 5a1c6104407ba1ce192c13071f0efde828e677cb (plain)
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
use diaphragm_core::{
    core_shapes::{StraightPath, Text},
    types::{Bounds, Float, Point2D, ShapeContext},
    ComplexShape, DrawResult, Drawable, SolverContext,
};

#[derive(Debug, Clone)]
pub struct Delimiter {
    pub width: Float,
    pub label: Drawable<Text>,
}

#[derive(Debug, Clone)]
pub struct LabeledDelimiter {
    pub delimiters: Vec<Delimiter>,
    pub tick_height: Float,
}

impl ComplexShape for LabeledDelimiter {
    fn draw(&self, context: &ShapeContext, solver: &mut dyn SolverContext) -> DrawResult {
        let mut result = DrawResult::new();

        let bounds_left = context.bounds().left(solver);
        let bounds_right = context.bounds().right(solver);
        let bounds_bottom = context.bounds().bottom(solver);
        let bounds_width = context.bounds().width(solver);

        let tick_top = context.bounds().top(solver);
        let tick_bottom = solver.float_add(&[tick_top, self.tick_height]);

        let first_tick = Drawable::builder(StraightPath::new(vec![
            context.bounds().top_left(solver),
            Point2D::new(bounds_left, tick_bottom),
        ]))
        .bounds(
            Bounds::builder()
                .top(tick_top)
                .left(bounds_left)
                .height(self.tick_height)
                .width(Float::Fixed(0.))
                .build(solver),
        )
        .stroke(context.stroke().clone())
        .build(solver);

        let baseline_y = first_tick.bounds().vert_center(solver);

        result.push(first_tick);

        // TODO: split everything into functions

        let baseline = Drawable::builder(StraightPath::new(vec![
            Point2D::new(bounds_left, baseline_y),
            Point2D::new(bounds_right, baseline_y),
        ]))
        .bounds(
            Bounds::builder()
                .top(baseline_y)
                .left(bounds_left)
                .width(bounds_width)
                .height(Float::Fixed(0.))
                .build(solver),
        )
        .stroke(context.stroke().clone())
        .build(solver);

        result.push(baseline);

        let mut section_end = bounds_left;
        let mut all_label_bottoms = vec![];

        for &Delimiter {
            width: section_width,
            ref label,
        } in &self.delimiters
        {
            let section_begin = section_end;
            section_end = solver.float_add(&[section_end, section_width]);

            let section_half_width = solver.float_div(section_width, Float::Fixed(2.));
            let section_middle = solver.float_add(&[section_begin, section_half_width]);

            let tick = Drawable::builder(StraightPath::new(vec![
                Point2D::new(section_end, tick_top),
                Point2D::new(section_end, tick_bottom),
            ]))
            .bounds(
                Bounds::builder()
                    .top(tick_top)
                    .left(section_end)
                    .height(self.tick_height)
                    .width(Float::Fixed(0.))
                    .build(solver),
            )
            .stroke(context.stroke().clone())
            .build(solver);

            result.push(tick);

            let label_top = label.bounds().top(solver);
            let label_bottom = label.bounds().bottom(solver);
            let label_horiz_center = label.bounds().horiz_center(solver);

            let label_top_constraint = solver.float_eq(label_top, tick_bottom);
            solver.constrain(label_top_constraint);

            let label_middle_constraint = solver.float_eq(label_horiz_center, section_middle);
            solver.constrain(label_middle_constraint);

            result.push(label.clone());

            all_label_bottoms.push(label_bottom);
        }

        let wanted_bounds_bottom = solver.float_max(&all_label_bottoms);
        let bounds_bottom_constraint = solver.float_eq(bounds_bottom, wanted_bounds_bottom);
        solver.constrain(bounds_bottom_constraint);

        result
    }
}