Improved performance in modifiers

This commit is contained in:
alonso.torres
2025-11-11 12:04:23 +01:00
parent c1638817b2
commit 2d63730bfa
9 changed files with 113 additions and 57 deletions

View File

@@ -767,6 +767,11 @@
:always :always
(ctl/update-flex-child value)))) (ctl/update-flex-child value))))
(defn remove-children-set
[shapes children-to-remove]
(let [remove? (set children-to-remove)]
(d/removev remove? shapes)))
(defn apply-modifier (defn apply-modifier
[shape operation] [shape operation]
(let [type (dm/get-prop operation :type)] (let [type (dm/get-prop operation :type)]
@@ -793,7 +798,7 @@
:remove-children :remove-children
(let [value (dm/get-prop operation :value)] (let [value (dm/get-prop operation :value)]
(update shape :shapes remove-children value)) (update shape :shapes remove-children-set value))
:scale-content :scale-content
(let [value (dm/get-prop operation :value)] (let [value (dm/get-prop operation :value)]
@@ -810,11 +815,6 @@
(defn apply-structure-modifiers (defn apply-structure-modifiers
"Apply structure changes to a shape" "Apply structure changes to a shape"
[shape modifiers] [shape modifiers]
(let [remove-children (as-> shape $
(fn [shapes children-to-remove] (reduce apply-modifier $ (dm/get-prop modifiers :structure-parent))
(let [remove? (set children-to-remove)] (reduce apply-modifier $ (dm/get-prop modifiers :structure-child))))
(d/removev remove? shapes)))]
(as-> shape $
(reduce apply-modifier $ (dm/get-prop modifiers :structure-parent))
(reduce apply-modifier $ (dm/get-prop modifiers :structure-child)))))

View File

@@ -19,7 +19,6 @@
[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.modifiers :as ctm]
[app.common.types.path :as path] [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]]

View File

@@ -51,6 +51,13 @@ pub fn identitish(m: &Matrix) -> bool {
&& is_close_to(m.skew_y(), 0.0) && is_close_to(m.skew_y(), 0.0)
} }
pub fn is_move_only_matrix(m: &Matrix) -> bool {
is_close_to(m.scale_x(), 1.0)
&& is_close_to(m.scale_y(), 1.0)
&& is_close_to(m.skew_x(), 0.0)
&& is_close_to(m.skew_y(), 0.0)
}
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
pub struct Bounds { pub struct Bounds {
pub nw: Point, pub nw: Point,

View File

@@ -1,5 +1,7 @@
use skia_safe::{self as skia}; use skia_safe::{self as skia};
use indexmap::IndexSet;
use crate::uuid::Uuid; use crate::uuid::Uuid;
use std::borrow::Cow; use std::borrow::Cow;
use std::cell::{OnceCell, RefCell}; use std::cell::{OnceCell, RefCell};
@@ -342,9 +344,14 @@ impl Shape {
matches!( matches!(
self.shape_type, self.shape_type,
Type::Frame(Frame { Type::Frame(Frame {
layout: Some(layouts::Layout::FlexLayout(_, FlexData { layout: Some(layouts::Layout::FlexLayout(
direction: layouts::FlexDirection::RowReverse | layouts::FlexDirection::ColumnReverse, .. _,
})), FlexData {
direction: layouts::FlexDirection::RowReverse
| layouts::FlexDirection::ColumnReverse,
..
}
)),
.. ..
}) })
) )
@@ -1279,13 +1286,14 @@ impl Shape {
} }
pub fn apply_structure(&mut self, structure: &Vec<StructureEntry>) { pub fn apply_structure(&mut self, structure: &Vec<StructureEntry>) {
let mut result: Vec<Uuid> = Vec::from_iter(self.children.iter().copied()); let mut result = IndexSet::<Uuid>::from_iter(self.children.iter().copied());
let mut to_remove = HashSet::<&Uuid>::new(); let mut to_remove = HashSet::<&Uuid>::new();
for st in structure { for st in structure {
match st.entry_type { match st.entry_type {
StructureEntryType::AddChild => { StructureEntryType::AddChild => {
result.insert(st.index as usize, st.id); let index = usize::min(result.len() - 1, st.index as usize);
result.shift_insert(index, st.id);
} }
StructureEntryType::RemoveChild => { StructureEntryType::RemoveChild => {
to_remove.insert(&st.id); to_remove.insert(&st.id);

View File

@@ -6,11 +6,12 @@ mod flex_layout;
pub mod common; pub mod common;
pub mod grid_layout; pub mod grid_layout;
use crate::math::{self as math, bools, identitish, Bounds, Matrix, Point}; use crate::math::{self as math, bools, identitish, is_close_to, Bounds, Matrix, Point};
use common::GetBounds; use common::GetBounds;
use crate::shapes::{ use crate::shapes::{
ConstraintH, ConstraintV, Frame, Group, GrowType, Layout, Modifier, Shape, TransformEntry, Type, ConstraintH, ConstraintV, Frame, Group, GrowType, Layout, Modifier, Shape, TransformEntry,
TransformEntrySource, Type,
}; };
use crate::state::{ShapesPoolRef, State}; use crate::state::{ShapesPoolRef, State};
use crate::uuid::Uuid; use crate::uuid::Uuid;
@@ -75,7 +76,7 @@ fn propagate_children(
child.ignore_constraints, child.ignore_constraints,
); );
result.push_back(Modifier::transform(*child_id, transform)); result.push_back(Modifier::transform_propagate(*child_id, transform));
} }
result result
@@ -182,33 +183,43 @@ fn propagate_transform(
let mut transform = entry.transform; let mut transform = entry.transform;
// NOTA: No puedo utilizar un clone porque entonces estaríamos // Only check the text layout when the width/height changes
// perdiendo la referencia al contenido del layout... if !is_close_to(shape_bounds_before.width(), shape_bounds_after.width())
if let Type::Text(text_content) = &mut shape.shape_type.clone() { || !is_close_to(shape_bounds_before.height(), shape_bounds_after.height())
if text_content.needs_update_layout() { {
text_content.update_layout(shape.selrect); if let Type::Text(text_content) = &mut shape.shape_type.clone() {
} match text_content.grow_type() {
match text_content.grow_type() { GrowType::AutoHeight => {
GrowType::AutoHeight => { if text_content.needs_update_layout() {
let height = text_content.size.height; text_content.update_layout(shape.selrect);
let resize_transform = math::resize_matrix( }
&shape_bounds_after, let height = text_content.size.height;
&shape_bounds_after, let resize_transform = math::resize_matrix(
shape_bounds_after.width(), &shape_bounds_after,
height, &shape_bounds_after,
); shape_bounds_after.width(),
shape_bounds_after = shape_bounds_after.transform(&resize_transform); height,
transform.post_concat(&resize_transform); );
shape_bounds_after = shape_bounds_after.transform(&resize_transform);
transform.post_concat(&resize_transform);
}
GrowType::AutoWidth => {
if text_content.needs_update_layout() {
text_content.update_layout(shape.selrect);
}
let width = text_content.width();
let height = text_content.size.height;
let resize_transform = math::resize_matrix(
&shape_bounds_after,
&shape_bounds_after,
width,
height,
);
shape_bounds_after = shape_bounds_after.transform(&resize_transform);
transform.post_concat(&resize_transform);
}
GrowType::Fixed => {}
} }
GrowType::AutoWidth => {
let width = text_content.width();
let height = text_content.size.height;
let resize_transform =
math::resize_matrix(&shape_bounds_after, &shape_bounds_after, width, height);
shape_bounds_after = shape_bounds_after.transform(&resize_transform);
transform.post_concat(&resize_transform);
}
GrowType::Fixed => {}
} }
} }
@@ -234,12 +245,19 @@ fn propagate_transform(
shape_modif.post_concat(&transform); shape_modif.post_concat(&transform);
modifiers.insert(shape.id, shape_modif); modifiers.insert(shape.id, shape_modif);
if shape.has_layout() { let is_resize = !math::is_move_only_matrix(&transform);
let is_propagate = entry.source == TransformEntrySource::Propagate;
// If this is a layout and we're only moving don't need to reflow
if shape.has_layout() && is_resize {
entries.push_back(Modifier::reflow(shape.id)); entries.push_back(Modifier::reflow(shape.id));
} }
if let Some(parent) = shape.parent_id.and_then(|id| shapes.get(&id)) { if let Some(parent) = shape.parent_id.and_then(|id| shapes.get(&id)) {
if parent.has_layout() || parent.is_group_like() { // When the parent is either a group or a layout we only mark for reflow
// if the current transformation is not a move propagation.
// If it's a move propagation we don't need to reflow, the parent is already changed.
if (parent.has_layout() || parent.is_group_like()) && (is_resize || !is_propagate) {
entries.push_back(Modifier::reflow(parent.id)); entries.push_back(Modifier::reflow(parent.id));
} }
} }
@@ -360,7 +378,14 @@ pub fn propagate_modifiers(
) -> Vec<TransformEntry> { ) -> Vec<TransformEntry> {
let mut entries: VecDeque<_> = modifiers let mut entries: VecDeque<_> = modifiers
.iter() .iter()
.map(|entry| Modifier::Transform(entry.clone())) .map(|entry| {
// If we receibe a identity matrix we force a reflow
if math::identitish(&entry.transform) {
Modifier::Reflow(entry.id)
} else {
Modifier::Transform(entry.clone())
}
})
.collect(); .collect();
let mut modifiers = HashMap::<Uuid, Matrix>::new(); let mut modifiers = HashMap::<Uuid, Matrix>::new();
@@ -407,7 +432,7 @@ pub fn propagate_modifiers(
modifiers modifiers
.iter() .iter()
.map(|(key, val)| TransformEntry::new(*key, *val)) .map(|(key, val)| TransformEntry::from_input(*key, *val))
.collect() .collect()
} }

View File

@@ -1,4 +1,4 @@
use crate::math::{Bounds, Matrix}; use crate::math::{is_move_only_matrix, Bounds, Matrix};
use crate::shapes::{ConstraintH, ConstraintV}; use crate::shapes::{ConstraintH, ConstraintV};
pub fn calculate_resize( pub fn calculate_resize(
@@ -110,7 +110,7 @@ pub fn propagate_shape_constraints(
// can propagate as is // can propagate as is
if (ignore_constrainst if (ignore_constrainst
|| constraint_h == ConstraintH::Scale && constraint_v == ConstraintV::Scale) || constraint_h == ConstraintH::Scale && constraint_v == ConstraintV::Scale)
|| transform.is_translate() || is_move_only_matrix(&transform)
{ {
return transform; return transform;
} }

View File

@@ -623,7 +623,7 @@ pub fn reflow_flex_layout(
transform.post_concat(&Matrix::translate(delta_v)); transform.post_concat(&Matrix::translate(delta_v));
} }
result.push_back(Modifier::transform(child.id, transform)); result.push_back(Modifier::transform_propagate(child.id, transform));
shape_anchor = next_anchor( shape_anchor = next_anchor(
layout_data, layout_data,

View File

@@ -791,7 +791,7 @@ pub fn reflow_grid_layout(
transform.post_concat(&Matrix::translate(delta_v)); transform.post_concat(&Matrix::translate(delta_v));
} }
result.push_back(Modifier::transform(child.id, transform)); result.push_back(Modifier::transform_propagate(child.id, transform));
} }
if shape.is_layout_horizontal_auto() || shape.is_layout_vertical_auto() { if shape.is_layout_horizontal_auto() || shape.is_layout_vertical_auto() {

View File

@@ -12,8 +12,8 @@ pub enum Modifier {
} }
impl Modifier { impl Modifier {
pub fn transform(id: Uuid, transform: Matrix) -> Self { pub fn transform_propagate(id: Uuid, transform: Matrix) -> Self {
Modifier::Transform(TransformEntry::new(id, transform)) Modifier::Transform(TransformEntry::from_propagate(id, transform))
} }
pub fn parent(id: Uuid, transform: Matrix) -> Self { pub fn parent(id: Uuid, transform: Matrix) -> Self {
Modifier::Transform(TransformEntry::parent(id, transform)) Modifier::Transform(TransformEntry::parent(id, transform))
@@ -23,19 +23,35 @@ impl Modifier {
} }
} }
#[derive(PartialEq, Debug, Clone)]
pub enum TransformEntrySource {
Input,
Propagate,
}
#[derive(PartialEq, Debug, Clone)] #[derive(PartialEq, Debug, Clone)]
#[repr(C)] #[repr(C)]
pub struct TransformEntry { pub struct TransformEntry {
pub id: Uuid, pub id: Uuid,
pub transform: Matrix, pub transform: Matrix,
pub source: TransformEntrySource,
pub propagate: bool, pub propagate: bool,
} }
impl TransformEntry { impl TransformEntry {
pub fn new(id: Uuid, transform: Matrix) -> Self { pub fn from_input(id: Uuid, transform: Matrix) -> Self {
TransformEntry { TransformEntry {
id, id,
transform, transform,
source: TransformEntrySource::Input,
propagate: true,
}
}
pub fn from_propagate(id: Uuid, transform: Matrix) -> Self {
TransformEntry {
id,
transform,
source: TransformEntrySource::Propagate,
propagate: true, propagate: true,
} }
} }
@@ -43,6 +59,7 @@ impl TransformEntry {
TransformEntry { TransformEntry {
id, id,
transform, transform,
source: TransformEntrySource::Propagate,
propagate: false, propagate: false,
} }
} }
@@ -70,7 +87,7 @@ impl SerializableResult for TransformEntry {
0.0, 0.0,
1.0, 1.0,
); );
TransformEntry::new(id, transform) TransformEntry::from_input(id, transform)
} }
fn as_bytes(&self) -> Self::BytesType { fn as_bytes(&self) -> Self::BytesType {
@@ -176,7 +193,7 @@ mod tests {
#[test] #[test]
fn test_serialization() { fn test_serialization() {
let entry = TransformEntry::new( let entry = TransformEntry::from_input(
Uuid::new_v4(), Uuid::new_v4(),
Matrix::new_all(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 0.0, 0.0, 1.0), Matrix::new_all(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 0.0, 0.0, 1.0),
); );