Node group colors and toggling (draft)

This commit is contained in:
2026-05-14 16:32:06 +02:00
parent 589b551558
commit 87245233b7
7 changed files with 82 additions and 19 deletions

View File

@@ -66,6 +66,7 @@ export const config = {
}, },
{ {
label: 'Sala / Auditorium', label: 'Sala / Auditorium',
color: 'rgb(212, 96, 75)',
children: [ children: [
{ {
label: 'Peplano / Platea', label: 'Peplano / Platea',
@@ -108,6 +109,7 @@ export const config = {
}, },
{ {
label: 'Scena', label: 'Scena',
color: 'rgb(75, 100, 212)',
children: [ children: [
{ {
label: 'Palcoscenico', label: 'Palcoscenico',
@@ -129,6 +131,7 @@ export const config = {
}, },
{ {
label: 'Spazi tecnici', label: 'Spazi tecnici',
color: 'rgb(42, 139, 75)',
children: [ children: [
{ {
label: 'Spazio tecnico superiore', label: 'Spazio tecnico superiore',
@@ -152,6 +155,7 @@ export const config = {
}, },
{ {
label: 'Orchestra', label: 'Orchestra',
color: 'rgb(187, 120, 218)',
children: [ children: [
{ {
label: 'Fossa orchestra', label: 'Fossa orchestra',

View File

@@ -34,8 +34,46 @@ export default class extends Controller {
*/ */
const id = event?.params.node; const id = event?.params.node;
const status = event?.target?.checked; const status = event?.target?.checked;
const node = AppState.normalizedNodes.find(n => n.id === id);
if (node.children.length > 0) {
this.#toggleGroup(node, status);
this.#syncGroupCheckboxes(node, status, this.layersTarget);
} else {
ATON.getSceneNode(id).toggle(status); ATON.getSceneNode(id).toggle(status);
AppState.normalizedNodes.find(n => n.id === id).active = status; node.active = status;
}
}
/**
* Recursively toggle children in a nodes group
* @param {Object} groupNode
* @param {Boolean} status
*/
#toggleGroup(groupNode, status) {
for (const child of groupNode.children) {
if (child.model) {
ATON.getSceneNode(child.id).toggle(status);
child.active = status;
}
if (child.children.length > 0) {
this.#toggleGroup(child, status);
}
}
}
#syncGroupCheckboxes(groupNode, status, container) {
for (const child of groupNode.children) {
const checkbox = container.querySelector(
`[data-menu-node-param="${child.id}"]`
);
console.debug(checkbox, child.id);
if (checkbox) checkbox.checked = status;
if (child.children.length > 0) {
this.#syncGroupCheckboxes(child, status, container);
}
}
} }
/** /**
* Clone a <template> by id * Clone a <template> by id

View File

@@ -79,6 +79,11 @@ function loadNodes(nodes) {
node.load(n.model); node.load(n.model);
node.setRotation(...config.scene.initRotation); node.setRotation(...config.scene.initRotation);
node.setMaterial(new THREE.MeshPhongMaterial({
transparent: false,
color: n.color ?? '#fff',
}));
// Apply any transparency before attaching to scene // Apply any transparency before attaching to scene
if (n.opacity !== undefined && n.opacity !== null) { if (n.opacity !== undefined && n.opacity !== null) {
node.setMaterial(new THREE.MeshPhongMaterial({ node.setMaterial(new THREE.MeshPhongMaterial({

View File

@@ -28,41 +28,49 @@
* @param {Array} flatList * @param {Array} flatList
* @param {Number} depth * @param {Number} depth
*/ */
function traverse(node, flatList, depth = 1) { function traverseTree(node, flatList, depth = 1, inheritedColor = null) {
if (!node.label) { if (!node.label) {
console.error("Node missing label:", node); console.error("Node missing label:", node);
return; return;
} }
const color = node.color ?? inheritedColor;
const normNode = { const normNode = {
...node, ...node,
id: node.id ?? node.label, id: node.id ?? node.label,
isMain: node.isMain ?? false, isMain: node.isMain ?? false,
active: true, active: true,
color: color ?? null,
depth,
children: [],
}; };
if (node.model) { if (node.model) {
normNode.model = node.model; normNode.model = node.model;
} }
flatList.push({
...normNode, flatList.push(normNode);
depth
});
if (node.children && Array.isArray(node.children)) { if (node.children && Array.isArray(node.children)) {
for(let child of node.children) { for(const child of node.children) {
traverse(child, flatList, depth + 1); normNode.children.push(
traverseTree(child, flatList, depth + 1, color)
);
} }
} }
return normNode;
} }
/** /**
* Create a flat list of nodes from * Create a flat list of nodes from
* the nested structure in config * the nested structure in config
* @param {Array} nodes * @param {Array} nodes
* @returns {NormalizedSceneNode[]} A flat list of nodes * @returns {{flat: NormalizedSceneNode[], normNode: object}} A flat list of nodes
**/ **/
export function normalizeNodes (nodes) { export function normalizeNodes (nodes) {
let flatList = []; let flatList = [];
traverse(nodes, flatList); const normNode = traverseTree(nodes, flatList);
return flatList; return {flat: flatList, normNode};
} }

View File

@@ -46,6 +46,13 @@
<script type="text/javascript" src="../../vendor/three/examples/js/controls/DragControls.js"></script> <script type="text/javascript" src="../../vendor/three/examples/js/controls/DragControls.js"></script>
<script type="text/javascript" src="/dist/ATON.min.js"></script> <script type="text/javascript" src="/dist/ATON.min.js"></script>
<script type="importmap">
{
"imports": {
"@hotwired/stimulus": "../../vendor/@hotwired/stimulus/dist/stimulus.js"
}
}
</script>
<!-- Main js entry --> <!-- Main js entry -->
<script type="module" src="./index.js"></script> <script type="module" src="./index.js"></script>
</head> </head>

View File

@@ -2,13 +2,14 @@ import { openScene } from "../../js/scene.js";
import { config } from "../../config.js"; import { config } from "../../config.js";
import AppState from "../../js/state.js"; import AppState from "../../js/state.js";
import { normalizeNodes } from "../../js/utils/nodeUtils.js"; import { normalizeNodes } from "../../js/utils/nodeUtils.js";
import { initUI, pauseAudio } from "../../js/ui.js"; import { initStimulus } from "../../js/utils/stimulus.js";
initStimulus();
AppState.currentScene = 'salvador'; AppState.currentScene = 'salvador';
AppState.sceneHasAudio = true;
const marker = config.markers.find(m => m.id === 'salvador'); const marker = config.markers.find(m => m.id === 'salvador');
AppState.normalizedNodes = normalizeNodes(marker.nodes); AppState.normalizedNodes = normalizeNodes(marker.nodes).flat;
console.debug(AppState.normalizedNodes);
openScene(marker, AppState.normalizedNodes); openScene(marker, AppState.normalizedNodes);
initUI();
pauseAudio('[data-bs-dismiss="modal"]');

View File

@@ -8,6 +8,6 @@ initStimulus();
AppState.currentScene = 'ssgp'; AppState.currentScene = 'ssgp';
const marker = config.markers.find(m => m.id === 'ssgp'); const marker = config.markers.find(m => m.id === 'ssgp');
AppState.normalizedNodes = normalizeNodes(marker.nodes); AppState.normalizedNodes = normalizeNodes(marker.nodes).flat;
openScene(marker, AppState.normalizedNodes); openScene(marker, AppState.normalizedNodes);