diff --git a/docs/source/conf.py b/docs/source/conf.py index f9caae2..b89c57f 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'LinkunGao' # The full version, including alpha/beta/rc tags -release = 'v1.13.33' +release = 'v1.14.0' # -- General configuration --------------------------------------------------- diff --git a/docs/source/release/release.md b/docs/source/release/release.md index a0a712c..9bf93b1 100644 --- a/docs/source/release/release.md +++ b/docs/source/release/release.md @@ -1560,3 +1560,22 @@ copperNrrdLoader( ) sceneIn?.loadNrrd(url, loadBar1, false, funa, opts); ``` + +## Release v1.14.0 + +- Update objloader. + - now the callback function works. + - can get mesh out side. +- Update CopperMScene. + - set the trackball control as default controller. +- Update copper3d_nrrd_plugin. + - display as micorns, not the pixels. + - pixels \* spacing = mm + - addressed the threejs loader to load the 16-bits nrrds. + - addressed the spacing issue among each direction of nrrd. +- Update nrrd_tools to fit the new nrrd volume format by threejs loader. + - drag function. + - paint function. + - save mask data function. + - undo function. + - crosshair function. diff --git a/package-lock.json b/package-lock.json index 5003c81..06fd49b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,18 @@ { "name": "copper3d_visualisation", - "version": "1.13.33", + "version": "1.14.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "copper3d_visualisation", - "version": "1.13.33", + "version": "1.14.0", "license": "Apache-2.0", "dependencies": { "@types/dat.gui": "^0.7.9", "@types/three": "^0.140.0", "copper3d_plugin_heart_k": "^1.0.14", - "copper3d_plugin_nrrd": "^1.3.0", + "copper3d_plugin_nrrd": "^1.3.1", "dat.gui": "^0.7.9", "dicom-parser": "^1.8.13", "fflate": "^0.7.3", @@ -515,9 +515,9 @@ "license": "ISC" }, "node_modules/copper3d_plugin_nrrd": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/copper3d_plugin_nrrd/-/copper3d_plugin_nrrd-1.3.0.tgz", - "integrity": "sha512-M2qqa87tBcOTM6wy6GU6bUax3zM2iFtRSPvDlVfIcohVVPY6Tj0hKvtEpwIuK3A1debKyJrZnhT5rnzqD1fvSQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/copper3d_plugin_nrrd/-/copper3d_plugin_nrrd-1.3.1.tgz", + "integrity": "sha512-DTe+H3It6Xkd6T5aVtjqlmhdTzScb3+fVqcrHQuVFjF8d7xfANIhg1nZ0WREKvqjrmAeSiDcgQiaG8Zqm1frYg==", "dependencies": { "fflate": "^0.7.4", "three": "^0.148.0" @@ -3090,9 +3090,9 @@ "version": "1.0.8" }, "copper3d_plugin_nrrd": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/copper3d_plugin_nrrd/-/copper3d_plugin_nrrd-1.3.0.tgz", - "integrity": "sha512-M2qqa87tBcOTM6wy6GU6bUax3zM2iFtRSPvDlVfIcohVVPY6Tj0hKvtEpwIuK3A1debKyJrZnhT5rnzqD1fvSQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/copper3d_plugin_nrrd/-/copper3d_plugin_nrrd-1.3.1.tgz", + "integrity": "sha512-DTe+H3It6Xkd6T5aVtjqlmhdTzScb3+fVqcrHQuVFjF8d7xfANIhg1nZ0WREKvqjrmAeSiDcgQiaG8Zqm1frYg==", "requires": { "fflate": "^0.7.4", "three": "^0.148.0" diff --git a/package.json b/package.json index 6a2a44b..94eb402 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "copper3d_visualisation", "description": "A 3d visualisation package base on threejs provides multiple scenes and Nrrd image load funtion.", - "version": "1.13.33", + "version": "1.14.0", "main": "dist/bundle.umd.js", "moudle": "dist/bundle.esm.js", "types": "dist/types/index.d.ts", @@ -48,7 +48,7 @@ "@types/dat.gui": "^0.7.9", "@types/three": "^0.140.0", "copper3d_plugin_heart_k": "^1.0.14", - "copper3d_plugin_nrrd": "^1.3.0", + "copper3d_plugin_nrrd": "^1.3.1", "dat.gui": "^0.7.9", "dicom-parser": "^1.8.13", "fflate": "^0.7.3", diff --git a/src/Loader/copperNrrdLoader.ts b/src/Loader/copperNrrdLoader.ts index bffffe9..fded75b 100644 --- a/src/Loader/copperNrrdLoader.ts +++ b/src/Loader/copperNrrdLoader.ts @@ -68,10 +68,10 @@ export function copperNrrdLoader( const initIndexY = Math.floor(dimensions[1] / 2); const initIndexX = Math.floor(dimensions[0] / 2); - const sliceZ = volume.extractSlice("z", initIndexZ); - const sliceY = volume.extractSlice("y", initIndexY); + const sliceZ = volume.extractSlice("z", initIndexZ * ratio[2]); + const sliceY = volume.extractSlice("y", initIndexY * ratio[1]); //x plane - const sliceX = volume.extractSlice("x", initIndexX); + const sliceX = volume.extractSlice("x", initIndexX * ratio[0]); sliceZ.initIndex = initIndexZ; sliceY.initIndex = initIndexY; sliceX.initIndex = initIndexX; @@ -98,13 +98,13 @@ export function copperNrrdLoader( if (gui) { gui - .add(sliceX, "index", 0, volume.RASDimensions[0], 1) + .add(sliceX, "index", 0, volume.RASDimensions[0] - 1, 1) .name("indexX") .onChange(function () { sliceX.repaint.call(sliceX); }); gui - .add(sliceY, "index", 0, volume.RASDimensions[1], 1) + .add(sliceY, "index", 0, volume.RASDimensions[1] - 1, 1) .name("indexY") .onChange(function () { sliceY.repaint.call(sliceY); diff --git a/src/Scene/commonSceneMethod.ts b/src/Scene/commonSceneMethod.ts index 8a3a480..e760818 100644 --- a/src/Scene/commonSceneMethod.ts +++ b/src/Scene/commonSceneMethod.ts @@ -293,7 +293,7 @@ export default class commonScene { !!callback && callback(obj); }, // called when loading is in progresses (xhr: any) => { - console.log((xhr.loaded / xhr.total) * 100 + "% loaded"); + // console.log((xhr.loaded / xhr.total) * 100 + "% loaded"); }, // called when loading has errors (error: any) => { diff --git a/src/Scene/copperMScene.ts b/src/Scene/copperMScene.ts index f664d95..402fcb3 100644 --- a/src/Scene/copperMScene.ts +++ b/src/Scene/copperMScene.ts @@ -6,6 +6,7 @@ import { TrackballControls } from "three/examples/jsm/controls/TrackballControls import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; import { GLTF } from "three/examples/jsm/loaders/GLTFLoader"; import { copperGltfLoader } from "../Loader/copperGltfLoader"; +import { objLoader } from "../Loader/copperOBJLoader"; import { isPickedModel, throttle } from "../Utils/raycaster"; import { mouseMovePositionType, @@ -17,6 +18,7 @@ import { isIOS } from "../Utils/utils"; import commonScene from "./commonSceneMethod"; + const IS_IOS = isIOS(); export default class copperMScene extends commonScene { @@ -66,8 +68,10 @@ export default class copperMScene extends commonScene { this.vignette.mesh.renderOrder = -1; this.copperControl = new Controls(this.camera); - // this.controls = new TrackballControls(this.camera, container); - this.controls = new OrbitControls(this.camera, this.container); + this.controls = new TrackballControls(this.camera, this.container); + this.controls.rotateSpeed = 0.02; + this.controls.staticMoving = true + // this.controls = new OrbitControls(this.camera, this.container); this.preRenderCallbackFunctions = { index: 0, cache: [], @@ -126,6 +130,7 @@ export default class copperMScene extends commonScene { default: this.controls = new TrackballControls(this.camera, this.container); this.controls.rotateSpeed = 0.01; + this.controls.staticMoving = true break; } } @@ -251,6 +256,53 @@ export default class copperMScene extends commonScene { copperNrrdLoader1(url, this.scene, this.container, callback); } + loadOBJ(url: string, callback?: (mesh: THREE.Group) => void) { + objLoader.load( + url, + (obj) => { + obj.traverse((child) => { + if ((child as THREE.Mesh).isMesh) { + // (child as THREE.Mesh).material = new THREE.MeshStandardMaterial({ + // side: THREE.DoubleSide, + // color: 0xffffff, + // }); + // ((child as THREE.Mesh).material as THREE.MeshPhongMaterial).color = + // new THREE.Color(0xffffff); + } + }); + const box = new THREE.Box3().setFromObject(obj); + const size = box.getSize(new THREE.Vector3()).length(); + const center = box.getCenter(new THREE.Vector3()); + + this.controls.maxDistance = size * 10; + obj.position.x += obj.position.x - center.x; + obj.position.y += obj.position.y - center.y; + obj.position.z += obj.position.z - center.z; + + if (!this.cameraPositionFlag) { + this.camera.position.copy(center); + this.camera.position.x += size / 2.0; + this.camera.position.y += size / 5.0; + this.camera.position.z += size / 2.0; + this.camera.lookAt(center); + this.viewPoint = this.setViewPoint( + this.camera as THREE.PerspectiveCamera, + [center.x, center.y, center.z] + ); + } + this.scene.add(obj); + !!callback && callback(obj); + }, // called when loading is in progresses + (xhr: any) => { + // console.log((xhr.loaded / xhr.total) * 100 + "% loaded"); + }, + // called when loading has errors + (error: any) => { + console.log("An error happened"); + } + ); + } + drawWholeNrrd(nrrdSlices: nrrdSliceType) { getWholeSlices( nrrdSlices, diff --git a/src/Utils/nrrd_tool.ts b/src/Utils/nrrd_tool.ts index ecc2d5a..baf2b4f 100644 --- a/src/Utils/nrrd_tool.ts +++ b/src/Utils/nrrd_tool.ts @@ -251,6 +251,8 @@ export class nrrd_tools { /** * + * entry function + * * @param allSlices - all nrrd contrast slices * { * x:slice, @@ -270,8 +272,12 @@ export class nrrd_tools { this.allSlicesArray[0].x.volume.dimensions[1]; this.nrrd_states.nrrd_z_centimeter = this.allSlicesArray[0].x.volume.dimensions[2]; - this.nrrd_states.dimensions = this.allSlicesArray[0].x.volume.dimensions; this.nrrd_states.voxelSpacing = this.allSlicesArray[0].x.volume.spacing; + this.nrrd_states.ratios.x = this.allSlicesArray[0].x.volume.spacing[0] + this.nrrd_states.ratios.y = this.allSlicesArray[0].x.volume.spacing[1] + this.nrrd_states.ratios.z = this.allSlicesArray[0].x.volume.spacing[2] + this.nrrd_states.dimensions = this.allSlicesArray[0].x.volume.dimensions; + this.allSlicesArray.forEach((item, index) => { item.x.contrastOrder = index; @@ -437,12 +443,13 @@ export class nrrd_tools { if (axis === "z") { if (this.nrrd_states.isCursorSelect && !this.cursorPage.z.updated) { if (this.axis === "x") { - this.nrrd_states.oldIndex = this.nrrd_states.currentIndex = + this.nrrd_states.currentIndex = Math.ceil( (this.cursorPage.x.cursorPageX / this.nrrd_states.nrrd_z_pixel) * this.nrrd_states.dimensions[2] ); + this.nrrd_states.oldIndex = this.nrrd_states.currentIndex*this.nrrd_states.ratios.z this.nrrd_states.cursorPageX = Math.ceil( (this.cursorPage.x.index / this.nrrd_states.dimensions[0]) * @@ -450,13 +457,13 @@ export class nrrd_tools { ); } if (this.axis === "y") { - this.nrrd_states.oldIndex = this.nrrd_states.currentIndex = + this.nrrd_states.currentIndex = Math.ceil( (this.cursorPage.y.cursorPageY / this.nrrd_states.nrrd_z_pixel) * this.nrrd_states.dimensions[2] ); - + this.nrrd_states.oldIndex = this.nrrd_states.currentIndex*this.nrrd_states.ratios.z this.nrrd_states.cursorPageY = Math.ceil( (this.cursorPage.y.index / this.nrrd_states.dimensions[1]) * this.nrrd_states.nrrd_y_pixel @@ -464,33 +471,34 @@ export class nrrd_tools { } this.cursorPage.z.updated = true; } else { - this.nrrd_states.oldIndex = this.nrrd_states.currentIndex = - this.cursorPage.z.index; + this.nrrd_states.currentIndex = this.cursorPage.z.index; + this.nrrd_states.oldIndex = this.cursorPage.z.index * this.nrrd_states.ratios.z; this.nrrd_states.cursorPageX = this.cursorPage.z.cursorPageX; this.nrrd_states.cursorPageY = this.cursorPage.z.cursorPageY; } } else if (axis === "x") { if (this.nrrd_states.isCursorSelect && !this.cursorPage.x.updated) { if (this.axis === "z") { - this.nrrd_states.oldIndex = this.nrrd_states.currentIndex = + this.nrrd_states.currentIndex = Math.ceil( (this.cursorPage.z.cursorPageX / this.nrrd_states.nrrd_x_pixel) * this.nrrd_states.dimensions[0] ); + this.nrrd_states.oldIndex = this.nrrd_states.currentIndex * this.nrrd_states.ratios.x this.nrrd_states.cursorPageX = Math.floor( (this.cursorPage.z.index / this.nrrd_states.dimensions[2]) * this.nrrd_states.nrrd_z_pixel ); } if (this.axis === "y") { - this.nrrd_states.oldIndex = this.nrrd_states.currentIndex = + this.nrrd_states.currentIndex = Math.ceil( (this.cursorPage.y.cursorPageX / this.nrrd_states.nrrd_y_pixel) * this.nrrd_states.dimensions[1] ); - + this.nrrd_states.oldIndex = this.nrrd_states.currentIndex * this.nrrd_states.ratios.x this.nrrd_states.cursorPageX = this.cursorPage.y.cursorPageY; this.nrrd_states.cursorPageY = Math.ceil( (this.cursorPage.y.index / this.nrrd_states.dimensions[1]) * @@ -499,32 +507,34 @@ export class nrrd_tools { } this.cursorPage.x.updated = true; } else { - this.nrrd_states.oldIndex = this.nrrd_states.currentIndex = - this.cursorPage.x.index; + this.nrrd_states.currentIndex = this.cursorPage.x.index; + this.nrrd_states.oldIndex = this.cursorPage.x.index * this.nrrd_states.ratios.x this.nrrd_states.cursorPageX = this.cursorPage.x.cursorPageX; this.nrrd_states.cursorPageY = this.cursorPage.x.cursorPageY; } } else if (axis === "y") { if (this.nrrd_states.isCursorSelect && !this.cursorPage.y.updated) { if (this.axis === "z") { - this.nrrd_states.oldIndex = this.nrrd_states.currentIndex = + this.nrrd_states.currentIndex = Math.ceil( (this.cursorPage.z.cursorPageY / this.nrrd_states.nrrd_y_pixel) * this.nrrd_states.dimensions[1] ); + this.nrrd_states.oldIndex = this.nrrd_states.currentIndex * this.nrrd_states.ratios.y this.nrrd_states.cursorPageY = Math.ceil( (this.cursorPage.z.index / this.nrrd_states.dimensions[2]) * this.nrrd_states.nrrd_z_pixel ); } if (this.axis === "x") { - this.nrrd_states.oldIndex = this.nrrd_states.currentIndex = + this.nrrd_states.currentIndex = Math.ceil( (this.cursorPage.x.cursorPageY / this.nrrd_states.nrrd_x_pixel) * this.nrrd_states.dimensions[0] ); + this.nrrd_states.oldIndex = this.nrrd_states.currentIndex * this.nrrd_states.ratios.y this.nrrd_states.cursorPageX = Math.ceil( (this.cursorPage.x.index / this.nrrd_states.dimensions[0]) * this.nrrd_states.nrrd_x_pixel @@ -533,8 +543,8 @@ export class nrrd_tools { } this.cursorPage.y.updated = true; } else { - this.nrrd_states.oldIndex = this.nrrd_states.currentIndex = - this.cursorPage.y.index; + this.nrrd_states.currentIndex = this.cursorPage.y.index; + this.nrrd_states.oldIndex = this.cursorPage.y.index * this.nrrd_states.ratios.y this.nrrd_states.cursorPageX = this.cursorPage.y.cursorPageX; this.nrrd_states.cursorPageY = this.cursorPage.y.cursorPageY; } @@ -645,7 +655,8 @@ export class nrrd_tools { } getCurrentSliceIndex() { - return Math.ceil(this.mainPreSlice.index); + + return Math.ceil(this.mainPreSlice.index/this.nrrd_states.RSARatio); } getIsShowContrastState() { @@ -728,11 +739,11 @@ export class nrrd_tools { this.nrrd_states.oldIndex = this.nrrd_states.maxIndex; if (this.initState) { - this.nrrd_states.oldIndex = this.mainPreSlice.initIndex; + this.nrrd_states.oldIndex = this.mainPreSlice.initIndex*this.nrrd_states.RSARatio; this.nrrd_states.currentIndex = this.mainPreSlice.initIndex; } else { // !need to change - + // todo this.mainPreSlice.index = this.nrrd_states.oldIndex; } @@ -745,14 +756,17 @@ export class nrrd_tools { this.setMainPreSlice(); this.setOriginCanvasAndPre(); this.currentShowingSlice = this.mainPreSlice; + this.nrrd_states.oldIndex = this.mainPreSlice.initIndex*this.nrrd_states.RSARatio; + this.nrrd_states.currentIndex = this.mainPreSlice.initIndex; this.undoArray = [ { - sliceIndex: this.mainPreSlice.index, + // todo + // sliceIndex: this.mainPreSlice.index, + sliceIndex:this.nrrd_states.currentIndex, undos: [], }, ]; - this.nrrd_states.oldIndex = this.mainPreSlice.initIndex; - this.nrrd_states.currentIndex = this.mainPreSlice.initIndex; + // compute max index this.updateMaxIndex(); this.updateShowNumDiv(this.nrrd_states.contrastNum); @@ -909,12 +923,11 @@ export class nrrd_tools { handleOnMouseMove, false ); - // this.nrrd_states.oldIndex = this.mainPreSlice.index; sensivity = this.sensitiveArray[this.gui_states.dragSensitivity - 1]; } }; handleOnMouseMove = throttle((ev: MouseEvent) => { - // this.nrrd_states.oldIndex = this.mainPreSlice.index; + if (y - ev.offsetY / h >= 0) { move = -Math.ceil(((y - ev.offsetY / h) * 10) / sensivity); } else { @@ -974,7 +987,9 @@ export class nrrd_tools { contrastModifyNum = move % this.displaySlices.length; this.nrrd_states.contrastNum += contrastModifyNum; if (move > 0) { - if (this.mainPreSlice.index <= this.nrrd_states.maxIndex) { + // todo + // if (this.mainPreSlice.index <= this.nrrd_states.maxIndex) { + if (this.nrrd_states.currentIndex <= this.nrrd_states.maxIndex) { sliceModifyNum = Math.floor(move / this.displaySlices.length); if (this.nrrd_states.contrastNum > this.displaySlices.length - 1) { @@ -995,12 +1010,11 @@ export class nrrd_tools { sliceModifyNum = move; } - // this.updateShowNumDiv(this.contrastNum, this.oldIndex); - - let newIndex = this.nrrd_states.oldIndex + sliceModifyNum; + // let newIndex = this.nrrd_states.oldIndex + sliceModifyNum; + let newIndex = this.nrrd_states.currentIndex + sliceModifyNum; if ( - newIndex != this.nrrd_states.oldIndex || + newIndex != this.nrrd_states.currentIndex || this.nrrd_states.showContrast ) { if (newIndex > this.nrrd_states.maxIndex) { @@ -1010,12 +1024,14 @@ export class nrrd_tools { newIndex = this.nrrd_states.minIndex; this.nrrd_states.contrastNum = 0; } else { - this.mainPreSlice.index = newIndex; - this.nrrd_states.currentIndex = newIndex; - if (newIndex != this.nrrd_states.oldIndex) + this.mainPreSlice.index = newIndex*this.nrrd_states.RSARatio; + // clear drawing canvas, and display next slice + if (newIndex != this.nrrd_states.currentIndex){ this.drawingCanvasLayerOne.width = this.drawingCanvasLayerOne.width; + } + this.displayCanvas.width = this.displayCanvas.width; if (this.nrrd_states.changedWidth === 0) { @@ -1025,12 +1041,13 @@ export class nrrd_tools { // get the slice that need to be updated on displayCanvas let needToUpdateSlice = this.updateCurrentContrastSlice(); + needToUpdateSlice.repaint.call(needToUpdateSlice); this.drawDragSlice(needToUpdateSlice.canvas, newIndex); } - // this.nrrd_states.oldIndex = this.mainPreSlice.index; - this.nrrd_states.oldIndex = newIndex; + this.nrrd_states.currentIndex = newIndex; + this.nrrd_states.oldIndex = newIndex*this.nrrd_states.RSARatio; this.updateShowNumDiv(this.nrrd_states.contrastNum); } } @@ -1053,7 +1070,7 @@ export class nrrd_tools { this.paintImages.y.length > 0 || this.paintImages.z.length > 0 ) { - if (newIndex != this.nrrd_states.oldIndex) { + if (newIndex != this.nrrd_states.currentIndex) { this.paintedImage = this.filterDrawedImage( this.axis, this.nrrd_states.currentIndex @@ -1135,6 +1152,8 @@ export class nrrd_tools { let panelMoveInnerX = 0; let panelMoveInnerY = 0; + // todo + // let currentSliceIndex = this.mainPreSlice.index; let currentSliceIndex = this.mainPreSlice.index; // draw lines starts position @@ -1221,6 +1240,7 @@ export class nrrd_tools { } // when switch slice, clear previousDrawingImage + // todo if (currentSliceIndex !== this.mainPreSlice.index) { this.previousDrawingImage = this.emptyCtx.createImageData(1, 1); currentSliceIndex = this.mainPreSlice.index; diff --git a/src/index.ts b/src/index.ts index 9bcebe1..c2afbbe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -36,10 +36,10 @@ import { import "./css/style.css"; -export const REVISION = "v1.13.33"; +export const REVISION = "v1.14.0"; console.log( - "%cCopper3D Visualisation %cBeta:v1.13.33", + "%cCopper3D Visualisation %cBeta:v1.14.0", "padding: 3px;color:white; background:#023047", "padding: 3px;color:white; background:#f50a25" );