- Nouveau mode "cloud_compare" (en plus de "const_height") : comparaison entre une surface
superieure et une limite inferieure, avec appariement de densite avant le niveau 0 puis
decimation synchronisee x2 sur 5 etapes. UI : selecteur de mode + upload double avec labels
explicites (surface superieure / limite inferieure).
- Remplissage des trous de scan (interpolation Delaunay bornee par max-edge-length) avant chaque
calcul de volume, dans les deux modes. -VOLUME n'ayant aucune option native pour ca (verifie dans
les sources CloudCompare), on rasterize+comble+exporte en nuage avant de le passer a -VOLUME.
Override utilisateur possible pour la distance max, afin de ne pas combler les parties concaves
du contour de l'objet mesure.
- Fix critique : CloudCompare formate les grands nombres avec une virgule comme separateur de
milliers ("Volume: 14,244.46") ; le parsing tronquait silencieusement a la premiere virgule
(14 244 devenait 14). Trouve en testant avec un vrai nuage industriel.
- Validation avec 2 vrais nuages utilisateur : le mode Z constant donnait ~50m3 (plan de reference
sans rapport avec la base reelle de l'amas, nuage contenant du contexte environnant) ; le nouveau
mode comparaison donne ~14 244m3, stable a +/-4% meme a 16x de decimation.
- CLAUDE.md mis a jour avec tous ces findings (limitation -VOLUME/LEAVE_EMPTY, technique de
contournement, piege du separateur de milliers, architecture du mode 2 nuages).
259 lines
19 KiB
Markdown
259 lines
19 KiB
Markdown
# CLAUDE.md
|
|
|
|
Notes pour toute future session travaillant sur ce repo. Ce fichier documente des faits
|
|
verifies empiriquement sur CloudCompare 2.13.2 (Windows officiel ET paquet apt Debian trixie) —
|
|
pas de la documentation officielle recopiee, mais des comportements observes directement, souvent
|
|
non documentes ou contredisant la doc/le wiki.
|
|
|
|
## Contexte du projet
|
|
|
|
Pipeline : nuage de points (LAS/LAZ/COPC.laz/BIN) -> calcul de volume 2.5D CloudCompare -> decimation
|
|
spatiale progressive x2 sur 5 etapes (chainee, chaque niveau decime depuis le precedent, pas depuis
|
|
l'original) -> volume recalcule a chaque niveau -> assemblage des 6 nuages dans un seul `.bin` ->
|
|
rapport CSV/JSON + images. UI web (NestJS + Tailwind CDN) avec historique des runs (SQLite).
|
|
E57 volontairement hors perimetre.
|
|
|
|
## CloudCompare CLI — comportements verifies
|
|
|
|
### `-VOLUME` (calcul de volume 2.5D)
|
|
|
|
- Sur un nuage **unique**, utiliser `-CONST_HEIGHT <valeur>` comme plan de reference (le nuage charge
|
|
est le "ceiling", le plan constant est le "ground"). Sans `-CONST_HEIGHT`, la commande attend DEUX
|
|
nuages charges.
|
|
- Syntaxe complete : `-VOLUME -GRID_STEP <val> [-VERT_DIR 0/1/2] [-CONST_HEIGHT <val>] [-GROUND_IS_FIRST] [-OUTPUT_MESH]`.
|
|
- **Piege majeur non documente** : le rapport texte `VolumeCalculationReport_<date>.txt` n'est genere
|
|
QUE si `-AUTO_SAVE ON` (le defaut). Avec `-AUTO_SAVE OFF` (qu'on utilise partout ailleurs pour
|
|
eviter de polluer le dossier de travail avec des fichiers intermediaires), la commande `-VOLUME`
|
|
s'execute (log "[2.5D VOLUME CALCULATION] finished") mais **aucun rapport n'est ecrit, aucune
|
|
erreur n'est loggee**. Il faut explicitement repasser `-AUTO_SAVE ON` juste pour l'appel `-VOLUME`.
|
|
Voir `CcRunnerService.run(args, cwd, { autoSave: true })`.
|
|
- Avec `-AUTO_SAVE ON`, `-VOLUME` sauvegarde AUSSI automatiquement une grille de difference de hauteur
|
|
nommee `<nom>_HEIGHT_DIFFERENCE_<date>.bin` qu'on ne veut pas garder — on la detecte (meme technique
|
|
que pour le rapport, cf ci-dessous) et on la supprime juste apres (voir pipeline.service.ts).
|
|
- **CloudCompare ecrit ses fichiers de sortie automatiques (rapport, grilles) relativement au dossier
|
|
du fichier charge via `-O`, pas au `cwd` du process.** Si on charge `-O levels/L0_full.bin`, le
|
|
rapport atterrit dans `levels/VolumeCalculationReport_*.txt`, pas a la racine du `cwd`. D'ou la
|
|
necessite d'une recherche **recursive** (`CcRunnerService.findLatestFile`) plutot qu'un simple
|
|
`readdir` du dossier de travail.
|
|
- Format du rapport texte (verifie, stable) — cle:valeur, une ligne par info, parsable par regex :
|
|
```
|
|
Volume: 7.834176
|
|
Surface: 100.774998
|
|
----------------------
|
|
Added volume: (+)7.975798
|
|
Removed volume: (-)0.141622
|
|
----------------------
|
|
Matching cells: 99.8%
|
|
Non-matching cells:
|
|
ground = 0.2%
|
|
ceil = 0.0%
|
|
Average neighbors per cell: 8.0 / 8.0
|
|
```
|
|
- **`Matching cells: X%`** est le meilleur signal natif de robustesse : c'est le % de cellules de la
|
|
grille de calcul ou les deux surfaces sont effectivement comparables. Il s'effondre des que la
|
|
decimation produit un nuage plus clairseme que la resolution de grille utilisee pour le calcul de
|
|
volume — exactement l'indicateur cherche pour ce projet (voir `matchingCellsWarnThreshold` dans
|
|
`config.ts`, defaut 90%).
|
|
- **`-GRID_STEP` doit etre adapte a CHAQUE niveau, pas fige sur la resolution du niveau 0.** Premiere
|
|
version du pipeline : un `GRID_STEP` unique (derive du niveau 0) reutilise sur les 6 niveaux "pour
|
|
rester comparable". En pratique ca fait exactement l'inverse de ce qu'on veut tester : une fois la
|
|
decimation plus grossiere que cette grille fixe, `matching cells %` s'effondre mecaniquement
|
|
(observe : 99% -> 58% -> 13% -> 3% -> 0.9% -> 0.2% sur 5 niveaux) parce que la grille est trop fine
|
|
pour les points restants, pas parce que le volume est vraiment devenu impossible a estimer. Ce n'est
|
|
pas un signal de robustesse utile, juste un artefact de resolution. **Fix retenu** : `GRID_STEP` par
|
|
niveau = pas spatial de CE niveau x `gridStepMultiplier` (le niveau 0 utilise `initialStep x
|
|
multiplier`). Chaque niveau a donc sa propre grille adaptee a sa propre densite de points — le
|
|
`matching cells %` refletera alors la vraie perte d'information due a la decimation, pas un
|
|
desalignement grille/densite. Voir `LevelResult.gridStep` (par niveau) et
|
|
`RunReport.params.gridStepMultiplier` (le seul parametre encore global).
|
|
|
|
- **`-VOLUME` n'a AUCUNE option de remplissage de trous (empty cell filling), meme si `-RASTERIZE`
|
|
en a.** Verifie en clonant les sources (`qCC/ccCommandRaster.cpp`, `CommandVolume25D::process`) :
|
|
l'appel a `ccVolumeCalcTool::ComputeVolume(...)` est fait avec `ccRasterGrid::LEAVE_EMPTY` **code en
|
|
dur** pour le ground ET le ceil, non exposable via un flag CLI (`-EMPTY_FILL`, `-MAX_EDGE_LENGTH`
|
|
etc. existent bien comme constantes dans le fichier mais seule `CommandRasterize::process` les
|
|
consomme ; `CommandVolume25D::process` ne les lit jamais). **Contournement retenu** : avant chaque
|
|
`-VOLUME`, on rasterize+comble chaque nuage separement (`-RASTERIZE -GRID_STEP g -EMPTY_FILL INTERP
|
|
-MAX_EDGE_LENGTH m -OUTPUT_CLOUD -SAVE_CLOUDS FILE filled.bin`), on charge ensuite CE nuage comble
|
|
(pas l'original) dans `-VOLUME`. Verifie empiriquement (nuage synthetique avec trou d'occlusion
|
|
delibere, rayon 0.6m) : sans comblement, matching cells 96.4% / volume sous-estime ; avec
|
|
`-MAX_EDGE_LENGTH 0.3` (> diametre du trou), trou comble, matching cells 98.9% ; avec
|
|
`-MAX_EDGE_LENGTH 0.08` (< diametre du trou), AUCUN effet (identique au cas non comble) — confirme
|
|
que le parametre borne bien la portee du remplissage, empechant de combler les parties concaves du
|
|
contour d'un objet (exactement le risque signale par l'utilisateur). Voir
|
|
`PipelineService.prepareCloudAtLevel` (bloc "Remplissage des trous").
|
|
- **Piege associe** : `-RASTERIZE ... -OUTPUT_CLOUD` avec `-SAVE_CLOUDS FILE "a.bin b.bin"` (2 noms)
|
|
echoue avec `Invalid parameter: specified 2 file names, but there are 1 clouds` — le nuage comble
|
|
**remplace** le nuage original dans la liste des entites chargees (pas un ajout), il ne faut donc
|
|
fournir qu'UN SEUL nom de fichier a `-SAVE_CLOUDS FILE`.
|
|
- Le nuage "comble" est un nuage-grille (un point par cellule non vide, ex: 39971 points pour une
|
|
grille 201x201 avec quelques cellules hors de l'enveloppe convexe) — PAS le nuage original avec des
|
|
points ajoutes. On le garde uniquement pour le calcul de volume (`raw/L{n}_filled.bin`) ; le nuage
|
|
BRUT (non comble) reste utilise pour l'export, les cartes de hauteur et le chainage de decimation,
|
|
afin de montrer les vrais trous de scan et ne pas faire chainer l'interpolation d'un niveau vers
|
|
le suivant (qui composerait l'erreur).
|
|
|
|
- **Piege critique de parsing** : CloudCompare formate les **grands nombres avec une virgule comme
|
|
separateur de milliers** dans le rapport texte (`Volume: 14,244.464657`, pas `14244.464657`). Une
|
|
regex naive `[-\d.eE]+` s'arrete a la virgule et tronque silencieusement la valeur (14 244 devient
|
|
**14**, sans aucune erreur) — bug reel decouvert en testant avec un vrai nuage industriel (volume
|
|
affiche a tort ~50m3 au lieu de ~14 244m3 avant fix, meme si dans ce cas precis c'etait en plus
|
|
combine a un mauvais choix de plan de reference, voir plus bas). **Fix** : regex `[-\d,.eE]+` puis
|
|
`.replace(/,/g, '')` avant `Number(...)`. Voir `CcRunnerService.parseVolumeReportFile` et le test
|
|
`cc-runner.service.spec.ts` ("grands volumes avec separateur de milliers").
|
|
|
|
### `-SS SPATIAL` / `-SS RANDOM` (sous-echantillonnage)
|
|
|
|
- `-SS SPATIAL <distance>` : distance minimale entre points, PAS un ratio de points cible. Le nombre
|
|
de points obtenu depend de la densite reelle du nuage — jamais garanti a un pourcentage exact.
|
|
- `-SS RANDOM <n>` : sous-echantillonne a `n` points. Si `n` >= nombre de points du nuage, ne plante
|
|
pas (comportement observe : garde le nuage tel quel).
|
|
- Log de sortie a parser : `[SUBSAMPLE]` puis `Result: <N> points` (regex utilisee :
|
|
`/\[SUBSAMPLE\][\s\S]*?Result:\s*(\d+)\s*points/`).
|
|
- `-AUTO_SAVE OFF` fonctionne correctement ici (contrairement a `-VOLUME`) : pas de fichier auto-sauve
|
|
parasite, seul `-SAVE_CLOUDS FILE "nom.bin"` explicite produit un fichier, avec le nom voulu.
|
|
|
|
### `-SAVE_CLOUDS`
|
|
|
|
- `-SAVE_CLOUDS FILE "nom.ext"` : nom de sortie explicite (fonctionne, contrairement a ce que
|
|
certains threads du forum CloudCompare laissent penser).
|
|
- `-SAVE_CLOUDS ALL_AT_ONCE FILE "nom.bin"` avec **plusieurs nuages charges** (plusieurs `-O`) sans
|
|
`-MERGE_CLOUDS` : sauvegarde tous les nuages comme entites **distinctes** dans un seul `.bin` (le
|
|
format `.bin` de CloudCompare supporte nativement une hierarchie de plusieurs nuages). Verifie en
|
|
rouvrant le fichier : chaque nuage retrouve son point count exact, sans fusion de geometrie. C'est
|
|
la technique utilisee pour assembler les 6 niveaux de decimation dans un seul fichier de sortie.
|
|
**`-MERGE_CLOUDS` fusionnerait la geometrie en un seul nuage — a ne PAS utiliser ici.**
|
|
- Pas de commande `-RENAME_CLOUDS` (n'existe pas, testee = "Unknown or misplaced command"). Le nom de
|
|
chaque nuage dans le `.bin` fusionne est derive du nom de fichier source charge — on nomme donc les
|
|
fichiers intermediaires de facon descriptive (`L0_full.bin`, `L1_step0.05.bin`, ...) plutot que de
|
|
chercher a renommer les entites apres coup.
|
|
|
|
### Autres commandes
|
|
|
|
- `-RASTERIZE -GRID_STEP <val> -OUTPUT_RASTER_Z` : export GeoTIFF. **Fonctionne sur le build Windows
|
|
officiel, mais PLANTE (assertion `false` dans `ccRasterizeTool.cpp:ExportGeoTiff`, core dump) sur le
|
|
paquet apt Debian trixie**, faute de support GDAL compile dans ce paquet. Ne pas utiliser cette
|
|
voie dans l'image Docker — voir section "Paquet apt Debian" ci-dessous pour la solution retenue.
|
|
- Les commandes s'appliquent comme une **machine a etats sequentielle sur UN SEUL process** : on ne
|
|
peut pas "reprendre" une session precedente. Chaque etape logique du pipeline (stats, conversion,
|
|
decimation, volume, export) = un appel CLI independant, avec son propre `-O` de rechargement.
|
|
- `-SILENT` doit etre le tout premier argument (ou juste apres `-VERBOSITY`).
|
|
- `-PREC <n>` controle la precision decimale des exports ASCII (`-C_EXPORT_FMT ASC`).
|
|
|
|
## Mode comparaison a 2 nuages (`cloud_compare`)
|
|
|
|
En plus du mode `const_height` (nuage unique vs plan Z constant), un second mode compare 2 nuages
|
|
entre eux : le nuage du **haut** ("top", surface superieure de l'amas) et celui du **bas** ("bottom",
|
|
limite inferieure/base/socle). Motivation reelle : sur un vrai nuage industriel ("Amas 1 Nuage.las",
|
|
~150m de long), le mode `const_height` donnait un volume de ~50m3 alors que l'objet fait clairement
|
|
plus — cause identifiee par inspection de la heightmap : le nuage a une empreinte **diagonale** dans
|
|
sa bounding box (~150x113m), et surtout le plan Z constant (altitude min globale du nuage entier)
|
|
n'a aucun rapport avec la base reelle de l'amas si le fichier contient aussi du terrain environnant.
|
|
Avec le second nuage ("Plan d'ajustement NUAGE.las", une base fittee specifiquement sous l'amas), le
|
|
volume calcule est de ~14 244m3, stable a +/-4% meme a 16x de decimation (129 points) — la aussi
|
|
confirme par inspection visuelle (les deux nuages ont exactement la meme empreinte au sol).
|
|
**Enseignement general : le mode `const_height` n'est fiable QUE si le nuage ne contient QUE l'objet
|
|
mesure (pas de terrain/contexte environnant) ; sinon `cloud_compare` avec une base dediee est le seul
|
|
mode qui donne un chiffre correct.**
|
|
|
|
Implementation :
|
|
- **Appariement de densite** avant le niveau 0 : on calcule la distance mediane au plus proche voisin
|
|
de chaque nuage independamment, on prend le MAX des deux (`matchedSpacing`), puis on decime (une
|
|
seule fois, `-SS SPATIAL matchedSpacing`) le nuage le plus dense pour le ramener a la meme resolution
|
|
que l'autre — l'operation est un no-op si le nuage est deja plus clairseme que `matchedSpacing` (`-SS
|
|
SPATIAL` ne peut pas densifier). Le niveau 0 du mode `cloud_compare` EST ce nuage appari (contrairement
|
|
au mode `const_height` ou le niveau 0 est le nuage brut, jamais decime) : `spatialStep(niveau) =
|
|
initialStep * facteur^niveau` pour TOUS les niveaux 0..5 en mode compare (vs `null` puis
|
|
`initialStep * facteur^niveau` pour niveaux 1..5 en mode const_height).
|
|
- **Ordre de chargement pour `-VOLUME`** : `-O <top_comble.bin> -O <bottom_comble.bin> -VOLUME
|
|
-GRID_STEP g` (sans `-CONST_HEIGHT`, sans `-GROUND_IS_FIRST`). Verifie dans les sources
|
|
(`CommandVolume25D::process`) : le premier nuage charge = "ceil", le second = "ground" (sauf
|
|
`-GROUND_IS_FIRST` qui les inverse) — charger top puis bottom donne directement ceil=top,
|
|
ground=bottom, ce qui correspond au sens voulu.
|
|
- Remplissage des trous (voir section `-VOLUME` plus haut) applique aux DEUX nuages independamment,
|
|
a chaque niveau, avec le meme `gridStep`/`maxEdgeLength` adaptatifs que le mode single-cloud.
|
|
- La decimation des DEUX nuages est synchronisee (meme `spatialStep` a chaque niveau) ; si l'un des
|
|
deux tombe sous `minPointsToContinue`, toute la progression s'arrete (les deux surfaces sont
|
|
necessaires pour une comparaison valide).
|
|
|
|
## Paquet apt Debian `cloudcompare` (trixie, 2.13.2) — limitations et contournements
|
|
|
|
Choix d'archi : `debian:trixie-slim` + `apt-get install cloudcompare` plutot qu'une compilation depuis
|
|
les sources (des heures de build, fragile). Debian trixie est la seule distro testee avec une version
|
|
recente (2.13.2) directement en apt `main` (Ubuntu jammy/noble n'ont que 2.11.3 en `universe`).
|
|
Mais ce paquet est **allege par rapport au build officiel Windows** :
|
|
|
|
1. **Pas de plugin LAS/LAZ** (`dpkg -L cloudcompare` ne montre que
|
|
`libQCORE_IO_PLUGIN.so`, aucun `QLAS_IO_PLUGIN`). Ouvrir un `.las`/`.laz` donne :
|
|
`[Load] Can't guess file format: unhandled file extension 'las'`.
|
|
**Solution retenue** : conversion LAS/LAZ/COPC -> ASCII XYZ via `laspy` (Python, `python/las_to_xyz.py`)
|
|
*avant* tout traitement CloudCompare. L'import ASCII XYZ, lui, fonctionne nativement sans aucun
|
|
plugin (verifie : `CloudCompare -O fichier.xyz` marche directement sur ce paquet). Un `.copc.laz`
|
|
est lu par laspy comme un LAZ standard (l'indexation octree COPC est ignoree, seuls les points
|
|
comptent ici).
|
|
2. **Export GeoTIFF cassé** (voir `-RASTERIZE` ci-dessus). **Solution retenue** : les cartes de hauteur
|
|
sont generees nous-memes (`python/render_images.py heightmap`) via export ASCII du nuage +
|
|
binning `scipy.stats.binned_statistic_2d` + rendu `matplotlib`, sans passer par le rasterizer
|
|
CloudCompare.
|
|
3. **CloudCompare reste une appli Qt, meme en `-SILENT`** : plante avec `QXcbConnection: Could not
|
|
connect to display` sans serveur X. Necessite `xvfb`. On demarre UN Xvfb persistant dans
|
|
`docker/entrypoint.sh` (pas un `xvfb-run` par appel CLI, plus rapide et evite les conflits de
|
|
lockfile entre appels concurrents).
|
|
4. Si un futur besoin necessite E57 : le meme probleme de plugin manquant se posera probablement
|
|
(a verifier — `QE57_IO_PLUGIN` n'apparaissait pas non plus dans `dpkg -L cloudcompare`).
|
|
|
|
**Si CloudCompare est mis a jour dans une future version du paquet Debian et regagne GDAL/LAS**, ces
|
|
contournements resteront fonctionnels sans rien casser (ils n'utilisent jamais les fonctionnalites
|
|
manquantes), mais pourraient etre simplifies.
|
|
|
|
## Pieges d'implementation generaux (au-dela de CloudCompare)
|
|
|
|
- **Chemins `/c/...` (style MSYS/git-bash) vs chemins Windows natifs** : un `python.exe` natif Windows
|
|
ne comprend PAS `/c/Users/...` — seulement `C:/Users/...` ou `C:\Users\...`. `curl`/`cat`/`ls`
|
|
(MSYS) acceptent les deux, mais un process Windows natif lance depuis bash avec un chemin `/c/...`
|
|
en argument echouera silencieusement (`FileNotFoundError`) si ce chemin est passe tel quel a une
|
|
fonction Python (`open(...)`) plutot que d'etre resolu par le shell. Toujours utiliser des chemins
|
|
style `C:/...` quand on passe un chemin en argument a un outil natif Windows depuis bash.
|
|
- **TypeScript + closures + `let` mutable** : `tsc` peut perdre le narrowing d'une variable `let`
|
|
mutee dans une closure asynchrone appelee avant un `return` conditionnel (erreur `Property 'x' does
|
|
not exist on type 'never'`). Contournement : accumuler dans un tableau (`push`) plutot que de muter
|
|
une variable `best` capturee, puis trier/reduire a la fin.
|
|
- **Nest CLI + fichiers de test** : `nest build` doit utiliser `tsconfig.build.json` (qui exclut
|
|
`**/*.spec.ts`) pour ne pas emettre les tests dans `dist/`. Le `tsconfig.json` de base (sans
|
|
exclude) reste utilise par `tsc --noEmit` (script `typecheck`) et par `ts-jest`, pour que les tests
|
|
soient bien type-checkes.
|
|
- **`better-sqlite3`** necessite des outils de build natifs (python3/make/g++) — presents par defaut
|
|
dans l'image `node:20-bookworm` utilisee pour le stage de build, absents volontairement du stage
|
|
final (juste `node_modules` deja compile est copie).
|
|
|
|
## Deploiement (Coolify)
|
|
|
|
- Image construite en deux etapes : build (Node, avec lint+typecheck+test+compilation) puis runtime
|
|
(Debian + CloudCompare + xvfb + Python). Le build Docker **echoue** si lint, typecheck ou tests
|
|
echouent (`RUN npm run lint|typecheck|test` avant `RUN npm run build` dans le Dockerfile) — sert de
|
|
garde-fou avant tout deploiement automatique via webhook.
|
|
- `docker-compose.yml` utilise un **volume nomme** (`app-data:/data`), pas un bind mount vers `./data` :
|
|
Coolify re-clone le repo a chaque deploiement, un bind mount relatif au checkout perdrait les
|
|
donnees (uploads, resultats, base sqlite) a chaque redeploy.
|
|
- `HEALTHCHECK` dans le Dockerfile (`curl` sur `/health`, route publique non authentifiee) : Coolify
|
|
l'utilise pour determiner si le deploiement a reussi.
|
|
- Le port d'ecoute est configurable via `PORT` (`config.ts` lit `process.env.PORT`, defaut 3000).
|
|
- Auth HTTP Basic globale (UI + API) via middleware Express (`app.use`, pas un Guard Nest) enregistre
|
|
dans `main.ts` **avant** `app.listen()` — verifie empiriquement que ca protege bien aussi les
|
|
fichiers statiques servis par `ServeStaticModule` (pas seulement les routes `@Controller`), malgre
|
|
le fait que `ServeStaticModule` s'enregistre via le systeme de modules Nest plutot que directement
|
|
sur l'instance `app`. Desactivable si `AUTH_USERNAME`/`AUTH_PASSWORD_HASH` absents (dev local).
|
|
|
|
## Constantes du pipeline (voir `src/config.ts`)
|
|
|
|
- `decimationFactor = 2`, `decimationSteps = 5` (fixes par la spec, pas exposes en config utilisateur).
|
|
- `gridStepMultiplier = 2` : grid step de `-VOLUME` a CHAQUE niveau = pas spatial de ce niveau x2
|
|
(niveau 0 : `initialStep x2`). Adapte dynamiquement, pas fige — voir section `-VOLUME` plus haut.
|
|
- `statsSampleCap = 50000` : nb de points echantillonnes pour estimer la distance mediane au plus
|
|
proche voisin (KD-tree scipy) qui sert de pas spatial initial.
|
|
- `maxEdgeLengthMultiplier = 3` : distance max d'interpolation Delaunay (remplissage de trous) a
|
|
CHAQUE niveau = gridStep de ce niveau x3. Override utilisateur possible (`maxEdgeLengthOverride`,
|
|
valeur absolue en metres) si ce defaut comble trop/pas assez pour un nuage donne.
|
|
- `minPointsToContinue = 25` : la decimation s'arrete si un niveau (ou l'un des 2 nuages en mode
|
|
compare) tombe en dessous.
|
|
- `matchingCellsWarnThreshold = 90` : seuil d'affichage "a verifier" dans l'UI.
|