From 82ffe3187a32bad4ecca0736882a23793a800822 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Fri, 3 Feb 2023 19:59:43 +0100 Subject: add support for images, and some chores - SVG files not yet supported - Remove dead commented code - Add inherent constraints for text, path, and images - Formatting --- core/src/core_shapes.rs | 174 +++++++++++++++++++++++++++++------------------- core/src/rendering.rs | 29 ++++++-- core/src/runtime.rs | 107 +++++++---------------------- core/src/solving.rs | 2 + 4 files changed, 156 insertions(+), 156 deletions(-) (limited to 'core/src') diff --git a/core/src/core_shapes.rs b/core/src/core_shapes.rs index e6d79c9..c380870 100644 --- a/core/src/core_shapes.rs +++ b/core/src/core_shapes.rs @@ -1,7 +1,12 @@ -use crate::types::{CoreShapeContext, DefinedCoreShapeContext, DefinedPoint2D, Point2D}; +use std::path::PathBuf; -pub use super::text::{DefinedText, Text}; +use crate::{ + text::{DefinedText, Text}, + types::{CoreShapeContext, DefinedCoreShapeContext, DefinedPoint2D, Float, Point2D}, + Renderer, SolverContext, +}; +#[derive(Debug, Clone)] pub struct CoreDrawable { pub(crate) shape: CoreShape, pub(crate) context: CoreShapeContext, @@ -11,12 +16,91 @@ impl CoreDrawable { pub fn new(shape: CoreShape, context: CoreShapeContext) -> Self { Self { shape, context } } + + // TODO: I don't like this design + // TODO: plus, having an enum for CoreShape could be bad since types have + // various sizes + pub(crate) fn inherent_constraints( + &self, + solver: &mut dyn SolverContext, + renderer: &mut dyn Renderer, + ) { + match &self.shape { + CoreShape::Text(text) => { + let (text_width, text_height) = renderer.text_extents(&text.content, &text.font); + + let calculated_width = + solver.float_mul(&[Float::Fixed(text_width), text.font.size]); + let bounds_width = self.context.bounds().width(solver); + let width_constraint = solver.float_eq(bounds_width, calculated_width); + solver.constrain(width_constraint); + + let calculated_height = + solver.float_mul(&[Float::Fixed(text_height), text.font.size]); + let bounds_height = self.context.bounds().height(solver); + let height_constraint = solver.float_eq(bounds_height, calculated_height); + solver.constrain(height_constraint); + } + CoreShape::StraightPath(path) => { + let (all_x, all_y): (Vec<_>, Vec<_>) = + path.points.iter().map(|p| (p.x, p.y)).unzip(); + + let min_x = solver.float_min(&all_x); + let max_x = solver.float_max(&all_x); + let min_y = solver.float_min(&all_y); + let max_y = solver.float_max(&all_y); + + let bounds_top = self.context.bounds().top(solver); + let bounds_bottom = self.context.bounds().bottom(solver); + let bounds_left = self.context.bounds().left(solver); + let bounds_right = self.context.bounds().right(solver); + + let top_constraint = solver.float_eq(bounds_top, min_x); + solver.constrain(top_constraint); + + let bottom_constraint = solver.float_eq(bounds_bottom, max_x); + solver.constrain(bottom_constraint); + + let left_constraint = solver.float_eq(bounds_left, min_y); + solver.constrain(left_constraint); + + let right_constraint = solver.float_eq(bounds_right, max_y); + solver.constrain(right_constraint); + } + CoreShape::Image(image) => { + if !image.keep_aspect_ratio { + return; + } + + let scale_x = solver.new_free_float(); + let scale_y = solver.new_free_float(); + + let constraint = solver.float_eq(scale_x, scale_y); + solver.constrain(constraint); + + let (orig_width, orig_height) = renderer.geometry_for_image(&image.path); + + let orig_width_scaled = solver.float_mul(&[scale_x, Float::Fixed(orig_width)]); + let width_constraint = + solver.float_eq(self.context.bounds().width, orig_width_scaled); + solver.constrain(width_constraint); + + let orig_height_scaled = solver.float_mul(&[scale_y, Float::Fixed(orig_height)]); + let height_constraint = + solver.float_eq(self.context.bounds().height, orig_height_scaled); + solver.constrain(height_constraint); + } + _ => (), + } + } } +#[derive(Debug, Clone)] pub enum CoreShape { Rectangle(Rectangle), Text(Text), StraightPath(StraightPath), + Image(Image), } impl From for CoreShape { @@ -37,6 +121,12 @@ impl From for CoreShape { } } +impl From for CoreShape { + fn from(image: Image) -> Self { + CoreShape::Image(image) + } +} + pub struct DefinedCoreDrawable { pub(crate) shape: DefinedCoreShape, pub(crate) context: DefinedCoreShapeContext, @@ -46,72 +136,12 @@ pub enum DefinedCoreShape { Rectangle(Rectangle), Text(DefinedText), StraightPath(DefinedStraightPath), + Image(Image), } -/* -pub trait CoreShape { - fn constrain( - &self, - _context: &ShapeContext, - _solver: &mut dyn SolverContext, - _renderer: &dyn Renderer, - ) { - } - fn to_render(&self, model: &dyn SolverModel) -> Option>; -} - -impl ComplexShape for T { - fn as_core_shape(&self) -> Option<&dyn CoreShape> { - Some(self) - } - - fn draw(&self, _context: &ShapeContext, _solver: &mut dyn SolverContext) -> DrawResult { - panic!("Tried to decompose core shape") - } -} -*/ - #[derive(Copy, Clone, Debug, Default)] pub struct Rectangle {} -/* -impl CoreShape for Rectangle { - fn to_render(&self, _model: &dyn SolverModel) -> Option> { - Some(Box::new(*self)) - } -} -*/ - -// TODO: re-enable this in some way -/* -impl CoreShape for Text { - fn constrain( - &self, - context: &ShapeContext, - solver: &mut dyn SolverContext, - renderer: &dyn Renderer, - ) { - let height_constraint = solver.float_eq(context.bounds.height, self.font.size); - solver.constrain(height_constraint); - - // TODO: handle multiline - let (width, height) = renderer.text_extents(&self.content, &self.font); - dbg!(height); - - let scale = solver.float_div(self.font.size, Float::Fixed(height)); - - let calculated_width = solver.float_mul(&[Float::Fixed(width), scale]); - let width_constraint = solver.float_eq(context.bounds.width, calculated_width); - solver.constrain(width_constraint); - } - - fn to_render(&self, model: &dyn SolverModel) -> Option> { - self.fixate(model) - .map(|path| -> Box { Box::new(path) }) - } -} -*/ - #[derive(Clone, Debug, Default)] pub struct StraightPath { pub(crate) points: Vec, @@ -127,11 +157,17 @@ pub struct DefinedStraightPath { pub(crate) points: Vec, } -/* -impl CoreShape for StraightPath { - fn to_render(&self, model: &dyn SolverModel) -> Option> { - self.fixate(model) - .map(|path| -> Box { Box::new(path) }) +#[derive(Clone, Debug, Default)] +pub struct Image { + pub(crate) path: PathBuf, + pub(crate) keep_aspect_ratio: bool, +} + +impl Image { + pub fn new(path: PathBuf, keep_aspect_ratio: bool) -> Self { + Self { + path, + keep_aspect_ratio, + } } } -*/ diff --git a/core/src/rendering.rs b/core/src/rendering.rs index 73daa2a..9c719c4 100644 --- a/core/src/rendering.rs +++ b/core/src/rendering.rs @@ -1,7 +1,11 @@ -use super::core_shapes::*; -use super::styles::{DefinedDashStyle, DefinedStrokeStyle, FillStyle, Pattern}; -use super::text::{DefinedFontDescription, FontDescription}; -use super::types::DefinedCoreShapeContext; +use std::path::Path; + +use crate::{ + core_shapes::*, + styles::{DefinedDashStyle, DefinedStrokeStyle, FillStyle, Pattern}, + text::{DefinedFontDescription, DefinedText, FontDescription}, + types::DefinedCoreShapeContext, +}; pub trait Renderer { // Must be called once before any drawing happens @@ -19,6 +23,9 @@ pub trait Renderer { // For a font of size 1. fn text_extents(&self, text: &str, font: &FontDescription) -> (f64, f64); fn show_text(&mut self, text: &str, font: &DefinedFontDescription); + + fn show_image(&mut self, path: &Path, x: f64, y: f64, width: f64, height: f64); + fn geometry_for_image(&mut self, path: &Path) -> (f64, f64); } pub trait Render { @@ -31,6 +38,7 @@ impl Render for DefinedCoreShape { Self::Rectangle(r) => r.render(context, renderer), Self::Text(t) => t.render(context, renderer), Self::StraightPath(p) => p.render(context, renderer), + Self::Image(i) => i.render(context, renderer), } } } @@ -96,3 +104,16 @@ impl Render for DefinedStraightPath { draw(&context.fill, &context.stroke, renderer); } } + +impl Render for Image { + fn render(&self, context: DefinedCoreShapeContext, renderer: &mut dyn Renderer) { + // TODO: what about pattern, and fill color? Do we do something with that? + renderer.show_image( + &self.path, + context.bounds.left, + context.bounds.top, + context.bounds.width, + context.bounds.height, + ); + } +} diff --git a/core/src/runtime.rs b/core/src/runtime.rs index 6b1b5d1..5fd17e9 100644 --- a/core/src/runtime.rs +++ b/core/src/runtime.rs @@ -1,11 +1,11 @@ -use crate::core_shapes::CoreDrawable; -use crate::rendering::Render; -use crate::types::Bounds; - -// use super::complex_shapes::{ComplexShape, Drawable}; -use super::rendering::Renderer; -use super::solving::{Constrainable, SolverContext}; +use crate::{ + core_shapes::CoreDrawable, + rendering::{Render, Renderer}, + solving::{Constrainable, SolverContext}, + types::Bounds, +}; +// TODO: // const RECURSION_LIMIT: u64 = 10_000; pub struct Runtime<'a> { @@ -20,15 +20,10 @@ impl<'a> Runtime<'a> { Self { solver_ctx, renderer, - // drawables: Vec::new(), drawables: Vec::new(), } } - // pub fn add_drawable(&mut self, drawable: Drawable) { - // self.drawables.push(drawable.into()) - // } - pub fn add_drawable(&mut self, drawable: CoreDrawable) { self.drawables.push(drawable) } @@ -41,88 +36,34 @@ impl<'a> Runtime<'a> { &mut *self.renderer } - pub fn render(mut self, bounds: Bounds) { - let model = self.solver_ctx.solve(); + pub fn render(self, bounds: Bounds) { + // Separate self into several variables, so we can get mutable references to each at the + // same time + let Runtime { + mut solver_ctx, + mut renderer, + drawables, + } = self; + + for drawable in &drawables { + drawable.inherent_constraints(&mut *solver_ctx, &mut *renderer); + } + + let model = solver_ctx.solve(); let bounds = bounds .fixate(&*model) .expect("Could not fixate figure bounds"); - self.renderer.set_size(bounds.width, bounds.height); + renderer.set_size(bounds.width, bounds.height); - for drawable in &self.drawables { + for drawable in &drawables { let defined_drawable = drawable .fixate(&*model) .expect("Could not fixate core shape"); defined_drawable .shape - .render(defined_drawable.context, &mut *self.renderer); + .render(defined_drawable.context, &mut *renderer); } } - - // TODO: preserve ordering of shapes - // pub fn render(mut self) { - // let mut drawables = self.drawables; - // let mut waited_on_variables = Vec::new(); - // let mut core_shapes = Vec::new(); - // - // /* - // for drawable in &self.shapes { - // let bounds = &drawable.bounds; - // let shape = &drawable.shape; - // - // if let Some(core_shape) = shape.to_render() { - // drawables.push((*drawable).clone()); - // continue; - // } - // - // let mut result = shape.draw(bounds); - // drawables.append(&mut result.subshapes); - // waited_on_variables.append(&mut result.waiting_on); - // } - // */ - // - // let mut recursion_count = 0; - // - // while !drawables.is_empty() { - // recursion_count += 1; - // - // if recursion_count > RECURSION_LIMIT { - // panic!("Recursion limit reached"); - // } - // - // let mut tmp_drawables = Vec::new(); - // - // for drawable in drawables.drain(..) { - // let shape_ctx = &drawable.context; - // let shape = &drawable.shape; - // - // if let Some(core_shape) = shape.as_core_shape() { - // core_shape.constrain(shape_ctx, &mut *self.solver_ctx, &*self.renderer); - // core_shapes.push((shape.dyn_clone(), shape_ctx.clone())); // Better to Arc? Cow? - // continue; - // } - // - // let mut result = shape.draw(shape_ctx, &mut *self.solver_ctx); - // tmp_drawables.append(&mut result.subshapes); - // waited_on_variables.append(&mut result.waiting_on); - // } - // - // drawables = tmp_drawables; - // } - // - // let model = self.solver_ctx.solve(); - // - // // Delay rendering core shapes until later to have all the constraints - // for (core_shape, shape_ctx) in core_shapes { - // let core_shape = core_shape.as_core_shape().unwrap(); - // - // match (core_shape.to_render(&*model), shape_ctx.fixate(&*model)) { - // (Some(defined_shape), Some(shape_ctx)) => { - // defined_shape.render(shape_ctx, &mut *self.renderer) - // } - // _ => panic!("Failed to fixate core shape"), - // } - // } - // } } diff --git a/core/src/solving.rs b/core/src/solving.rs index bfe3ff3..63e4562 100644 --- a/core/src/solving.rs +++ b/core/src/solving.rs @@ -176,11 +176,13 @@ pub trait Constrainable { impl Constrainable for CoreShape { type Fixated = DefinedCoreShape; + // TODO: why &self, why not self? fn fixate(&self, model: &dyn SolverModel) -> Option { match self { CoreShape::Rectangle(r) => Some(DefinedCoreShape::Rectangle(*r)), CoreShape::Text(t) => t.fixate(model).map(DefinedCoreShape::Text), CoreShape::StraightPath(p) => p.fixate(model).map(DefinedCoreShape::StraightPath), + CoreShape::Image(i) => Some(DefinedCoreShape::Image(i.clone())), } } } -- cgit v1.2.3