UI/qubescene: dynamically recalculate the positions
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
// Working SolidJS + Three.js cube scene with grid arrangement
|
// Working SolidJS + Three.js cube scene with reactive positioning
|
||||||
import { createSignal, createEffect, onCleanup, onMount } from "solid-js";
|
import { createSignal, createEffect, onCleanup, onMount, createMemo } from "solid-js";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
|
|
||||||
// Cube Data Model
|
// Cube Data Model
|
||||||
@@ -29,7 +29,7 @@ export function CubeScene() {
|
|||||||
let isAnimating = false; // Flag to prevent multiple loops
|
let isAnimating = false; // Flag to prevent multiple loops
|
||||||
let frameCount = 0;
|
let frameCount = 0;
|
||||||
|
|
||||||
const [cubes, setCubes] = createSignal<CubeData[]>([]);
|
const [ids, setIds] = createSignal<string[]>([]);
|
||||||
const [selectedIds, setSelectedIds] = createSignal<Set<string>>(new Set());
|
const [selectedIds, setSelectedIds] = createSignal<Set<string>>(new Set());
|
||||||
const [cameraInfo, setCameraInfo] = createSignal({
|
const [cameraInfo, setCameraInfo] = createSignal({
|
||||||
position: { x: 0, y: 0, z: 0 },
|
position: { x: 0, y: 0, z: 0 },
|
||||||
@@ -50,6 +50,15 @@ export function CubeScene() {
|
|||||||
return [x, 0.5, z];
|
return [x, 0.5, z];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reactive cubes memo - this recalculates whenever ids() changes
|
||||||
|
const cubes = createMemo(() => {
|
||||||
|
return ids().map((id, index) => ({
|
||||||
|
id,
|
||||||
|
position: getGridPosition(index),
|
||||||
|
color: "blue",
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
// Create multi-colored cube materials for different faces
|
// Create multi-colored cube materials for different faces
|
||||||
function createCubeMaterials() {
|
function createCubeMaterials() {
|
||||||
const materials = [
|
const materials = [
|
||||||
@@ -62,6 +71,7 @@ export function CubeScene() {
|
|||||||
];
|
];
|
||||||
return materials;
|
return materials;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createBaseMaterials() {
|
function createBaseMaterials() {
|
||||||
const materials = [
|
const materials = [
|
||||||
new THREE.MeshBasicMaterial({ color: 0xdce4e5 }), // Right face - medium
|
new THREE.MeshBasicMaterial({ color: 0xdce4e5 }), // Right face - medium
|
||||||
@@ -87,52 +97,25 @@ export function CubeScene() {
|
|||||||
// === Add/Delete Cube API ===
|
// === Add/Delete Cube API ===
|
||||||
function addCube() {
|
function addCube() {
|
||||||
const id = crypto.randomUUID();
|
const id = crypto.randomUUID();
|
||||||
const currentCount = cubes().length;
|
setIds((prev) => [...prev, id]);
|
||||||
const cube: CubeData = {
|
|
||||||
id,
|
|
||||||
position: getGridPosition(currentCount),
|
|
||||||
color: "blue",
|
|
||||||
};
|
|
||||||
setCubes((prev) => [...prev, cube]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteSelectedCubes(ids: Set<string>) {
|
function deleteSelectedCubes(selectedSet: Set<string>) {
|
||||||
ids.forEach((id) => {
|
if (selectedSet.size === 0) return;
|
||||||
deleteCube(id);
|
|
||||||
});
|
setIds((prev) => prev.filter(id => !selectedSet.has(id)));
|
||||||
setSelectedIds(new Set<string>()); // Clear selection after deletion
|
setSelectedIds(new Set<string>()); // Clear selection after deletion
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteCube(id: string) {
|
function deleteCube(id: string) {
|
||||||
// Remove cube mesh
|
setIds((prev) => prev.filter(cubeId => cubeId !== id));
|
||||||
const mesh = meshMap.get(id);
|
|
||||||
if (mesh) {
|
|
||||||
scene.remove(mesh);
|
|
||||||
mesh.geometry.dispose();
|
|
||||||
// Dispose materials properly
|
|
||||||
if (Array.isArray(mesh.material)) {
|
|
||||||
mesh.material.forEach((material) => material.dispose());
|
|
||||||
} else {
|
|
||||||
mesh.material.dispose();
|
|
||||||
}
|
|
||||||
meshMap.delete(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove base mesh - THIS WAS MISSING!
|
// Also remove from selection if it was selected
|
||||||
const base = baseMap.get(id);
|
setSelectedIds((prev) => {
|
||||||
if (base) {
|
const next = new Set(prev);
|
||||||
scene.remove(base);
|
next.delete(id);
|
||||||
base.geometry.dispose();
|
return next;
|
||||||
// Dispose base materials properly
|
});
|
||||||
if (Array.isArray(base.material)) {
|
|
||||||
base.material.forEach((material) => material.dispose());
|
|
||||||
} else {
|
|
||||||
base.material.dispose();
|
|
||||||
}
|
|
||||||
baseMap.delete(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
setCubes((prev) => prev.filter((c) => c.id !== id));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleSelection(id: string) {
|
function toggleSelection(id: string) {
|
||||||
@@ -385,14 +368,18 @@ export function CubeScene() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Effect to manage cube meshes
|
// Effect to manage cube meshes - this runs whenever cubes() changes
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
|
const currentCubes = cubes();
|
||||||
const existing = new Set(meshMap.keys());
|
const existing = new Set(meshMap.keys());
|
||||||
|
|
||||||
// Update existing cubes and create new ones
|
// Update existing cubes and create new ones
|
||||||
cubes().forEach((cube) => {
|
currentCubes.forEach((cube) => {
|
||||||
if (!meshMap.has(cube.id)) {
|
const existingMesh = meshMap.get(cube.id);
|
||||||
// Create cube mesh
|
const existingBase = baseMap.get(cube.id);
|
||||||
|
|
||||||
|
if (!existingMesh) {
|
||||||
|
// Create new cube mesh
|
||||||
const cubeMaterials = createCubeMaterials();
|
const cubeMaterials = createCubeMaterials();
|
||||||
const mesh = new THREE.Mesh(sharedCubeGeometry, cubeMaterials);
|
const mesh = new THREE.Mesh(sharedCubeGeometry, cubeMaterials);
|
||||||
mesh.castShadow = true;
|
mesh.castShadow = true;
|
||||||
@@ -402,18 +389,51 @@ export function CubeScene() {
|
|||||||
scene.add(mesh);
|
scene.add(mesh);
|
||||||
meshMap.set(cube.id, mesh);
|
meshMap.set(cube.id, mesh);
|
||||||
|
|
||||||
// Create base mesh
|
// Create new base mesh
|
||||||
const base = createCubeBase(cube.position);
|
const base = createCubeBase(cube.position);
|
||||||
base.userData.id = cube.id;
|
base.userData.id = cube.id;
|
||||||
scene.add(base);
|
scene.add(base);
|
||||||
baseMap.set(cube.id, base);
|
baseMap.set(cube.id, base);
|
||||||
|
} else {
|
||||||
|
// Update existing mesh position
|
||||||
|
existingMesh.position.set(...cube.position);
|
||||||
|
if (existingBase) {
|
||||||
|
existingBase.position.set(cube.position[0], cube.position[1] - 0.5 - 0.025, cube.position[2]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
existing.delete(cube.id);
|
existing.delete(cube.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Remove cubes that are no longer in the state
|
// Remove cubes that are no longer in the state
|
||||||
existing.forEach((id) => {
|
existing.forEach((id) => {
|
||||||
deleteCube(id);
|
// Remove cube mesh
|
||||||
|
const mesh = meshMap.get(id);
|
||||||
|
if (mesh) {
|
||||||
|
scene.remove(mesh);
|
||||||
|
mesh.geometry.dispose();
|
||||||
|
// Dispose materials properly
|
||||||
|
if (Array.isArray(mesh.material)) {
|
||||||
|
mesh.material.forEach((material) => material.dispose());
|
||||||
|
} else {
|
||||||
|
mesh.material.dispose();
|
||||||
|
}
|
||||||
|
meshMap.delete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove base mesh
|
||||||
|
const base = baseMap.get(id);
|
||||||
|
if (base) {
|
||||||
|
scene.remove(base);
|
||||||
|
base.geometry.dispose();
|
||||||
|
// Dispose base materials properly
|
||||||
|
if (Array.isArray(base.material)) {
|
||||||
|
base.material.forEach((material) => material.dispose());
|
||||||
|
} else {
|
||||||
|
base.material.dispose();
|
||||||
|
}
|
||||||
|
baseMap.delete(id);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
updateMeshColors();
|
updateMeshColors();
|
||||||
@@ -457,9 +477,9 @@ export function CubeScene() {
|
|||||||
<div>
|
<div>
|
||||||
<div style={{ "margin-bottom": "10px" }}>
|
<div style={{ "margin-bottom": "10px" }}>
|
||||||
<button onClick={addCube}>Add Cube</button>
|
<button onClick={addCube}>Add Cube</button>
|
||||||
<button onClick={()=>deleteSelectedCubes(selectedIds())}>Delete Cube</button>
|
<button onClick={()=>deleteSelectedCubes(selectedIds())}>Delete Selected</button>
|
||||||
<span style={{ "margin-left": "10px" }}>
|
<span style={{ "margin-left": "10px" }}>
|
||||||
Selected: {selectedIds().size} cubes
|
Selected: {selectedIds().size} cubes | Total: {ids().length} cubes
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -499,4 +519,4 @@ export function CubeScene() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user