diff --git a/config.js b/config.js index 0380c1e..f8209e0 100644 --- a/config.js +++ b/config.js @@ -48,9 +48,6 @@ export const config = { coords: [45.4401, 12.3408], nodes: { label: 'Teatro', - model: 'models/ssgp/Teatro_SSGP_Full_ConSottrazioni.glb', - opacity: 0.0, - isInvisible: true, children: [ /* { @@ -145,17 +142,17 @@ export const config = { label: 'Ballatoio', model: 'models/ssgp/Teatro_SSGP_Ballatoio.glb', }, - { - label: 'Spazio tecnico inferiore', - model: 'models/ssgp/Teatro_SSGP_Spazio_tecnico_inf.glb', - }, - ] + ], + }, + { + label: 'Spazio tecnico inferiore', + model: 'models/ssgp/Teatro_SSGP_Spazio_tecnico_inf.glb', }, ] }, { label: 'Orchestra', - color: 'rgb(187, 120, 218)', + color: 'rgb(195, 153, 235)', children: [ { label: 'Fossa orchestra', diff --git a/css/app.css b/css/app.css index 30d0ca5..4feb549 100644 --- a/css/app.css +++ b/css/app.css @@ -36,6 +36,10 @@ input[type="checkbox"] { background-image: url('/a/scaenae/assets/icons/section_z.png'); } +.tab-pane details > summary { + padding-left: 0; + list-style-position: outside; +} /* Tooltips from https://codepen.io/pure-css/pen/bddggP */ span:after { text-align: left; diff --git a/js/controllers/menu_controller.js b/js/controllers/menu_controller.js index 79b8344..1fc97bf 100644 --- a/js/controllers/menu_controller.js +++ b/js/controllers/menu_controller.js @@ -21,7 +21,7 @@ export default class extends Controller { ATON.UI.setSidePanelRight(); ATON.UI.showSidePanel({header: 'Menu'}); this.#buildMenuPanel(ATON.UI.elSidePanel); - this.#buildLayersMenu(AppState.normalizedNodes, this.layersTarget); + this.#buildLayersMenu(AppState.treeNodes, this.layersTarget); this.#buildOntologyMenu(await traverseOntology(ontologyJsonPath), this.ontologyTarget); } /** @@ -66,8 +66,6 @@ export default class extends Controller { `[data-menu-node-param="${child.id}"]` ); - console.debug(checkbox, child.id); - if (checkbox) checkbox.checked = status; if (child.children.length > 0) { @@ -95,27 +93,79 @@ export default class extends Controller { } /** * @todo Don't rebuild it every time, use caching, return a container with checkboxes - * @param {Array} nodes The normalized scene nodes (IDs and status) + * @param {Array} tree The normalized scene nodes tree * @param {HTMLElement} tab Tab content element */ - #buildLayersMenu(nodes, tab) { - for(let node of nodes) { - const menuItem = html` -
- - -
- `; - - // Awful? + #buildLayersMenu(tree, tab) { + for (const node of tree) { tab.appendChild( - domParser.parseFromString(menuItem, 'text/html').querySelector('div') + node.children.length > 0 + ? this.#createLayerGroup(node) + : this.#createLayerToggle(node) ); } } + /** + * @param {import("../state.js").NormalizedSceneNode} node + * @returns {HTMLDivElement} + */ + #createLayerToggle(node) { + const checkbox = html` +
+ + +
+ `; + + return domParser.parseFromString(checkbox, 'text/html').querySelector('div'); + } + /** + * @param {import("../state.js").NormalizedSceneNode} node + * @returns {HTMLDetailsElement} + */ + #createLayerGroup(node) { + const { trigger, collapseDiv } = this.#createNodeCollapse(node); + const wrapper = document.createElement('div'); + + collapseDiv.appendChild(this.#createLayerToggle(node)); + + for (const child of node.children) { + collapseDiv.appendChild( + child.children.length > 0 + ? this.#createLayerGroup(child) + : this.#createLayerToggle(child) + ); + } + + wrapper.appendChild(trigger); + wrapper.appendChild(collapseDiv); + wrapper.classList.add('m-0', 'p-0'); + + return wrapper; + } + + /** + * + * @param {import("../state.js").NormalizedSceneNode} node + * @returns {{trigger: HTMLButtonElement, collapseDiv: HTMLDivElement}} + */ + #createNodeCollapse(node) { + const cleanId = node.id.replace(/[\s\/\-]+/g, ''); + const trigger = document.createElement('button'); + trigger.className = 'btn btn-link p-0 fs-6 fw-bold text-decoration-none text-reset'; + trigger.setAttribute('data-bs-toggle', 'collapse'); + trigger.setAttribute('data-bs-target', `#group-${cleanId}`); + trigger.innerHTML = `${node.id}`; + + const collapseDiv = document.createElement('div'); + collapseDiv.className = 'collapse'; + collapseDiv.id = `group-${cleanId}`; + + return {trigger, collapseDiv}; + } /** * Temporary implementation to show domains only * @todo Don't rebuild it every time, use caching, return a container diff --git a/js/state.js b/js/state.js index aa5177e..230d4f0 100644 --- a/js/state.js +++ b/js/state.js @@ -23,6 +23,7 @@ let AppState = { * @type {NormalizedSceneNode[]} normalizedNodes */ normalizedNodes: [], + treeNodes: {}, mainNodeId: null, currentScene: null, sceneHasAudio: false, diff --git a/js/utils/clipping.js b/js/utils/clipping.js index 8ad7e1a..f9d6c9b 100644 --- a/js/utils/clipping.js +++ b/js/utils/clipping.js @@ -31,12 +31,14 @@ function createClippingPlaneMesh (boundingSphere) { * @param {String} axis */ function dragClipper (planeMesh, axis) { - const controls = new THREE.DragControls( - [planeMesh], + const controls = new THREE.TransformControls( ATON.Nav._camera, ATON._renderer.domElement, ); + controls.attach(planeMesh); + controls.setMode('translate'); + const startPosition = new THREE.Vector3(); // Only move along the selected axis (exlude the others) const excludedAxes = ['x', 'y', 'z'].filter(a => a != axis); diff --git a/js/utils/nodeUtils.js b/js/utils/nodeUtils.js index 518e923..46b24cc 100644 --- a/js/utils/nodeUtils.js +++ b/js/utils/nodeUtils.js @@ -66,11 +66,11 @@ function traverseTree(node, flatList, depth = 1, inheritedColor = null) { * Create a flat list of nodes from * the nested structure in config * @param {Array} nodes - * @returns {{flat: NormalizedSceneNode[], normNode: object}} A flat list of nodes + * @returns {{flat: NormalizedSceneNode[], tree: NormalizedSceneNode[]}} A flat list of nodes **/ export function normalizeNodes (nodes) { let flatList = []; const normNode = traverseTree(nodes, flatList); - return {flat: flatList, normNode}; + return {flat: flatList, tree: normNode.children}; } diff --git a/scenes/ssgp/index.js b/scenes/ssgp/index.js index 0c3f841..d8154da 100644 --- a/scenes/ssgp/index.js +++ b/scenes/ssgp/index.js @@ -9,5 +9,8 @@ initStimulus(); AppState.currentScene = 'ssgp'; const marker = config.markers.find(m => m.id === 'ssgp'); AppState.normalizedNodes = normalizeNodes(marker.nodes).flat; +AppState.treeNodes = normalizeNodes(marker.nodes).tree; + +//console.debug(AppState.treeNodes); openScene(marker, AppState.normalizedNodes);