Skip to content

46 fix elevation control panel bug #55

Merged
merged 9 commits into from
Oct 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
href="/src/MeasurementControl/measurementsPanel.css"
/>
<link rel="stylesheet" href="/src/AnnotationControl/annotationPanel.css" />
<link rel="stylesheet" href="src/AcceptedFiltering/threePanels.css" />
<link rel="stylesheet" href="src/Filter/filter.css" />
</head>

<body>
Expand Down
48 changes: 11 additions & 37 deletions src/AcceptedFiltering/threePanels.css → src/Filter/filter.css
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
/* ---------- Buttons (shared look) ---------- */
/* Reuse your accepted button style for all four */
#btnDoElevationControl,
#doAcceptedFiltering,
#btnTHU,
#btnTVU,
#btnTHUFilter {
#doAcceptedFiltering {
display: flex;
width: 100%;
margin: 6px 0 10px;
Expand All @@ -22,55 +19,34 @@
}

#btnDoElevationControl:hover,
#doAcceptedFiltering:hover,
#btnTHU:hover,
#btnTVU:hover,
#btnTHUFilter:hover {
#doAcceptedFiltering:hover {
background-color: #8f8f8f;
}

#btnDoElevationControl:active,
#doAcceptedFiltering:active,
#btnTHU:active,
#btnTVU:active,
#btnTHUFilter:active {
#doAcceptedFiltering:active {
transform: scale(0.97);
background-color: #a8a6a6;
}

/* Optional: “active mode” outline if you toggle a class via JS */
#btnDoElevationControl.active,
#doAcceptedFiltering.active,
#btnTHU.active,
#btnTVU.active,
#btnTHUFilter:active {
#doAcceptedFiltering.active {
outline: 2px solid #7ba8ff;
outline-offset: 1px;
}

/* THU/TVU side-by-side */
#thu_tvu_list .thu-tvu-row {
display: flex;
gap: 6px;
}
#thu_tvu_list .thu-tvu-row > button {
flex: 1 1 50%;
margin: 0;
}

/* ---------- Panels / moved containers ---------- */
/* Keep Potree’s moved subtrees neat and full-width inside our panels */
#elevation2_list [id='materials.elevation_container'],
#thu_tvu_list [id='materials.extra_container'] {
#elevation_list [id='materials.elevation_container'] {
width: 100%;
box-sizing: border-box;
padding: 6px 8px; /* small breathing room since we moved it out of Appearance */
}

/* Slight spacing inside our panel lists (under the button) */
#elevation2_list,
#accepted_list_host,
#thu_tvu_list {
#elevation_list,
#accepted_list_host {
display: block;
padding: 4px 0;
}
Expand Down Expand Up @@ -101,16 +77,14 @@

/* ---------- Accordions / headers (light touch) ---------- */
/* Don’t fight jQuery-UI’s theme. Just small spacing adjustments. */
#menu_elevation2 + .pv-menu-list,
#menu_accepted + .pv-menu-list,
#menu_thu_tvu + .pv-menu-list {
#menu_elevation + .pv-menu-list,
#menu_accepted + .pv-menu-list {
padding-top: 6px;
}

/* Optional: header label color alignment with dark UI */
#menu_elevation2 span,
#menu_accepted span,
#menu_thu_tvu span {
#menu_elevation span,
#menu_accepted span {
color: #e6e6e6;
font-weight: 600;
letter-spacing: 0.2px;
Expand Down
28 changes: 16 additions & 12 deletions src/AcceptedFiltering/threePanels.js → src/Filter/filter.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// Three Potree sidebar sections with buttons + panel bodies:
// • Elevation → moves #materials.elevation_container into our Elevation body
// • Accepted → custom UI fully defined here (no external module)

/** Two Potree sidebar sections with buttons + panel bodies:
*
* • Elevation → moves #materials.elevation_container into our Elevation body
* Used for controlling the elevation gradient with a slider and altering between different gradient schemes
* • Accepted → custom UI fully defined here (no external module)
* Used for indicating which points/surveys are accepted as the seabed and which are not
*/
const byId = (id) => document.getElementById(id)

