circuit-json-to-gltf 0.0.13 → 0.0.15

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.d.ts CHANGED
@@ -135,7 +135,7 @@ declare function clearSTLCache(): void;
135
135
  declare function loadOBJ(url: string, transform?: CoordinateTransformConfig): Promise<OBJMesh>;
136
136
  declare function clearOBJCache(): void;
137
137
 
138
- declare function loadGLB(url: string, transform?: CoordinateTransformConfig): Promise<STLMesh>;
138
+ declare function loadGLB(url: string, transform?: CoordinateTransformConfig): Promise<STLMesh | OBJMesh>;
139
139
  declare function clearGLBCache(): void;
140
140
 
141
141
  declare function convertCircuitJsonTo3D(circuitJson: CircuitJson, options?: CircuitTo3DOptions): Promise<Scene3D>;
package/dist/index.js CHANGED
@@ -534,11 +534,59 @@ function parseGLB(buffer, transform) {
534
534
  axisMapping: { x: "x", y: "z", z: "y" }
535
535
  };
536
536
  const transformedTriangles = transformTriangles(triangles, finalConfig);
537
+ const hasColors = transformedTriangles.some((t) => t.color !== void 0);
538
+ if (hasColors) {
539
+ return convertToOBJMesh(transformedTriangles);
540
+ }
537
541
  return {
538
542
  triangles: transformedTriangles,
539
543
  boundingBox: calculateBoundingBox3(transformedTriangles)
540
544
  };
541
545
  }
