brep-io-kernel 1.0.34 → 1.0.35
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-kernel/brep-kernel.js +8357 -8091
- package/package.json +1 -1
- package/src/UI/fileManagerWidget.js +497 -77
- package/src/exporters/threeMF.js +170 -12
- package/src/features/assemblyComponent/AssemblyComponentFeature.js +322 -25
- package/src/githubStorage.js +101 -44
- package/src/services/componentLibrary.js +10 -4
package/src/exporters/threeMF.js
CHANGED
|
@@ -211,10 +211,161 @@ function _resolveColorHex(meta, keys) {
|
|
|
211
211
|
return _parseColorToHex(raw);
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Compute per-triangle material indices as they will be assigned in build3MFModelXML.
|
|
216
|
+
* Returns an array of length triCount where each entry is a material index (number),
|
|
217
|
+
* or null when the triangle would fall back to the default resource (no material).
|
|
218
|
+
*
|
|
219
|
+
* This mirrors the metadata color / face-tag fallback logic used by the exporter,
|
|
220
|
+
* so downstream importers can predict ThreeMFLoader's mesh ordering.
|
|
221
|
+
*/
|
|
222
|
+
export function computeTriangleMaterialIndices(solid, mesh, opts = {}) {
|
|
223
|
+
try {
|
|
224
|
+
if (!solid || !mesh) return null;
|
|
225
|
+
const triVerts = mesh.triVerts;
|
|
226
|
+
const triCount = (triVerts && triVerts.length) ? ((triVerts.length / 3) | 0) : 0;
|
|
227
|
+
if (!triCount) return null;
|
|
228
|
+
|
|
229
|
+
const faceIDs = (mesh.faceID && mesh.faceID.length === triCount) ? mesh.faceID : null;
|
|
230
|
+
if (!faceIDs || !faceIDs.length) return null;
|
|
231
|
+
|
|
232
|
+
const useMetadataColors = opts.useMetadataColors !== false;
|
|
233
|
+
const includeFaceTags = opts.includeFaceTags !== false;
|
|
234
|
+
const metadataManager = opts.metadataManager && typeof opts.metadataManager.getMetadata === 'function'
|
|
235
|
+
? opts.metadataManager
|
|
236
|
+
: null;
|
|
237
|
+
|
|
238
|
+
let idToFaceName = (solid && solid._idToFaceName instanceof Map) ? solid._idToFaceName : null;
|
|
239
|
+
if (!idToFaceName && solid && solid._faceNameToID instanceof Map) {
|
|
240
|
+
const inverted = new Map();
|
|
241
|
+
for (const [faceName, faceId] of solid._faceNameToID.entries()) {
|
|
242
|
+
if (faceId == null || faceName == null) continue;
|
|
243
|
+
inverted.set(faceId, String(faceName));
|
|
244
|
+
}
|
|
245
|
+
if (inverted.size) idToFaceName = inverted;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
let solidColorHex = null;
|
|
249
|
+
if (useMetadataColors) {
|
|
250
|
+
try {
|
|
251
|
+
const solidMeta = (metadataManager && solid?.name)
|
|
252
|
+
? metadataManager.getMetadata(solid.name)
|
|
253
|
+
: null;
|
|
254
|
+
solidColorHex = _resolveColorHex(solidMeta, ['solidColor', 'color'])
|
|
255
|
+
|| _resolveColorHex(solid?.userData?.metadata || null, ['solidColor', 'color']);
|
|
256
|
+
} catch {
|
|
257
|
+
solidColorHex = null;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
let faceColorById = null;
|
|
262
|
+
if (useMetadataColors && idToFaceName) {
|
|
263
|
+
faceColorById = new Map();
|
|
264
|
+
const seen = new Set();
|
|
265
|
+
for (let t = 0; t < faceIDs.length; t++) {
|
|
266
|
+
const fid = faceIDs[t] >>> 0;
|
|
267
|
+
if (seen.has(fid)) continue;
|
|
268
|
+
seen.add(fid);
|
|
269
|
+
const faceName = idToFaceName.get(fid) || `FACE_${fid}`;
|
|
270
|
+
let faceHex = null;
|
|
271
|
+
if (metadataManager) {
|
|
272
|
+
try { faceHex = _resolveColorHex(metadataManager.getMetadata(faceName), ['faceColor', 'color']); } catch { faceHex = null; }
|
|
273
|
+
}
|
|
274
|
+
if (!faceHex) {
|
|
275
|
+
try {
|
|
276
|
+
const faceMeta = (typeof solid.getFaceMetadata === 'function') ? solid.getFaceMetadata(faceName) : null;
|
|
277
|
+
faceHex = _resolveColorHex(faceMeta, ['faceColor', 'color']);
|
|
278
|
+
} catch { faceHex = null; }
|
|
279
|
+
}
|
|
280
|
+
if (faceHex) faceColorById.set(fid, faceHex);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const hasFaceColors = !!(faceColorById && faceColorById.size);
|
|
285
|
+
const hasSolidColor = !!solidColorHex;
|
|
286
|
+
const hasMetadataColors = !!(useMetadataColors && (hasSolidColor || hasFaceColors));
|
|
287
|
+
|
|
288
|
+
let faceMatIndexById = null;
|
|
289
|
+
let solidMatIndex = null;
|
|
290
|
+
let defaultMatIndex = null;
|
|
291
|
+
let useFaceTagsFallback = false;
|
|
292
|
+
|
|
293
|
+
if (hasMetadataColors) {
|
|
294
|
+
const colorToIndex = new Map();
|
|
295
|
+
const addMaterial = (hex, label) => {
|
|
296
|
+
if (!hex) return null;
|
|
297
|
+
if (!colorToIndex.has(hex)) {
|
|
298
|
+
colorToIndex.set(hex, colorToIndex.size);
|
|
299
|
+
}
|
|
300
|
+
return colorToIndex.get(hex);
|
|
301
|
+
};
|
|
302
|
+
if (hasSolidColor) {
|
|
303
|
+
const solidLabel = solid?.name ? `${solid.name}_SOLID` : 'SOLID';
|
|
304
|
+
solidMatIndex = addMaterial(solidColorHex, solidLabel);
|
|
305
|
+
} else {
|
|
306
|
+
const defaultLabel = solid?.name ? `${solid.name}_DEFAULT` : 'DEFAULT';
|
|
307
|
+
const defaultHex = _parseColorToHex(opts.defaultFaceColor) || '#c0c0c0';
|
|
308
|
+
defaultMatIndex = addMaterial(defaultHex, defaultLabel);
|
|
309
|
+
}
|
|
310
|
+
if (hasFaceColors && faceColorById) {
|
|
311
|
+
faceMatIndexById = new Map();
|
|
312
|
+
for (const [fid, hex] of faceColorById.entries()) {
|
|
313
|
+
const faceName = idToFaceName ? (idToFaceName.get(fid) || `FACE_${fid}`) : `FACE_${fid}`;
|
|
314
|
+
const idx = addMaterial(hex, faceName);
|
|
315
|
+
faceMatIndexById.set(fid, idx);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
} else if (includeFaceTags) {
|
|
319
|
+
useFaceTagsFallback = true;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
let faceIndexOf = null;
|
|
323
|
+
if (useFaceTagsFallback) {
|
|
324
|
+
const uniqueIds = [];
|
|
325
|
+
const seen = new Set();
|
|
326
|
+
for (let t = 0; t < faceIDs.length; t++) {
|
|
327
|
+
const fid = faceIDs[t] >>> 0;
|
|
328
|
+
if (seen.has(fid)) continue;
|
|
329
|
+
seen.add(fid);
|
|
330
|
+
uniqueIds.push(fid);
|
|
331
|
+
}
|
|
332
|
+
const idToMatIdx = new Map();
|
|
333
|
+
for (let i = 0; i < uniqueIds.length; i++) idToMatIdx.set(uniqueIds[i], i);
|
|
334
|
+
faceIndexOf = (fid) => idToMatIdx.get(fid) ?? 0;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (!hasMetadataColors && !useFaceTagsFallback && solidMatIndex == null) {
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const triMat = new Array(triCount);
|
|
342
|
+
for (let t = 0; t < triCount; t++) {
|
|
343
|
+
const fid = faceIDs[t] >>> 0;
|
|
344
|
+
let idx = null;
|
|
345
|
+
if (faceMatIndexById && faceMatIndexById.has(fid)) {
|
|
346
|
+
idx = faceMatIndexById.get(fid);
|
|
347
|
+
} else if (useFaceTagsFallback && faceIndexOf) {
|
|
348
|
+
idx = faceIndexOf(fid);
|
|
349
|
+
} else if (solidMatIndex != null) {
|
|
350
|
+
idx = solidMatIndex;
|
|
351
|
+
} else if (defaultMatIndex != null) {
|
|
352
|
+
idx = defaultMatIndex;
|
|
353
|
+
} else {
|
|
354
|
+
idx = null;
|
|
355
|
+
}
|
|
356
|
+
triMat[t] = idx;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return triMat;
|
|
360
|
+
} catch {
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
214
365
|
/**
|
|
215
366
|
* Build the core 3MF model XML for one or more solids.
|
|
216
367
|
* @param {Array} solids Array of SOLID-like objects that expose getMesh() and name.
|
|
217
|
-
* @param {{unit?: 'millimeter'|'inch'|'foot'|'meter'|'centimeter'|'micron', precision?: number, scale?: number, metadataManager?: any, useMetadataColors?: boolean, includeFaceTags?: boolean, applyWorldTransform?: boolean}} opts
|
|
368
|
+
* @param {{unit?: 'millimeter'|'inch'|'foot'|'meter'|'centimeter'|'micron', precision?: number, scale?: number, metadataManager?: any, useMetadataColors?: boolean, includeFaceTags?: boolean, applyWorldTransform?: boolean, defaultFaceColor?: any}} opts
|
|
218
369
|
* @returns {string}
|
|
219
370
|
*/
|
|
220
371
|
export function build3MFModelXML(solids, opts = {}) {
|
|
@@ -276,6 +427,7 @@ export function build3MFModelXML(solids, opts = {}) {
|
|
|
276
427
|
let faceColorById = null;
|
|
277
428
|
let faceMatIndexById = null;
|
|
278
429
|
let solidMatIndex = null;
|
|
430
|
+
let defaultMatIndex = null;
|
|
279
431
|
let useFaceTagsFallback = false;
|
|
280
432
|
|
|
281
433
|
const faceIDs = (mesh.faceID && mesh.faceID.length === tCount) ? mesh.faceID : null;
|
|
@@ -324,15 +476,20 @@ export function build3MFModelXML(solids, opts = {}) {
|
|
|
324
476
|
return colorToIndex.get(hex);
|
|
325
477
|
};
|
|
326
478
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
479
|
+
if (hasSolidColor) solidMatIndex = addMaterial(solidColorHex, `${rawName}_SOLID`);
|
|
480
|
+
if (!hasSolidColor) {
|
|
481
|
+
// Ensure triangles without per-face colors still map to a basematerial
|
|
482
|
+
const defaultHex = _parseColorToHex(opts.defaultFaceColor) || '#c0c0c0';
|
|
483
|
+
defaultMatIndex = addMaterial(defaultHex, `${rawName}_DEFAULT`);
|
|
484
|
+
}
|
|
485
|
+
if (hasFaceColors) {
|
|
486
|
+
faceMatIndexById = new Map();
|
|
487
|
+
for (const [fid, hex] of faceColorById.entries()) {
|
|
488
|
+
const faceName = idToFaceName ? (idToFaceName.get(fid) || `FACE_${fid}`) : `FACE_${fid}`;
|
|
489
|
+
const idx = addMaterial(hex, faceName);
|
|
490
|
+
faceMatIndexById.set(fid, idx);
|
|
335
491
|
}
|
|
492
|
+
}
|
|
336
493
|
|
|
337
494
|
if (materials.length > 0) {
|
|
338
495
|
matPid = nextId++;
|
|
@@ -343,9 +500,10 @@ export function build3MFModelXML(solids, opts = {}) {
|
|
|
343
500
|
lines.push(` <base${nameAttr} displaycolor="${entry.color}"/>`);
|
|
344
501
|
}
|
|
345
502
|
lines.push(' </basematerials>');
|
|
346
|
-
if (solidMatIndex != null) {
|
|
503
|
+
if (solidMatIndex != null || defaultMatIndex != null) {
|
|
347
504
|
objectPidAttr = ` pid="${matPid}"`;
|
|
348
|
-
|
|
505
|
+
const idx = (solidMatIndex != null) ? solidMatIndex : defaultMatIndex;
|
|
506
|
+
objectPindexAttr = ` pindex="${idx}"`;
|
|
349
507
|
}
|
|
350
508
|
}
|
|
351
509
|
} else if (includeFaceTags && faceIDs) {
|
|
@@ -514,7 +672,7 @@ function rootRelsXML({ thumbnailPath, viewImages } = {}) {
|
|
|
514
672
|
/**
|
|
515
673
|
* Generate a 3MF zip archive as Uint8Array.
|
|
516
674
|
* @param {Array} solids Array of SOLID-like objects that expose getMesh() and name.
|
|
517
|
-
* @param {{unit?: string, precision?: number, scale?: number, metadataManager?: any, useMetadataColors?: boolean, includeFaceTags?: boolean, applyWorldTransform?: boolean}} opts
|
|
675
|
+
* @param {{unit?: string, precision?: number, scale?: number, metadataManager?: any, useMetadataColors?: boolean, includeFaceTags?: boolean, applyWorldTransform?: boolean, defaultFaceColor?: any}} opts
|
|
518
676
|
* @returns {Promise<Uint8Array>}
|
|
519
677
|
*/
|
|
520
678
|
export async function generate3MF(solids, opts = {}) {
|