feat: wall thickness

This commit is contained in:
mozzie 2023-09-06 13:53:53 +08:00
parent ea5983776f
commit 075323164d
7 changed files with 297 additions and 4 deletions

View File

@ -20,7 +20,8 @@
"mitt": "3.0.1", "mitt": "3.0.1",
"@tavi/i18n": "^1.5.0", "@tavi/i18n": "^1.5.0",
"@tavi/util": "1.0.0", "@tavi/util": "1.0.0",
"js-cookie": "3.0.5" "js-cookie": "3.0.5",
"three": "0.156.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.21.8", "@babel/core": "^7.21.8",
@ -62,6 +63,7 @@
"webpack-dev-server": "^4.13.3", "webpack-dev-server": "^4.13.3",
"webpack-merge": "^5.8.0", "webpack-merge": "^5.8.0",
"webpackbar": "^5.0.2", "webpackbar": "^5.0.2",
"@types/js-cookie": "3.0.3" "@types/js-cookie": "3.0.3",
"@types/three": "0.155.1"
} }
} }

BIN
apps/aorta/public/1.stl Normal file

Binary file not shown.

View File

@ -0,0 +1,156 @@
import React, { useEffect, useRef } from "react";
import * as THREE from "three";
import { STLLoader } from "three/examples/jsm/loaders/STLLoader";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { autoScale, getTriangleVertexs } from "./util";
export const STLViewer: React.FC = () => {
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (containerRef.current) {
const width = containerRef.current.clientWidth;
const height = containerRef.current.clientHeight;
// 创建场景、相机和渲染器
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
camera.position.z = 5;
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(width, height);
containerRef.current.appendChild(renderer.domElement);
// 加载STL文件
const loader = new STLLoader();
loader.load("/1.stl", (geometry) => {
const s = autoScale(geometry);
// geometry.center();
// const material = new THREE.MeshPhongMaterial({
// color: "lightgrey", // 使用更亮的颜色
// shininess: 100, // 增加亮度
// specular: 0x222222, // 调整镜面高光的颜色
// // side: THREE.DoubleSide,
// // wireframe: true,
// });
// const mesh = new THREE.Mesh(geometry, material);
// mesh.scale.set(scale, scale, scale);
// scene.add(mesh);
// 输出三角形顶点信息
const triangles = getTriangleVertexs(geometry);
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
const newTriangles = [];
triangles.forEach((triangleVertices) => {
const A = triangleVertices[0];
const B = triangleVertices[1];
const C = triangleVertices[2];
// 计算三角形的法线
let normal = new THREE.Vector3();
let cb = new THREE.Vector3();
let ab = new THREE.Vector3();
cb.subVectors(C, B);
ab.subVectors(A, B);
cb.cross(ab).normalize();
normal.copy(cb);
// 创建新的顶点它们沿着法线方向内移2个单位
let A1 = new THREE.Vector3()
.copy(A)
.add(normal.clone().multiplyScalar(-2));
let B1 = new THREE.Vector3()
.copy(B)
.add(normal.clone().multiplyScalar(-2));
let C1 = new THREE.Vector3()
.copy(C)
.add(normal.clone().multiplyScalar(-2));
// 添加新的三角形
newTriangles.push([A1, B1, C1]);
// 创建封闭表面的新面片
newTriangles.push([A, B, A1]);
newTriangles.push([B, B1, A1]);
newTriangles.push([B, C, B1]);
newTriangles.push([C, C1, B1]);
newTriangles.push([C, A, C1]);
newTriangles.push([A, A1, C1]);
});
const g = new THREE.BufferGeometry();
// 将newTriangles数据分解为顶点和索引数组
const vertices = [];
const indices = [];
newTriangles.forEach((triangle, index) => {
triangle.forEach((vertex) => {
vertices.push(vertex.x, vertex.y, vertex.z);
});
indices.push(index * 3, index * 3 + 1, index * 3 + 2);
});
// 设置vertices和indices到geometry
g.setAttribute(
"position",
new THREE.Float32BufferAttribute(vertices, 3)
);
g.setIndex(indices);
// 为了确保光照和阴影正确,我们需要计算几何体的面的法线
g.computeVertexNormals();
g.center();
// 创建网格使用MeshPhongMaterial或其他的材料
const material = new THREE.MeshPhongMaterial({
color: 0x00ff00,
side: THREE.DoubleSide,
});
const mesh = new THREE.Mesh(g, material);
mesh.scale.set(s, s, s);
// 添加网格到场景
scene.add(mesh);
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
});
// 添加环境光源
const ambientLight = new THREE.AmbientLight(0xffffff, 1); // 第二个参数为强度
scene.add(ambientLight);
// 添加方向光源
const directionalLight = new THREE.DirectionalLight(0xffffff, 2);
directionalLight.position.set(1, 1, 1).normalize();
scene.add(directionalLight);
// 添加XYZ轴
const axesHelper = new THREE.AxesHelper(2);
scene.add(axesHelper);
// 添加OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 渲染函数
const animate = () => {
requestAnimationFrame(animate);
renderer.render(scene, camera);
};
animate();
}
}, []);
return <div ref={containerRef} style={{ width: "100vw", height: "100vh" }} />;
};

