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, } #[derive(Debug, Clone)] pub struct LabeledDelimiter { pub delimiters: Vec, 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 } }