Skip to content

Commit

Permalink
Merge pull request #63 from TDT4290-group-4/dev
Browse files Browse the repository at this point in the history
Merge sprint 3 changes into main
  • Loading branch information
tobiaobr authored Nov 10, 2025
2 parents 3eb004c + cfb22a9 commit 210e7ed
Show file tree
Hide file tree
Showing 30 changed files with 2,147 additions and 129 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,7 @@ Then in a new terminal run the tests:
```bash
npm run test
```

### User Guide

For a complete user guide on fundamentals and all functionality click [here](Userguide.md).
443 changes: 443 additions & 0 deletions Userguide.md

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
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" />
<link rel="stylesheet" href="src/2DProfileOverride/2DProfileOverride.css" />
</head>

<body>
Expand Down
Binary file added public/img/00_icons.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/01_menuBarTabs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/02_ElevationControlTab.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/03_ElevationControlTab.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/04_AcceptedFilter.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/05_AcceptedFilter.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/06_Measurements.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/07_SavedLocations.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/08_Appearance.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/09_Tools.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/10_Scene.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/11_SavedLocations.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/12_Measurements_updated.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/13_Tools_updated.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/14_Animation.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions src/2DProfileOverride/2DProfileOverride.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/* Keep profile SVG content (axes/labels) from being clipped */
#profileSVG {
overflow: visible !important;
}

/* Improve axis label readability against varying backgrounds */
#profile_window .axis text {
paint-order: stroke;
stroke: #000;
stroke-width: 3px;
stroke-linejoin: round;
}
224 changes: 224 additions & 0 deletions src/2DProfileOverride/2DProfileOverride.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
import { ecef, wgs84 } from '../config.js'

/**
* Initialize runtime overrides for Potree 2D profile behavior.
* - Patches ProfileWindow.addPoints: converts each point's Z to elevation instead.
* - Rewrites selection info table to show lon/lat/elevation.
*/
export function init2DProfileOverride(viewer) {
const tryPatchAddPoints = (target) => {
if (!target || target.__elevationPatchApplied) return false
const originalAddPoints = target.addPoints
if (typeof originalAddPoints !== 'function') return false

target.addPoints = function patchedAddPoints(pointcloud, points) {
try {
if (!points || !points.data || !points.data.position) {
return originalAddPoints.call(this, pointcloud, points)
}

const srcPos = points.data.position
// Divide by 3 because positions are [x0,y0,z0, x1,y1,z1, ...]
const count = points.numPoints || Math.floor(srcPos.length / 3)
const posArray = srcPos.constructor || Float32Array

// Clone without changing original buffers
const cloned = {
...points,
data: { ...points.data, position: new posArray(srcPos) }
}
const dstPos = cloned.data.position

const pcx = pointcloud?.position?.x || 0
const pcy = pointcloud?.position?.y || 0
const pcz = pointcloud?.position?.z || 0

// Preserve world ECEF Z per point (best-effort). Some Potree UIs attach
// selectedPoint from these attributes; we keep it on the cloned structure
// for later retrieval in the selection panel override.
const ecefZWorld = new Float64Array(count)

for (let i = 0; i < count; i++) {
const ix = 3 * i
const x = srcPos[ix + 0] + pcx
const y = srcPos[ix + 1] + pcy
const z = srcPos[ix + 2] + pcz

const [, , elevation] = proj4(ecef, wgs84, [x, y, z])
// Internally, Potree adds pointcloud.position.z back later.
dstPos[ix + 2] = elevation - pcz

ecefZWorld[i] = z
}

cloned.data.ecefZWorld = ecefZWorld

const result = originalAddPoints.call(this, pointcloud, cloned)

// Try to tag currently selectedPoint with the original ECEF Z if available
try {
if (
this &&
this.selectedPoint &&
Number.isFinite(this.selectedPoint.index)
) {
const idx = this.selectedPoint.index
if (ecefZWorld && idx >= 0 && idx < ecefZWorld.length) {
this.selectedPoint.ecefZWorld = ecefZWorld[idx]
}
}
} catch {}

return result
} catch (err) {
console.warn(
'2DProfileOverride: failed to apply elevation override',
err
)
return originalAddPoints.call(this, pointcloud, points)
}
}

target.__elevationPatchApplied = true
return true
}

let patched = false
if (viewer && viewer.profileWindow) {
patched = tryPatchAddPoints(viewer.profileWindow)
}
if (window.Potree?.ProfileWindow?.prototype) {
patched =
tryPatchAddPoints(window.Potree.ProfileWindow.prototype) || patched
}

const afterPatched = () => {
attachSelectionInfoOverride(viewer)
}

if (patched) {
afterPatched()
} else {
// Poll until the profile window/prototype is available
let tries = 0
const maxTries = 40
const timer = setInterval(() => {
tries += 1
if (
tryPatchAddPoints(window.Potree?.ProfileWindow?.prototype) ||
(viewer?.profileWindow && tryPatchAddPoints(viewer.profileWindow))
) {
clearInterval(timer)
afterPatched()
} else if (tries >= maxTries) {
clearInterval(timer)
}
}, 250)
}
}