View File

@ -1,7 +1,13 @@
import { STLViewer } from "./STLViewer";
interface RootViewerProps { interface RootViewerProps {
children?: JSX.Element; children?: JSX.Element;
} }
export const RootViewer = (props: RootViewerProps) => { export const RootViewer = (props: RootViewerProps) => {
return <div>RootViewer</div>; return (
<div>
<STLViewer />
</div>
);
}; };

View File

@ -0,0 +1,68 @@
import * as THREE from "three";
export const autoScale = (geometry: THREE.BufferGeometry) => {
// 计算模型的边界
geometry.computeBoundingBox();
// 获取模型的大小
const modelSize = geometry.boundingBox!.getSize(new THREE.Vector3());
const maxSize = Math.max(modelSize.x, modelSize.y, modelSize.z);
// 根据模型的大小设置一个缩放因子
const scaleFactor = 2 / maxSize; // 假设我们希望模型最大为2单位
return scaleFactor;
};
export const getTriangleVertexs = (geometry: THREE.BufferGeometry) => {
console.time("计算三角面面片顶点");
const positions = geometry.attributes.position;
const triangles = [];
if (geometry.index) {
const indices = geometry.index.array;
for (let i = 0; i < indices.length; i += 3) {
const triangleVertices = [
new THREE.Vector3(
positions.getX(indices[i]),
positions.getY(indices[i]),
positions.getZ(indices[i])
),
new THREE.Vector3(
positions.getX(indices[i + 1]),
positions.getY(indices[i + 1]),
positions.getZ(indices[i + 1])
),
new THREE.Vector3(
positions.getX(indices[i + 2]),
positions.getY(indices[i + 2]),
positions.getZ(indices[i + 2])
),
];
triangles.push(triangleVertices);
}
} else {
for (let i = 0; i < positions.count; i += 3) {
const triangleVertices = [
new THREE.Vector3(
positions.getX(i),
positions.getY(i),
positions.getZ(i)
),
new THREE.Vector3(
positions.getX(i + 1),
positions.getY(i + 1),
positions.getZ(i + 1)
),
new THREE.Vector3(
positions.getX(i + 2),
positions.getY(i + 2),
positions.getZ(i + 2)
),
];
triangles.push(triangleVertices);
}
}
console.timeEnd("计算三角面面片顶点");
return triangles;
};

View File

@ -1,4 +1,4 @@
import { Body, Controller, Get, Inject, Param, Post, Res } from '@nestjs/common'; import { Body, Controller, Post, Inject, Res } from '@nestjs/common';
import { ClientProxy } from '@nestjs/microservices'; import { ClientProxy } from '@nestjs/microservices';
import { Response } from 'express'; import { Response } from 'express';
import { firstValueFrom } from 'rxjs'; import { firstValueFrom } from 'rxjs';

