circuit-json-to-gltf 0.0.11 → 0.0.13
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 +110 -15
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -742,6 +742,95 @@ function clearGLBCache() {
|
|
|
742
742
|
glbCache.clear();
|
|
743
743
|
}
|
|
744
744
|
|
|
745
|
+
// lib/loaders/gltf.ts
|
|
746
|
+
async function fetchAsArrayBuffer(url) {
|
|
747
|
+
const response = await fetch(url);
|
|
748
|
+
if (!response.ok) {
|
|
749
|
+
throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
|
|
750
|
+
}
|
|
751
|
+
return response.arrayBuffer();
|
|
752
|
+
}
|
|
753
|
+
function dataUriToArrayBuffer(uri) {
|
|
754
|
+
const a = uri.split(",");
|
|
755
|
+
const byteString = atob(a[1]);
|
|
756
|
+
const ab = new ArrayBuffer(byteString.length);
|
|
757
|
+
const ia = new Uint8Array(ab);
|
|
758
|
+
for (let i = 0; i < byteString.length; i++) {
|
|
759
|
+
ia[i] = byteString.charCodeAt(i);
|
|
760
|
+
}
|
|
761
|
+
return ab;
|
|
762
|
+
}
|
|
763
|
+
async function fetchGltfAndConvertToGlb(url) {
|
|
764
|
+
const gltfResponse = await fetch(url);
|
|
765
|
+
if (!gltfResponse.ok) {
|
|
766
|
+
throw new Error(`Failed to fetch glTF file: ${gltfResponse.statusText}`);
|
|
767
|
+
}
|
|
768
|
+
const gltf = await gltfResponse.json();
|
|
769
|
+
const bufferPromises = [];
|
|
770
|
+
if (gltf.buffers) {
|
|
771
|
+
for (const buffer of gltf.buffers) {
|
|
772
|
+
if (buffer.uri) {
|
|
773
|
+
if (buffer.uri.startsWith("data:")) {
|
|
774
|
+
bufferPromises.push(Promise.resolve(dataUriToArrayBuffer(buffer.uri)));
|
|
775
|
+
} else {
|
|
776
|
+
const bufferUrl = new URL(buffer.uri, url).toString();
|
|
777
|
+
bufferPromises.push(fetchAsArrayBuffer(bufferUrl));
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
const buffers = await Promise.all(bufferPromises);
|
|
783
|
+
let binaryBuffer = new ArrayBuffer(0);
|
|
784
|
+
if (buffers.length > 0 && buffers[0]) {
|
|
785
|
+
binaryBuffer = buffers[0];
|
|
786
|
+
}
|
|
787
|
+
if (gltf.buffers && gltf.buffers.length > 0) {
|
|
788
|
+
delete gltf.buffers[0].uri;
|
|
789
|
+
gltf.buffers[0].byteLength = binaryBuffer.byteLength;
|
|
790
|
+
}
|
|
791
|
+
const jsonString = JSON.stringify(gltf);
|
|
792
|
+
const jsonBuffer = new TextEncoder().encode(jsonString);
|
|
793
|
+
const jsonPadding = (4 - jsonBuffer.length % 4) % 4;
|
|
794
|
+
const binaryPadding = (4 - binaryBuffer.byteLength % 4) % 4;
|
|
795
|
+
const totalLength = 12 + // header
|
|
796
|
+
(8 + jsonBuffer.length + jsonPadding) + // json chunk
|
|
797
|
+
(binaryBuffer.byteLength > 0 ? 8 + binaryBuffer.byteLength + binaryPadding : 0);
|
|
798
|
+
const glbBuffer = new ArrayBuffer(totalLength);
|
|
799
|
+
const dataView = new DataView(glbBuffer);
|
|
800
|
+
let offset = 0;
|
|
801
|
+
dataView.setUint32(offset, 1179937895, true);
|
|
802
|
+
offset += 4;
|
|
803
|
+
dataView.setUint32(offset, 2, true);
|
|
804
|
+
offset += 4;
|
|
805
|
+
dataView.setUint32(offset, totalLength, true);
|
|
806
|
+
offset += 4;
|
|
807
|
+
dataView.setUint32(offset, jsonBuffer.length + jsonPadding, true);
|
|
808
|
+
offset += 4;
|
|
809
|
+
dataView.setUint32(offset, 1313821514, true);
|
|
810
|
+
offset += 4;
|
|
811
|
+
new Uint8Array(glbBuffer, offset).set(jsonBuffer);
|
|
812
|
+
offset += jsonBuffer.length;
|
|
813
|
+
for (let i = 0; i < jsonPadding; i++) {
|
|
814
|
+
dataView.setUint8(offset++, 32);
|
|
815
|
+
}
|
|
816
|
+
if (binaryBuffer.byteLength > 0) {
|
|
817
|
+
dataView.setUint32(offset, binaryBuffer.byteLength + binaryPadding, true);
|
|
818
|
+
offset += 4;
|
|
819
|
+
dataView.setUint32(offset, 5130562, true);
|
|
820
|
+
offset += 4;
|
|
821
|
+
new Uint8Array(glbBuffer, offset).set(new Uint8Array(binaryBuffer));
|
|
822
|
+
offset += binaryBuffer.byteLength;
|
|
823
|
+
for (let i = 0; i < binaryPadding; i++) {
|
|
824
|
+
dataView.setUint8(offset++, 0);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
return glbBuffer;
|
|
828
|
+
}
|
|
829
|
+
async function loadGLTF(url, transform) {
|
|
830
|
+
const glb_buffer = await fetchGltfAndConvertToGlb(url);
|
|
831
|
+
return parseGLB(glb_buffer, transform);
|
|
832
|
+
}
|
|
833
|
+
|
|
745
834
|
// lib/converters/board-renderer.ts
|
|
746
835
|
import { convertCircuitJsonToPcbSvg } from "circuit-to-svg";
|
|
747
836
|
async function renderBoardLayer(circuitJson, options) {
|
|
@@ -900,8 +989,15 @@ async function convertCircuitJsonTo3D(circuitJson, options = {}) {
|
|
|
900
989
|
const cadComponents = db.cad_component?.list?.() ?? [];
|
|
901
990
|
const pcbComponentIdsWith3D = /* @__PURE__ */ new Set();
|
|
902
991
|
for (const cad of cadComponents) {
|
|
903
|
-
|
|
904
|
-
|
|
992
|
+
let { model_stl_url, model_obj_url, model_glb_url, model_gltf_url } = cad;
|
|
993
|
+
const hasModelUrl = Boolean(
|
|
994
|
+
model_stl_url || model_obj_url || model_glb_url || model_gltf_url
|
|
995
|
+
);
|
|
996
|
+
if (!hasModelUrl && cad.footprinter_string) {
|
|
997
|
+
model_glb_url = `https://modelcdn.tscircuit.com/jscad_models/${cad.footprinter_string}.glb`;
|
|
998
|
+
}
|
|
999
|
+
if (!model_stl_url && !model_obj_url && !model_glb_url && !model_gltf_url)
|
|
1000
|
+
continue;
|
|
905
1001
|
pcbComponentIdsWith3D.add(cad.pcb_component_id);
|
|
906
1002
|
const pcbComponent = db.pcb_component.get(cad.pcb_component_id);
|
|
907
1003
|
const size = cad.size ?? {
|
|
@@ -914,26 +1010,25 @@ async function convertCircuitJsonTo3D(circuitJson, options = {}) {
|
|
|
914
1010
|
y: boardThickness / 2 + size.y / 2,
|
|
915
1011
|
z: pcbComponent?.center.y ?? 0
|
|
916
1012
|
};
|
|
1013
|
+
const meshType = model_stl_url ? "stl" : model_obj_url ? "obj" : model_gltf_url ? "gltf" : "glb";
|
|
917
1014
|
const box = {
|
|
918
1015
|
center,
|
|
919
1016
|
size,
|
|
920
|
-
meshUrl: model_stl_url || model_obj_url || model_glb_url,
|
|
921
|
-
meshType
|
|
1017
|
+
meshUrl: model_stl_url || model_obj_url || model_glb_url || model_gltf_url,
|
|
1018
|
+
meshType
|
|
922
1019
|
};
|
|
923
1020
|
if (cad.rotation) {
|
|
924
1021
|
box.rotation = convertRotationFromCadRotation(cad.rotation);
|
|
925
1022
|
}
|
|
926
|
-
const defaultTransform = coordinateTransform ?? (model_glb_url ? void 0 : COORDINATE_TRANSFORMS.Z_UP_TO_Y_UP_USB_FIX);
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
} catch (error) {
|
|
936
|
-
console.warn(`Failed to load 3D model: ${error}`);
|
|
1023
|
+
const defaultTransform = coordinateTransform ?? (model_glb_url || model_gltf_url ? void 0 : COORDINATE_TRANSFORMS.Z_UP_TO_Y_UP_USB_FIX);
|
|
1024
|
+
if (model_stl_url) {
|
|
1025
|
+
box.mesh = await loadSTL(model_stl_url, defaultTransform);
|
|
1026
|
+
} else if (model_obj_url) {
|
|
1027
|
+
box.mesh = await loadOBJ(model_obj_url, defaultTransform);
|
|
1028
|
+
} else if (model_glb_url) {
|
|
1029
|
+
box.mesh = await loadGLB(model_glb_url, defaultTransform);
|
|
1030
|
+
} else if (model_gltf_url) {
|
|
1031
|
+
box.mesh = await loadGLTF(model_gltf_url, defaultTransform);
|
|
937
1032
|
}
|
|
938
1033
|
if (!box.mesh) {
|
|
939
1034
|
box.color = componentColor;
|
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.
|
|
5
|
+
"version": "0.0.13",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "bun test tests/",
|
|
8
8
|
"format": "biome format --write .",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"circuit-json": "^0.0.267",
|
|
29
29
|
"circuit-to-svg": "^0.0.175",
|
|
30
30
|
"looks-same": "^9.0.1",
|
|
31
|
-
"poppygl": "^0.0.
|
|
31
|
+
"poppygl": "^0.0.9",
|
|
32
32
|
"react": "^19.1.1",
|
|
33
33
|
"react-cosmos": "^7.0.0",
|
|
34
34
|
"react-cosmos-plugin-vite": "^7.0.0",
|