mirror of
https://github.com/penpot/penpot.git
synced 2025-12-12 06:24:17 +01:00
✨ Improved performance in modifiers
This commit is contained in:
@@ -767,6 +767,11 @@
|
||||
:always
|
||||
(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
|
||||
[shape operation]
|
||||
(let [type (dm/get-prop operation :type)]
|
||||
@@ -793,7 +798,7 @@
|
||||
|
||||
:remove-children
|
||||
(let [value (dm/get-prop operation :value)]
|
||||
(update shape :shapes remove-children value))
|
||||
(update shape :shapes remove-children-set value))
|
||||
|
||||
:scale-content
|
||||
(let [value (dm/get-prop operation :value)]
|
||||
@@ -810,11 +815,6 @@
|
||||
(defn apply-structure-modifiers
|
||||
"Apply structure changes to a shape"
|
||||
[shape modifiers]
|
||||
(let [remove-children
|
||||
(fn [shapes children-to-remove]
|
||||
(let [remove? (set children-to-remove)]
|
||||
(d/removev remove? shapes)))]
|
||||
|
||||
(as-> shape $
|
||||
(reduce apply-modifier $ (dm/get-prop modifiers :structure-parent))
|
||||
(reduce apply-modifier $ (dm/get-prop modifiers :structure-child)))))
|
||||
(as-> shape $
|
||||
(reduce apply-modifier $ (dm/get-prop modifiers :structure-parent))
|
||||
(reduce apply-modifier $ (dm/get-prop modifiers :structure-child))))
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
[app.common.types.component :as ctk]
|
||||
[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.attrs :refer [editable-attrs]]
|
||||
|
||||
@@ -51,6 +51,13 @@ pub fn identitish(m: &Matrix) -> bool {
|
||||
&& 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)]
|
||||
pub struct Bounds {
|
||||
pub nw: Point,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use skia_safe::{self as skia};
|
||||
|
||||
use indexmap::IndexSet;
|
||||
|
||||
use crate::uuid::Uuid;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::{OnceCell, RefCell};
|
||||
@@ -342,9 +344,14 @@ impl Shape {
|
||||
matches!(
|
||||
self.shape_type,
|
||||
Type::Frame(Frame {
|
||||
layout: Some(layouts::Layout::FlexLayout(_, FlexData {
|
||||
direction: layouts::FlexDirection::RowReverse | layouts::FlexDirection::ColumnReverse, ..
|
||||
})),
|
||||
layout: Some(layouts::Layout::FlexLayout(
|
||||
_,
|
||||
FlexData {
|
||||
direction: layouts::FlexDirection::RowReverse
|
||||
| layouts::FlexDirection::ColumnReverse,
|
||||
..
|
||||
}
|
||||
)),
|
||||
..
|
||||
})
|
||||
)
|
||||
@@ -1279,13 +1286,14 @@ impl Shape {
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
for st in structure {
|
||||
match st.entry_type {
|
||||
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 => {
|
||||
to_remove.insert(&st.id);
|
||||
|
||||
@@ -6,11 +6,12 @@ mod flex_layout;
|
||||
pub mod common;
|
||||
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 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::uuid::Uuid;
|
||||
@@ -75,7 +76,7 @@ fn propagate_children(
|
||||
child.ignore_constraints,
|
||||
);
|
||||
|
||||
result.push_back(Modifier::transform(*child_id, transform));
|
||||
result.push_back(Modifier::transform_propagate(*child_id, transform));
|
||||
}
|
||||
|
||||
result
|
||||
@@ -182,33 +183,43 @@ fn propagate_transform(
|
||||
|
||||
let mut transform = entry.transform;
|
||||
|
||||
// NOTA: No puedo utilizar un clone porque entonces estaríamos
|
||||
// perdiendo la referencia al contenido del layout...
|
||||
if let Type::Text(text_content) = &mut shape.shape_type.clone() {
|
||||
if text_content.needs_update_layout() {
|
||||
text_content.update_layout(shape.selrect);
|
||||
}
|
||||
match text_content.grow_type() {
|
||||
GrowType::AutoHeight => {
|
||||
let height = text_content.size.height;
|
||||
let resize_transform = math::resize_matrix(
|
||||
&shape_bounds_after,
|
||||
&shape_bounds_after,
|
||||
shape_bounds_after.width(),
|
||||
height,
|
||||
);
|
||||
shape_bounds_after = shape_bounds_after.transform(&resize_transform);
|
||||
transform.post_concat(&resize_transform);
|
||||
// Only check the text layout when the width/height changes
|
||||
if !is_close_to(shape_bounds_before.width(), shape_bounds_after.width())
|
||||
|| !is_close_to(shape_bounds_before.height(), shape_bounds_after.height())
|
||||
{
|
||||
if let Type::Text(text_content) = &mut shape.shape_type.clone() {
|
||||
match text_content.grow_type() {
|
||||
GrowType::AutoHeight => {
|
||||
if text_content.needs_update_layout() {
|
||||
text_content.update_layout(shape.selrect);
|
||||
}
|
||||
let height = text_content.size.height;
|
||||
let resize_transform = math::resize_matrix(
|
||||
&shape_bounds_after,
|
||||
&shape_bounds_after,
|
||||
shape_bounds_after.width(),
|
||||
height,
|
||||
);
|
||||
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);
|
||||
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));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -360,7 +378,14 @@ pub fn propagate_modifiers(
|
||||
) -> Vec<TransformEntry> {
|
||||
let mut entries: VecDeque<_> = modifiers
|
||||
.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();
|
||||
|
||||
let mut modifiers = HashMap::<Uuid, Matrix>::new();
|
||||
@@ -407,7 +432,7 @@ pub fn propagate_modifiers(
|
||||
|
||||
modifiers
|
||||
.iter()
|
||||
.map(|(key, val)| TransformEntry::new(*key, *val))
|
||||
.map(|(key, val)| TransformEntry::from_input(*key, *val))
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::math::{Bounds, Matrix};
|
||||
use crate::math::{is_move_only_matrix, Bounds, Matrix};
|
||||
use crate::shapes::{ConstraintH, ConstraintV};
|
||||
|
||||
pub fn calculate_resize(
|
||||
@@ -110,7 +110,7 @@ pub fn propagate_shape_constraints(
|
||||
// can propagate as is
|
||||
if (ignore_constrainst
|
||||
|| constraint_h == ConstraintH::Scale && constraint_v == ConstraintV::Scale)
|
||||
|| transform.is_translate()
|
||||
|| is_move_only_matrix(&transform)
|
||||
{
|
||||
return transform;
|
||||
}
|
||||
|
||||
@@ -623,7 +623,7 @@ pub fn reflow_flex_layout(
|
||||
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(
|
||||
layout_data,
|
||||
|
||||
@@ -791,7 +791,7 @@ pub fn reflow_grid_layout(
|
||||
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() {
|
||||
|
||||
@@ -12,8 +12,8 @@ pub enum Modifier {
|
||||
}
|
||||
|
||||
impl Modifier {
|
||||
pub fn transform(id: Uuid, transform: Matrix) -> Self {
|
||||
Modifier::Transform(TransformEntry::new(id, transform))
|
||||
pub fn transform_propagate(id: Uuid, transform: Matrix) -> Self {
|
||||
Modifier::Transform(TransformEntry::from_propagate(id, transform))
|
||||
}
|
||||
pub fn parent(id: Uuid, transform: Matrix) -> Self {
|
||||
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)]
|
||||
#[repr(C)]
|
||||
pub struct TransformEntry {
|
||||
pub id: Uuid,
|
||||
pub transform: Matrix,
|
||||
pub source: TransformEntrySource,
|
||||
pub propagate: bool,
|
||||
}
|
||||
|
||||
impl TransformEntry {
|
||||
pub fn new(id: Uuid, transform: Matrix) -> Self {
|
||||
pub fn from_input(id: Uuid, transform: Matrix) -> Self {
|
||||
TransformEntry {
|
||||
id,
|
||||
transform,
|
||||
source: TransformEntrySource::Input,
|
||||
propagate: true,
|
||||
}
|
||||
}
|
||||
pub fn from_propagate(id: Uuid, transform: Matrix) -> Self {
|
||||
TransformEntry {
|
||||
id,
|
||||
transform,
|
||||
source: TransformEntrySource::Propagate,
|
||||
propagate: true,
|
||||
}
|
||||
}
|
||||
@@ -43,6 +59,7 @@ impl TransformEntry {
|
||||
TransformEntry {
|
||||
id,
|
||||
transform,
|
||||
source: TransformEntrySource::Propagate,
|
||||
propagate: false,
|
||||
}
|
||||
}
|
||||
@@ -70,7 +87,7 @@ impl SerializableResult for TransformEntry {
|
||||
0.0,
|
||||
1.0,
|
||||
);
|
||||
TransformEntry::new(id, transform)
|
||||
TransformEntry::from_input(id, transform)
|
||||
}
|
||||
|
||||
fn as_bytes(&self) -> Self::BytesType {
|
||||
@@ -176,7 +193,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
let entry = TransformEntry::new(
|
||||
let entry = TransformEntry::from_input(
|
||||
Uuid::new_v4(),
|
||||
Matrix::new_all(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 0.0, 0.0, 1.0),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user