mirror of
https://github.com/brunopostle/ifcurl.git
synced 2026-07-12 10:18:14 +00:00
Initial commit
This commit is contained in:
commit
3d2ff476d6
2 changed files with 479 additions and 0 deletions
203
PLAN.md
Normal file
203
PLAN.md
Normal file
|
|
@ -0,0 +1,203 @@
|
||||||
|
# IFC URL — Development Plan
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
IFC URL requires work across four distinct areas:
|
||||||
|
|
||||||
|
1. A Python core library and CLI tool
|
||||||
|
2. A preview service with Gitea integration
|
||||||
|
3. Desktop app integration (Bonsai, IFC Viewer)
|
||||||
|
4. The federation extension to `IFCDOCUMENTREFERENCE`
|
||||||
|
|
||||||
|
The Python core is the foundation for all other work. The Gitea integration
|
||||||
|
and desktop app work can proceed in parallel once the core exists.
|
||||||
|
|
||||||
|
### Language ownership
|
||||||
|
|
||||||
|
| Component | Language | Rationale |
|
||||||
|
|---|---|---|
|
||||||
|
| URL parsing, git fetch, selector execution, rendering | Python | Built on IfcOpenShell |
|
||||||
|
| Bonsai plugin | Python | Bonsai is a Blender/Python addon |
|
||||||
|
| CLI tool | Python | Wraps the core library |
|
||||||
|
| Preview service | Python | Wraps the core library |
|
||||||
|
| Gitea plugin | Go | Gitea is written in Go; the plugin only calls an HTTP endpoint |
|
||||||
|
| Web viewer integration | JavaScript | Future, not in current scope |
|
||||||
|
|
||||||
|
The Python core is a strong candidate for eventual contribution to
|
||||||
|
IfcOpenShell, but this is not a prerequisite for any phase.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 1 — Python core and CLI tool
|
||||||
|
|
||||||
|
Build a Python library that resolves an `ifc://` URL and produces a rendered
|
||||||
|
PNG. This validates the spec against real IFC files and is the foundation for
|
||||||
|
all subsequent work.
|
||||||
|
|
||||||
|
### Library responsibilities
|
||||||
|
|
||||||
|
- Parse an `ifc://` URL into its components
|
||||||
|
- Infer git transport from URL structure (SSH, HTTPS, local)
|
||||||
|
- Fetch the IFC file from the repository at the specified ref
|
||||||
|
- Execute the IfcOpenShell selector against the loaded model
|
||||||
|
- Apply camera (perspective and orthographic), clipping planes, and
|
||||||
|
visibility mode
|
||||||
|
- Render a static PNG
|
||||||
|
|
||||||
|
### CLI interface
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ifc-url render "ifc://example.com/org/repo@abc123?path=model.ifc&selector=IfcWall&fov=60"
|
||||||
|
ifc-url render "ifc://example.com/org/repo@heads/main?path=model.ifc&camera=0,0,8,0,0,-1,0,1,0&scale=50"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
- IfcOpenShell handles selector execution, model parsing, and geometry
|
||||||
|
- Rendering requires a headless approach; options include IfcOpenShell's own
|
||||||
|
geometry processing with a software or headless OpenGL renderer
|
||||||
|
- Git access uses the platform credential store for HTTPS and ssh-agent or
|
||||||
|
key files for SSH
|
||||||
|
- The CLI accepts the URL as a plain string so it can be called from
|
||||||
|
non-Python contexts
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2 — Preview service
|
||||||
|
|
||||||
|
Wrap the Phase 1 library in an HTTP service for use by Gitea and other
|
||||||
|
consumers.
|
||||||
|
|
||||||
|
### Endpoint
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /preview
|
||||||
|
body: { url: "ifc://..." }
|
||||||
|
response: image/png
|
||||||
|
```
|
||||||
|
|
||||||
|
### Caching
|
||||||
|
|
||||||
|
| Tier | Cache key | Contents | Notes |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 2 | commit hash | Parsed model structure | Shared across requests |
|
||||||
|
| 3 | commit hash + selector | Resolved GUID set | |
|
||||||
|
| 4 | full URL hash | Rendered PNG | Not applied to mutable refs |
|
||||||
|
|
||||||
|
Mutable refs (`@heads/`, `@HEAD`) must not be cached at tier 4 since the
|
||||||
|
underlying commit may change. Commit hash refs are immutable and can be
|
||||||
|
cached indefinitely at all tiers.
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
|
||||||
|
- **Co-located with Gitea** — uses the requesting user's Gitea session token,
|
||||||
|
passed from the Gitea plugin
|
||||||
|
- **Standalone** — sideloaded token configuration per git host
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 3 — Gitea integration
|
||||||
|
|
||||||
|
Add `ifc://` link detection and preview embedding to Gitea.
|
||||||
|
|
||||||
|
### Go plugin responsibilities
|
||||||
|
|
||||||
|
- Detect `ifc://` links in post and comment content
|
||||||
|
- Call the preview service with the URL and the current user's session token
|
||||||
|
- Embed the returned PNG inline with a link that opens the URL
|
||||||
|
- Degrade gracefully when the preview service is unavailable (show the raw
|
||||||
|
URL rather than failing)
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
- The plugin has no IFC awareness; all IFC logic lives in the preview service
|
||||||
|
- Repeated embeds of the same URL across different posts do not trigger
|
||||||
|
re-renders — the preview service cache handles deduplication
|
||||||
|
- The preview service may run as a companion process on the same host or
|
||||||
|
as a separately configured external service
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 4 — Bonsai integration
|
||||||
|
|
||||||
|
Add `ifc://` handling to Bonsai.
|
||||||
|
|
||||||
|
### Protocol handler
|
||||||
|
|
||||||
|
Register `ifc://` as an OS protocol handler on installation (Windows,
|
||||||
|
macOS, Linux). On receiving a URL, resolve the git source, load the model,
|
||||||
|
and apply the view state from the URL parameters.
|
||||||
|
|
||||||
|
### "Copy view URL"
|
||||||
|
|
||||||
|
A button that generates an `ifc://` URL from the current Bonsai state:
|
||||||
|
|
||||||
|
- Git remote, ref, and file path of the open model
|
||||||
|
- Active selection converted to an IfcOpenShell selector
|
||||||
|
- Current camera converted to the `camera` + `fov` or `scale` format,
|
||||||
|
in IFC world coordinates
|
||||||
|
- Active clipping planes as `clip` parameters
|
||||||
|
- Current visibility mode
|
||||||
|
|
||||||
|
The button always generates a commit hash ref by default. Generating a
|
||||||
|
`@heads/` ref is a secondary option for authoring links intended to track
|
||||||
|
a branch.
|
||||||
|
|
||||||
|
### Federation
|
||||||
|
|
||||||
|
When opening a model via `ifc://`, Bonsai resolves `IFCDOCUMENTREFERENCE`
|
||||||
|
locations using the repo and ref from the root file's URL as context.
|
||||||
|
`ifc://` locations in linked model references are resolved recursively.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 5 — IFC Viewer integration
|
||||||
|
|
||||||
|
Equivalent to Phase 4 for the IFC Viewer desktop application.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 6 — Federation extension
|
||||||
|
|
||||||
|
Extend the `IFCDOCUMENTREFERENCE` location field in Bonsai to accept
|
||||||
|
`ifc://` URLs, enabling cross-repo and cross-server federation.
|
||||||
|
|
||||||
|
### Tasks
|
||||||
|
|
||||||
|
- When saving a linked model reference, offer the option to write the
|
||||||
|
location as an `ifc://` URL rather than a relative path
|
||||||
|
- When a relative path would traverse above the repository root, prompt
|
||||||
|
the user to convert it to an `ifc://` URL
|
||||||
|
- Resolve `ifc://` location fields through the Phase 4 URL resolver
|
||||||
|
|
||||||
|
The transformation matrix in `IFCDOCUMENTREFERENCE` is unaffected by this
|
||||||
|
change. Phase 4 must be complete before this phase begins.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Future work
|
||||||
|
|
||||||
|
### BCF export
|
||||||
|
|
||||||
|
An additional preview service endpoint returning a BCF file:
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /bcf
|
||||||
|
body: { url: "ifc://..." }
|
||||||
|
response: application/zip
|
||||||
|
```
|
||||||
|
|
||||||
|
Resolves the selector to a GUID set and constructs a BCF viewpoint from the
|
||||||
|
camera, clipping planes, and visibility state. No implementation is planned
|
||||||
|
before Phase 2 is complete.
|
||||||
|
|
||||||
|
### Web viewer integration
|
||||||
|
|
||||||
|
JavaScript integration with web-based IFC viewers (IFC.js, xeokit) and
|
||||||
|
browser-side forum embedding. Not in current scope.
|
||||||
|
|
||||||
|
### IfcOpenShell contribution
|
||||||
|
|
||||||
|
Once the Python core is stable, the URL parsing and resolution logic is a
|
||||||
|
candidate for contribution to IfcOpenShell as a shared utility. This is not
|
||||||
|
a prerequisite for any phase.
|
||||||
276
SPECIFICATION.md
Normal file
276
SPECIFICATION.md
Normal file
|
|
@ -0,0 +1,276 @@
|
||||||
|
# IFC URL — Specification
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
|
||||||
|
IFC URL is a URL scheme for addressing a specific view of an IFC model stored
|
||||||
|
in a Git repository. A single URL encodes the model source, an optional
|
||||||
|
element selection, and an optional camera viewpoint.
|
||||||
|
|
||||||
|
The scheme identifier is `ifc://`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Git source encoding
|
||||||
|
|
||||||
|
### Transport inference
|
||||||
|
|
||||||
|
The git transport is inferred from the URL structure. No transport prefix is
|
||||||
|
required.
|
||||||
|
|
||||||
|
| URL form | Inferred transport |
|
||||||
|
|---|---|
|
||||||
|
| `ifc://user@host/org/repo` | SSH |
|
||||||
|
| `ifc://host/org/repo` | HTTPS |
|
||||||
|
| `ifc:///path/to/repo` | Local file |
|
||||||
|
|
||||||
|
The presence of `user@` userinfo signals SSH. A bare hostname signals HTTPS.
|
||||||
|
An empty authority (triple slash) signals a local file path.
|
||||||
|
|
||||||
|
Credentials are never embedded in the URL. Authentication is handled by the
|
||||||
|
platform's git credential store (desktop apps) or by sideloaded tokens
|
||||||
|
(preview service).
|
||||||
|
|
||||||
|
SCP-style SSH addresses (`git@host:org/repo`) are normalised to
|
||||||
|
`ifc://git@host/org/repo` when generating URLs.
|
||||||
|
|
||||||
|
### Ref format
|
||||||
|
|
||||||
|
Refs follow git's namespace structure to prevent ambiguity between branches
|
||||||
|
and tags that share a name.
|
||||||
|
|
||||||
|
| Ref type | Form | Example |
|
||||||
|
|---|---|---|
|
||||||
|
| Branch | `@heads/<name>` | `@heads/main` |
|
||||||
|
| Tag | `@tags/<name>` | `@tags/v1.2` |
|
||||||
|
| Commit hash | `@<hash>` | `@abc123def` |
|
||||||
|
| Default branch | `@HEAD` | `@HEAD` |
|
||||||
|
|
||||||
|
The `@` character delimits the repository path from the ref. It is always
|
||||||
|
present.
|
||||||
|
|
||||||
|
### Full structure
|
||||||
|
|
||||||
|
```
|
||||||
|
ifc://[user@]host/org/repo@<ref>?<parameters>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Parameters
|
||||||
|
|
||||||
|
All parameters are optional. A URL with no parameters opens the model at its
|
||||||
|
default view with no selection.
|
||||||
|
|
||||||
|
### `path`
|
||||||
|
|
||||||
|
Path to the IFC file within the repository, relative to the repository root.
|
||||||
|
|
||||||
|
```
|
||||||
|
path=models/architectural/building.ifc
|
||||||
|
```
|
||||||
|
|
||||||
|
May be omitted when the target is a federated root file whose linked model
|
||||||
|
references are defined within the IFC itself (see section 5).
|
||||||
|
|
||||||
|
### `selector`
|
||||||
|
|
||||||
|
An IfcOpenShell selector expression, percent-encoded. The selector syntax is
|
||||||
|
specified at:
|
||||||
|
|
||||||
|
https://docs.ifcopenshell.org/ifcopenshell-python/selector_syntax.html
|
||||||
|
|
||||||
|
Selectors are single-line expressions. Filters within a group are
|
||||||
|
comma-separated; groups are joined with `+` for union.
|
||||||
|
|
||||||
|
```
|
||||||
|
selector=IfcWall
|
||||||
|
selector=IfcWall,+Name="Core+Wall"
|
||||||
|
selector=IfcWall+IfcSlab
|
||||||
|
selector=IfcWall,+Pset_WallCommon.FireRating=2HR
|
||||||
|
selector=325Q7Fhnf67OZC$$r43uzK
|
||||||
|
```
|
||||||
|
|
||||||
|
When omitted, no selection is applied.
|
||||||
|
|
||||||
|
### `camera`
|
||||||
|
|
||||||
|
Nine comma-separated decimal values defining the camera in IFC world
|
||||||
|
coordinates (see section 4):
|
||||||
|
|
||||||
|
```
|
||||||
|
camera=px,py,pz,dx,dy,dz,ux,uy,uz
|
||||||
|
```
|
||||||
|
|
||||||
|
| Values | Meaning |
|
||||||
|
|---|---|
|
||||||
|
| `px,py,pz` | Camera position |
|
||||||
|
| `dx,dy,dz` | View direction vector (unit vector) |
|
||||||
|
| `ux,uy,uz` | Up vector (unit vector) |
|
||||||
|
|
||||||
|
`camera` must be accompanied by either `fov` (perspective) or `scale`
|
||||||
|
(orthographic). Their presence signals the projection type. Exactly one must
|
||||||
|
be present if `camera` is present.
|
||||||
|
|
||||||
|
When `camera` is omitted entirely, the viewer fits the view to the selection
|
||||||
|
if a selector is present, or shows the model's default view otherwise.
|
||||||
|
|
||||||
|
### `fov`
|
||||||
|
|
||||||
|
Field of view in degrees. Signals perspective projection.
|
||||||
|
|
||||||
|
```
|
||||||
|
fov=60
|
||||||
|
```
|
||||||
|
|
||||||
|
### `scale`
|
||||||
|
|
||||||
|
View-to-world scale. Signals orthographic projection. The value represents
|
||||||
|
how many model units correspond to one unit in the normalised view, equivalent
|
||||||
|
to BCF's `ViewToWorldScale`.
|
||||||
|
|
||||||
|
```
|
||||||
|
scale=50
|
||||||
|
```
|
||||||
|
|
||||||
|
### `clip`
|
||||||
|
|
||||||
|
A clipping plane, defined by a point on the plane and a normal vector pointing
|
||||||
|
toward the visible side. All values are in IFC world coordinates.
|
||||||
|
|
||||||
|
```
|
||||||
|
clip=px,py,pz,nx,ny,nz
|
||||||
|
```
|
||||||
|
|
||||||
|
Repeatable. Multiple `clip` parameters define the intersection of their
|
||||||
|
half-spaces.
|
||||||
|
|
||||||
|
```
|
||||||
|
clip=0,0,3,0,0,-1&clip=0,0,0,0,0,1
|
||||||
|
```
|
||||||
|
|
||||||
|
### `visibility`
|
||||||
|
|
||||||
|
Controls how selected and unselected elements are displayed.
|
||||||
|
|
||||||
|
| Value | Behaviour |
|
||||||
|
|---|---|
|
||||||
|
| `highlight` | Show all elements, highlight selection (default) |
|
||||||
|
| `ghost` | Show selection normally, dim all other elements |
|
||||||
|
| `isolate` | Show only selected elements, hide all others |
|
||||||
|
|
||||||
|
When omitted, `highlight` is assumed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Coordinate system
|
||||||
|
|
||||||
|
All spatial values (`camera`, `clip`) are expressed in the IFC project's
|
||||||
|
world coordinate system, consistent with BCF viewpoint conventions. Local
|
||||||
|
coordinate systems, object placements, and georeferenced survey coordinates
|
||||||
|
are not used.
|
||||||
|
|
||||||
|
Where an IFC file uses `IfcMapConversion` (IFC 4.1+) to define a
|
||||||
|
georeferenced coordinate system, camera and clipping plane values remain in
|
||||||
|
IFC world coordinates, not in the mapped survey coordinate system.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Federation
|
||||||
|
|
||||||
|
Federated models are referenced within the IFC file itself using
|
||||||
|
`IFCDOCUMENTINFORMATION` with a `LINKED_MODEL` purpose, associated with the
|
||||||
|
root `IFCPROJECT` entity via `IFCRELASSOCIATESDOCUMENT`. The location is
|
||||||
|
carried in `IFCDOCUMENTREFERENCE`:
|
||||||
|
|
||||||
|
```
|
||||||
|
#46471=IFCDOCUMENTINFORMATION('X','model.ifc',$,$,$,$,'LINKED_MODEL',$,$,$,$,$,$,$,$,$,$);
|
||||||
|
#46472=IFCRELASSOCIATESDOCUMENT('0mypTfsjT...',$,$,$,(#10510),#46471);
|
||||||
|
#46473=IFCDOCUMENTREFERENCE('../conservatory-ifc/model.ifc','<4x4 matrix>',$,$,#46471);
|
||||||
|
```
|
||||||
|
|
||||||
|
The second field of `IFCDOCUMENTREFERENCE` is a 4x4 transformation matrix
|
||||||
|
(sixteen comma-separated values) that positions the linked model in the shared
|
||||||
|
coordinate system. This is resolved entirely by the viewer.
|
||||||
|
|
||||||
|
### Location field values
|
||||||
|
|
||||||
|
The location field (first field of `IFCDOCUMENTREFERENCE`) supports:
|
||||||
|
|
||||||
|
- **Relative path** — resolved relative to the root IFC file's directory
|
||||||
|
within the repository, using the same repo and ref context
|
||||||
|
- **Absolute local path** — resolved on the local filesystem (desktop use only)
|
||||||
|
- **`ifc://` URL** — resolved as a full IFC URL, enabling cross-repo and
|
||||||
|
cross-server federation
|
||||||
|
|
||||||
|
When a relative path would traverse above the repository root, an `ifc://`
|
||||||
|
URL must be used instead. This is the primary motivation for supporting
|
||||||
|
`ifc://` in the location field.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. BCF interoperability
|
||||||
|
|
||||||
|
The camera and selection in an IFC URL map directly to BCF viewpoint concepts.
|
||||||
|
|
||||||
|
**IFC URL to BCF viewpoint:**
|
||||||
|
1. Resolve the selector against the model to obtain a set of GUIDs
|
||||||
|
2. Construct a BCF `PerspectiveCamera` or `OrthogonalCamera` from the camera
|
||||||
|
parameters
|
||||||
|
3. Add clipping planes if present
|
||||||
|
4. Populate `Components/Selection` with the resolved GUIDs
|
||||||
|
|
||||||
|
**BCF viewpoint to IFC URL:**
|
||||||
|
1. Extract the GUID list from `Components/Selection`
|
||||||
|
2. Construct an IfcOpenShell GUID selector
|
||||||
|
3. Convert the BCF camera to `camera`, `fov` or `scale` parameters
|
||||||
|
4. Convert clipping planes to `clip` parameters
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Viewer compliance
|
||||||
|
|
||||||
|
A compliant viewer must:
|
||||||
|
|
||||||
|
- Register `ifc://` as an OS-level protocol handler
|
||||||
|
- Resolve the git source (repo, ref, path) and fetch the IFC file
|
||||||
|
- Resolve an IfcOpenShell selector against the loaded model
|
||||||
|
- Apply perspective camera (`camera` + `fov`)
|
||||||
|
- Apply orthographic camera (`camera` + `scale`)
|
||||||
|
- Apply clipping planes (`clip`)
|
||||||
|
- Apply visibility mode (`highlight`, `ghost`, `isolate`)
|
||||||
|
- Resolve relative `IFCDOCUMENTREFERENCE` paths using the root file's repo
|
||||||
|
and ref context
|
||||||
|
- Resolve `ifc://` `IFCDOCUMENTREFERENCE` locations
|
||||||
|
|
||||||
|
Optional extensions:
|
||||||
|
|
||||||
|
- Semantic colouring
|
||||||
|
- Advanced visual styles beyond the three visibility modes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Complete examples
|
||||||
|
|
||||||
|
Open a model at the default view:
|
||||||
|
|
||||||
|
```
|
||||||
|
ifc://example.com/org/project@heads/main?path=models/building.ifc
|
||||||
|
```
|
||||||
|
|
||||||
|
Perspective view of selected walls:
|
||||||
|
|
||||||
|
```
|
||||||
|
ifc://git@example.com/org/project@abc123def?path=models/building.ifc&selector=IfcWall,+Name="Core+Wall"&camera=10,20,5,0,-1,0,0,0,1&fov=60&visibility=ghost
|
||||||
|
```
|
||||||
|
|
||||||
|
Orthographic plan view at level 2 with a clipping plane:
|
||||||
|
|
||||||
|
```
|
||||||
|
ifc://example.com/org/project@tags/v2.0?path=models/building.ifc&camera=0,0,8,0,0,-1,0,1,0&scale=50&clip=0,0,5,0,0,-1
|
||||||
|
```
|
||||||
|
|
||||||
|
Local file, default branch:
|
||||||
|
|
||||||
|
```
|
||||||
|
ifc:///home/alice/projects/office@HEAD?path=model.ifc
|
||||||
|
```
|
||||||
Loading…
Add table
Reference in a new issue