546
+ function convertToOBJMesh(triangles) {
547
+ const colorGroups = /* @__PURE__ */ new Map();
548
+ for (const triangle of triangles) {
549
+ const colorKey = triangle.color ? JSON.stringify(triangle.color) : "default";
550
+ if (!colorGroups.has(colorKey)) {
551
+ colorGroups.set(colorKey, []);
552
+ }
553
+ colorGroups.get(colorKey).push(triangle);
554
+ }
555
+ const materials = /* @__PURE__ */ new Map();
556
+ const materialIndexMap = /* @__PURE__ */ new Map();
557
+ let materialIndex = 0;
558
+ const trianglesWithMaterialIndex = [];
559
+ for (const [colorKey, groupTriangles] of colorGroups) {
560
+ const materialName = `Material_${materialIndex}`;
561
+ materialIndexMap.set(materialName, materialIndex);
562
+ if (colorKey === "default") {
563
+ materials.set(materialName, {
564
+ name: materialName,
565
+ color: [179, 179, 179, 1]
566
+ // 0.7 * 255 = 179
567
+ });
568
+ } else {
569
+ const color = JSON.parse(colorKey);
570
+ materials.set(materialName, {
571
+ name: materialName,
572
+ color
573
+ });
574
+ }
575
+ for (const triangle of groupTriangles) {
576
+ trianglesWithMaterialIndex.push({
577
+ ...triangle,
578
+ materialIndex
579
+ });
580
+ }
581
+ materialIndex++;
582
+ }
583
+ return {
584
+ triangles: trianglesWithMaterialIndex,
585
+ boundingBox: calculateBoundingBox3(trianglesWithMaterialIndex),
586
+ materials,
587
+ materialIndexMap
588
+ };
589
+ }
542
590
  function extractTrianglesFromGLTF(gltf, binaryBuffer) {
543
591
  const triangles = [];
544
592
  if (!gltf.meshes || !gltf.accessors || !gltf.bufferViews) {
@@ -570,6 +618,29 @@ function extractTrianglesFromGLTF(gltf, binaryBuffer) {
570
618
  binaryBuffer
571
619
  );
572
620
  }
621
+ let vertexColors;
622
+ const colorAccessorIndex = primitive.attributes.COLOR_0;
623
+ if (colorAccessorIndex !== void 0) {
624
+ const colorAccessor = gltf.accessors[colorAccessorIndex];
625
+ vertexColors = getAccessorData(
626
+ colorAccessor,
627
+ gltf.bufferViews,
628
+ binaryBuffer
629
+ );
630
+ }
631
+ let materialColor;
632
+ if (!vertexColors && primitive.material !== void 0 && gltf.materials) {
633
+ const material = gltf.materials[primitive.material];
634
+ if (material?.pbrMetallicRoughness?.baseColorFactor) {
635
+ const factor = material.pbrMetallicRoughness.baseColorFactor;
636
+ materialColor = [
637
+ Math.round(factor[0] * 255),
638
+ Math.round(factor[1] * 255),
639
+ Math.round(factor[2] * 255),
640
+ factor[3]
641
+ ];
642
+ }
643
+ }
573
644
  let indices;
574
645
  if (primitive.indices !== void 0) {
575
646
  const indexAccessor = gltf.accessors[primitive.indices];
@@ -619,7 +690,44 @@ function extractTrianglesFromGLTF(gltf, binaryBuffer) {
619
690
  } else {
620
691
  normal = computeNormal(v0, v1, v2);
621
692
  }
622
- triangles.push({ vertices: [v0, v1, v2], normal });
693
+ let triangleColor;
694
+ if (vertexColors) {
695
+ const componentsPerColor = vertexColors.length / vertexCount;
696
+ if (componentsPerColor === 3) {
697
+ triangleColor = [
698
+ Math.round(
699
+ (vertexColors[i0 * 3] + vertexColors[i1 * 3] + vertexColors[i2 * 3]) / 3 * 255
700
+ ),
701
+ Math.round(
702
+ (vertexColors[i0 * 3 + 1] + vertexColors[i1 * 3 + 1] + vertexColors[i2 * 3 + 1]) / 3 * 255
703
+ ),
704
+ Math.round(
705
+ (vertexColors[i0 * 3 + 2] + vertexColors[i1 * 3 + 2] + vertexColors[i2 * 3 + 2]) / 3 * 255
706
+ ),
707
+ 1
708
+ ];
709
+ } else if (componentsPerColor === 4) {
710
+ triangleColor = [
711
+ Math.round(
712
+ (vertexColors[i0 * 4] + vertexColors[i1 * 4] + vertexColors[i2 * 4]) / 3 * 255
713
+ ),
714
+ Math.round(
715
+ (vertexColors[i0 * 4 + 1] + vertexColors[i1 * 4 + 1] + vertexColors[i2 * 4 + 1]) / 3 * 255
716
+ ),
717
+ Math.round(
718
+ (vertexColors[i0 * 4 + 2] + vertexColors[i1 * 4 + 2] + vertexColors[i2 * 4 + 2]) / 3 * 255
719
+ ),
720
+ (vertexColors[i0 * 4 + 3] + vertexColors[i1 * 4 + 3] + vertexColors[i2 * 4 + 3]) / 3
721
+ ];
722
+ }
723
+ } else {
724
+ triangleColor = materialColor;
725
+ }
726
+ triangles.push({
727
+ vertices: [v0, v1, v2],
728
+ normal,
729
+ color: triangleColor
730
+ });
623
731
  }
624
732
  } else {
625
733
  for (let i = 0; i < vertexCount; i += 3) {
@@ -648,7 +756,44 @@ function extractTrianglesFromGLTF(gltf, binaryBuffer) {
648
756
  } else {
649
757
  normal = computeNormal(v0, v1, v2);
650
758
  }
651
- triangles.push({ vertices: [v0, v1, v2], normal });
759
+ let triangleColor;
760
+ if (vertexColors) {
761
+ const componentsPerColor = vertexColors.length / vertexCount;
762
+ if (componentsPerColor === 3) {
763
+ triangleColor = [
764
+ Math.round(
765
+ (vertexColors[i * 3] + vertexColors[(i + 1) * 3] + vertexColors[(i + 2) * 3]) / 3 * 255
766
+ ),
767
+ Math.round(
768
+ (vertexColors[i * 3 + 1] + vertexColors[(i + 1) * 3 + 1] + vertexColors[(i + 2) * 3 + 1]) / 3 * 255
769
+ ),
770
+ Math.round(
771
+ (vertexColors[i * 3 + 2] + vertexColors[(i + 1) * 3 + 2] + vertexColors[(i + 2) * 3 + 2]) / 3 * 255
772
+ ),
773
+ 1
774
+ ];
775
+ } else if (componentsPerColor === 4) {
776
+ triangleColor = [
777
+ Math.round(
778
+ (vertexColors[i * 4] + vertexColors[(i + 1) * 4] + vertexColors[(i + 2) * 4]) / 3 * 255
779
+ ),
780
+ Math.round(
781
+ (vertexColors[i * 4 + 1] + vertexColors[(i + 1) * 4 + 1] + vertexColors[(i + 2) * 4 + 1]) / 3 * 255
782
+ ),
783
+ Math.round(
784
+ (vertexColors[i * 4 + 2] + vertexColors[(i + 1) * 4 + 2] + vertexColors[(i + 2) * 4 + 2]) / 3 * 255
785
+ ),
786
+ (vertexColors[i * 4 + 3] + vertexColors[(i + 1) * 4 + 3] + vertexColors[(i + 2) * 4 + 3]) / 3
787
+ ];
788
+ }
789
+ } else {
790
+ triangleColor = materialColor;
791
+ }
792
+ triangles.push({
793
+ vertices: [v0, v1, v2],
794
+ normal,
795
+ color: triangleColor
796
+ });
652
797
  }
653
798
  }
654
799
  }
