16. Draw Contrast In MainArea#
16.1. Main page#
Html
<template>
<div id="bg" ref="base_container">
<div ref="c_gui" id="gui"></div>
<div ref="nrrd_c" class="nrrd_c"></div>
<NavBar
:file-num="fileNum"
:max="max"
:immediate-slice-num="immediateSliceNum"
:contrast-index="contrastNum"
:is-axis-clicked="isAxisClicked"
:init-slice-index="initSliceIndex"
@on-slice-change="getSliceChangedNum"
@reset-main-area-size="resetMainAreaSize"
@on-change-orientation="resetSlicesOrientation"
@on-open-dialog="onOpenDialog"
></NavBar>
<Upload
:dialog="dialog"
@on-close-dialog="onCloseDialog"
@get-load-files-urls="readyToLoad"
></Upload>
</div>
</template>
<style lang="scss">
#bg {
width: 100vw;
height: 100vh;
/* border: 1px solid palevioletred; */
overflow: hidden;
}
#gui {
position: absolute;
top: 1px;
right: 0px;
z-index: 100;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.nrrd_c {
position: fixed;
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.copper3d_sliceNumber {
position: fixed !important;
width: 300px;
text-align: center;
top: 5% !important;
right: 1% !important;
left: 0px !important;
margin: 0 auto;
border: 1px solid salmon;
border-radius: 10px;
padding: 5px;
color: crimson;
}
.copper3D_loading_progress {
color: crimson !important;
}
</style>
config copper3D
import * as Copper from "copper3d";
import "copper3d/dist/css/style.css";
import { GUI } from "dat.gui";
import { TrackballControls } from "three/examples/jsm/controls/TrackballControls";
import { getCurrentInstance, onMounted, ref, watchEffect, reactive } from "vue";
import NavBar from "../components/NavBar.vue";
import Upload from "../components/Upload.vue";
let refs = null;
let appRenderer: Copper.copperRenderer;
let max = ref(0);
let immediateSliceNum = ref(0);
let contrastNum = ref(0);
let isAxisClicked = ref(false);
let dialog = ref(false);
let initSliceIndex = ref(0);
let scene: Copper.copperScene | undefined;
let bg: HTMLDivElement = ref<any>(null);
let c_gui: HTMLDivElement = ref<any>(null);
let nrrd_c: HTMLDivElement = ref<any>(null);
let pre_slices = ref();
let gui = new GUI({ width: 300, autoPlace: false });
let selectedContrastFolder: GUI;
let nrrdTools: Copper.nrrd_tools;
let loadBarMain: Copper.loadingBarType;
let urls: Array<string> = [];
let filesCount = ref(0);
let fileNum = ref(0);
let firstLoad = true;
let allSlices: Array<any> = [];
type selecedType = {
[key: string]: boolean;
};
onMounted(() => {
let { $refs } = (getCurrentInstance() as any).proxy;
refs = $refs;
bg = refs.base_container;
c_gui = $refs.c_gui;
nrrd_c = $refs.nrrd_c;
c_gui.appendChild(gui.domElement);
appRenderer = new Copper.copperRenderer(bg);
loadBarMain = Copper.loading();
nrrdTools = new Copper.nrrd_tools(nrrd_c);
nrrd_c.appendChild(loadBarMain.loadingContainer);
loadModel("nrrd_tools");
document.addEventListener("keydown", (e) => {
if (e.code === "KeyF") {
Copper.fullScreenListenner(bg);
}
});
const state = {
showContrast: false,
};
gui.add(state, "showContrast").onChange((flag) => {
nrrdTools.setShowInMainArea(flag);
isAxisClicked.value = false;
if (flag) {
max.value = nrrdTools.getMaxSliceNum()[1];
} else {
max.value = nrrdTools.getMaxSliceNum()[0];
}
});
selectedContrastFolder = gui.addFolder("select display contrast");
appRenderer.animate();
});
config copper3d environment
function loadModel(name: string) {
scene = appRenderer.getSceneByName(name) as Copper.copperScene;
if (scene == undefined) {
scene = appRenderer.createScene(name) as Copper.copperScene;
if (scene) {
// const sub = scene.addSubView();
// nrrd_c.appendChild(sub);
appRenderer.setCurrentScene(scene);
if (scene) {
// loadAllNrrds(urls);
scene.loadViewUrl("/copper3d_examples/nrrd_view.json");
}
Copper.setHDRFilePath("/copper3d_examples/venice_sunset_1k.hdr");
scene.updateBackground("#5454ad", "#18e5a7");
}
appRenderer.updateEnvironment();
}
}
setup opreation functions
const readyToLoad = (urlsArray: Array<string>) => {
fileNum.value = urlsArray.length;
urls = urlsArray;
if (urls.length > 0) loadAllNrrds(urls);
};
const onOpenDialog = (flag: boolean) => {
dialog.value = flag;
};
const onCloseDialog = (flag: boolean) => {
dialog.value = flag;
};
const resetSlicesOrientation = (axis: "x" | "y" | "z") => {
nrrdTools.setSliceOrientation(axis);
const status = nrrdTools.getIsShowContrastState();
isAxisClicked.value = true;
if (status) {
max.value = nrrdTools.getMaxSliceNum()[1];
} else {
max.value = nrrdTools.getMaxSliceNum()[0];
}
};
const getSliceChangedNum = (sliceNum: number) => {
nrrdTools.setSliceMoving(sliceNum);
};
const resetMainAreaSize = (factor: number) => {
nrrdTools.setMainAreaSize(factor);
};
watch changes after load all files and config nrrd_tools
watchEffect(() => {
if (
filesCount.value != 0 &&
allSlices.length != 0 &&
filesCount.value === urls.length
) {
console.log("All files ready!");
nrrdTools.clear();
allSlices.sort((a: any, b: any) => {
return a.order - b.order;
});
nrrdTools.setAllSlices(allSlices);
initSliceIndex.value = nrrdTools.getCurrentSliceIndex();
const getSliceNum = (index: number, contrastindex: number) => {
immediateSliceNum.value = index;
contrastNum.value = contrastindex;
};
if (firstLoad) {
nrrdTools.drag({
showNumber: true,
getSliceNum,
});
nrrdTools.draw(scene as Copper.copperScene, gui);
scene?.addPreRenderCallbackFunction(nrrdTools.start);
} else {
nrrdTools.redrawMianPreOnDisplayCanvas();
}
max.value = nrrdTools.getMaxSliceNum()[0];
filesCount.value = 0;
firstLoad = false;
const selectedState: selecedType = {};
for (let i = 0; i < allSlices.length - 1; i++) {
const key = "contrast" + i;
selectedState[key] = true;
}
nrrdTools.removeGuiFolderChilden(selectedContrastFolder);
for (let i = 0; i < allSlices.length - 1; i++) {
selectedContrastFolder
.add(selectedState, "contrast" + i)
.onChange((flag) => {
if (flag) {
fileNum.value += 1;
nrrdTools.removeSkip(i);
} else {
fileNum.value -= 1;
nrrdTools.addSkip(i);
}
});
}
}
});
load NRRD image
const loadAllNrrds = (urls: Array<string>) => {
allSlices = [];
const mainPreArea = (
volume: any,
nrrdMesh: Copper.nrrdMeshesType,
nrrdSlices: Copper.nrrdSliceType
// gui?: GUI
) => {
const newNrrdSlice = Object.assign(nrrdSlices, { order: 0 });
allSlices.push(newNrrdSlice);
volume1 = volume;
// scene?.subScene.add(nrrdMesh.z);
pre_slices.value = nrrdSlices;
// readyMain.value = true;
filesCount.value += 1;
};
scene?.loadNrrd(urls[0], loadBarMain, mainPreArea);
for (let i = 1; i < urls.length; i++) {
scene?.loadNrrd(
urls[i],
loadBarMain,
(
volume: any,
nrrdMesh: Copper.nrrdMeshesType,
nrrdSlices: Copper.nrrdSliceType
) => {
const newNrrdSlice = Object.assign(nrrdSlices, { order: i });
allSlices.push(newNrrdSlice);
filesCount.value += 1;
}
);
}
};
16.3. Component Upload#
Html
<template>
<div v-if="dialog" @click="closeDialog" class="upload_container">
<el-upload
:auto-upload="false"
@change="loaded"
@remove="removeLoadFile"
class="upload-demo"
drag
action="/"
multiple
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">
Drop file here or <em>click to upload</em>
</div>
<template #tip>
<div class="el-upload__tip">Please drag or upload NRRD files!</div>
</template>
</el-upload>
</div>
</template>
Css
.upload_container {
position: fixed;
z-index: 1000;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
justify-content: center;
}
.upload-demo {
width: 60vw;
}
Ts
import { UploadFilled } from "@element-plus/icons-vue";
import type { UploadFile } from "element-plus";
type Props = {
dialog?: boolean;
};
withDefaults(defineProps<Props>(), {
dialog: false,
});
let files: Array<File> = [];
let urls: Array<string> = [];
let loadedFiles = false;
const emit = defineEmits(["onCloseDialog", "getLoadFilesUrls"]);
const closeDialog = (e: MouseEvent) => {
let e1 = e.currentTarget;
let e2 = e.target;
if (e1 === e2) {
// openDialog.value = false;
files.forEach((file) => {
const url = URL.createObjectURL(file);
urls.push(url);
});
emit("getLoadFilesUrls", urls);
emit("onCloseDialog", false);
loadedFiles = true;
resetLoadState();
}
};
const resetLoadState = () => {
files = [];
urls.forEach((url) => {
URL.revokeObjectURL(url);
});
urls = [];
};
const loaded = (uploadFile: UploadFile) => {
if (loadedFiles) {
loadedFiles = false;
resetLoadState();
}
let filename = uploadFile.name;
let file: File | undefined;
if (filename.match(/\.(nrrd)$/)) {
file = uploadFile.raw as File;
files.push(file);
}
if (!file) {
onError("No .nrrd asset found!");
}
};
const removeLoadFile = (uploadFile: UploadFile) => {
files = files.filter((file) => {
return file.name !== uploadFile.name;
});
};
/**
* @param {Error} error
*/
const onError = (error: string | Error) => {
let message = ((error as Error) || {}).message || error.toString();
if (message.match(/ProgressEvent/)) {
message =
"Unable to retrieve this file. Check JS console and browser network tab.";
} else if (message.match(/Unexpected token/)) {
message = `Unable to parse file content. Verify that this file is valid. Error: "${message}"`;
} else if (
error &&
(error as any).target &&
(error as any).target instanceof Image
) {
message = "Missing texture: " + (error as any).target.src.split("/").pop();
}
window.alert(message);
console.error(error);
};
16.4. result#