/**
Expand Down Expand Up @@ -88,7 +91,7 @@ function ensurePanelScaffold(listId) {
* @param {'elevation'|'accepted'} key
*/
function showOnly(key) {
const elevBody = byId('elevation2_list')?.querySelector('.panel-body')
const elevBody = byId('elevation_list')?.querySelector('.panel-body')
const accBody = byId('accepted_list_host')?.querySelector('.panel-body')

if (elevBody) elevBody.style.display = key === 'elevation' ? '' : 'none'
Expand Down Expand Up @@ -150,7 +153,7 @@ function selectCloudNode(hooks) {
* @param {number} pollEvery
* @returns {Promise<HTMLElement|null>}
*/
async function waitForOrPoll(id, softMs = 1400, pollEvery = 120) {
async function waitForOrPoll(id, softMs = 1400, pollEvery = 10) {
const start = performance.now()
while (performance.now() - start < softMs) {
const el = byId(id)
Expand All @@ -168,17 +171,17 @@ function createElevationPanel() {
insertSection({
headerId: 'menu_elevation',
headerText: 'Elevation Control',
listId: 'elevation2_list'
listId: 'elevation_list'
})
ensurePanelScaffold('elevation2_list')
ensurePanelScaffold('elevation_list')
}

/**
* Ensures the Elevation action button is present and wired.
* @param {{onActivateElevation?:Function}} hooks
*/
function ensureElevationButton(hooks) {
const { btns } = ensurePanelScaffold('elevation2_list')
const { btns } = ensurePanelScaffold('elevation_list')
if (!btns || byId('btnDoElevationControl')) return

const btn = document.createElement('button')
Expand Down Expand Up @@ -219,7 +222,7 @@ function setUpElevationSlider(hooks) {
* @returns {boolean} true if moved or already in place
*/
function moveElevationContainer(hooks) {
const { body } = ensurePanelScaffold('elevation2_list')
const { body } = ensurePanelScaffold('elevation_list')
const src = byId('materials.elevation_container')
if (!body || !src) return false

Expand All @@ -228,6 +231,7 @@ function moveElevationContainer(hooks) {
setUpElevationSlider(hooks)
accordionRefresh()
}
src.style.removeProperty('display')
return true
}

Expand Down Expand Up @@ -412,7 +416,7 @@ function attachSelfHealing(activeGetter, hooks) {
const mode = activeGetter()
if (mode === 'elevation') {
const src = byId('materials.elevation_container')
const { body } = ensurePanelScaffold('elevation2_list')
const { body } = ensurePanelScaffold('elevation_list')
if (src && body && src.parentNode !== body) moveElevationContainer(hooks)
}
})
Expand All @@ -426,7 +430,7 @@ function attachSelfHealing(activeGetter, hooks) {
* @param {object} viewer Potree viewer (not used directly here but available to hooks)
* @param {{onActivateElevation?:Function, onActivateAccepted?:Function, selectCloudOnce?:Function, onElevationRangeChange?:Function}} hooks
*/
export function initThreePanels(viewer, hooks = {}) {
export function initFilterPanels(viewer, hooks = {}) {
// Build sections
initElevationControls(hooks)
initAcceptedControlsInline(hooks)
Expand Down
25 changes: 18 additions & 7 deletions src/potreeViewer.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { initAnnotationsPanel } from './AnnotationControl/annotationPanel.js'
import { initMeasurementsPanel } from './MeasurementControl/measurementsPanel.js'
import { initMiniMap } from './MiniMap/miniMap.js'
import {
initThreePanels,
toggleAcceptedLegend
} from './AcceptedFiltering/threePanels.js'
import { initFilterPanels, toggleAcceptedLegend } from './Filter/filter.js'
import { ecef } from './config.js'

/**
Expand Down Expand Up @@ -90,7 +87,7 @@ export async function createPotreeViewer(
})
}

initThreePanels(viewer, {
initFilterPanels(viewer, {
onActivateElevation: () => {
const $ = window.jQuery || window.$
const slider = $ ? $('#sldHeightRange') : null
Expand Down Expand Up @@ -208,13 +205,23 @@ function overrideShaderForGradient(pc) {
}
}

// Prevent overlapping scroll freezing when activating filters quickly
let suppressionActive = false

/**
* Freeze all scrollable ancestors of a given root during an action (e.g., jsTree select)
* Need this so that when Elevation control or Accepted filter is activated the sidebar doesn't scroll down to the Scene panel
*
* @param {*} action
*/
function suppressSidebarAutoScroll(action, holdMs = 350) {
// --- Re-entrancy guard ---
if (suppressionActive) {
action()
return
}
suppressionActive = true

// anchor on the tree root; fall back to the menu if not found
const treeRoot =
document.querySelector('#scene_objects') ||
Expand All @@ -231,8 +238,10 @@ function suppressSidebarAutoScroll(action, holdMs = 350) {
if (canScroll) scrollers.push(el)
el = el.parentElement
}

if (!scrollers.length) {
action()
suppressionActive = false
return
}

Expand Down Expand Up @@ -264,20 +273,19 @@ function suppressSidebarAutoScroll(action, holdMs = 350) {

const origFocus = HTMLElement.prototype.focus
HTMLElement.prototype.focus = function (opts) {
// force preventScroll behavior even if caller didn't ask
try {
origFocus.call(this, { ...(opts || {}), preventScroll: true })
} catch {
origFocus.call(this)
}
}

try {
action()
} finally {
const until = performance.now() + holdMs

const restoreLoop = () => {
// keep snapping until the selection animations/handlers settle
states.forEach(({ el, top, left }) => {
el.scrollTop = top
el.scrollLeft = left
Expand All @@ -300,8 +308,11 @@ function suppressSidebarAutoScroll(action, holdMs = 350) {
if (h) el.removeEventListener('scroll', h)
el.style.overflow = overflow
})
// --- Release the guard ---
suppressionActive = false
}
}

requestAnimationFrame(restoreLoop)
}
}
Expand Down