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