Removed modifiers from code

This commit is contained in:
alonso.torres 2025-10-28 17:11:02 +01:00
parent f6eb492329
commit dbf9bdceb5
18 changed files with 194 additions and 458 deletions

View File

@ -19,6 +19,7 @@
[app.common.types.component :as ctk] [app.common.types.component :as ctk]
[app.common.types.container :as ctn] [app.common.types.container :as ctn]
[app.common.types.modifiers :as ctm] [app.common.types.modifiers :as ctm]
[app.common.types.path :as path]
[app.common.types.shape-tree :as ctst] [app.common.types.shape-tree :as ctst]
[app.common.types.shape.attrs :refer [editable-attrs]] [app.common.types.shape.attrs :refer [editable-attrs]]
[app.common.types.shape.layout :as ctl] [app.common.types.shape.layout :as ctl]
@ -669,6 +670,8 @@
snap-pixel? snap-pixel?
(and (not ignore-snap-pixel) (contains? (:workspace-layout state) :snap-pixel-grid)) (and (not ignore-snap-pixel) (contains? (:workspace-layout state) :snap-pixel-grid))
_ (wasm.api/clean-geometry-modifiers)
transforms transforms
(into {} (wasm.api/propagate-modifiers geometry-entries snap-pixel?)) (into {} (wasm.api/propagate-modifiers geometry-entries snap-pixel?))
@ -685,12 +688,24 @@
modifiers (dm/get-in modif-tree [shape-id :modifiers])] modifiers (dm/get-in modif-tree [shape-id :modifiers])]
(-> shape (-> shape
(gsh/apply-transform transform) (gsh/apply-transform transform)
(ctm/apply-structure-modifiers modifiers))))] (ctm/apply-structure-modifiers modifiers))))
bool-ids
(into #{}
(comp
(mapcat (partial cfh/get-parents-with-self objects))
(filter cfh/bool-shape?)
(map :id))
ids)]
(rx/of (rx/of
(clear-local-transform) (clear-local-transform)
(ptk/event ::dwg/move-frame-guides {:ids ids :transforms transforms}) (ptk/event ::dwg/move-frame-guides {:ids ids :transforms transforms})
(ptk/event ::dwcm/move-frame-comment-threads transforms) (ptk/event ::dwcm/move-frame-comment-threads transforms)
(dwsh/update-shapes ids update-shape options)))))) (dwsh/update-shapes ids update-shape options)
;; The update to the bool path needs to be in a different operation because it
;; needs to have the updated children info
(dwsh/update-shapes bool-ids path/update-bool-shape (assoc options :with-objects? true)))))))
(def ^:private (def ^:private
xf-rotation-shape xf-rotation-shape

View File

@ -314,8 +314,8 @@
:ignore-constraints (contains? layout :scale-text)))))) :ignore-constraints (contains? layout :scale-text))))))
(rx/take-until stopper)) (rx/take-until stopper))
;; The last event we need to use the old method so the elements are correctly positioned until ;; The last event we need to use the old method so the elements are correctly
;; all the logic is implemented in wasm ;; positioned until all the logic is implemented in wasm
(->> resize-events-stream (->> resize-events-stream
(rx/take-until stopper) (rx/take-until stopper)
(rx/last) (rx/last)

View File

@ -8,7 +8,7 @@
"A WASM based render API" "A WASM based render API"
(:require (:require
["react-dom/server" :as rds] ["react-dom/server" :as rds]
[app.common.data :as d :refer [not-empty?]] [app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.math :as mth] [app.common.math :as mth]
[app.common.types.fills :as types.fills] [app.common.types.fills :as types.fills]
@ -850,20 +850,14 @@
[pending] [pending]
(let [event (js/CustomEvent. "wasm:set-objects-finished") (let [event (js/CustomEvent. "wasm:set-objects-finished")
pending (-> (d/index-by :key :callback pending) vals)] pending (-> (d/index-by :key :callback pending) vals)]
(if (not-empty? pending) (->> (rx/from pending)
(->> (rx/from pending) (rx/merge-map (fn [callback] (callback)))
(rx/merge-map (fn [callback] (callback))) (rx/reduce conj [])
(rx/tap (fn [_] (request-render "set-objects"))) (rx/subs! (fn [_]
(rx/reduce conj []) (clear-drawing-cache)
(rx/subs! (fn [_] (request-render "pending-finished")
(clear-drawing-cache) (h/call wasm/internal-module "_update_shape_text_layout_for_all")
(request-render "pending-finished") (.dispatchEvent ^js js/document event))))))
(h/call wasm/internal-module "_update_shape_text_layout_for_all")
(.dispatchEvent ^js js/document event))))
(do
(clear-drawing-cache)
(request-render "pending-finished")
(.dispatchEvent ^js js/document event)))))
(defn process-object (defn process-object
[shape] [shape]
@ -988,6 +982,10 @@
[] []
(h/call wasm/internal-module "_clean_modifiers")) (h/call wasm/internal-module "_clean_modifiers"))
(defn clean-geometry-modifiers
[]
(h/call wasm/internal-module "_clean_geometry_modifiers"))
(defn set-modifiers (defn set-modifiers
[modifiers] [modifiers]
@ -1116,7 +1114,6 @@
(defn calculate-bool (defn calculate-bool
[bool-type ids] [bool-type ids]
(let [size (mem/get-alloc-size ids UUID-U8-SIZE) (let [size (mem/get-alloc-size ids UUID-U8-SIZE)
heap (mem/get-heap-u32) heap (mem/get-heap-u32)
offset (mem/alloc->offset-32 size)] offset (mem/alloc->offset-32 size)]

View File

@ -495,11 +495,7 @@ pub extern "C" fn get_selection_rect() -> *mut u8 {
with_state_mut!(state, { with_state_mut!(state, {
let bbs: Vec<_> = entries let bbs: Vec<_> = entries
.iter() .iter()
.flat_map(|id| { .flat_map(|id| state.shapes.get(id).map(|b| b.bounds()))
let default = Matrix::default();
let modifier = state.modifiers.get(id).unwrap_or(&default);
state.shapes.get(id).map(|b| b.bounds().transform(modifier))
})
.collect(); .collect();
let result_bound = if bbs.len() == 1 { let result_bound = if bbs.len() == 1 {
@ -569,9 +565,15 @@ pub extern "C" fn set_structure_modifiers() {
#[no_mangle] #[no_mangle]
pub extern "C" fn clean_modifiers() { pub extern "C" fn clean_modifiers() {
with_state_mut!(state, { with_state_mut!(state, {
state.structure.clear();
state.scale_content.clear(); state.scale_content.clear();
// state.modifiers.clear(); state.shapes.clean_modifiers();
state.shapes.clean_structure();
});
}
#[no_mangle]
pub extern "C" fn clean_geometry_modifiers() {
with_state_mut!(state, {
state.shapes.clean_modifiers(); state.shapes.clean_modifiers();
}); });
} }

View File

@ -32,6 +32,7 @@ pub fn are_close_points(a: impl Into<(f32, f32)>, b: impl Into<(f32, f32)>) -> b
is_close_to(a_x, b_x) && is_close_to(a_y, b_y) is_close_to(a_x, b_x) && is_close_to(a_y, b_y)
} }
#[allow(dead_code)]
pub fn is_close_matrix(m: &Matrix, other: &Matrix) -> bool { pub fn is_close_matrix(m: &Matrix, other: &Matrix) -> bool {
is_close_to(m.scale_x(), other.scale_x()) is_close_to(m.scale_x(), other.scale_x())
&& is_close_to(m.scale_y(), other.scale_y()) && is_close_to(m.scale_y(), other.scale_y())

View File

@ -388,8 +388,6 @@ pub fn bool_from_shapes(
bool_type: BoolType, bool_type: BoolType,
children_ids: &IndexSet<Uuid>, children_ids: &IndexSet<Uuid>,
shapes: ShapesPoolRef, shapes: ShapesPoolRef,
modifiers: &HashMap<Uuid, Matrix>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
) -> Path { ) -> Path {
if children_ids.is_empty() { if children_ids.is_empty() {
return Path::default(); return Path::default();
@ -399,13 +397,13 @@ pub fn bool_from_shapes(
return Path::default(); return Path::default();
}; };
let mut current_path = child.to_path(shapes, modifiers, structure); let mut current_path = child.to_path(shapes);
for idx in (0..children_ids.len() - 1).rev() { for idx in (0..children_ids.len() - 1).rev() {
let Some(other) = shapes.get(&children_ids[idx]) else { let Some(other) = shapes.get(&children_ids[idx]) else {
continue; continue;
}; };
let other_path = other.to_path(shapes, modifiers, structure); let other_path = other.to_path(shapes);
let (segs_a, segs_b) = split_segments(&current_path, &other_path); let (segs_a, segs_b) = split_segments(&current_path, &other_path);
@ -422,26 +420,15 @@ pub fn bool_from_shapes(
current_path current_path
} }
pub fn update_bool_to_path( #[allow(dead_code)]
shape: &Shape, pub fn update_bool_to_path(shape: &mut Shape, shapes: ShapesPoolRef) {
shapes: ShapesPoolRef, let children_ids = shape.children_ids(true);
modifiers: &HashMap<Uuid, Matrix>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
) -> Shape {
let mut shape = shape.clone();
let children_ids = shape.modified_children_ids(structure.get(&shape.id), true);
let Type::Bool(bool_data) = &mut shape.shape_type else { let Type::Bool(bool_data) = &mut shape.shape_type else {
return shape; return;
}; };
bool_data.path = bool_from_shapes(
bool_data.bool_type, bool_data.path = bool_from_shapes(bool_data.bool_type, &children_ids, shapes);
&children_ids,
shapes,
modifiers,
structure,
);
shape
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -450,14 +437,14 @@ pub fn debug_render_bool_paths(
render_state: &mut RenderState, render_state: &mut RenderState,
shape: &Shape, shape: &Shape,
shapes: ShapesPoolRef, shapes: ShapesPoolRef,
modifiers: &HashMap<Uuid, Matrix>, _modifiers: &HashMap<Uuid, Matrix>,
structure: &HashMap<Uuid, Vec<StructureEntry>>, _structure: &HashMap<Uuid, Vec<StructureEntry>>,
) { ) {
let canvas = render_state.surfaces.canvas(SurfaceId::Strokes); let canvas = render_state.surfaces.canvas(SurfaceId::Strokes);
let mut shape = shape.clone(); let mut shape = shape.clone();
let children_ids = shape.modified_children_ids(structure.get(&shape.id), true); let children_ids = shape.children_ids(true);
let Type::Bool(bool_data) = &mut shape.shape_type else { let Type::Bool(bool_data) = &mut shape.shape_type else {
return; return;
@ -471,13 +458,13 @@ pub fn debug_render_bool_paths(
return; return;
}; };
let mut current_path = child.to_path(shapes, modifiers, structure); let mut current_path = child.to_path(shapes);
for idx in (0..children_ids.len() - 1).rev() { for idx in (0..children_ids.len() - 1).rev() {
let Some(other) = shapes.get(&children_ids[idx]) else { let Some(other) = shapes.get(&children_ids[idx]) else {
continue; continue;
}; };
let other_path = other.to_path(shapes, modifiers, structure); let other_path = other.to_path(shapes);
let (segs_a, segs_b) = split_segments(&current_path, &other_path); let (segs_a, segs_b) = split_segments(&current_path, &other_path);

View File

@ -22,8 +22,7 @@ pub use surfaces::{SurfaceId, Surfaces};
use crate::performance; use crate::performance;
use crate::shapes::{ use crate::shapes::{
all_with_ancestors, Blur, BlurType, Corners, Fill, Shadow, Shape, SolidColor, Stroke, all_with_ancestors, Blur, BlurType, Corners, Fill, Shadow, Shape, SolidColor, Stroke, Type,
StructureEntry, Type,
}; };
use crate::state::{ShapesPoolMutRef, ShapesPoolRef}; use crate::state::{ShapesPoolMutRef, ShapesPoolRef};
use crate::tiles::{self, PendingTiles, TileRect}; use crate::tiles::{self, PendingTiles, TileRect};
@ -31,8 +30,6 @@ use crate::uuid::Uuid;
use crate::view::Viewbox; use crate::view::Viewbox;
use crate::wapi; use crate::wapi;
use crate::math;
use crate::math::bools;
use indexmap::IndexSet; use indexmap::IndexSet;
pub use fonts::*; pub use fonts::*;
@ -63,13 +60,11 @@ impl NodeRenderState {
/// Calculates the clip bounds for child elements of a given shape. /// Calculates the clip bounds for child elements of a given shape.
/// ///
/// This function determines the clipping region that should be applied to child elements /// This function determines the clipping region that should be applied to child elements
/// when rendering. It takes into account the element's selection rectangle, transform, /// when rendering. It takes into account the element's selection rectangle, transform.
/// and any additional modifiers.
/// ///
/// # Parameters /// # Parameters
/// ///
/// * `element` - The shape element for which to calculate clip bounds /// * `element` - The shape element for which to calculate clip bounds
/// * `modifiers` - Optional transformation matrix to apply to the bounds
/// * `offset` - Optional offset (x, y) to adjust the bounds position. When provided, /// * `offset` - Optional offset (x, y) to adjust the bounds position. When provided,
/// the bounds are translated by the negative of this offset, effectively moving /// the bounds are translated by the negative of this offset, effectively moving
/// the clipping region to compensate for coordinate system transformations. /// the clipping region to compensate for coordinate system transformations.
@ -78,7 +73,6 @@ impl NodeRenderState {
pub fn get_children_clip_bounds( pub fn get_children_clip_bounds(
&self, &self,
element: &Shape, element: &Shape,
modifiers: Option<&Matrix>,
offset: Option<(f32, f32)>, offset: Option<(f32, f32)>,
) -> Option<(Rect, Option<Corners>, Matrix)> { ) -> Option<(Rect, Option<Corners>, Matrix)> {
if self.id.is_nil() || !element.clip() { if self.id.is_nil() || !element.clip() {
@ -97,10 +91,6 @@ impl NodeRenderState {
transform.post_translate(bounds.center()); transform.post_translate(bounds.center());
transform.pre_translate(-bounds.center()); transform.pre_translate(-bounds.center());
if let Some(modifier) = modifiers {
transform.post_concat(modifier);
}
let corners = match &element.shape_type { let corners = match &element.shape_type {
Type::Rect(data) => data.corners, Type::Rect(data) => data.corners,
Type::Frame(data) => data.corners, Type::Frame(data) => data.corners,
@ -119,12 +109,10 @@ impl NodeRenderState {
/// # Parameters /// # Parameters
/// ///
/// * `element` - The shape element for which to calculate shadow clip bounds /// * `element` - The shape element for which to calculate shadow clip bounds
/// * `modifiers` - Optional transformation matrix to apply to the bounds
/// * `shadow` - The shadow configuration containing blur, offset, and other properties /// * `shadow` - The shadow configuration containing blur, offset, and other properties
pub fn get_nested_shadow_clip_bounds( pub fn get_nested_shadow_clip_bounds(
&self, &self,
element: &Shape, element: &Shape,
modifiers: Option<&Matrix>,
shadow: &Shadow, shadow: &Shadow,
) -> Option<(Rect, Option<Corners>, Matrix)> { ) -> Option<(Rect, Option<Corners>, Matrix)> {
if self.id.is_nil() { if self.id.is_nil() {
@ -142,10 +130,6 @@ impl NodeRenderState {
transform.post_translate(element.center()); transform.post_translate(element.center());
transform.pre_translate(-element.center()); transform.pre_translate(-element.center());
if let Some(modifier) = modifiers {
transform.post_concat(modifier);
}
let corners = match &element.shape_type { let corners = match &element.shape_type {
Type::Rect(data) => data.corners, Type::Rect(data) => data.corners,
Type::Frame(data) => data.corners, Type::Frame(data) => data.corners,
@ -275,28 +259,6 @@ pub fn get_cache_size(viewbox: Viewbox, scale: f32) -> skia::ISize {
.into() .into()
} }
fn is_modified_child(
shape: &Shape,
shapes: ShapesPoolRef,
modifiers: &HashMap<Uuid, Matrix>,
) -> bool {
if modifiers.is_empty() {
return false;
}
let ids = shape.all_children(shapes, true, false);
let default = &Matrix::default();
let parent_modifier = modifiers.get(&shape.id).unwrap_or(default);
// Returns true if the transform of any child is different to the parent's
ids.iter().any(|id| {
!math::is_close_matrix(
parent_modifier,
modifiers.get(id).unwrap_or(&Matrix::default()),
)
})
}
impl RenderState { impl RenderState {
pub fn new(width: i32, height: i32) -> RenderState { pub fn new(width: i32, height: i32) -> RenderState {
// This needs to be done once per WebGL context. // This needs to be done once per WebGL context.
@ -476,9 +438,6 @@ impl RenderState {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn render_shape( pub fn render_shape(
&mut self, &mut self,
shapes: ShapesPoolRef,
modifiers: &HashMap<Uuid, Matrix>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
shape: &Shape, shape: &Shape,
scale_content: Option<&f32>, scale_content: Option<&f32>,
clip_bounds: Option<(Rect, Option<Corners>, Matrix)>, clip_bounds: Option<(Rect, Option<Corners>, Matrix)>,
@ -546,10 +505,6 @@ impl RenderState {
// We don't want to change the value in the global state // We don't want to change the value in the global state
let mut shape: Cow<Shape> = Cow::Borrowed(shape); let mut shape: Cow<Shape> = Cow::Borrowed(shape);
if let Some(shape_modifiers) = modifiers.get(&shape.id) {
shape.to_mut().apply_transform(shape_modifiers);
}
let mut nested_blur_value = 0.; let mut nested_blur_value = 0.;
for nested_blur in self.nested_blurs.iter().flatten() { for nested_blur in self.nested_blurs.iter().flatten() {
if !nested_blur.hidden && nested_blur.blur_type == BlurType::LayerBlur { if !nested_blur.hidden && nested_blur.blur_type == BlurType::LayerBlur {
@ -580,11 +535,6 @@ impl RenderState {
match &shape.shape_type { match &shape.shape_type {
Type::SVGRaw(sr) => { Type::SVGRaw(sr) => {
if let Some(shape_modifiers) = modifiers.get(&shape.id) {
self.surfaces
.canvas(fills_surface_id)
.concat(shape_modifiers);
}
self.surfaces.canvas(fills_surface_id).concat(&matrix); self.surfaces.canvas(fills_surface_id).concat(&matrix);
if let Some(svg) = shape.svg.as_ref() { if let Some(svg) = shape.svg.as_ref() {
svg.render(self.surfaces.canvas(fills_surface_id)) svg.render(self.surfaces.canvas(fills_surface_id))
@ -751,20 +701,7 @@ impl RenderState {
s.canvas().concat(&matrix); s.canvas().concat(&matrix);
}); });
// For boolean shapes, there's no need to calculate children because let shape = &shape;
// when painting the shape, the necessary path is already calculated
let shape = if let Type::Bool(_) = &shape.shape_type {
// If any child transform doesn't match the parent transform means
// that the children is transformed and we need to recalculate the
// boolean
if is_modified_child(&shape, shapes, modifiers) {
&bools::update_bool_to_path(&shape, shapes, modifiers, structure)
} else {
&shape
}
} else {
&shape
};
if shape.fills.is_empty() if shape.fills.is_empty()
&& !matches!(shape.shape_type, Type::Group(_)) && !matches!(shape.shape_type, Type::Group(_))
@ -837,12 +774,7 @@ impl RenderState {
} }
} }
pub fn render_from_cache( pub fn render_from_cache(&mut self, shapes: ShapesPoolRef) {
&mut self,
shapes: ShapesPoolRef,
modifiers: &HashMap<Uuid, Matrix>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
) {
let scale = self.get_cached_scale(); let scale = self.get_cached_scale();
if let Some(snapshot) = &self.cached_target_snapshot { if let Some(snapshot) = &self.cached_target_snapshot {
let canvas = self.surfaces.canvas(SurfaceId::Target); let canvas = self.surfaces.canvas(SurfaceId::Target);
@ -875,7 +807,7 @@ impl RenderState {
debug::render(self); debug::render(self);
} }
ui::render(self, shapes, modifiers, structure); ui::render(self, shapes);
debug::render_wasm_label(self); debug::render_wasm_label(self);
self.flush_and_submit(); self.flush_and_submit();
@ -885,8 +817,6 @@ impl RenderState {
pub fn start_render_loop( pub fn start_render_loop(
&mut self, &mut self,
tree: ShapesPoolRef, tree: ShapesPoolRef,
modifiers: &HashMap<Uuid, Matrix>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
scale_content: &HashMap<Uuid, f32>, scale_content: &HashMap<Uuid, f32>,
timestamp: i32, timestamp: i32,
) -> Result<(), String> { ) -> Result<(), String> {
@ -937,7 +867,7 @@ impl RenderState {
self.current_tile = None; self.current_tile = None;
self.render_in_progress = true; self.render_in_progress = true;
self.apply_drawing_to_render_canvas(None); self.apply_drawing_to_render_canvas(None);
self.process_animation_frame(tree, modifiers, structure, scale_content, timestamp)?; self.process_animation_frame(tree, scale_content, timestamp)?;
performance::end_measure!("start_render_loop"); performance::end_measure!("start_render_loop");
Ok(()) Ok(())
} }
@ -945,21 +875,13 @@ impl RenderState {
pub fn process_animation_frame( pub fn process_animation_frame(
&mut self, &mut self,
tree: ShapesPoolRef, tree: ShapesPoolRef,
modifiers: &HashMap<Uuid, Matrix>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
scale_content: &HashMap<Uuid, f32>, scale_content: &HashMap<Uuid, f32>,
timestamp: i32, timestamp: i32,
) -> Result<(), String> { ) -> Result<(), String> {
performance::begin_measure!("process_animation_frame"); performance::begin_measure!("process_animation_frame");
if self.render_in_progress { if self.render_in_progress {
if tree.len() != 0 { if tree.len() != 0 {
self.render_shape_tree_partial( self.render_shape_tree_partial(tree, scale_content, timestamp)?;
tree,
modifiers,
structure,
scale_content,
timestamp,
)?;
} else { } else {
println!("Empty tree"); println!("Empty tree");
} }
@ -1031,9 +953,6 @@ impl RenderState {
#[inline] #[inline]
pub fn render_shape_exit( pub fn render_shape_exit(
&mut self, &mut self,
tree: ShapesPoolRef,
modifiers: &HashMap<Uuid, Matrix>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
element: &Shape, element: &Shape,
visited_mask: bool, visited_mask: bool,
scale_content: Option<&f32>, scale_content: Option<&f32>,
@ -1091,9 +1010,6 @@ impl RenderState {
element_strokes.to_mut().clear_shadows(); element_strokes.to_mut().clear_shadows();
element_strokes.to_mut().clip_content = false; element_strokes.to_mut().clip_content = false;
self.render_shape( self.render_shape(
tree,
modifiers,
structure,
&element_strokes, &element_strokes,
scale_content, scale_content,
None, None,
@ -1179,9 +1095,6 @@ impl RenderState {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn render_drop_black_shadow( fn render_drop_black_shadow(
&mut self, &mut self,
shapes: ShapesPoolRef,
modifiers: &HashMap<Uuid, Matrix>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
shape: &Shape, shape: &Shape,
shadow: &Shadow, shadow: &Shadow,
scale_content: Option<&f32>, scale_content: Option<&f32>,
@ -1233,9 +1146,6 @@ impl RenderState {
.translate(translation); .translate(translation);
self.render_shape( self.render_shape(
shapes,
modifiers,
structure,
&plain_shape, &plain_shape,
scale_content, scale_content,
clip_bounds, clip_bounds,
@ -1254,8 +1164,6 @@ impl RenderState {
pub fn render_shape_tree_partial_uncached( pub fn render_shape_tree_partial_uncached(
&mut self, &mut self,
tree: ShapesPoolRef, tree: ShapesPoolRef,
modifiers: &HashMap<Uuid, Matrix>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
scale_content: &HashMap<Uuid, f32>, scale_content: &HashMap<Uuid, f32>,
timestamp: i32, timestamp: i32,
) -> Result<(bool, bool), String> { ) -> Result<(bool, bool), String> {
@ -1272,10 +1180,11 @@ impl RenderState {
} = node_render_state; } = node_render_state;
is_empty = false; is_empty = false;
let element = tree.get(&node_id).ok_or(
"Error: Element with root_id {node_render_state.id} not found in the tree." let element = tree.get(&node_id).ok_or(format!(
.to_string(), "Error: Element with root_id {} not found in the tree.",
)?; node_render_state.id
))?;
// If the shape is not in the tile set, then we update // If the shape is not in the tile set, then we update
// it. // it.
@ -1284,14 +1193,7 @@ impl RenderState {
} }
if visited_children { if visited_children {
self.render_shape_exit( self.render_shape_exit(element, visited_mask, scale_content.get(&element.id));
tree,
modifiers,
structure,
element,
visited_mask,
scale_content.get(&element.id),
);
continue; continue;
} }
@ -1351,9 +1253,6 @@ impl RenderState {
// First pass: Render shadow in black to establish alpha mask // First pass: Render shadow in black to establish alpha mask
self.render_drop_black_shadow( self.render_drop_black_shadow(
tree,
modifiers,
structure,
element, element,
shadow, shadow,
scale_content.get(&element.id), scale_content.get(&element.id),
@ -1369,17 +1268,11 @@ impl RenderState {
if shadow_shape.hidden { if shadow_shape.hidden {
continue; continue;
} }
let clip_bounds = node_render_state.get_nested_shadow_clip_bounds( let clip_bounds = node_render_state
element, .get_nested_shadow_clip_bounds(element, shadow);
modifiers.get(&element.id),
shadow,
);
if !matches!(shadow_shape.shape_type, Type::Text(_)) { if !matches!(shadow_shape.shape_type, Type::Text(_)) {
self.render_drop_black_shadow( self.render_drop_black_shadow(
tree,
modifiers,
structure,
shadow_shape, shadow_shape,
shadow, shadow,
scale_content.get(&element.id), scale_content.get(&element.id),
@ -1415,9 +1308,6 @@ impl RenderState {
new_shadow_paint.set_blend_mode(skia::BlendMode::SrcOver); new_shadow_paint.set_blend_mode(skia::BlendMode::SrcOver);
self.render_shape( self.render_shape(
tree,
modifiers,
structure,
shadow_shape, shadow_shape,
scale_content.get(&element.id), scale_content.get(&element.id),
clip_bounds, clip_bounds,
@ -1456,9 +1346,6 @@ impl RenderState {
.clear(skia::Color::TRANSPARENT); .clear(skia::Color::TRANSPARENT);
self.render_shape( self.render_shape(
tree,
modifiers,
structure,
element, element,
scale_content.get(&element.id), scale_content.get(&element.id),
clip_bounds, clip_bounds,
@ -1497,14 +1384,10 @@ impl RenderState {
}); });
if element.is_recursive() { if element.is_recursive() {
let children_clip_bounds = node_render_state.get_children_clip_bounds( let children_clip_bounds =
element, node_render_state.get_children_clip_bounds(element, None);
modifiers.get(&element.id),
None,
);
let mut children_ids = let mut children_ids = element.children_ids(false);
element.modified_children_ids(structure.get(&element.id), false);
// Z-index ordering on Layouts // Z-index ordering on Layouts
if element.has_layout() { if element.has_layout() {
@ -1538,8 +1421,6 @@ impl RenderState {
pub fn render_shape_tree_partial( pub fn render_shape_tree_partial(
&mut self, &mut self,
tree: ShapesPoolRef, tree: ShapesPoolRef,
modifiers: &HashMap<Uuid, Matrix>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
scale_content: &HashMap<Uuid, f32>, scale_content: &HashMap<Uuid, f32>,
timestamp: i32, timestamp: i32,
) -> Result<(), String> { ) -> Result<(), String> {
@ -1566,13 +1447,8 @@ impl RenderState {
} }
} else { } else {
performance::begin_measure!("render_shape_tree::uncached"); performance::begin_measure!("render_shape_tree::uncached");
let (is_empty, early_return) = self.render_shape_tree_partial_uncached( let (is_empty, early_return) =
tree, self.render_shape_tree_partial_uncached(tree, scale_content, timestamp)?;
modifiers,
structure,
scale_content,
timestamp,
)?;
if early_return { if early_return {
return Ok(()); return Ok(());
} }
@ -1606,7 +1482,7 @@ impl RenderState {
let Some(root) = tree.get(&Uuid::nil()) else { let Some(root) = tree.get(&Uuid::nil()) else {
return Err(String::from("Root shape not found")); return Err(String::from("Root shape not found"));
}; };
let root_ids = root.modified_children_ids(structure.get(&root.id), false); let root_ids = root.children_ids(false);
// If we finish processing every node rendering is complete // If we finish processing every node rendering is complete
// let's check if there are more pending nodes // let's check if there are more pending nodes
@ -1649,7 +1525,7 @@ impl RenderState {
debug::render(self); debug::render(self);
} }
ui::render(self, tree, modifiers, structure); ui::render(self, tree);
debug::render_wasm_label(self); debug::render_wasm_label(self);
Ok(()) Ok(())
@ -1678,6 +1554,7 @@ impl RenderState {
// Then, add the shape to the new tiles // Then, add the shape to the new tiles
for tile in new_tiles { for tile in new_tiles {
self.remove_cached_tile_shape(tile, shape.id);
self.tiles.add_shape_at(tile, shape.id); self.tiles.add_shape_at(tile, shape.id);
} }
} }
@ -1689,27 +1566,18 @@ impl RenderState {
self.tiles.remove_shape_at(tile, id); self.tiles.remove_shape_at(tile, id);
} }
pub fn rebuild_tiles_shallow( pub fn rebuild_tiles_shallow(&mut self, tree: ShapesPoolRef) {
&mut self,
tree: ShapesPoolRef,
modifiers: &HashMap<Uuid, Matrix>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
) {
performance::begin_measure!("rebuild_tiles_shallow"); performance::begin_measure!("rebuild_tiles_shallow");
self.tiles.invalidate(); self.tiles.invalidate();
self.surfaces.remove_cached_tiles(self.background_color); self.surfaces.remove_cached_tiles(self.background_color);
let mut nodes = vec![Uuid::nil()]; let mut nodes = vec![Uuid::nil()];
while let Some(shape_id) = nodes.pop() { while let Some(shape_id) = nodes.pop() {
if let Some(shape) = tree.get(&shape_id) { if let Some(shape) = tree.get(&shape_id) {
let mut shape: Cow<Shape> = Cow::Borrowed(shape);
if shape_id != Uuid::nil() { if shape_id != Uuid::nil() {
if let Some(modifier) = modifiers.get(&shape_id) {
shape.to_mut().apply_transform(modifier);
}
self.update_tile_for(&shape, tree); self.update_tile_for(&shape, tree);
} else { } else {
// We only need to rebuild tiles from the first level. // We only need to rebuild tiles from the first level.
let children = shape.modified_children_ids(structure.get(&shape.id), false); let children = shape.children_ids(false);
for child_id in children.iter() { for child_id in children.iter() {
nodes.push(*child_id); nodes.push(*child_id);
} }
@ -1719,27 +1587,18 @@ impl RenderState {
performance::end_measure!("rebuild_tiles_shallow"); performance::end_measure!("rebuild_tiles_shallow");
} }
pub fn rebuild_tiles( pub fn rebuild_tiles(&mut self, tree: ShapesPoolRef) {
&mut self,
tree: ShapesPoolRef,
modifiers: &HashMap<Uuid, Matrix>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
) {
performance::begin_measure!("rebuild_tiles"); performance::begin_measure!("rebuild_tiles");
self.tiles.invalidate(); self.tiles.invalidate();
self.surfaces.remove_cached_tiles(self.background_color); self.surfaces.remove_cached_tiles(self.background_color);
let mut nodes = vec![Uuid::nil()]; let mut nodes = vec![Uuid::nil()];
while let Some(shape_id) = nodes.pop() { while let Some(shape_id) = nodes.pop() {
if let Some(shape) = tree.get(&shape_id) { if let Some(shape) = tree.get(&shape_id) {
let mut shape: Cow<Shape> = Cow::Borrowed(shape);
if shape_id != Uuid::nil() { if shape_id != Uuid::nil() {
if let Some(modifier) = modifiers.get(&shape_id) {
shape.to_mut().apply_transform(modifier);
}
self.update_tile_for(&shape, tree); self.update_tile_for(&shape, tree);
} }
let children = shape.modified_children_ids(structure.get(&shape.id), false); let children = shape.children_ids(false);
for child_id in children.iter() { for child_id in children.iter() {
nodes.push(*child_id); nodes.push(*child_id);
} }

View File

@ -1,21 +1,12 @@
use skia_safe::{self as skia}; use skia_safe::{self as skia};
use std::collections::HashMap;
use crate::math::{Matrix, Rect}; use crate::math::Rect;
use crate::shapes::modifiers::grid_layout::grid_cell_data; use crate::shapes::modifiers::grid_layout::grid_cell_data;
use crate::shapes::{Shape, StructureEntry}; use crate::shapes::Shape;
use crate::state::ShapesPoolRef; use crate::state::ShapesPoolRef;
use crate::uuid::Uuid;
pub fn render_overlay( pub fn render_overlay(zoom: f32, canvas: &skia::Canvas, shape: &Shape, shapes: ShapesPoolRef) {
zoom: f32, let cells = grid_cell_data(shape, shapes, true);
canvas: &skia::Canvas,
shape: &Shape,
shapes: ShapesPoolRef,
modifiers: &HashMap<Uuid, Matrix>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
) {
let cells = grid_cell_data(shape, shapes, modifiers, structure, true);
let mut paint = skia::Paint::default(); let mut paint = skia::Paint::default();
paint.set_style(skia::PaintStyle::Stroke); paint.set_style(skia::PaintStyle::Stroke);

View File

@ -1,18 +1,9 @@
use skia_safe::{self as skia, Color4f}; use skia_safe::{self as skia, Color4f};
use std::collections::HashMap;
use super::{RenderState, ShapesPoolRef, SurfaceId}; use super::{RenderState, ShapesPoolRef, SurfaceId};
use crate::math::Matrix;
use crate::render::grid_layout; use crate::render::grid_layout;
use crate::shapes::StructureEntry;
use crate::uuid::Uuid;
pub fn render( pub fn render(render_state: &mut RenderState, shapes: ShapesPoolRef) {
render_state: &mut RenderState,
shapes: ShapesPoolRef,
modifiers: &HashMap<Uuid, Matrix>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
) {
let canvas = render_state.surfaces.canvas(SurfaceId::UI); let canvas = render_state.surfaces.canvas(SurfaceId::UI);
canvas.clear(Color4f::new(0.0, 0.0, 0.0, 0.0)); canvas.clear(Color4f::new(0.0, 0.0, 0.0, 0.0));
@ -29,7 +20,7 @@ pub fn render(
if let Some(id) = render_state.show_grid { if let Some(id) = render_state.show_grid {
if let Some(shape) = shapes.get(&id) { if let Some(shape) = shapes.get(&id) {
grid_layout::render_overlay(zoom, canvas, shape, shapes, modifiers, structure); grid_layout::render_overlay(zoom, canvas, shape, shapes);
} }
} }

View File

@ -47,6 +47,7 @@ pub use svgraw::*;
pub use text::*; pub use text::*;
pub use transform::*; pub use transform::*;
use crate::math::bools as math_bools;
use crate::math::{self, Bounds, Matrix, Point}; use crate::math::{self, Bounds, Matrix, Point};
use indexmap::IndexSet; use indexmap::IndexSet;
@ -309,6 +310,10 @@ impl Shape {
matches!(self.shape_type, Type::Frame(_)) matches!(self.shape_type, Type::Frame(_))
} }
pub fn is_bool(&self) -> bool {
matches!(self.shape_type, Type::Bool(_))
}
pub fn is_group_like(&self) -> bool { pub fn is_group_like(&self) -> bool {
matches!(self.shape_type, Type::Group(_)) || matches!(self.shape_type, Type::Bool(_)) matches!(self.shape_type, Type::Group(_)) || matches!(self.shape_type, Type::Bool(_))
} }
@ -901,6 +906,7 @@ impl Shape {
self.children.first() self.children.first()
} }
// TODO: Review this to use children_ids_iter instead
pub fn children_ids(&self, include_hidden: bool) -> IndexSet<Uuid> { pub fn children_ids(&self, include_hidden: bool) -> IndexSet<Uuid> {
if include_hidden { if include_hidden {
return self.children.clone().into_iter().rev().collect(); return self.children.clone().into_iter().rev().collect();
@ -1164,7 +1170,7 @@ impl Shape {
for st in structure { for st in structure {
match st.entry_type { match st.entry_type {
StructureEntryType::AddChild => { StructureEntryType::AddChild => {
result.insert(result.len() - st.index as usize, st.id); result.insert(st.index as usize, st.id);
} }
StructureEntryType::RemoveChild => { StructureEntryType::RemoveChild => {
to_remove.insert(&st.id); to_remove.insert(&st.id);
@ -1182,6 +1188,7 @@ impl Shape {
pub fn transformed( pub fn transformed(
&self, &self,
shapes_pool: ShapesPoolRef,
transform: Option<&Matrix>, transform: Option<&Matrix>,
structure: Option<&Vec<StructureEntry>>, structure: Option<&Vec<StructureEntry>>,
) -> Self { ) -> Self {
@ -1192,6 +1199,9 @@ impl Shape {
if let Some(structure) = structure { if let Some(structure) = structure {
shape.to_mut().apply_structure(structure); shape.to_mut().apply_structure(structure);
} }
if self.is_bool() {
math_bools::update_bool_to_path(shape.to_mut(), shapes_pool)
}
shape.into_owned() shape.into_owned()
} }
@ -1247,73 +1257,6 @@ impl Shape {
.count() .count()
} }
/*
Returns the list of children taking into account the structure modifiers
*/
pub fn modified_children_ids(
&self,
structure: Option<&Vec<StructureEntry>>,
include_hidden: bool,
) -> IndexSet<Uuid> {
if let Some(structure) = structure {
let mut result: Vec<Uuid> =
Vec::from_iter(self.children_ids(include_hidden).iter().copied());
let mut to_remove = HashSet::<&Uuid>::new();
for st in structure {
match st.entry_type {
StructureEntryType::AddChild => {
result.insert(result.len() - st.index as usize, st.id);
}
StructureEntryType::RemoveChild => {
to_remove.insert(&st.id);
}
_ => {}
}
}
let ret: IndexSet<Uuid> = result
.iter()
.filter(|id| !to_remove.contains(id))
.copied()
.collect();
ret
} else {
self.children_ids(include_hidden)
}
}
pub fn modified_children_ids_iter<'a>(
&'a self,
structure: Option<&'a Vec<StructureEntry>>,
include_hidden: bool,
) -> Box<dyn Iterator<Item = Cow<'a, Uuid>> + 'a> {
if let Some(structure) = structure {
let mut result: Vec<Cow<'a, Uuid>> = self
.children_ids_iter(include_hidden)
.map(Cow::Borrowed)
.collect();
let mut to_remove = HashSet::<Cow<'a, Uuid>>::new();
for st in structure {
match st.entry_type {
StructureEntryType::AddChild => {
result.insert(result.len() - st.index as usize, Cow::Owned(st.id));
}
StructureEntryType::RemoveChild => {
to_remove.insert(Cow::Owned(st.id));
}
_ => {}
}
}
Box::new(result.into_iter().filter(move |id| !to_remove.contains(id)))
} else {
Box::new(self.children_ids_iter(include_hidden).map(Cow::Borrowed))
}
}
pub fn drop_shadow_paints(&self) -> Vec<skia_safe::Paint> { pub fn drop_shadow_paints(&self) -> Vec<skia_safe::Paint> {
let drop_shadows: Vec<&Shadow> = self.drop_shadows_visible().collect(); let drop_shadows: Vec<&Shadow> = self.drop_shadows_visible().collect();

View File

@ -10,8 +10,7 @@ use crate::math::{self as math, bools, identitish, Bounds, Matrix, Point};
use common::GetBounds; use common::GetBounds;
use crate::shapes::{ use crate::shapes::{
ConstraintH, ConstraintV, Frame, Group, GrowType, Layout, Modifier, Shape, StructureEntry, ConstraintH, ConstraintV, Frame, Group, GrowType, Layout, Modifier, Shape, TransformEntry, Type,
TransformEntry, Type,
}; };
use crate::state::{ShapesPoolRef, State}; use crate::state::{ShapesPoolRef, State};
use crate::uuid::Uuid; use crate::uuid::Uuid;
@ -24,10 +23,9 @@ fn propagate_children(
parent_bounds_after: &Bounds, parent_bounds_after: &Bounds,
transform: Matrix, transform: Matrix,
bounds: &HashMap<Uuid, Bounds>, bounds: &HashMap<Uuid, Bounds>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
scale_content: &HashMap<Uuid, f32>, scale_content: &HashMap<Uuid, f32>,
) -> VecDeque<Modifier> { ) -> VecDeque<Modifier> {
let children_ids = shape.modified_children_ids(structure.get(&shape.id), true); let children_ids = shape.children_ids(true);
if children_ids.is_empty() || identitish(&transform) { if children_ids.is_empty() || identitish(&transform) {
return VecDeque::new(); return VecDeque::new();
@ -92,12 +90,11 @@ fn calculate_group_bounds(
shape: &Shape, shape: &Shape,
shapes: ShapesPoolRef, shapes: ShapesPoolRef,
bounds: &HashMap<Uuid, Bounds>, bounds: &HashMap<Uuid, Bounds>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
) -> Option<Bounds> { ) -> Option<Bounds> {
let shape_bounds = bounds.find(shape); let shape_bounds = bounds.find(shape);
let mut result = Vec::<Point>::new(); let mut result = Vec::<Point>::new();
for child_id in shape.modified_children_ids_iter(structure.get(&shape.id), true) { for child_id in shape.children_ids_iter(true) {
let Some(child) = shapes.get(&child_id) else { let Some(child) = shapes.get(&child_id) else {
continue; continue;
}; };
@ -112,23 +109,15 @@ fn calculate_bool_bounds(
shape: &Shape, shape: &Shape,
shapes: ShapesPoolRef, shapes: ShapesPoolRef,
bounds: &HashMap<Uuid, Bounds>, bounds: &HashMap<Uuid, Bounds>,
modifiers: &HashMap<Uuid, Matrix>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
) -> Option<Bounds> { ) -> Option<Bounds> {
let shape_bounds = bounds.find(shape); let shape_bounds = bounds.find(shape);
let children_ids = shape.modified_children_ids(structure.get(&shape.id), true); let children_ids = shape.children_ids(true);
let Type::Bool(bool_data) = &shape.shape_type else { let Type::Bool(bool_data) = &shape.shape_type else {
return Some(shape_bounds); return Some(shape_bounds);
}; };
let path = bools::bool_from_shapes( let path = bools::bool_from_shapes(bool_data.bool_type, &children_ids, shapes);
bool_data.bool_type,
&children_ids,
shapes,
modifiers,
structure,
);
Some(path.bounds()) Some(path.bounds())
} }
@ -235,7 +224,6 @@ fn propagate_transform(
&shape_bounds_after, &shape_bounds_after,
transform, transform,
bounds, bounds,
&state.structure,
&state.scale_content, &state.scale_content,
); );
entries.append(&mut children); entries.append(&mut children);
@ -265,7 +253,6 @@ fn propagate_reflow(
bounds: &mut HashMap<Uuid, Bounds>, bounds: &mut HashMap<Uuid, Bounds>,
layout_reflows: &mut Vec<Uuid>, layout_reflows: &mut Vec<Uuid>,
reflown: &mut HashSet<Uuid>, reflown: &mut HashSet<Uuid>,
modifiers: &HashMap<Uuid, Matrix>,
) { ) {
let Some(shape) = state.shapes.get(id) else { let Some(shape) = state.shapes.get(id) else {
return; return;
@ -303,7 +290,7 @@ fn propagate_reflow(
} }
} }
Type::Group(Group { masked: true }) => { Type::Group(Group { masked: true }) => {
let children_ids = shape.modified_children_ids(state.structure.get(&shape.id), true); let children_ids = shape.children_ids(true);
if let Some(child) = shapes.get(&children_ids[0]) { if let Some(child) = shapes.get(&children_ids[0]) {
let child_bounds = bounds.find(child); let child_bounds = bounds.find(child);
bounds.insert(shape.id, child_bounds); bounds.insert(shape.id, child_bounds);
@ -312,18 +299,14 @@ fn propagate_reflow(
reflown.insert(*id); reflown.insert(*id);
} }
Type::Group(_) => { Type::Group(_) => {
if let Some(shape_bounds) = if let Some(shape_bounds) = calculate_group_bounds(shape, shapes, bounds) {
calculate_group_bounds(shape, shapes, bounds, &state.structure)
{
bounds.insert(shape.id, shape_bounds); bounds.insert(shape.id, shape_bounds);
reflow_parent = true; reflow_parent = true;
} }
reflown.insert(*id); reflown.insert(*id);
} }
Type::Bool(_) => { Type::Bool(_) => {
if let Some(shape_bounds) = if let Some(shape_bounds) = calculate_bool_bounds(shape, shapes, bounds) {
calculate_bool_bounds(shape, shapes, bounds, modifiers, &state.structure)
{
bounds.insert(shape.id, shape_bounds); bounds.insert(shape.id, shape_bounds);
reflow_parent = true; reflow_parent = true;
} }
@ -365,24 +348,12 @@ fn reflow_shape(
}; };
if let Some(Layout::FlexLayout(layout_data, flex_data)) = &frame_data.layout { if let Some(Layout::FlexLayout(layout_data, flex_data)) = &frame_data.layout {
let mut children = flex_layout::reflow_flex_layout( let mut children =
shape, flex_layout::reflow_flex_layout(shape, layout_data, flex_data, shapes, bounds);
layout_data,
flex_data,
shapes,
bounds,
&state.structure,
);
entries.append(&mut children); entries.append(&mut children);
} else if let Some(Layout::GridLayout(layout_data, grid_data)) = &frame_data.layout { } else if let Some(Layout::GridLayout(layout_data, grid_data)) = &frame_data.layout {
let mut children = grid_layout::reflow_grid_layout( let mut children =
shape, grid_layout::reflow_grid_layout(shape, layout_data, grid_data, shapes, bounds);
layout_data,
grid_data,
shapes,
bounds,
&state.structure,
);
entries.append(&mut children); entries.append(&mut children);
} }
reflown.insert(*id); reflown.insert(*id);
@ -398,12 +369,6 @@ pub fn propagate_modifiers(
.map(|entry| Modifier::Transform(entry.clone())) .map(|entry| Modifier::Transform(entry.clone()))
.collect(); .collect();
for id in state.structure.keys() {
if id != &Uuid::nil() {
entries.push_back(Modifier::Reflow(*id));
}
}
let mut modifiers = HashMap::<Uuid, Matrix>::new(); let mut modifiers = HashMap::<Uuid, Matrix>::new();
let mut bounds = HashMap::<Uuid, Bounds>::new(); let mut bounds = HashMap::<Uuid, Bounds>::new();
let mut reflown = HashSet::<Uuid>::new(); let mut reflown = HashSet::<Uuid>::new();
@ -432,7 +397,6 @@ pub fn propagate_modifiers(
&mut bounds, &mut bounds,
&mut layout_reflows, &mut layout_reflows,
&mut reflown, &mut reflown,
&modifiers,
), ),
} }
} }

View File

@ -2,7 +2,7 @@
use crate::math::{self as math, Bounds, Matrix, Point, Vector, VectorExt}; use crate::math::{self as math, Bounds, Matrix, Point, Vector, VectorExt};
use crate::shapes::{ use crate::shapes::{
AlignContent, AlignItems, AlignSelf, FlexData, JustifyContent, LayoutData, LayoutItem, AlignContent, AlignItems, AlignSelf, FlexData, JustifyContent, LayoutData, LayoutItem,
Modifier, Shape, StructureEntry, Modifier, Shape,
}; };
use crate::state::ShapesPoolRef; use crate::state::ShapesPoolRef;
use crate::uuid::Uuid; use crate::uuid::Uuid;
@ -181,11 +181,10 @@ fn initialize_tracks(
flex_data: &FlexData, flex_data: &FlexData,
shapes: ShapesPoolRef, shapes: ShapesPoolRef,
bounds: &HashMap<Uuid, Bounds>, bounds: &HashMap<Uuid, Bounds>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
) -> Vec<TrackData> { ) -> Vec<TrackData> {
let mut tracks = Vec::<TrackData>::new(); let mut tracks = Vec::<TrackData>::new();
let mut current_track = TrackData::default(); let mut current_track = TrackData::default();
let mut children = shape.modified_children_ids(structure.get(&shape.id), true); let mut children = shape.children_ids(true);
let mut first = true; let mut first = true;
if flex_data.is_reverse() { if flex_data.is_reverse() {
@ -435,7 +434,6 @@ fn calculate_track_data(
layout_bounds: &Bounds, layout_bounds: &Bounds,
shapes: ShapesPoolRef, shapes: ShapesPoolRef,
bounds: &HashMap<Uuid, Bounds>, bounds: &HashMap<Uuid, Bounds>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
) -> Vec<TrackData> { ) -> Vec<TrackData> {
let layout_axis = LayoutAxis::new(shape, layout_bounds, layout_data, flex_data); let layout_axis = LayoutAxis::new(shape, layout_bounds, layout_data, flex_data);
let mut tracks = initialize_tracks( let mut tracks = initialize_tracks(
@ -445,7 +443,6 @@ fn calculate_track_data(
flex_data, flex_data,
shapes, shapes,
bounds, bounds,
structure,
); );
distribute_fill_main_space(&layout_axis, &mut tracks); distribute_fill_main_space(&layout_axis, &mut tracks);
@ -576,20 +573,11 @@ pub fn reflow_flex_layout(
flex_data: &FlexData, flex_data: &FlexData,
shapes: ShapesPoolRef, shapes: ShapesPoolRef,
bounds: &mut HashMap<Uuid, Bounds>, bounds: &mut HashMap<Uuid, Bounds>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
) -> VecDeque<Modifier> { ) -> VecDeque<Modifier> {
let mut result = VecDeque::new(); let mut result = VecDeque::new();
let layout_bounds = &bounds.find(shape); let layout_bounds = &bounds.find(shape);
let layout_axis = LayoutAxis::new(shape, layout_bounds, layout_data, flex_data); let layout_axis = LayoutAxis::new(shape, layout_bounds, layout_data, flex_data);
let tracks = calculate_track_data( let tracks = calculate_track_data(shape, layout_data, flex_data, layout_bounds, shapes, bounds);
shape,
layout_data,
flex_data,
layout_bounds,
shapes,
bounds,
structure,
);
for track in tracks.iter() { for track in tracks.iter() {
let total_shapes_size = track.shapes.iter().map(|s| s.main_size).sum::<f32>(); let total_shapes_size = track.shapes.iter().map(|s| s.main_size).sum::<f32>();

View File

@ -2,7 +2,7 @@ use crate::math::{self as math, intersect_rays, Bounds, Matrix, Point, Ray, Vect
use crate::shapes::{ use crate::shapes::{
AlignContent, AlignItems, AlignSelf, Frame, GridCell, GridData, GridTrack, GridTrackType, AlignContent, AlignItems, AlignSelf, Frame, GridCell, GridData, GridTrack, GridTrackType,
JustifyContent, JustifyItems, JustifySelf, Layout, LayoutData, LayoutItem, Modifier, Shape, JustifyContent, JustifyItems, JustifySelf, Layout, LayoutData, LayoutItem, Modifier, Shape,
StructureEntry, Type, Type,
}; };
use crate::state::ShapesPoolRef; use crate::state::ShapesPoolRef;
use crate::uuid::Uuid; use crate::uuid::Uuid;
@ -603,8 +603,6 @@ pub fn create_cell_data<'a>(
pub fn grid_cell_data<'a>( pub fn grid_cell_data<'a>(
shape: &Shape, shape: &Shape,
shapes: ShapesPoolRef<'a>, shapes: ShapesPoolRef<'a>,
modifiers: &HashMap<Uuid, Matrix>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
allow_empty: bool, allow_empty: bool,
) -> Vec<CellData<'a>> { ) -> Vec<CellData<'a>> {
let Type::Frame(Frame { let Type::Frame(Frame {
@ -616,26 +614,8 @@ pub fn grid_cell_data<'a>(
}; };
let bounds = &mut HashMap::<Uuid, Bounds>::new(); let bounds = &mut HashMap::<Uuid, Bounds>::new();
let shape = &mut shape.clone();
if let Some(modifiers) = modifiers.get(&shape.id) {
shape.apply_transform(modifiers);
}
let layout_bounds = shape.bounds(); let layout_bounds = shape.bounds();
let children = shape.modified_children_ids(structure.get(&shape.id), false); let children = shape.children_ids(false);
for child_id in children.iter() {
let Some(child) = shapes.get(child_id) else {
continue;
};
if let Some(modifier) = modifiers.get(child_id) {
let mut b = bounds.find(child);
b.transform_mut(modifier);
bounds.insert(*child_id, b);
}
}
let column_tracks = calculate_tracks( let column_tracks = calculate_tracks(
true, true,
@ -725,11 +705,10 @@ pub fn reflow_grid_layout(
grid_data: &GridData, grid_data: &GridData,
shapes: ShapesPoolRef, shapes: ShapesPoolRef,
bounds: &mut HashMap<Uuid, Bounds>, bounds: &mut HashMap<Uuid, Bounds>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
) -> VecDeque<Modifier> { ) -> VecDeque<Modifier> {
let mut result = VecDeque::new(); let mut result = VecDeque::new();
let layout_bounds = bounds.find(shape); let layout_bounds = bounds.find(shape);
let children = shape.modified_children_ids(structure.get(&shape.id), true); let children = shape.children_ids(true);
let column_tracks = calculate_tracks( let column_tracks = calculate_tracks(
true, true,

View File

@ -1,22 +1,13 @@
use skia_safe::Matrix; use super::{Corners, Path, Segment, Shape, Type};
use super::{Corners, Path, Segment, Shape, StructureEntry, Type};
use crate::math; use crate::math;
use crate::shapes::text_paths::TextPaths; use crate::shapes::text_paths::TextPaths;
use crate::state::ShapesPoolRef; use crate::state::ShapesPoolRef;
use crate::uuid::Uuid;
use std::collections::HashMap;
const BEZIER_CIRCLE_C: f32 = 0.551_915_05; const BEZIER_CIRCLE_C: f32 = 0.551_915_05;
pub trait ToPath { pub trait ToPath {
fn to_path( fn to_path(&self, shapes: ShapesPoolRef) -> Path;
&self,
shapes: ShapesPoolRef,
modifiers: &HashMap<Uuid, Matrix>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
) -> Path;
} }
enum CornerType { enum CornerType {
@ -180,33 +171,28 @@ fn transform_segments(segments: Vec<Segment>, shape: &Shape) -> Vec<Segment> {
} }
impl ToPath for Shape { impl ToPath for Shape {
fn to_path( fn to_path(&self, shapes: ShapesPoolRef) -> Path {
&self,
shapes: ShapesPoolRef,
modifiers: &HashMap<Uuid, Matrix>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
) -> Path {
match &self.shape_type { match &self.shape_type {
Type::Frame(ref frame) => { Type::Frame(ref frame) => {
let children = self.modified_children_ids(structure.get(&self.id), true); let children = self.children_ids(true);
let mut result = Path::new(rect_segments(&self, frame.corners)); let mut result = Path::new(rect_segments(&self, frame.corners));
for id in children { for id in children {
let Some(shape) = shapes.get(&id) else { let Some(shape) = shapes.get(&id) else {
continue; continue;
}; };
result = join_paths(result, shape.to_path(shapes, modifiers, structure)); result = join_paths(result, shape.to_path(shapes));
} }
result result
} }
Type::Group(_) => { Type::Group(_) => {
let children = self.modified_children_ids(structure.get(&self.id), true); let children = self.children_ids(true);
let mut result = Path::default(); let mut result = Path::default();
for id in children { for id in children {
let Some(shape) = shapes.get(&id) else { let Some(shape) = shapes.get(&id) else {
continue; continue;
}; };
result = join_paths(result, shape.to_path(shapes, modifiers, structure)); result = join_paths(result, shape.to_path(shapes));
} }
// Force closure of the group path // Force closure of the group path
let mut segments = result.segments().clone(); let mut segments = result.segments().clone();

View File

@ -8,7 +8,6 @@ pub use text_editor::*;
use crate::render::RenderState; use crate::render::RenderState;
use crate::shapes::Shape; use crate::shapes::Shape;
use crate::shapes::StructureEntry;
use crate::tiles; use crate::tiles;
use crate::uuid::Uuid; use crate::uuid::Uuid;
@ -24,9 +23,7 @@ pub(crate) struct State<'a> {
pub text_editor_state: TextEditorState, pub text_editor_state: TextEditorState,
pub current_id: Option<Uuid>, pub current_id: Option<Uuid>,
pub shapes: ShapesPool<'a>, pub shapes: ShapesPool<'a>,
pub modifiers: HashMap<Uuid, skia::Matrix>,
pub scale_content: HashMap<Uuid, f32>, pub scale_content: HashMap<Uuid, f32>,
pub structure: HashMap<Uuid, Vec<StructureEntry>>,
} }
impl<'a> State<'a> { impl<'a> State<'a> {
@ -36,9 +33,7 @@ impl<'a> State<'a> {
text_editor_state: TextEditorState::new(), text_editor_state: TextEditorState::new(),
current_id: None, current_id: None,
shapes: ShapesPool::new(), shapes: ShapesPool::new(),
modifiers: HashMap::new(),
scale_content: HashMap::new(), scale_content: HashMap::new(),
structure: HashMap::new(),
} }
} }
@ -65,29 +60,18 @@ impl<'a> State<'a> {
} }
pub fn render_from_cache(&mut self) { pub fn render_from_cache(&mut self) {
self.render_state self.render_state.render_from_cache(&self.shapes);
.render_from_cache(&self.shapes, &self.modifiers, &self.structure);
} }
pub fn start_render_loop(&mut self, timestamp: i32) -> Result<(), String> { pub fn start_render_loop(&mut self, timestamp: i32) -> Result<(), String> {
self.render_state.start_render_loop( self.render_state
&self.shapes, .start_render_loop(&self.shapes, &self.scale_content, timestamp)?;
&self.modifiers,
&self.structure,
&self.scale_content,
timestamp,
)?;
Ok(()) Ok(())
} }
pub fn process_animation_frame(&mut self, timestamp: i32) -> Result<(), String> { pub fn process_animation_frame(&mut self, timestamp: i32) -> Result<(), String> {
self.render_state.process_animation_frame( self.render_state
&self.shapes, .process_animation_frame(&self.shapes, &self.scale_content, timestamp)?;
&self.modifiers,
&self.structure,
&self.scale_content,
timestamp,
)?;
Ok(()) Ok(())
} }
@ -147,6 +131,8 @@ impl<'a> State<'a> {
panic!("Invalid current shape") panic!("Invalid current shape")
}; };
shape.set_parent(id); shape.set_parent(id);
// TODO this clone doesn't seem necessary
shape.clone() shape.clone()
}; };
@ -166,6 +152,7 @@ impl<'a> State<'a> {
let Some(shape) = self.current_shape() else { let Some(shape) = self.current_shape() else {
panic!("Invalid current shape") panic!("Invalid current shape")
}; };
// TODO: Remove this clone
if !shape.id.is_nil() { if !shape.id.is_nil() {
self.render_state self.render_state
.update_tile_for(&shape.clone(), &self.shapes); .update_tile_for(&shape.clone(), &self.shapes);
@ -173,13 +160,11 @@ impl<'a> State<'a> {
} }
pub fn rebuild_tiles_shallow(&mut self) { pub fn rebuild_tiles_shallow(&mut self) {
self.render_state self.render_state.rebuild_tiles_shallow(&self.shapes);
.rebuild_tiles_shallow(&self.shapes, &self.modifiers, &self.structure);
} }
pub fn rebuild_tiles(&mut self) { pub fn rebuild_tiles(&mut self) {
self.render_state self.render_state.rebuild_tiles(&self.shapes);
.rebuild_tiles(&self.shapes, &self.modifiers, &self.structure);
} }
pub fn rebuild_modifier_tiles(&mut self, ids: Vec<Uuid>) { pub fn rebuild_modifier_tiles(&mut self, ids: Vec<Uuid>) {
@ -204,7 +189,7 @@ impl<'a> State<'a> {
let bounds = shape.bounds(); let bounds = shape.bounds();
let position = Point::new(pos_x, pos_y); let position = Point::new(pos_x, pos_y);
let cells = grid_cell_data(shape, &self.shapes, &self.modifiers, &self.structure, true); let cells = grid_cell_data(shape, &self.shapes, true);
for cell in cells { for cell in cells {
let points = &[ let points = &[

View File

@ -2,6 +2,7 @@ use std::collections::HashMap;
use std::iter; use std::iter;
use crate::performance; use crate::performance;
use crate::shapes;
use crate::shapes::Shape; use crate::shapes::Shape;
use crate::uuid::Uuid; use crate::uuid::Uuid;
@ -208,11 +209,15 @@ impl<'a> ShapesPoolImpl<'a> {
// Extend the lifetime of id to 'a - safe because it's the same Uuid stored in shapes[idx].id // Extend the lifetime of id to 'a - safe because it's the same Uuid stored in shapes[idx].id
let id_ref: &'a Uuid = &*(id as *const Uuid); let id_ref: &'a Uuid = &*(id as *const Uuid);
if (*modifiers_ptr).contains_key(&id_ref) || (*structure_ptr).contains_key(&id_ref) { if self.to_update_bool(&*shape_ptr)
|| (*modifiers_ptr).contains_key(&id_ref)
|| (*structure_ptr).contains_key(&id_ref)
{
if let Some(cell) = (*cache_ptr).get(&id_ref) { if let Some(cell) = (*cache_ptr).get(&id_ref) {
Some(cell.get_or_init(|| { Some(cell.get_or_init(|| {
let shape = &*shape_ptr; let shape = &*shape_ptr;
shape.transformed( shape.transformed(
&self,
(*modifiers_ptr).get(&id_ref), (*modifiers_ptr).get(&id_ref),
(*structure_ptr).get(&id_ref), (*structure_ptr).get(&id_ref),
) )
@ -246,14 +251,25 @@ impl<'a> ShapesPoolImpl<'a> {
// Convert HashMap<Uuid, V> to HashMap<&'a Uuid, V> using references from shapes and // Convert HashMap<Uuid, V> to HashMap<&'a Uuid, V> using references from shapes and
// Initialize the cache cells because later we don't want to have the mutable pointer // Initialize the cache cells because later we don't want to have the mutable pointer
let mut ids = Vec::<Uuid>::new();
let mut modifiers_with_refs = HashMap::with_capacity(modifiers.len()); let mut modifiers_with_refs = HashMap::with_capacity(modifiers.len());
for (uuid, matrix) in modifiers { for (uuid, matrix) in modifiers {
if let Some(uuid_ref) = self.get_uuid_ref(&uuid) { if let Some(uuid_ref) = self.get_uuid_ref(&uuid) {
self.modified_shape_cache.insert(uuid_ref, OnceCell::new()); // self.modified_shape_cache.insert(uuid_ref, OnceCell::new());
modifiers_with_refs.insert(uuid_ref, matrix); modifiers_with_refs.insert(uuid_ref, matrix);
ids.push(*uuid_ref);
} }
} }
self.modifiers = modifiers_with_refs; self.modifiers = modifiers_with_refs;
let all_ids = shapes::all_with_ancestors(&ids, &self, true);
for uuid in all_ids {
if let Some(uuid_ref) = self.get_uuid_ref(&uuid) {
self.modified_shape_cache.insert(uuid_ref, OnceCell::new());
}
}
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -261,13 +277,22 @@ impl<'a> ShapesPoolImpl<'a> {
// Convert HashMap<Uuid, V> to HashMap<&'a Uuid, V> using references from shapes and // Convert HashMap<Uuid, V> to HashMap<&'a Uuid, V> using references from shapes and
// Initialize the cache cells because later we don't want to have the mutable pointer // Initialize the cache cells because later we don't want to have the mutable pointer
let mut structure_with_refs = HashMap::with_capacity(structure.len()); let mut structure_with_refs = HashMap::with_capacity(structure.len());
let mut ids = Vec::<Uuid>::new();
for (uuid, entries) in structure { for (uuid, entries) in structure {
if let Some(uuid_ref) = self.get_uuid_ref(&uuid) { if let Some(uuid_ref) = self.get_uuid_ref(&uuid) {
self.modified_shape_cache.insert(uuid_ref, OnceCell::new());
structure_with_refs.insert(uuid_ref, entries); structure_with_refs.insert(uuid_ref, entries);
ids.push(*uuid_ref);
} }
} }
self.structure = structure_with_refs; self.structure = structure_with_refs;
let all_ids = shapes::all_with_ancestors(&ids, &self, true);
for uuid in all_ids {
if let Some(uuid_ref) = self.get_uuid_ref(&uuid) {
self.modified_shape_cache.insert(uuid_ref, OnceCell::new());
}
}
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -290,4 +315,33 @@ impl<'a> ShapesPoolImpl<'a> {
// and won't be reallocated. // and won't be reallocated.
unsafe { Some(&*(&self.shapes[idx].id as *const Uuid)) } unsafe { Some(&*(&self.shapes[idx].id as *const Uuid)) }
} }
fn to_update_bool(&self, shape: &Shape) -> bool {
// TODO: Check if any of the children is in the modifiers with a
// different matrix than the current one.
shape.is_bool()
}
} }
// fn is_modified_child(
// shape: &Shape,
// shapes: ShapesPoolRef,
// modifiers: &HashMap<Uuid, Matrix>,
// ) -> bool {
// if modifiers.is_empty() {
// return false;
// }
//
// let ids = shape.all_children(shapes, true, false);
// let default = &Matrix::default();
// let parent_modifier = modifiers.get(&shape.id).unwrap_or(default);
//
// // Returns true if the transform of any child is different to the parent's
// ids.iter().any(|id| {
// !math::is_close_matrix(
// parent_modifier,
// modifiers.get(id).unwrap_or(&Matrix::default()),
// )
// })
// }

View File

@ -213,7 +213,7 @@ pub extern "C" fn set_shape_path_content() {
pub extern "C" fn current_to_path() -> *mut u8 { pub extern "C" fn current_to_path() -> *mut u8 {
let mut result = Vec::<RawSegmentData>::default(); let mut result = Vec::<RawSegmentData>::default();
with_current_shape!(state, |shape: &Shape| { with_current_shape!(state, |shape: &Shape| {
let path = shape.to_path(&state.shapes, &state.modifiers, &state.structure); let path = shape.to_path(&state.shapes);
result = path result = path
.segments() .segments()
.iter() .iter()

View File

@ -57,13 +57,7 @@ pub extern "C" fn calculate_bool(raw_bool_type: u8) -> *mut u8 {
let bool_type = RawBoolType::from(raw_bool_type).into(); let bool_type = RawBoolType::from(raw_bool_type).into();
let result; let result;
with_state!(state, { with_state!(state, {
let path = math::bools::bool_from_shapes( let path = math::bools::bool_from_shapes(bool_type, &entries, &state.shapes);
bool_type,
&entries,
&state.shapes,
&state.modifiers,
&state.structure,
);
result = path result = path
.segments() .segments()
.iter() .iter()