@thatopen/services 0.3.10 → 0.3.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/revit.d.ts +9 -0
- package/dist/cli/lib/revit-addin.d.ts +20 -0
- package/dist/cli.js +101 -0
- package/dist/cloud/LasToPotree.d.ts +59 -0
- package/dist/cloud/SplatToFq.d.ts +38 -0
- package/dist/cloud/index.d.ts +2 -0
- package/docs/builtin/paths.json +86 -86
- package/docs/cloud/GaussianSplatConverter.md +43 -0
- package/docs/cloud/PointCloudConverter.md +53 -0
- package/docs/revit-collab-quickstart.md +151 -0
- package/package.json +95 -95
- package/src/built-in/ClashesManager/example.ts +283 -283
- package/src/built-in/UIManager/src/clashes-panel/example.ts +160 -160
- package/src/built-in/UIManager/src/models-list/example.ts +122 -118
- package/src/cli/templates/app/README.md +217 -241
- package/src/cli/templates/app/package.json +33 -33
- package/src/cli/templates/app/src/setups/camera-tools.ts +0 -63
- package/src/cli/templates/app/src/setups/camera-views.ts +0 -201
- package/src/cli/templates/app/src/setups/card-header.ts +0 -35
- package/src/cli/templates/app/src/setups/clipper-tool.ts +0 -773
- package/src/cli/templates/app/src/setups/clipper.ts +0 -75
- package/src/cli/templates/app/src/setups/cloud-runner.ts +0 -13
- package/src/cli/templates/app/src/setups/exploded-view.ts +0 -246
- package/src/cli/templates/app/src/setups/helper-panel.ts +0 -88
- package/src/cli/templates/app/src/setups/hider.ts +0 -98
- package/src/cli/templates/app/src/setups/index.ts +0 -23
- package/src/cli/templates/app/src/setups/inspection.ts +0 -136
- package/src/cli/templates/app/src/setups/measurement-panel.ts +0 -188
- package/src/cli/templates/app/src/setups/measurement-tool.ts +0 -588
- package/src/cli/templates/app/src/setups/measurements.ts +0 -133
- package/src/cli/templates/app/src/setups/navigation-gizmo.ts +0 -316
- package/src/cli/templates/app/src/setups/plans-panel.ts +0 -619
- package/src/cli/templates/app/src/setups/right-sidebar.ts +0 -178
- package/src/cli/templates/app/src/setups/styles-panel.ts +0 -110
- package/src/cli/templates/app/src/setups/styles.ts +0 -375
- package/src/cli/templates/app/src/setups/tool-mode-manager.ts +0 -156
- package/src/cli/templates/app/src/setups/tool-mode.ts +0 -23
- package/src/cli/templates/app/src/setups/toolbar.ts +0 -229
- package/src/cli/templates/app/src/setups/ui-manager.ts +0 -27
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `thatopen revit ...` — Revit collaboration commands. They drive the local
|
|
5
|
+
* That Open Revit add-in (BT3): publish a shared central, join one, and sync.
|
|
6
|
+
* Auth comes from `thatopen login` (~/.thatopen/config.json) and is forwarded to
|
|
7
|
+
* the add-in, so the user never re-enters a token here.
|
|
8
|
+
*/
|
|
9
|
+
export declare const revitCommand: Command;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Talks to the local That Open Revit add-in (BT3). The add-in runs a
|
|
3
|
+
* 127.0.0.1 command listener and, on startup, writes its port + token to
|
|
4
|
+
* %APPDATA%\ThatOpen\revit-addin.json so the CLI can discover it. Revit-specific
|
|
5
|
+
* work (create central, create local, sync) happens inside the add-in; the CLI
|
|
6
|
+
* only drives it — the same "CLI does everything" model as the rest of `thatopen`.
|
|
7
|
+
*/
|
|
8
|
+
export interface AddinInfo {
|
|
9
|
+
port: number;
|
|
10
|
+
token: string;
|
|
11
|
+
pid: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function discoverAddin(): AddinInfo;
|
|
14
|
+
export declare function callAddin(cmd: string, body?: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
15
|
+
/**
|
|
16
|
+
* Push the logged-in user's credentials (API url + token) into the add-in so it
|
|
17
|
+
* talks to the right account and environment (dev/prod). Idempotent; called
|
|
18
|
+
* before each command.
|
|
19
|
+
*/
|
|
20
|
+
export declare function configureAddin(apiUrl: string, accessToken: string): Promise<void>;
|
package/dist/cli.js
CHANGED
|
@@ -11672,6 +11672,106 @@ const swapCommand = new Command2("swap").description("Toggle between stable publ
|
|
|
11672
11672
|
console.error("Failed to install dependencies. Run `npm install` manually.");
|
|
11673
11673
|
}
|
|
11674
11674
|
});
|
|
11675
|
+
function discoveryFile() {
|
|
11676
|
+
const appData = process.env.APPDATA || require$$2.join(node_os.homedir(), "AppData", "Roaming");
|
|
11677
|
+
return require$$2.join(appData, "ThatOpen", "revit-addin.json");
|
|
11678
|
+
}
|
|
11679
|
+
function discoverAddin() {
|
|
11680
|
+
const file = discoveryFile();
|
|
11681
|
+
if (!require$$3.existsSync(file)) {
|
|
11682
|
+
console.error("");
|
|
11683
|
+
console.error(" The That Open Revit add-in is not running.");
|
|
11684
|
+
console.error(" Open Revit 2026 (with the add-in installed) and try again.");
|
|
11685
|
+
console.error(" If it is not installed yet, install it and restart Revit.");
|
|
11686
|
+
console.error("");
|
|
11687
|
+
process.exit(1);
|
|
11688
|
+
}
|
|
11689
|
+
try {
|
|
11690
|
+
return JSON.parse(require$$3.readFileSync(file, "utf-8"));
|
|
11691
|
+
} catch {
|
|
11692
|
+
console.error(`Could not read the add-in discovery file at ${file}.`);
|
|
11693
|
+
process.exit(1);
|
|
11694
|
+
}
|
|
11695
|
+
}
|
|
11696
|
+
async function callAddin(cmd, body = {}) {
|
|
11697
|
+
const info = discoverAddin();
|
|
11698
|
+
let res;
|
|
11699
|
+
try {
|
|
11700
|
+
res = await fetch(`http://127.0.0.1:${info.port}/${cmd}`, {
|
|
11701
|
+
method: "POST",
|
|
11702
|
+
headers: {
|
|
11703
|
+
"Content-Type": "application/json",
|
|
11704
|
+
"X-Bt3-Token": info.token
|
|
11705
|
+
},
|
|
11706
|
+
body: JSON.stringify(body),
|
|
11707
|
+
signal: AbortSignal.timeout(6e5)
|
|
11708
|
+
});
|
|
11709
|
+
} catch {
|
|
11710
|
+
console.error(
|
|
11711
|
+
`Could not reach the Revit add-in on port ${info.port}. Is Revit still open?`
|
|
11712
|
+
);
|
|
11713
|
+
process.exit(1);
|
|
11714
|
+
}
|
|
11715
|
+
const text = await res.text();
|
|
11716
|
+
let json;
|
|
11717
|
+
try {
|
|
11718
|
+
json = text ? JSON.parse(text) : {};
|
|
11719
|
+
} catch {
|
|
11720
|
+
json = { raw: text };
|
|
11721
|
+
}
|
|
11722
|
+
if (!res.ok || json.ok === false || json.error) {
|
|
11723
|
+
const msg = json.error || json.err || res.statusText;
|
|
11724
|
+
console.error(`Revit add-in error: ${msg}`);
|
|
11725
|
+
process.exit(1);
|
|
11726
|
+
}
|
|
11727
|
+
return json;
|
|
11728
|
+
}
|
|
11729
|
+
async function configureAddin(apiUrl, accessToken) {
|
|
11730
|
+
await callAddin("settings", {
|
|
11731
|
+
base: apiUrl.replace(/\/$/, ""),
|
|
11732
|
+
key: accessToken
|
|
11733
|
+
});
|
|
11734
|
+
}
|
|
11735
|
+
const revitCommand = new Command2("revit").description(
|
|
11736
|
+
"Revit collaboration — drive the That Open Revit add-in from the CLI"
|
|
11737
|
+
);
|
|
11738
|
+
async function connect() {
|
|
11739
|
+
const cfg = requireResolvedConfig();
|
|
11740
|
+
await configureAddin(cfg.apiUrl, cfg.accessToken);
|
|
11741
|
+
return cfg;
|
|
11742
|
+
}
|
|
11743
|
+
revitCommand.command("status").description("Show the add-in status and the current project / central").action(async () => {
|
|
11744
|
+
await connect();
|
|
11745
|
+
const r = await callAddin("status");
|
|
11746
|
+
console.log(JSON.stringify(r, null, 2));
|
|
11747
|
+
});
|
|
11748
|
+
revitCommand.command("publish-central").description("Turn a .rvt into a shared central and upload it to a project").requiredOption("--project <id>", "Platform project id").requiredOption("--doc <name>", "A short name for this central (e.g. tower-central)").requiredOption("--file <path>", "Absolute path to the source .rvt on this machine").action(async (opts) => {
|
|
11749
|
+
await connect();
|
|
11750
|
+
console.log(`Publishing "${opts.file}" as central "${opts.doc}"...`);
|
|
11751
|
+
console.log("(Revit is enabling worksharing and saving the central — this can take a bit.)");
|
|
11752
|
+
const r = await callAddin("publish-central", {
|
|
11753
|
+
project: opts.project,
|
|
11754
|
+
doc: opts.doc,
|
|
11755
|
+
file: opts.file
|
|
11756
|
+
});
|
|
11757
|
+
console.log(`Published. Central: ${r.central} (version ${r.version}).`);
|
|
11758
|
+
console.log(`Collaborators can now join with:`);
|
|
11759
|
+
console.log(` thatopen revit join --project ${opts.project} --doc ${opts.doc}`);
|
|
11760
|
+
});
|
|
11761
|
+
revitCommand.command("join").description("Download a shared central and create + open your local in Revit").requiredOption("--project <id>", "Platform project id").requiredOption("--doc <name>", "The central name to join (from whoever published it)").action(async (opts) => {
|
|
11762
|
+
await connect();
|
|
11763
|
+
console.log(`Joining "${opts.doc}"...`);
|
|
11764
|
+
const r = await callAddin("join", { project: opts.project, doc: opts.doc });
|
|
11765
|
+
console.log(`Joined. Your local was created and opened in Revit:`);
|
|
11766
|
+
console.log(` ${r.local}`);
|
|
11767
|
+
console.log(`Model as usual, then run "thatopen revit sync" (or the "Sync to team" button).`);
|
|
11768
|
+
});
|
|
11769
|
+
revitCommand.command("sync").description("Synchronize your local with the team (queued, no divergence)").action(async () => {
|
|
11770
|
+
await connect();
|
|
11771
|
+
console.log("Syncing with the team...");
|
|
11772
|
+
const r = await callAddin("sync");
|
|
11773
|
+
console.log(`Synced. v${r.verB} → v${r.verA}.`);
|
|
11774
|
+
});
|
|
11675
11775
|
const pkg = JSON.parse(
|
|
11676
11776
|
require$$3.readFileSync(require$$2.join(__dirname, "..", "package.json"), "utf-8")
|
|
11677
11777
|
);
|
|
@@ -11699,4 +11799,5 @@ program.addCommand(serveCommand);
|
|
|
11699
11799
|
program.addCommand(runCommand);
|
|
11700
11800
|
program.addCommand(localServerCommand);
|
|
11701
11801
|
program.addCommand(swapCommand);
|
|
11802
|
+
program.addCommand(revitCommand);
|
|
11702
11803
|
program.parse(process.argv);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** Execution params for the PointCloudConverter cloud component. */
|
|
2
|
+
export interface LasToPotreeParams {
|
|
3
|
+
/** Action (SIMPLE|PLAN|BUILD|MERGE) */
|
|
4
|
+
action?: string;
|
|
5
|
+
/** LAS Point Cloud File ID */
|
|
6
|
+
fileId: string;
|
|
7
|
+
/** Split level (PLAN) */
|
|
8
|
+
K?: number;
|
|
9
|
+
/** Global grid JSON (BUILD/MERGE) */
|
|
10
|
+
grid?: string;
|
|
11
|
+
/** Cell name (BUILD) */
|
|
12
|
+
cell?: string;
|
|
13
|
+
/** Tile descriptors JSON (MERGE) */
|
|
14
|
+
tiles?: string;
|
|
15
|
+
/** Shared entry item id (BUILD/MERGE) */
|
|
16
|
+
entryId?: string;
|
|
17
|
+
/** Cell LAS shard hidden-file ids JSON (BUILD cell-file mode) */
|
|
18
|
+
shards?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* **PointCloudConverter** — Convert a LAS point cloud into a streamable Potree 2.0 octree.
|
|
22
|
+
*
|
|
23
|
+
* Reads an uploaded .las and builds a Potree 2.0 octree (metadata.json + hierarchy.bin + chopped octree pieces), published as one visible `.potree` entry whose hidden files hold the streamed chunks. The viewer byte-ranges those chunks, so a cloud opens without downloading it whole.
|
|
24
|
+
*
|
|
25
|
+
* The conversion itself lives in @thatopen-platform/components-beta (realityCapture); this component is only the platform I/O around it.
|
|
26
|
+
*
|
|
27
|
+
* Notes:
|
|
28
|
+
* - PLAN/BUILD/MERGE is a MEMORY workaround, not a speed feature — a billion-point cloud cannot fit in one worker. It needs many chained executions, and the platform's file API is whole-file-only (it ignores HTTP Range) at ~7 MB/s, so a 50GB / 2B-point cloud would spend ~9 hours just moving bytes, most of it network. Convert those OFFLINE and upload the .potree directly.
|
|
29
|
+
* - LAZ is not supported (it is compressed; the LASF signature check rejects it).
|
|
30
|
+
*
|
|
31
|
+
* Accepts `.las` → produces `.potree`.
|
|
32
|
+
*
|
|
33
|
+
* The execution's `resultMessage` carries the produced file id as
|
|
34
|
+
* `[potreeFileId=<id>]`; use {@link LasToPotree.parseResult} rather than re-inventing the regex.
|
|
35
|
+
*/
|
|
36
|
+
export declare const LasToPotree: {
|
|
37
|
+
/** The name the component is PROVISIONED under (what `listComponents({projectId})` returns). */
|
|
38
|
+
readonly publishedAs: "PointCloudConverter";
|
|
39
|
+
/** The repo/slug name it is published from. */
|
|
40
|
+
readonly slug: "las-to-potree";
|
|
41
|
+
/** Source extensions this converts. */
|
|
42
|
+
readonly accepts: readonly ["las"];
|
|
43
|
+
/** The extension it produces. */
|
|
44
|
+
readonly produces: "potree";
|
|
45
|
+
/** Valid `action` values. */
|
|
46
|
+
readonly actions: readonly ["SIMPLE", "PLAN", "BUILD", "MERGE"];
|
|
47
|
+
/** Hard limits — exceed them and the component FAILS with a message, it does not silently degrade. */
|
|
48
|
+
readonly limits: {
|
|
49
|
+
readonly SIMPLE: {
|
|
50
|
+
readonly maxPoints: 60000000;
|
|
51
|
+
readonly reason: "The whole cloud is held in one worker (~7-8GB, no disk). Above this the component FAILS with a clear message rather than dying silently.";
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Pull the produced file id out of an execution's `resultMessage`.
|
|
56
|
+
* Returns null when the message carries none (e.g. the execution failed).
|
|
57
|
+
*/
|
|
58
|
+
readonly parseResult: (resultMessage: string | undefined | null) => string | null;
|
|
59
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** Execution params for the GaussianSplatConverter cloud component. */
|
|
2
|
+
export interface SplatToFqParams {
|
|
3
|
+
/** Gaussian-Splat PLY File ID */
|
|
4
|
+
fileId: string;
|
|
5
|
+
/** Gaussian scale (metric calibration, default 1) */
|
|
6
|
+
gaussianScale?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* **GaussianSplatConverter** — Convert a 3D Gaussian Splat PLY into a streamable FQ LOD tree.
|
|
10
|
+
*
|
|
11
|
+
* Reads an uploaded .ply (plain INRIA 3DGS, or a PlayCanvas compressed.ply) and builds an octree of flat-quant (FQ) LOD nodes, published as one visible `.splat` entry whose hidden files hold the streamed nodes. The viewer fetches only the nodes it needs at the LOD it needs.
|
|
12
|
+
*
|
|
13
|
+
* Pure CPU: no GPU, no WebGPU, no WebP, no k-means — which is what lets it run in a cloud worker at all (the splat-transform toolchain it replaces requires WebGPU). The codec and converter live in @thatopen-platform/components-beta (realityCapture); this component is only the platform I/O around it.
|
|
14
|
+
*
|
|
15
|
+
* Notes:
|
|
16
|
+
* - gaussianScale is a METRIC CALIBRATION, not a quality knob: reality-capture splats are non-metric, so this scales them to real-world metres. It is PER-CAPTURE (alcardete calibrates to ~70) — default 1, and recalibrate in the viewer.
|
|
17
|
+
* - Spherical harmonics (view-dependent colour) are NOT carried: the FQ codec stores a flat RGBA per splat, and the renderer evaluates no SH. A capture therefore renders matte compared to its source. This is a known fidelity gap, not a bug.
|
|
18
|
+
*
|
|
19
|
+
* Accepts `.ply` → produces `.splat`.
|
|
20
|
+
*
|
|
21
|
+
* The execution's `resultMessage` carries the produced file id as
|
|
22
|
+
* `[splatFileId=<id>]`; use {@link SplatToFq.parseResult} rather than re-inventing the regex.
|
|
23
|
+
*/
|
|
24
|
+
export declare const SplatToFq: {
|
|
25
|
+
/** The name the component is PROVISIONED under (what `listComponents({projectId})` returns). */
|
|
26
|
+
readonly publishedAs: "GaussianSplatConverter";
|
|
27
|
+
/** The repo/slug name it is published from. */
|
|
28
|
+
readonly slug: "splat-to-fq";
|
|
29
|
+
/** Source extensions this converts. */
|
|
30
|
+
readonly accepts: readonly ["ply"];
|
|
31
|
+
/** The extension it produces. */
|
|
32
|
+
readonly produces: "splat";
|
|
33
|
+
/**
|
|
34
|
+
* Pull the produced file id out of an execution's `resultMessage`.
|
|
35
|
+
* Returns null when the message carries none (e.g. the execution failed).
|
|
36
|
+
*/
|
|
37
|
+
readonly parseResult: (resultMessage: string | undefined | null) => string | null;
|
|
38
|
+
};
|
package/docs/builtin/paths.json
CHANGED
|
@@ -1,86 +1,86 @@
|
|
|
1
|
-
[
|
|
2
|
-
{
|
|
3
|
-
"path": "src/built-in/CDEManager/example.ts",
|
|
4
|
-
"description": "CDEManager — CDEManager is the central state machine for a Common Data Environment. It holds files, folders, metadata schemas, viewer groups, and user permissions, and exposes a typed state machine that all file operations — selecting, editing, uploading, viewing — route through. It has no DOM or Three.js dependencies and can run in Node, a browser, or a server-side worker wherever `@thatopen/components` is available."
|
|
5
|
-
},
|
|
6
|
-
{
|
|
7
|
-
"path": "src/built-in/CDEManager/examples/FileEditing/example.ts",
|
|
8
|
-
"description": "Bulk file editing — stage, preview, revert, and commit — A project manager preparing a deliverable handoff needs to update discipline tags across fifteen IFC files after a scope change, rename each to match the new naming convention, and archive three superseded models — all before anything is sent to the backend. Doing the changes one file at a time risks leaving the file list in a half-updated state if the session is interrupted, and there is no way to review exactly what will change before committing."
|
|
9
|
-
},
|
|
10
|
-
{
|
|
11
|
-
"path": "src/built-in/CDEManager/examples/FileMoves/example.ts",
|
|
12
|
-
"description": "Drag-and-drop file and folder moves — A coordinator restructures the project after a scope change: structural files move from the root into a dedicated discipline folder, a MEP sub-folder needs to be promoted to the top level, and a few documents need to come back to root to appear in the general view. Drag-and-drop is the natural gesture for all of this, but each drag session has intermediate stages — the file is \"in the air\", hovering over a candidate target — and every panel watching the file table should see the right state without coordinating directly with the drag component."
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
"path": "src/built-in/CDEManager/examples/FileOperations/example.ts",
|
|
16
|
-
"description": "File operations — inline edit, rename, view, download, archive — BIM coordinators reviewing a model drop repeat the same small actions throughout their session: correcting a discipline tag that changed after a scope revision, renaming a file to match the agreed convention, opening it alongside the structural model to check for clashes, handing it off to a sub-consultant, or removing a superseded version from the live set. If every panel in the platform handles these separately, two panels looking at the same file at the same time can show contradictory states — one says \"uploading\", another still shows the old name."
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
"path": "src/built-in/CDEManager/examples/FileTransfers/example.ts",
|
|
20
|
-
"description": "File uploads, versioning, and the download cache — A site coordinator drops a batch of files onto the CDE every Monday after the weekend review: some are entirely new models, some are revised versions of files already in the project, and sometimes the metadata group they upload into should be stamped automatically onto the new files. If the platform makes the coordinator sort this out manually, it slows the handoff and introduces naming errors. And if an upload fails halfway through a batch, the file table should land in a safe state rather than freeze at a loading spinner."
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
"path": "src/built-in/CDEManager/examples/FolderManagement/example.ts",
|
|
24
|
-
"description": "Folder management — create, rename, archive, and commit — A project reaches the construction phase and the coordination team needs to restructure the folder hierarchy: add a \"Facades\" discipline folder, rename \"Structural\" to its official abbreviation, and archive two superseded folders that have been merged. Doing these one at a time through separate API calls means the folder tree flickers between invalid states as each change lands — a freshly created folder appears before its sibling is renamed, and the archived folders briefly disappear before the new one is visible."
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
"path": "src/built-in/CDEManager/examples/MetadataSchema/example.ts",
|
|
28
|
-
"description": "Metadata schema — define, stage, and publish field definitions — Every project has its own vocabulary for describing files: discipline codes, revision phases, area zones, review statuses. A metadata schema defines which fields exist, what values they accept, and whether they are mandatory. Without a structured schema, each user invents their own labels and the file list becomes impossible to filter or report on consistently."
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
"path": "src/built-in/CDEManager/examples/MetadataViews/example.ts",
|
|
32
|
-
"description": "Metadata views — organize fields into named table layouts — A structural engineer and an MEP coordinator look at the same file list but care about completely different metadata. Showing every field to everyone produces a wide table that is hard to navigate. Metadata views let each role define which fields appear in which order — saving the layout so the table opens correctly next time without requiring a manual setup every session."
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
"path": "src/built-in/CDEManager/examples/ProjectConfig/example.ts",
|
|
36
|
-
"description": "Project configuration — read, write, and reload persistent data — A CDE application needs to store two kinds of configuration: project-wide settings that all team members share (the metadata schema, custom naming rules) and personal settings that belong to one user (which columns are visible, which metadata views are pinned). Both live inside the project's file storage under a reserved system folder, so they travel with the project and don't rely on a separate configuration service."
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
"path": "src/built-in/ClashesManager/example.ts",
|
|
40
|
-
"description": "ClashesManager — Structural engineers and BIM coordinators routinely detect geometric clashes between disciplines — structural steel penetrating ductwork, pipes clashing with slabs — and need to track the status of each issue through resolution without leaving their BIM environment."
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
"path": "src/built-in/UIManager/src/app/example.ts",
|
|
44
|
-
"description": "top-app — Front-end developers building BIM web apps need to coordinate several async initialization steps before the UI can be shown — loading the engine, preparing fragment workers, registering loaders — and users need visible feedback on what is happening during that wait, rather than a blank screen."
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
"path": "src/built-in/UIManager/src/app/examples/AppState/example.ts",
|
|
48
|
-
"description": "Shared reactive state with AppState — Developers building interactive BIM panels often need sibling components — a filter selector and a results table, for example — to stay in sync without a shared parent. Lifting state to a common ancestor and threading it down as props works for shallow trees but becomes unwieldy as the component tree grows."
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
"path": "src/built-in/UIManager/src/app/examples/Banner/example.ts",
|
|
52
|
-
"description": "Persistent banners with top:banner — Some application states need a persistent, visible indicator that stays on screen until the state is explicitly cleared — an archived-files mode, a read-only permission warning, a degraded-connectivity notice. A toast auto-dismisses after a few seconds, which is wrong for these cases. A modal blocks interaction entirely, which is too heavy."
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
"path": "src/built-in/UIManager/src/app/examples/Contexts/example.ts",
|
|
56
|
-
"description": "Consuming platform contexts in child components — Developers building custom BIM panels need access to the 3D engine, the platform API, and the current project's metadata — but threading these objects down through props across multiple component layers forces every intermediate element to know about data it doesn't use, making trees brittle and hard to refactor."
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
"path": "src/built-in/UIManager/src/app/examples/FileDrop/example.ts",
|
|
60
|
-
"description": "File drag-and-drop with top:files-dropped — Desktop BIM workflows often rely on dragging files straight from the OS file manager onto the browser window — but wiring a document-level dragenter, dragover, dragleave, and drop handler that correctly tracks nested enter/leave counts, shows a visual overlay, and forwards the files to the right component is tedious boilerplate to repeat for every app."
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
"path": "src/built-in/UIManager/src/app/examples/FloatingLayout/example.ts",
|
|
64
|
-
"description": "Floating layout — Developers building BIM coordination tools often want the 3D model to fill the entire window while control panels float above it — but standard grid layouts give every area equal standing, shrinking the viewer into one cell and leaving visible seams between it and the panels."
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
"path": "src/built-in/UIManager/src/app/examples/Modal/example.ts",
|
|
68
|
-
"description": "Confirmation modals with top:modal — Destructive or irreversible actions — deleting records, overwriting files, leaving unsaved changes — should ask the user to confirm before proceeding. Opening a custom dialog element from a deeply nested component means threading a reference to a controller up through every layer of the tree."
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
"path": "src/built-in/UIManager/src/app/examples/Notifications/example.ts",
|
|
72
|
-
"description": "Toasts and cross-component notifications — Developers building BIM processing UIs need to surface status updates, errors, and async feedback to users from different points in the component tree — but reaching back to the app root to trigger a notification from a deeply nested component requires threading a reference through every layer in between."
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
"path": "src/built-in/UIManager/src/app/examples/Sidebar/example.ts",
|
|
76
|
-
"description": "Sidebar navigation — Product teams building BIM apps often need multiple distinct views — a 3D model viewer, a data browser, a settings screen — but wiring a custom nav bar and managing view transitions from scratch adds complexity before any real feature work begins."
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
"path": "src/built-in/UIManager/src/clashes-panel/example.ts",
|
|
80
|
-
"description": "clashes-panel — BIM coordinators need a dedicated review environment where they can see all detected clashes, filter by status, navigate to each issue in the 3D viewer, and mark items as resolved — without writing any UI code themselves."
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
"path": "src/built-in/UIManager/src/models-list/example.ts",
|
|
84
|
-
"description": "top-models-list — top-models-list is the project files
|
|
85
|
-
}
|
|
86
|
-
]
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"path": "src/built-in/CDEManager/example.ts",
|
|
4
|
+
"description": "CDEManager — CDEManager is the central state machine for a Common Data Environment. It holds files, folders, metadata schemas, viewer groups, and user permissions, and exposes a typed state machine that all file operations — selecting, editing, uploading, viewing — route through. It has no DOM or Three.js dependencies and can run in Node, a browser, or a server-side worker wherever `@thatopen/components` is available."
|
|
5
|
+
},
|
|
6
|
+
{
|
|
7
|
+
"path": "src/built-in/CDEManager/examples/FileEditing/example.ts",
|
|
8
|
+
"description": "Bulk file editing — stage, preview, revert, and commit — A project manager preparing a deliverable handoff needs to update discipline tags across fifteen IFC files after a scope change, rename each to match the new naming convention, and archive three superseded models — all before anything is sent to the backend. Doing the changes one file at a time risks leaving the file list in a half-updated state if the session is interrupted, and there is no way to review exactly what will change before committing."
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"path": "src/built-in/CDEManager/examples/FileMoves/example.ts",
|
|
12
|
+
"description": "Drag-and-drop file and folder moves — A coordinator restructures the project after a scope change: structural files move from the root into a dedicated discipline folder, a MEP sub-folder needs to be promoted to the top level, and a few documents need to come back to root to appear in the general view. Drag-and-drop is the natural gesture for all of this, but each drag session has intermediate stages — the file is \"in the air\", hovering over a candidate target — and every panel watching the file table should see the right state without coordinating directly with the drag component."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"path": "src/built-in/CDEManager/examples/FileOperations/example.ts",
|
|
16
|
+
"description": "File operations — inline edit, rename, view, download, archive — BIM coordinators reviewing a model drop repeat the same small actions throughout their session: correcting a discipline tag that changed after a scope revision, renaming a file to match the agreed convention, opening it alongside the structural model to check for clashes, handing it off to a sub-consultant, or removing a superseded version from the live set. If every panel in the platform handles these separately, two panels looking at the same file at the same time can show contradictory states — one says \"uploading\", another still shows the old name."
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"path": "src/built-in/CDEManager/examples/FileTransfers/example.ts",
|
|
20
|
+
"description": "File uploads, versioning, and the download cache — A site coordinator drops a batch of files onto the CDE every Monday after the weekend review: some are entirely new models, some are revised versions of files already in the project, and sometimes the metadata group they upload into should be stamped automatically onto the new files. If the platform makes the coordinator sort this out manually, it slows the handoff and introduces naming errors. And if an upload fails halfway through a batch, the file table should land in a safe state rather than freeze at a loading spinner."
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"path": "src/built-in/CDEManager/examples/FolderManagement/example.ts",
|
|
24
|
+
"description": "Folder management — create, rename, archive, and commit — A project reaches the construction phase and the coordination team needs to restructure the folder hierarchy: add a \"Facades\" discipline folder, rename \"Structural\" to its official abbreviation, and archive two superseded folders that have been merged. Doing these one at a time through separate API calls means the folder tree flickers between invalid states as each change lands — a freshly created folder appears before its sibling is renamed, and the archived folders briefly disappear before the new one is visible."
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"path": "src/built-in/CDEManager/examples/MetadataSchema/example.ts",
|
|
28
|
+
"description": "Metadata schema — define, stage, and publish field definitions — Every project has its own vocabulary for describing files: discipline codes, revision phases, area zones, review statuses. A metadata schema defines which fields exist, what values they accept, and whether they are mandatory. Without a structured schema, each user invents their own labels and the file list becomes impossible to filter or report on consistently."
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"path": "src/built-in/CDEManager/examples/MetadataViews/example.ts",
|
|
32
|
+
"description": "Metadata views — organize fields into named table layouts — A structural engineer and an MEP coordinator look at the same file list but care about completely different metadata. Showing every field to everyone produces a wide table that is hard to navigate. Metadata views let each role define which fields appear in which order — saving the layout so the table opens correctly next time without requiring a manual setup every session."
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"path": "src/built-in/CDEManager/examples/ProjectConfig/example.ts",
|
|
36
|
+
"description": "Project configuration — read, write, and reload persistent data — A CDE application needs to store two kinds of configuration: project-wide settings that all team members share (the metadata schema, custom naming rules) and personal settings that belong to one user (which columns are visible, which metadata views are pinned). Both live inside the project's file storage under a reserved system folder, so they travel with the project and don't rely on a separate configuration service."
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"path": "src/built-in/ClashesManager/example.ts",
|
|
40
|
+
"description": "ClashesManager — Structural engineers and BIM coordinators routinely detect geometric clashes between disciplines — structural steel penetrating ductwork, pipes clashing with slabs — and need to track the status of each issue through resolution without leaving their BIM environment."
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"path": "src/built-in/UIManager/src/app/example.ts",
|
|
44
|
+
"description": "top-app — Front-end developers building BIM web apps need to coordinate several async initialization steps before the UI can be shown — loading the engine, preparing fragment workers, registering loaders — and users need visible feedback on what is happening during that wait, rather than a blank screen."
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"path": "src/built-in/UIManager/src/app/examples/AppState/example.ts",
|
|
48
|
+
"description": "Shared reactive state with AppState — Developers building interactive BIM panels often need sibling components — a filter selector and a results table, for example — to stay in sync without a shared parent. Lifting state to a common ancestor and threading it down as props works for shallow trees but becomes unwieldy as the component tree grows."
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"path": "src/built-in/UIManager/src/app/examples/Banner/example.ts",
|
|
52
|
+
"description": "Persistent banners with top:banner — Some application states need a persistent, visible indicator that stays on screen until the state is explicitly cleared — an archived-files mode, a read-only permission warning, a degraded-connectivity notice. A toast auto-dismisses after a few seconds, which is wrong for these cases. A modal blocks interaction entirely, which is too heavy."
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"path": "src/built-in/UIManager/src/app/examples/Contexts/example.ts",
|
|
56
|
+
"description": "Consuming platform contexts in child components — Developers building custom BIM panels need access to the 3D engine, the platform API, and the current project's metadata — but threading these objects down through props across multiple component layers forces every intermediate element to know about data it doesn't use, making trees brittle and hard to refactor."
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"path": "src/built-in/UIManager/src/app/examples/FileDrop/example.ts",
|
|
60
|
+
"description": "File drag-and-drop with top:files-dropped — Desktop BIM workflows often rely on dragging files straight from the OS file manager onto the browser window — but wiring a document-level dragenter, dragover, dragleave, and drop handler that correctly tracks nested enter/leave counts, shows a visual overlay, and forwards the files to the right component is tedious boilerplate to repeat for every app."
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"path": "src/built-in/UIManager/src/app/examples/FloatingLayout/example.ts",
|
|
64
|
+
"description": "Floating layout — Developers building BIM coordination tools often want the 3D model to fill the entire window while control panels float above it — but standard grid layouts give every area equal standing, shrinking the viewer into one cell and leaving visible seams between it and the panels."
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"path": "src/built-in/UIManager/src/app/examples/Modal/example.ts",
|
|
68
|
+
"description": "Confirmation modals with top:modal — Destructive or irreversible actions — deleting records, overwriting files, leaving unsaved changes — should ask the user to confirm before proceeding. Opening a custom dialog element from a deeply nested component means threading a reference to a controller up through every layer of the tree."
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"path": "src/built-in/UIManager/src/app/examples/Notifications/example.ts",
|
|
72
|
+
"description": "Toasts and cross-component notifications — Developers building BIM processing UIs need to surface status updates, errors, and async feedback to users from different points in the component tree — but reaching back to the app root to trigger a notification from a deeply nested component requires threading a reference through every layer in between."
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"path": "src/built-in/UIManager/src/app/examples/Sidebar/example.ts",
|
|
76
|
+
"description": "Sidebar navigation — Product teams building BIM apps often need multiple distinct views — a 3D model viewer, a data browser, a settings screen — but wiring a custom nav bar and managing view transitions from scratch adds complexity before any real feature work begins."
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"path": "src/built-in/UIManager/src/clashes-panel/example.ts",
|
|
80
|
+
"description": "clashes-panel — BIM coordinators need a dedicated review environment where they can see all detected clashes, filter by status, navigate to each issue in the 3D viewer, and mark items as resolved — without writing any UI code themselves."
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"path": "src/built-in/UIManager/src/models-list/example.ts",
|
|
84
|
+
"description": "top-models-list — top-models-list is the project's 3D-viewable-files tree: it mirrors the real storage folder structure, converts IFC to fragments, and loads `.frag`/`.potree`/`.splat` models into the 3D scene. Its loaders are PLUGGABLE — the panel owns the UI and the orchestration (progress, refresh, association), but the HOST decides how each extra file format is loaded, so you can teach it new formats without forking the built-in."
|
|
85
|
+
}
|
|
86
|
+
]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# GaussianSplatConverter
|
|
2
|
+
|
|
3
|
+
Convert a 3D Gaussian Splat PLY into a streamable FQ LOD tree.
|
|
4
|
+
|
|
5
|
+
Reads an uploaded .ply (plain INRIA 3DGS, or a PlayCanvas compressed.ply) and builds an octree of flat-quant (FQ) LOD nodes, published as one visible `.splat` entry whose hidden files hold the streamed nodes. The viewer fetches only the nodes it needs at the LOD it needs.
|
|
6
|
+
|
|
7
|
+
Pure CPU: no GPU, no WebGPU, no WebP, no k-means — which is what lets it run in a cloud worker at all (the splat-transform toolchain it replaces requires WebGPU). The codec and converter live in @thatopen-platform/components-beta (realityCapture); this component is only the platform I/O around it.
|
|
8
|
+
|
|
9
|
+
- **Accepts:** `.ply`
|
|
10
|
+
- **Produces:** `.splat`
|
|
11
|
+
- **Result:** the execution's `resultMessage` contains `[splatFileId=<id>]`
|
|
12
|
+
|
|
13
|
+
## Actions
|
|
14
|
+
|
|
15
|
+
| action | what it does | params |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| `default` | Convert the splat in one execution. | `fileId`, `gaussianScale` |
|
|
18
|
+
|
|
19
|
+
## Params
|
|
20
|
+
|
|
21
|
+
| id | type | label |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| `fileId` | string | Gaussian-Splat PLY File ID |
|
|
24
|
+
| `gaussianScale` | string | Gaussian scale (metric calibration, default 1) |
|
|
25
|
+
|
|
26
|
+
## Notes
|
|
27
|
+
|
|
28
|
+
- gaussianScale is a METRIC CALIBRATION, not a quality knob: reality-capture splats are non-metric, so this scales them to real-world metres. It is PER-CAPTURE (alcardete calibrates to ~70) — default 1, and recalibrate in the viewer.
|
|
29
|
+
- Spherical harmonics (view-dependent colour) are NOT carried: the FQ codec stores a flat RGBA per splat, and the renderer evaluates no SH. A capture therefore renders matte compared to its source. This is a known fidelity gap, not a bug.
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { SplatToFq } from "@thatopen/services";
|
|
35
|
+
|
|
36
|
+
const { executionId } = await client.executeComponent(componentId, {
|
|
37
|
+
projectId,
|
|
38
|
+
fileId,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const exec = await client.getExecution(executionId);
|
|
42
|
+
const producedFileId = SplatToFq.parseResult(exec.resultMessage);
|
|
43
|
+
```
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# PointCloudConverter
|
|
2
|
+
|
|
3
|
+
Convert a LAS point cloud into a streamable Potree 2.0 octree.
|
|
4
|
+
|
|
5
|
+
Reads an uploaded .las and builds a Potree 2.0 octree (metadata.json + hierarchy.bin + chopped octree pieces), published as one visible `.potree` entry whose hidden files hold the streamed chunks. The viewer byte-ranges those chunks, so a cloud opens without downloading it whole.
|
|
6
|
+
|
|
7
|
+
The conversion itself lives in @thatopen-platform/components-beta (realityCapture); this component is only the platform I/O around it.
|
|
8
|
+
|
|
9
|
+
- **Accepts:** `.las`
|
|
10
|
+
- **Produces:** `.potree`
|
|
11
|
+
- **Result:** the execution's `resultMessage` contains `[potreeFileId=<id>]`
|
|
12
|
+
|
|
13
|
+
## Actions
|
|
14
|
+
|
|
15
|
+
| action | what it does | params |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| `SIMPLE` | Build the whole cloud in ONE execution. The default, and what you almost always want. | `fileId` |
|
|
18
|
+
| `PLAN` | For clouds too big for SIMPLE: stream the source once and return an adaptive set of tiles that each fit a worker. | `fileId`, `K` |
|
|
19
|
+
| `BUILD` | Build ONE tile of a PLAN as an aligned subtree of the global octree. Fired once per tile. | `grid`, `cell`, `entryId`, `shards` |
|
|
20
|
+
| `MERGE` | Graft the built tiles into one global octree and publish the `.potree` entry. The tiles' bytes are referenced, never copied. | `grid`, `tiles`, `entryId` |
|
|
21
|
+
|
|
22
|
+
## Params
|
|
23
|
+
|
|
24
|
+
| id | type | label |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| `action` | string | Action (SIMPLE|PLAN|BUILD|MERGE) |
|
|
27
|
+
| `fileId` | string | LAS Point Cloud File ID |
|
|
28
|
+
| `K` | number | Split level (PLAN) |
|
|
29
|
+
| `grid` | string | Global grid JSON (BUILD/MERGE) |
|
|
30
|
+
| `cell` | string | Cell name (BUILD) |
|
|
31
|
+
| `tiles` | string | Tile descriptors JSON (MERGE) |
|
|
32
|
+
| `entryId` | string | Shared entry item id (BUILD/MERGE) |
|
|
33
|
+
| `shards` | string | Cell LAS shard hidden-file ids JSON (BUILD cell-file mode) |
|
|
34
|
+
|
|
35
|
+
## Notes
|
|
36
|
+
|
|
37
|
+
- PLAN/BUILD/MERGE is a MEMORY workaround, not a speed feature — a billion-point cloud cannot fit in one worker. It needs many chained executions, and the platform's file API is whole-file-only (it ignores HTTP Range) at ~7 MB/s, so a 50GB / 2B-point cloud would spend ~9 hours just moving bytes, most of it network. Convert those OFFLINE and upload the .potree directly.
|
|
38
|
+
- LAZ is not supported (it is compressed; the LASF signature check rejects it).
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { LasToPotree } from "@thatopen/services";
|
|
44
|
+
|
|
45
|
+
const { executionId } = await client.executeComponent(componentId, {
|
|
46
|
+
projectId,
|
|
47
|
+
fileId,
|
|
48
|
+
action: "SIMPLE",
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const exec = await client.getExecution(executionId);
|
|
52
|
+
const producedFileId = LasToPotree.parseResult(exec.resultMessage);
|
|
53
|
+
```
|