diff --git a/assets/icons/section_x.png b/assets/icons/section_x.png index 7fba521..0d6700d 100644 Binary files a/assets/icons/section_x.png and b/assets/icons/section_x.png differ diff --git a/assets/icons/section_z.png b/assets/icons/section_z.png index 0d6700d..7fba521 100644 Binary files a/assets/icons/section_z.png and b/assets/icons/section_z.png differ diff --git a/config.js b/config.js index d8e9402..85fa927 100644 --- a/config.js +++ b/config.js @@ -16,11 +16,12 @@ const theater2Popup = ` export const config = { scene : { - initialExposure: 0.7, + initialExposure: 0.85, autoLP: false, shadows: false, initLightDir: [0.2,-0.3,-0.7], initRotation: [0, 1.5, 0], + ambientOcclusion: false, }, menu : { //audioBtn1 diff --git a/css/app.css b/css/app.css index be5ad61..30d0ca5 100644 --- a/css/app.css +++ b/css/app.css @@ -1,26 +1,114 @@ body { height: 100vh; } -.c-hand { - cursor: pointer; -} + input[type="checkbox"] { cursor: pointer; } + +.c-hand { + cursor: pointer; +} + +.w-max-content { + width: max-content; +} + #clipper-bar .btn { background-repeat: no-repeat; background-position: center; background-size: 80%; } + #map { height: 100%; } + #clipX { background-image: url('/a/scaenae/assets/icons/section_x.png'); } + #clipY { background-image: url('/a/scaenae/assets/icons/section_y.png'); } + #clipZ { background-image: url('/a/scaenae/assets/icons/section_z.png'); +} + +/* Tooltips from https://codepen.io/pure-css/pen/bddggP */ +span:after { + text-align: left; + white-space: normal; +} + +span:focus { + outline: none; +} + +.app-tooltip { + cursor: help; + position: relative; +} + +/*== common styles for both parts of app-tooltip tip ==*/ +.app-tooltip::before, +.app-tooltip::after { + left: 50%; + opacity: 0; + position: absolute; + z-index: -100; +} + +.app-tooltip:hover::before, +.app-tooltip:focus::before, +.app-tooltip:hover::after, +.app-tooltip:focus::after { + opacity: 1; + transform: scale(1) translateY(0); + z-index: 100; +} + +/*== pointer tip ==*/ +.app-tooltip::before { + border-style: solid; + border-width: 1em 0.75em 0 0.75em; + border-color: #3E474F transparent transparent transparent; + bottom: 100%; + content: ""; + margin-left: -0.5em; + transition: all .65s cubic-bezier(.84,-0.18,.31,1.26), opacity .65s .5s; + transform: scale(.6) translateY(-90%); +} + +.app-tooltip:hover::before, +.app-tooltip:focus::before { + transition: all .65s cubic-bezier(.84,-0.18,.31,1.26) .2s; +} + +/*== speech bubble ==*/ +.app-tooltip::after { + background: #3E474F; + border-radius: .25em; + bottom: 180%; + color: #EDEFF0; + content: attr(data-tip); + margin-left: -4.75em; + padding: 1em; + transition: all .65s cubic-bezier(.84,-0.18,.31,1.26) .2s; + transform: scale(.6) translateY(50%); + width: 17.5em; +} + +.app-tooltip:hover::after, +.app-tooltip:focus::after { + transition: all .65s cubic-bezier(.84,-0.18,.31,1.26); +} + +@media (max-width: 760px) { + .app-tooltip::after { + font-size: .75em; + margin-left: -5em; + width: 10em; + } } \ No newline at end of file diff --git a/js/controllers/menu_controller.js b/js/controllers/menu_controller.js index 7344f77..3ac3f0e 100644 --- a/js/controllers/menu_controller.js +++ b/js/controllers/menu_controller.js @@ -1,5 +1,5 @@ // Global ATON -import { Controller } from "@hotwired/stimulus" +import { Controller } from "@hotwired/stimulus"; import AppState from "../state.js"; import { traverseOntology } from "../ontology.js"; @@ -16,7 +16,6 @@ export default class extends Controller { } /** * Open settings panel - * @param {Event} event */ async toggleMenu() { ATON.UI.setSidePanelRight(); @@ -33,8 +32,8 @@ export default class extends Controller { * The node's id * @type {string} */ - const id = event.params.node; - const status = event.target.checked; + const id = event?.params.node; + const status = event?.target?.checked; ATON.getSceneNode(id).toggle(status); AppState.normalizedNodes.find(n => n.id === id).active = status; } @@ -44,7 +43,7 @@ export default class extends Controller { * @returns {DocumentFragment} */ #cloneTemplate(id) { - return document.getElementById(id).content.cloneNode(true); + return document.getElementById(id)?.content?.cloneNode(true); } /** * Create the left-side settings panel @@ -58,7 +57,7 @@ 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} nodes The normalized scene nodes (IDs and status) * @param {HTMLElement} tab Tab content element */ #buildLayersMenu(nodes, tab) { diff --git a/js/scene.js b/js/scene.js index 4801a87..4d6f042 100644 --- a/js/scene.js +++ b/js/scene.js @@ -64,8 +64,8 @@ export function openScene (marker, nodes) { ATON.setAutoLP(config.scene.autoLP); AppState.lightProbe = config.scene.autoLP; - toggleAmbientOcclusion(true); - AppState.ambientOcclusion = true; + toggleAmbientOcclusion(config.scene.ambientOcclusion); + AppState.ambientOcclusion = config.scene.ambientOcclusion; AppState.root = ATON.getRootScene(); } diff --git a/js/utils/environment.js b/js/utils/environment.js index 8960133..7c34c20 100644 --- a/js/utils/environment.js +++ b/js/utils/environment.js @@ -1,3 +1,5 @@ +/** @import { Vector3 } from 'three' */ + // Global ATON and THREE import AppState from "../state.js"; @@ -7,8 +9,7 @@ import AppState from "../state.js"; */ /** - * - * @param {THREE.Vector3} vector - An object with x,y,z coordinates + * @param {Vector3} vector - An object with x,y,z coordinates */ export function changeLightDirection(vector) { ATON.setMainLightDirection(vector); @@ -26,14 +27,12 @@ export function toggleAmbientOcclusion(isEnabled) { * Slider to change light direction, based on ATON.UI * @param {String} direction - The axis direction, one of 'x','y','z' * @param {String} label - The slider label - * @param {Array} range - The slider's range + * @param {[number, number]} range - The slider's range * @param {Number} step - The slider's step */ export function createLightSlider(direction, label, range, step) { const currentVal = AppState.lightDirection[direction]; - console.debug(currentVal); - const lightSlider = ATON.UI.createSlider({ range, label, @@ -48,19 +47,20 @@ export function createLightSlider(direction, label, range, step) { }, }); - lightSlider.classList.add('ms-4'); - lightSlider.querySelector('input').step = step; + lightSlider.classList.add('ms-1'); + const input = lightSlider.querySelector('input'); + if (input) input.step = step; return lightSlider; } /** * Slider to change the env exposure level, based on ATON.UI * @param {String} label - The slider label - * @param {Array} range - The slider's range + * @param {[number, number]} range - The slider's range * @param {Number} step - The slider's step */ export function createExposureSlider(label, range, step = 0.05) { - const currentVal = AppState.exposure; + const currentVal = String(AppState.exposure); const exposureSlider = ATON.UI.createSlider({ range, @@ -74,8 +74,9 @@ export function createExposureSlider(label, range, step = 0.05) { }, }); - exposureSlider.classList.add('ms-4'); - exposureSlider.querySelector('input').step = step; + exposureSlider.classList.add('ms-1'); + let input = exposureSlider.querySelector('input'); + if (input) input.step = step; return exposureSlider; } \ No newline at end of file diff --git a/scenes/ssgp/index.html b/scenes/ssgp/index.html index 07356db..9952b8c 100644 --- a/scenes/ssgp/index.html +++ b/scenes/ssgp/index.html @@ -98,30 +98,36 @@