mirror of
https://github.com/brunopostle/ifcurl.git
synced 2026-07-12 10:18:14 +00:00
viewer.html: implement clip= clipping planes
applyClipPlanes() parses all repeatable clip= params (6 floats: point + normal in IFC world coords), transforms each via toThree() into Three.js space, and sets world.renderer.three.clippingPlanes. syncCameraUrl already preserves clip= params when rebuilding the URL on camera movement. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
459047c42a
commit
a33a4f9296
2 changed files with 25 additions and 5 deletions
|
|
@ -1,4 +1,4 @@
|
|||
{"id":"ifcurl-bab","title":"Implement clip= parameter in viewer.html","description":"The ifc:// spec defines a repeatable clip= parameter (6 floats: point x,y,z + normal x,y,z in IFC world coords). The Python ifcurl renderer handles it, but viewer.html ignores it entirely — no parsing, no Three.js clipping planes, no round-trip into the URL on camera sync. Implement: parse clip= from the ifc:// URL, apply as THREE.Plane clipping planes to the renderer (with IFC→Three.js coordinate transform), and include current clip planes when rebuilding the URL.","status":"open","priority":2,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-04-24T05:45:58Z","created_by":"Bruno Postle","updated_at":"2026-04-24T05:45:58Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"id":"ifcurl-bab","title":"Implement clip= parameter in viewer.html","description":"The ifc:// spec defines a repeatable clip= parameter (6 floats: point x,y,z + normal x,y,z in IFC world coords). The Python ifcurl renderer handles it, but viewer.html ignores it entirely — no parsing, no Three.js clipping planes, no round-trip into the URL on camera sync. Implement: parse clip= from the ifc:// URL, apply as THREE.Plane clipping planes to the renderer (with IFC→Three.js coordinate transform), and include current clip planes when rebuilding the URL.","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-24T05:45:58Z","created_by":"Bruno Postle","updated_at":"2026-04-24T06:09:31Z","started_at":"2026-04-24T06:08:44Z","closed_at":"2026-04-24T06:09:31Z","close_reason":"applyClipPlanes() parses all clip= params, transforms IFC→Three.js coords via toThree(), constructs THREE.Plane objects and sets on world.renderer.three.clippingPlanes. syncCameraUrl already preserves clip= params unchanged.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"id":"ifcurl-yju","title":"viewer.html: structured form UI for ifc:// URL components","description":"Replace the single ifc:// URL text input with individual labelled fields for each component: host/repository, ref (branch/tag/commit), path (file within repo), selector (IfcOpenShell selector), camera, fov/scale. Editing any field should update the ifc:// URL and reload the model. The raw ifc:// URL should still be visible/copyable. This makes the viewer usable without needing to hand-edit a URL string.","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-23T21:07:11Z","created_by":"Bruno Postle","updated_at":"2026-04-23T21:32:17Z","started_at":"2026-04-23T21:12:42Z","closed_at":"2026-04-23T21:32:17Z","close_reason":"Structured form UI implemented with repo/ref/path/selector fields; crash recovered, changes committed.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"id":"ifcurl-2zf","title":"viewer.html: FOV control and restore fov=/scale= from URL","description":"Two gaps: (1) No UI to change the camera FOV — add a control (e.g. a small number input or slider in the toolbar) so the user can adjust perspective FOV or orthographic scale. (2) applyCameraParam reads position/direction but ignores the fov= and scale= params written by syncCameraUrl — restore these when placing camera from URL so a shared link opens at exactly the recorded viewpoint.","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-23T20:42:24Z","created_by":"Bruno Postle","updated_at":"2026-04-23T21:07:42Z","started_at":"2026-04-23T20:52:48Z","closed_at":"2026-04-23T21:07:42Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"id":"ifcurl-yga","title":"viewer.html: implement ifc:// selector= parameter","description":"The ifc:// URL spec supports a selector= param using IfcOpenShell selector syntax (e.g. selector=IfcWall, selector=IfcSlab.Name='Floor'). The viewer currently ignores it. Implement selector filtering in the browser: parse the selector= value, query matching elements from the loaded FragmentsModel, and apply highlight/isolate/ghost visibility to them. @thatopen/components has Highlighter and Hider components that may be usable. The selector syntax used is the IfcOpenShell Python selector syntax — a JS port or subset may be needed.","notes":"Selector syntax docs: https://docs.ifcopenshell.org/ifcopenshell-python/selector_syntax.html — Python implementation in ifcopenshell.util.selector module. Need a JS port or subset. Syntax includes: IfcType, .Attribute='value', #GlobalId, [pset.prop], boolean operators (+ for union, , for intersection within group).","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-23T20:42:22Z","created_by":"Bruno Postle","updated_at":"2026-04-24T05:58:30Z","started_at":"2026-04-24T05:51:01Z","closed_at":"2026-04-24T05:58:30Z","close_reason":"applySelector() implemented: byCategory classification + prefix subtype matching + hider.isolate(). Supports simple type names and + union. Attribute filters noted as future work.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
|
|
|
|||
|
|
@ -313,6 +313,19 @@
|
|||
history.replaceState(null, "", pageUrl.toString());
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Apply clip= planes from the ifc:// URL to the Three.js renderer.
|
||||
// Each clip= is 6 floats: point x,y,z + normal x,y,z in IFC world coords.
|
||||
// -----------------------------------------------------------------------
|
||||
function applyClipPlanes(world, clips) {
|
||||
const planes = clips.map(([px, py, pz, nx, ny, nz]) => {
|
||||
const p3 = toThree(px, py, pz);
|
||||
const n3 = toThree(nx, ny, nz).normalize();
|
||||
return new THREE.Plane(n3, -n3.dot(p3));
|
||||
});
|
||||
world.renderer.three.clippingPlanes = planes;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Apply selector= by isolating matching IFC elements.
|
||||
// Supports simple type names and + union: "IfcWall+IfcSlab".
|
||||
|
|
@ -476,10 +489,17 @@
|
|||
}
|
||||
model.useCamera(threeCamera);
|
||||
|
||||
const qIdxSel = ifcUrl.indexOf("?");
|
||||
const selectorStr = new URLSearchParams(
|
||||
qIdxSel < 0 ? "" : ifcUrl.slice(qIdxSel + 1)
|
||||
).get("selector") ?? "";
|
||||
const qIdxPost = ifcUrl.indexOf("?");
|
||||
const qsPost = new URLSearchParams(
|
||||
qIdxPost < 0 ? "" : ifcUrl.slice(qIdxPost + 1)
|
||||
);
|
||||
|
||||
const clips = qsPost.getAll("clip")
|
||||
.map(s => s.split(",").map(Number))
|
||||
.filter(v => v.length === 6 && !v.some(isNaN));
|
||||
if (clips.length) applyClipPlanes(world, clips);
|
||||
|
||||
const selectorStr = qsPost.get("selector") ?? "";
|
||||
if (selectorStr) {
|
||||
statusEl.textContent = "Applying selector…";
|
||||
await applySelector(components, selectorStr);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue