circuit-json-to-gltf 0.0.12 → 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 +107 -16
- package/package.json +1 -1
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,12 +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
|
-
let { model_stl_url, model_obj_url, model_glb_url } = cad;
|
|
904
|
-
const hasModelUrl = Boolean(
|
|
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
|
+
);
|
|
905
996
|
if (!hasModelUrl && cad.footprinter_string) {
|
|
906
997
|
model_glb_url = `https://modelcdn.tscircuit.com/jscad_models/${cad.footprinter_string}.glb`;
|
|
907
998
|
}
|
|
908
|
-
if (!model_stl_url && !model_obj_url && !model_glb_url)
|
|
999
|
+
if (!model_stl_url && !model_obj_url && !model_glb_url && !model_gltf_url)
|
|
1000
|
+
continue;
|
|
909
1001
|
pcbComponentIdsWith3D.add(cad.pcb_component_id);
|
|
910
1002
|
const pcbComponent = db.pcb_component.get(cad.pcb_component_id);
|
|
911
1003
|
const size = cad.size ?? {
|
|
@@ -918,26 +1010,25 @@ async function convertCircuitJsonTo3D(circuitJson, options = {}) {
|
|
|
918
1010
|
y: boardThickness / 2 + size.y / 2,
|
|
919
1011
|
z: pcbComponent?.center.y ?? 0
|
|
920
1012
|
};
|
|
1013
|
+
const meshType = model_stl_url ? "stl" : model_obj_url ? "obj" : model_gltf_url ? "gltf" : "glb";
|
|
921
1014
|
const box = {
|
|
922
1015
|
center,
|
|
923
1016
|
size,
|
|
924
|
-
meshUrl: model_stl_url || model_obj_url || model_glb_url,
|
|
925
|
-
meshType
|
|
1017
|
+
meshUrl: model_stl_url || model_obj_url || model_glb_url || model_gltf_url,
|
|
1018
|
+
meshType
|
|
926
1019
|
};
|
|
927
1020
|
if (cad.rotation) {
|
|
928
1021
|
box.rotation = convertRotationFromCadRotation(cad.rotation);
|
|
929
1022
|
}
|
|
930
|
-
const defaultTransform = coordinateTransform ?? (model_glb_url ? void 0 : COORDINATE_TRANSFORMS.Z_UP_TO_Y_UP_USB_FIX);
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
} catch (error) {
|
|
940
|
-
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);
|
|
941
1032
|
}
|
|
942
1033
|
if (!box.mesh) {
|
|
943
1034
|
box.color = componentColor;
|