mirror of
https://github.com/brunopostle/ifcurl.git
synced 2026-07-12 10:18:14 +00:00
Deploy viewer as Forgejo static file; update footer.tmpl URL
viewer.html is now served directly from Forgejo at /assets/viewer.html, fetching IFC bytes via Forgejo's raw URL (same-origin, auth inherited). footer.tmpl updated to point to /assets/viewer.html instead of the ifcurl service. No proxy needed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
13b412c92c
commit
c2bbcc62c2
3 changed files with 219 additions and 6 deletions
|
|
@ -1,11 +1,11 @@
|
|||
{"id":"ifcurl-cmd","title":"Apply forward IFC→Three.js transform when setting camera from URL params","description":"When the viewer page loads with a camera= param in the ifc:// URL, the 9 floats are in IFC world coordinates. Apply the IFC→Three.js transform to convert them before passing them to the Three.js camera / camera-controls. This is the forward direction of the same transform used in the inverse task.","status":"open","priority":2,"issue_type":"bug","owner":"bruno@postle.net","created_at":"2026-04-23T06:12:38Z","created_by":"Bruno Postle","updated_at":"2026-04-23T06:12:38Z","dependencies":[{"issue_id":"ifcurl-cmd","depends_on_id":"ifcurl-ldk","type":"blocks","created_at":"2026-04-23T07:14:10Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0}
|
||||
{"id":"ifcurl-j2s","title":"Apply inverse IFC↔Three.js transform in syncCameraUrl","description":"Using the transform found in the research task, apply its inverse to camera.position, the direction vector, and camera.up before serialising them as the ifc:// camera param in syncCameraUrl. The result should be in IFC world coordinates (Z-up, matching BCF viewpoint conventions) not Three.js scene space.","status":"open","priority":2,"issue_type":"bug","owner":"bruno@postle.net","created_at":"2026-04-23T06:12:37Z","created_by":"Bruno Postle","updated_at":"2026-04-23T06:12:37Z","dependencies":[{"issue_id":"ifcurl-j2s","depends_on_id":"ifcurl-ldk","type":"blocks","created_at":"2026-04-23T07:14:09Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0}
|
||||
{"id":"ifcurl-ldk","title":"Find IFC↔Three.js coordinate transform in @thatopen/components source","description":"Grep ~/src/engine_components and ~/src/engine_fragment for the matrix or transform applied when an IFC model is loaded into Three.js scene space. IFC uses Z-up; Three.js uses Y-up. @thatopen/components almost certainly applies a 90° X-axis rotation (and possibly a scale) when constructing the Three.Group from IFC geometry. Record the exact transform so the inverse can be applied in syncCameraUrl.","status":"in_progress","priority":2,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-23T06:12:36Z","created_by":"Bruno Postle","updated_at":"2026-04-23T06:19:35Z","started_at":"2026-04-23T06:19:35Z","dependency_count":0,"dependent_count":2,"comment_count":0}
|
||||
{"id":"ifcurl-49o","title":"Deploy footer.tmpl to Forgejo custom templates directory","description":"Copy forgejo/templates/custom/footer.tmpl from the repo to /etc/forgejo/templates/custom/footer.tmpl on the Forgejo server. No Forgejo restart needed — custom templates are loaded dynamically. Verify the View in 3D button appears on an .ifc file page.","status":"open","priority":2,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-04-23T06:12:22Z","created_by":"Bruno Postle","updated_at":"2026-04-23T06:12:22Z","dependencies":[{"issue_id":"ifcurl-49o","depends_on_id":"ifcurl-ohq","type":"blocks","created_at":"2026-04-23T07:14:08Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0}
|
||||
{"id":"ifcurl-ohq","title":"Update footer.tmpl: point viewer button to Forgejo static file URL","description":"Change the VIEWER_BASE URL in forgejo/templates/custom/footer.tmpl from http://localhost:8000/viewer to the Forgejo static file URL determined by the research task (ifcurl-5t3). The button href should become something like /assets/viewer.html?url=ifc://... rather than the ifcurl service URL.","status":"open","priority":2,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-04-23T06:12:21Z","created_by":"Bruno Postle","updated_at":"2026-04-23T06:12:21Z","dependencies":[{"issue_id":"ifcurl-ohq","depends_on_id":"ifcurl-5t3","type":"blocks","created_at":"2026-04-23T07:14:07Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0}
|
||||
{"id":"ifcurl-4yu","title":"Deploy viewer.html to Forgejo server","description":"Copy the viewer static file from the repo to the correct location on the Forgejo server so it is served at its expected URL. Document the deployment step in the repo (e.g. Makefile target or install notes).","status":"open","priority":2,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-04-23T06:12:11Z","created_by":"Bruno Postle","updated_at":"2026-04-23T06:12:11Z","dependencies":[{"issue_id":"ifcurl-4yu","depends_on_id":"ifcurl-55f","type":"blocks","created_at":"2026-04-23T07:14:05Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0}
|
||||
{"id":"ifcurl-49o","title":"Deploy footer.tmpl to Forgejo custom templates directory","description":"Copy forgejo/templates/custom/footer.tmpl from the repo to /etc/forgejo/templates/custom/footer.tmpl on the Forgejo server. No Forgejo restart needed — custom templates are loaded dynamically. Verify the View in 3D button appears on an .ifc file page.","status":"closed","priority":2,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-04-23T06:12:22Z","created_by":"Bruno Postle","updated_at":"2026-04-23T19:47:16Z","closed_at":"2026-04-23T19:47:16Z","close_reason":"Closed","dependencies":[{"issue_id":"ifcurl-49o","depends_on_id":"ifcurl-ohq","type":"blocks","created_at":"2026-04-23T07:14:08Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0}
|
||||
{"id":"ifcurl-ohq","title":"Update footer.tmpl: point viewer button to Forgejo static file URL","description":"Change the VIEWER_BASE URL in forgejo/templates/custom/footer.tmpl from http://localhost:8000/viewer to the Forgejo static file URL determined by the research task (ifcurl-5t3). The button href should become something like /assets/viewer.html?url=ifc://... rather than the ifcurl service URL.","status":"closed","priority":2,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-23T06:12:21Z","created_by":"Bruno Postle","updated_at":"2026-04-23T19:47:15Z","started_at":"2026-04-23T18:48:37Z","closed_at":"2026-04-23T19:47:15Z","close_reason":"Closed","dependencies":[{"issue_id":"ifcurl-ohq","depends_on_id":"ifcurl-5t3","type":"blocks","created_at":"2026-04-23T07:14:07Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0}
|
||||
{"id":"ifcurl-4yu","title":"Deploy viewer.html to Forgejo server","description":"Copy the viewer static file from the repo to the correct location on the Forgejo server so it is served at its expected URL. Document the deployment step in the repo (e.g. Makefile target or install notes).","status":"closed","priority":2,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-04-23T06:12:11Z","created_by":"Bruno Postle","updated_at":"2026-04-23T19:47:16Z","closed_at":"2026-04-23T19:47:16Z","close_reason":"Closed","dependencies":[{"issue_id":"ifcurl-4yu","depends_on_id":"ifcurl-55f","type":"blocks","created_at":"2026-04-23T07:14:05Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0}
|
||||
{"id":"ifcurl-cuh","title":"Remove /viewer and /proxy endpoints from service.py","description":"Once the viewer HTML is served as a Forgejo static file and fetches IFC bytes directly from Forgejo (using the browser session for auth), the /viewer and /proxy endpoints in ifcurl/service.py are defunct. Remove them and clean up the associated VIEWER_HTML constant in viewer.py.","status":"open","priority":2,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-04-23T06:12:10Z","created_by":"Bruno Postle","updated_at":"2026-04-23T06:12:10Z","dependencies":[{"issue_id":"ifcurl-cuh","depends_on_id":"ifcurl-55f","type":"blocks","created_at":"2026-04-23T07:14:06Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0}
|
||||
{"id":"ifcurl-55f","title":"Move viewer HTML to Forgejo custom static file","description":"Extract the VIEWER_HTML constant from ifcurl/viewer.py and write it as a static HTML file in the repo under the Forgejo custom static directory (path determined by the research task). The file should be a standalone page — no dependency on the ifcurl service for serving.","status":"open","priority":2,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-04-23T06:12:09Z","created_by":"Bruno Postle","updated_at":"2026-04-23T06:12:09Z","dependencies":[{"issue_id":"ifcurl-55f","depends_on_id":"ifcurl-5t3","type":"blocks","created_at":"2026-04-23T07:14:03Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":2,"comment_count":0}
|
||||
{"id":"ifcurl-55f","title":"Move viewer HTML to Forgejo custom static file","description":"Extract the VIEWER_HTML constant from ifcurl/viewer.py and write it as a static HTML file in the repo under the Forgejo custom static directory (path determined by the research task). The file should be a standalone page — no dependency on the ifcurl service for serving.","status":"closed","priority":2,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-23T06:12:09Z","created_by":"Bruno Postle","updated_at":"2026-04-23T19:47:15Z","started_at":"2026-04-23T18:48:26Z","closed_at":"2026-04-23T19:47:15Z","close_reason":"Closed","dependencies":[{"issue_id":"ifcurl-55f","depends_on_id":"ifcurl-5t3","type":"blocks","created_at":"2026-04-23T07:14:03Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":2,"comment_count":0}
|
||||
{"id":"ifcurl-5t3","title":"Research Forgejo custom static file serving","description":"Determine where Forgejo looks for custom static files and what URL they get served at. Check the Forgejo docs and source for the custom/ static directory convention. The result feeds into the viewer HTML deployment and the footer.tmpl URL update.","notes":"Forgejo serves custom static files from \u003cCustomPath\u003e/public/assets/ at URL /assets/*. CustomPath is /var/lib/forgejo/custom on this system. No auth required. Any file type. Place viewer.html at /var/lib/forgejo/custom/public/assets/viewer.html → served at /assets/viewer.html.","status":"closed","priority":2,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-23T06:11:58Z","created_by":"Bruno Postle","updated_at":"2026-04-23T06:50:05Z","started_at":"2026-04-23T06:19:46Z","closed_at":"2026-04-23T06:50:05Z","close_reason":"Closed","dependency_count":0,"dependent_count":2,"comment_count":0}
|
||||
{"id":"ifcurl-7x1","title":"Forgejo goldmark extension: handle bare ifc:// URLs in markdown","description":"The goldmark extension in the patched Forgejo binary (modules/markup/markdown/ifc_url.go) currently only detects ifc:// URLs when written in explicit markdown link form [Title](ifc://...). It should also auto-link bare ifc:// URLs pasted inline in markdown text, the same way goldmark auto-links http:// URLs. Extend the extension (or add a separate autolink pass) to detect raw ifc:// URL tokens and wrap them in the preview embed.","status":"closed","priority":2,"issue_type":"bug","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-23T05:58:02Z","created_by":"Bruno Postle","updated_at":"2026-04-23T06:50:06Z","started_at":"2026-04-23T06:19:47Z","closed_at":"2026-04-23T06:50:06Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"id":"ifcurl-ahd","title":"Serve viewer HTML as Forgejo static files instead of ifcurl /viewer endpoint","description":"Currently the viewer is served from the ifcurl preview service at GET /viewer, and IFC bytes are fetched via GET /proxy to work around CORS. This has two problems: it pipes all IFC data through the ifcurl service (bandwidth/latency), and it bypasses the browser's existing Forgejo authentication (cookies/session) so private repos won't work. Better architecture: deploy the viewer HTML as a Forgejo custom static file (e.g. /assets/viewer.html or via a custom template). The viewer JS can then fetch IFC bytes directly from Forgejo's raw file URL using the browser's existing session, so auth is inherited automatically and no proxy is needed. The /viewer and /proxy endpoints in ifcurl/service.py can be removed once this is working.","status":"closed","priority":2,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-04-23T05:56:15Z","created_by":"Bruno Postle","updated_at":"2026-04-23T06:14:34Z","closed_at":"2026-04-23T06:14:34Z","close_reason":"Split into atomic tasks: ifcurl-5t3, ifcurl-55f, ifcurl-cuh, ifcurl-4yu","dependency_count":0,"dependent_count":2,"comment_count":0}
|
||||
|
|
@ -22,4 +22,4 @@
|
|||
{"id":"ifcurl-vam","title":"Phase 6: Bonsai offer ifc:// URL when saving linked model reference","description":"When saving an IFCDOCUMENTREFERENCE in Bonsai, 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. Requires Phase 4 federation (ifcurl-bqt).","status":"deferred","priority":4,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-04-23T05:49:16Z","created_by":"Bruno Postle","updated_at":"2026-04-23T06:15:13Z","defer_until":"2026-12-01T00:00:00Z","dependencies":[{"issue_id":"ifcurl-vam","depends_on_id":"ifcurl-bqt","type":"blocks","created_at":"2026-04-23T06:49:23Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0}
|
||||
{"id":"ifcurl-9k2","title":"Phase 5: IFC Viewer integration (equivalent of Phase 4)","description":"Add ifc:// OS protocol handler and 'Copy view URL' button to the IFC Viewer desktop application, equivalent to the Bonsai Phase 4 integration. Depends on Phase 4 being complete as a reference implementation.","status":"deferred","priority":4,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-04-23T05:49:08Z","created_by":"Bruno Postle","updated_at":"2026-04-23T06:15:12Z","defer_until":"2026-12-01T00:00:00Z","dependencies":[{"issue_id":"ifcurl-9k2","depends_on_id":"ifcurl-0oj","type":"blocks","created_at":"2026-04-23T06:49:14Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0}
|
||||
{"id":"ifcurl-prc","title":"Phase 3c: 3D diff view for IFC PR merges","description":"After a PR merge completes, surface a 'View merge in 3D' button using the Phase 3b viewer. Use @thatopen/components highlighter to colour added, removed, and modified elements differently, driven by comparing the merged IFC against either parent. Requires Phase 3b viewer (ifcurl-i1s) to be working.","status":"open","priority":4,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-04-23T05:48:30Z","created_by":"Bruno Postle","updated_at":"2026-04-23T05:48:30Z","dependencies":[{"issue_id":"ifcurl-prc","depends_on_id":"ifcurl-i1s","type":"blocks","created_at":"2026-04-23T06:48:36Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"memory","key":"forgejo-build-and-deploy-procedure-go-build-c","value":"Forgejo build and deploy procedure: go build -C /home/bruno/src/forgejo -tags 'sqlite sqlite_unlock_notify' -ldflags \"-X 'forgejo.org/modules/setting.StaticRootPath=/usr/share/forgejo'\" -o /home/bruno/src/forgejo/forgejo . — then sudo cp forgejo /usr/bin/forgejo. StaticRootPath /usr/share/forgejo must have options/ copied from source after major source updates (sudo cp -r /home/bruno/src/forgejo/options /usr/share/forgejo/). CustomPath is /var/lib/forgejo/custom."}
|
||||
{"_type":"memory","key":"forgejo-build-and-deploy-procedure-go-build-c","value":"Forgejo build and deploy procedure: go build -C /home/bruno/src/forgejo -tags 'sqlite sqlite_unlock_notify' -ldflags \"-X 'forgejo.org/modules/setting.StaticRootPath=/usr/share/forgejo'\" -o /home/bruno/src/forgejo/forgejo . — then sudo cp forgejo /usr/bin/forgejo. StaticRootPath=/usr/share/forgejo. CustomPath=/var/lib/forgejo/custom. Custom templates go to /var/lib/forgejo/custom/templates/custom/ (e.g. footer.tmpl → /var/lib/forgejo/custom/templates/custom/footer.tmpl). Custom static files go to /etc/forgejo/public/assets/ and are served at /assets/*. /var/lib/forgejo is root/forgejo-only. /etc/forgejo has conf/ public/ templates/ but only public/assets/ is used for static files."}
|
||||
|
|
|
|||
213
forgejo/custom/public/assets/viewer.html
Normal file
213
forgejo/custom/public/assets/viewer.html
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>IFC Viewer</title>
|
||||
<link rel="icon" href="data:,">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body { background: #1c1c1e; color: #f0f0f0;
|
||||
font-family: system-ui, sans-serif; overflow: hidden; }
|
||||
#viewer { position: fixed; inset: 0; }
|
||||
#toolbar {
|
||||
position: fixed; top: 0; left: 0; right: 0; z-index: 10;
|
||||
display: flex; align-items: center; gap: 8px; padding: 8px 12px;
|
||||
background: rgba(28,28,30,0.85); backdrop-filter: blur(8px);
|
||||
border-bottom: 1px solid rgba(255,255,255,0.08);
|
||||
}
|
||||
#url-display {
|
||||
flex: 1; font-size: 12px; color: #a0a0a8; font-family: monospace;
|
||||
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
}
|
||||
#status {
|
||||
position: fixed; bottom: 12px; left: 50%;
|
||||
transform: translateX(-50%); z-index: 10;
|
||||
font-size: 13px; color: #a0a0a8;
|
||||
background: rgba(28,28,30,0.85);
|
||||
padding: 4px 12px; border-radius: 4px;
|
||||
pointer-events: none;
|
||||
}
|
||||
#status:empty { display: none; }
|
||||
</style>
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"three": "https://esm.sh/three@0.177.0",
|
||||
"three/addons/": "https://esm.sh/three@0.177.0/examples/jsm/",
|
||||
"web-ifc": "https://unpkg.com/web-ifc@0.0.75/web-ifc-api.js",
|
||||
"@thatopen/fragments": "https://esm.sh/@thatopen/fragments@3.3.1?external=three,web-ifc",
|
||||
"@thatopen/components": "https://esm.sh/@thatopen/components@3.3.1?external=three,web-ifc,@thatopen/fragments"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="toolbar">
|
||||
<span id="url-display"></span>
|
||||
</div>
|
||||
<div id="viewer"></div>
|
||||
<div id="status">Loading…</div>
|
||||
|
||||
<script type="module">
|
||||
import * as THREE from "three";
|
||||
import * as OBC from "@thatopen/components";
|
||||
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const ifcUrl = params.get("url") ?? "";
|
||||
const statusEl = document.getElementById("status");
|
||||
const urlEl = document.getElementById("url-display");
|
||||
|
||||
urlEl.textContent = ifcUrl || "No ifc:// URL provided";
|
||||
urlEl.title = ifcUrl;
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Rebuild the ifc:// URL with the current camera position, direction, and
|
||||
// up vector, update the toolbar display and the browser address bar.
|
||||
// -----------------------------------------------------------------------
|
||||
function syncCameraUrl(camera) {
|
||||
const pos = camera.position;
|
||||
const dir = new THREE.Vector3();
|
||||
camera.getWorldDirection(dir);
|
||||
const up = camera.up;
|
||||
|
||||
const f = v => parseFloat(v.toFixed(4));
|
||||
const cameraParam = [
|
||||
f(pos.x), f(pos.y), f(pos.z),
|
||||
f(dir.x), f(dir.y), f(dir.z),
|
||||
f(up.x), f(up.y), f(up.z),
|
||||
].join(",");
|
||||
|
||||
const qIdx = ifcUrl.indexOf("?");
|
||||
const base = qIdx < 0 ? ifcUrl : ifcUrl.slice(0, qIdx);
|
||||
const qs = new URLSearchParams(qIdx < 0 ? "" : ifcUrl.slice(qIdx + 1));
|
||||
qs.set("camera", cameraParam);
|
||||
if (camera.isPerspectiveCamera) {
|
||||
qs.set("fov", f(camera.fov).toString());
|
||||
qs.delete("scale");
|
||||
} else {
|
||||
qs.set("scale", f(camera.top - camera.bottom).toString());
|
||||
qs.delete("fov");
|
||||
}
|
||||
const newIfcUrl = base + "?" + qs.toString().replace(/%2C/g, ",");
|
||||
|
||||
urlEl.textContent = newIfcUrl;
|
||||
urlEl.title = newIfcUrl;
|
||||
|
||||
const pageUrl = new URL(window.location.href);
|
||||
pageUrl.searchParams.set("url", newIfcUrl);
|
||||
history.replaceState(null, "", pageUrl.toString());
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Convert an ifc:// URL to a Forgejo raw file URL so the browser fetches
|
||||
// directly from Forgejo using its existing session (no proxy needed).
|
||||
//
|
||||
// ifc://host:port/org/repo@{hash}?path=file.ifc
|
||||
// → http://host:port/org/repo/raw/commit/{hash}/file.ifc
|
||||
// -----------------------------------------------------------------------
|
||||
function toRawUrl(raw) {
|
||||
const body = raw.slice("ifc://".length);
|
||||
const atIdx = body.indexOf("@");
|
||||
const qIdx = body.indexOf("?");
|
||||
const hostRepo = body.slice(0, atIdx);
|
||||
const ref = body.slice(atIdx + 1, qIdx < 0 ? undefined : qIdx);
|
||||
const qs = new URLSearchParams(qIdx < 0 ? "" : body.slice(qIdx + 1));
|
||||
const filePath = qs.get("path") ?? "";
|
||||
|
||||
const slashIdx = hostRepo.indexOf("/");
|
||||
const host = hostRepo.slice(0, slashIdx);
|
||||
const repoPath = hostRepo.slice(slashIdx + 1);
|
||||
|
||||
let refSeg;
|
||||
if (ref.startsWith("heads/")) refSeg = "branch/" + ref.slice(6);
|
||||
else if (ref.startsWith("tags/")) refSeg = "tag/" + ref.slice(5);
|
||||
else refSeg = "commit/" + ref;
|
||||
|
||||
const protocol = host.startsWith("localhost") ? "http" : "https";
|
||||
return `${protocol}://${host}/${repoPath}/raw/${refSeg}/${filePath}`;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Main
|
||||
// -----------------------------------------------------------------------
|
||||
async function main() {
|
||||
if (!ifcUrl) {
|
||||
statusEl.textContent = "No ifc:// URL provided.";
|
||||
return;
|
||||
}
|
||||
|
||||
// --- World setup ---
|
||||
const components = new OBC.Components();
|
||||
const worlds = components.get(OBC.Worlds);
|
||||
const world = worlds.create();
|
||||
const container = document.getElementById("viewer");
|
||||
|
||||
world.scene = new OBC.SimpleScene(components);
|
||||
world.renderer = new OBC.SimpleRenderer(components, container);
|
||||
world.camera = new OBC.OrthoPerspectiveCamera(components);
|
||||
world.scene.setup();
|
||||
components.init();
|
||||
|
||||
// --- FragmentsManager: must be init'd before ifcLoader.load() ---
|
||||
// Firefox blocks cross-origin module workers; wrap in a blob URL so the
|
||||
// worker is always same-origin regardless of where it was fetched from.
|
||||
const fragments = components.get(OBC.FragmentsManager);
|
||||
const workerResp = await fetch(
|
||||
"https://unpkg.com/@thatopen/fragments@3.3.1/dist/Worker/worker.mjs"
|
||||
);
|
||||
const workerUrl = URL.createObjectURL(
|
||||
new Blob([await workerResp.text()], { type: "application/javascript" })
|
||||
);
|
||||
fragments.init(workerUrl);
|
||||
|
||||
// --- IfcLoader: wasm config goes into setup() ---
|
||||
const ifcLoader = components.get(OBC.IfcLoader);
|
||||
await ifcLoader.setup({
|
||||
autoSetWasm: false,
|
||||
wasm: {
|
||||
path: "https://unpkg.com/web-ifc@0.0.75/",
|
||||
absolute: true,
|
||||
},
|
||||
});
|
||||
|
||||
// --- Fetch IFC bytes directly from Forgejo ---
|
||||
statusEl.textContent = "Fetching IFC file…";
|
||||
let buffer;
|
||||
try {
|
||||
const resp = await fetch(toRawUrl(ifcUrl));
|
||||
if (!resp.ok) throw new Error(`HTTP ${resp.status} ${resp.statusText}`);
|
||||
buffer = new Uint8Array(await resp.arrayBuffer());
|
||||
} catch (err) {
|
||||
statusEl.textContent = `Fetch error: ${err.message}`;
|
||||
return;
|
||||
}
|
||||
|
||||
// --- Parse and render ---
|
||||
statusEl.textContent = "Parsing IFC…";
|
||||
try {
|
||||
const model = await ifcLoader.load(buffer, true, "model");
|
||||
world.scene.three.add(model.object);
|
||||
|
||||
const box = model.box;
|
||||
if (!box.isEmpty()) {
|
||||
await world.camera.controls.fitToBox(box, false);
|
||||
}
|
||||
model.useCamera(world.camera.three);
|
||||
await fragments.core.update(true);
|
||||
|
||||
syncCameraUrl(world.camera.three);
|
||||
world.camera.controls.addEventListener("rest", () => {
|
||||
syncCameraUrl(world.camera.three);
|
||||
});
|
||||
|
||||
statusEl.textContent = "";
|
||||
} catch (err) {
|
||||
statusEl.textContent = `Render error: ${err.message}`;
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
(function () {
|
||||
"use strict";
|
||||
|
||||
var VIEWER_BASE = "http://localhost:8000/viewer";
|
||||
var VIEWER_BASE = "/assets/viewer.html";
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Find a commit-based href on this page.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue