From e6a5081513354d18aa62718db5588e0eeefa0325 Mon Sep 17 00:00:00 2001 From: AdrianSolberg Date: Sat, 25 Oct 2025 16:11:20 +0200 Subject: [PATCH 01/10] feat(#40): load multiple point clouds simultaneously --- src/config.js | 18 +++++++++++++++++- src/main.js | 4 ++-- src/potreeViewer.js | 36 ++++++++++++++++++++++-------------- 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/config.js b/src/config.js index 09c17aa..761f8fd 100644 --- a/src/config.js +++ b/src/config.js @@ -1,4 +1,20 @@ -export const POTREE_POINTCLOUD_URL = '/pointclouds/data_converted/metadata.json' +export const POTREE_POINTCLOUD_URLS = [ + '/pointclouds/cell_1/metadata.json', + '/pointclouds/cell_2/metadata.json', + '/pointclouds/cell_3/metadata.json', + '/pointclouds/cell_4/metadata.json', + '/pointclouds/cell_5/metadata.json', + '/pointclouds/cell_6/metadata.json', + '/pointclouds/cell_7/metadata.json', + '/pointclouds/cell_8/metadata.json', + '/pointclouds/cell_9/metadata.json', + '/pointclouds/cell_10/metadata.json', + '/pointclouds/cell_11/metadata.json', + '/pointclouds/cell_12/metadata.json', + '/pointclouds/cell_13/metadata.json', + '/pointclouds/cell_14/metadata.json', + '/pointclouds/cell_15/metadata.json' +] export const POTREE_SETTINGS = { edl: true, diff --git a/src/main.js b/src/main.js index e11565e..87aaafe 100644 --- a/src/main.js +++ b/src/main.js @@ -1,4 +1,4 @@ -import { POTREE_POINTCLOUD_URL, POTREE_SETTINGS } from './config.js' +import { POTREE_POINTCLOUD_URLS, POTREE_SETTINGS } from './config.js' import { createCesiumViewer, loadCountryBorders } from './cesiumViewer.js' import { createPotreeViewer } from './potreeViewer.js' import { syncCameras } from './cameraSync.js' @@ -18,7 +18,7 @@ async function init() { window.potreeViewer = await createPotreeViewer( 'potree_render_area', - POTREE_POINTCLOUD_URL, + POTREE_POINTCLOUD_URLS, POTREE_SETTINGS ) diff --git a/src/potreeViewer.js b/src/potreeViewer.js index 3d80cf0..1e94159 100644 --- a/src/potreeViewer.js +++ b/src/potreeViewer.js @@ -10,11 +10,15 @@ import { ecef } from './config.js' * Initializes the Potree viewer used to visualize the point cloud. * * @param containerId - id of the container - * @param pointcloudUrl - url path to the point cloud + * @param pointcloudUrls - url paths to the point clouds * @param settings - other settings * @returns Potree viewer */ -export async function createPotreeViewer(containerId, pointcloudUrl, settings) { +export async function createPotreeViewer( + containerId, + pointcloudUrls, + settings +) { const viewer = new Potree.Viewer(document.getElementById(containerId), { useDefaultRenderLoop: false }) @@ -66,9 +70,22 @@ export async function createPotreeViewer(containerId, pointcloudUrl, settings) { initAnnotationsPanel(viewer) }) - const e = await Potree.loadPointCloud(pointcloudUrl) - const pc = e.pointcloud - viewer.scene.addPointCloud(pc) + for (const url of pointcloudUrls) { + const e = await Potree.loadPointCloud(url) + const pc = e.pointcloud + viewer.scene.addPointCloud(pc) + + pc.material.pointSizeType = Potree.PointSizeType.ADAPTIVE + pc.material.shape = Potree.PointShape.CIRCLE + overrideShaderForGradient(pc) + + //The default activeAttributeName is set to elevation and the color gradient to VIRIDIS for good visualization + pc.material.elevationRange = [-10000, 0] + pc.material.activeAttributeName = 'elevation' + pc.material.gradient = Potree.Gradients['VIRIDIS'] + + e.pointcloud.projection = ecef + } // Change name of default background from 'None' to 'Globe"' $('#background_options_none') @@ -78,15 +95,6 @@ export async function createPotreeViewer(containerId, pointcloudUrl, settings) { viewer.setBackground('globe') - pc.material.pointSizeType = Potree.PointSizeType.ADAPTIVE - pc.material.shape = Potree.PointShape.CIRCLE - overrideShaderForGradient(pc) - - //The default activeAttributeName is set to elevation and the color gradient to VIRIDIS for good visualization - pc.material.elevationRange = [-10000, 0] - pc.material.activeAttributeName = 'elevation' - pc.material.gradient = Potree.Gradients['VIRIDIS'] - // Initialize camera position and target point (manually chosen) viewer.scene.view.setView( [3961574.044, 1494736.334, 8348318.575], // Initial camera position From a986ba5dfce529378053e522a61843e45bd01f75 Mon Sep 17 00:00:00 2001 From: AdrianSolberg Date: Sat, 25 Oct 2025 16:21:23 +0200 Subject: [PATCH 02/10] fix(#40): make accepted filtering work with multiple clouds --- src/potreeViewer.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/potreeViewer.js b/src/potreeViewer.js index 1e94159..b395993 100644 --- a/src/potreeViewer.js +++ b/src/potreeViewer.js @@ -43,6 +43,14 @@ export async function createPotreeViewer( $('#menu_filters').next().show() viewer.toggleSidebar() + // Helper function to update all point clouds for Accepted filtering + function updateAllCloudsAccepted(gradientName) { + pointclouds.forEach(pc => { + pc.material.activeAttributeName = 'Accepted' + pc.material.gradient = Potree.Gradients[gradientName] + }) + } + initThreePanels(viewer, { onActivateElevation: () => { if (!pc) return @@ -51,9 +59,7 @@ export async function createPotreeViewer( suppressSidebarAutoScroll(clickCloudIconOnce) }, onActivateAccepted: () => { - if (!pc) return - pc.material.activeAttributeName = 'accepted' - pc.material.gradient = Potree.Gradients['GRAYSCALE'] + updateAllCloudsAccepted('GRAYSCALE') toggleAcceptedLegend(true) suppressSidebarAutoScroll(clickCloudIconOnce) } @@ -70,6 +76,7 @@ export async function createPotreeViewer( initAnnotationsPanel(viewer) }) + const pointclouds = [] for (const url of pointcloudUrls) { const e = await Potree.loadPointCloud(url) const pc = e.pointcloud @@ -85,6 +92,8 @@ export async function createPotreeViewer( pc.material.gradient = Potree.Gradients['VIRIDIS'] e.pointcloud.projection = ecef + + pointclouds.push(pc) } // Change name of default background from 'None' to 'Globe"' From 86ed2ca89cb8d7d188ca03a41c3ac78e142c8e8c Mon Sep 17 00:00:00 2001 From: AdrianSolberg Date: Sat, 25 Oct 2025 20:57:44 +0200 Subject: [PATCH 03/10] fix(#40): fix elevation control for multiple clouds --- src/AcceptedFiltering/threePanels.js | 21 +++++----- src/potreeViewer.js | 59 +++++++++++++++++++++++++--- 2 files changed, 65 insertions(+), 15 deletions(-) diff --git a/src/AcceptedFiltering/threePanels.js b/src/AcceptedFiltering/threePanels.js index 2ab84f4..9722f28 100644 --- a/src/AcceptedFiltering/threePanels.js +++ b/src/AcceptedFiltering/threePanels.js @@ -181,10 +181,10 @@ function ensureElevationButton(hooks) { } /** - * Reconnects the Elevation slider label to reflect the current range. - * Assumes #sldHeightRange and #lblHeightRange exist. + * Sets up a elevation range slider for interactive updates + * @param hooks - Callback object with onElevationRangeChange method */ -function rebindElevationLabel() { +function setUpElevationSlider(hooks) { const $ = window.jQuery || window.$ const slider = $ ? $('#sldHeightRange') : null const label = byId('lblHeightRange') @@ -194,11 +194,12 @@ function rebindElevationLabel() { const low = slider.slider('values', 0) const high = slider.slider('values', 1) label.textContent = `${low.toFixed(2)} to ${high.toFixed(2)}` + hooks?.onElevationRangeChange([low, high]) } slider.slider({ min: -10000, max: 0, values: [-10000, 0] }) slider.off('slide.custom slidestop.custom change.custom') - slider.on('slide.custom', 'slidestop.custom change.custom', update) + slider.on('slide.custom slidestop.custom change.custom', update) update() } @@ -206,14 +207,14 @@ function rebindElevationLabel() { * Moves Potree's Elevation container under the Elevation panel body and rebinds label. * @returns {boolean} true if moved or already in place */ -function moveElevationContainer() { +function moveElevationContainer(hooks) { const { body } = ensurePanelScaffold('elevation2_list') const src = byId('materials.elevation_container') if (!body || !src) return false if (src.parentNode !== body) { body.appendChild(src) - rebindElevationLabel() + setUpElevationSlider(hooks) accordionRefresh() } return true @@ -229,7 +230,7 @@ function initElevationControls(hooks) { const root = byId('potree_menu') || document.body const obs = new MutationObserver(() => { - if (byId('materials.elevation_container')) moveElevationContainer() + if (byId('materials.elevation_container')) moveElevationContainer(hooks) }) obs.observe(root, { childList: true, subtree: true }) } @@ -362,7 +363,7 @@ async function ensurePanelCaptured(mode, hooks) { selectCloudNode(hooks) src = await waitForOrPoll('materials.elevation_container', 1800) } - if (src) moveElevationContainer() + if (src) moveElevationContainer(hooks) return } } @@ -401,7 +402,7 @@ function attachSelfHealing(activeGetter) { if (mode === 'elevation') { const src = byId('materials.elevation_container') const { body } = ensurePanelScaffold('elevation2_list') - if (src && body && src.parentNode !== body) moveElevationContainer() + if (src && body && src.parentNode !== body) moveElevationContainer(hooks) } }) obs.observe(root, { childList: true, subtree: true }) @@ -433,7 +434,7 @@ export function initThreePanels(viewer, hooks = {}) { setActive('accepted') ) - attachSelfHealing(getActive) + attachSelfHealing(getActive, hooks) // Default: auto-activate Elevation once clickOnce('btnDoElevationControl') diff --git a/src/potreeViewer.js b/src/potreeViewer.js index b395993..79ba064 100644 --- a/src/potreeViewer.js +++ b/src/potreeViewer.js @@ -43,6 +43,21 @@ export async function createPotreeViewer( $('#menu_filters').next().show() viewer.toggleSidebar() + // Helper function to update all point clouds' elevation range + function updateAllCloudsElevation(range) { + pointclouds.forEach(pc => { + pc.material.activeAttributeName = 'elevation' + pc.material.elevationRange = range + }) + } + + // Helper function to update all point clouds' gradient + function updateAllCloudsGradient(gradientName) { + pointclouds.forEach(pc => { + pc.material.gradient = Potree.Gradients[gradientName] + }) + } + // Helper function to update all point clouds for Accepted filtering function updateAllCloudsAccepted(gradientName) { pointclouds.forEach(pc => { @@ -53,18 +68,25 @@ export async function createPotreeViewer( initThreePanels(viewer, { onActivateElevation: () => { - if (!pc) return - pc.material.activeAttributeName = 'elevation' - pc.material.gradient = Potree.Gradients['VIRIDIS'] + const $ = window.jQuery || window.$ + const slider = $ ? $('#sldHeightRange') : null + const values = slider?.slider('values') ?? [] + const low = typeof values[0] === 'number' ? values[0] : -10000 + const high = typeof values[1] === 'number' ? values[1] : 0 + + updateAllCloudsElevation([low, high]) + updateAllCloudsGradient('VIRIDIS') suppressSidebarAutoScroll(clickCloudIconOnce) }, onActivateAccepted: () => { updateAllCloudsAccepted('GRAYSCALE') toggleAcceptedLegend(true) suppressSidebarAutoScroll(clickCloudIconOnce) - } + }, + onElevationRangeChange: updateAllCloudsElevation }) - // // // helper + + // helper function clickCloudIconOnce() { const icon = document.querySelector( '#scene_objects i.jstree-themeicon-custom' @@ -72,6 +94,8 @@ export async function createPotreeViewer( if (icon) icon.dispatchEvent(new MouseEvent('click', { bubbles: true })) } + overrideGradientSchemeClick(pointclouds); + initMeasurementsPanel(viewer) initAnnotationsPanel(viewer) }) @@ -276,3 +300,28 @@ function suppressSidebarAutoScroll(action, holdMs = 350) { requestAnimationFrame(restoreLoop) } } + + +/** + * Overrides the click event handlers for gradient scheme selection to apply + * gradients to multiple point clouds. + * + * @param pointclouds - Array of point cloud objects + */ +function overrideGradientSchemeClick(pointclouds) { + const gradientContainer = document.getElementById('elevation_gradient_scheme_selection'); + const spans = gradientContainer.querySelectorAll('span'); + if (spans.length) { + spans.forEach((span, idx) => { + span.onclick = () => { + const gradientNames = Object.keys(Potree.Gradients); + const gradientName = gradientNames[idx]; + if (gradientName) { + pointclouds.forEach(pc => { + pc.material.gradient = Potree.Gradients[gradientName]; + }); + } + }; + }); + } +} \ No newline at end of file From ee4f63a820ef7ce6076eca1861ee0ca956308f03 Mon Sep 17 00:00:00 2001 From: AdrianSolberg Date: Sat, 25 Oct 2025 21:01:39 +0200 Subject: [PATCH 04/10] fix(#40): make globe background option work again --- src/AcceptedFiltering/threePanels.js | 4 ++-- src/potreeViewer.js | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/AcceptedFiltering/threePanels.js b/src/AcceptedFiltering/threePanels.js index 9722f28..a807b07 100644 --- a/src/AcceptedFiltering/threePanels.js +++ b/src/AcceptedFiltering/threePanels.js @@ -182,7 +182,7 @@ function ensureElevationButton(hooks) { /** * Sets up a elevation range slider for interactive updates - * @param hooks - Callback object with onElevationRangeChange method + * @param {{onElevationRangeChange?:Function}} hooks */ function setUpElevationSlider(hooks) { const $ = window.jQuery || window.$ @@ -413,7 +413,7 @@ function attachSelfHealing(activeGetter) { /** * Public entrypoint: builds Elevation and Accepted panels and wires behavior. * @param {object} viewer Potree viewer (not used directly here but available to hooks) - * @param {{onActivateElevation?:Function, onActivateAccepted?:Function, selectCloudOnce?:Function}} hooks + * @param {{onActivateElevation?:Function, onActivateAccepted?:Function, selectCloudOnce?:Function, onElevationRangeChange?:Function}} hooks */ export function initThreePanels(viewer, hooks = {}) { // Build sections diff --git a/src/potreeViewer.js b/src/potreeViewer.js index 79ba064..4f9bba2 100644 --- a/src/potreeViewer.js +++ b/src/potreeViewer.js @@ -96,6 +96,8 @@ export async function createPotreeViewer( overrideGradientSchemeClick(pointclouds); + makeGlobeBackgroundOption() + initMeasurementsPanel(viewer) initAnnotationsPanel(viewer) }) @@ -324,4 +326,20 @@ function overrideGradientSchemeClick(pointclouds) { }; }); } +} + +/** + * Converts the "None" background option to a "Globe" background option and sets it as the default. + */ +function makeGlobeBackgroundOption() { + const bgInput = document.getElementById('background_options_none'); + const bgLabel = document.querySelector('label[for="background_options_none"]'); + + if (bgInput && bgLabel) { + bgLabel.textContent = 'Globe'; + bgInput.id = 'background_options_globe'; + bgInput.value = 'globe'; + bgLabel.setAttribute('for', 'background_options_globe'); + bgLabel.click(); + } } \ No newline at end of file From 6b7b721c44aa3ea6f63fa5a623b1074670df73ad Mon Sep 17 00:00:00 2001 From: AdrianSolberg Date: Sat, 25 Oct 2025 21:44:13 +0200 Subject: [PATCH 05/10] fix(#40): remember previous elevation gradient --- src/potreeViewer.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/potreeViewer.js b/src/potreeViewer.js index 4f9bba2..9ecbfa2 100644 --- a/src/potreeViewer.js +++ b/src/potreeViewer.js @@ -43,6 +43,9 @@ export async function createPotreeViewer( $('#menu_filters').next().show() viewer.toggleSidebar() + // Store the last used elevation gradient + let lastElevationGradient = 'VIRIDIS'; + // Helper function to update all point clouds' elevation range function updateAllCloudsElevation(range) { pointclouds.forEach(pc => { @@ -75,7 +78,7 @@ export async function createPotreeViewer( const high = typeof values[1] === 'number' ? values[1] : 0 updateAllCloudsElevation([low, high]) - updateAllCloudsGradient('VIRIDIS') + updateAllCloudsGradient(lastElevationGradient) suppressSidebarAutoScroll(clickCloudIconOnce) }, onActivateAccepted: () => { @@ -94,9 +97,12 @@ export async function createPotreeViewer( if (icon) icon.dispatchEvent(new MouseEvent('click', { bubbles: true })) } - overrideGradientSchemeClick(pointclouds); + function setLastElevationGradient(gradientName) { + lastElevationGradient = gradientName; + } + overrideGradientSchemeClick(pointclouds, setLastElevationGradient); - makeGlobeBackgroundOption() + makeGlobeBackgroundOption() initMeasurementsPanel(viewer) initAnnotationsPanel(viewer) @@ -309,8 +315,9 @@ function suppressSidebarAutoScroll(action, holdMs = 350) { * gradients to multiple point clouds. * * @param pointclouds - Array of point cloud objects + * @param {Function} setLastElevationGradient - Callback function to store the last selected gradient name */ -function overrideGradientSchemeClick(pointclouds) { +function overrideGradientSchemeClick(pointclouds, setLastElevationGradient) { const gradientContainer = document.getElementById('elevation_gradient_scheme_selection'); const spans = gradientContainer.querySelectorAll('span'); if (spans.length) { @@ -320,8 +327,9 @@ function overrideGradientSchemeClick(pointclouds) { const gradientName = gradientNames[idx]; if (gradientName) { pointclouds.forEach(pc => { - pc.material.gradient = Potree.Gradients[gradientName]; + pc.material.gradient = Potree.Gradients[gradientName]; }); + setLastElevationGradient(gradientName); } }; }); From d83e23d4558dc378ce6ca6deec8a7a91ebf2dfc1 Mon Sep 17 00:00:00 2001 From: AdrianSolberg Date: Sat, 25 Oct 2025 21:57:39 +0200 Subject: [PATCH 06/10] fix(#40): make it possible to close accordions --- src/AcceptedFiltering/threePanels.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/AcceptedFiltering/threePanels.js b/src/AcceptedFiltering/threePanels.js index a807b07..c42aa0b 100644 --- a/src/AcceptedFiltering/threePanels.js +++ b/src/AcceptedFiltering/threePanels.js @@ -43,8 +43,17 @@ function insertSection({ headerId, headerText, listId }) { menu.appendChild(header) menu.appendChild(panel) } - + accordionRefresh() + + header.addEventListener('click', () => { + if ($(menu).accordion && $(menu).data('uiAccordion')) return + if (window.jQuery) { + const $p = window.jQuery(panel) + $p.is(':visible') ? $p.slideUp(350) : $p.slideDown(350) + return + } + }) } /** From 843b1c8fe2cffa925a31947026bf1c08fbc82ec5 Mon Sep 17 00:00:00 2001 From: AdrianSolberg Date: Sat, 25 Oct 2025 22:07:22 +0200 Subject: [PATCH 07/10] docs(#40): mention point cloud paths in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 625f104..0e480c8 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Molloy Explorer is a 3D seabed visualization tool built with **Potree**. It allo ### Add point cloud data -Place the point cloud data (in Potree format with EPSG:4978 coordinates) in `public/pointclouds/data_converted`. +Place the point cloud data (in Potree format with EPSG:4978 coordinates) in `public/pointclouds`. Ensure the point cloud folder names match the paths specified in `src/config.js`, either by renaming the point cloud folders or by updating the paths. **Note:** Point cloud files should not be committed to Git. From d1580ada1b6375edd921b8b33e0701e6d9d8ae22 Mon Sep 17 00:00:00 2001 From: AdrianSolberg Date: Sat, 25 Oct 2025 22:07:57 +0200 Subject: [PATCH 08/10] style(#40): run prettier formatting --- src/AcceptedFiltering/threePanels.js | 2 +- src/potreeViewer.js | 55 ++++++++++++++-------------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/AcceptedFiltering/threePanels.js b/src/AcceptedFiltering/threePanels.js index c42aa0b..b20fa43 100644 --- a/src/AcceptedFiltering/threePanels.js +++ b/src/AcceptedFiltering/threePanels.js @@ -43,7 +43,7 @@ function insertSection({ headerId, headerText, listId }) { menu.appendChild(header) menu.appendChild(panel) } - + accordionRefresh() header.addEventListener('click', () => { diff --git a/src/potreeViewer.js b/src/potreeViewer.js index 9ecbfa2..530e59d 100644 --- a/src/potreeViewer.js +++ b/src/potreeViewer.js @@ -43,12 +43,12 @@ export async function createPotreeViewer( $('#menu_filters').next().show() viewer.toggleSidebar() - // Store the last used elevation gradient - let lastElevationGradient = 'VIRIDIS'; + // Store the last used elevation gradient + let lastElevationGradient = 'VIRIDIS' // Helper function to update all point clouds' elevation range function updateAllCloudsElevation(range) { - pointclouds.forEach(pc => { + pointclouds.forEach((pc) => { pc.material.activeAttributeName = 'elevation' pc.material.elevationRange = range }) @@ -56,14 +56,14 @@ export async function createPotreeViewer( // Helper function to update all point clouds' gradient function updateAllCloudsGradient(gradientName) { - pointclouds.forEach(pc => { + pointclouds.forEach((pc) => { pc.material.gradient = Potree.Gradients[gradientName] }) } // Helper function to update all point clouds for Accepted filtering function updateAllCloudsAccepted(gradientName) { - pointclouds.forEach(pc => { + pointclouds.forEach((pc) => { pc.material.activeAttributeName = 'Accepted' pc.material.gradient = Potree.Gradients[gradientName] }) @@ -98,11 +98,11 @@ export async function createPotreeViewer( } function setLastElevationGradient(gradientName) { - lastElevationGradient = gradientName; + lastElevationGradient = gradientName } - overrideGradientSchemeClick(pointclouds, setLastElevationGradient); + overrideGradientSchemeClick(pointclouds, setLastElevationGradient) - makeGlobeBackgroundOption() + makeGlobeBackgroundOption() initMeasurementsPanel(viewer) initAnnotationsPanel(viewer) @@ -309,7 +309,6 @@ function suppressSidebarAutoScroll(action, holdMs = 350) { } } - /** * Overrides the click event handlers for gradient scheme selection to apply * gradients to multiple point clouds. @@ -318,21 +317,23 @@ function suppressSidebarAutoScroll(action, holdMs = 350) { * @param {Function} setLastElevationGradient - Callback function to store the last selected gradient name */ function overrideGradientSchemeClick(pointclouds, setLastElevationGradient) { - const gradientContainer = document.getElementById('elevation_gradient_scheme_selection'); - const spans = gradientContainer.querySelectorAll('span'); + const gradientContainer = document.getElementById( + 'elevation_gradient_scheme_selection' + ) + const spans = gradientContainer.querySelectorAll('span') if (spans.length) { spans.forEach((span, idx) => { span.onclick = () => { - const gradientNames = Object.keys(Potree.Gradients); - const gradientName = gradientNames[idx]; + const gradientNames = Object.keys(Potree.Gradients) + const gradientName = gradientNames[idx] if (gradientName) { - pointclouds.forEach(pc => { - pc.material.gradient = Potree.Gradients[gradientName]; - }); - setLastElevationGradient(gradientName); + pointclouds.forEach((pc) => { + pc.material.gradient = Potree.Gradients[gradientName] + }) + setLastElevationGradient(gradientName) } - }; - }); + } + }) } } @@ -340,14 +341,14 @@ function overrideGradientSchemeClick(pointclouds, setLastElevationGradient) { * Converts the "None" background option to a "Globe" background option and sets it as the default. */ function makeGlobeBackgroundOption() { - const bgInput = document.getElementById('background_options_none'); - const bgLabel = document.querySelector('label[for="background_options_none"]'); + const bgInput = document.getElementById('background_options_none') + const bgLabel = document.querySelector('label[for="background_options_none"]') if (bgInput && bgLabel) { - bgLabel.textContent = 'Globe'; - bgInput.id = 'background_options_globe'; - bgInput.value = 'globe'; - bgLabel.setAttribute('for', 'background_options_globe'); - bgLabel.click(); + bgLabel.textContent = 'Globe' + bgInput.id = 'background_options_globe' + bgInput.value = 'globe' + bgLabel.setAttribute('for', 'background_options_globe') + bgLabel.click() } -} \ No newline at end of file +} From f19fd5635b985d928349068937ee809e1b9a0c96 Mon Sep 17 00:00:00 2001 From: AdrianSolberg Date: Sun, 26 Oct 2025 14:50:39 +0100 Subject: [PATCH 09/10] fix(#40): minor coderabbit improvements --- src/AcceptedFiltering/threePanels.js | 10 ++++++---- src/AnnotationControl/annotationPanel.js | 8 +++++--- src/potreeViewer.js | 16 +++++----------- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/AcceptedFiltering/threePanels.js b/src/AcceptedFiltering/threePanels.js index b20fa43..249df0f 100644 --- a/src/AcceptedFiltering/threePanels.js +++ b/src/AcceptedFiltering/threePanels.js @@ -47,12 +47,14 @@ function insertSection({ headerId, headerText, listId }) { accordionRefresh() header.addEventListener('click', () => { - if ($(menu).accordion && $(menu).data('uiAccordion')) return - if (window.jQuery) { - const $p = window.jQuery(panel) + const $ = window.jQuery || window.$ + if ($ && $.fn?.accordion && $(menu).data('uiAccordion')) return + if ($) { + const $p = $(panel) $p.is(':visible') ? $p.slideUp(350) : $p.slideDown(350) return } + panel.style.display = panel.style.display === 'none' ? '' : 'none' }) } @@ -404,7 +406,7 @@ async function switchMode(mode, hook, hooksBag = {}) { * @param {()=>'elevation'|'accepted'} activeGetter * @returns {MutationObserver} */ -function attachSelfHealing(activeGetter) { +function attachSelfHealing(activeGetter, hooks) { const root = byId('potree_menu') || document.body const obs = new MutationObserver(() => { const mode = activeGetter() diff --git a/src/AnnotationControl/annotationPanel.js b/src/AnnotationControl/annotationPanel.js index 4bdd4ec..cffe313 100644 --- a/src/AnnotationControl/annotationPanel.js +++ b/src/AnnotationControl/annotationPanel.js @@ -56,12 +56,14 @@ export function initAnnotationsPanel(viewer) { // Toggle collapse header.addEventListener('click', () => { - if ($(menu).accordion && $(menu).data('uiAccordion')) return - if (window.jQuery) { - const $p = window.jQuery(panel) + const $ = window.jQuery || window.$ + if ($ && $.fn?.accordion && $(menu).data('uiAccordion')) return + if ($) { + const $p = $(panel) $p.is(':visible') ? $p.slideUp(350) : $p.slideDown(350) return } + panel.style.display = panel.style.display === 'none' ? '' : 'none' }) targetContainer = panel.querySelector('#annotations_list') } diff --git a/src/potreeViewer.js b/src/potreeViewer.js index 530e59d..b348295 100644 --- a/src/potreeViewer.js +++ b/src/potreeViewer.js @@ -35,6 +35,8 @@ export async function createPotreeViewer( viewer.loadSettingsFromURL() viewer.setDescription('Molloy Explorer') + const pointclouds = [] + viewer.loadGUI(() => { viewer.setLanguage('en') $('#menu_appearance').next().show() @@ -108,7 +110,6 @@ export async function createPotreeViewer( initAnnotationsPanel(viewer) }) - const pointclouds = [] for (const url of pointcloudUrls) { const e = await Potree.loadPointCloud(url) const pc = e.pointcloud @@ -128,14 +129,6 @@ export async function createPotreeViewer( pointclouds.push(pc) } - // Change name of default background from 'None' to 'Globe"' - $('#background_options_none') - .text('Globe') - .attr('id', 'background_options_globe') - .val('globe') - - viewer.setBackground('globe') - // Initialize camera position and target point (manually chosen) viewer.scene.view.setView( [3961574.044, 1494736.334, 8348318.575], // Initial camera position @@ -320,10 +313,11 @@ function overrideGradientSchemeClick(pointclouds, setLastElevationGradient) { const gradientContainer = document.getElementById( 'elevation_gradient_scheme_selection' ) + if (!gradientContainer) return const spans = gradientContainer.querySelectorAll('span') if (spans.length) { spans.forEach((span, idx) => { - span.onclick = () => { + span.addEventListener('click', () => { const gradientNames = Object.keys(Potree.Gradients) const gradientName = gradientNames[idx] if (gradientName) { @@ -332,7 +326,7 @@ function overrideGradientSchemeClick(pointclouds, setLastElevationGradient) { }) setLastElevationGradient(gradientName) } - } + }) }) } } From 8a0bd38e7204443d6c05d35f1c47056a10873c65 Mon Sep 17 00:00:00 2001 From: AdrianSolberg Date: Sun, 26 Oct 2025 15:02:20 +0100 Subject: [PATCH 10/10] refactor(#40): move point cloud loading before loadGUI --- src/potreeViewer.js | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/potreeViewer.js b/src/potreeViewer.js index b348295..a4903c1 100644 --- a/src/potreeViewer.js +++ b/src/potreeViewer.js @@ -36,6 +36,24 @@ export async function createPotreeViewer( viewer.setDescription('Molloy Explorer') const pointclouds = [] + for (const url of pointcloudUrls) { + const e = await Potree.loadPointCloud(url) + const pc = e.pointcloud + viewer.scene.addPointCloud(pc) + + pc.material.pointSizeType = Potree.PointSizeType.ADAPTIVE + pc.material.shape = Potree.PointShape.CIRCLE + overrideShaderForGradient(pc) + + //The default activeAttributeName is set to elevation and the color gradient to VIRIDIS for good visualization + pc.material.elevationRange = [-10000, 0] + pc.material.activeAttributeName = 'elevation' + pc.material.gradient = Potree.Gradients['VIRIDIS'] + + e.pointcloud.projection = ecef + + pointclouds.push(pc) + } viewer.loadGUI(() => { viewer.setLanguage('en') @@ -110,25 +128,6 @@ export async function createPotreeViewer( initAnnotationsPanel(viewer) }) - for (const url of pointcloudUrls) { - const e = await Potree.loadPointCloud(url) - const pc = e.pointcloud - viewer.scene.addPointCloud(pc) - - pc.material.pointSizeType = Potree.PointSizeType.ADAPTIVE - pc.material.shape = Potree.PointShape.CIRCLE - overrideShaderForGradient(pc) - - //The default activeAttributeName is set to elevation and the color gradient to VIRIDIS for good visualization - pc.material.elevationRange = [-10000, 0] - pc.material.activeAttributeName = 'elevation' - pc.material.gradient = Potree.Gradients['VIRIDIS'] - - e.pointcloud.projection = ecef - - pointclouds.push(pc) - } - // Initialize camera position and target point (manually chosen) viewer.scene.view.setView( [3961574.044, 1494736.334, 8348318.575], // Initial camera position