@@ -1000,14 +1145,19 @@ async function convertCircuitJsonTo3D(circuitJson, options = {}) {
1000
1145
  continue;
1001
1146
  pcbComponentIdsWith3D.add(cad.pcb_component_id);
1002
1147
  const pcbComponent = db.pcb_component.get(cad.pcb_component_id);
1148
+ const isBottomLayer = pcbComponent?.layer === "bottom";
1003
1149
  const size = cad.size ?? {
1004
1150
  x: pcbComponent?.width ?? 2,
1005
1151
  y: defaultComponentHeight,
1006
1152
  z: pcbComponent?.height ?? 2
1007
1153
  };
1008
- const center = cad.position ? { x: cad.position.x, y: cad.position.z, z: cad.position.y } : {
1154
+ const center = cad.position ? {
1155
+ x: cad.position.x,
1156
+ y: isBottomLayer ? -Math.abs(cad.position.z) : cad.position.z,
1157
+ z: cad.position.y
1158
+ } : {
1009
1159
  x: pcbComponent?.center.x ?? 0,
1010
- y: boardThickness / 2 + size.y / 2,
1160
+ y: isBottomLayer ? -(boardThickness / 2 + size.y / 2) : boardThickness / 2 + size.y / 2,
1011
1161
  z: pcbComponent?.center.y ?? 0
1012
1162
  };
1013
1163
  const meshType = model_stl_url ? "stl" : model_obj_url ? "obj" : model_gltf_url ? "gltf" : "glb";
@@ -1018,7 +1168,35 @@ async function convertCircuitJsonTo3D(circuitJson, options = {}) {
1018
1168
  meshType
1019
1169
  };
1020
1170
  if (cad.rotation) {
1021
- box.rotation = convertRotationFromCadRotation(cad.rotation);
1171
+ if (model_glb_url || model_gltf_url) {
1172
+ box.rotation = convertRotationFromCadRotation({
1173
+ x: isBottomLayer ? cad.rotation.x + 180 : cad.rotation.x,
1174
+ y: cad.rotation.z,
1175
+ // Circuit Z rotation becomes model Y rotation
1176
+ z: cad.rotation.y
1177
+ // Circuit Y rotation becomes model Z rotation
1178
+ });
1179
+ } else {
1180
+ box.rotation = convertRotationFromCadRotation({
1181
+ x: isBottomLayer ? cad.rotation.x + 180 : cad.rotation.x,
1182
+ y: cad.rotation.y,
1183
+ z: cad.rotation.z
1184
+ });
1185
+ }
1186
+ } else if (isBottomLayer) {
1187
+ if (model_glb_url || model_gltf_url) {
1188
+ box.rotation = convertRotationFromCadRotation({
1189
+ x: 180,
1190
+ y: 0,
1191
+ z: 0
1192
+ });
1193
+ } else {
1194
+ box.rotation = convertRotationFromCadRotation({
1195
+ x: 180,
1196
+ y: 0,
1197
+ z: 0
1198
+ });
1199
+ }
1022
1200
  }
1023
1201
  const defaultTransform = coordinateTransform ?? (model_glb_url || model_gltf_url ? void 0 : COORDINATE_TRANSFORMS.Z_UP_TO_Y_UP_USB_FIX);
1024
1202
  if (model_stl_url) {
@@ -1044,10 +1222,11 @@ async function convertCircuitJsonTo3D(circuitJson, options = {}) {
1044
1222
  Math.min(component.width, component.height),
1045
1223
  defaultComponentHeight
1046
1224
  );
1225
+ const isBottomLayer = component.layer === "bottom";
1047
1226
  boxes.push({
1048
1227
  center: {
1049
1228
  x: component.center.x,
1050
- y: boardThickness / 2 + compHeight / 2,
1229
+ y: isBottomLayer ? -(boardThickness / 2 + compHeight / 2) : boardThickness / 2 + compHeight / 2,
1051
1230
  z: component.center.y
1052
1231
  },
1053
1232
  size: {
@@ -1710,18 +1889,19 @@ var GLTFBuilder = class {
1710
1889
  const meshDataArray = createMeshFromOBJ(objMesh);
1711
1890
  const objMaterialIndices = /* @__PURE__ */ new Map();
1712
1891
  for (const [name, objMaterial] of objMesh.materials) {
1713
- const dissolve = objMaterial.dissolve ?? 1;
1714
- const alpha = 1 - dissolve;
1715
- let baseColor = [0.3, 0.3, 0.3, alpha];
1892
+ let baseColor = [0.3, 0.3, 0.3, 1];
1716
1893
  if (objMaterial.color) {
1717
1894
  const color = typeof objMaterial.color === "string" ? this.parseColorString(objMaterial.color) : [
1718
1895
  objMaterial.color[0] / 255,
1719
1896
  objMaterial.color[1] / 255,
1720
1897
  objMaterial.color[2] / 255,
1721
- alpha
1898
+ objMaterial.color[3]
1899
+ // Use the alpha from the color
1722
1900
  ];
1723
- baseColor = [color[0], color[1], color[2], alpha];
1901
+ baseColor = [color[0], color[1], color[2], color[3]];
1724
1902
  }
1903
+ const alpha = objMaterial.dissolve !== void 0 ? 1 - objMaterial.dissolve : baseColor[3];
1904
+ baseColor[3] = alpha;
1725
1905
  const gltfMaterialIndex = this.addMaterial({
1726
1906
  name: `OBJ_${name}`,
1727
1907
  pbrMetallicRoughness: {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "circuit-json-to-gltf",
3
3
  "main": "dist/index.js",
4
4
  "type": "module",
5
- "version": "0.0.13",
5
+ "version": "0.0.15",
6
6
  "scripts": {
7
7
  "test": "bun test tests/",
8
8
  "format": "biome format --write .",