occt-gltf-addon 0.1.0 → 0.1.1

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/README.md CHANGED
@@ -55,6 +55,19 @@ await convertSTEPToGLTF({
55
55
 
56
56
  ---
57
57
 
58
+ #### Smoke Test(安装后快速验证)
59
+
60
+ ```bash
61
+ npm run smoke -- /abs/path/to/model.step /tmp/occt-gltf-out --draco --zup
62
+ ```
63
+
64
+ 参数:
65
+ - `--draco`:启用 Draco 输出(可选)
66
+ - `--zup`:导出 Z-up(用于 three.js Z-up 世界;可选)
67
+ - `--quiet`:静默日志(可选)
68
+
69
+ ---
70
+
58
71
  #### 关键选项说明(简版)
59
72
 
60
73
  - **tessellation.linearDeflection / angularDeflection**:B-Rep 三角化精度
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "occt-gltf-addon",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "STEP (.step/.stp) to glTF (GLB) converter using OpenCascade (OCCT) via a Node.js N-API addon",
5
5
  "type": "commonjs",
6
6
  "main": "index.js",
@@ -46,7 +46,8 @@
46
46
  "CMakeLists.txt",
47
47
  "src/**",
48
48
  "scripts/**",
49
- "example.js"
49
+ "example.js",
50
+ "smoke_test.js"
50
51
  ],
51
52
  "scripts": {
52
53
  "install": "node scripts/install.js",
@@ -54,6 +55,7 @@
54
55
  "clean": "rm -rf build",
55
56
  "rebuild": "npm run clean && npm run build",
56
57
  "example": "node example.js",
58
+ "smoke": "node smoke_test.js",
57
59
  "test": "node -e \"console.log('no tests')\"",
58
60
 
59
61
  "docker:build:linux-amd64": "bash docker/build-linux-amd64.sh",
@@ -53,15 +53,24 @@ function main() {
53
53
  note("OCCT_ROOT is not set. If build fails, set OCCT_ROOT to your OCCT install prefix (e.g. '/opt/homebrew', '/usr', '/usr/local', or your custom install).");
54
54
  }
55
55
 
56
- // Use the locally installed cmake-js (dependency).
57
- const cmakeJsBin = path.join(pkgRoot, 'node_modules', '.bin', process.platform === 'win32' ? 'cmake-js.cmd' : 'cmake-js');
58
- if (!fs.existsSync(cmakeJsBin)) {
59
- note(`cmake-js not found at ${cmakeJsBin}. Try: 'npm install' again.`);
56
+ note('Building native addon with cmake-js...');
57
+
58
+ // Locate cmake-js CLI in a way that works with npm hoisting.
59
+ let cmakeJsCli = null;
60
+ try {
61
+ // eslint-disable-next-line global-require
62
+ cmakeJsCli = require.resolve('cmake-js/bin/cmake-js');
63
+ } catch (e) {
64
+ cmakeJsCli = null;
65
+ }
66
+
67
+ if (!cmakeJsCli || !fs.existsSync(cmakeJsCli)) {
68
+ note('cmake-js is not available (dependency missing).');
69
+ note("Try: 'npm install' again.");
60
70
  process.exit(1);
61
71
  }
62
72
 
63
- note('Building native addon with cmake-js...');
64
- run(cmakeJsBin, ['compile', '-O', 'build'], { cwd: pkgRoot, env: process.env });
73
+ run(process.execPath, [cmakeJsCli, 'compile', '-O', 'build'], { cwd: pkgRoot, env: process.env });
65
74
  }
66
75
 
67
76
  try {
package/smoke_test.js ADDED
@@ -0,0 +1,152 @@
1
+ /* eslint-disable no-console */
2
+ const fs = require('fs');
3
+ const os = require('os');
4
+ const path = require('path');
5
+
6
+ function usage() {
7
+ console.error('Usage: node smoke_test.js <input.step> [outputDir] [--draco] [--zup] [--quiet]');
8
+ console.error('');
9
+ console.error('Notes:');
10
+ console.error('- Requires OCCT runtime libraries to be available at runtime (LD_LIBRARY_PATH / rpath / system install).');
11
+ console.error('- If you use three.js Z-up world (THREE.Object3D.DEFAULT_UP.set(0,0,1)), pass --zup.');
12
+ }
13
+
14
+ function readU32LE(buf, off) {
15
+ return buf.readUInt32LE(off);
16
+ }
17
+
18
+ function parseGlbHeaderAndJson(glbPath) {
19
+ const fd = fs.openSync(glbPath, 'r');
20
+ try {
21
+ const header = Buffer.alloc(12);
22
+ fs.readSync(fd, header, 0, 12, 0);
23
+ const magic = header.toString('ascii', 0, 4);
24
+ const version = readU32LE(header, 4);
25
+
26
+ if (magic !== 'glTF' || version !== 2) {
27
+ throw new Error(`Not a glTF 2.0 GLB: magic=${magic}, version=${version}`);
28
+ }
29
+
30
+ const chunk0 = Buffer.alloc(8);
31
+ fs.readSync(fd, chunk0, 0, 8, 12);
32
+ const jsonLen = readU32LE(chunk0, 0);
33
+ const jsonType = readU32LE(chunk0, 4);
34
+ if (jsonType !== 0x4e4f534a) {
35
+ throw new Error(`Invalid JSON chunk type: 0x${jsonType.toString(16)}`);
36
+ }
37
+
38
+ const jsonBuf = Buffer.alloc(jsonLen);
39
+ fs.readSync(fd, jsonBuf, 0, jsonLen, 20);
40
+ const jsonStr = jsonBuf.toString('utf8').replace(/\s+$/g, '');
41
+ const gltf = JSON.parse(jsonStr);
42
+ return { gltf };
43
+ } finally {
44
+ fs.closeSync(fd);
45
+ }
46
+ }
47
+
48
+ async function main() {
49
+ const argv = process.argv.slice(2);
50
+ const flags = new Set(argv.filter((a) => a.startsWith('--')));
51
+ const positional = argv.filter((a) => !a.startsWith('--'));
52
+
53
+ const inputPath = positional[0];
54
+ if (!inputPath) {
55
+ usage();
56
+ process.exit(1);
57
+ }
58
+
59
+ const outputDir = positional[1] || fs.mkdtempSync(path.join(os.tmpdir(), 'occt-gltf-addon-'));
60
+ fs.mkdirSync(outputDir, { recursive: true });
61
+
62
+ const enableDraco = flags.has('--draco');
63
+ const zUp = flags.has('--zup');
64
+ const quiet = flags.has('--quiet');
65
+
66
+ const baseName = path.parse(inputPath).name || 'out';
67
+ const outMin = path.join(outputDir, `${baseName}.min.glb`);
68
+ const outNormal = path.join(outputDir, `${baseName}.glb`);
69
+
70
+ // Require the package under test.
71
+ // In a consumer project, this should be: require('occt-gltf-addon')
72
+ // Here we use '.' so it works when running inside the package directory too.
73
+ // eslint-disable-next-line global-require, import/no-dynamic-require
74
+ const { convertSTEPToGLTF } = require('.');
75
+
76
+ const outputOpts = {
77
+ unit: 'm',
78
+ bakeTransforms: true,
79
+ center: zUp ? 'xy' : 'xz',
80
+ zUp,
81
+ draco: enableDraco
82
+ ? { enabled: true, compressionLevel: 7, quantizationBitsPosition: 14, quantizationBitsNormal: 10 }
83
+ : { enabled: false },
84
+ };
85
+
86
+ const variants = [
87
+ {
88
+ outputPath: outMin,
89
+ tessellation: { linearDeflection: 5, angularDeflection: Math.PI / 3 },
90
+ filter: { minBBoxDiagonal: 60 },
91
+ output: outputOpts,
92
+ },
93
+ {
94
+ outputPath: outNormal,
95
+ tessellation: { linearDeflection: 0.2, angularDeflection: 0.6, smoothNormals: true, normalCreaseAngle: Math.PI / 3 },
96
+ output: outputOpts,
97
+ },
98
+ ];
99
+
100
+ console.log('[smoke] input:', inputPath);
101
+ console.log('[smoke] outputDir:', outputDir);
102
+ console.log('[smoke] draco:', enableDraco);
103
+ console.log('[smoke] zUp:', zUp);
104
+
105
+ await convertSTEPToGLTF({
106
+ inputPath,
107
+ logLevel: quiet ? 'quiet' : 'info',
108
+ variants,
109
+ });
110
+
111
+ for (const p of [outMin, outNormal]) {
112
+ if (!fs.existsSync(p)) {
113
+ throw new Error(`Missing output file: ${p}`);
114
+ }
115
+ const st = fs.statSync(p);
116
+ if (st.size < 32) {
117
+ throw new Error(`Output file too small: ${p} (${st.size} bytes)`);
118
+ }
119
+
120
+ const { gltf } = parseGlbHeaderAndJson(p);
121
+ const nodes = Array.isArray(gltf.nodes) ? gltf.nodes.length : 0;
122
+ const meshes = Array.isArray(gltf.meshes) ? gltf.meshes.length : 0;
123
+ const nodesWithMatrix = Array.isArray(gltf.nodes)
124
+ ? gltf.nodes.reduce((n, x) => n + (x && Object.prototype.hasOwnProperty.call(x, 'matrix') ? 1 : 0), 0)
125
+ : 0;
126
+
127
+ if (nodes === 0 || meshes === 0) {
128
+ throw new Error(`Invalid glTF content in ${p}: nodes=${nodes}, meshes=${meshes}`);
129
+ }
130
+ if (nodesWithMatrix !== 0) {
131
+ throw new Error(`Expected baked transforms (no node.matrix). Got nodesWithMatrix=${nodesWithMatrix} in ${p}`);
132
+ }
133
+
134
+ if (enableDraco) {
135
+ const used = gltf.extensionsUsed || [];
136
+ if (!used.includes('KHR_draco_mesh_compression')) {
137
+ throw new Error(`Expected Draco extensionUsed in ${p}`);
138
+ }
139
+ }
140
+
141
+ console.log('[smoke] OK:', p, `(${st.size} bytes)`);
142
+ }
143
+
144
+ console.log('[smoke] PASS');
145
+ }
146
+
147
+ main().catch((e) => {
148
+ console.error('[smoke] FAIL');
149
+ console.error(e && (e.stack || e.message || e));
150
+ process.exit(1);
151
+ });
152
+