mattercontrol/src/stores/scene.ts

169 lines
4.1 KiB
TypeScript
Raw Normal View History

2026-01-29 00:24:41 -08:00
import { defineStore } from 'pinia'
2026-01-29 00:20:16 -08:00
import { shallowRef, computed } from 'vue'
import * as THREE from 'three'
export interface SceneObject {
id: string
name: string
mesh: THREE.Mesh
visible: boolean
locked: boolean
}
2026-01-29 00:24:41 -08:00
export const useSceneStore = defineStore('scene', () => {
// Use shallowRef to avoid Vue making THREE.Mesh objects deeply reactive
const objects = shallowRef<SceneObject[]>([])
2026-01-29 01:26:33 -08:00
const selectedIds = shallowRef<Set<string>>(new Set())
2026-01-29 00:24:41 -08:00
let threeScene: THREE.Scene | null = null
let nextId = 1
2026-01-29 00:20:16 -08:00
2026-01-29 00:24:41 -08:00
function generateId(): string {
return `obj_${nextId++}`
2026-01-29 00:20:16 -08:00
}
2026-01-29 00:24:41 -08:00
function generateName(baseName: string): string {
const existingNames = objects.value.map((o) => o.name)
if (!existingNames.includes(baseName)) {
return baseName
}
let counter = 1
while (existingNames.includes(`${baseName} ${counter}`)) {
counter++
}
return `${baseName} ${counter}`
2026-01-29 00:20:16 -08:00
}
2026-01-29 00:24:41 -08:00
function setScene(scene: THREE.Scene) {
threeScene = scene
2026-01-29 00:20:16 -08:00
}
function addObject(mesh: THREE.Mesh, name?: string): SceneObject {
2026-01-29 00:24:41 -08:00
if (!threeScene) {
2026-01-29 00:20:16 -08:00
throw new Error('Scene not initialized. Call setScene() first.')
}
const baseName = name ?? 'Object'
const objectName = generateName(baseName)
const sceneObject: SceneObject = {
id: generateId(),
name: objectName,
mesh,
visible: true,
locked: false,
}
2026-01-29 00:24:41 -08:00
threeScene.add(mesh)
2026-01-29 00:20:16 -08:00
objects.value = [...objects.value, sceneObject]
return sceneObject
}
function removeObject(id: string): boolean {
2026-01-29 01:06:48 -08:00
const obj = objects.value.find((o) => o.id === id)
if (!obj) return false
2026-01-29 00:20:16 -08:00
2026-01-29 00:24:41 -08:00
if (threeScene) {
threeScene.remove(obj.mesh)
2026-01-29 00:20:16 -08:00
}
obj.mesh.geometry.dispose()
if (obj.mesh.material instanceof THREE.Material) {
obj.mesh.material.dispose()
}
objects.value = objects.value.filter((o) => o.id !== id)
return true
}
function clearScene(): void {
const ids = objects.value.map((o) => o.id)
for (const id of ids) {
removeObject(id)
}
}
function getObject(id: string): SceneObject | undefined {
return objects.value.find((o) => o.id === id)
}
function setObjectVisible(id: string, visible: boolean): void {
objects.value = objects.value.map((obj) => {
if (obj.id === id) {
obj.mesh.visible = visible
return { ...obj, visible }
}
return obj
})
}
function setObjectLocked(id: string, locked: boolean): void {
2026-01-29 16:26:27 -08:00
objects.value = objects.value.map((obj) => (obj.id === id ? { ...obj, locked } : obj))
2026-01-29 00:20:16 -08:00
}
function renameObject(id: string, name: string): void {
2026-01-29 16:26:27 -08:00
objects.value = objects.value.map((obj) => (obj.id === id ? { ...obj, name } : obj))
2026-01-29 00:20:16 -08:00
}
2026-01-29 01:26:33 -08:00
function select(id: string): void {
const obj = objects.value.find((o) => o.id === id)
if (!obj || obj.locked) return
selectedIds.value = new Set([id])
}
2026-01-29 01:29:25 -08:00
function addToSelection(id: string): void {
const obj = objects.value.find((o) => o.id === id)
if (!obj || obj.locked) return
selectedIds.value = new Set([...selectedIds.value, id])
}
function toggleSelection(id: string): void {
const obj = objects.value.find((o) => o.id === id)
if (!obj || obj.locked) return
const newSet = new Set(selectedIds.value)
if (newSet.has(id)) {
newSet.delete(id)
} else {
newSet.add(id)
}
selectedIds.value = newSet
}
function selectAll(): void {
const unlocked = objects.value.filter((o) => !o.locked).map((o) => o.id)
selectedIds.value = new Set(unlocked)
}
2026-01-29 01:26:33 -08:00
function clearSelection(): void {
selectedIds.value = new Set()
}
2026-01-29 00:20:16 -08:00
const objectList = computed(() => objects.value)
const objectCount = computed(() => objects.value.length)
2026-01-29 16:26:27 -08:00
const selectedObjects = computed(() => objects.value.filter((o) => selectedIds.value.has(o.id)))
2026-01-29 00:20:16 -08:00
return {
2026-01-29 00:24:41 -08:00
// State
objects,
2026-01-29 01:26:33 -08:00
selectedIds,
2026-01-29 00:24:41 -08:00
// Getters
objectList,
objectCount,
2026-01-29 01:26:33 -08:00
selectedObjects,
2026-01-29 00:24:41 -08:00
// Actions
2026-01-29 00:20:16 -08:00
setScene,
addObject,
removeObject,
clearScene,
getObject,
setObjectVisible,
setObjectLocked,
renameObject,
2026-01-29 01:26:33 -08:00
select,
2026-01-29 01:29:25 -08:00
addToSelection,
toggleSelection,
selectAll,
2026-01-29 01:26:33 -08:00
clearSelection,
2026-01-29 00:20:16 -08:00
}
2026-01-29 00:24:41 -08:00
})