개요
vtk.js와 bootstrap을 이용하여, 간단히 볼륨템플릿을 바꿔보는 프로그램을 만들어 보겠습니다.
먼저, 준비사항으로써 본 프로젝트에서 사용할 첨부 파일을 public/leonhong.vti 에 놓습니다.
ps. pptx 파일은 개인정보가 있어서 첨부하지 않으므로 임으로 pptx 파일을 만들어서 사용하세요.
소스코드는 아래와 같습니다.
import pptFile from './leonhong.pptx'
import 'bootstrap/dist/css/bootstrap.min.css';
import { Container, Button, Row, Col, Card, Navbar } from 'react-bootstrap';
import { useRef, useEffect } from 'react';
// Load the rendering pieces we want to use (for both WebGL and WebGPU)
import '@kitware/vtk.js/Rendering/Profiles/Volume';
// Force DataAccessHelper to have access to various data source
import '@kitware/vtk.js/IO/Core/DataAccessHelper/HtmlDataAccessHelper';
import '@kitware/vtk.js/IO/Core/DataAccessHelper/HttpDataAccessHelper';
import '@kitware/vtk.js/IO/Core/DataAccessHelper/JSZipDataAccessHelper';
import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction';
import vtkPiecewiseFunction from '@kitware/vtk.js/Common/DataModel/PiecewiseFunction';
import vtkVolume from '@kitware/vtk.js/Rendering/Core/Volume';
import vtkVolumeMapper from '@kitware/vtk.js/Rendering/Core/VolumeMapper';
import vtkRenderer from '@kitware/vtk.js/Rendering/Core/Renderer';
import vtkRenderWindow from '@kitware/vtk.js/Rendering/Core/RenderWindow';
import vtkOpenGLRenderWindow from '@kitware/vtk.js/Rendering/OpenGL/RenderWindow';
import xmlImageDataReader from '@kitware/vtk.js/IO/XML/XMLImageDataReader';
import vtkRenderWindowInteractor from '@kitware/vtk.js/Rendering/Core/RenderWindowInteractor';
import vtkInteractorStyleTrackballCamera from '@kitware/vtk.js/Interaction/Style/InteractorStyleTrackballCamera';
import robot from './logo.svg'
function App() {
const vtkContainerRef = useRef(null);
const context = useRef(null);
useEffect(() => {
if (!context.current) {
const actor = vtkVolume.newInstance();
const mapper = vtkVolumeMapper.newInstance();
const renderWindow = vtkRenderWindow.newInstance();
const renderer = vtkRenderer.newInstance({ background: [0.0, 0.0, 0.0] });
const openglRenderWindow = vtkOpenGLRenderWindow.newInstance();
const interactor = vtkRenderWindowInteractor.newInstance();
const interStyle = vtkInteractorStyleTrackballCamera.newInstance();
const xmlReader = xmlImageDataReader.newInstance();
const ctfun = vtkColorTransferFunction.newInstance();
const ofun = vtkPiecewiseFunction.newInstance();
renderWindow.addRenderer(renderer);
renderWindow.addView(openglRenderWindow);
vtkContainerRef.current.style.height = '380px';
openglRenderWindow.setContainer(vtkContainerRef.current);
openglRenderWindow.setSize(300, 300);
interactor.setView(openglRenderWindow);
interactor.initialize();
interactor.bindEvents(vtkContainerRef.current);
interactor.setInteractorStyle(interStyle);
mapper.setSampleDistance(0.7);
actor.setMapper(mapper);
// create color and opacity transfer functions
ctfun.removeAllPoints();
ctfun.addRGBPoint(-3000.0, 0.0, 0.0, 0.0);
ctfun.addRGBPoint(-1024.0, 0.0, 0.0, 0.0);
ctfun.addRGBPoint(1016,120,0,0);
ctfun.addRGBPoint(1603,1,0.56,0.34);
ctfun.addRGBPoint(2776,1,1,1);
ctfun.addRGBPoint(5297,1,1,1);
ofun.removeAllPoints();
ofun.addPoint(-1024,0.000000);
ofun.addPoint(674,0.000000);
ofun.addPoint(1372,0.053731);
ofun.addPoint(3023,0.180000);
ofun.addPoint(5274,0.343284);
actor.getProperty().setRGBTransferFunction(0, ctfun);
actor.getProperty().setScalarOpacity(0, ofun);
//actor.getProperty().setScalarOpacityUnitDistance(0, 4.5);
actor.getProperty().setScalarOpacityUnitDistance(0, 1.5);
actor.getProperty().setInterpolationTypeToLinear();
actor.getProperty().setUseGradientOpacity(0, true);
actor.getProperty().setGradientOpacityMinimumValue(0, 15);
actor.getProperty().setGradientOpacityMinimumOpacity(0, 0.0);
actor.getProperty().setGradientOpacityMaximumValue(0, 100);
actor.getProperty().setGradientOpacityMaximumOpacity(0, 1.0);
actor.getProperty().setShade(false);
actor.getProperty().setAmbient(0.2);
actor.getProperty().setDiffuse(0.7);
actor.getProperty().setSpecular(0.3);
actor.getProperty().setSpecularPower(8.0);
mapper.setInputConnection(xmlReader.getOutputPort());
xmlReader.setUrl(process.env.PUBLIC_URL+'/leonhong.vti').then(() => {
xmlReader.loadData().then(() =>{
renderer.addVolume(actor);
renderer.resetCamera();
renderer.getActiveCamera().zoom(1.5);
renderer.getActiveCamera().elevation(-90);
renderer.updateLightsGeometryToFollowCamera();
renderWindow.render();
});
});
context.current = {
actor,
mapper,
renderWindow,
renderer,
openglRenderWindow,
interactor,
interStyle,
xmlReader,
ctfun,
ofun
};
}
}, [vtkContainerRef]);
function onClickBone(){
const ctfun = context.current.ctfun;
ctfun.removeAllPoints();
ctfun.addRGBPoint(-3000.0, 0.0, 0.0, 0.0);
ctfun.addRGBPoint(-1024.0, 0.0, 0.0, 0.0);
ctfun.addRGBPoint(1007,0.51,0.09,0.07);
ctfun.addRGBPoint(1181,0.99,0.93,0.76);
ctfun.addRGBPoint(1350,1,1,1);
ctfun.addRGBPoint(2678,1,1,1);
const ofun = context.current.ofun;
ofun.removeAllPoints();
ofun.addPoint(-1024,0.000000);
ofun.addPoint(-979,0.000000);
ofun.addPoint(990,0.000000);
ofun.addPoint(1106,0.303731);
ofun.addPoint(1324,1.000000);
ofun.addPoint(3070,1.000000);
context.current.actor.getProperty().setShade(true);
context.current.renderWindow.render();
}
function onClickTBone(){
const ctfun = context.current.ctfun;
ctfun.removeAllPoints();
ctfun.addRGBPoint(-3000.0, 0.0, 0.0, 0.0);
ctfun.addRGBPoint(-1024.0, 0.0, 0.0, 0.0);
ctfun.addRGBPoint(1016,120,0,0);
ctfun.addRGBPoint(1603,1,0.56,0.34);
ctfun.addRGBPoint(2776,1,1,1);
ctfun.addRGBPoint(5297,1,1,1);
const ofun = context.current.ofun;
ofun.removeAllPoints();
ofun.addPoint(-1024,0.000000);
ofun.addPoint(674,0.000000);
ofun.addPoint(1372,0.053731);
ofun.addPoint(3023,0.180000);
ofun.addPoint(5274,0.343284);
context.current.actor.getProperty().setShade(false);
context.current.renderWindow.render();
}
function onClickSTissue(){
const ctfun = context.current.ctfun;
ctfun.removeAllPoints();
ctfun.addRGBPoint(-3000.0, 0.0, 0.0, 0.0);
ctfun.addRGBPoint(-1024.0, 0.0, 0.0, 0.0);
ctfun.addRGBPoint(-661,0,0,0);
ctfun.addRGBPoint(-627,1,1,1);
ctfun.addRGBPoint(2678,1,1,1);
const ofun = context.current.ofun;
ofun.removeAllPoints();
ofun.addPoint(-1024,0.000000);
ofun.addPoint(-552,0.000000);
ofun.addPoint(-461,0.703731);
ofun.addPoint(-324,1.000000);
ofun.addPoint(2678,1.000000);
context.current.actor.getProperty().setShade(true);
context.current.renderWindow.render();
}
return (
<div className="App d-grid gap-2">
<Navbar bg="primary" variant="dark">
<Container>
<Navbar.Brand href="#home">ON3D Cloud Service</Navbar.Brand>
<Navbar.Toggle />
<Navbar.Collapse className="justify-content-end">
<Navbar.Text>
Signed in as: <a href="#login">leonhong</a>
</Navbar.Text>
</Navbar.Collapse>
</Container>
</Navbar>
<div/>
<Container>
<Row>
<Col><Button variant="outline-secondary" onClick={onClickBone} > Bone Mode </Button></Col>
<Col><Button variant="outline-success" onClick={onClickTBone} > Trans. Bone </Button></Col>
<Col><Button variant="outline-danger" onClick={onClickSTissue} > Soft Tissue </Button></Col>
</Row>
</Container>
<div/>
<div className="mainView" ref={vtkContainerRef}></div>
<br/>
<Card>
<Card.Header as="h5">Report</Card.Header>
<Card.Body>
<Card.Title>Diagnosis Information Summary</Card.Title>
<Card.Text>
It seems good that the mandible position moves backward, and tooth correction is needed.
</Card.Text>
<a href={pptFile} download="leonhong.pptx" target='_blank'>
<Button variant="primary">Download Detail Report</Button>
</a>
</Card.Body>
</Card>
</div>
);
}
export default App;
모바일 웹브라우저로 시연한 영상은 다음과 같습니다.
https://www.youtube.com/watch?v=zPFJaeQmsgA
'ETC' 카테고리의 다른 글
수학 관련 용어 (0) | 2023.04.26 |
---|---|
vtkRenderWindow를 div에 연결하기 - Vue (0) | 2022.05.24 |
BCI 관련 자료들 (0) | 2021.12.20 |
Volume Rendering - React (0) | 2021.12.05 |
React를 이용한 VTK.js 개발하기 (0) | 2021.12.05 |