mirror of
https://github.com/penpot/penpot.git
synced 2025-12-11 22:14:05 +01:00
✨ Changes to modifiers
This commit is contained in:
@@ -18,7 +18,6 @@
|
||||
[app.main.data.helpers :as dsh]
|
||||
[app.main.features :as features]
|
||||
[app.main.worker :as mw]
|
||||
[app.render-wasm.api :as wasm.api]
|
||||
[app.render-wasm.shape :as wasm.shape]
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
@@ -113,15 +112,7 @@
|
||||
(update-in state [:files file-id :data] apply-changes))]
|
||||
|
||||
(let [objects (dm/get-in state [:files file-id :data :pages-index (:current-page-id state) :objects])]
|
||||
(run!
|
||||
(fn [[shape-id props]]
|
||||
(wasm.api/use-shape shape-id)
|
||||
(let [shape (get objects shape-id)]
|
||||
(run! (partial wasm.shape/set-shape-wasm-attr! shape) props)))
|
||||
@shape-changes))
|
||||
|
||||
(wasm.api/update-shape-tiles)
|
||||
(wasm.api/request-render "set-wasm-attrs")
|
||||
(wasm.shape/process-shape-changes! objects @shape-changes))
|
||||
|
||||
state)
|
||||
|
||||
|
||||
@@ -226,22 +226,26 @@
|
||||
wasm-props
|
||||
(concat clean-props wasm-props)
|
||||
|
||||
wasm-props
|
||||
;; Stores a map shape -> set of properties changed
|
||||
;; this is the standard format used by process-shape-changes
|
||||
shape-changes
|
||||
(-> (group-by first wasm-props)
|
||||
(update-vals #(map second %)))]
|
||||
(update-vals #(into #{} (map (comp :property second)) %)))
|
||||
|
||||
;; Props are grouped by id and then assoc to the shape the new value
|
||||
(run! (fn [[id properties]]
|
||||
;; Create a new objects only with the temporary modifications
|
||||
objects-changed
|
||||
(->> wasm-props
|
||||
(reduce
|
||||
(fn [objects [id properties]]
|
||||
(let [shape
|
||||
(->> properties
|
||||
(reduce
|
||||
(fn [shape {:keys [property value]}]
|
||||
(assoc shape property value))
|
||||
(get objects id)))]
|
||||
|
||||
;; With the new values to the shape change multi props
|
||||
(wasm.shape/set-wasm-multi-attrs! shape (->> properties (map :property)))))
|
||||
wasm-props)))
|
||||
(assoc objects id shape)))
|
||||
objects))]
|
||||
(wasm.shape/process-shape-changes! objects-changed shape-changes)))
|
||||
|
||||
(defn clear-local-transform []
|
||||
(ptk/reify ::clear-local-transform
|
||||
|
||||
@@ -6,12 +6,14 @@
|
||||
|
||||
(ns app.render-wasm.shape
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.transit :as t]
|
||||
[app.common.types.shape :as shape]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.refs :as refs]
|
||||
[app.render-wasm.api :as api]
|
||||
[beicon.v2.core :as rx]
|
||||
[cljs.core :as c]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
@@ -118,8 +120,11 @@
|
||||
(-write writer (str "#penpot/shape " (:id delegate)))))
|
||||
|
||||
;; --- SHAPE IMPL
|
||||
|
||||
(defn set-shape-wasm-attr!
|
||||
;; When an attribute is sent to WASM it could still be pending some side operations
|
||||
;; for example: font loading when changing a text, this is an async operation that will
|
||||
;; resolve eventually.
|
||||
;; The `set-wasm-attr!` can return a list of callbacks to be executed in a second pass.
|
||||
(defn- set-wasm-attr!
|
||||
[shape k]
|
||||
(let [v (get shape k)
|
||||
id (get shape :id)]
|
||||
@@ -224,14 +229,32 @@
|
||||
(ctl/flex-layout? shape)
|
||||
(api/set-flex-layout shape)))
|
||||
|
||||
;; Property not in WASM
|
||||
nil)))
|
||||
|
||||
(defn set-wasm-multi-attrs!
|
||||
(defn process-shape!
|
||||
[shape properties]
|
||||
(let [shape-id (dm/get-prop shape :id)]
|
||||
(when (shape-in-current-page? shape-id)
|
||||
(if (shape-in-current-page? shape-id)
|
||||
(do
|
||||
(api/use-shape shape-id)
|
||||
(run! (partial set-shape-wasm-attr! shape) properties))))
|
||||
(->> properties
|
||||
(mapcat #(set-wasm-attr! shape %))
|
||||
(d/index-by :key :callback)
|
||||
(vals)
|
||||
(rx/from)
|
||||
(rx/mapcat (fn [callback] (callback)))
|
||||
(rx/reduce conj [])))
|
||||
(rx/empty))))
|
||||
|
||||
(defn process-shape-changes!
|
||||
[objects shape-changes]
|
||||
(->> (rx/from shape-changes)
|
||||
(rx/mapcat (fn [[shape-id props]] (process-shape! (get objects shape-id) props)))
|
||||
(rx/subs!
|
||||
(fn [_]
|
||||
(api/update-shape-tiles)
|
||||
(api/request-render "set-wasm-attrs")))))
|
||||
|
||||
(defn- impl-assoc
|
||||
[self k v]
|
||||
|
||||
@@ -20,6 +20,7 @@ use mem::SerializableResult;
|
||||
use shapes::{StructureEntry, StructureEntryType, TransformEntry};
|
||||
use skia_safe as skia;
|
||||
use state::State;
|
||||
use std::collections::HashMap;
|
||||
use utils::uuid_from_u32_quartet;
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -537,6 +538,7 @@ pub extern "C" fn set_structure_modifiers() {
|
||||
.collect();
|
||||
|
||||
with_state_mut!(state, {
|
||||
let mut structure = HashMap::new();
|
||||
for entry in entries {
|
||||
match entry.entry_type {
|
||||
StructureEntryType::ScaleContent => {
|
||||
@@ -548,15 +550,17 @@ pub extern "C" fn set_structure_modifiers() {
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
state.structure.entry(entry.parent).or_insert_with(Vec::new);
|
||||
state
|
||||
.structure
|
||||
structure.entry(entry.parent).or_insert_with(Vec::new);
|
||||
structure
|
||||
.get_mut(&entry.parent)
|
||||
.expect("Parent not found for entry")
|
||||
.push(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
if !structure.is_empty() {
|
||||
state.shapes.set_structure(structure);
|
||||
}
|
||||
});
|
||||
|
||||
mem::free_bytes();
|
||||
@@ -567,7 +571,8 @@ pub extern "C" fn clean_modifiers() {
|
||||
with_state_mut!(state, {
|
||||
state.structure.clear();
|
||||
state.scale_content.clear();
|
||||
state.modifiers.clear();
|
||||
// state.modifiers.clear();
|
||||
state.shapes.clean_modifiers();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -595,11 +600,16 @@ pub extern "C" fn set_modifiers() {
|
||||
.map(|data| TransformEntry::from_bytes(data.try_into().unwrap()))
|
||||
.collect();
|
||||
|
||||
with_state_mut!(state, {
|
||||
let mut modifiers = HashMap::new();
|
||||
let mut ids = Vec::<Uuid>::new();
|
||||
for entry in entries {
|
||||
state.modifiers.insert(entry.id, entry.transform);
|
||||
modifiers.insert(entry.id, entry.transform);
|
||||
ids.push(entry.id);
|
||||
}
|
||||
state.rebuild_modifier_tiles();
|
||||
|
||||
with_state_mut!(state, {
|
||||
state.set_modifiers(modifiers);
|
||||
state.rebuild_modifier_tiles(ids);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1134,13 +1134,8 @@ impl RenderState {
|
||||
self.get_rect_bounds(rect)
|
||||
}
|
||||
|
||||
pub fn get_shape_extrect_bounds(
|
||||
&mut self,
|
||||
shape: &Shape,
|
||||
tree: &ShapesPool,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
) -> Rect {
|
||||
let rect = shape.extrect(tree, modifiers);
|
||||
pub fn get_shape_extrect_bounds(&mut self, shape: &Shape, tree: &ShapesPool) -> Rect {
|
||||
let rect = shape.extrect(tree);
|
||||
self.get_rect_bounds(rect)
|
||||
}
|
||||
|
||||
@@ -1278,7 +1273,7 @@ impl RenderState {
|
||||
// If the shape is not in the tile set, then we update
|
||||
// it.
|
||||
if self.tiles.get_tiles_of(node_id).is_none() {
|
||||
self.update_tile_for(element, tree, modifiers);
|
||||
self.update_tile_for(element, tree);
|
||||
}
|
||||
|
||||
if visited_children {
|
||||
@@ -1297,18 +1292,14 @@ impl RenderState {
|
||||
let transformed_element: Cow<Shape> = Cow::Borrowed(element);
|
||||
|
||||
let is_visible = transformed_element
|
||||
.extrect(tree, modifiers)
|
||||
.extrect(tree)
|
||||
.intersects(self.render_area)
|
||||
&& !transformed_element.hidden
|
||||
&& !transformed_element.visually_insignificant(
|
||||
self.get_scale(),
|
||||
tree,
|
||||
modifiers,
|
||||
);
|
||||
&& !transformed_element.visually_insignificant(self.get_scale(), tree);
|
||||
|
||||
if self.options.is_debug_visible() {
|
||||
let shape_extrect_bounds =
|
||||
self.get_shape_extrect_bounds(&transformed_element, tree, modifiers);
|
||||
self.get_shape_extrect_bounds(&transformed_element, tree);
|
||||
debug::render_debug_shape(self, None, Some(shape_extrect_bounds));
|
||||
}
|
||||
|
||||
@@ -1657,23 +1648,14 @@ impl RenderState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_tiles_for_shape(
|
||||
&mut self,
|
||||
shape: &Shape,
|
||||
tree: &ShapesPool,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
) -> TileRect {
|
||||
pub fn get_tiles_for_shape(&mut self, shape: &Shape, tree: &ShapesPool) -> TileRect {
|
||||
let extrect = shape.extrect(tree);
|
||||
let tile_size = tiles::get_tile_size(self.get_scale());
|
||||
tiles::get_tiles_for_rect(shape.extrect(tree, modifiers), tile_size)
|
||||
tiles::get_tiles_for_rect(extrect, tile_size)
|
||||
}
|
||||
|
||||
pub fn update_tile_for(
|
||||
&mut self,
|
||||
shape: &Shape,
|
||||
tree: &ShapesPool,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
) {
|
||||
let TileRect(rsx, rsy, rex, rey) = self.get_tiles_for_shape(shape, tree, modifiers);
|
||||
pub fn update_tile_for(&mut self, shape: &Shape, tree: &ShapesPool) {
|
||||
let TileRect(rsx, rsy, rex, rey) = self.get_tiles_for_shape(shape, tree);
|
||||
let old_tiles: HashSet<tiles::Tile> = self
|
||||
.tiles
|
||||
.get_tiles_of(shape.id)
|
||||
@@ -1717,7 +1699,7 @@ impl RenderState {
|
||||
if let Some(modifier) = modifiers.get(&shape_id) {
|
||||
shape.to_mut().apply_transform(modifier);
|
||||
}
|
||||
self.update_tile_for(&shape, tree, modifiers);
|
||||
self.update_tile_for(&shape, tree);
|
||||
} else {
|
||||
// We only need to rebuild tiles from the first level.
|
||||
let children = shape.modified_children_ids(structure.get(&shape.id), false);
|
||||
@@ -1747,7 +1729,7 @@ impl RenderState {
|
||||
if let Some(modifier) = modifiers.get(&shape_id) {
|
||||
shape.to_mut().apply_transform(modifier);
|
||||
}
|
||||
self.update_tile_for(&shape, tree, modifiers);
|
||||
self.update_tile_for(&shape, tree);
|
||||
}
|
||||
|
||||
let children = shape.modified_children_ids(structure.get(&shape.id), false);
|
||||
@@ -1771,15 +1753,11 @@ impl RenderState {
|
||||
&mut self,
|
||||
shape_ids: &IndexSet<Uuid>,
|
||||
tree: &mut ShapesPool,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
) {
|
||||
for shape_id in shape_ids {
|
||||
if let Some(shape) = tree.get_mut(shape_id) {
|
||||
shape.invalidate_extrect();
|
||||
}
|
||||
if let Some(shape) = tree.get(shape_id) {
|
||||
if !shape.id.is_nil() {
|
||||
self.update_tile_for(shape, tree, modifiers);
|
||||
self.update_tile_for(shape, tree);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1791,14 +1769,9 @@ impl RenderState {
|
||||
/// Additionally, it processes all ancestors of modified shapes to ensure their
|
||||
/// extended rectangles are properly recalculated and their tiles are updated.
|
||||
/// This is crucial for frames and groups that contain transformed children.
|
||||
pub fn rebuild_modifier_tiles(
|
||||
&mut self,
|
||||
tree: &mut ShapesPool,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
) {
|
||||
let ids: Vec<_> = modifiers.keys().collect();
|
||||
pub fn rebuild_modifier_tiles(&mut self, tree: &mut ShapesPool, ids: Vec<Uuid>) {
|
||||
let ancestors = all_with_ancestors(&ids, tree, false);
|
||||
self.invalidate_and_update_tiles(&ancestors, tree, modifiers);
|
||||
self.invalidate_and_update_tiles(&ancestors, tree);
|
||||
}
|
||||
|
||||
pub fn get_scale(&self) -> f32 {
|
||||
|
||||
@@ -3,7 +3,7 @@ use skia_safe::{self as skia};
|
||||
use crate::uuid::Uuid;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::OnceCell;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::HashSet;
|
||||
use std::iter::once;
|
||||
|
||||
mod blend;
|
||||
@@ -196,11 +196,11 @@ pub struct Shape {
|
||||
// # Returns
|
||||
// A set of ancestor UUIDs in traversal order (closest ancestor first)
|
||||
pub fn all_with_ancestors(
|
||||
shapes: &[&Uuid],
|
||||
shapes: &[Uuid],
|
||||
shapes_pool: &ShapesPool,
|
||||
include_hidden: bool,
|
||||
) -> IndexSet<Uuid> {
|
||||
let mut pending = Vec::from(shapes);
|
||||
let mut pending = Vec::from_iter(shapes.iter());
|
||||
let mut result = IndexSet::new();
|
||||
|
||||
while !pending.is_empty() {
|
||||
@@ -677,13 +677,8 @@ impl Shape {
|
||||
self.selrect.width()
|
||||
}
|
||||
|
||||
pub fn visually_insignificant(
|
||||
&self,
|
||||
scale: f32,
|
||||
shapes_pool: &ShapesPool,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
) -> bool {
|
||||
let extrect = self.extrect(shapes_pool, modifiers);
|
||||
pub fn visually_insignificant(&self, scale: f32, shapes_pool: &ShapesPool) -> bool {
|
||||
let extrect = self.extrect(shapes_pool);
|
||||
extrect.width() * scale < MIN_VISIBLE_SIZE && extrect.height() * scale < MIN_VISIBLE_SIZE
|
||||
}
|
||||
|
||||
@@ -726,14 +721,10 @@ impl Shape {
|
||||
self.selrect
|
||||
}
|
||||
|
||||
pub fn extrect(
|
||||
&self,
|
||||
shapes_pool: &ShapesPool,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
) -> math::Rect {
|
||||
pub fn extrect(&self, shapes_pool: &ShapesPool) -> math::Rect {
|
||||
*self
|
||||
.extrect
|
||||
.get_or_init(|| self.calculate_extrect(shapes_pool, modifiers))
|
||||
.get_or_init(|| self.calculate_extrect(shapes_pool))
|
||||
}
|
||||
|
||||
pub fn get_text_content(&self) -> &TextContent {
|
||||
@@ -843,12 +834,7 @@ impl Shape {
|
||||
rect
|
||||
}
|
||||
|
||||
fn apply_children_bounds(
|
||||
&self,
|
||||
mut rect: math::Rect,
|
||||
shapes_pool: &ShapesPool,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
) -> math::Rect {
|
||||
fn apply_children_bounds(&self, mut rect: math::Rect, shapes_pool: &ShapesPool) -> math::Rect {
|
||||
let include_children = match self.shape_type {
|
||||
Type::Group(_) => true,
|
||||
Type::Frame(_) => !self.clip_content,
|
||||
@@ -858,15 +844,7 @@ impl Shape {
|
||||
if include_children {
|
||||
for child_id in self.children_ids(false) {
|
||||
if let Some(child_shape) = shapes_pool.get(&child_id) {
|
||||
// Create a copy of the child shape to apply any transformations
|
||||
let mut transformed_element: Cow<Shape> = Cow::Borrowed(child_shape);
|
||||
if let Some(modifier) = modifiers.get(&child_id) {
|
||||
transformed_element.to_mut().apply_transform(modifier);
|
||||
}
|
||||
|
||||
// Get the child's extended rectangle and join it with the container's rectangle
|
||||
let child_extrect = transformed_element.extrect(shapes_pool, modifiers);
|
||||
rect.join(child_extrect);
|
||||
rect.join(child_shape.extrect(shapes_pool));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -874,12 +852,8 @@ impl Shape {
|
||||
rect
|
||||
}
|
||||
|
||||
pub fn calculate_extrect(
|
||||
&self,
|
||||
shapes_pool: &ShapesPool,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
) -> math::Rect {
|
||||
let shape = self.transformed(modifiers.get(&self.id));
|
||||
pub fn calculate_extrect(&self, shapes_pool: &ShapesPool) -> math::Rect {
|
||||
let shape = self;
|
||||
let max_stroke = Stroke::max_bounds_width(shape.strokes.iter(), shape.is_open());
|
||||
|
||||
let mut rect = match &shape.shape_type {
|
||||
@@ -902,7 +876,7 @@ impl Shape {
|
||||
rect = self.apply_stroke_bounds(rect, max_stroke);
|
||||
rect = self.apply_shadow_bounds(rect);
|
||||
rect = self.apply_blur_bounds(rect);
|
||||
rect = self.apply_children_bounds(rect, shapes_pool, modifiers);
|
||||
rect = self.apply_children_bounds(rect, shapes_pool);
|
||||
|
||||
rect
|
||||
}
|
||||
@@ -1177,11 +1151,41 @@ impl Shape {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn transformed(&self, transform: Option<&Matrix>) -> Self {
|
||||
pub fn apply_structure(&mut self, structure: &Vec<StructureEntry>) {
|
||||
let mut result: Vec<Uuid> = Vec::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(result.len() - st.index as usize, st.id);
|
||||
}
|
||||
StructureEntryType::RemoveChild => {
|
||||
to_remove.insert(&st.id);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
self.children = result
|
||||
.iter()
|
||||
.filter(|id| !to_remove.contains(id))
|
||||
.copied()
|
||||
.collect();
|
||||
}
|
||||
|
||||
pub fn transformed(
|
||||
&self,
|
||||
transform: Option<&Matrix>,
|
||||
structure: Option<&Vec<StructureEntry>>) -> Self
|
||||
{
|
||||
let mut shape: Cow<Shape> = Cow::Borrowed(self);
|
||||
if let Some(transform) = transform {
|
||||
shape.to_mut().apply_transform(transform);
|
||||
}
|
||||
if let Some(structure) = structure {
|
||||
shape.to_mut().apply_structure(structure);
|
||||
}
|
||||
shape.into_owned()
|
||||
}
|
||||
|
||||
|
||||
@@ -186,11 +186,10 @@ impl ToPath for Shape {
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
) -> Path {
|
||||
let shape = self.transformed(modifiers.get(&self.id));
|
||||
match shape.shape_type {
|
||||
match &self.shape_type {
|
||||
Type::Frame(ref frame) => {
|
||||
let children = shape.modified_children_ids(structure.get(&shape.id), true);
|
||||
let mut result = Path::new(rect_segments(&shape, frame.corners));
|
||||
let children = self.modified_children_ids(structure.get(&self.id), true);
|
||||
let mut result = Path::new(rect_segments(&self, frame.corners));
|
||||
for id in children {
|
||||
let Some(shape) = shapes.get(&id) else {
|
||||
continue;
|
||||
@@ -201,7 +200,7 @@ impl ToPath for Shape {
|
||||
}
|
||||
|
||||
Type::Group(_) => {
|
||||
let children = shape.modified_children_ids(structure.get(&shape.id), true);
|
||||
let children = self.modified_children_ids(structure.get(&self.id), true);
|
||||
let mut result = Path::default();
|
||||
for id in children {
|
||||
let Some(shape) = shapes.get(&id) else {
|
||||
@@ -215,13 +214,13 @@ impl ToPath for Shape {
|
||||
Path::new(segments)
|
||||
}
|
||||
|
||||
Type::Bool(bool_data) => bool_data.path,
|
||||
Type::Bool(bool_data) => bool_data.path.clone(),
|
||||
|
||||
Type::Rect(ref rect) => Path::new(rect_segments(&shape, rect.corners)),
|
||||
Type::Rect(ref rect) => Path::new(rect_segments(&self, rect.corners)),
|
||||
|
||||
Type::Path(path_data) => path_data,
|
||||
Type::Path(path_data) => path_data.clone(),
|
||||
|
||||
Type::Circle => Path::new(circle_segments(&shape)),
|
||||
Type::Circle => Path::new(circle_segments(&self)),
|
||||
|
||||
Type::SVGRaw(_) => Path::default(),
|
||||
|
||||
@@ -232,7 +231,7 @@ impl ToPath for Shape {
|
||||
result = join_paths(result, Path::from_skia_path(path));
|
||||
}
|
||||
|
||||
Path::new(transform_segments(result.segments().clone(), &shape))
|
||||
Path::new(transform_segments(result.segments().clone(), &self))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,8 +114,7 @@ impl State {
|
||||
// We don't really do a self.shapes.remove so that redo/undo keep working
|
||||
if let Some(shape) = self.shapes.get(&id) {
|
||||
let tiles::TileRect(rsx, rsy, rex, rey) =
|
||||
self.render_state
|
||||
.get_tiles_for_shape(shape, &self.shapes, &self.modifiers);
|
||||
self.render_state.get_tiles_for_shape(shape, &self.shapes);
|
||||
for x in rsx..=rex {
|
||||
for y in rsy..=rey {
|
||||
let tile = tiles::Tile(x, y);
|
||||
@@ -159,8 +158,7 @@ impl State {
|
||||
|
||||
pub fn update_tile_for_shape(&mut self, shape_id: Uuid) {
|
||||
if let Some(shape) = self.shapes.get(&shape_id) {
|
||||
self.render_state
|
||||
.update_tile_for(shape, &self.shapes, &self.modifiers);
|
||||
self.render_state.update_tile_for(shape, &self.shapes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,7 +168,7 @@ impl State {
|
||||
};
|
||||
if !shape.id.is_nil() {
|
||||
self.render_state
|
||||
.update_tile_for(&shape.clone(), &self.shapes, &self.modifiers);
|
||||
.update_tile_for(&shape.clone(), &self.shapes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,9 +182,9 @@ impl State {
|
||||
.rebuild_tiles(&self.shapes, &self.modifiers, &self.structure);
|
||||
}
|
||||
|
||||
pub fn rebuild_modifier_tiles(&mut self) {
|
||||
pub fn rebuild_modifier_tiles(&mut self, ids: Vec<Uuid>) {
|
||||
self.render_state
|
||||
.rebuild_modifier_tiles(&mut self.shapes, &self.modifiers);
|
||||
.rebuild_modifier_tiles(&mut self.shapes, ids);
|
||||
}
|
||||
|
||||
pub fn font_collection(&self) -> &FontCollection {
|
||||
@@ -217,4 +215,8 @@ impl State {
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn set_modifiers(&mut self, modifiers: HashMap<Uuid, skia::Matrix>) {
|
||||
self.shapes.set_modifiers(modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,11 @@ use crate::performance;
|
||||
use crate::shapes::Shape;
|
||||
use crate::uuid::Uuid;
|
||||
|
||||
use crate::shapes::StructureEntry;
|
||||
use crate::skia;
|
||||
|
||||
use std::cell::OnceCell;
|
||||
|
||||
const SHAPES_POOL_ALLOC_MULTIPLIER: f32 = 1.3;
|
||||
|
||||
/// A pool allocator for `Shape` objects that attempts to minimize memory reallocations.
|
||||
@@ -20,8 +25,13 @@ const SHAPES_POOL_ALLOC_MULTIPLIER: f32 = 1.3;
|
||||
///
|
||||
pub struct ShapesPool {
|
||||
shapes: Vec<Shape>,
|
||||
shapes_uuid_to_idx: HashMap<Uuid, usize>,
|
||||
counter: usize,
|
||||
|
||||
shapes_uuid_to_idx: HashMap<Uuid, usize>,
|
||||
|
||||
modified_shape_cache: HashMap<Uuid, OnceCell<Shape>>,
|
||||
modifiers: HashMap<Uuid, skia::Matrix>,
|
||||
structure: HashMap<Uuid, Vec<StructureEntry>>,
|
||||
}
|
||||
|
||||
impl ShapesPool {
|
||||
@@ -30,6 +40,10 @@ impl ShapesPool {
|
||||
shapes: vec![],
|
||||
counter: 0,
|
||||
shapes_uuid_to_idx: HashMap::default(),
|
||||
|
||||
modified_shape_cache: HashMap::default(),
|
||||
modifiers: HashMap::default(),
|
||||
structure: HashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,8 +90,23 @@ impl ShapesPool {
|
||||
|
||||
pub fn get(&self, id: &Uuid) -> Option<&Shape> {
|
||||
let idx = *self.shapes_uuid_to_idx.get(id)?;
|
||||
if self.modifiers.contains_key(id) || self.structure.contains_key(id) {
|
||||
if let Some(cell) = self.modified_shape_cache.get(id) {
|
||||
Some(cell.get_or_init(|| {
|
||||
let shape = &self.shapes[idx];
|
||||
shape.transformed(
|
||||
self.modifiers.get(id),
|
||||
self.structure.get(id)
|
||||
)
|
||||
}))
|
||||
} else {
|
||||
let shape = &self.shapes[idx];
|
||||
Some(shape)
|
||||
}
|
||||
} else {
|
||||
Some(&self.shapes[idx])
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn iter(&self) -> std::slice::Iter<'_, Shape> {
|
||||
@@ -87,4 +116,44 @@ impl ShapesPool {
|
||||
pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, Shape> {
|
||||
self.shapes.iter_mut()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn clean_shape_cache(&mut self) {
|
||||
self.modified_shape_cache.clear()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn set_modifiers(&mut self, modifiers: HashMap<Uuid, skia::Matrix>) {
|
||||
// self.clean_shape_cache();
|
||||
|
||||
// Initialize the cache cells because
|
||||
// later we don't want to have the mutable pointer
|
||||
for key in modifiers.keys() {
|
||||
self.modified_shape_cache.insert(*key, OnceCell::new());
|
||||
}
|
||||
self.modifiers = modifiers;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn set_structure(&mut self, structure: HashMap<Uuid, Vec<StructureEntry>>) {
|
||||
// self.clean_shape_cache();
|
||||
// Initialize the cache cells because
|
||||
// later we don't want to have the mutable pointer
|
||||
for key in structure.keys() {
|
||||
self.modified_shape_cache.insert(*key, OnceCell::new());
|
||||
}
|
||||
self.structure = structure;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn clean_modifiers(&mut self) {
|
||||
self.clean_shape_cache();
|
||||
self.modifiers = HashMap::default();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn clean_structure(&mut self) {
|
||||
self.clean_shape_cache();
|
||||
self.structure = HashMap::default();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user