create-ifc-lite 1.11.0 → 1.11.2

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/index.js CHANGED
@@ -5,12 +5,14 @@
5
5
  import { existsSync, mkdirSync } from 'fs';
6
6
  import { join } from 'path';
7
7
  import { createBasicTemplate } from './templates/basic.js';
8
+ import { createThreejsTemplate } from './templates/threejs.js';
8
9
  import { createServerTemplate } from './templates/server.js';
9
10
  import { createServerNativeTemplate } from './templates/server-native.js';
10
11
  import { fixViewerTemplate } from './utils/config-fixers.js';
11
12
  import { downloadViewer } from './utils/download.js';
12
13
  const TEMPLATES = {
13
14
  basic: 'basic',
15
+ threejs: 'threejs',
14
16
  react: 'react',
15
17
  server: 'server',
16
18
  'server-native': 'server-native',
@@ -28,12 +30,14 @@ function printUsage() {
28
30
 
29
31
  Examples:
30
32
  npx create-ifc-lite my-ifc-app
33
+ npx create-ifc-lite my-viewer --template threejs
31
34
  npx create-ifc-lite my-viewer --template react
32
35
  npx create-ifc-lite my-backend --template server
33
36
  npx create-ifc-lite my-backend --template server-native
34
37
 
35
38
  Templates:
36
39
  basic Minimal TypeScript project for parsing IFC files
40
+ threejs Three.js viewer (WebGL, no WebGPU required)
37
41
  react Full-featured React + Vite viewer with WebGPU rendering
38
42
  server Docker-based IFC processing server with TypeScript client
39
43
  server-native Native binary server (no Docker required)
@@ -56,7 +60,7 @@ async function main() {
56
60
  template = t;
57
61
  }
58
62
  else {
59
- console.error(`Invalid template: ${t}. Available: basic, react, server, server-native`);
63
+ console.error(`Invalid template: ${t}. Available: basic, threejs, react, server, server-native`);
60
64
  process.exit(1);
61
65
  }
62
66
  }
@@ -70,7 +74,11 @@ async function main() {
70
74
  process.exit(1);
71
75
  }
72
76
  console.log(`\n Creating IFC-Lite project in ${targetDir}...\n`);
73
- if (template === 'react') {
77
+ if (template === 'threejs') {
78
+ mkdirSync(targetDir, { recursive: true });
79
+ createThreejsTemplate(targetDir, projectName);
80
+ }
81
+ else if (template === 'react') {
74
82
  // Download the actual viewer from GitHub
75
83
  const success = await downloadViewer(targetDir, projectName);
76
84
  if (success) {
@@ -108,7 +116,7 @@ async function main() {
108
116
  }
109
117
  else {
110
118
  console.log(` npm install`);
111
- if (template === 'react') {
119
+ if (template === 'react' || template === 'threejs') {
112
120
  console.log(` npm run dev`);
113
121
  }
114
122
  else {
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Scaffold a Three.js IFC viewer project using @ifc-lite/geometry.
3
+ * No WebGPU required — renders via Three.js WebGLRenderer.
4
+ */
5
+ export declare function createThreejsTemplate(targetDir: string, projectName: string): void;
@@ -0,0 +1,258 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ import { mkdirSync, writeFileSync } from 'fs';
5
+ import { join } from 'path';
6
+ import { getPackageVersion } from '../utils/config-fixers.js';
7
+ /**
8
+ * Scaffold a Three.js IFC viewer project using @ifc-lite/geometry.
9
+ * No WebGPU required — renders via Three.js WebGLRenderer.
10
+ */
11
+ export function createThreejsTemplate(targetDir, projectName) {
12
+ const geometryVersion = getPackageVersion('@ifc-lite/geometry');
13
+ // package.json
14
+ writeFileSync(join(targetDir, 'package.json'), JSON.stringify({
15
+ name: projectName,
16
+ version: '0.1.0',
17
+ private: true,
18
+ type: 'module',
19
+ scripts: {
20
+ dev: 'vite',
21
+ build: 'tsc && vite build',
22
+ preview: 'vite preview',
23
+ },
24
+ dependencies: {
25
+ '@ifc-lite/geometry': geometryVersion,
26
+ three: '^0.183.0',
27
+ },
28
+ devDependencies: {
29
+ '@types/three': '^0.183.0',
30
+ typescript: '^5.3.0',
31
+ vite: '^7.0.0',
32
+ },
33
+ }, null, 2));
34
+ // tsconfig.json
35
+ writeFileSync(join(targetDir, 'tsconfig.json'), JSON.stringify({
36
+ compilerOptions: {
37
+ target: 'ES2022',
38
+ module: 'ESNext',
39
+ moduleResolution: 'bundler',
40
+ strict: true,
41
+ esModuleInterop: true,
42
+ skipLibCheck: true,
43
+ outDir: 'dist',
44
+ },
45
+ include: ['src'],
46
+ }, null, 2));
47
+ // vite.config.ts
48
+ writeFileSync(join(targetDir, 'vite.config.ts'), `import { defineConfig } from 'vite';
49
+
50
+ export default defineConfig({
51
+ optimizeDeps: {
52
+ exclude: ['@ifc-lite/wasm'],
53
+ },
54
+ server: {
55
+ headers: {
56
+ 'Cross-Origin-Opener-Policy': 'same-origin',
57
+ 'Cross-Origin-Embedder-Policy': 'require-corp',
58
+ },
59
+ },
60
+ });
61
+ `);
62
+ // index.html
63
+ writeFileSync(join(targetDir, 'index.html'), `<!DOCTYPE html>
64
+ <html lang="en">
65
+ <head>
66
+ <meta charset="UTF-8" />
67
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
68
+ <title>${projectName}</title>
69
+ <style>
70
+ * { margin: 0; padding: 0; box-sizing: border-box; }
71
+ body { font-family: system-ui, sans-serif; background: #1a1a2e; color: #fff; overflow: hidden; }
72
+ #app { display: flex; flex-direction: column; height: 100vh; }
73
+ header { padding: 0.75rem 1rem; background: #16213e; display: flex; gap: 1rem; align-items: center; }
74
+ header h1 { font-size: 1rem; font-weight: 600; }
75
+ #file-input { padding: 0.4rem 0.8rem; background: #0f3460; border: 1px solid #533483; border-radius: 4px; color: #fff; cursor: pointer; }
76
+ #status { color: #888; font-size: 0.85rem; }
77
+ #canvas-container { flex: 1; position: relative; }
78
+ canvas { display: block; width: 100%; height: 100%; }
79
+ </style>
80
+ </head>
81
+ <body>
82
+ <div id="app">
83
+ <header>
84
+ <h1>${projectName}</h1>
85
+ <input type="file" id="file-input" accept=".ifc" />
86
+ <span id="status">Drop an IFC file to view</span>
87
+ </header>
88
+ <div id="canvas-container">
89
+ <canvas id="viewer"></canvas>
90
+ </div>
91
+ </div>
92
+ <script type="module" src="/src/main.ts"></script>
93
+ </body>
94
+ </html>
95
+ `);
96
+ // src/
97
+ mkdirSync(join(targetDir, 'src'));
98
+ // src/ifc-to-threejs.ts
99
+ writeFileSync(join(targetDir, 'src', 'ifc-to-threejs.ts'), `import * as THREE from 'three';
100
+ import type { MeshData } from '@ifc-lite/geometry';
101
+
102
+ /**
103
+ * Convert a single MeshData into a Three.js Mesh.
104
+ */
105
+ export function meshDataToThree(mesh: MeshData): THREE.Mesh {
106
+ const geometry = new THREE.BufferGeometry();
107
+ geometry.setAttribute('position', new THREE.BufferAttribute(mesh.positions, 3));
108
+ geometry.setAttribute('normal', new THREE.BufferAttribute(mesh.normals, 3));
109
+ geometry.setIndex(new THREE.BufferAttribute(mesh.indices, 1));
110
+
111
+ const [r, g, b, a] = mesh.color;
112
+ const material = new THREE.MeshStandardMaterial({
113
+ color: new THREE.Color(r, g, b),
114
+ transparent: a < 1,
115
+ opacity: a,
116
+ side: a < 1 ? THREE.DoubleSide : THREE.FrontSide,
117
+ depthWrite: a >= 1,
118
+ });
119
+
120
+ const threeMesh = new THREE.Mesh(geometry, material);
121
+ threeMesh.userData.expressId = mesh.expressId;
122
+ return threeMesh;
123
+ }
124
+ `);
125
+ // src/main.ts
126
+ writeFileSync(join(targetDir, 'src', 'main.ts'), `import * as THREE from 'three';
127
+ import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
128
+ import { GeometryProcessor } from '@ifc-lite/geometry';
129
+ import { meshDataToThree } from './ifc-to-threejs.js';
130
+
131
+ const canvas = document.getElementById('viewer') as HTMLCanvasElement | null;
132
+ const fileInput = document.getElementById('file-input') as HTMLInputElement | null;
133
+ const status = document.getElementById('status');
134
+ if (!canvas || !fileInput || !status) {
135
+ throw new Error('Required DOM elements not found: viewer, file-input, or status');
136
+ }
137
+
138
+ // Three.js setup
139
+ const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
140
+ renderer.setPixelRatio(window.devicePixelRatio);
141
+ renderer.toneMapping = THREE.ACESFilmicToneMapping;
142
+
143
+ const scene = new THREE.Scene();
144
+ scene.background = new THREE.Color(0x1a1a2e);
145
+
146
+ const camera = new THREE.PerspectiveCamera(50, 1, 0.1, 10000);
147
+ camera.position.set(20, 15, 20);
148
+
149
+ const controls = new OrbitControls(camera, canvas);
150
+ controls.enableDamping = false;
151
+
152
+ // Lighting
153
+ scene.add(new THREE.AmbientLight(0xffffff, 0.6));
154
+ const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
155
+ dirLight.position.set(50, 80, 50);
156
+ scene.add(dirLight);
157
+
158
+ // IFC-Lite geometry processor
159
+ const geometry = new GeometryProcessor();
160
+
161
+ // Resize
162
+ function resize() {
163
+ const container = canvas.parentElement ?? document.body;
164
+ renderer.setSize(container.clientWidth, container.clientHeight);
165
+ camera.aspect = container.clientWidth / container.clientHeight;
166
+ camera.updateProjectionMatrix();
167
+ }
168
+ window.addEventListener('resize', resize);
169
+ resize();
170
+
171
+ // Render loop
172
+ (function animate() {
173
+ requestAnimationFrame(animate);
174
+ controls.update();
175
+ renderer.render(scene, camera);
176
+ })();
177
+
178
+ // File loading
179
+ fileInput.addEventListener('change', async () => {
180
+ const file = fileInput.files?.[0];
181
+ if (!file) return;
182
+
183
+ status.textContent = 'Loading ' + file.name + '...';
184
+
185
+ try {
186
+ await geometry.init();
187
+ const buffer = new Uint8Array(await file.arrayBuffer());
188
+
189
+ // Clear previous model and release GPU resources
190
+ const toRemove = scene.children.filter(
191
+ (c) => c instanceof THREE.Mesh || c instanceof THREE.Group
192
+ );
193
+ for (const obj of toRemove) {
194
+ scene.remove(obj);
195
+ obj.traverse((child) => {
196
+ if (child instanceof THREE.Mesh) {
197
+ child.geometry.dispose();
198
+ if (Array.isArray(child.material)) {
199
+ child.material.forEach((m) => m.dispose());
200
+ } else {
201
+ child.material.dispose();
202
+ }
203
+ }
204
+ });
205
+ }
206
+
207
+ // Stream geometry
208
+ let count = 0;
209
+ for await (const event of geometry.processStreaming(buffer)) {
210
+ if (event.type === 'batch') {
211
+ for (const mesh of event.meshes) {
212
+ scene.add(meshDataToThree(mesh));
213
+ }
214
+ count += event.meshes.length;
215
+ status.textContent = 'Loaded ' + count + ' meshes...';
216
+ }
217
+ if (event.type === 'complete') {
218
+ // Fit camera
219
+ const box = new THREE.Box3().setFromObject(scene);
220
+ const center = box.getCenter(new THREE.Vector3());
221
+ const size = box.getSize(new THREE.Vector3());
222
+ const d = Math.max(size.x, size.y, size.z) * 1.5;
223
+ camera.position.set(center.x + d * 0.5, center.y + d * 0.5, center.z + d * 0.5);
224
+ controls.target.copy(center);
225
+ controls.update();
226
+ camera.near = Math.max(size.x, size.y, size.z) * 0.001;
227
+ camera.far = Math.max(size.x, size.y, size.z) * 100;
228
+ camera.updateProjectionMatrix();
229
+
230
+ status.textContent = file.name + ' — ' + event.totalMeshes + ' meshes';
231
+ }
232
+ }
233
+ } catch (err: any) {
234
+ status.textContent = 'Error: ' + err.message;
235
+ console.error(err);
236
+ }
237
+ });
238
+ `);
239
+ // README
240
+ writeFileSync(join(targetDir, 'README.md'), `# ${projectName}
241
+
242
+ Three.js IFC viewer using [IFC-Lite](https://github.com/louistrue/ifc-lite).
243
+
244
+ ## Quick Start
245
+
246
+ \`\`\`bash
247
+ npm install
248
+ npm run dev
249
+ \`\`\`
250
+
251
+ Open http://localhost:5173 and drop an IFC file.
252
+
253
+ ## Learn More
254
+
255
+ - [Three.js Integration Guide](https://louistrue.github.io/ifc-lite/tutorials/threejs-integration/)
256
+ - [IFC-Lite Documentation](https://louistrue.github.io/ifc-lite/)
257
+ `);
258
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-ifc-lite",
3
- "version": "1.11.0",
3
+ "version": "1.11.2",
4
4
  "description": "Create IFC-Lite projects with one command",
5
5
  "type": "module",
6
6
  "bin": {