diff --git a/src/AnnotationControl/annotationPanel.css b/src/AnnotationControl/annotationPanel.css index 13a3b2e..f250dba 100644 --- a/src/AnnotationControl/annotationPanel.css +++ b/src/AnnotationControl/annotationPanel.css @@ -27,8 +27,8 @@ img.button-icon[src$='/annotation.svg'] { padding: 8px; border-radius: 4px; border: 1px solid #404a50; - background: #2f383d; - color: #cfd5d8; + background: #636262; + color: #636262; font-family: inherit; font-size: 12px; line-height: 1.3; @@ -44,10 +44,6 @@ img.button-icon[src$='/annotation.svg'] { margin-top: 6px; } -.annotation-add-button { - margin: 10px 0; -} - .annotation-empty { opacity: 0.6; padding: 10px; @@ -350,37 +346,68 @@ img.button-icon[src$='/annotation.svg'] { flex: 0 0 18px; } +.pv-menu-list_annotations-panel { + display: flex; + flex-direction: column; + align-items: center; + overflow: hidden; +} + /* Add button */ .annotation-add-button { - background: linear-gradient(180deg, #f6f6f6 0%, #e9e9e9 100%); - color: #222; - padding: 8px 16px; - min-width: 140px; - height: 38px; display: block; - margin: 12px auto; - border-radius: 6px; + width: 80%; + margin: 20px 0 10px; + padding: 10px 10px; font-size: 13px; - font-weight: 700; - box-shadow: 0 1px 0 rgba(255, 255, 255, 0.6) inset; - border: 1px solid #cfcfcf; + font-weight: 500; + background-color: #636262; + color: #ffffff; + border: 1px solid #555; + border-radius: 4px; cursor: pointer; - text-align: center; -} -.annotation-add-button .add-label { - color: #222; - font-weight: 700; + transition: + background-color 0.2s ease, + transform 0.1s ease; } + .annotation-add-button:hover { - background: linear-gradient(180deg, #f3f3f3 0%, #e2e2e2 100%); - border-color: #bfbfbf; + background-color: #8f8f8f; } -.annotation-add-button:active { - transform: translateY(1px); - background: linear-gradient(180deg, #e9e9e9 0%, #dbdbdb 100%); - box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.06); + +#labelToggleContainer { + margin: 8px 0 6px; + padding-left: 4px; } -.annotation-add-button:focus { - outline: 2px solid rgba(100, 100, 100, 0.12); - outline-offset: 2px; +#labelToggleContainer .labels-legend { + font-size: 13px; + color: #ddd; + margin-bottom: 4px; +} + +.toggle-group { + display: flex; + width: 265px; + border: 1px solid black; + border-radius: 4px; + overflow: hidden; +} +.toggle-group button { + flex: 1; + padding: 6px 15px; + background: #a7a9aa; + color: #3d3c3c; + border: 0; + cursor: pointer; + font-weight: 300; + transition: background 0.2s; +} + +.toggle-group button:not(:last-child) { + border-right: 1px solid #555; +} + +.toggle-group button.active { + background: #c7c9ca; + color: #000; } diff --git a/src/AnnotationControl/annotationPanel.js b/src/AnnotationControl/annotationPanel.js index cffe313..684094d 100644 --- a/src/AnnotationControl/annotationPanel.js +++ b/src/AnnotationControl/annotationPanel.js @@ -31,7 +31,7 @@ export function initAnnotationsPanel(viewer) { header.appendChild(headerSpan) const panel = document.createElement('div') - panel.className = 'pv-menu-list annotations-panel' + panel.className = 'pv-menu-list_annotations-panel' const listContainerDiv = document.createElement('div') listContainerDiv.id = 'annotations_list' @@ -68,6 +68,47 @@ export function initAnnotationsPanel(viewer) { targetContainer = panel.querySelector('#annotations_list') } } + + // --- Add Show/Hide labels toggle group --- + const panelEl = + targetContainer.closest('.pv-menu-list_annotations-panel') || + targetContainer.parentElement + + if (!panelEl.querySelector('#labelToggleContainer')) { + const controls = document.createElement('div') + controls.id = 'labelToggleContainer' + controls.innerHTML = ` +

Show/Hide saved locations

+
+ + +
+ ` + // Insert before list of annotations + panelEl.insertBefore(controls, targetContainer) + + // show/hide all annotations + const setLabelsVisible = (visible) => { + const cont = document.getElementById('potree_annotation_container') + if (cont) cont.style.display = visible ? '' : 'none' + } + + const showBtn = controls.querySelector('#showLabelsBtn') + const hideBtn = controls.querySelector('#hideLabelsBtn') + + showBtn.addEventListener('click', () => { + setLabelsVisible(true) + showBtn.classList.add('active') + hideBtn.classList.remove('active') + }) + + hideBtn.addEventListener('click', () => { + setLabelsVisible(false) + hideBtn.classList.add('active') + showBtn.classList.remove('active') + }) + } + if (!targetContainer) { console.warn( 'Annotations list container not found and dynamic injection failed' @@ -947,6 +988,18 @@ export function initAnnotationsPanel(viewer) { annotationHeader.nextElementSibling.style.display = '' } } + // Ensure annotation labels are shown when starting to add a location. + // The "Show" button will be shown as active and reveals Potree's annotation container. + try { + const showBtn = document.getElementById('showLabelsBtn') + const hideBtn = document.getElementById('hideLabelsBtn') + if (showBtn) showBtn.classList.add('active') + if (hideBtn) hideBtn.classList.remove('active') + const cont = document.getElementById('potree_annotation_container') + if (cont) cont.style.display = '' + } catch (e) { + console.warn('Could not enable annotation labels on add', e) + } // Capture current camera view (position) at the moment the user clicks Add let camPos = null try { diff --git a/src/Filter/filter.css b/src/Filter/filter.css index be09a06..e3a377d 100644 --- a/src/Filter/filter.css +++ b/src/Filter/filter.css @@ -4,6 +4,7 @@ #doAcceptedFiltering { display: flex; width: 100%; + justify-content: center; margin: 6px 0 10px; padding: 10px 10px; font-size: 13px; @@ -102,22 +103,32 @@ align-items: center; margin: 3px 0; font-size: 13px; - color: #ddd; /* visible text */ + color: #ddd; } -/* Color boxes */ +/* Color boxes → now circles */ #accepted_legend .legend-color { - width: 16px; - height: 16px; - border: 1px solid #777; - margin-right: 8px; - border-radius: 2px; + width: 20px; + height: 8px; + border-radius: 4px; /* pill shape */ + margin-left: 6px; } #accepted_legend .legend-color.accepted { background-color: #fff; + border: #0008; + box-shadow: 0 0 4px 1px #fff8; } #accepted_legend .legend-color.not-accepted { background-color: #000; + box-shadow: 0 0 4px 1px #fff8; +} + +#gradient_repeat_option fieldset { + margin: 15px 0px 12px 0px !important; +} + +#gradient_repeat_option fieldset legend { + margin: 0px 0px 5px 0px !important; } diff --git a/src/Filter/filter.js b/src/Filter/filter.js index 40c5d56..981b6a8 100644 --- a/src/Filter/filter.js +++ b/src/Filter/filter.js @@ -189,6 +189,10 @@ function ensureElevationButton(hooks) { btn.type = 'button' btn.textContent = 'Activate elevation control' btn.addEventListener('click', () => { + const elevBtn = byId('btnDoElevationControl') + const accBtn = byId('doAcceptedFiltering') + if (elevBtn) elevBtn.style.display = 'none' + if (accBtn) accBtn.style.display = '' switchMode('elevation', hooks?.onActivateElevation, hooks) }) btns.appendChild(btn) @@ -283,6 +287,10 @@ function ensureAcceptedButton(hooks) { btn.type = 'button' btn.textContent = 'Activate accepted filter' btn.addEventListener('click', () => { + const accBtn = byId('doAcceptedFiltering') + const elevBtn = byId('btnDoElevationControl') + if (accBtn) accBtn.style.display = 'none' + if (elevBtn) elevBtn.style.display = '' switchMode('accepted', hooks?.onActivateAccepted, hooks) }) btns.appendChild(btn) @@ -303,12 +311,12 @@ function ensureAcceptedLegend() { legend.style.display = 'none' legend.innerHTML = `
+ Accepted points displayed as:
- Accepted points
+ Not accepted points displayed as:
- Not accepted points
` list.appendChild(legend) diff --git a/src/MeasurementControl/measurementsPanel.css b/src/MeasurementControl/measurementsPanel.css index 96f735f..a848631 100644 --- a/src/MeasurementControl/measurementsPanel.css +++ b/src/MeasurementControl/measurementsPanel.css @@ -271,3 +271,32 @@ border-top: 1px solid #303a3f; padding-top: 10px; } + +.tool-with-label { + display: flex; + flex-direction: row; + align-items: center; + margin-top: 10px; + cursor: pointer; + border-radius: 4px; + padding: 4px; +} +.tool-with-label:hover { + box-shadow: 0 0 5px #fff8; +} + +.tool-with-label:hover img { + filter: brightness(1.7); +} + +.tool-with-label:hover .tool-label { + color: #fff; +} + +.tool-label { + font-size: 14px; + margin-top: 2px; + margin-left: 4px; + color: #aaa; + pointer-events: none; +} diff --git a/src/MeasurementControl/measurementsPanel.js b/src/MeasurementControl/measurementsPanel.js index 6b70ac2..3021108 100644 --- a/src/MeasurementControl/measurementsPanel.js +++ b/src/MeasurementControl/measurementsPanel.js @@ -29,7 +29,7 @@ export function initMeasurementsPanel(viewer) { panel.appendChild(toolsHostDiv) panel.appendChild(listContainerDiv) // Insert before filters/tools if possible, else append at end - const tools = document.getElementById('menu_tools') + const tools = document.getElementById('menu_appearance') if (tools) { menu.insertBefore(panel, tools) menu.insertBefore(header, panel) @@ -650,6 +650,45 @@ export function initMeasurementsPanel(viewer) { toolsHost.appendChild(existingTools) } + // After tools are moved into `toolsHost` + const toolDescriptions = { + 'angle.png': 'Measure angle', + 'point.svg': 'Inspect point', + 'distance.svg': 'Measure distance', + 'height.svg': 'Measure height', + 'circle.svg': 'Circle', + 'azimuth.svg': 'Azimuth', + 'area.svg': 'Area', + 'volume.svg': 'Volume', + 'sphere_distances.svg': 'Sphere volume', + 'profile.svg': '2D height profile', + 'reset_tools.svg': 'Remove all' + } + + const toolIcons = existingTools.querySelectorAll('img') + toolIcons.forEach((img) => { + const src = img.getAttribute('src') + const file = src.split('/').pop() // extract icon name + const baseName = file.replace(/\.[^/.]+$/, '') + + if (toolDescriptions[file]) { + const wrapper = document.createElement('div') + wrapper.className = 'tool-with-label' + wrapper.id = `tool-wrapper-${baseName}` + + wrapper.addEventListener('click', () => img.click()) + + img.parentNode.insertBefore(wrapper, img) + wrapper.appendChild(img) + + const label = document.createElement('span') + label.className = 'tool-label' + label.textContent = toolDescriptions[file] + label.id = `label-${file.replace(/\.[^/.]+$/, '')}` + wrapper.appendChild(label) + } + }) + // Move measurement options UI into our tools host if (toolsHost) { const measOptions = document.getElementById('measurement_options_show') diff --git a/src/potreeViewer.js b/src/potreeViewer.js index 5baef9a..b78539d 100644 --- a/src/potreeViewer.js +++ b/src/potreeViewer.js @@ -56,10 +56,7 @@ export async function createPotreeViewer( viewer.loadGUI(() => { viewer.setLanguage('en') - $('#menu_appearance').next().show() - $('#menu_tools').next().show() - $('#menu_scene').next().show() - $('#menu_filters').next().show() + $('#menu_filters').remove() viewer.toggleSidebar() // Store the last used elevation gradient