Repository navigation
feat(projects): add multi-model IFC viewer with shared-coordinate alignment - #811
Open
dronov-dmitry wants to merge 6 commits into
Open
dronov-dmitry wants to merge 6 commits into
dronov-dmitry wants to merge 6 commits into
Conversation
…gnment
- projects/viewer.{html,ts}: load several IFC files into one scene with the
single "Add IFC…" button (it appends to the current session, so an empty
scene is a valid starting point: open viewer.html with no query params,
then add files or drop them on the page), models panel with per-model
hide/unload, coordinate mode selector (shared first-model frame vs
per-file normalization), drag & drop, repeated ?model= URL params and IFC
signature validation before conversion
- vite.config.ts: always add a "New empty session" link to the dev server
home page and list projects/<name>/*.ifc below it (the section rebuilds
on projects/ changes)
- .gitignore, projects/README.md: keep local models and the generated gallery
out of the repo, document the layout
Closes ThatOpen#810
dronov-dmitry
force-pushed
the
feat/multi-model-ifc-viewer
branch
from
October 10, 2026 19:14
0352b87 to
803ed2b
Compare
…page - rename the home page link to "New empty session (multimodel)" so it matches what the session actually is (any number of IFC files in one scene) - remove the optional "All projects" gallery link: the gallery is a local generated artifact and is not part of the repo; the home page already lists every projects/<name>/*.ifc on its own - regenerate index.html with forward-slash example paths (the committed copy was generated on Windows and used backslashes, which break on the web)
projects/viewer.ts imports three directly; without a root dependency the dev server resolves it only through workspace hoisting and the vite dedupe option has nothing to dedupe against.
Adds the ТЗ-002 add-ons lab (12 add-ons) on top of the multi-model viewer: - addons.ts: mutually-exclusive radio list + per-addon panels for Highlighter, Hoverer, Hider, Clipper, Measurements (length/area/angle/ volume), ItemsFinder, Classifier, BoundingBoxer, Mesher, Views, Viewpoints and Marker. - Clipper now cuts the model reliably: planes go to the renderer's GLOBAL clipping list (local per-material planes never clip fragment shaders compiled before the plane existed), the plane is kept parallel to the double-clicked face and slid through the model center so the two halves are visible, and Delete/'Delete all'/drag re-sync the cut. - viewer.ts: RendererWith2D (CSS2D overlay for the Marker add-on), fire-and-forget guard for fragments.core.update (worker FINISH fence can hang on an empty scene), window.__bim addons/highlighter hooks. - vite.config.ts: alias @thatopen/components to the local core sources so the front package uses the same component registry; .gitignore the pnpm artifacts (the repo installs with yarn).
Length and Area reused the last pointermove-driven pick (lastPick / _temp.point), which goes stale whenever the camera moves without the cursor moving (wheel zoom suspends picks for cameraStopDelay ms), so measurements landed where the cursor was, not on the double-clicked surface. Both tools now take a fresh pick at click time; Area also projects the click onto the in-progress polygon plane within pickTolerance so the shape stays planar. AngleMeasurement.endCreation now requires the third point (clickCount === 3): an external Enter after only two points would previously commit an angle whose end was still the origin.
…pper The Measurements add-on called only measurer.create() on dblclick and never endCreation(), so Area (free mode) and Volume — which commit their result exclusively through endCreation() — never reached the list: no value, no fill, nothing to delete. Enter now finishes the in-progress area/volume measurement. The panel's read-only values list becomes a Clipper-style row list: each row shows the measurement value and a × button that deletes just that item (list.delete + onItemDeleted re-render), with Clear all kept for bulk removal. The dead .addon-values CSS rule is dropped.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds a multi-model IFC viewer with an add-ons lab (ТЗ-002), served by the existing dev server:
projects/viewer.html,projects/viewer.ts,projects/addons.ts. Several IFC files can be loaded into one window and placed in a common coordinate system; 12 add-ons can be toggled from the sidebar.What it solves
Add IFC…, which appends one or more files to the current session; drag & drop appends as well, and?model=can be repeated in the URL. Openingviewer.htmlwith no query params starts an empty session (the status reads "No models loaded — click "Add IFC…" or drop files on this page"), and unloading every model returns to it. There is no separate "replace" mode, so an existing scene cannot be dropped by accident.Hide/Showand unload (disposeModel), camera fit over all models that are currently visible (hidden ones are not framed).COORDINATE_TO_ORIGIN: true, so each file is normalized by its own coordination matrix (GetCoordinationMatrix→meshes.coordinates, readable viamodel.getCoordinates());IfcLoader.load(data, coordinate)only togglesfragments.core.settings.autoCoordinate, which re-expresses every later model in the first model's frame (coords_first − coords_i);shared (first model)(default) /from each file— switchable without reloading. The page callsload()withcoordinate = falseand owns the positioning via its ownapplyAlignment().Show all/Hide allcontrol:Highlighter,Hoverer,Hider— element select/hover/hide;Clipper— section planes;Measurements— length / area / angle / volume;ItemsFinder,Classifier,BoundingBoxer,Mesher— search/classify/bound/build meshes;Views,Viewpoints,Marker— saved cameras and CSS2D labels.Highlighterstarts on by default when a model is loaded.three.clippingPlaneslist (localClippingPlanes = false); local per-material planes never clip fragment meshes whose shaders were compiled before the plane existed;keepCameraSidekeeps the near half visible;Delete,Delete alland dragging the plane re-sync the cut.Measurementsadd-on got a round of usability fixes (commits825c497,a4bb3b0):measurer.create(), but both tools finalize exclusively throughendCreation()(free-mode Area accumulates polygon points, Volume accumulates picked items), so measurements never reached the list — no value, no fill, nothing to delete.Enternow finishes the in-progress area/volume measurement; Length and Angle keep finalizing through their own dblclick sequences.LengthMeasurementreused the pointermove-drivenlastPick, which goes stale whenever the camera moves without the cursor moving (wheel zoom suspends picks forcameraStopDelayms), andAreaMeasurementconsumed the same stale preview point for every vertex after the first. Both tools now take a fresh pick at click time; Area also projects the click onto the in-progress polygon plane withinpickToleranceso the shape stays planar.AngleMeasurement.endCreation()now requires the third point (clickCount === 3); previously an externalEnterafter only two points would commit an angle whose end was still the origin.×button that deletes just that item (list.delete+onItemDeletedre-render);Clear allis kept for bulk removal. The dead.addon-valuesCSS rule is dropped fromviewer.html.ISO-10303-21) is validated before conversion: the dev server answers200 text/htmlfor missing files, which previously crashed the tab, and a failing file no longer stops the rest of the batch.vite.config.tsadds aProjectssection with aNew empty sessionlink (always present, including a fresh clone with no local projects) followed by one link perprojects/<name>/*.ifc. The section is rebuilt wheneverprojects/changes..gitignorekeeps local models and the generated gallery out of the repo;projects/README.mddocuments the layout; pnpm artifacts (pnpm-lock.yaml,pnpm-workspace.yaml) are ignored (the repo installs with yarn).Verification
?model=/?title=preloading and hide/unload; 21/21 on two files picked at once, second-click lockout while a batch loads,Hide/Show/×, drag & drop of two files, thecoords_first − coords_iformula in both coordinate modes (positions reset infrom each file, restored inshared), several?model=params and a failing?model=URL. Fit-to-view was checked to frame only the visible models.Delete all).prettier --checkand the repo ESLint config pass for the new files; the measurement fixes also passtsc --noEmitfor the touchedpackages/frontfiles andprettier --checkon all changed files.packages/front/src/core/Marker/src/renderer-with-2d.ts(positioning-context guard so an absolutely positioned container keeps its layout),packages/front/src/measurement/{Length,Area,Angle}Measurement(click-time picks and the angle commit guard) andvite.config.ts(alias@thatopen/componentsto the local core sources so the front package shares the same component registry), but notpackage.jsonoryarn.lock.Additional context
Closes #810in the commit message).three,@thatopen/componentssources and the already-pinnedweb-ifc@0.0.77from unpkg (same as the other examples).window.__bim({ components, world, fragments, ifcLoader, addons, highlighter }) is a tiny debug hook used by the checks above — happy to drop it if unwanted.examples/) if that fits the repo structure better.What is the purpose of this pull request?
Before submitting the PR, please make sure you do the following:
feat(examples): add hello-world example).fixes #123).Video:
https://youtu.be/a8Wp80GinH8
Closes #810