Idiotic instance panel...

This commit is contained in:
2026-07-08 13:01:48 +02:00
parent 56d3f0a20f
commit 8fec26f69f
8 changed files with 149 additions and 25 deletions

View File

@@ -57,6 +57,16 @@ input[type="checkbox"] {
padding-left: 0; padding-left: 0;
list-style-position: outside; list-style-position: outside;
} }
#instances li > a {
color: #dee2e6;
}
#instances li > a:hover {
color: var(--bs-nav-link-color);
text-decoration: underline;
}
#instance-panel {
z-index: 999;
}
/* Tooltips from https://codepen.io/pure-css/pen/bddggP */ /* Tooltips from https://codepen.io/pure-css/pen/bddggP */
span:after { span:after {
text-align: left; text-align: left;

View File

@@ -58,7 +58,7 @@
<script type="module" src="./index.js"></script> <script type="module" src="./index.js"></script>
</head> </head>
<body data-bs-theme="light" data-controller="menu modal" data-action="semantic-modal-show@document->modal#showSemanticModal"> <body data-bs-theme="light" data-controller="menu modal node" data-action="semantic-modal-show@document->modal#showSemanticModal">
<div id="toolbar" class="aton-toolbar-top w-100" <div id="toolbar" class="aton-toolbar-top w-100"
data-controller="toolbar clipper" data-clipper-enabled-value="false"> data-controller="toolbar clipper" data-clipper-enabled-value="false">
<div class="bg-dark-subtle position-absolute opacity-50 mt-1 ms-1 rounded"> <div class="bg-dark-subtle position-absolute opacity-50 mt-1 ms-1 rounded">
@@ -174,6 +174,21 @@
</div> </div>
</div> </div>
<!-- Instance panel (template it...)-->
<div class="position-absolute top-0 start-0 w-25 h-100 container instance-panel d-none bg-dark" data-node-target="panel">
<div class="container border-bottom border-secondary-subtle w-100">
<h1 class="fs-4 py-2 text-center"></h1>
</div>
<div class="row h-100">
<div class="col-5 border-end border-secondary-subtle h-100">
<ul id="properties"></ul>
</div>
<div class="col-7">
<p class="text-center fs-4 pt-4 mt-4">Sono inesorabilmente vuoto...</p>
</div>
</div>
</div>
<div class="aton-poweredby" > <div class="aton-poweredby" >
<a href="https://aton.ispc.cnr.it/site/" target="_blank"><img src="/res/aton-logo.png"></a> <a href="https://aton.ispc.cnr.it/site/" target="_blank"><img src="/res/aton-logo.png"></a>
</div> </div>

View File

@@ -14,6 +14,7 @@ export default class extends Controller {
async connect() { async connect() {
console.log('#menu controller connected'); console.log('#menu controller connected');
this.ontologyObject = await loadOntology(ontologyJsonPath); this.ontologyObject = await loadOntology(ontologyJsonPath);
AppState.ontology = this.ontologyObject;
} }
/** /**
* Open settings panel * Open settings panel
@@ -240,7 +241,6 @@ export default class extends Controller {
tab.appendChild(content); tab.appendChild(content);
const classesTab = content.querySelector('#classes'); const classesTab = content.querySelector('#classes');
const instancesTab = content.querySelector('#instances');
for (let domain of domains) { for (let domain of domains) {
const heading = document.createElement('span'); const heading = document.createElement('span');
@@ -266,17 +266,20 @@ export default class extends Controller {
classesTab.appendChild(domainList); classesTab.appendChild(domainList);
} }
const instancesTab = content.querySelector('#instances');
const navList = instancesTab.querySelector('ul');
const sortedItLabels = ontology.getInstanceLabels().map(l => l.it).sort(); const sortedItLabels = ontology.getInstanceLabels().map(l => l.it).sort();
for (let label of sortedItLabels) { for (let label of sortedItLabels) {
const instanceItem = document.createElement('div'); const instanceItem = document.createElement('li');
instanceItem.className = 'border-bottom ps-3 py-2 c-hand'; instanceItem.className = 'nav-item border-bottom ps-3 py-2 c-hand';
instanceItem.textContent = label; instanceItem.innerHTML = html`
instanceItem.setAttribute('data-controller', 'node'); <a class="nav-link">${label}</a>
instanceItem.setAttribute('data-node-id-value', label); `;
instanceItem.setAttribute('data-action', 'click->node#transition'); instanceItem.setAttribute('data-node-id-param', label);
instanceItem.setAttribute('data-action', 'click->node#open');
instancesTab.appendChild(instanceItem); navList.appendChild(instanceItem);
} }
} }
@@ -299,7 +302,9 @@ export default class extends Controller {
const content = html` const content = html`
<div class="tab-content" id="ontoTabsContent"> <div class="tab-content" id="ontoTabsContent">
<div class="tab-pane fade p-3 show active" id="classes" role="tabpanel" aria-labelledby="classes-tab" tabindex="0"></div> <div class="tab-pane fade p-3 show active" id="classes" role="tabpanel" aria-labelledby="classes-tab" tabindex="0"></div>
<div class="tab-pane fade" id="instances" role="tabpanel" aria-labelledby="instances-tab" tabindex="0"></div> <div class="tab-pane fade" id="instances" role="tabpanel" aria-labelledby="instances-tab" tabindex="0">
<ul class="nav flex-column"></ul>
</div>
</div> </div>
`; `;

View File

@@ -1,13 +1,16 @@
// Global ATON // Global ATON
import { Controller } from "@hotwired/stimulus" import { Controller } from "@hotwired/stimulus"
import AppState from "../state.js"; import AppState from "../state.js";
import { createSemanticNode } from "../scene.js";
import { Ontology } from "../ontology.js";
const html = String.raw;
/** /**
* Handle events for the clipper toolbar, * Handle events for the clipper toolbar,
* related to the clipping module * related to the clipping module
*/ */
export default class extends Controller { export default class extends Controller {
static targets = ['instance', 'node']; static targets = ['instance', 'node', 'panel'];
static values = { static values = {
id: String, id: String,
}; };
@@ -17,13 +20,75 @@ export default class extends Controller {
} }
/** /**
* Transition to this ATON node (request POV) * Transition to ATON node (request POV)
* if instance is architectural and
* open the instance panel
*/ */
transition() { open({ params: {id} }) {
const node = ATON.getSceneNode(this.idValue); const node = ATON.getSceneNode(id);
const normNode = AppState.normalizedNodes.find(n => n.id === id);
if (node) ATON.Nav.requestPOVbyNode(node); if (node) ATON.Nav.requestPOVbyNode(node);
if (normNode) this.addSemanticNode(normNode);
const ontology = new Ontology;
ontology.data = AppState.ontology;
const instance = ontology.getInstanceByLabel(id, 'it');
if (instance) this.openInstancePanel(instance);
} }
/**
* Attaches a semantic node to
* an existing scene node using
* its 3D model for the semantic shape
* @param {import("../state.js").NormalizedSceneNode} normNode
*/
addSemanticNode(normNode) {
if (!AppState.semanticNodes.get(normNode.label)) {
createSemanticNode(normNode.model, normNode.label);
AppState.semanticNodes.set(normNode.label, normNode.content);
}
} }
/**
* Populates an instance panel based on the
* instance data and opens it
* @param {Object} instance
*/
openInstancePanel(instance) {
const properties = instance.literalProperties;
// Populate left side properties column
/**
* @type {HTMLDivElement}
*/
const panel = this.panelTarget;
const list = panel.querySelector('#properties');
list.innerHTML = '';
panel.querySelector('h1').textContent = instance.label.it;
for (const prop of properties) {
// Include only DC properties (OK?)
const schema = prop.property.split(':')[0];
if (schema === 'dcterms' && Object.keys(prop.propertyLabel).length !== 0) {
const item = document.createElement('li');
item.className = 'py-2';
item.innerHTML = html`
<span class="text-primary-emphasis d-inline-block">${prop.propertyLabel.it ?? prop.propertyLabel.en}</span>
<br />
${prop.value.value}
`;
list.appendChild(item);
}
}
panel.classList.remove('d-none');
}
closeInstancePanel() {
}
}

View File

@@ -19,6 +19,31 @@ export class Ontology {
return this.#data.instancesById[id]; return this.#data.instancesById[id];
} }
/**
* @todo Revise loop approach...
* @param {String} label
* @param {String} lang The language identifier, either 'en' or 'it'
* @returns {Object} The instance object as per onto JSON... (document it!!)
*/
getInstanceByLabel(label, lang) {
if (!['en','it'].includes(lang)) {
throw new Error(`Invalid language string provided: '${lang}'`);
}
let instance = {};
for (const key of Object.keys(this.#data.instancesById)) {
const currentInstance = this.#data.instancesById[key];
const currentLabel = currentInstance.label[lang];
if (currentLabel === label) {
instance = currentInstance;
break;
}
}
return instance;
}
/** /**
* @returns {Array<{en: string, it: string}>} The bloody array of label objects * @returns {Array<{en: string, it: string}>} The bloody array of label objects
*/ */

View File

@@ -107,12 +107,6 @@ function loadNodes(nodes) {
AppState.clipping.boundingSphere = node.getBound(); AppState.clipping.boundingSphere = node.getBound();
} }
if (n.isSemantic) {
createSemanticNode(n.model, n.label);
AppState.semanticNodes.set(n.label, n.content);
//ATON.getSemanticRoot().attach(semNode);
}
AppState.nodes.push({ AppState.nodes.push({
id: n.label, id: n.label,
active: n.isInvisible ? false: true, active: n.isInvisible ? false: true,
@@ -130,14 +124,19 @@ function loadNodes(nodes) {
* @param {string} id The original scene node ID (3D object) * @param {string} id The original scene node ID (3D object)
* @returns {ATON.SceneNode} * @returns {ATON.SceneNode}
*/ */
function createSemanticNode(model, id) { export function createSemanticNode(model, id) {
// Default/highlight materials for semantic node // Default/highlight materials for semantic node
let matSemDef = new THREE.MeshStandardMaterial({ let matSemDef = new THREE.MeshStandardMaterial({
color: '#f7e6af', color: '#81eb81',
transparent: true, transparent: true,
opacity: 0.2, opacity: 0.4,
}); });
let matSemHL = ATON.MatHub.materials.semanticShapeHL; let matSemHL = new THREE.MeshStandardMaterial({
color: '#e9757b',
transparent: true,
opacity: 0.5,
});
const semNode = ATON.createSemanticNode(id) const semNode = ATON.createSemanticNode(id)
.setCloneOnLoadHit(false) .setCloneOnLoadHit(false)

View File

@@ -23,6 +23,10 @@ let AppState = {
* @type {NormalizedSceneNode[]} normalizedNodes * @type {NormalizedSceneNode[]} normalizedNodes
*/ */
normalizedNodes: [], normalizedNodes: [],
/**
* The loaded ontology object
*/
ontology: {},
treeNodes: {}, treeNodes: {},
semanticNodes: new Map, semanticNodes: new Map,
mainNodeId: null, mainNodeId: null,

View File

@@ -20,6 +20,7 @@
* @property {Boolean} isMain * @property {Boolean} isMain
* @property {Number|null} opacity * @property {Number|null} opacity
* @property {Boolean} active * @property {Boolean} active
* @property {String} content
*/ */
/** /**