feat: 算法输出文件measurement.json接口调试
This commit is contained in:
parent
db8ba53589
commit
595e144a13
|
@ -58,4 +58,10 @@ pnpm config set virtual-store-dir-max-length 70
|
||||||
- CORONAL(冠状位):
|
- CORONAL(冠状位):
|
||||||
• 方向:冠状面,与矢状面垂直,沿身体的左右方向。
|
• 方向:冠状面,与矢状面垂直,沿身体的左右方向。
|
||||||
• 描述:将身体分为前(腹侧)和后(背侧)部分。
|
• 描述:将身体分为前(腹侧)和后(背侧)部分。
|
||||||
• 应用:适用于查看从前面到后面的结构,如面部和脑部的解剖。
|
• 应用:适用于查看从前面到后面的结构,如面部和脑部的解剖。
|
||||||
|
|
||||||
|
## 区分片子的类型
|
||||||
|
|
||||||
|
头部扫描:通常总长度在100毫米以下。
|
||||||
|
主动脉根部平扫:总长度通常在100毫米到200毫米之间。
|
||||||
|
腹部扫描:总长度通常超过200毫米。
|
|
@ -179,3 +179,57 @@ export const downloadSeriesDicomFiles = async (
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export /**
|
||||||
|
* 根据SeriesInstanceUID获取总的扫描长度
|
||||||
|
* @param seriesInstanceUID 序列实例UID
|
||||||
|
* @returns 返回总的扫描长度(单位:毫米)
|
||||||
|
*/
|
||||||
|
const getTotalScanLength = async (
|
||||||
|
seriesInstanceUID: string
|
||||||
|
): Promise<number | null> => {
|
||||||
|
try {
|
||||||
|
// 1. 查找序列ID
|
||||||
|
const findResponse = await axios.post(`${OrthancServerRoot}/tools/find`, {
|
||||||
|
Level: "Series",
|
||||||
|
Query: {
|
||||||
|
SeriesInstanceUID: seriesInstanceUID,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const seriesIds = findResponse.data;
|
||||||
|
if (seriesIds.length === 0) {
|
||||||
|
console.error("Series not found");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const seriesId = seriesIds[0];
|
||||||
|
|
||||||
|
// 2. 获取共享标签,提取SliceThickness
|
||||||
|
const tagsResponse = await axios.get(
|
||||||
|
`${OrthancServerRoot}/series/${seriesId}/shared-tags`
|
||||||
|
);
|
||||||
|
const tags = tagsResponse.data;
|
||||||
|
const sliceThickness = parseFloat(tags["0018,0050"]["Value"]); // (0018,0050) SliceThickness
|
||||||
|
|
||||||
|
if (isNaN(sliceThickness)) {
|
||||||
|
console.error("SliceThickness not found");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 获取实例列表,计算层数
|
||||||
|
const instancesResponse = await axios.get(
|
||||||
|
`${OrthancServerRoot}/series/${seriesId}/instances`
|
||||||
|
);
|
||||||
|
const instances = instancesResponse.data;
|
||||||
|
const numberOfSlices = instances.length;
|
||||||
|
console.log("numberOfSlices", numberOfSlices);
|
||||||
|
console.log("sliceThickness", sliceThickness);
|
||||||
|
|
||||||
|
// 4. 计算总的扫描长度
|
||||||
|
const totalLength = Number(sliceThickness) * numberOfSlices;
|
||||||
|
return totalLength;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import { downloadSeriesDicomFiles } from "../../core/pacs";
|
import { downloadSeriesDicomFiles, getTotalScanLength } from "../../core/pacs";
|
||||||
import { executeInferTask } from "../../core/alg";
|
import { executeInferTask } from "../../core/alg";
|
||||||
import { InferDeviceEnum, InferStructuralEnum } from "../../core/alg.type";
|
import { InferDeviceEnum, InferStructuralEnum } from "../../core/alg.type";
|
||||||
import { db } from "../../core/db";
|
import { db } from "../../core/db";
|
||||||
import log from "electron-log";
|
import log from "electron-log";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { app, ipcMain } from "electron";
|
import { app, ipcMain } from "electron";
|
||||||
|
import { findSTLFiles } from "./util";
|
||||||
|
import { readFileSync } from "node:fs";
|
||||||
|
|
||||||
export const registerAlgHandler = () => {
|
export const registerAlgHandler = () => {
|
||||||
ipcMain.handle("device:infer:set", async (_event, inferDevice) => {
|
ipcMain.handle("device:infer:set", async (_event, inferDevice) => {
|
||||||
|
@ -21,11 +23,16 @@ export const registerAlgHandler = () => {
|
||||||
ipcMain.on("model:infer", async (event, SeriesInstanceUIDs) => {
|
ipcMain.on("model:infer", async (event, SeriesInstanceUIDs) => {
|
||||||
// 构造推理任务参数列表
|
// 构造推理任务参数列表
|
||||||
const pu = InferDeviceEnum.GPU;
|
const pu = InferDeviceEnum.GPU;
|
||||||
const module = InferStructuralEnum.AORTA;
|
|
||||||
const turbo = true;
|
const turbo = true;
|
||||||
const seg_schedule = true;
|
const seg_schedule = true;
|
||||||
for (let i = 0; i < SeriesInstanceUIDs.length; i++) {
|
for (let i = 0; i < SeriesInstanceUIDs.length; i++) {
|
||||||
const SeriesInstanceUID = SeriesInstanceUIDs[i];
|
const SeriesInstanceUID = SeriesInstanceUIDs[i];
|
||||||
|
const physicalLength = await getTotalScanLength(SeriesInstanceUID);
|
||||||
|
const module =
|
||||||
|
physicalLength && physicalLength < 200
|
||||||
|
? InferStructuralEnum.AORTA
|
||||||
|
: InferStructuralEnum.PERI;
|
||||||
// 下载dicom到本地,获取文件夹路径
|
// 下载dicom到本地,获取文件夹路径
|
||||||
const img_path = await downloadSeriesDicomFiles(SeriesInstanceUID);
|
const img_path = await downloadSeriesDicomFiles(SeriesInstanceUID);
|
||||||
const save_path = path.join(
|
const save_path = path.join(
|
||||||
|
@ -42,12 +49,38 @@ export const registerAlgHandler = () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle("alg:assets", (_event, SeriesInstanceUID) => {
|
ipcMain.handle("alg:assets", async (_event, SeriesInstanceUID) => {
|
||||||
const assetsPath = path.join(
|
const physicalLength = await getTotalScanLength(SeriesInstanceUID);
|
||||||
|
const module =
|
||||||
|
physicalLength && physicalLength < 200
|
||||||
|
? InferStructuralEnum.AORTA
|
||||||
|
: InferStructuralEnum.PERI;
|
||||||
|
const rootPath = path.join(
|
||||||
app.getPath("userData"),
|
app.getPath("userData"),
|
||||||
"output",
|
"output",
|
||||||
SeriesInstanceUID
|
SeriesInstanceUID
|
||||||
);
|
);
|
||||||
console.log("assetsPath", assetsPath);
|
const stlsPath = path.join(rootPath, module, "visualization");
|
||||||
|
const stls = await findSTLFiles(stlsPath);
|
||||||
|
if (stls.length > 0) {
|
||||||
|
try {
|
||||||
|
// 读取测量json
|
||||||
|
const measurementPath = path.join(rootPath, module, "measurement.json");
|
||||||
|
const measurementData = readFileSync(measurementPath, "utf-8");
|
||||||
|
// 读取 STL 文件并转换为 ArrayBuffer
|
||||||
|
const stlFiles = stls.map((file) => {
|
||||||
|
const filePath = path.join(stlsPath, file);
|
||||||
|
const data = readFileSync(filePath);
|
||||||
|
return {
|
||||||
|
fileName: file,
|
||||||
|
data: data.buffer,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return { stlFiles, measurement: JSON.parse(measurementData) };
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error reading measurement.json:", error);
|
||||||
|
throw new Error("Failed to read or parse measurement.json");
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
30
apps/desktop/electron/ipcEvent/alg/util.ts
Normal file
30
apps/desktop/electron/ipcEvent/alg/util.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取指定路径下的所有.stl文件
|
||||||
|
* @param dirPath 要搜索的目录路径
|
||||||
|
* @returns 返回包含所有.stl文件名的数组
|
||||||
|
*/
|
||||||
|
export const findSTLFiles = (dirPath: string): Promise<string[]> => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
// 检查路径是否存在
|
||||||
|
if (!fs.existsSync(dirPath)) {
|
||||||
|
resolve([]); // 路径不存在时返回空数组
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步读取目录内容
|
||||||
|
fs.readdir(dirPath, (err, files) => {
|
||||||
|
if (err) {
|
||||||
|
resolve([]); // 读取错误时返回空数组
|
||||||
|
} else {
|
||||||
|
// 过滤出.stl文件
|
||||||
|
const stlFiles = files.filter(
|
||||||
|
(file) => path.extname(file).toLowerCase() === ".stl"
|
||||||
|
);
|
||||||
|
resolve(stlFiles);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
|
@ -77,9 +77,11 @@
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"zod": "3.23.8",
|
"zod": "3.23.8",
|
||||||
"mitt": "3.0.1",
|
"mitt": "3.0.1",
|
||||||
"p-limit": "6.1.0"
|
"p-limit": "6.1.0",
|
||||||
|
"three": "0.164.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/three": "0.164.0",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
"@types/lodash": "4.17.7",
|
"@types/lodash": "4.17.7",
|
||||||
"@types/node": "22.5.2",
|
"@types/node": "22.5.2",
|
||||||
|
|
133
apps/desktop/src/pages/Viewer/ModelViewer/AortaViewer.tsx
Normal file
133
apps/desktop/src/pages/Viewer/ModelViewer/AortaViewer.tsx
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
import React, { useEffect, useRef } from "react";
|
||||||
|
import * as THREE from "three";
|
||||||
|
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
|
||||||
|
import { loadModels } from "./util";
|
||||||
|
|
||||||
|
interface AlgAssets {
|
||||||
|
stlFiles: { fileName: string; data: ArrayBuffer }[];
|
||||||
|
measurement: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AortaViewer: React.FC<{ SeriesInstanceUID: string }> = ({
|
||||||
|
SeriesInstanceUID,
|
||||||
|
}) => {
|
||||||
|
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||||
|
const rendererRef = useRef<THREE.WebGLRenderer>();
|
||||||
|
const sceneRef = useRef<THREE.Scene>(new THREE.Scene());
|
||||||
|
const cameraRef = useRef<THREE.PerspectiveCamera>();
|
||||||
|
const controlsRef = useRef<OrbitControls>();
|
||||||
|
const groupRef = useRef(new THREE.Group());
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (SeriesInstanceUID) {
|
||||||
|
window.ipcRenderer
|
||||||
|
.invoke("alg:assets", SeriesInstanceUID)
|
||||||
|
.then((assets: AlgAssets) => {
|
||||||
|
const { stlFiles, measurement } = assets;
|
||||||
|
console.log(measurement);
|
||||||
|
Promise.all(loadModels(stlFiles)).then((meshes) => {
|
||||||
|
meshes.forEach((mesh) => {
|
||||||
|
if (mesh) groupRef.current.add(mesh);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("Error invoking alg:assets:", error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [SeriesInstanceUID]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!SeriesInstanceUID || !canvasRef.current) return;
|
||||||
|
|
||||||
|
const { clientWidth, clientHeight } = canvasRef.current;
|
||||||
|
|
||||||
|
if (!rendererRef.current) {
|
||||||
|
// 初始化渲染器
|
||||||
|
initRenderer(clientWidth, clientHeight);
|
||||||
|
initCamera(clientWidth, clientHeight);
|
||||||
|
initControls();
|
||||||
|
initLights();
|
||||||
|
initScene();
|
||||||
|
|
||||||
|
// 开始渲染循环
|
||||||
|
startRenderLoop();
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleResize = () => {
|
||||||
|
if (rendererRef.current && cameraRef.current) {
|
||||||
|
const { clientWidth, clientHeight } = canvasRef.current!;
|
||||||
|
rendererRef.current.setSize(clientWidth, clientHeight);
|
||||||
|
cameraRef.current.aspect = clientWidth / clientHeight;
|
||||||
|
cameraRef.current.updateProjectionMatrix();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("resize", handleResize);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("resize", handleResize);
|
||||||
|
};
|
||||||
|
}, [SeriesInstanceUID]);
|
||||||
|
|
||||||
|
// 初始化渲染器
|
||||||
|
const initRenderer = (width: number, height: number) => {
|
||||||
|
const renderer = new THREE.WebGLRenderer({
|
||||||
|
antialias: true,
|
||||||
|
canvas: canvasRef.current!,
|
||||||
|
});
|
||||||
|
renderer.setSize(width, height);
|
||||||
|
renderer.setClearColor(0x000000);
|
||||||
|
rendererRef.current = renderer;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化相机
|
||||||
|
const initCamera = (width: number, height: number) => {
|
||||||
|
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 10000);
|
||||||
|
camera.position.set(5, 5, 5);
|
||||||
|
camera.lookAt(0, 0, 0);
|
||||||
|
cameraRef.current = camera;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化控制器
|
||||||
|
const initControls = () => {
|
||||||
|
const controls = new OrbitControls(cameraRef.current!, canvasRef.current!);
|
||||||
|
controls.target.set(0, 0, 0); // 初始目标点
|
||||||
|
controls.update();
|
||||||
|
controlsRef.current = controls;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化光源
|
||||||
|
const initLights = () => {
|
||||||
|
const ambientLight = new THREE.AmbientLight(0xffffff);
|
||||||
|
const directionalLight = new THREE.DirectionalLight(0xffffff);
|
||||||
|
directionalLight.position.set(1, 1, 1);
|
||||||
|
sceneRef.current.add(ambientLight, directionalLight);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化场景
|
||||||
|
const initScene = () => {
|
||||||
|
const axesHelper = new THREE.AxesHelper(1000);
|
||||||
|
sceneRef.current.add(axesHelper);
|
||||||
|
sceneRef.current.add(groupRef.current);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 开始渲染循环
|
||||||
|
const startRenderLoop = () => {
|
||||||
|
const animate = () => {
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
controlsRef.current?.update();
|
||||||
|
rendererRef.current!.render(sceneRef.current!, cameraRef.current!);
|
||||||
|
};
|
||||||
|
animate();
|
||||||
|
};
|
||||||
|
|
||||||
|
return <canvas ref={canvasRef} style={{ width: "100%", height: "100%" }} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
// if (canvasRef.current && cameraRef.current && rendererRef.current) {
|
||||||
|
// const { clientWidth, clientHeight } = canvasRef.current;
|
||||||
|
// cameraRef.current.aspect = clientWidth / clientHeight;
|
||||||
|
// rendererRef.current.setPixelRatio(window.devicePixelRatio * 2);
|
||||||
|
// cameraRef.current.updateProjectionMatrix();
|
||||||
|
// }
|
|
@ -1,5 +1,6 @@
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useLocation } from "react-router-dom";
|
import { useLocation } from "react-router-dom";
|
||||||
|
import { AortaViewer } from "./AortaViewer";
|
||||||
|
|
||||||
interface Model3DViewerProps {}
|
interface Model3DViewerProps {}
|
||||||
|
|
||||||
|
@ -9,13 +10,11 @@ export const Model3DViewer = (props: Model3DViewerProps) => {
|
||||||
const queryParams = new URLSearchParams(location.search);
|
const queryParams = new URLSearchParams(location.search);
|
||||||
const SeriesInstanceUID = queryParams.get("SeriesInstanceUID");
|
const SeriesInstanceUID = queryParams.get("SeriesInstanceUID");
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
window.ipcRenderer.invoke("alg:assets", SeriesInstanceUID).then((res) => {
|
|
||||||
console.log(res);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>{SeriesInstanceUID ? <div>3d</div> : <div>启动AI分析该数据</div>}</div>
|
<div className="w-full h-full">
|
||||||
|
{SeriesInstanceUID && (
|
||||||
|
<AortaViewer SeriesInstanceUID={SeriesInstanceUID} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
55
apps/desktop/src/pages/Viewer/ModelViewer/util.tsx
Normal file
55
apps/desktop/src/pages/Viewer/ModelViewer/util.tsx
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import { STLLoader } from "three/examples/jsm/loaders/STLLoader";
|
||||||
|
import * as THREE from "three";
|
||||||
|
|
||||||
|
const createObjectURLFromData = (data: ArrayBuffer): string => {
|
||||||
|
const blob = new Blob([data], { type: "application/octet-stream" });
|
||||||
|
return URL.createObjectURL(blob);
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadSTLFile = (
|
||||||
|
stlLoader: STLLoader,
|
||||||
|
url: string,
|
||||||
|
fileName: string
|
||||||
|
): Promise<THREE.Mesh> => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
stlLoader.load(
|
||||||
|
url,
|
||||||
|
(geometry: THREE.BufferGeometry) => {
|
||||||
|
const material = new THREE.MeshLambertMaterial({
|
||||||
|
// color: "red",
|
||||||
|
transparent: true,
|
||||||
|
side: THREE.DoubleSide,
|
||||||
|
});
|
||||||
|
const mesh = new THREE.Mesh(geometry, material);
|
||||||
|
mesh.name = fileName;
|
||||||
|
resolve(mesh);
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
(error: Error) => {
|
||||||
|
console.error(`Error loading STL file ${fileName}:`, error);
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const loadModels = (
|
||||||
|
stlFiles: { fileName: string; data: ArrayBuffer }[]
|
||||||
|
) => {
|
||||||
|
const loadingManager = new THREE.LoadingManager();
|
||||||
|
const stlLoader = new STLLoader(loadingManager);
|
||||||
|
|
||||||
|
const loadPromises = stlFiles.map((file) => {
|
||||||
|
const { fileName, data } = file;
|
||||||
|
const url = createObjectURLFromData(data);
|
||||||
|
try {
|
||||||
|
return loadSTLFile(stlLoader, url, fileName);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to load STL file: ${fileName}`, error);
|
||||||
|
} finally {
|
||||||
|
URL.revokeObjectURL(url); // Clean up URL after use
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return loadPromises;
|
||||||
|
};
|
|
@ -208,6 +208,9 @@ importers:
|
||||||
tailwindcss-animate:
|
tailwindcss-animate:
|
||||||
specifier: ^1.0.7
|
specifier: ^1.0.7
|
||||||
version: 1.0.7(tailwindcss@3.4.10)
|
version: 1.0.7(tailwindcss@3.4.10)
|
||||||
|
three:
|
||||||
|
specifier: 0.164.1
|
||||||
|
version: 0.164.1
|
||||||
zod:
|
zod:
|
||||||
specifier: 3.23.8
|
specifier: 3.23.8
|
||||||
version: 3.23.8
|
version: 3.23.8
|
||||||
|
@ -227,6 +230,9 @@ importers:
|
||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
specifier: ^18.2.21
|
specifier: ^18.2.21
|
||||||
version: 18.3.0
|
version: 18.3.0
|
||||||
|
'@types/three':
|
||||||
|
specifier: 0.164.0
|
||||||
|
version: 0.164.0
|
||||||
'@typescript-eslint/eslint-plugin':
|
'@typescript-eslint/eslint-plugin':
|
||||||
specifier: ^7.1.1
|
specifier: ^7.1.1
|
||||||
version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4)
|
version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4)
|
||||||
|
@ -2125,6 +2131,9 @@ packages:
|
||||||
resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==}
|
resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
|
|
||||||
|
'@tweenjs/tween.js@23.1.3':
|
||||||
|
resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==}
|
||||||
|
|
||||||
'@types/babel__core@7.20.5':
|
'@types/babel__core@7.20.5':
|
||||||
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
|
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
|
||||||
|
|
||||||
|
@ -2222,6 +2231,12 @@ packages:
|
||||||
'@types/stack-trace@0.0.33':
|
'@types/stack-trace@0.0.33':
|
||||||
resolution: {integrity: sha512-O7in6531Bbvlb2KEsJ0dq0CHZvc3iWSR5ZYMtvGgnHA56VgriAN/AU2LorfmcvAl2xc9N5fbCTRyMRRl8nd74g==}
|
resolution: {integrity: sha512-O7in6531Bbvlb2KEsJ0dq0CHZvc3iWSR5ZYMtvGgnHA56VgriAN/AU2LorfmcvAl2xc9N5fbCTRyMRRl8nd74g==}
|
||||||
|
|
||||||
|
'@types/stats.js@0.17.3':
|
||||||
|
resolution: {integrity: sha512-pXNfAD3KHOdif9EQXZ9deK82HVNaXP5ZIF5RP2QG6OQFNTaY2YIetfrE9t528vEreGQvEPRDDc8muaoYeK0SxQ==}
|
||||||
|
|
||||||
|
'@types/three@0.164.0':
|
||||||
|
resolution: {integrity: sha512-SFDofn9dJVrE+1DKta7xj7lc4ru7B3S3yf10NsxOserW57aQlB6GxtAS1UK5To3LfEMN5HUHMu3n5v+M5rApgA==}
|
||||||
|
|
||||||
'@types/tough-cookie@4.0.5':
|
'@types/tough-cookie@4.0.5':
|
||||||
resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==}
|
resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==}
|
||||||
|
|
||||||
|
@ -3315,6 +3330,9 @@ packages:
|
||||||
fflate@0.7.3:
|
fflate@0.7.3:
|
||||||
resolution: {integrity: sha512-0Zz1jOzJWERhyhsimS54VTqOteCNwRtIlh8isdL0AXLo0g7xNTfTL7oWrkmCnPhZGocKIkWHBistBrrpoNH3aw==}
|
resolution: {integrity: sha512-0Zz1jOzJWERhyhsimS54VTqOteCNwRtIlh8isdL0AXLo0g7xNTfTL7oWrkmCnPhZGocKIkWHBistBrrpoNH3aw==}
|
||||||
|
|
||||||
|
fflate@0.8.2:
|
||||||
|
resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
|
||||||
|
|
||||||
file-entry-cache@6.0.1:
|
file-entry-cache@6.0.1:
|
||||||
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
|
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
|
||||||
engines: {node: ^10.12.0 || >=12.0.0}
|
engines: {node: ^10.12.0 || >=12.0.0}
|
||||||
|
@ -3967,6 +3985,9 @@ packages:
|
||||||
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
|
||||||
|
meshoptimizer@0.18.1:
|
||||||
|
resolution: {integrity: sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==}
|
||||||
|
|
||||||
micromatch@4.0.8:
|
micromatch@4.0.8:
|
||||||
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
||||||
engines: {node: '>=8.6'}
|
engines: {node: '>=8.6'}
|
||||||
|
@ -5197,6 +5218,9 @@ packages:
|
||||||
thenify@3.3.1:
|
thenify@3.3.1:
|
||||||
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
|
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
|
||||||
|
|
||||||
|
three@0.164.1:
|
||||||
|
resolution: {integrity: sha512-iC/hUBbl1vzFny7f5GtqzVXYjMJKaTPxiCxXfrvVdBi1Sf+jhd1CAkitiFwC7mIBFCo3MrDLJG97yisoaWig0w==}
|
||||||
|
|
||||||
throttle-debounce@5.0.2:
|
throttle-debounce@5.0.2:
|
||||||
resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==}
|
resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==}
|
||||||
engines: {node: '>=12.22'}
|
engines: {node: '>=12.22'}
|
||||||
|
@ -7594,6 +7618,8 @@ snapshots:
|
||||||
|
|
||||||
'@tootallnate/once@2.0.0': {}
|
'@tootallnate/once@2.0.0': {}
|
||||||
|
|
||||||
|
'@tweenjs/tween.js@23.1.3': {}
|
||||||
|
|
||||||
'@types/babel__core@7.20.5':
|
'@types/babel__core@7.20.5':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/parser': 7.25.4
|
'@babel/parser': 7.25.4
|
||||||
|
@ -7713,6 +7739,16 @@ snapshots:
|
||||||
|
|
||||||
'@types/stack-trace@0.0.33': {}
|
'@types/stack-trace@0.0.33': {}
|
||||||
|
|
||||||
|
'@types/stats.js@0.17.3': {}
|
||||||
|
|
||||||
|
'@types/three@0.164.0':
|
||||||
|
dependencies:
|
||||||
|
'@tweenjs/tween.js': 23.1.3
|
||||||
|
'@types/stats.js': 0.17.3
|
||||||
|
'@types/webxr': 0.5.20
|
||||||
|
fflate: 0.8.2
|
||||||
|
meshoptimizer: 0.18.1
|
||||||
|
|
||||||
'@types/tough-cookie@4.0.5': {}
|
'@types/tough-cookie@4.0.5': {}
|
||||||
|
|
||||||
'@types/verror@1.10.10':
|
'@types/verror@1.10.10':
|
||||||
|
@ -9131,6 +9167,8 @@ snapshots:
|
||||||
|
|
||||||
fflate@0.7.3: {}
|
fflate@0.7.3: {}
|
||||||
|
|
||||||
|
fflate@0.8.2: {}
|
||||||
|
|
||||||
file-entry-cache@6.0.1:
|
file-entry-cache@6.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
flat-cache: 3.2.0
|
flat-cache: 3.2.0
|
||||||
|
@ -9802,6 +9840,8 @@ snapshots:
|
||||||
|
|
||||||
merge2@1.4.1: {}
|
merge2@1.4.1: {}
|
||||||
|
|
||||||
|
meshoptimizer@0.18.1: {}
|
||||||
|
|
||||||
micromatch@4.0.8:
|
micromatch@4.0.8:
|
||||||
dependencies:
|
dependencies:
|
||||||
braces: 3.0.3
|
braces: 3.0.3
|
||||||
|
@ -11260,6 +11300,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
any-promise: 1.3.0
|
any-promise: 1.3.0
|
||||||
|
|
||||||
|
three@0.164.1: {}
|
||||||
|
|
||||||
throttle-debounce@5.0.2: {}
|
throttle-debounce@5.0.2: {}
|
||||||
|
|
||||||
through2@2.0.5:
|
through2@2.0.5:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user