script-volume-auto/CLAUDE.md
Nicolas Fryder 051209b022 Suppression de run, parametres CC exposes, pourcentages vs L0, carte de comparaison unique
- Bouton "Supprimer" sur chaque run (liste + detail), DELETE deja expose cote API.
- Tous les parametres CloudCompare mirroires dans l'UI (mirroir de la boite "Compute Volume" du
  GUI) : strategie de remplissage (leave_empty/min/max/custom/interpolate/kriging), hauteur de
  cellule (moyenne/min/max/mediane, -PROJ), direction de projection (X/Y/Z, -VERT_DIR),
  multiplicateur de grille. Nuage comble desormais exporte en ASCII (pas .bin) : -VOLUME le lit
  nativement, evite un aller-retour inutile puisqu'il faut de toute facon un export ASCII pour la
  carte de comparaison.
- Tableau : volume/surface/volume ajoute/volume retire avec % absolu et % relatif vs niveau 0.
  Matching cells retire du tableau et des graphiques (mais toujours calcule/stocke, juste plus
  affiche).
- Carte de comparaison unique par niveau (bleu->rouge façon CloudCompare, colormap jet) a la place
  des heightmaps separees par nuage : diff = ceil - ground (ou ceil - plan constant), calculee
  nous-memes par binning numpy sur une grille commune.
- README + CLAUDE.md : avertissement explicite sur la persistance Coolify quand le build pack est
  "Application"/Dockerfile plutot que "Docker Compose" (docker-compose.yml et son volume nomme sont
  alors completement ignores par Coolify, il faut un volume "Storages" configure manuellement).
- Valide en local avec les 2 vrais nuages utilisateur (mode comparaison) : resultats identiques a
  avant (14 244 -> 14 722 m3 selon le niveau), cartes de comparaison generees a chaque niveau,
  bouton suppression teste via l'UI.
2026-07-10 17:03:33 +02:00

23 KiB

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 et la carte de comparaison (raw/L{n}_filled.xyz, en ASCII — pas .bin : -VOLUME lit l'ASCII nativement, et ca evite un aller-retour BIN inutile puisqu'on a de toute facon besoin d'un export ASCII pour la carte de comparaison Python). Le nuage BRUT (non comble, en .bin) reste utilise pour l'assemblage final et le chainage de decimation, afin de ne pas faire chainer l'interpolation d'un niveau vers le suivant (qui composerait l'erreur).
    • Toutes les strategies -EMPTY_FILL de CloudCompare sont exposees dans l'UI (mirroir de la boite de dialogue "Compute Volume" du GUI), pas seulement INTERP : LEAVE_EMPTY (aucun flag), MIN_H/MAX_H (hauteur min/max des cellules voisines), CUSTOM_H (necessite -CUSTOM_HEIGHT <valeur>), INTERP (necessite -MAX_EDGE_LENGTH <valeur>), KRIGING (necessite -KRIGING_KNN <n>). Voir emptyFillArgs() dans pipeline.service.ts.
    • -PROJ <AVG|MIN|MAX|MED> (type de projection par cellule, "Hauteur de cellule" dans le GUI) et -VERT_DIR <0|1|2> (direction de projection X/Y/Z) sont egalement exposes. Contrairement a -EMPTY_FILL, -VOLUME accepte bien -VERT_DIR nativement (verifie dans les sources) — on le passe donc aux DEUX commandes (-RASTERIZE pour le remplissage, -VOLUME pour le calcul) afin qu'elles restent coherentes. -PROJ en revanche n'existe QUE pour -RASTERIZE (comme -EMPTY_FILL : -VOLUME a PROJ_AVERAGE_VALUE code en dur) — mais comme le nuage envoye a -VOLUME est deja notre grille-comble (1 point/cellule), le "moyennage" interne de -VOLUME devient une passe-plat : c'est bien NOTRE -PROJ (au moment du remplissage) qui determine le resultat final, pas celui hardcode dans -VOLUME. INV_VAR (variance inverse) n'est pas expose : necessite un champ scalaire dedie qu'on n'a pas dans ce pipeline.
  • 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 images sont generees nous-memes (python/render_images.py diffmap) via export ASCII des nuages combles + binning scipy.stats.binned_statistic_2d sur une grille commune + rendu matplotlib (colormap jet, bleu->rouge façon CloudCompare), sans passer par le rasterizer CloudCompare. Une seule image de comparaison par niveau (ceil vs ground/plan constant), pas une carte par nuage — remplace l'ancienne approche (une heightmap par nuage) qui ne permettait pas de voir directement l'ecart entre les deux surfaces.
  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.
    • Mais si la resource Coolify est de type "Application" (build pack Dockerfile, pas "Docker Compose"), docker-compose.yml est completement ignore par Coolify, y compris son volumes:. Le VOLUME ["/data"] du Dockerfile cree alors juste un volume anonyme a chaque nouveau conteneur, jamais reattache au precedent -> toutes les donnees disparaissent a chaque redeploy sans aucune erreur visible. Il faut explicitement ajouter un volume persistant sur /data dans l'onglet "Storages" de l'application Coolify. Observe reellement sur ce projet : Coolify auto-detecte par defaut un repo avec package.json comme un projet Node et choisit le build pack Nixpacks (qui ignore le Dockerfile et tente de build un projet Node generique — echoue forcement ici, aucune connaissance de CloudCompare/xvfb/Python). Il faut forcer manuellement le build pack sur "Dockerfile" dans les reglages de l'application — et donc, une fois ce changement fait, ne pas oublier le volume "Storages" puisqu'on est passe en mode "Application".
  • 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).
  • Piege .env + hash bcrypt : un hash bcrypt contient des $ ($2a$10$eg...). Docker Compose interprete $ dans un fichier .env comme le debut d'une substitution de variable — sans echappement, AUTH_PASSWORD_HASH=$2a$10$eg... est tronque silencieusement a $2a$10. (verifie via docker compose config : la valeur affichee etait bien tronquee, le reste du hash disparaissait sans aucune erreur). Fix : dans .env, doubler chaque $ du hash ($$2a$$10$$eg...) ; docker compose les collapse en $ uniques au runtime (reverifie via docker exec ... printenv AUTH_PASSWORD_HASH : le conteneur recoit bien le hash original complet, un seul $). Documente dans .env.example. Coolify (qui n'utilise pas de fichier .env mais son propre formulaire "Environment Variables") n'a probablement pas ce probleme puisqu'il n'y a pas de fichier .env a parser — a confirmer au premier deploiement avec l'auth active.
    • Autre piege distinct rencontre en testant : apres docker compose up -d (sans --build), le conteneur peut tourner sur une image cachee obsolete ne contenant pas les derniers changements de code (auth absente, etc.) sans aucun avertissement. Toujours utiliser docker compose up -d --build en local pour etre sur de tester le code courant.

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.