mirror of
https://github.com/penpot/penpot.git
synced 2025-05-24 17:36:11 +02:00
✨ Reparent modifiers
This commit is contained in:
parent
f3d13005b2
commit
daf048e258
10 changed files with 283 additions and 52 deletions
|
@ -167,7 +167,7 @@
|
||||||
ptk/EffectEvent
|
ptk/EffectEvent
|
||||||
(effect [_ state _]
|
(effect [_ state _]
|
||||||
(when (features/active-feature? state "render-wasm/v1")
|
(when (features/active-feature? state "render-wasm/v1")
|
||||||
(wasm.api/set-modifiers nil)))
|
(wasm.api/clean-modifiers)))
|
||||||
|
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
|
@ -417,6 +417,48 @@
|
||||||
modifiers (calculate-modifiers state ignore-constraints ignore-snap-pixel modif-tree page-id params)]
|
modifiers (calculate-modifiers state ignore-constraints ignore-snap-pixel modif-tree page-id params)]
|
||||||
(assoc state :workspace-modifiers modifiers))))))
|
(assoc state :workspace-modifiers modifiers))))))
|
||||||
|
|
||||||
|
(defn- parse-structure-modifiers
|
||||||
|
[modif-tree]
|
||||||
|
(into
|
||||||
|
[]
|
||||||
|
(mapcat
|
||||||
|
(fn [[parent-id data]]
|
||||||
|
(when (ctm/has-structure? (:modifiers data))
|
||||||
|
(->> data
|
||||||
|
:modifiers
|
||||||
|
:structure-parent
|
||||||
|
(mapcat
|
||||||
|
(fn [modifier]
|
||||||
|
(case (:type modifier)
|
||||||
|
:remove-children
|
||||||
|
(->> (:value modifier)
|
||||||
|
(map (fn [child-id]
|
||||||
|
{:type :remove-children
|
||||||
|
:parent parent-id
|
||||||
|
:id child-id
|
||||||
|
:index 0})))
|
||||||
|
|
||||||
|
:add-children
|
||||||
|
(->> (:value modifier)
|
||||||
|
(map (fn [child-id]
|
||||||
|
{:type :add-children
|
||||||
|
:parent parent-id
|
||||||
|
:id child-id
|
||||||
|
:index (:index modifier)})))
|
||||||
|
nil)))))))
|
||||||
|
modif-tree))
|
||||||
|
|
||||||
|
(defn- parse-geometry-modifiers
|
||||||
|
[modif-tree]
|
||||||
|
(into
|
||||||
|
[]
|
||||||
|
(keep
|
||||||
|
(fn [[id data]]
|
||||||
|
(when (ctm/has-geometry? (:modifiers data))
|
||||||
|
{:id id
|
||||||
|
:transform (ctm/modifiers->transform (:modifiers data))})))
|
||||||
|
modif-tree))
|
||||||
|
|
||||||
(defn set-wasm-modifiers
|
(defn set-wasm-modifiers
|
||||||
([modif-tree]
|
([modif-tree]
|
||||||
(set-wasm-modifiers modif-tree false))
|
(set-wasm-modifiers modif-tree false))
|
||||||
|
@ -431,15 +473,13 @@
|
||||||
(ptk/reify ::set-wasm-modifiers
|
(ptk/reify ::set-wasm-modifiers
|
||||||
ptk/EffectEvent
|
ptk/EffectEvent
|
||||||
(effect [_ _ _]
|
(effect [_ _ _]
|
||||||
(let [entries
|
(wasm.api/clean-modifiers)
|
||||||
(->> modif-tree
|
(let [structure-entries (parse-structure-modifiers modif-tree)]
|
||||||
(mapv (fn [[id data]]
|
(wasm.api/set-structure-modifiers structure-entries)
|
||||||
{:id id
|
(let [geometry-entries (parse-geometry-modifiers modif-tree)
|
||||||
:transform (ctm/modifiers->transform (:modifiers data))})))
|
|
||||||
|
|
||||||
modifiers-new
|
modifiers-new
|
||||||
(wasm.api/propagate-modifiers entries)]
|
(wasm.api/propagate-modifiers geometry-entries)]
|
||||||
(wasm.api/set-modifiers modifiers-new))))))
|
(wasm.api/set-modifiers modifiers-new)))))))
|
||||||
|
|
||||||
(defn set-selrect-transform
|
(defn set-selrect-transform
|
||||||
[modifiers]
|
[modifiers]
|
||||||
|
@ -654,4 +694,3 @@
|
||||||
(if undo-transation?
|
(if undo-transation?
|
||||||
(rx/of (dwu/commit-undo-transaction undo-id))
|
(rx/of (dwu/commit-undo-transaction undo-id))
|
||||||
(rx/empty))))))))
|
(rx/empty))))))))
|
||||||
|
|
||||||
|
|
|
@ -821,6 +821,22 @@
|
||||||
(clear-drawing-cache)
|
(clear-drawing-cache)
|
||||||
(request-render "set-objects")))))))
|
(request-render "set-objects")))))))
|
||||||
|
|
||||||
|
(defn set-structure-modifiers
|
||||||
|
[entries]
|
||||||
|
(when-not (empty? entries)
|
||||||
|
(let [offset (mem/alloc-bytes-32 (mem/get-list-size entries 40))
|
||||||
|
heapu32 (mem/get-heap-u32)]
|
||||||
|
(loop [entries (seq entries)
|
||||||
|
current-offset offset]
|
||||||
|
(when-not (empty? entries)
|
||||||
|
(let [{:keys [type parent id index] :as entry} (first entries)]
|
||||||
|
(sr/heapu32-set-u32 (sr/translate-structure-modifier-type type) heapu32 (+ current-offset 0))
|
||||||
|
(sr/heapu32-set-u32 (or index 0) heapu32 (+ current-offset 1))
|
||||||
|
(sr/heapu32-set-uuid parent heapu32 (+ current-offset 2))
|
||||||
|
(sr/heapu32-set-uuid id heapu32 (+ current-offset 6))
|
||||||
|
(recur (rest entries) (+ current-offset 10)))))
|
||||||
|
(h/call wasm/internal-module "_set_structure_modifiers"))))
|
||||||
|
|
||||||
(defn propagate-modifiers
|
(defn propagate-modifiers
|
||||||
[entries]
|
[entries]
|
||||||
(let [offset (mem/alloc-bytes-32 (modifier-get-entries-size entries))
|
(let [offset (mem/alloc-bytes-32 (modifier-get-entries-size entries))
|
||||||
|
@ -852,11 +868,13 @@
|
||||||
(h/call wasm/internal-module "_set_canvas_background" rgba)
|
(h/call wasm/internal-module "_set_canvas_background" rgba)
|
||||||
(request-render "set-canvas-background")))
|
(request-render "set-canvas-background")))
|
||||||
|
|
||||||
|
(defn clean-modifiers
|
||||||
|
[]
|
||||||
|
(h/call wasm/internal-module "_clean_modifiers"))
|
||||||
|
|
||||||
(defn set-modifiers
|
(defn set-modifiers
|
||||||
[modifiers]
|
[modifiers]
|
||||||
(if (empty? modifiers)
|
(when-not (empty? modifiers)
|
||||||
(h/call wasm/internal-module "_clean_modifiers")
|
|
||||||
|
|
||||||
(let [offset (mem/alloc-bytes-32 (* MODIFIER-ENTRY-SIZE (count modifiers)))
|
(let [offset (mem/alloc-bytes-32 (* MODIFIER-ENTRY-SIZE (count modifiers)))
|
||||||
heapu32 (mem/get-heap-u32)
|
heapu32 (mem/get-heap-u32)
|
||||||
heapf32 (mem/get-heap-f32)]
|
heapf32 (mem/get-heap-f32)]
|
||||||
|
|
|
@ -44,6 +44,10 @@
|
||||||
(aset u32-arr 3 (aget buffer 3))
|
(aset u32-arr 3 (aget buffer 3))
|
||||||
(js/Uint8Array. (.-buffer u32-arr))))
|
(js/Uint8Array. (.-buffer u32-arr))))
|
||||||
|
|
||||||
|
(defn heapu32-set-u32
|
||||||
|
[value heap offset]
|
||||||
|
(aset heap offset value))
|
||||||
|
|
||||||
(defn heapu32-set-uuid
|
(defn heapu32-set-uuid
|
||||||
[id heap offset]
|
[id heap offset]
|
||||||
(let [buffer (uuid/get-u32 id)]
|
(let [buffer (uuid/get-u32 id)]
|
||||||
|
@ -262,3 +266,9 @@
|
||||||
:inner-shadow 1
|
:inner-shadow 1
|
||||||
0))
|
0))
|
||||||
|
|
||||||
|
|
||||||
|
(defn translate-structure-modifier-type
|
||||||
|
[type]
|
||||||
|
(case type
|
||||||
|
:remove-children 1
|
||||||
|
:add-children 2))
|
||||||
|
|
|
@ -16,7 +16,7 @@ mod wapi;
|
||||||
mod wasm;
|
mod wasm;
|
||||||
|
|
||||||
use crate::mem::SerializableResult;
|
use crate::mem::SerializableResult;
|
||||||
use crate::shapes::{BoolType, ConstraintH, ConstraintV, TransformEntry, Type};
|
use crate::shapes::{BoolType, ConstraintH, ConstraintV, StructureEntry, TransformEntry, Type};
|
||||||
use crate::utils::uuid_from_u32_quartet;
|
use crate::utils::uuid_from_u32_quartet;
|
||||||
use crate::uuid::Uuid;
|
use crate::uuid::Uuid;
|
||||||
use indexmap::IndexSet;
|
use indexmap::IndexSet;
|
||||||
|
@ -607,9 +607,31 @@ pub extern "C" fn propagate_modifiers() -> *mut u8 {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn set_structure_modifiers() {
|
||||||
|
let bytes = mem::bytes();
|
||||||
|
|
||||||
|
let entries: Vec<_> = bytes
|
||||||
|
.chunks(40)
|
||||||
|
.map(|data| StructureEntry::from_bytes(data.try_into().unwrap()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
with_state!(state, {
|
||||||
|
for entry in entries {
|
||||||
|
if !state.structure.contains_key(&entry.parent) {
|
||||||
|
state.structure.insert(entry.parent, Vec::new());
|
||||||
|
}
|
||||||
|
state.structure.get_mut(&entry.parent).unwrap().push(entry);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mem::free_bytes();
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn clean_modifiers() {
|
pub extern "C" fn clean_modifiers() {
|
||||||
with_state!(state, {
|
with_state!(state, {
|
||||||
|
state.structure.clear();
|
||||||
state.modifiers.clear();
|
state.modifiers.clear();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ mod surfaces;
|
||||||
mod text;
|
mod text;
|
||||||
mod tiles;
|
mod tiles;
|
||||||
|
|
||||||
use crate::shapes::{Corners, Shape, Type};
|
use crate::shapes::{modified_children_ids, Corners, Shape, StructureEntry, Type};
|
||||||
use gpu_state::GpuState;
|
use gpu_state::GpuState;
|
||||||
use options::RenderOptions;
|
use options::RenderOptions;
|
||||||
use surfaces::{SurfaceId, Surfaces};
|
use surfaces::{SurfaceId, Surfaces};
|
||||||
|
@ -430,6 +430,7 @@ impl RenderState {
|
||||||
&mut self,
|
&mut self,
|
||||||
tree: &mut HashMap<Uuid, Shape>,
|
tree: &mut HashMap<Uuid, Shape>,
|
||||||
modifiers: &HashMap<Uuid, Matrix>,
|
modifiers: &HashMap<Uuid, Matrix>,
|
||||||
|
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||||
timestamp: i32,
|
timestamp: i32,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
if self.render_in_progress {
|
if self.render_in_progress {
|
||||||
|
@ -487,7 +488,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, timestamp)?;
|
self.process_animation_frame(tree, modifiers, structure, timestamp)?;
|
||||||
performance::end_measure!("start_render_loop");
|
performance::end_measure!("start_render_loop");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -496,11 +497,12 @@ impl RenderState {
|
||||||
&mut self,
|
&mut self,
|
||||||
tree: &mut HashMap<Uuid, Shape>,
|
tree: &mut HashMap<Uuid, Shape>,
|
||||||
modifiers: &HashMap<Uuid, Matrix>,
|
modifiers: &HashMap<Uuid, Matrix>,
|
||||||
|
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||||
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 {
|
||||||
self.render_shape_tree(tree, modifiers, timestamp)?;
|
self.render_shape_tree(tree, modifiers, structure, timestamp)?;
|
||||||
self.flush();
|
self.flush();
|
||||||
|
|
||||||
if self.render_in_progress {
|
if self.render_in_progress {
|
||||||
|
@ -594,11 +596,13 @@ impl RenderState {
|
||||||
&mut self,
|
&mut self,
|
||||||
tree: &mut HashMap<Uuid, Shape>,
|
tree: &mut HashMap<Uuid, Shape>,
|
||||||
modifiers: &HashMap<Uuid, Matrix>,
|
modifiers: &HashMap<Uuid, Matrix>,
|
||||||
|
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||||
timestamp: i32,
|
timestamp: i32,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
if !self.render_in_progress {
|
if !self.render_in_progress {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let scale = self.get_scale();
|
let scale = self.get_scale();
|
||||||
let mut should_stop = false;
|
let mut should_stop = false;
|
||||||
|
|
||||||
|
@ -631,6 +635,7 @@ impl RenderState {
|
||||||
visited_mask,
|
visited_mask,
|
||||||
mask,
|
mask,
|
||||||
} = node_render_state;
|
} = node_render_state;
|
||||||
|
|
||||||
is_empty = false;
|
is_empty = false;
|
||||||
let element = tree.get_mut(&node_id).ok_or(
|
let element = tree.get_mut(&node_id).ok_or(
|
||||||
"Error: Element with root_id {node_render_state.id} not found in the tree."
|
"Error: Element with root_id {node_render_state.id} not found in the tree."
|
||||||
|
@ -709,7 +714,8 @@ impl RenderState {
|
||||||
let children_clip_bounds = node_render_state
|
let children_clip_bounds = node_render_state
|
||||||
.get_children_clip_bounds(element, modifiers.get(&element.id));
|
.get_children_clip_bounds(element, modifiers.get(&element.id));
|
||||||
|
|
||||||
let mut children_ids = element.children_ids();
|
let mut children_ids =
|
||||||
|
modified_children_ids(element, structure.get(&element.id));
|
||||||
|
|
||||||
// Z-index ordering on Layouts
|
// Z-index ordering on Layouts
|
||||||
if element.has_layout() {
|
if element.has_layout() {
|
||||||
|
@ -757,31 +763,28 @@ impl RenderState {
|
||||||
.canvas(SurfaceId::Current)
|
.canvas(SurfaceId::Current)
|
||||||
.clear(self.background_color);
|
.clear(self.background_color);
|
||||||
|
|
||||||
|
let Some(root) = tree.get(&Uuid::nil()) else {
|
||||||
|
return Err(String::from("Root shape not found"));
|
||||||
|
};
|
||||||
|
let root_ids = modified_children_ids(&root, structure.get(&root.id));
|
||||||
|
|
||||||
// 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
|
||||||
if let Some(next_tile_with_distance) = self.pending_tiles.pop() {
|
if let Some(next_tile_with_distance) = self.pending_tiles.pop() {
|
||||||
let (x, y, _) = next_tile_with_distance;
|
let (x, y, _) = next_tile_with_distance;
|
||||||
let next_tile = (x, y);
|
let next_tile = (x, y);
|
||||||
self.update_render_context(next_tile);
|
self.update_render_context(next_tile);
|
||||||
|
|
||||||
if !self.surfaces.has_cached_tile_surface(next_tile) {
|
if !self.surfaces.has_cached_tile_surface(next_tile) {
|
||||||
if let Some(ids) = self.tiles.get_shapes_at(next_tile) {
|
if let Some(ids) = self.tiles.get_shapes_at(next_tile) {
|
||||||
// We only need first level shapes
|
// We only need first level shapes
|
||||||
let mut valid_ids: Vec<Uuid> = ids
|
let mut valid_ids: Vec<Uuid> = ids
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|id| {
|
.filter_map(|id| root_ids.get(id).map(|_| *id))
|
||||||
tree.get(id)
|
|
||||||
.filter(|element| element.parent_id == Some(Uuid::nil()))
|
|
||||||
.map(|_| *id)
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// These shapes for the tile should be ordered as they are in the parent node
|
// These shapes for the tile should be ordered as they are in the parent node
|
||||||
if let Some(root) = tree.get(&Uuid::nil()) {
|
valid_ids.sort_by_key(|id| root_ids.get_index_of(id));
|
||||||
let root_ids = &root.children_ids();
|
|
||||||
valid_ids.sort_by_key(|id| {
|
|
||||||
root_ids.iter().rev().position(|root_id| root_id == id)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.pending_nodes.extend(valid_ids.into_iter().map(|id| {
|
self.pending_nodes.extend(valid_ids.into_iter().map(|id| {
|
||||||
NodeRenderState {
|
NodeRenderState {
|
||||||
|
@ -843,6 +846,7 @@ impl RenderState {
|
||||||
&mut self,
|
&mut self,
|
||||||
tree: &mut HashMap<Uuid, Shape>,
|
tree: &mut HashMap<Uuid, Shape>,
|
||||||
modifiers: &HashMap<Uuid, Matrix>,
|
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();
|
||||||
|
@ -857,7 +861,9 @@ impl RenderState {
|
||||||
}
|
}
|
||||||
self.update_tile_for(&shape);
|
self.update_tile_for(&shape);
|
||||||
}
|
}
|
||||||
for child_id in shape.children_ids().iter() {
|
|
||||||
|
let children = modified_children_ids(&shape, structure.get(&shape.id));
|
||||||
|
for child_id in children.iter() {
|
||||||
nodes.push(*child_id);
|
nodes.push(*child_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use skia_safe::{self as skia};
|
||||||
|
|
||||||
use crate::render::BlendMode;
|
use crate::render::BlendMode;
|
||||||
use crate::uuid::Uuid;
|
use crate::uuid::Uuid;
|
||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
mod blurs;
|
mod blurs;
|
||||||
mod bools;
|
mod bools;
|
||||||
|
@ -659,9 +659,9 @@ impl Shape {
|
||||||
self.children.first()
|
self.children.first()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn children_ids(&self) -> Vec<Uuid> {
|
pub fn children_ids(&self) -> IndexSet<Uuid> {
|
||||||
if let Type::Bool(_) = self.shape_type {
|
if let Type::Bool(_) = self.shape_type {
|
||||||
vec![]
|
IndexSet::<Uuid>::new()
|
||||||
} else if let Type::Group(group) = self.shape_type {
|
} else if let Type::Group(group) = self.shape_type {
|
||||||
if group.masked {
|
if group.masked {
|
||||||
self.children.iter().skip(1).cloned().collect()
|
self.children.iter().skip(1).cloned().collect()
|
||||||
|
@ -858,6 +858,40 @@ impl Shape {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Returns the list of children taking into account the structure modifiers
|
||||||
|
*/
|
||||||
|
pub fn modified_children_ids(
|
||||||
|
element: &Shape,
|
||||||
|
structure: Option<&Vec<StructureEntry>>,
|
||||||
|
) -> IndexSet<Uuid> {
|
||||||
|
if let Some(structure) = structure {
|
||||||
|
let mut result: Vec<Uuid> = Vec::from_iter(element.children_ids().iter().map(|id| *id));
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
StructureEntryType::RemoveChild => {
|
||||||
|
to_remove.insert(&st.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret: IndexSet<Uuid> = result
|
||||||
|
.iter()
|
||||||
|
.filter(|id| !to_remove.contains(id))
|
||||||
|
.map(|id| *id)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
ret
|
||||||
|
} else {
|
||||||
|
element.children_ids()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -8,7 +8,8 @@ use common::GetBounds;
|
||||||
|
|
||||||
use crate::math::{identitish, Bounds, Matrix, Point};
|
use crate::math::{identitish, Bounds, Matrix, Point};
|
||||||
use crate::shapes::{
|
use crate::shapes::{
|
||||||
ConstraintH, ConstraintV, Frame, Group, Layout, Modifier, Shape, TransformEntry, Type,
|
modified_children_ids, ConstraintH, ConstraintV, Frame, Group, Layout, Modifier, Shape,
|
||||||
|
StructureEntry, TransformEntry, Type,
|
||||||
};
|
};
|
||||||
use crate::state::State;
|
use crate::state::State;
|
||||||
use crate::uuid::Uuid;
|
use crate::uuid::Uuid;
|
||||||
|
@ -20,14 +21,17 @@ 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>>,
|
||||||
) -> VecDeque<Modifier> {
|
) -> VecDeque<Modifier> {
|
||||||
if shape.children.len() == 0 || identitish(transform) {
|
let children_ids = modified_children_ids(shape, structure.get(&shape.id));
|
||||||
|
|
||||||
|
if children_ids.len() == 0 || identitish(transform) {
|
||||||
return VecDeque::new();
|
return VecDeque::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut result = VecDeque::new();
|
let mut result = VecDeque::new();
|
||||||
|
|
||||||
for child_id in shape.children.iter() {
|
for child_id in children_ids.iter() {
|
||||||
let Some(child) = shapes.get(child_id) else {
|
let Some(child) = shapes.get(child_id) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
@ -81,10 +85,13 @@ fn calculate_group_bounds(
|
||||||
shape: &Shape,
|
shape: &Shape,
|
||||||
shapes: &HashMap<Uuid, Shape>,
|
shapes: &HashMap<Uuid, Shape>,
|
||||||
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.children.iter() {
|
|
||||||
|
let children_ids = modified_children_ids(shape, structure.get(&shape.id));
|
||||||
|
for child_id in children_ids.iter() {
|
||||||
let Some(child) = shapes.get(child_id) else {
|
let Some(child) = shapes.get(child_id) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
@ -103,6 +110,13 @@ pub fn propagate_modifiers(state: &State, modifiers: Vec<TransformEntry>) -> Vec
|
||||||
.iter()
|
.iter()
|
||||||
.map(|entry| Modifier::Transform(entry.clone()))
|
.map(|entry| Modifier::Transform(entry.clone()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
for (id, _) in &state.structure {
|
||||||
|
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();
|
||||||
|
|
||||||
|
@ -133,6 +147,7 @@ pub fn propagate_modifiers(state: &State, modifiers: Vec<TransformEntry>) -> Vec
|
||||||
&shape_bounds_after,
|
&shape_bounds_after,
|
||||||
entry.transform,
|
entry.transform,
|
||||||
&bounds,
|
&bounds,
|
||||||
|
&state.structure,
|
||||||
);
|
);
|
||||||
|
|
||||||
entries.append(&mut children);
|
entries.append(&mut children);
|
||||||
|
@ -191,7 +206,9 @@ pub fn propagate_modifiers(state: &State, modifiers: Vec<TransformEntry>) -> Vec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Type::Group(Group { masked: true }) => {
|
Type::Group(Group { masked: true }) => {
|
||||||
if let Some(child) = shapes.get(&shape.children[0]) {
|
let children_ids =
|
||||||
|
modified_children_ids(shape, state.structure.get(&shape.id));
|
||||||
|
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);
|
||||||
reflow_parent = true;
|
reflow_parent = true;
|
||||||
|
@ -199,7 +216,7 @@ pub fn propagate_modifiers(state: &State, modifiers: Vec<TransformEntry>) -> Vec
|
||||||
}
|
}
|
||||||
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;
|
||||||
|
@ -210,7 +227,7 @@ pub fn propagate_modifiers(state: &State, modifiers: Vec<TransformEntry>) -> Vec
|
||||||
// new path... impossible right now. I'm going to use for the moment the group
|
// new path... impossible right now. I'm going to use for the moment the group
|
||||||
// calculation
|
// calculation
|
||||||
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;
|
||||||
|
@ -250,6 +267,7 @@ pub fn propagate_modifiers(state: &State, modifiers: Vec<TransformEntry>) -> Vec
|
||||||
flex_data,
|
flex_data,
|
||||||
shapes,
|
shapes,
|
||||||
&mut bounds,
|
&mut bounds,
|
||||||
|
&state.structure,
|
||||||
);
|
);
|
||||||
entries.append(&mut children);
|
entries.append(&mut children);
|
||||||
}
|
}
|
||||||
|
@ -313,7 +331,8 @@ mod tests {
|
||||||
&bounds_before,
|
&bounds_before,
|
||||||
&bounds_after,
|
&bounds_after,
|
||||||
transform,
|
transform,
|
||||||
&HashMap::<Uuid, Bounds>::new(),
|
&HashMap::new(),
|
||||||
|
&HashMap::new(),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(result.len(), 1);
|
assert_eq!(result.len(), 1);
|
||||||
|
@ -342,7 +361,7 @@ mod tests {
|
||||||
shapes.insert(parent_id, parent.clone());
|
shapes.insert(parent_id, parent.clone());
|
||||||
|
|
||||||
let bounds =
|
let bounds =
|
||||||
calculate_group_bounds(&parent, &shapes, &HashMap::<Uuid, Bounds>::new()).unwrap();
|
calculate_group_bounds(&parent, &shapes, &HashMap::new(), &HashMap::new()).unwrap();
|
||||||
|
|
||||||
assert_eq!(bounds.width(), 3.0);
|
assert_eq!(bounds.width(), 3.0);
|
||||||
assert_eq!(bounds.height(), 3.0);
|
assert_eq!(bounds.height(), 3.0);
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
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,
|
modified_children_ids, AlignContent, AlignItems, AlignSelf, FlexData, JustifyContent,
|
||||||
Modifier, Shape,
|
LayoutData, LayoutItem, Modifier, Shape, StructureEntry,
|
||||||
};
|
};
|
||||||
use crate::uuid::Uuid;
|
use crate::uuid::Uuid;
|
||||||
|
|
||||||
|
@ -180,10 +180,11 @@ fn initialize_tracks(
|
||||||
flex_data: &FlexData,
|
flex_data: &FlexData,
|
||||||
shapes: &HashMap<Uuid, Shape>,
|
shapes: &HashMap<Uuid, Shape>,
|
||||||
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.children.clone();
|
let mut children = modified_children_ids(shape, structure.get(&shape.id));
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
|
|
||||||
if !flex_data.is_reverse() {
|
if !flex_data.is_reverse() {
|
||||||
|
@ -421,6 +422,7 @@ fn calculate_track_data(
|
||||||
layout_bounds: &Bounds,
|
layout_bounds: &Bounds,
|
||||||
shapes: &HashMap<Uuid, Shape>,
|
shapes: &HashMap<Uuid, Shape>,
|
||||||
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(
|
||||||
|
@ -430,6 +432,7 @@ fn calculate_track_data(
|
||||||
flex_data,
|
flex_data,
|
||||||
shapes,
|
shapes,
|
||||||
bounds,
|
bounds,
|
||||||
|
structure,
|
||||||
);
|
);
|
||||||
|
|
||||||
if !layout_axis.is_auto_main {
|
if !layout_axis.is_auto_main {
|
||||||
|
@ -550,11 +553,20 @@ pub fn reflow_flex_layout(
|
||||||
flex_data: &FlexData,
|
flex_data: &FlexData,
|
||||||
shapes: &HashMap<Uuid, Shape>,
|
shapes: &HashMap<Uuid, Shape>,
|
||||||
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(shape, layout_data, flex_data, layout_bounds, shapes, bounds);
|
let tracks = calculate_track_data(
|
||||||
|
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>();
|
||||||
|
|
|
@ -98,6 +98,66 @@ impl SerializableResult for TransformEntry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
|
pub enum StructureEntryType {
|
||||||
|
RemoveChild,
|
||||||
|
AddChild,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructureEntryType {
|
||||||
|
pub fn from_u32(value: u32) -> Self {
|
||||||
|
match value {
|
||||||
|
1 => Self::RemoveChild,
|
||||||
|
2 => Self::AddChild,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct StructureEntry {
|
||||||
|
pub entry_type: StructureEntryType,
|
||||||
|
pub index: u32,
|
||||||
|
pub parent: Uuid,
|
||||||
|
pub id: Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructureEntry {
|
||||||
|
pub fn new(entry_type: StructureEntryType, index: u32, parent: Uuid, id: Uuid) -> Self {
|
||||||
|
StructureEntry {
|
||||||
|
entry_type,
|
||||||
|
index,
|
||||||
|
parent,
|
||||||
|
id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_bytes(bytes: [u8; 40]) -> Self {
|
||||||
|
let entry_type = StructureEntryType::from_u32(u32::from_le_bytes([
|
||||||
|
bytes[0], bytes[1], bytes[2], bytes[3],
|
||||||
|
]));
|
||||||
|
|
||||||
|
let index = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
|
||||||
|
|
||||||
|
let parent = uuid_from_u32_quartet(
|
||||||
|
u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]),
|
||||||
|
u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]),
|
||||||
|
u32::from_le_bytes([bytes[16], bytes[17], bytes[18], bytes[19]]),
|
||||||
|
u32::from_le_bytes([bytes[20], bytes[21], bytes[22], bytes[23]]),
|
||||||
|
);
|
||||||
|
|
||||||
|
let id = uuid_from_u32_quartet(
|
||||||
|
u32::from_le_bytes([bytes[24], bytes[25], bytes[26], bytes[27]]),
|
||||||
|
u32::from_le_bytes([bytes[28], bytes[29], bytes[30], bytes[31]]),
|
||||||
|
u32::from_le_bytes([bytes[32], bytes[33], bytes[34], bytes[35]]),
|
||||||
|
u32::from_le_bytes([bytes[36], bytes[37], bytes[38], bytes[39]]),
|
||||||
|
);
|
||||||
|
|
||||||
|
StructureEntry::new(entry_type, index, parent, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -4,6 +4,7 @@ use skia_safe as skia;
|
||||||
|
|
||||||
use crate::render::RenderState;
|
use crate::render::RenderState;
|
||||||
use crate::shapes::Shape;
|
use crate::shapes::Shape;
|
||||||
|
use crate::shapes::StructureEntry;
|
||||||
use crate::uuid::Uuid;
|
use crate::uuid::Uuid;
|
||||||
|
|
||||||
/// This struct holds the state of the Rust application between JS calls.
|
/// This struct holds the state of the Rust application between JS calls.
|
||||||
|
@ -17,6 +18,7 @@ pub(crate) struct State<'a> {
|
||||||
pub current_shape: Option<&'a mut Shape>,
|
pub current_shape: Option<&'a mut Shape>,
|
||||||
pub shapes: HashMap<Uuid, Shape>,
|
pub shapes: HashMap<Uuid, Shape>,
|
||||||
pub modifiers: HashMap<Uuid, skia::Matrix>,
|
pub modifiers: HashMap<Uuid, skia::Matrix>,
|
||||||
|
pub structure: HashMap<Uuid, Vec<StructureEntry>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> State<'a> {
|
impl<'a> State<'a> {
|
||||||
|
@ -27,6 +29,7 @@ impl<'a> State<'a> {
|
||||||
current_shape: None,
|
current_shape: None,
|
||||||
shapes: HashMap::with_capacity(capacity),
|
shapes: HashMap::with_capacity(capacity),
|
||||||
modifiers: HashMap::new(),
|
modifiers: HashMap::new(),
|
||||||
|
structure: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,14 +42,22 @@ impl<'a> State<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
self.render_state.start_render_loop(
|
||||||
.start_render_loop(&mut self.shapes, &self.modifiers, timestamp)?;
|
&mut self.shapes,
|
||||||
|
&self.modifiers,
|
||||||
|
&self.structure,
|
||||||
|
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
|
self.render_state.process_animation_frame(
|
||||||
.process_animation_frame(&mut self.shapes, &self.modifiers, timestamp)?;
|
&mut self.shapes,
|
||||||
|
&self.modifiers,
|
||||||
|
&self.structure,
|
||||||
|
timestamp,
|
||||||
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +120,7 @@ impl<'a> State<'a> {
|
||||||
|
|
||||||
pub fn rebuild_tiles(&mut self) {
|
pub fn rebuild_tiles(&mut self) {
|
||||||
self.render_state
|
self.render_state
|
||||||
.rebuild_tiles(&mut self.shapes, &self.modifiers);
|
.rebuild_tiles(&mut self.shapes, &self.modifiers, &self.structure);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rebuild_modifier_tiles(&mut self) {
|
pub fn rebuild_modifier_tiles(&mut self) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue