mirror of
https://github.com/wassname/optuna-dashboard.git
synced 2026-10-04 12:50:44 +08:00
Add rhino3dm support
This commit is contained in:
1 parent
cb212970d1
commit
1fe1f6bb09
2 files changed
+70
-25
No files matched your search
@@ -3,6 +3,7 @@ import React, { useState } from "react"
|
||||
import { Canvas } from "@react-three/fiber"
|
||||
import { GizmoHelper, GizmoViewport, OrbitControls } from "@react-three/drei"
|
||||
import { STLLoader } from "three/examples/jsm/loaders/STLLoader"
|
||||
import { Rhino3dmLoader } from "three/examples/jsm/loaders/3DMLoader"
|
||||
import { PerspectiveCamera } from "three"
|
||||
|
||||
interface ModelViewerProps {
|
||||
@@ -10,6 +11,7 @@ interface ModelViewerProps {
|
||||
width: string
|
||||
height: string
|
||||
hasGizmo: boolean
|
||||
filetype: string | undefined
|
||||
}
|
||||
|
||||
function CustomGizmoHelper(): JSX.Element {
|
||||
@@ -24,23 +26,46 @@ function CustomGizmoHelper(): JSX.Element {
|
||||
}
|
||||
|
||||
export function ModelViewer(props: ModelViewerProps): JSX.Element {
|
||||
const [geometry, setGeometry] = useState<THREE.BufferGeometry>()
|
||||
const [geometry, setGeometry] = useState<THREE.BufferGeometry[]>([])
|
||||
const [modelSize, setModelSize] = useState<THREE.Vector3>()
|
||||
|
||||
React.useEffect(() => {
|
||||
const loader = new STLLoader()
|
||||
loader.load(props.src, (geometry: THREE.BufferGeometry) => {
|
||||
if (geometry) {
|
||||
setGeometry(geometry)
|
||||
geometry.computeBoundingBox()
|
||||
if (geometry.boundingBox === null) {
|
||||
setModelSize(new THREE.Vector3(10, 10, 10))
|
||||
} else {
|
||||
const size = geometry.boundingBox.getSize(new THREE.Vector3())
|
||||
setModelSize(size)
|
||||
if ("stl" === props.filetype) {
|
||||
const stlLoader = new STLLoader()
|
||||
stlLoader.load(props.src, (stlMesh: THREE.BufferGeometry) => {
|
||||
if (stlMesh) {
|
||||
setGeometry([stlMesh])
|
||||
stlMesh.computeBoundingBox()
|
||||
if (stlMesh.boundingBox === null) {
|
||||
setModelSize(new THREE.Vector3(10, 10, 10))
|
||||
} else {
|
||||
const size = stlMesh.boundingBox.getSize(new THREE.Vector3())
|
||||
setModelSize(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
} else if ("3dm" === props.filetype) {
|
||||
const loader = new Rhino3dmLoader()
|
||||
loader.setLibraryPath("https://cdn.jsdelivr.net/npm/rhino3dm@7.15.0/")
|
||||
loader.load(props.src, (object: THREE.Object3D) => {
|
||||
object.traverse(function (child) {
|
||||
// rotate to y-up
|
||||
child.rotateX(-Math.PI / 4)
|
||||
})
|
||||
const meshes = object.children as THREE.Mesh[]
|
||||
const rhinoMeshes = meshes.map((mesh) => mesh.geometry)
|
||||
if (rhinoMeshes.length > 0) {
|
||||
setGeometry(rhinoMeshes)
|
||||
rhinoMeshes[0].computeBoundingBox()
|
||||
if (rhinoMeshes[0].boundingBox === null) {
|
||||
setModelSize(new THREE.Vector3(10, 10, 10))
|
||||
} else {
|
||||
const size = rhinoMeshes[0].boundingBox.getSize(new THREE.Vector3())
|
||||
setModelSize(size)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
const cameraPosition = modelSize
|
||||
? [modelSize.x * 1.5, modelSize.y * 1.5, modelSize.z * 1.5]
|
||||
@@ -51,8 +76,8 @@ export function ModelViewer(props: ModelViewerProps): JSX.Element {
|
||||
: 45,
|
||||
aspect: window.innerWidth / window.innerHeight,
|
||||
near: 0.1,
|
||||
position: new THREE.Vector3(...cameraPosition),
|
||||
far: 1000,
|
||||
position: cameraPosition,
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -67,11 +92,12 @@ export function ModelViewer(props: ModelViewerProps): JSX.Element {
|
||||
/>
|
||||
{props.hasGizmo && <CustomGizmoHelper />}
|
||||
<axesHelper />
|
||||
{geometry && (
|
||||
<mesh geometry={geometry}>
|
||||
<meshNormalMaterial />
|
||||
</mesh>
|
||||
)}
|
||||
{geometry.length > 0 &&
|
||||
geometry.map((geo, index) => (
|
||||
<mesh key={index} geometry={geo}>
|
||||
<meshNormalMaterial />
|
||||
</mesh>
|
||||
))}
|
||||
</Canvas>
|
||||
)
|
||||
}
|
||||
@@ -330,7 +330,9 @@ const TrialArtifact: FC<{ trial: Trial }> = ({ trial }) => {
|
||||
const [openDeleteArtifactDialog, renderDeleteArtifactDialog] =
|
||||
useDeleteArtifactDialog()
|
||||
const [dragOver, setDragOver] = useState<boolean>(false)
|
||||
const [open3dModelViewer, setOpen3dModelViewer] = useState<boolean>(false)
|
||||
const [open3dModelViewer, setOpen3dModelViewer] = useState<{
|
||||
[key: string]: boolean
|
||||
}>({})
|
||||
|
||||
const width = "200px"
|
||||
const height = "150px"
|
||||
@@ -442,7 +444,10 @@ const TrialArtifact: FC<{ trial: Trial }> = ({ trial }) => {
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
} else if (a.filename.endsWith(".stl")) {
|
||||
} else if (
|
||||
a.filename.endsWith(".stl") ||
|
||||
a.filename.endsWith(".3dm")
|
||||
) {
|
||||
return (
|
||||
<Card
|
||||
key={a.artifact_id}
|
||||
@@ -468,6 +473,7 @@ const TrialArtifact: FC<{ trial: Trial }> = ({ trial }) => {
|
||||
width={width}
|
||||
height={height}
|
||||
hasGizmo={false}
|
||||
filetype={a.filename.split(".").pop()}
|
||||
/>
|
||||
</Box>
|
||||
<CardContent
|
||||
@@ -488,20 +494,32 @@ const TrialArtifact: FC<{ trial: Trial }> = ({ trial }) => {
|
||||
{a.filename}
|
||||
</Typography>
|
||||
<IconButton
|
||||
aria-label="shoe artifact 3d model"
|
||||
aria-label="show artifact 3d model"
|
||||
size="small"
|
||||
color="inherit"
|
||||
sx={{ margin: "auto 0" }}
|
||||
onClick={() => {
|
||||
setOpen3dModelViewer(true)
|
||||
setOpen3dModelViewer(() => {
|
||||
const obj = { ...open3dModelViewer }
|
||||
obj[a.filename] = true
|
||||
return obj
|
||||
})
|
||||
}}
|
||||
>
|
||||
<OpenWithIcon />
|
||||
</IconButton>
|
||||
<Modal
|
||||
open={open3dModelViewer}
|
||||
open={
|
||||
a.filename in open3dModelViewer
|
||||
? open3dModelViewer[a.filename]
|
||||
: false
|
||||
}
|
||||
onClose={() => {
|
||||
setOpen3dModelViewer(false)
|
||||
setOpen3dModelViewer(() => {
|
||||
const obj = { ...open3dModelViewer }
|
||||
obj[a.filename] = false
|
||||
return obj
|
||||
})
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
@@ -519,6 +537,7 @@ const TrialArtifact: FC<{ trial: Trial }> = ({ trial }) => {
|
||||
width={`${innerWidth * 0.8}px`}
|
||||
height={`${innerHeight * 0.8}px`}
|
||||
hasGizmo={true}
|
||||
filetype={a.filename.split(".").pop()}
|
||||
/>
|
||||
</Box>
|
||||
</Modal>
|
||||
|
||||
Reference in new issue
Block a user