View File

@ -49,6 +49,9 @@ importers:
react-router-dom: react-router-dom:
specifier: 6.14.1 specifier: 6.14.1
version: 6.14.1(react-dom@18.2.0)(react@18.2.0) version: 6.14.1(react-dom@18.2.0)(react@18.2.0)
three:
specifier: 0.156.1
version: registry.npmmirror.com/three@0.156.1
devDependencies: devDependencies:
'@babel/core': '@babel/core':
specifier: ^7.21.8 specifier: ^7.21.8
@ -86,6 +89,9 @@ importers:
'@types/react-router-dom': '@types/react-router-dom':
specifier: 5.3.3 specifier: 5.3.3
version: 5.3.3 version: 5.3.3
'@types/three':
specifier: 0.155.1
version: registry.npmmirror.com/@types/three@0.155.1
babel-loader: babel-loader:
specifier: ^9.1.2 specifier: ^9.1.2
version: 9.1.2(@babel/core@7.21.8)(webpack@5.75.0) version: 9.1.2(@babel/core@7.21.8)(webpack@5.75.0)
@ -12727,6 +12733,12 @@ packages:
name: '@tsconfig/node16' name: '@tsconfig/node16'
version: 1.0.4 version: 1.0.4
registry.npmmirror.com/@tweenjs/tween.js@18.6.4:
resolution: {integrity: sha512-lB9lMjuqjtuJrx7/kOkqQBtllspPIN+96OvTCeJ2j5FEzinoAXTdAMFnDAQT1KVPRlnYfBrqxtqP66vDM40xxQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@tweenjs/tween.js/-/tween.js-18.6.4.tgz}
name: '@tweenjs/tween.js'
version: 18.6.4
dev: true
registry.npmmirror.com/@types/body-parser@1.19.2: registry.npmmirror.com/@types/body-parser@1.19.2:
resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/body-parser/-/body-parser-1.19.2.tgz} resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/body-parser/-/body-parser-1.19.2.tgz}
name: '@types/body-parser' name: '@types/body-parser'
@ -12882,6 +12894,31 @@ packages:
'@types/node': registry.npmmirror.com/@types/node@20.3.3 '@types/node': registry.npmmirror.com/@types/node@20.3.3
dev: true dev: true
registry.npmmirror.com/@types/stats.js@0.17.0:
resolution: {integrity: sha512-9w+a7bR8PeB0dCT/HBULU2fMqf6BAzvKbxFboYhmDtDkKPiyXYbjoe2auwsXlEFI7CFNMF1dCv3dFH5Poy9R1w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/stats.js/-/stats.js-0.17.0.tgz}
name: '@types/stats.js'
version: 0.17.0
dev: true
registry.npmmirror.com/@types/three@0.155.1:
resolution: {integrity: sha512-uNUwnz/wWRxahjIqTtDYQ1qdE1R1py21obxfuILkT+kKrjocMwRLQQA1l8nMxfQU7VXb7CXu04ucMo8OqZt4ZA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/three/-/three-0.155.1.tgz}
name: '@types/three'
version: 0.155.1
dependencies:
'@tweenjs/tween.js': registry.npmmirror.com/@tweenjs/tween.js@18.6.4
'@types/stats.js': registry.npmmirror.com/@types/stats.js@0.17.0
'@types/webxr': registry.npmmirror.com/@types/webxr@0.5.4
fflate: registry.npmmirror.com/fflate@0.6.10
lil-gui: registry.npmmirror.com/lil-gui@0.17.0
meshoptimizer: registry.npmmirror.com/meshoptimizer@0.18.1
dev: true
registry.npmmirror.com/@types/webxr@0.5.4:
resolution: {integrity: sha512-41gfGLTtqXZhcmoDlLDHqMJDuwAMwhHwXf9Q2job3TUBsvkNfPNI/3IWVEtLH4tyY1ElWtfwIaoNeqeEX238/Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/webxr/-/webxr-0.5.4.tgz}
name: '@types/webxr'
version: 0.5.4
dev: true
registry.npmmirror.com/@typescript-eslint/eslint-plugin@5.61.0(@typescript-eslint/parser@5.61.0)(eslint@8.44.0)(typescript@5.1.3): registry.npmmirror.com/@typescript-eslint/eslint-plugin@5.61.0(@typescript-eslint/parser@5.61.0)(eslint@8.44.0)(typescript@5.1.3):
resolution: {integrity: sha512-A5l/eUAug103qtkwccSCxn8ZRwT+7RXWkFECdA4Cvl1dOlDUgTpAOfSEElZn2uSUxhdDpnCdetrf0jvU4qrL+g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.61.0.tgz} resolution: {integrity: sha512-A5l/eUAug103qtkwccSCxn8ZRwT+7RXWkFECdA4Cvl1dOlDUgTpAOfSEElZn2uSUxhdDpnCdetrf0jvU4qrL+g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.61.0.tgz}
id: registry.npmmirror.com/@typescript-eslint/eslint-plugin/5.61.0 id: registry.npmmirror.com/@typescript-eslint/eslint-plugin/5.61.0
@ -14866,6 +14903,12 @@ packages:
reusify: 1.0.4 reusify: 1.0.4
dev: true dev: true
registry.npmmirror.com/fflate@0.6.10:
resolution: {integrity: sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/fflate/-/fflate-0.6.10.tgz}
name: fflate
version: 0.6.10
dev: true
registry.npmmirror.com/figures@3.2.0: registry.npmmirror.com/figures@3.2.0:
resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/figures/-/figures-3.2.0.tgz} resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/figures/-/figures-3.2.0.tgz}
name: figures name: figures
@ -15693,6 +15736,12 @@ packages:
type-check: registry.npmmirror.com/type-check@0.4.0 type-check: registry.npmmirror.com/type-check@0.4.0
dev: true dev: true
registry.npmmirror.com/lil-gui@0.17.0:
resolution: {integrity: sha512-MVBHmgY+uEbmJNApAaPbtvNh1RCAeMnKym82SBjtp5rODTYKWtM+MXHCifLe2H2Ti1HuBGBtK/5SyG4ShQ3pUQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/lil-gui/-/lil-gui-0.17.0.tgz}
name: lil-gui
version: 0.17.0
dev: true
registry.npmmirror.com/loader-runner@4.3.0: registry.npmmirror.com/loader-runner@4.3.0:
resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/loader-runner/-/loader-runner-4.3.0.tgz} resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/loader-runner/-/loader-runner-4.3.0.tgz}
name: loader-runner name: loader-runner
@ -15877,6 +15926,12 @@ packages:
engines: {node: '>= 8'} engines: {node: '>= 8'}
dev: true dev: true
registry.npmmirror.com/meshoptimizer@0.18.1:
resolution: {integrity: sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/meshoptimizer/-/meshoptimizer-0.18.1.tgz}
name: meshoptimizer
version: 0.18.1
dev: true
registry.npmmirror.com/methods@1.1.2: registry.npmmirror.com/methods@1.1.2:
resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/methods/-/methods-1.1.2.tgz} resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/methods/-/methods-1.1.2.tgz}
name: methods name: methods
@ -17358,6 +17413,12 @@ packages:
any-promise: registry.npmmirror.com/any-promise@1.3.0 any-promise: registry.npmmirror.com/any-promise@1.3.0
dev: false dev: false
registry.npmmirror.com/three@0.156.1:
resolution: {integrity: sha512-kP7H0FK9d/k6t/XvQ9FO6i+QrePoDcNhwl0I02+wmUJRNSLCUIDMcfObnzQvxb37/0Uc9TDT0T1HgsRRrO6SYQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/three/-/three-0.156.1.tgz}
name: three
version: 0.156.1
dev: false
registry.npmmirror.com/through@2.3.8: registry.npmmirror.com/through@2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/through/-/through-2.3.8.tgz} resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/through/-/through-2.3.8.tgz}
name: through name: through