mirror of
https://github.com/brunopostle/ifcurl.git
synced 2026-07-12 02:08:13 +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