Add drop grid cells in wasm

This commit is contained in:
alonso.torres 2025-06-27 15:56:45 +02:00 committed by Belén Albeza
parent 52a4fc6030
commit 8dcb376b18
12 changed files with 215 additions and 119 deletions

View file

@ -36,7 +36,9 @@
[app.main.data.workspace.undo :as dwu]
[app.main.features :as features]
[app.main.snap :as snap]
[app.main.store :as st]
[app.main.streams :as ms]
[app.render-wasm.api :as wasm.api]
[app.util.array :as array]
[app.util.dom :as dom]
[app.util.keyboard :as kbd]
@ -602,6 +604,15 @@
(rx/take 1)
(rx/map #(start-move from-position))))))
(defn get-drop-cell
[target-frame objects position]
(if (features/active-feature? @st/state "render-wasm/v1")
(do
(wasm.api/use-shape target-frame)
(let [cell (wasm.api/get-grid-coords position)]
(when (not= cell [-1 -1]) cell)))
(gslg/get-drop-cell target-frame objects position)))
(defn set-ghost-displacement
[move-vector]
(ptk/reify ::set-ghost-displacement
@ -621,7 +632,8 @@
ptk/WatchEvent
(watch [_ state stream]
(let [page-id (:current-page-id state)
(let [prev-cell-data (volatile! nil)
page-id (:current-page-id state)
objects (dsh/lookup-page-objects state page-id)
selected (dsh/lookup-selected state {:omit-blocked? true})
ids (if (nil? ids) selected ids)
@ -685,7 +697,7 @@
flex-layout? (ctl/flex-layout? objects target-frame)
grid-layout? (ctl/grid-layout? objects target-frame)
drop-index (when flex-layout? (gslf/get-drop-index target-frame objects position))
cell-data (when (and grid-layout? (not mod?)) (gslg/get-drop-cell target-frame objects position))]
cell-data (when (and grid-layout? (not mod?)) (get-drop-cell target-frame objects position))]
(array move-vector target-frame drop-index cell-data))))
(rx/take-until stopper))
@ -693,9 +705,15 @@
modifiers-stream
(->> move-stream
(rx/with-latest-from array/conj ms/mouse-position-shift)
(rx/tap
(fn [[_ _ _ cell-data _]]
(when (some? cell-data)
(vreset! prev-cell-data cell-data))))
(rx/map
(fn [[move-vector target-frame drop-index cell-data shift?]]
(let [x-disp? (> (mth/abs (:x move-vector)) (mth/abs (:y move-vector)))
(let [cell-data (or cell-data @prev-cell-data)
x-disp? (> (mth/abs (:x move-vector)) (mth/abs (:y move-vector)))
[move-vector snap-ignore-axis]
(cond
(and shift? x-disp?)

View file

@ -1045,6 +1045,18 @@
(h/call wasm/internal-module "_hide_grid")
(request-render "clear-grid"))
(defn get-grid-coords
[position]
(let [offset (h/call wasm/internal-module
"_get_grid_coords"
(get position :x)
(get position :y))
heapi32 (mem/get-heap-i32)
row (aget heapi32 (mem/ptr8->ptr32 (+ offset 0)))
column (aget heapi32 (mem/ptr8->ptr32 (+ offset 4)))]
(h/call wasm/internal-module "_free_bytes")
[row column]))
(defonce module
(delay
(if (exists? js/dynamicImport)

View file

@ -50,6 +50,11 @@
[]
(unchecked-get ^js wasm/internal-module "HEAPU32"))
(defn get-heap-i32
"Returns a Uint32Array view of the heap"
[]
(unchecked-get ^js wasm/internal-module "HEAP32"))
(defn get-heap-f32
"Returns a Float32Array view of the heap"
[]

View file

@ -35,7 +35,7 @@ EMCC_CFLAGS="--no-entry \
-sMAX_WEBGL_VERSION=2 \
-sMODULARIZE=1 \
-sEXPORT_NAME=createRustSkiaModule \
-sEXPORTED_RUNTIME_METHODS=GL,stringToUTF8,HEAPU8,HEAPU32,HEAPF32 \
-sEXPORTED_RUNTIME_METHODS=GL,stringToUTF8,HEAPU8,HEAP32,HEAPU32,HEAPF32 \
-sEXPORT_ES6=1"
export EM_CACHE="/tmp/emsdk_cache";

View file

@ -748,6 +748,19 @@ pub extern "C" fn hide_grid() {
});
}
#[no_mangle]
pub extern "C" fn get_grid_coords(pos_x: f32, pos_y: f32) -> *mut u8 {
let row: i32;
let col: i32;
with_state!(state, {
(row, col) = state.get_grid_coords(pos_x, pos_y);
});
let mut bytes = vec![0; 8];
bytes[0..4].clone_from_slice(&row.to_le_bytes());
bytes[4..8].clone_from_slice(&col.to_le_bytes());
mem::write_bytes(bytes)
}
fn main() {
#[cfg(target_arch = "wasm32")]
init_gl!();

View file

@ -21,7 +21,7 @@ use options::RenderOptions;
use surfaces::{SurfaceId, Surfaces};
use crate::performance;
use crate::shapes::{modified_children_ids, Corners, Fill, Shape, StructureEntry, Type};
use crate::shapes::{Corners, Fill, Shape, StructureEntry, Type};
use crate::tiles::{self, PendingTiles, TileRect};
use crate::uuid::Uuid;
use crate::view::Viewbox;
@ -929,7 +929,7 @@ impl RenderState {
node_render_state.get_children_clip_bounds(element, modifiers.get(&element.id));
let mut children_ids =
modified_children_ids(element, structure.get(&element.id), false);
element.modified_children_ids(structure.get(&element.id), false);
// Z-index ordering on Layouts
if element.has_layout() {
@ -1019,7 +1019,7 @@ impl RenderState {
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), false);
let root_ids = root.modified_children_ids(structure.get(&root.id), false);
// If we finish processing every node rendering is complete
// let's check if there are more pending nodes
@ -1118,7 +1118,7 @@ impl RenderState {
self.update_tile_for(&shape);
} else {
// We only need to rebuild tiles from the first level.
let children = modified_children_ids(&shape, structure.get(&shape.id), false);
let children = shape.modified_children_ids(structure.get(&shape.id), false);
for child_id in children.iter() {
nodes.push(*child_id);
}
@ -1148,7 +1148,7 @@ impl RenderState {
self.update_tile_for(&shape);
}
let children = modified_children_ids(&shape, structure.get(&shape.id), false);
let children = shape.modified_children_ids(structure.get(&shape.id), false);
for child_id in children.iter() {
nodes.push(*child_id);
}

View file

@ -1,11 +1,9 @@
use skia_safe::{self as skia};
use std::collections::HashMap;
use crate::shapes::modifiers::common::GetBounds;
use crate::math::{Bounds, Matrix, Rect};
use crate::shapes::modifiers::grid_layout::{calculate_tracks, create_cell_data};
use crate::shapes::{modified_children_ids, Frame, Layout, Shape, StructureEntry, Type};
use crate::math::{Matrix, Rect};
use crate::shapes::modifiers::grid_layout::grid_cell_data;
use crate::shapes::{Shape, StructureEntry};
use crate::uuid::Uuid;
pub fn render_overlay(
@ -16,67 +14,7 @@ pub fn render_overlay(
modifiers: &HashMap<Uuid, Matrix>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
) {
let Type::Frame(Frame {
layout: Some(Layout::GridLayout(layout_data, grid_data)),
..
}) = &shape.shape_type
else {
return;
};
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 children = modified_children_ids(shape, structure.get(&shape.id), 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(
true,
shape,
layout_data,
grid_data,
&layout_bounds,
&grid_data.cells,
shapes,
bounds,
);
let row_tracks = calculate_tracks(
false,
shape,
layout_data,
grid_data,
&layout_bounds,
&grid_data.cells,
shapes,
bounds,
);
let cells = create_cell_data(
&layout_bounds,
&children,
shapes,
&grid_data.cells,
&column_tracks,
&row_tracks,
true,
);
let cells = grid_cell_data(shape.clone(), shapes, modifiers, structure, true);
let mut paint = skia::Paint::default();
paint.set_style(skia::PaintStyle::Stroke);

View file

@ -967,19 +967,18 @@ impl Shape {
pub fn has_inner_strokes(&self) -> bool {
self.strokes.iter().any(|s| s.kind == StrokeKind::Inner)
}
}
/*
/*
Returns the list of children taking into account the structure modifiers
*/
pub fn modified_children_ids(
element: &Shape,
*/
pub fn modified_children_ids(
&self,
structure: Option<&Vec<StructureEntry>>,
include_hidden: bool,
) -> IndexSet<Uuid> {
) -> IndexSet<Uuid> {
if let Some(structure) = structure {
let mut result: Vec<Uuid> =
Vec::from_iter(element.children_ids(include_hidden).iter().copied());
Vec::from_iter(self.children_ids(include_hidden).iter().copied());
let mut to_remove = HashSet::<&Uuid>::new();
for st in structure {
@ -1002,7 +1001,8 @@ pub fn modified_children_ids(
ret
} else {
element.children_ids(include_hidden)
self.children_ids(include_hidden)
}
}
}

View file

@ -8,8 +8,8 @@ use common::GetBounds;
use crate::math::{self as math, identitish, Bounds, Matrix, Point};
use crate::shapes::{
auto_height, modified_children_ids, set_paragraphs_width, ConstraintH, ConstraintV, Frame,
Group, GrowType, Layout, Modifier, Shape, StructureEntry, TransformEntry, Type,
auto_height, set_paragraphs_width, ConstraintH, ConstraintV, Frame, Group, GrowType, Layout,
Modifier, Shape, StructureEntry, TransformEntry, Type,
};
use crate::state::State;
use crate::uuid::Uuid;
@ -25,7 +25,7 @@ fn propagate_children(
structure: &HashMap<Uuid, Vec<StructureEntry>>,
scale_content: &HashMap<Uuid, f32>,
) -> VecDeque<Modifier> {
let children_ids = modified_children_ids(shape, structure.get(&shape.id), true);
let children_ids = shape.modified_children_ids(structure.get(&shape.id), true);
if children_ids.is_empty() || identitish(transform) {
return VecDeque::new();
@ -95,7 +95,7 @@ fn calculate_group_bounds(
let shape_bounds = bounds.find(shape);
let mut result = Vec::<Point>::new();
let children_ids = modified_children_ids(shape, structure.get(&shape.id), true);
let children_ids = shape.modified_children_ids(structure.get(&shape.id), true);
for child_id in children_ids.iter() {
let Some(child) = shapes.get(child_id) else {
continue;
@ -263,7 +263,7 @@ fn propagate_reflow(
}
}
Type::Group(Group { masked: true }) => {
let children_ids = modified_children_ids(shape, state.structure.get(&shape.id), true);
let children_ids = shape.modified_children_ids(state.structure.get(&shape.id), true);
if let Some(child) = shapes.get(&children_ids[0]) {
let child_bounds = bounds.find(child);
bounds.insert(shape.id, child_bounds);

View file

@ -1,8 +1,8 @@
#![allow(dead_code)]
use crate::math::{self as math, Bounds, Matrix, Point, Vector, VectorExt};
use crate::shapes::{
modified_children_ids, AlignContent, AlignItems, AlignSelf, FlexData, JustifyContent,
LayoutData, LayoutItem, Modifier, Shape, StructureEntry,
AlignContent, AlignItems, AlignSelf, FlexData, JustifyContent, LayoutData, LayoutItem,
Modifier, Shape, StructureEntry,
};
use crate::uuid::Uuid;
@ -184,7 +184,7 @@ fn initialize_tracks(
) -> Vec<TrackData> {
let mut tracks = Vec::<TrackData>::new();
let mut current_track = TrackData::default();
let mut children = modified_children_ids(shape, structure.get(&shape.id), true);
let mut children = shape.modified_children_ids(structure.get(&shape.id), true);
let mut first = true;
if flex_data.is_reverse() {

View file

@ -1,8 +1,8 @@
use crate::math::{self as math, intersect_rays, Bounds, Matrix, Point, Ray, Vector, VectorExt};
use crate::shapes::{
modified_children_ids, AlignContent, AlignItems, AlignSelf, GridCell, GridData, GridTrack,
GridTrackType, JustifyContent, JustifyItems, JustifySelf, LayoutData, LayoutItem, Modifier,
Shape, StructureEntry,
AlignContent, AlignItems, AlignSelf, Frame, GridCell, GridData, GridTrack, GridTrackType,
JustifyContent, JustifyItems, JustifySelf, Layout, LayoutData, LayoutItem, Modifier, Shape,
StructureEntry, Type,
};
use crate::uuid::Uuid;
use indexmap::IndexSet;
@ -21,6 +21,8 @@ pub struct CellData<'a> {
pub height: f32,
pub align_self: Option<AlignSelf>,
pub justify_self: Option<JustifySelf>,
pub row: usize,
pub column: usize,
}
#[derive(Debug)]
@ -590,12 +592,84 @@ pub fn create_cell_data<'a>(
height: cell_bounds.height(),
align_self: cell.align_self,
justify_self: cell.justify_self,
row: row_start,
column: column_start,
});
}
result
}
pub fn grid_cell_data<'a>(
shape: Shape,
shapes: &'a HashMap<Uuid, &mut Shape>,
modifiers: &HashMap<Uuid, Matrix>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
allow_empty: bool,
) -> Vec<CellData<'a>> {
let Type::Frame(Frame {
layout: Some(Layout::GridLayout(layout_data, grid_data)),
..
}) = &shape.shape_type
else {
return vec![];
};
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 children = shape.modified_children_ids(structure.get(&shape.id), 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(
true,
shape,
layout_data,
grid_data,
&layout_bounds,
&grid_data.cells,
shapes,
bounds,
);
let row_tracks = calculate_tracks(
false,
shape,
layout_data,
grid_data,
&layout_bounds,
&grid_data.cells,
shapes,
bounds,
);
create_cell_data(
&layout_bounds,
&children,
shapes,
&grid_data.cells,
&column_tracks,
&row_tracks,
allow_empty,
)
}
fn child_position(
child: &Shape,
layout_bounds: &Bounds,
@ -655,7 +729,7 @@ pub fn reflow_grid_layout(
) -> VecDeque<Modifier> {
let mut result = VecDeque::new();
let layout_bounds = bounds.find(shape);
let children = modified_children_ids(shape, structure.get(&shape.id), true);
let children = shape.modified_children_ids(structure.get(&shape.id), true);
let column_tracks = calculate_tracks(
true,

View file

@ -1,7 +1,7 @@
use std::collections::{hash_map::Entry, HashMap};
use std::{iter, vec};
use skia_safe as skia;
use skia_safe::{self as skia, Path, Point};
use crate::performance;
use crate::render::RenderState;
@ -10,6 +10,8 @@ use crate::shapes::StructureEntry;
use crate::tiles;
use crate::uuid::Uuid;
use crate::shapes::modifiers::grid_layout::grid_cell_data;
const SHAPES_POOL_ALLOC_MULTIPLIER: f32 = 1.3;
/// A pool allocator for `Shape` objects that attempts to minimize memory reallocations.
@ -224,4 +226,38 @@ impl<'a> State<'a> {
self.render_state
.rebuild_modifier_tiles(&mut self.shapes, &self.modifiers);
}
pub fn get_grid_coords(&mut self, pos_x: f32, pos_y: f32) -> (i32, i32) {
let Some(shape) = self.current_shape() else {
return (-1, -1);
};
let bounds = shape.bounds();
let position = Point::new(pos_x, pos_y);
let cells = grid_cell_data(
shape.clone(),
&self.shapes,
&self.modifiers,
&self.structure,
true,
);
for cell in cells {
let points = &[
cell.anchor,
cell.anchor + bounds.hv(cell.width),
cell.anchor + bounds.hv(cell.width) + bounds.vv(cell.height),
cell.anchor + bounds.vv(cell.height),
];
let polygon = Path::polygon(points, true, None, None);
if polygon.contains(position) {
return (cell.row as i32 + 1, cell.column as i32 + 1);
}
}
(-1, -1)
}
}