// Rewrites the selection properties table inside the profile window
function attachSelectionInfoOverride(viewer) {
const getInfoEl = () => document.getElementById('profileSelectionProperties')
let infoEl = getInfoEl()
if (!infoEl) {
const obs = new MutationObserver(() => {
infoEl = getInfoEl()
if (infoEl) {
obs.disconnect()
monitorSelectionInfo(viewer, infoEl)
}
})
obs.observe(document.body, { childList: true, subtree: true })
} else {
monitorSelectionInfo(viewer, infoEl)
}
}

function monitorSelectionInfo(viewer, infoEl) {
let scheduled = false
let selfUpdating = false

const updateOnce = () => {
scheduled = false
selfUpdating = true
try {
const table = infoEl.querySelector('table')
if (!table) return
const rows = [...table.querySelectorAll('tr')]
if (rows.length < 3) return

const pw = viewer?.profileWindow
const pos = pw?.viewerPickSphere?.position
const sp = pw?.selectedPoint
if (!pos || !sp) return

// Use preserved ECEF Z if we have it; otherwise fall back to pos.z
const trueZ = Number(sp?.ecefZWorld ?? NaN)
let lon, lat
if (Number.isFinite(trueZ)) {
;[lon, lat] = proj4(ecef, wgs84, [pos.x, pos.y, trueZ])
} else {
;[lon, lat] = proj4(ecef, wgs84, [pos.x, pos.y, pos.z])
}
const elevation = pos.z

const setRow = (row, label, val) => {
const tds = row.querySelectorAll('td')
if (tds[0] && tds[0].textContent !== label) tds[0].textContent = label
if (tds[1]) {
const txt = Number.isFinite(val)
? val.toLocaleString(undefined, {
minimumFractionDigits: 4,
maximumFractionDigits: 4
})
: ''
if (tds[1].textContent !== txt) tds[1].textContent = txt
}
}

setRow(rows[0], 'lon', lon)
setRow(rows[1], 'lat', lat)
setRow(rows[2], 'elevation', elevation)

// Remove unwanted rows from the hover info table
const labelsToHide = new Set([
'intensity',
'return number',
'number of returns',
'classification flags',
'classification',
'user data',
'scan angle',
'gps time',
'gps-time',
'rgba'
])

// iterate over a snapshot to avoid issues while removing
const allRows = [...table.querySelectorAll('tr')]
for (let i = 3; i < allRows.length; i++) {
const row = allRows[i]
const labelCell = row.querySelector('td')
const label = (labelCell?.textContent || '').trim().toLowerCase()
if (labelsToHide.has(label)) {
row.remove()
}
}
} finally {
setTimeout(() => {
selfUpdating = false
}, 0)
}
}

const mo = new MutationObserver(() => {
if (selfUpdating) return
if (!scheduled) {
scheduled = true
requestAnimationFrame(updateOnce)
}
})
mo.observe(infoEl, { childList: true, subtree: true })
requestAnimationFrame(updateOnce)
}
Loading

0 comments on commit 210e7ed

Please sign in to comment.