rii-mango / Papaya

A pure JavaScript medical research image viewer.
Other
553 stars 205 forks source link

Get coordinates where mouse is/click #215

Open JosephIsaacTurner opened 1 year ago

JosephIsaacTurner commented 1 year ago

Hi guys,

I am using Papaya to make a webpage where users can search based on coordinates. A good example of something similar is neurosynth (https://neurosynth.org/locations/). I want users to be able to search based on where they click on the papaya viewer. Just like the neurosynth viewer, which allows one to search based on the selected coordinate.

This is all straightforward.

I just want to know how to return the xyz coordinates where the user's mouse is hovering and/or where was last clicked.

It seems like it should be simple, because the papaya viewer always knows the coordinates of the mouse when you hover over the viewer (you can see at the bottom); I just don't know how to actually get those values and use them lol.

Thanks

cynde commented 1 year ago

@JosephIsaacTurner is this what you're looking for? https://github.com/rii-mango/Papaya/issues/166

JosephIsaacTurner commented 1 year ago

I actually figured out a solution to my problem. I couldn't figure it out anywhere else, even on the page mentioned by @cynde.

There's a method called papaya.viewer.Viewer.prototype.getCurrentValueAt This method calculates the coordinates in anatomical space, which is what I am trying to get. The method itself is called by a few other methods. Importantly, whenever the screen is clicked, the papaya.viewer.Viewer.prototype.drawViewer calls the papaya.viewer.Viewer.prototype.getCurrentValueAt method and displays the anatomical coordinates.

So, what I did was added a boolean parameter to the papaya.viewer.Viewer.prototype.getCurrentValueAt method, and whenever the papaya.viewer.Viewer.prototype.drawViewer calls the method, this parameter is set as true; whenever true, I change the value of an HTML attribute in my page to show the coordinates created by the papaya.viewer.Viewer.prototype.getCurrentValueAt method.

It might be easier to understand looking at my code for these methods: I added the click parameter to this method, and put the coordinates myX, myY, and myZ into an HTML element called "selectedCorodinate". The click parameter is only true when it is called by the papaya.viewer.Viewer.prototype.drawViewer method, which only happens when users click on a coordinate. If you want to constantly update the coordinate position as users hover over coordinates (rather than just click) you would get rid of the click parameter.

This is what it looks like:

papaya.viewer.Viewer.prototype.getCurrentValueAt = function(x, y, z, click) {

// Check if viewer is in world space
if (this.worldSpace) {
    if(click){
    let myX = (x - this.volume.header.origin.x) * this.volume.header.voxelDimensions.xSize;
    let myY = (this.volume.header.origin.y - y) * this.volume.header.voxelDimensions.ySize;
    let myZ = (this.volume.header.origin.z - z) * this.volume.header.voxelDimensions.zSize;
    document.getElementById("selectedCoordinate").innerHTML = "<b>What's Here? Click to search:</b> <a href='" + `LesionProject.php?x=${myX}&y=${myY}&z=${myZ}` + "'>" + String(myX) + " " + String(myY) + " " + String(myZ) +"</a>";
    // console.log(
        //     (x - this.volume.header.origin.x) * this.volume.header.voxelDimensions.xSize,
        //     (this.volume.header.origin.y - y) * this.volume.header.voxelDimensions.ySize,
        //     (this.volume.header.origin.z - z) * this.volume.header.voxelDimensions.zSize,
        // );
    }

  // If in world space, get the voxel value at the given world coordinate in the current screen volume at the current timepoint
  return this.currentScreenVolume.volume.getVoxelAtCoordinate(
    (x - this.volume.header.origin.x) * this.volume.header.voxelDimensions.xSize,
    (this.volume.header.origin.y - y) * this.volume.header.voxelDimensions.ySize,
    (this.volume.header.origin.z - z) * this.volume.header.voxelDimensions.zSize,
    this.currentScreenVolume.currentTimepoint,
    false
  );
} else {
    if(click){
      let myX =  x * this.volume.header.voxelDimensions.xSize;
      let myY = y * this.volume.header.voxelDimensions.ySize;
      let myZ = z * this.volume.header.voxelDimensions.zSize;
      document.getElementById("selectedCoordinate").innerHTML = "<b>What's Here? Click to search:</b> <a href='" + `LesionProject.php?x=${myX}&y=${myY}&z=${myZ}` + "'>" + String(myX) + " " + String(myY) + " " + String(myZ) +"</a>";
      // console.log(
        //     x * this.volume.header.voxelDimensions.xSize,
        //     y * this.volume.header.voxelDimensions.ySize,
        //     z * this.volume.header.voxelDimensions.zSize
        // );
    }

  // If not in world space, get the voxel value at the given millimeter coordinate in the current screen volume at the current timepoint
  return this.currentScreenVolume.volume.getVoxelAtMM(
    x * this.volume.header.voxelDimensions.xSize,
    y * this.volume.header.voxelDimensions.ySize,
    z * this.volume.header.voxelDimensions.zSize,
    this.currentScreenVolume.currentTimepoint,
    false
  );
}

};

You can copy this snippet and replace the method in your version of papaya.js.

You will also want to change the method that calls it; The method is really big, and I made a minute change near the very end (see bold text):

papaya.viewer.Viewer.prototype.drawViewer=function(c,b){ var d="Yes"===this.container.preferences.radiological, e="Yes"===this.container.preferences.showOrientation; this.initialized?(this.context.save(),b?(this.axialSlice.repaint(this.currentCoord.z,c,this.worldSpace), this.coronalSlice.repaint(this.currentCoord.y,c,this.worldSpace), this.sagittalSlice.repaint(this.currentCoord.x,c,this.worldSpace)): ((c||this.draggingSliceDir!==papaya.viewer.ScreenSlice.DIRECTION_AXIAL) &&this.axialSlice.updateSlice(this.currentCoord.z,c,this.worldSpace), (c||this.draggingSliceDir!==papaya.viewer.ScreenSlice.DIRECTION_CORONAL) &&this.coronalSlice.updateSlice(this.currentCoord.y,c,this.worldSpace), (c||this.draggingSliceDir!==papaya.viewer.ScreenSlice.DIRECTION_SAGITTAL) &&this.sagittalSlice.updateSlice(this.currentCoord.x,c,this.worldSpace)), this.context.fillStyle=papaya.viewer.Viewer.BACKGROUND_COLOR, "No"===this.container.preferences.smoothDisplay?(this.context.imageSmoothingEnabled=!1, this.context.webkitImageSmoothingEnabled=!1,this.context.mozImageSmoothingEnabled=!1,this.context.msImageSmoothingEnabled=!1): (this.context.imageSmoothingEnabled=!0, this.context.webkitImageSmoothingEnabled=!0, this.context.mozImageSmoothingEnabled=!0, this.context.msImageSmoothingEnabled=!0), this.context.setTransform(1,0,0,1,0,0), this.context.fillRect(this.mainImage.screenOffsetX,this.mainImage.screenOffsetY,this.mainImage.screenDim,this.mainImage.screenDim), this.context.save(),this.context.beginPath(), this.context.rect(this.mainImage.screenOffsetX,this.mainImage.screenOffsetY,this.mainImage.screenDim,this.mainImage.screenDim), this.context.clip(), this.context.setTransform(this.mainImage.finalTransform[0][0],0,0,this.mainImage.finalTransform[1][1],this.mainImage.finalTransform[0][2],this.mainImage.finalTransform[1][2]), this.context.drawImage(this.mainImage.canvasMain,0,0), this.context.restore(), this.mainImage.canvasDTILines &&this.context.drawImage(this.mainImage.canvasDTILines,0,0), this.container.orthogonal &&(this.context.setTransform(1,0,0,1,0,0), this.context.fillRect(this.lowerImageBot.screenOffsetX, this.lowerImageBot.screenOffsetY,this.lowerImageBot.screenDim,this.lowerImageBot.screenDim), this.context.save(), this.context.beginPath(), this.context.rect(this.lowerImageBot.screenOffsetX,this.lowerImageBot.screenOffsetY,this.lowerImageBot.screenDim,this.lowerImageBot.screenDim), this.context.clip(), this.context.setTransform(this.lowerImageBot.finalTransform[0][0],0,0,this.lowerImageBot.finalTransform[1][1],this.lowerImageBot.finalTransform[0][2],this.lowerImageBot.finalTransform[1][2]), this.context.drawImage(this.lowerImageBot.canvasMain, 0,0), this.context.restore(), this.lowerImageBot.canvasDTILines &&this.context.drawImage(this.lowerImageBot.canvasDTILines,this.lowerImageBot.screenOffsetX,this.lowerImageBot.screenOffsetY), this.context.setTransform(1,0,0,1,0,0), this.context.fillRect(this.lowerImageTop.screenOffsetX,this.lowerImageTop.screenOffsetY,this.lowerImageTop.screenDim,this.lowerImageTop.screenDim), this.context.save(), this.context.beginPath(), this.context.rect(this.lowerImageTop.screenOffsetX,this.lowerImageTop.screenOffsetY,this.lowerImageTop.screenDim,this.lowerImageTop.screenDim), this.context.clip(), this.context.setTransform(this.lowerImageTop.finalTransform[0][0],0,0,this.lowerImageTop.finalTransform[1][1],this.lowerImageTop.finalTransform[0][2],this.lowerImageTop.finalTransform[1][2]), this.context.drawImage(this.lowerImageTop.canvasMain,0,0), this.context.restore(), this.lowerImageTop.canvasDTILines &&this.context.drawImage(this.lowerImageTop.canvasDTILines,this.lowerImageTop.screenOffsetX,this.lowerImageTop.screenOffsetY)), (e||d)&&this.drawOrientation(), "None"!==this.container.preferences.showCrosshairs&&this.drawCrosshairs(), "Yes"===this.container.preferences.showRuler&&this.drawRuler(), this.container.display &&this.container.display.drawDisplay(this.currentCoord.x,this.currentCoord.y,this.currentCoord.z, this.getCurrentValueAt(this.currentCoord.x,this.currentCoord.y,this.currentCoord.z, true))): this.drawEmptyViewer() };