001package plugins.kernel.canvas; 002 003import java.awt.AWTException; 004import java.awt.BorderLayout; 005import java.awt.Color; 006import java.awt.Component; 007import java.awt.Cursor; 008import java.awt.Graphics; 009import java.awt.Graphics2D; 010import java.awt.Image; 011import java.awt.Point; 012import java.awt.Rectangle; 013import java.awt.Robot; 014import java.awt.event.ActionEvent; 015import java.awt.event.ActionListener; 016import java.awt.event.KeyEvent; 017import java.awt.event.MouseEvent; 018import java.awt.event.MouseWheelEvent; 019import java.awt.image.BufferedImage; 020import java.beans.PropertyChangeEvent; 021import java.util.Arrays; 022import java.util.LinkedList; 023import java.util.List; 024import java.util.concurrent.Callable; 025import java.util.concurrent.LinkedBlockingQueue; 026 027import javax.swing.JToolBar; 028 029import icy.canvas.Canvas3D; 030import icy.canvas.CanvasLayerEvent; 031import icy.canvas.CanvasLayerEvent.LayersEventType; 032import icy.canvas.IcyCanvas; 033import icy.canvas.IcyCanvasEvent; 034import icy.canvas.IcyCanvasEvent.IcyCanvasEventType; 035import icy.canvas.Layer; 036import icy.common.exception.TooLargeArrayException; 037import icy.gui.component.button.IcyToggleButton; 038import icy.gui.util.ComponentUtil; 039import icy.gui.util.GuiUtil; 040import icy.gui.viewer.Viewer; 041import icy.image.IcyBufferedImage; 042import icy.image.lut.LUT; 043import icy.image.lut.LUT.LUTChannel; 044import icy.painter.Overlay; 045import icy.painter.VtkPainter; 046import icy.preferences.CanvasPreferences; 047import icy.preferences.XMLPreferences; 048import icy.resource.ResourceUtil; 049import icy.resource.icon.IcyIcon; 050import icy.roi.ROI; 051import icy.sequence.Sequence; 052import icy.sequence.SequenceEvent.SequenceEventType; 053import icy.system.IcyExceptionHandler; 054import icy.system.thread.ThreadUtil; 055import icy.type.collection.array.Array1DUtil; 056import icy.type.point.Point3D; 057import icy.util.ColorUtil; 058import icy.util.EventUtil; 059import icy.util.StringUtil; 060import icy.vtk.IcyVtkPanel; 061import icy.vtk.VtkImageVolume; 062import icy.vtk.VtkImageVolume.VtkVolumeBlendType; 063import icy.vtk.VtkImageVolume.VtkVolumeMapperType; 064import icy.vtk.VtkUtil; 065import plugins.kernel.canvas.VtkSettingPanel.SettingChangeListener; 066import vtk.vtkActor; 067import vtk.vtkActor2D; 068import vtk.vtkAxesActor; 069import vtk.vtkCamera; 070import vtk.vtkColorTransferFunction; 071import vtk.vtkCubeAxesActor; 072import vtk.vtkImageData; 073import vtk.vtkInformation; 074import vtk.vtkInformationIntegerKey; 075import vtk.vtkLight; 076import vtk.vtkOrientationMarkerWidget; 077import vtk.vtkPicker; 078import vtk.vtkPiecewiseFunction; 079import vtk.vtkProp; 080import vtk.vtkRenderWindow; 081import vtk.vtkRenderer; 082import vtk.vtkTextActor; 083import vtk.vtkTextProperty; 084 085/** 086 * VTK 3D canvas class. 087 * 088 * @author Stephane 089 */ 090@SuppressWarnings("deprecation") 091public class VtkCanvas extends Canvas3D implements ActionListener, SettingChangeListener 092{ 093 /** 094 * 095 */ 096 private static final long serialVersionUID = -1274251057822161271L; 097 098 /** 099 * icons 100 */ 101 public static final Image ICON_AXES3D = ResourceUtil.getAlphaIconAsImage("axes3d.png"); 102 public static final Image ICON_BOUNDINGBOX = ResourceUtil.getAlphaIconAsImage("bbox.png"); 103 public static final Image ICON_GRID = ResourceUtil.getAlphaIconAsImage("3x3_grid.png"); 104 public static final Image ICON_RULER = ResourceUtil.getAlphaIconAsImage("ruler.png"); 105 public static final Image ICON_RULERLABEL = ResourceUtil.getAlphaIconAsImage("ruler_label.png"); 106 public static final Image ICON_TARGET = ResourceUtil.getAlphaIconAsImage("target.png"); 107 108 /** 109 * properties 110 */ 111 public static final String PROPERTY_AXES = "axis"; 112 public static final String PROPERTY_BOUNDINGBOX = "boundingBox"; 113 public static final String PROPERTY_BOUNDINGBOX_GRID = "boundingBoxGrid"; 114 public static final String PROPERTY_BOUNDINGBOX_RULES = "boundingBoxRules"; 115 public static final String PROPERTY_BOUNDINGBOX_LABELS = "boundingBoxLabels"; 116 public static final String PROPERTY_LUT = "lut"; 117 public static final String PROPERTY_DATA = "data"; 118 public static final String PROPERTY_SCALE = "scale"; 119 public static final String PROPERTY_BOUNDS = "bounds"; 120 121 /** 122 * Used for outline visibility information in vtkActor 123 */ 124 125 public static final vtkInformationIntegerKey visibilityKey = new vtkInformationIntegerKey().MakeKey("Visibility", 126 "Property"); 127 /** 128 * preferences id 129 */ 130 protected static final String PREF_ID = "vtkCanvas"; 131 132 /** 133 * id 134 */ 135 protected static final String ID_BOUNDINGBOX = PROPERTY_BOUNDINGBOX; 136 protected static final String ID_BOUNDINGBOX_GRID = PROPERTY_BOUNDINGBOX_GRID; 137 protected static final String ID_BOUNDINGBOX_RULES = PROPERTY_BOUNDINGBOX_RULES; 138 protected static final String ID_BOUNDINGBOX_LABELS = PROPERTY_BOUNDINGBOX_LABELS; 139 // protected static final String ID_PICKONMOUSEMOVE = "pickOnMouseMove"; 140 protected static final String ID_AXES = PROPERTY_AXES; 141 protected static final String ID_SHADING = VtkSettingPanel.PROPERTY_SHADING; 142 protected static final String ID_BGCOLOR = VtkSettingPanel.PROPERTY_BG_COLOR; 143 protected static final String ID_MAPPER = VtkSettingPanel.PROPERTY_MAPPER; 144 protected static final String ID_SAMPLE = VtkSettingPanel.PROPERTY_SAMPLE; 145 protected static final String ID_BLENDING = VtkSettingPanel.PROPERTY_BLENDING; 146 protected static final String ID_INTERPOLATION = VtkSettingPanel.PROPERTY_INTERPOLATION; 147 protected static final String ID_AMBIENT = VtkSettingPanel.PROPERTY_AMBIENT; 148 protected static final String ID_DIFFUSE = VtkSettingPanel.PROPERTY_DIFFUSE; 149 protected static final String ID_SPECULAR = VtkSettingPanel.PROPERTY_SPECULAR; 150 151 /** 152 * basic vtk objects 153 */ 154 protected vtkRenderer renderer; 155 protected vtkRenderWindow renderWindow; 156 protected vtkCamera camera; 157 // protected vtkAxesActor axes; 158 protected vtkCubeAxesActor boundingBox; 159 protected vtkCubeAxesActor rulerBox; 160 protected vtkTextActor textInfo; 161 protected vtkTextProperty textProperty; 162 // protected vtkOrientationMarkerWidget widget; 163 protected vtkProp pickedObject; 164 165 /** 166 * volume data 167 */ 168 protected VtkImageVolume imageVolume; 169 170 /** 171 * GUI 172 */ 173 protected VtkSettingPanel settingPanel; 174 protected CustomVtkPanel panel3D; 175 protected IcyToggleButton axesButton; 176 protected IcyToggleButton boundingBoxButton; 177 protected IcyToggleButton gridButton; 178 protected IcyToggleButton rulerButton; 179 protected IcyToggleButton rulerLabelButton; 180 // protected IcyToggleButton pickOnMouseMoveButton; 181 182 /** 183 * internals 184 */ 185 protected PropertiesUpdater propertiesUpdater; 186 protected VtkOverlayUpdater overlayUpdater; 187 protected XMLPreferences preferences; 188 protected final EDTTask<Object> edtTask; 189 protected boolean initialized; 190 191 public VtkCanvas(Viewer viewer) 192 { 193 super(viewer); 194 195 initialized = false; 196 197 // more than 4 channels ? --> not supported by VTK 198 if (getImageSizeC() > 4) 199 throw new UnsupportedOperationException( 200 "VTK does not support image with more than 4 channels !\nYou should remove some channels in your image."); 201 202 // multi channel view 203 posC = -1; 204 // adjust LUT alpha level for 3D view 205 lut.setAlphaToLinear3D(); 206 207 pickedObject = null; 208 209 // create the properties and the VTK overlay updater processors 210 propertiesUpdater = new PropertiesUpdater(); 211 overlayUpdater = new VtkOverlayUpdater(); 212 213 preferences = CanvasPreferences.getPreferences().node(PREF_ID); 214 215 settingPanel = new VtkSettingPanel(); 216 panel = settingPanel; 217 218 // initialize VTK components & main GUI 219 panel3D = new CustomVtkPanel(); 220 panel3D.addKeyListener(this); 221 // set 3D view in center 222 add(panel3D, BorderLayout.CENTER); 223 224 // update nav bar & mouse infos 225 mouseInfPanel.setVisible(false); 226 updateZNav(); 227 updateTNav(); 228 229 // create toolbar buttons 230 axesButton = new IcyToggleButton(new IcyIcon(ICON_AXES3D)); 231 axesButton.setFocusable(false); 232 axesButton.setToolTipText("Display 3D axis"); 233 boundingBoxButton = new IcyToggleButton(new IcyIcon(ICON_BOUNDINGBOX)); 234 boundingBoxButton.setFocusable(false); 235 boundingBoxButton.setToolTipText("Display bounding box"); 236 gridButton = new IcyToggleButton(new IcyIcon(ICON_GRID)); 237 gridButton.setFocusable(false); 238 gridButton.setToolTipText("Display grid"); 239 rulerButton = new IcyToggleButton(new IcyIcon(ICON_RULER)); 240 rulerButton.setFocusable(false); 241 rulerButton.setToolTipText("Display rules"); 242 rulerLabelButton = new IcyToggleButton(new IcyIcon(ICON_RULERLABEL)); 243 rulerLabelButton.setFocusable(false); 244 rulerLabelButton.setToolTipText("Display rules label"); 245 // pickOnMouseMoveButton = new IcyToggleButton(new IcyIcon(ICON_TARGET)); 246 // pickOnMouseMoveButton.setFocusable(false); 247 // pickOnMouseMoveButton.setToolTipText("Enabled object focus on mouse over (slow)"); 248 249 // set fast rendering during initialization 250 panel3D.setCoarseRendering(1000); 251 252 renderer = panel3D.getRenderer(); 253 renderWindow = panel3D.getRenderWindow(); 254 camera = renderer.GetActiveCamera(); 255 256 // initialize text info actor (need to be done before the first getImageData() call ! 257 textInfo = new vtkTextActor(); 258 textInfo.SetInput("Not enough memory to display this 3D image !"); 259 textInfo.SetPosition(10, 10); 260 // not visible by default 261 textInfo.SetVisibility(0); 262 263 // change text properties 264 textProperty = textInfo.GetTextProperty(); 265 textProperty.SetFontFamilyToArial(); 266 267 // rebuild volume image 268 updateImageData(getImageData()); 269 270 final Sequence seq = getSequence(); 271 // setup volume scaling 272 if (seq != null) 273 imageVolume.setScale(seq.getPixelSizeX(), seq.getPixelSizeY(), seq.getPixelSizeZ()); 274 // setup volume LUT 275 imageVolume.setLUT(getLut()); 276 277 // initialize axe 278 // axes = new vtkAxesActor(); 279 // widget = new vtkOrientationMarkerWidget(); 280 // widget.SetOrientationMarker(axes); 281 // widget.SetInteractor(interactor); 282 // widget.SetViewport(0, 0, 0.3, 0.3); 283 // widget.SetEnabled(1); 284 285 // initialize bounding box 286 boundingBox = new vtkCubeAxesActor(); 287 boundingBox.SetBounds(imageVolume.getVolume().GetBounds()); 288 boundingBox.SetCamera(camera); 289 // set bounding box labels properties 290 boundingBox.SetFlyModeToStaticEdges(); 291 boundingBox.SetUseBounds(true); 292 boundingBox.XAxisLabelVisibilityOff(); 293 boundingBox.XAxisMinorTickVisibilityOff(); 294 boundingBox.XAxisTickVisibilityOff(); 295 boundingBox.YAxisLabelVisibilityOff(); 296 boundingBox.YAxisMinorTickVisibilityOff(); 297 boundingBox.YAxisTickVisibilityOff(); 298 boundingBox.ZAxisLabelVisibilityOff(); 299 boundingBox.ZAxisMinorTickVisibilityOff(); 300 boundingBox.ZAxisTickVisibilityOff(); 301 302 // initialize rules and box axis 303 rulerBox = new vtkCubeAxesActor(); 304 rulerBox.SetBounds(imageVolume.getVolume().GetBounds()); 305 rulerBox.SetCamera(camera); 306 307 // set bounding box labels properties 308 rulerBox.SetXUnits("micro meter"); 309 rulerBox.GetTitleTextProperty(0).SetColor(1.0, 0.0, 0.0); 310 rulerBox.GetLabelTextProperty(0).SetColor(1.0, 0.0, 0.0); 311 rulerBox.GetXAxesGridlinesProperty().SetColor(1.0, 0.0, 0.0); 312 rulerBox.GetXAxesGridpolysProperty().SetColor(1.0, 0.0, 0.0); 313 rulerBox.GetXAxesInnerGridlinesProperty().SetColor(1.0, 0.0, 0.0); 314 315 rulerBox.SetYUnits("micro meter"); 316 rulerBox.GetTitleTextProperty(1).SetColor(0.0, 1.0, 0.0); 317 rulerBox.GetLabelTextProperty(1).SetColor(0.0, 1.0, 0.0); 318 rulerBox.GetYAxesGridlinesProperty().SetColor(0.0, 1.0, 0.0); 319 rulerBox.GetYAxesGridpolysProperty().SetColor(0.0, 1.0, 0.0); 320 rulerBox.GetYAxesInnerGridlinesProperty().SetColor(0.0, 1.0, 0.0); 321 322 rulerBox.SetZUnits("micro meter"); 323 rulerBox.GetTitleTextProperty(2).SetColor(0.0, 0.0, 1.0); 324 rulerBox.GetLabelTextProperty(2).SetColor(0.0, 0.0, 1.0); 325 rulerBox.GetZAxesGridlinesProperty().SetColor(0.0, 0.0, 1.0); 326 rulerBox.GetZAxesGridpolysProperty().SetColor(0.0, 0.0, 1.0); 327 rulerBox.GetZAxesInnerGridlinesProperty().SetColor(0.0, 0.0, 1.0); 328 329 rulerBox.XAxisVisibilityOff(); 330 rulerBox.YAxisVisibilityOff(); 331 rulerBox.ZAxisVisibilityOff(); 332 333 rulerBox.SetGridLineLocation(VtkUtil.VTK_GRID_LINES_FURTHEST); 334 rulerBox.SetFlyModeToOuterEdges(); 335 rulerBox.SetUseBounds(true); 336 337 // restore settings 338 settingPanel.setBackgroundColor(new Color(preferences.getInt(ID_BGCOLOR, 0x000000))); 339 settingPanel.setVolumeBlendingMode( 340 VtkVolumeBlendType.values()[preferences.getInt(ID_BLENDING, VtkVolumeBlendType.COMPOSITE.ordinal())]); 341 342 // volume mapper 343 settingPanel.setGPURendering(preferences.getInt(ID_MAPPER, 0) != 0); 344 settingPanel.setVolumeInterpolation(preferences.getInt(ID_INTERPOLATION, VtkUtil.VTK_LINEAR_INTERPOLATION)); 345 settingPanel.setVolumeSample(preferences.getInt(ID_SAMPLE, 0)); 346 settingPanel.setVolumeAmbient(preferences.getDouble(ID_AMBIENT, 0.2d)); 347 settingPanel.setVolumeDiffuse(preferences.getDouble(ID_DIFFUSE, 0.4d)); 348 settingPanel.setVolumeSpecular(preferences.getDouble(ID_SPECULAR, 0.4d)); 349 settingPanel.setVolumeShading(preferences.getBoolean(ID_SHADING, false)); 350 axesButton.setSelected(preferences.getBoolean(ID_AXES, true)); 351 boundingBoxButton.setSelected(preferences.getBoolean(ID_BOUNDINGBOX, true)); 352 gridButton.setSelected(preferences.getBoolean(ID_BOUNDINGBOX_GRID, true)); 353 rulerButton.setSelected(preferences.getBoolean(ID_BOUNDINGBOX_RULES, false)); 354 rulerLabelButton.setSelected(preferences.getBoolean(ID_BOUNDINGBOX_LABELS, false)); 355 // pickOnMouseMoveButton.setSelected(preferences.getBoolean(ID_PICKONMOUSEMOVE, false)); 356 // always false by default (preferable) 357 // pickOnMouseMoveButton.setSelected(false); 358 359 // apply restored settings 360 setBackgroundColorInternal(settingPanel.getBackgroundColor()); 361 imageVolume.setBlendingMode(settingPanel.getVolumeBlendingMode()); 362 imageVolume.setGPURendering(settingPanel.getGPURendering()); 363 // mapper may change blending mode 364 settingPanel.setVolumeBlendingMode(imageVolume.getBlendingMode()); 365 imageVolume.setInterpolationMode(settingPanel.getVolumeInterpolation()); 366 imageVolume.setSampleResolution(settingPanel.getVolumeSample()); 367 imageVolume.setAmbient(settingPanel.getVolumeAmbient()); 368 imageVolume.setDiffuse(settingPanel.getVolumeDiffuse()); 369 imageVolume.setSpecular(settingPanel.getVolumeSpecular()); 370 imageVolume.setShade(settingPanel.getVolumeShading()); 371 // axes.SetVisibility(axesButton.isSelected() ? 1 : 0); 372 boundingBox.SetVisibility(boundingBoxButton.isSelected() ? 1 : 0); 373 rulerBox.SetDrawXGridlines(gridButton.isSelected() ? 1 : 0); 374 rulerBox.SetDrawYGridlines(gridButton.isSelected() ? 1 : 0); 375 rulerBox.SetDrawZGridlines(gridButton.isSelected() ? 1 : 0); 376 rulerBox.SetXAxisTickVisibility(rulerButton.isSelected() ? 1 : 0); 377 rulerBox.SetXAxisMinorTickVisibility(rulerButton.isSelected() ? 1 : 0); 378 rulerBox.SetYAxisTickVisibility(rulerButton.isSelected() ? 1 : 0); 379 rulerBox.SetYAxisMinorTickVisibility(rulerButton.isSelected() ? 1 : 0); 380 rulerBox.SetZAxisTickVisibility(rulerButton.isSelected() ? 1 : 0); 381 rulerBox.SetZAxisMinorTickVisibility(rulerButton.isSelected() ? 1 : 0); 382 rulerBox.SetXAxisLabelVisibility(rulerLabelButton.isSelected() ? 1 : 0); 383 rulerBox.SetYAxisLabelVisibility(rulerLabelButton.isSelected() ? 1 : 0); 384 rulerBox.SetZAxisLabelVisibility(rulerLabelButton.isSelected() ? 1 : 0); 385 // setPickOnMouseMove(pickOnMouseMoveButton.isSelected()); 386 387 // add volume to renderer 388 renderer.AddVolume(imageVolume.getVolume()); 389 // add bounding box & ruler 390 renderer.AddViewProp(boundingBox); 391 renderer.AddViewProp(rulerBox); 392 renderer.AddViewProp(textInfo); 393 394 // reset camera 395 resetCamera(); 396 // apply lut depending channel configuration 397 updateLut(); 398 399 // we can now listen for setting changes 400 settingPanel.addSettingChangeListener(this); 401 axesButton.addActionListener(this); 402 boundingBoxButton.addActionListener(this); 403 gridButton.addActionListener(this); 404 rulerButton.addActionListener(this); 405 rulerLabelButton.addActionListener(this); 406 // pickOnMouseMoveButton.addActionListener(this); 407 408 // create EDTTask object 409 edtTask = new EDTTask<Object>(); 410 // start the properties and VTK overlay updater processors 411 propertiesUpdater.start(); 412 overlayUpdater.start(); 413 414 // initialized ! 415 initialized = true; 416 417 // add layers actors 418 overlayUpdater.addProps(VtkUtil.getLayersProps(getLayers(false))); 419 } 420 421 @Override 422 public void shutDown() 423 { 424 final long st = System.currentTimeMillis(); 425 // wait for initialization to complete before shutdown (max 5s) 426 while (((System.currentTimeMillis() - st) < 5000L) && !initialized) 427 ThreadUtil.sleep(1); 428 429 propertiesUpdater.interrupt(); 430 try 431 { 432 // be sure there is no more processing here 433 propertiesUpdater.join(); 434 } 435 catch (InterruptedException e) 436 { 437 // can ignore safely 438 } 439 overlayUpdater.interrupt(); 440 try 441 { 442 // be sure there is no more processing here 443 overlayUpdater.join(); 444 } 445 catch (InterruptedException e) 446 { 447 // can ignore safely 448 } 449 450 // no more initialized (prevent extra useless processing) 451 initialized = false; 452 propertiesUpdater = null; 453 overlayUpdater = null; 454 455 // VTK stuff in EDT 456 invokeOnEDTSilent(new Runnable() 457 { 458 @Override 459 public void run() 460 { 461 renderer.RemoveAllViewProps(); 462 // renderer.Delete(); 463 // renderWindow.Delete(); 464 imageVolume.release(); 465 // widget.Delete(); 466 // axes.Delete(); 467 boundingBox.Delete(); 468 // camera.Delete(); 469 470 // dispose extra panel 3D stuff 471 panel3D.disposeInternal(); 472 } 473 }); 474 475 // AWTMultiCaster of vtkPanel keep reference of this frame so 476 // we have to release as most stuff we can 477 removeAll(); 478 panel.removeAll(); 479 480 renderer = null; 481 renderWindow = null; 482 imageVolume = null; 483 // widget = null; 484 // axes = null; 485 boundingBox = null; 486 camera = null; 487 488 panel3D.removeKeyListener(this); 489 panel3D = null; 490 panel = null; 491 492 // do parent shutdown now 493 super.shutDown(); 494 495 // call VTK GC: better if we can avoid this ! 496 // vtkObjectBase.JAVA_OBJECT_MANAGER.gc(false); 497 } 498 499 /** 500 * Returns initialized state of VtkCanvas 501 */ 502 public boolean isInitialized() 503 { 504 return initialized; 505 } 506 507 @Override 508 public void customizeToolbar(JToolBar toolBar) 509 { 510 toolBar.addSeparator(); 511 toolBar.add(axesButton); 512 toolBar.addSeparator(); 513 toolBar.add(boundingBoxButton); 514 toolBar.add(gridButton); 515 toolBar.add(rulerButton); 516 toolBar.add(rulerLabelButton); 517 // toolBar.addSeparator(); 518 // toolBar.add(pickOnMouseMoveButton); 519 } 520 521 @Override 522 protected Overlay createImageOverlay() 523 { 524 return new VtkCanvasImageOverlay(); 525 } 526 527 /** 528 * Request exclusive access to VTK rendering.<br> 529 * 530 * @deprecated Use <code>getVtkPanel().lock()</code> instead. 531 */ 532 @Deprecated 533 public void lock() 534 { 535 if (panel3D != null) 536 panel3D.lock(); 537 } 538 539 /** 540 * Release exclusive access from VTK rendering. 541 * 542 * @deprecated Use <code>getVtkPanel().unlock()</code> instead. 543 */ 544 @Deprecated 545 public void unlock() 546 { 547 if (panel3D != null) 548 panel3D.unlock(); 549 } 550 551 /** 552 * @deprecated Use {@link #getCamera()} instead 553 */ 554 @Deprecated 555 public vtkCamera getActiveCam() 556 { 557 return getCamera(); 558 } 559 560 /** 561 * @return the VTK scene camera object 562 */ 563 public vtkCamera getCamera() 564 { 565 return camera; 566 } 567 568 /** 569 * @return the VTK default scene light object.<br> 570 * Can be <code>null</code> if render window is not yet initialized. 571 */ 572 public vtkLight getLight() 573 { 574 return renderer.GetLights().GetNextItem(); 575 } 576 577 /** 578 * @return the VTK axes object 579 */ 580 public vtkAxesActor getAxes() 581 { 582 return panel3D.getAxesActor(); 583 } 584 585 /** 586 * @return the VTK bounding box object 587 */ 588 public vtkCubeAxesActor getBoundingBox() 589 { 590 return boundingBox; 591 } 592 593 /** 594 * @return the VTK ruler box object 595 */ 596 public vtkCubeAxesActor getRulerBox() 597 { 598 return rulerBox; 599 } 600 601 /** 602 * @deprecated there is no more orientation widget because of the jogl bug with multiple viewport. 603 */ 604 @Deprecated 605 public vtkOrientationMarkerWidget getWidget() 606 { 607 return null; 608 // return widget; 609 } 610 611 /** 612 * @return the VTK image volume object 613 */ 614 public VtkImageVolume getImageVolume() 615 { 616 return imageVolume; 617 } 618 619 /** 620 * Returns rendering background color 621 */ 622 public Color getBackgroundColor() 623 { 624 return settingPanel.getBackgroundColor(); 625 } 626 627 /** 628 * Sets rendering background color 629 */ 630 public void setBackgroundColor(Color value) 631 { 632 settingPanel.setBackgroundColor(value); 633 } 634 635 /** 636 * Returns <code>true</code> if the volume bounding box is visible. 637 */ 638 public boolean isBoundingBoxVisible() 639 { 640 return boundingBoxButton.isSelected(); 641 } 642 643 /** 644 * Enable / disable volume bounding box display. 645 */ 646 public void setBoundingBoxVisible(boolean value) 647 { 648 if (boundingBoxButton.isSelected() != value) 649 boundingBoxButton.doClick(); 650 } 651 652 /** 653 * Returns <code>true</code> if the volume bounding box grid is visible. 654 */ 655 public boolean isBoundingBoxGridVisible() 656 { 657 return gridButton.isSelected(); 658 } 659 660 /** 661 * Enable / disable volume bounding box grid display. 662 */ 663 public void setBoundingBoxGridVisible(boolean value) 664 { 665 if (gridButton.isSelected() != value) 666 gridButton.doClick(); 667 } 668 669 /** 670 * Returns <code>true</code> if the volume bounding box ruler are visible. 671 */ 672 public boolean isBoundingBoxRulerVisible() 673 { 674 return rulerButton.isSelected(); 675 } 676 677 /** 678 * Enable / disable volume bounding box ruler display. 679 */ 680 public void setBoundingBoxRulerVisible(boolean value) 681 { 682 if (rulerButton.isSelected() != value) 683 rulerButton.doClick(); 684 } 685 686 /** 687 * Returns <code>true</code> if the volume bounding box ruler labels are visible. 688 */ 689 public boolean isBoundingBoxRulerLabelsVisible() 690 { 691 return rulerLabelButton.isSelected(); 692 } 693 694 /** 695 * Enable / disable volume bounding box ruler labels display. 696 */ 697 public void setBoundingBoxRulerLabelsVisible(boolean value) 698 { 699 if (rulerLabelButton.isSelected() != value) 700 rulerLabelButton.doClick(); 701 } 702 703 /** 704 * @deprecated USe {@link #setBackgroundColorInternal(Color)} 705 */ 706 @Deprecated 707 public void setBoundingBoxColor(Color color) 708 { 709 setBackgroundColorInternal(color); 710 } 711 712 /** 713 * Set background color (internal) 714 */ 715 public void setBackgroundColorInternal(Color color) 716 { 717 renderer.SetBackground(Array1DUtil.floatArrayToDoubleArray(color.getColorComponents(null))); 718 719 final Color oppositeColor; 720 721 // adjust bounding box color 722 if (ColorUtil.getLuminance(color) > 128) 723 oppositeColor = Color.black; 724 else 725 oppositeColor = Color.white; 726 727 final float[] comp = oppositeColor.getRGBColorComponents(null); 728 729 final float r = comp[0]; 730 final float g = comp[0]; 731 final float b = comp[0]; 732 733 boundingBox.GetXAxesLinesProperty().SetColor(r, g, b); 734 boundingBox.GetYAxesLinesProperty().SetColor(r, g, b); 735 boundingBox.GetZAxesLinesProperty().SetColor(r, g, b); 736 737 rulerBox.GetXAxesGridlinesProperty().SetColor(r, g, b); 738 rulerBox.GetXAxesGridpolysProperty().SetColor(r, g, b); 739 rulerBox.GetXAxesInnerGridlinesProperty().SetColor(r, g, b); 740 rulerBox.GetXAxesLinesProperty().SetColor(r, g, b); 741 742 rulerBox.GetYAxesGridlinesProperty().SetColor(r, g, b); 743 rulerBox.GetYAxesGridpolysProperty().SetColor(r, g, b); 744 rulerBox.GetYAxesInnerGridlinesProperty().SetColor(r, g, b); 745 rulerBox.GetYAxesLinesProperty().SetColor(r, g, b); 746 747 rulerBox.GetZAxesGridlinesProperty().SetColor(r, g, b); 748 rulerBox.GetZAxesGridpolysProperty().SetColor(r, g, b); 749 rulerBox.GetZAxesInnerGridlinesProperty().SetColor(r, g, b); 750 rulerBox.GetZAxesLinesProperty().SetColor(r, g, b); 751 752 textProperty.SetColor(r, g, b); 753 } 754 755 /** 756 * Returns <code>true</code> if the 3D axis are visible. 757 */ 758 public boolean isAxisVisible() 759 { 760 return axesButton.isSelected(); 761 } 762 763 /** 764 * Enable / disable 3D axis display. 765 */ 766 public void setAxisVisible(boolean value) 767 { 768 if (axesButton.isSelected() != value) 769 axesButton.doClick(); 770 } 771 772 /** 773 * @see VtkImageVolume#getBlendingMode() 774 */ 775 public VtkVolumeBlendType getVolumeBlendingMode() 776 { 777 return settingPanel.getVolumeBlendingMode(); 778 } 779 780 /** 781 * @see VtkImageVolume#setBlendingMode(VtkVolumeBlendType) 782 */ 783 public void setVolumeBlendingMode(VtkVolumeBlendType value) 784 { 785 settingPanel.setVolumeBlendingMode(value); 786 } 787 788 /** 789 * @see VtkImageVolume#getSampleResolution() 790 */ 791 public int getVolumeSample() 792 { 793 return settingPanel.getVolumeSample(); 794 } 795 796 /** 797 * @see VtkImageVolume#setSampleResolution(double) 798 */ 799 public void setVolumeSample(int value) 800 { 801 settingPanel.setVolumeSample(value); 802 } 803 804 /** 805 * @see VtkImageVolume#getShade() 806 */ 807 public boolean isVolumeShadingEnable() 808 { 809 return settingPanel.getVolumeShading(); 810 } 811 812 /** 813 * @see VtkImageVolume#setShade(boolean) 814 */ 815 public void setVolumeShadingEnable(boolean value) 816 { 817 settingPanel.setVolumeShading(value); 818 } 819 820 /** 821 * @see VtkImageVolume#getAmbient() 822 */ 823 public double getVolumeAmbient() 824 { 825 return settingPanel.getVolumeAmbient(); 826 } 827 828 /** 829 * @see VtkImageVolume#setAmbient(double) 830 */ 831 public void setVolumeAmbient(double value) 832 { 833 settingPanel.setVolumeAmbient(value); 834 } 835 836 /** 837 * @see VtkImageVolume#getDiffuse() 838 */ 839 public double getVolumeDiffuse() 840 { 841 return settingPanel.getVolumeDiffuse(); 842 } 843 844 /** 845 * @see VtkImageVolume#setDiffuse(double) 846 */ 847 public void setVolumeDiffuse(double value) 848 { 849 settingPanel.setVolumeDiffuse(value); 850 } 851 852 /** 853 * @see VtkImageVolume#getSpecular() 854 */ 855 public double getVolumeSpecular() 856 { 857 return settingPanel.getVolumeSpecular(); 858 } 859 860 /** 861 * @see VtkImageVolume#setSpecular(double) 862 */ 863 public void setVolumeSpecular(double value) 864 { 865 settingPanel.setVolumeSpecular(value); 866 } 867 868 /** 869 * @see VtkImageVolume#getInterpolationMode() 870 */ 871 public int getVolumeInterpolation() 872 { 873 return settingPanel.getVolumeInterpolation(); 874 } 875 876 /** 877 * @see VtkImageVolume#setInterpolationMode(int) 878 */ 879 public void setVolumeInterpolation(int value) 880 { 881 settingPanel.setVolumeInterpolation(value); 882 } 883 884 /** 885 * @see VtkImageVolume#getGPURendering() 886 */ 887 public boolean getGPURendering() 888 { 889 return settingPanel.getGPURendering(); 890 } 891 892 /** 893 * @see VtkImageVolume#setGPURendering(boolean) 894 */ 895 public void setGPURendering(boolean value) 896 { 897 settingPanel.setGPURendering(value); 898 } 899 900 /** 901 * @deprecated Use {@link #getGPURendering()} instead 902 */ 903 @Deprecated 904 public VtkVolumeMapperType getVolumeMapperType() 905 { 906 if (getGPURendering()) 907 return VtkVolumeMapperType.RAYCAST_GPU_OPENGL; 908 909 return VtkVolumeMapperType.RAYCAST_CPU_FIXEDPOINT; 910 } 911 912 /** 913 * @deprecated Use {@link #setGPURendering(boolean)} instead 914 */ 915 @Deprecated 916 public void setVolumeMapperType(VtkVolumeMapperType value) 917 { 918 setGPURendering(VtkVolumeMapperType.RAYCAST_GPU_OPENGL.equals(value)); 919 } 920 921 /** 922 * @return visible state of the image volume object 923 * @see VtkImageVolume#isVisible() 924 */ 925 public boolean isVolumeVisible() 926 { 927 return imageVolume.isVisible(); 928 } 929 930 /** 931 * Sets the visible state of the image volume object 932 * 933 * @see VtkImageVolume#setVisible(boolean) 934 */ 935 public void setVolumeVisible(boolean value) 936 { 937 imageVolume.setVisible(value); 938 } 939 940 /** 941 * Force render refresh 942 */ 943 @Override 944 public void refresh() 945 { 946 if (!initialized) 947 return; 948 949 // refresh rendering 950 if (panel3D != null) 951 panel3D.repaint(); 952 } 953 954 // private void test() 955 // { 956 // // exemple de clipping a utiliser par la suite. 957 // if ( false ) 958 // { 959 // vtkPlane plane = new vtkPlane(); 960 // plane.SetOrigin(1000, 1000, 1000); 961 // plane.SetNormal( 1, 1, 0); 962 // volumeMapper.AddClippingPlane( plane ); 963 // } 964 // 965 // vtkOrientationMarkerWidget ow = new vtkOrientationMarkerWidget(); 966 // } 967 968 protected void resetCamera() 969 { 970 camera.SetViewUp(0, -1, 0); 971 renderer.ResetCamera(); 972 camera.Elevation(200); 973 renderer.ResetCameraClippingRange(); 974 } 975 976 /** 977 * @deprecated Use {@link #setVolumeSample(int)} instead 978 */ 979 @Deprecated 980 @Override 981 public void setVolumeDistanceSample(int value) 982 { 983 setVolumeSample(value); 984 } 985 986 // /** 987 // * Returns channel position based on enabled channel in LUT 988 // */ 989 // protected int getChannelPos() 990 // { 991 // final LUT lut = getLut(); 992 // int result = -1; 993 // 994 // for (int c = 0; c < lut.getNumChannel(); c++) 995 // { 996 // final LUTChannel lutChannel = lut.getLutChannel(c); 997 // 998 // if (lutChannel.isEnabled()) 999 // { 1000 // if (result == -1) 1001 // result = c; 1002 // else 1003 // return -1; 1004 // } 1005 // } 1006 // 1007 // return result; 1008 // } 1009 1010 /** 1011 * @deprecated Always enabled now (always return <code>true</code>) 1012 */ 1013 @Deprecated 1014 public boolean getPickOnMouseMove() 1015 { 1016 return true; 1017 // return pickOnMouseMoveButton.isSelected(); 1018 } 1019 1020 /** 1021 * @deprecated Always enable now 1022 */ 1023 @Deprecated 1024 public void setPickOnMouseMove(boolean value) 1025 { 1026 // if (pickOnMouseMoveButton.isSelected() != value) 1027 // pickOnMouseMoveButton.doClick(); 1028 } 1029 1030 /** 1031 * Returns the picked object on the last mouse move/drag event (can be <code>null</code> if no object was picked). 1032 * 1033 * @see #pickProp(int, int) 1034 */ 1035 public vtkProp getPickedObject() 1036 { 1037 return pickedObject; 1038 } 1039 1040 /** 1041 * @deprecated use {@link #pickProp(int, int)} instead. 1042 */ 1043 @Deprecated 1044 public vtkActor pick(int x, int y) 1045 { 1046 return (vtkActor) panel3D.pick(x, y); 1047 } 1048 1049 /** 1050 * Pick object at specified position and return it. 1051 * 1052 * @see #getPickedObject() 1053 * @see icy.vtk.IcyVtkPanel#pick(int, int) 1054 */ 1055 public vtkProp pickProp(int x, int y) 1056 { 1057 return panel3D.pick(x, y); 1058 } 1059 1060 /** 1061 * Return reached z world position (normalized) for specified display position 1062 */ 1063 public double getWorldZ(int x, int y) 1064 { 1065 final vtkRenderer r = getRenderer(); 1066 final vtkRenderWindow rw = getRenderWindow(); 1067 1068 if ((r == null) || (rw == null)) 1069 return 0d; 1070 1071 // need to revert Y axis 1072 return r.GetZ(x, rw.GetSize()[1] - y); 1073 } 1074 1075 public double getWorldZ(Point pt) 1076 { 1077 return getWorldZ(pt.x, pt.y); 1078 } 1079 1080 /** 1081 * Convert world coordinates to display coordinates 1082 */ 1083 public Point3D worldToDisplay(Point3D pt) 1084 { 1085 if (pt == null) 1086 return new Point3D.Double(); 1087 1088 return worldToDisplay(pt.getX(), pt.getY(), pt.getZ()); 1089 } 1090 1091 /** 1092 * Convert world coordinates to display coordinates 1093 */ 1094 public Point3D worldToDisplay(double x, double y, double z) 1095 { 1096 final vtkRenderer r = getRenderer(); 1097 final vtkRenderWindow rw = getRenderWindow(); 1098 1099 if ((r == null) || (rw == null)) 1100 return new Point3D.Double(); 1101 1102 r.SetWorldPoint(x, y, z, 1d); 1103 r.WorldToDisplay(); 1104 final Point3D result = new Point3D.Double(r.GetDisplayPoint()); 1105 1106 // need to revert Y axis 1107 result.setY(rw.GetSize()[1] - result.getY()); 1108 1109 return result; 1110 } 1111 1112 /** 1113 * Convert display coordinates to world coordinates. 1114 */ 1115 public Point3D displayToWorld(Point pt) 1116 { 1117 if (pt == null) 1118 return new Point3D.Double(); 1119 1120 return displayToWorld(pt.x, pt.y); 1121 } 1122 1123 /** 1124 * Convert display coordinates to world coordinates.<br> 1125 */ 1126 public Point3D displayToWorld(int x, int y) 1127 { 1128 // get camera focal point 1129 final double[] fp = camera.GetFocalPoint(); 1130 // transform it to display position (with Z info) 1131 final Point3D displayFP = worldToDisplay(fp[0], fp[1], fp[2]); 1132 // keep the Z info from focal point 1133 return displayToWorld(x, y, displayFP.getZ()); 1134 // return displayToWorld(x, y, getWorldZ(x, y)); 1135 } 1136 1137 /** 1138 * Convert display coordinates to world coordinates.<br> 1139 * Default value for Z should be 0d 1140 */ 1141 public Point3D displayToWorld(Point pt, double z) 1142 { 1143 if (pt == null) 1144 return new Point3D.Double(); 1145 1146 return displayToWorld(pt.getX(), pt.getY(), z); 1147 } 1148 1149 /** 1150 * Convert display coordinates to world coordinates.<br> 1151 * Default value for Z is 0d 1152 */ 1153 public Point3D displayToWorld(double x, double y, double z) 1154 { 1155 final vtkRenderer r = getRenderer(); 1156 final vtkRenderWindow rw = getRenderWindow(); 1157 1158 if ((r == null) || (rw == null)) 1159 return new Point3D.Double(); 1160 1161 // need to revert Y axis 1162 r.SetDisplayPoint(x, rw.GetSize()[1] - y, z); 1163 r.DisplayToWorld(); 1164 final double[] result = r.GetWorldPoint(); 1165 1166 // final vtkPicker picker = getPicker(); 1167 // pickProp((int)x, (int)y); 1168 // // picker.Pick(x, rw.GetSize()[1] - y, 0, r); 1169 // double[] pos = picker.GetPickPosition(); 1170 // 1171 // System.out.println("displayToWorld(" + x + ", " + y + ", " + z + "):"); 1172 // System.out.println(String.format("%.5g, %.5g, %.5g", result[0], result[1], result[2])); 1173 // System.out.println(String.format("from Pick: %.5g, %.5g, %.5g", pos[0], pos[1], pos[2])); 1174 1175 // normalize 1176 if (result[3] != 0d) 1177 { 1178 result[0] /= result[3]; 1179 result[1] /= result[3]; 1180 result[2] /= result[3]; 1181 } 1182 else 1183 { 1184 result[0] = 0d; 1185 result[1] = 0d; 1186 result[2] = 0d; 1187 } 1188 1189 return new Point3D.Double(result[0], result[1], result[2]); 1190 } 1191 1192 @Override 1193 public Point imageToCanvas(double x, double y, double z) 1194 { 1195 final double[] scaling = getVolumeScale(); 1196 final Point3D result = worldToDisplay(x * scaling[0], y * scaling[1], z * scaling[2]); 1197 1198 // System.out.println("imageToCanvas(" + x + ", " + y + ", " + z + "): " + result); 1199 1200 // ignore Z coordinate 1201 return new Point((int) result.getX(), (int) result.getY()); 1202 } 1203 1204 @Override 1205 public Point3D.Double canvasToImage(int x, int y) 1206 { 1207 final double[] scaling = getVolumeScale(); 1208 1209 // check scaling does not contains any 0 1210 for (double d : scaling) 1211 { 1212 if (d == 0d) 1213 return new Point3D.Double(); 1214 } 1215 1216 // get image position in 3D 1217 Point3D result = displayToWorld(x, y); 1218 1219 // get the view axis 1220 final double[] directionOfProjection = getCamera().GetDirectionOfProjection(); 1221 final double dirX = Math.abs(directionOfProjection[0]); 1222 final double dirY = Math.abs(directionOfProjection[1]); 1223 final double dirZ = Math.abs(directionOfProjection[2]); 1224 1225 // we always want to have 2D coordinates cancel position which is not "visible" axis 1226 if (dirX > dirY) 1227 { 1228 if (dirX > dirZ) 1229 result.setX(Double.NaN); 1230 else 1231 result.setZ(Double.NaN); 1232 } 1233 else 1234 { 1235 if (dirY > dirZ) 1236 result.setY(Double.NaN); 1237 else 1238 result.setZ(Double.NaN); 1239 } 1240 1241 result = new Point3D.Double(result.getX() / scaling[0], result.getY() / scaling[1], result.getZ() / scaling[2]); 1242 1243 // System.out.println("canvasToImage(" + x + ", " + y + "): " 1244 // + String.format("%.5g, %.5g, %.5g", result.getX(), result.getY(), result.getZ())); 1245 1246 return (Point3D.Double) result; 1247 } 1248 1249 /** 1250 * @deprecated Use {@link VtkUtil#getLayerProps(Layer)} instead. 1251 */ 1252 @Deprecated 1253 protected vtkProp[] getLayerActors(Layer layer) 1254 { 1255 return VtkUtil.getLayerProps(layer); 1256 } 1257 1258 protected void addLayerActors(Layer layer) 1259 { 1260 // not yet (or no more) initialized 1261 if (overlayUpdater == null) 1262 return; 1263 1264 overlayUpdater.addProps(VtkUtil.getLayerProps(layer)); 1265 } 1266 1267 protected void removeLayerActors(Layer layer) 1268 { 1269 // not yet (or no more) initialized 1270 if (overlayUpdater == null) 1271 return; 1272 1273 overlayUpdater.removeProps(VtkUtil.getLayerProps(layer)); 1274 } 1275 1276 protected void addLayersActors(List<Layer> layers) 1277 { 1278 // not yet (or no more) initialized 1279 if (overlayUpdater == null) 1280 return; 1281 1282 overlayUpdater.addProps(VtkUtil.getLayersProps(layers)); 1283 } 1284 1285 protected void updateBoundingBoxSize() 1286 { 1287 final double[] bounds = imageVolume.getVolume().GetBounds(); 1288 1289 boundingBox.SetBounds(bounds); 1290 rulerBox.SetBounds(bounds); 1291 } 1292 1293 /** 1294 * Build and get image data 1295 */ 1296 protected vtkImageData getImageData() 1297 { 1298 try 1299 { 1300 return VtkUtil.getImageData(getSequence(), getPositionT(), getPositionC()); 1301 } 1302 catch (TooLargeArrayException e) 1303 { 1304 // cannot allocate a such large contiguous array 1305 return null; 1306 } 1307 catch (OutOfMemoryError e) 1308 { 1309 // just not enough memory 1310 return null; 1311 } 1312 } 1313 1314 /** 1315 * update image data 1316 */ 1317 protected void updateImageData(vtkImageData data) 1318 { 1319 if (data != null) 1320 { 1321 imageVolume.setVolumeData(data); 1322 imageVolume.getVolume().SetVisibility(getImageLayer().isVisible() ? 1 : 0); 1323 1324 if (textInfo != null) 1325 textInfo.SetVisibility(0); 1326 } 1327 else 1328 { 1329 // no data --> hide volume 1330 imageVolume.getVolume().SetVisibility(0); 1331 1332 if (textInfo != null) 1333 { 1334 final Sequence seq = getSequence(); 1335 1336 // we have an image --> not enough memory to display it (show message) 1337 if ((seq != null) && !seq.isEmpty()) 1338 textInfo.SetVisibility(1); 1339 } 1340 } 1341 } 1342 1343 protected void updateLut() 1344 { 1345 final LUT lut = getLut(); 1346 1347 // update the whole LUT 1348 for (int c = 0; c < lut.getNumChannel(); c++) 1349 updateLut(lut.getLutChannel(c), c); 1350 } 1351 1352 protected void updateLut(LUTChannel lutChannel, int channel) 1353 { 1354 final Sequence sequence = getSequence(); 1355 if ((sequence == null) || sequence.isEmpty()) 1356 return; 1357 1358 final int ch = channel; 1359 final vtkColorTransferFunction colorMap = VtkUtil.getColorMap(lutChannel); 1360 final vtkPiecewiseFunction opacityMap = VtkUtil.getOpacityMap(lutChannel); 1361 1362 imageVolume.setColorMap(colorMap, ch); 1363 imageVolume.setOpacityMap(opacityMap, ch); 1364 } 1365 1366 @Override 1367 public Component getViewComponent() 1368 { 1369 return getVtkPanel(); 1370 } 1371 1372 public IcyVtkPanel getVtkPanel() 1373 { 1374 return panel3D; 1375 } 1376 1377 /** 1378 * @deprecated Use {@link #getVtkPanel()} 1379 */ 1380 @Deprecated 1381 @Override 1382 public IcyVtkPanel getPanel3D() 1383 { 1384 return getVtkPanel(); 1385 } 1386 1387 @Override 1388 public vtkRenderer getRenderer() 1389 { 1390 return renderer; 1391 } 1392 1393 public vtkRenderWindow getRenderWindow() 1394 { 1395 return renderWindow; 1396 } 1397 1398 /** 1399 * @see icy.vtk.IcyVtkPanel#getPicker() 1400 */ 1401 public vtkPicker getPicker() 1402 { 1403 return panel3D.getPicker(); 1404 } 1405 1406 /** 1407 * Get scaling for image volume rendering 1408 */ 1409 @Override 1410 public double[] getVolumeScale() 1411 { 1412 return imageVolume.getScale(); 1413 } 1414 1415 /** 1416 * Set scaling for image volume rendering 1417 */ 1418 @Override 1419 public void setVolumeScale(double x, double y, double z) 1420 { 1421 propertyChange(PROPERTY_SCALE, new double[] {x, y, z}); 1422 } 1423 1424 @Override 1425 public void keyPressed(KeyEvent e) 1426 { 1427 // send to overlays 1428 super.keyPressed(e); 1429 1430 // forward to view 1431 panel3D.keyPressed(e); 1432 1433 if (!e.isConsumed()) 1434 { 1435 switch (e.getKeyCode()) 1436 { 1437 case KeyEvent.VK_LEFT: 1438 if (EventUtil.isMenuControlDown(e, true)) 1439 setPositionT(Math.max(getPositionT() - 5, 0)); 1440 else 1441 setPositionT(Math.max(getPositionT() - 1, 0)); 1442 e.consume(); 1443 break; 1444 1445 case KeyEvent.VK_RIGHT: 1446 if (EventUtil.isMenuControlDown(e, true)) 1447 setPositionT(getPositionT() + 5); 1448 else 1449 setPositionT(getPositionT() + 1); 1450 e.consume(); 1451 break; 1452 1453 case KeyEvent.VK_NUMPAD2: 1454 if (EventUtil.isMenuControlDown(e, true)) 1455 panel3D.translateView(0, -50); 1456 else 1457 panel3D.translateView(0, -10); 1458 refresh(); 1459 e.consume(); 1460 break; 1461 1462 case KeyEvent.VK_NUMPAD4: 1463 if (EventUtil.isMenuControlDown(e, true)) 1464 panel3D.translateView(-50, 0); 1465 else 1466 panel3D.translateView(-10, 0); 1467 refresh(); 1468 e.consume(); 1469 break; 1470 1471 case KeyEvent.VK_NUMPAD6: 1472 if (EventUtil.isMenuControlDown(e, true)) 1473 panel3D.translateView(50, 0); 1474 else 1475 panel3D.translateView(10, 0); 1476 refresh(); 1477 e.consume(); 1478 break; 1479 1480 case KeyEvent.VK_NUMPAD8: 1481 if (EventUtil.isMenuControlDown(e, true)) 1482 panel3D.translateView(0, 50); 1483 else 1484 panel3D.translateView(0, 10); 1485 refresh(); 1486 e.consume(); 1487 break; 1488 } 1489 } 1490 } 1491 1492 @Override 1493 public void keyReleased(KeyEvent e) 1494 { 1495 // send to overlays 1496 super.keyReleased(e); 1497 1498 // forward to view 1499 panel3D.keyReleased(e); 1500 } 1501 1502 @Override 1503 protected void setPositionZInternal(int z) 1504 { 1505 // not supported, Z should stay at -1 1506 } 1507 1508 @Override 1509 protected void setPositionCInternal(int c) 1510 { 1511 // single channel mode is not possible here 1512 if (c != -1) 1513 return; 1514 1515 super.setPositionCInternal(c); 1516 } 1517 1518 @Override 1519 public double getScaleX() 1520 { 1521 final double dist = getCamera().GetDistance(); 1522 // cannot compute scaling 1523 if (dist <= 0d) 1524 return 1d; 1525 1526 final double imageSizeX = getImageSizeX(); 1527 // FIXME: from where come that x2 factor 1528 final double result = (2 * imageSizeX * getVolumeScale()[0]) / dist; 1529 final double canvasImageRatio = getCanvasSizeX() / ((imageSizeX == 0d) ? 1d : imageSizeX); 1530 1531 return result * canvasImageRatio; 1532 } 1533 1534 @Override 1535 public double getScaleY() 1536 { 1537 final double dist = getCamera().GetDistance(); 1538 // cannot compute scaling 1539 if (dist <= 0d) 1540 return 1d; 1541 1542 final double imageSizeY = getImageSizeY(); 1543 // FIXME: from where come that x2 factor 1544 final double result = (2 * imageSizeY * getVolumeScale()[1]) / dist; 1545 final double canvasImageRatio = getCanvasSizeY() / ((imageSizeY == 0d) ? 1d : imageSizeY); 1546 1547 return result * canvasImageRatio; 1548 } 1549 1550 @Override 1551 public void setMouseImagePosX(double value) 1552 { 1553 // just ignore NaN position (canvasToImage(..) can return NaN for specific dimension) 1554 if (!Double.isNaN(value)) 1555 super.setMouseImagePosX(value); 1556 } 1557 1558 @Override 1559 public void setMouseImagePosY(double value) 1560 { 1561 // just ignore NaN position (canvasToImage(..) can return NaN for specific dimension) 1562 if (!Double.isNaN(value)) 1563 super.setMouseImagePosY(value); 1564 } 1565 1566 @Override 1567 public void setMouseImagePosZ(double value) 1568 { 1569 // just ignore NaN position (canvasToImage(..) can return NaN for specific dimension) 1570 if (!Double.isNaN(value)) 1571 super.setMouseImagePosZ(value); 1572 } 1573 1574 @Override 1575 public BufferedImage getRenderedImage(int t, int c) 1576 { 1577 final CustomVtkPanel vp = panel3D; 1578 if (vp == null) 1579 return null; 1580 1581 // save position 1582 final int prevT = getPositionT(); 1583 final int prevC = getPositionC(); 1584 1585 // set wanted position (needed for correct overlay drawing) 1586 // we have to fire events else some stuff can miss the change 1587 setPositionT(t); 1588 setPositionC(c); 1589 try 1590 { 1591 final vtkImageData imageData = getImageData(); 1592 1593 // VTK need this to be called in the EDT 1594 invokeOnEDTSilent(new Runnable() 1595 { 1596 @Override 1597 public void run() 1598 { 1599 // set image data 1600 updateImageData(imageData); 1601 1602 // force fine rendering here 1603 vp.setForceFineRendering(true); 1604 try 1605 { 1606 // render now ! 1607 vp.paint(vp.getGraphics()); 1608 } 1609 finally 1610 { 1611 vp.setForceFineRendering(false); 1612 } 1613 } 1614 }); 1615 1616 try 1617 { 1618 final Robot robot = new Robot(); 1619 final Rectangle bounds = vp.getBounds(); 1620 // transform in screen coordinates 1621 bounds.setLocation(ComponentUtil.convertPointToScreen(bounds.getLocation(), vp)); 1622 // do the capture 1623 return robot.createScreenCapture(bounds); 1624 } 1625 catch (AWTException e) 1626 { 1627 IcyExceptionHandler.showErrorMessage(e, true); 1628 return null; 1629 } 1630 1631 // final int[] size = renderWindow.GetSize(); 1632 // final int w = size[0]; 1633 // final int h = size[1]; 1634 // final vtkUnsignedCharArray array = new vtkUnsignedCharArray(); 1635 // final vtkImageData imageData = getImageData(); 1636 // final BufferedImage[] result = new BufferedImage[1]; 1637 // 1638 // // VTK need this to be called in the EDT 1639 // invokeOnEDTSilent(new Runnable() 1640 // { 1641 // @Override 1642 // public void run() 1643 // { 1644 // // set image data 1645 // updateImageData(imageData); 1646 // 1647 // // force fine rendering here 1648 // panel3D.setForceFineRendering(true); 1649 // try 1650 // { 1651 // // render now ! 1652 // panel3D.paint(panel3D.getGraphics()); 1653 // } 1654 // finally 1655 // { 1656 // panel3D.setForceFineRendering(false); 1657 // } 1658 // 1659 // try 1660 // { 1661 // Robot r = new Robot(); 1662 // result[0] = r.createScreenCapture(SwingUtilities.convertRectangle(panel3D, getBounds(), null)); 1663 // } 1664 // catch (AWTException e) 1665 // { 1666 // // TODO Auto-generated catch block 1667 // e.printStackTrace(); 1668 // } 1669 // 1670 // // NOTE: in vtk the [0,0] pixel is bottom left, so a vertical flip is required 1671 // // NOTE: GetRGBACharPixelData gives problematic results depending on the platform 1672 // // (see comment about alpha and platform-dependence in the doc for vtkWindowToImageFilter) 1673 // // Since the canvas is opaque, simply use GetPixelData. 1674 // renderWindow.GetPixelData(0, 0, w - 1, h - 1, 1, array); 1675 // } 1676 // }); 1677 // 1678 // // convert the vtk array into a IcyBufferedImage 1679 // final byte[] inData = array.GetJavaArray(); 1680 // final BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); 1681 // final int[] outData = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); 1682 // 1683 // int inOffset = 0; 1684 // for (int y = h - 1; y >= 0; y--) 1685 // { 1686 // int outOffset = y * w; 1687 // 1688 // for (int x = 0; x < w; x++) 1689 // { 1690 // final int r = TypeUtil.unsign(inData[inOffset++]); 1691 // final int g = TypeUtil.unsign(inData[inOffset++]); 1692 // final int b = TypeUtil.unsign(inData[inOffset++]); 1693 // 1694 // outData[outOffset++] = (r << 16) | (g << 8) | (b << 0); 1695 // } 1696 // } 1697 // 1698 // return image; 1699 } 1700 finally 1701 { 1702 // restore position 1703 setPositionT(prevT); 1704 setPositionC(prevC); 1705 } 1706 } 1707 1708 @Override 1709 public BufferedImage getRenderedImage(int t, int z, int c, boolean canvasView) 1710 { 1711 if (z != -1) 1712 throw new UnsupportedOperationException( 1713 "Error: getRenderedImage(..) with z != -1 not supported on Canvas3D."); 1714 if (!canvasView) 1715 System.out.println("Warning: getRenderedImage(..) with canvasView = false not supported on Canvas3D."); 1716 1717 return getRenderedImage(t, c); 1718 } 1719 1720 protected void invokeOnEDT(Runnable task) throws InterruptedException 1721 { 1722 // in initialization --> just execute 1723 if (edtTask == null) 1724 { 1725 task.run(); 1726 return; 1727 } 1728 1729 edtTask.setTask(task); 1730 1731 try 1732 { 1733 ThreadUtil.invokeNow(edtTask); 1734 } 1735 catch (InterruptedException e) 1736 { 1737 throw e; 1738 } 1739 catch (Exception t) 1740 { 1741 // just ignore as this is async process 1742 System.out.println("[VTKCanvas] Warning:" + t); 1743 } 1744 } 1745 1746 protected void invokeOnEDTSilent(Runnable task) 1747 { 1748 try 1749 { 1750 invokeOnEDT(task); 1751 } 1752 catch (InterruptedException e) 1753 { 1754 // just ignore 1755 } 1756 } 1757 1758 @Override 1759 public void changed(IcyCanvasEvent event) 1760 { 1761 super.changed(event); 1762 1763 // avoid useless process during canvas initialization 1764 if (!initialized) 1765 return; 1766 1767 if (event.getType() == IcyCanvasEventType.POSITION_CHANGED) 1768 { 1769 switch (event.getDim()) 1770 { 1771 case C: 1772 propertyChange(PROPERTY_DATA, null); 1773 // layers can change depending position 1774 refresh(); 1775 break; 1776 1777 case T: 1778 propertyChange(PROPERTY_DATA, null); 1779 // layers can change depending position 1780 refresh(); 1781 break; 1782 1783 case Z: 1784 // shouldn't happen 1785 break; 1786 } 1787 } 1788 } 1789 1790 @Override 1791 protected void lutChanged(int channel) 1792 { 1793 super.lutChanged(channel); 1794 1795 // avoid useless process during canvas initialization 1796 if (!initialized) 1797 return; 1798 1799 propertyChange(PROPERTY_LUT, Integer.valueOf(channel)); 1800 } 1801 1802 @Override 1803 protected void sequenceOverlayChanged(Overlay overlay, SequenceEventType type) 1804 { 1805 super.sequenceOverlayChanged(overlay, type); 1806 1807 if (!initialized) 1808 return; 1809 1810 // refresh 1811 refresh(); 1812 } 1813 1814 @Override 1815 protected void sequenceDataChanged(IcyBufferedImage image, SequenceEventType type) 1816 { 1817 super.sequenceDataChanged(image, type); 1818 1819 // rebuild image data and bounds 1820 propertyChange(PROPERTY_DATA, null); 1821 propertyChange(PROPERTY_BOUNDS, null); 1822 } 1823 1824 @Override 1825 protected void sequenceMetaChanged(String metadataName) 1826 { 1827 super.sequenceMetaChanged(metadataName); 1828 1829 final Sequence sequence = getSequence(); 1830 if ((sequence == null) || sequence.isEmpty()) 1831 return; 1832 1833 // need to set scale ? 1834 if (StringUtil.isEmpty(metadataName) || (StringUtil.equals(metadataName, Sequence.ID_PIXEL_SIZE_X) 1835 || StringUtil.equals(metadataName, Sequence.ID_PIXEL_SIZE_Y) 1836 || StringUtil.equals(metadataName, Sequence.ID_PIXEL_SIZE_Z))) 1837 { 1838 setVolumeScale(sequence.getPixelSizeX(), sequence.getPixelSizeY(), sequence.getPixelSizeZ()); 1839 } 1840 } 1841 1842 @Override 1843 protected void layerChanged(CanvasLayerEvent event) 1844 { 1845 super.layerChanged(event); 1846 1847 if (!initialized) 1848 return; 1849 1850 if (event.getType() == LayersEventType.CHANGED) 1851 { 1852 final String propertyName = event.getProperty(); 1853 1854 // we ignore priority property here as we display in 3D 1855 if (propertyName.equals(Layer.PROPERTY_OPACITY) || propertyName.equals(Layer.PROPERTY_VISIBLE)) 1856 propertyChange(PROPERTY_LAYERS_VISIBLE, event.getSource()); 1857 } 1858 } 1859 1860 @Override 1861 protected void layerAdded(Layer layer) 1862 { 1863 super.layerAdded(layer); 1864 1865 addLayerActors(layer); 1866 } 1867 1868 @Override 1869 protected void layerRemoved(Layer layer) 1870 { 1871 super.layerRemoved(layer); 1872 1873 removeLayerActors(layer); 1874 } 1875 1876 @Override 1877 protected void layersVisibleChanged() 1878 { 1879 propertyChange(PROPERTY_LAYERS_VISIBLE, null); 1880 } 1881 1882 @Override 1883 public void actionPerformed(ActionEvent e) 1884 { 1885 final Object source = e.getSource(); 1886 1887 // translate button action to property change event 1888 if (source == axesButton) 1889 propertyChange(PROPERTY_AXES, Boolean.valueOf(axesButton.isSelected())); 1890 else if (source == boundingBoxButton) 1891 propertyChange(PROPERTY_BOUNDINGBOX, Boolean.valueOf(boundingBoxButton.isSelected())); 1892 else if (source == gridButton) 1893 propertyChange(PROPERTY_BOUNDINGBOX_GRID, Boolean.valueOf(gridButton.isSelected())); 1894 else if (source == rulerButton) 1895 propertyChange(PROPERTY_BOUNDINGBOX_RULES, Boolean.valueOf(rulerButton.isSelected())); 1896 else if (source == rulerLabelButton) 1897 propertyChange(PROPERTY_BOUNDINGBOX_LABELS, Boolean.valueOf(rulerLabelButton.isSelected())); 1898 // else if (source == pickOnMouseMoveButton) 1899 // preferences.putBoolean(ID_PICKONMOUSEMOVE, pickOnMouseMoveButton.isSelected()); 1900 } 1901 1902 protected void propertyChange(String name, Object value) 1903 { 1904 // we can ignore it in this case 1905 if (propertiesUpdater == null) 1906 return; 1907 1908 final Property prop = new Property(name, value); 1909 1910 propertiesUpdater.submit(prop); 1911 } 1912 1913 /* 1914 * Called when one of the value in setting panel has changed 1915 */ 1916 @Override 1917 public void settingChange(PropertyChangeEvent evt) 1918 { 1919 propertyChange(evt.getPropertyName(), evt.getNewValue()); 1920 } 1921 1922 protected class EDTTask<T> implements Callable<T> 1923 { 1924 protected Runnable task; 1925 1926 public void setTask(Runnable task) 1927 { 1928 this.task = task; 1929 } 1930 1931 @Override 1932 public T call() throws Exception 1933 { 1934 task.run(); 1935 1936 return null; 1937 } 1938 } 1939 1940 protected class CustomVtkPanel extends IcyVtkPanel 1941 { 1942 /** 1943 * 1944 */ 1945 private static final long serialVersionUID = -7399887230624608711L; 1946 1947 long lastRefreshTime; 1948 boolean forceFineRendering; 1949 1950 public CustomVtkPanel() 1951 { 1952 super(); 1953 1954 lastRefreshTime = 0L; 1955 forceFineRendering = false; 1956 // key events should be forwarded from the viewer 1957 removeKeyListener(this); 1958 } 1959 1960 public boolean getForceFineRendering() 1961 { 1962 return forceFineRendering; 1963 } 1964 1965 public void setForceFineRendering(boolean value) 1966 { 1967 forceFineRendering = value; 1968 } 1969 1970 /** 1971 * Update mouse cursor 1972 * 1973 * @param b 1974 */ 1975 protected void updateCursor(boolean consumedByCanvas) 1976 { 1977 // don't change custom cursor 1978 if (getCursor().getType() == Cursor.CUSTOM_CURSOR) 1979 return; 1980 1981 // consumed by canvas --> return it to origin 1982 if (consumedByCanvas) 1983 { 1984 GuiUtil.setCursor(this, Cursor.HAND_CURSOR); 1985 return; 1986 } 1987 1988 final Sequence seq = getSequence(); 1989 1990 if (seq != null) 1991 { 1992 final ROI overlappedRoi = seq.getFocusedROI(); 1993 1994 // overlapping an ROI ? 1995 if (overlappedRoi != null) 1996 { 1997 final Layer layer = getLayer(overlappedRoi); 1998 1999 if ((layer != null) && layer.isVisible()) 2000 { 2001 GuiUtil.setCursor(this, Cursor.HAND_CURSOR); 2002 return; 2003 } 2004 } 2005 2006 final List<ROI> selectedRois = seq.getSelectedROIs(); 2007 2008 // search if we are overriding ROI control points 2009 for (ROI selectedRoi : selectedRois) 2010 { 2011 final Layer layer = getLayer(selectedRoi); 2012 2013 if ((layer != null) && layer.isVisible() && selectedRoi.hasSelectedPoint()) 2014 { 2015 GuiUtil.setCursor(this, Cursor.HAND_CURSOR); 2016 return; 2017 } 2018 } 2019 } 2020 2021 GuiUtil.setCursor(this, Cursor.DEFAULT_CURSOR); 2022 } 2023 2024 @Override 2025 public void paint(Graphics g) 2026 { 2027 if (forceFineRendering) 2028 setFineRendering(); 2029 else 2030 { 2031 // several repaint in a short period of time --> set fast rendering for 1 second 2032 if ((lastRefreshTime != 0) && ((System.currentTimeMillis() - lastRefreshTime) < 250)) 2033 setCoarseRendering(1000); 2034 } 2035 2036 // call paint on overlays first 2037 paintLayers(getLayers(true), getImageLayer(), getSequence()); 2038 2039 // then do 3D rendering 2040 super.paint(g); 2041 2042 lastRefreshTime = System.currentTimeMillis(); 2043 } 2044 2045 /** 2046 * Paint layers 2047 */ 2048 protected void paintLayers(List<Layer> sortedLayers, final Layer imageLayer, Sequence seq) 2049 { 2050 final boolean lv = isLayersVisible(); 2051 2052 // call paint in inverse order to have first overlay "at top" 2053 for (int i = sortedLayers.size() - 1; i >= 0; i--) 2054 { 2055 final Layer layer = sortedLayers.get(i); 2056 2057 if (layer == null) 2058 continue; 2059 2060 // update VTK visibility flag 2061 for (vtkProp prop : VtkUtil.getLayerProps(layer)) 2062 { 2063 // image layer is not impacted by global layer visibility 2064 if (layer == imageLayer) 2065 { 2066 // we have a no empty image --> display it if layer is visible 2067 if (layer.isVisible() && (seq != null) && !seq.isEmpty()) 2068 prop.SetVisibility(1); 2069 else 2070 prop.SetVisibility(0); 2071 } 2072 else 2073 { 2074 boolean visible = lv && layer.isVisible(); 2075 // FIXME: find a better method to know visibility flags should not be impacted here 2076 final vtkInformation vtkInfo = prop.GetPropertyKeys(); 2077 2078 if (vtkInfo != null) 2079 { 2080 // pick the visibility info 2081 if ((vtkInfo.Has(visibilityKey) != 0) && (vtkInfo.Get(visibilityKey) == 0)) 2082 visible = false; 2083 } 2084 2085 // finally set the visibility state 2086 prop.SetVisibility(visible ? 1 : 0); 2087 } 2088 2089 // opacity seems to not be correctly handled in VTK ?? 2090 if (prop instanceof vtkActor) 2091 ((vtkActor) prop).GetProperty().SetOpacity(layer.getOpacity()); 2092 else if (prop instanceof vtkActor2D) 2093 ((vtkActor2D) prop).GetProperty().SetOpacity(layer.getOpacity()); 2094 } 2095 2096 // then do layer painting 2097 if (lv && layer.isVisible()) 2098 // important to set Graphics to null for VtkCanvas as some plugins rely on it to detect VtkCanvas 2099 // (note that this is not a good way of doing it) 2100 layer.getOverlay().paint(null, seq, VtkCanvas.this); 2101 } 2102 } 2103 2104 @Override 2105 public void mouseEntered(MouseEvent e) 2106 { 2107 // send mouse event to overlays 2108 VtkCanvas.this.mouseEntered(e, getMouseImagePos5D()); 2109 2110 super.mouseEntered(e); 2111 } 2112 2113 @Override 2114 public void mouseExited(MouseEvent e) 2115 { 2116 // send mouse event to overlays 2117 VtkCanvas.this.mouseExited(e, getMouseImagePos5D()); 2118 2119 super.mouseExited(e); 2120 } 2121 2122 @Override 2123 public void mouseClicked(MouseEvent e) 2124 { 2125 // send mouse event to overlays 2126 VtkCanvas.this.mouseClick(e, getMouseImagePos5D()); 2127 2128 super.mouseClicked(e); 2129 } 2130 2131 @Override 2132 public void mouseMoved(MouseEvent e) 2133 { 2134 // update mouse position 2135 setMousePos(e.getPoint()); 2136 2137 // get picked object (mouse move/drag event) 2138 pickedObject = pick(e.getX(), e.getY()); 2139 2140 // send mouse event to overlays 2141 VtkCanvas.this.mouseMove(e, getMouseImagePos5D()); 2142 2143 final boolean consumed = e.isConsumed(); 2144 2145 super.mouseMoved(e); 2146 2147 // refresh mouse cursor (do it after all process) 2148 updateCursor(!consumed && e.isConsumed()); 2149 } 2150 2151 @Override 2152 public void mouseDragged(MouseEvent e) 2153 { 2154 // update mouse position 2155 setMousePos(e.getPoint()); 2156 2157 // get picked object (mouse move/drag event) 2158 pickedObject = pick(e.getX(), e.getY()); 2159 2160 // send mouse event to overlays 2161 VtkCanvas.this.mouseDrag(e, getMouseImagePos5D()); 2162 2163 final boolean consumed = e.isConsumed(); 2164 2165 super.mouseDragged(e); 2166 2167 // refresh mouse cursor (do it after all process) 2168 updateCursor(!consumed && e.isConsumed()); 2169 } 2170 2171 @Override 2172 public void mousePressed(MouseEvent e) 2173 { 2174 // send mouse event to overlays 2175 VtkCanvas.this.mousePressed(e, getMouseImagePos5D()); 2176 2177 super.mousePressed(e); 2178 } 2179 2180 @Override 2181 public void mouseReleased(MouseEvent e) 2182 { 2183 // send mouse event to overlays 2184 VtkCanvas.this.mouseReleased(e, getMouseImagePos5D()); 2185 2186 super.mouseReleased(e); 2187 } 2188 2189 @Override 2190 public void mouseWheelMoved(MouseWheelEvent e) 2191 { 2192 // send mouse event to overlays 2193 VtkCanvas.this.mouseWheelMoved(e, getMouseImagePos5D()); 2194 2195 super.mouseWheelMoved(e); 2196 } 2197 2198 @Override 2199 public void keyPressed(KeyEvent e) 2200 { 2201 if (!e.isConsumed()) 2202 { 2203 switch (e.getKeyCode()) 2204 { 2205 case KeyEvent.VK_R: 2206 // reset view 2207 resetCamera(); 2208 2209 // also reset LUT 2210 if (EventUtil.isShiftDown(e, true)) 2211 { 2212 final Sequence sequence = getSequence(); 2213 final Viewer viewer = getViewer(); 2214 2215 if ((viewer != null) && (sequence != null)) 2216 { 2217 final LUT lut = sequence.createCompatibleLUT(); 2218 2219 // set default opacity for 3D display 2220 lut.setAlphaToLinear3D(); 2221 viewer.setLut(lut); 2222 } 2223 } 2224 else 2225 repaint(); 2226 2227 e.consume(); 2228 break; 2229 } 2230 } 2231 2232 super.keyPressed(e); 2233 } 2234 } 2235 2236 /** 2237 * Image overlay to encapsulate VTK image volume in a canvas layer 2238 */ 2239 protected class VtkCanvasImageOverlay extends IcyCanvasImageOverlay implements VtkPainter 2240 { 2241 public VtkCanvasImageOverlay() 2242 { 2243 super(); 2244 2245 // create image volume 2246 imageVolume = new VtkImageVolume(); 2247 } 2248 2249 @Override 2250 public void paint(Graphics2D g, Sequence sequence, IcyCanvas canvas) 2251 { 2252 // nothing here 2253 } 2254 2255 @Override 2256 public vtkProp[] getProps() 2257 { 2258 // return the image volume as prop 2259 return new vtkProp[] {imageVolume.getVolume()}; 2260 } 2261 } 2262 2263 /** 2264 * Property to update 2265 */ 2266 protected static class Property 2267 { 2268 String name; 2269 Object value; 2270 2271 public Property(String name, Object value) 2272 { 2273 super(); 2274 2275 this.name = name; 2276 this.value = value; 2277 } 2278 2279 @Override 2280 public boolean equals(Object obj) 2281 { 2282 if (obj instanceof Property) 2283 return name.equals(((Property) obj).name); 2284 2285 return super.equals(obj); 2286 } 2287 2288 @Override 2289 public int hashCode() 2290 { 2291 return name.hashCode(); 2292 } 2293 }; 2294 2295 /** 2296 * Properties updater helper class 2297 */ 2298 protected class PropertiesUpdater extends Thread 2299 { 2300 final LinkedBlockingQueue<Property> toUpdate; 2301 2302 public PropertiesUpdater() 2303 { 2304 super("VTK canvas properties updater"); 2305 2306 toUpdate = new LinkedBlockingQueue<VtkCanvas.Property>(256); 2307 } 2308 2309 public synchronized void submit(Property prop) 2310 { 2311 // remove previous property of same name 2312 if (toUpdate.remove(prop)) 2313 { 2314 // if we already had a layers visible update then we update all layers 2315 if (prop.name.equals(PROPERTY_LAYERS_VISIBLE)) 2316 prop.value = null; 2317 } 2318 2319 // add the property 2320 toUpdate.add(prop); 2321 } 2322 2323 protected void updateProperty(Property prop) throws InterruptedException 2324 { 2325 final String name = prop.name; 2326 final Object value = prop.value; 2327 2328 if (StringUtil.equals(name, VtkSettingPanel.PROPERTY_AMBIENT)) 2329 { 2330 final double d = ((Double) value).doubleValue(); 2331 2332 invokeOnEDT(new Runnable() 2333 { 2334 @Override 2335 public void run() 2336 { 2337 imageVolume.setAmbient(d); 2338 } 2339 }); 2340 2341 preferences.putDouble(ID_AMBIENT, d); 2342 } 2343 else if (StringUtil.equals(name, VtkSettingPanel.PROPERTY_DIFFUSE)) 2344 { 2345 final double d = ((Double) value).doubleValue(); 2346 2347 invokeOnEDT(new Runnable() 2348 { 2349 @Override 2350 public void run() 2351 { 2352 imageVolume.setDiffuse(d); 2353 } 2354 }); 2355 2356 preferences.putDouble(ID_DIFFUSE, d); 2357 } 2358 else if (StringUtil.equals(name, VtkSettingPanel.PROPERTY_SPECULAR)) 2359 { 2360 final double d = ((Double) value).doubleValue(); 2361 2362 invokeOnEDT(new Runnable() 2363 { 2364 @Override 2365 public void run() 2366 { 2367 imageVolume.setSpecular(d); 2368 } 2369 }); 2370 2371 preferences.putDouble(ID_SPECULAR, d); 2372 } 2373 else if (StringUtil.equals(name, VtkSettingPanel.PROPERTY_BG_COLOR)) 2374 { 2375 final Color color = (Color) value; 2376 2377 invokeOnEDT(new Runnable() 2378 { 2379 @Override 2380 public void run() 2381 { 2382 setBackgroundColorInternal(color); 2383 } 2384 }); 2385 2386 preferences.putInt(ID_BGCOLOR, color.getRGB()); 2387 } 2388 else if (StringUtil.equals(name, VtkSettingPanel.PROPERTY_INTERPOLATION)) 2389 { 2390 final int i = ((Integer) value).intValue(); 2391 2392 invokeOnEDT(new Runnable() 2393 { 2394 @Override 2395 public void run() 2396 { 2397 imageVolume.setInterpolationMode(i); 2398 } 2399 }); 2400 2401 preferences.putInt(ID_INTERPOLATION, i); 2402 } 2403 else if (StringUtil.equals(name, VtkSettingPanel.PROPERTY_MAPPER)) 2404 { 2405 final boolean gpuRendering = ((Boolean) value).booleanValue(); 2406 2407 invokeOnEDT(new Runnable() 2408 { 2409 @Override 2410 public void run() 2411 { 2412 imageVolume.setGPURendering(gpuRendering); 2413 } 2414 }); 2415 2416 preferences.putInt(ID_MAPPER, gpuRendering ? 1 : 0); 2417 } 2418 else if (StringUtil.equals(name, VtkSettingPanel.PROPERTY_BLENDING)) 2419 { 2420 final VtkVolumeBlendType type = (VtkVolumeBlendType) value; 2421 2422 invokeOnEDT(new Runnable() 2423 { 2424 @Override 2425 public void run() 2426 { 2427 imageVolume.setBlendingMode(type); 2428 } 2429 }); 2430 2431 preferences.putInt(ID_BLENDING, getVolumeBlendingMode().ordinal()); 2432 } 2433 else if (StringUtil.equals(name, VtkSettingPanel.PROPERTY_SAMPLE)) 2434 { 2435 final int i = ((Integer) value).intValue(); 2436 2437 invokeOnEDT(new Runnable() 2438 { 2439 @Override 2440 public void run() 2441 { 2442 imageVolume.setSampleResolution(i); 2443 } 2444 }); 2445 2446 preferences.putDouble(ID_SAMPLE, i); 2447 } 2448 else if (StringUtil.equals(name, PROPERTY_AXES)) 2449 { 2450 final boolean b = ((Boolean) value).booleanValue(); 2451 2452 invokeOnEDT(new Runnable() 2453 { 2454 @Override 2455 public void run() 2456 { 2457 panel3D.setAxisOrientationDisplayEnable(b); 2458 } 2459 }); 2460 2461 preferences.putBoolean(ID_AXES, b); 2462 } 2463 else if (StringUtil.equals(name, PROPERTY_BOUNDINGBOX)) 2464 { 2465 final boolean b = ((Boolean) value).booleanValue(); 2466 2467 invokeOnEDT(new Runnable() 2468 { 2469 @Override 2470 public void run() 2471 { 2472 boundingBox.SetVisibility(b ? 1 : 0); 2473 } 2474 }); 2475 2476 preferences.putBoolean(ID_BOUNDINGBOX, b); 2477 } 2478 else if (StringUtil.equals(name, PROPERTY_BOUNDINGBOX_GRID)) 2479 { 2480 final boolean b = ((Boolean) value).booleanValue(); 2481 2482 invokeOnEDT(new Runnable() 2483 { 2484 @Override 2485 public void run() 2486 { 2487 rulerBox.SetDrawXGridlines(b ? 1 : 0); 2488 rulerBox.SetDrawYGridlines(b ? 1 : 0); 2489 rulerBox.SetDrawZGridlines(b ? 1 : 0); 2490 } 2491 }); 2492 2493 preferences.putBoolean(ID_BOUNDINGBOX_GRID, b); 2494 } 2495 else if (StringUtil.equals(name, PROPERTY_BOUNDINGBOX_RULES)) 2496 { 2497 final boolean b = ((Boolean) value).booleanValue(); 2498 2499 invokeOnEDT(new Runnable() 2500 { 2501 @Override 2502 public void run() 2503 { 2504 rulerBox.SetXAxisTickVisibility(b ? 1 : 0); 2505 rulerBox.SetXAxisMinorTickVisibility(b ? 1 : 0); 2506 rulerBox.SetYAxisTickVisibility(b ? 1 : 0); 2507 rulerBox.SetYAxisMinorTickVisibility(b ? 1 : 0); 2508 rulerBox.SetZAxisTickVisibility(b ? 1 : 0); 2509 rulerBox.SetZAxisMinorTickVisibility(b ? 1 : 0); 2510 } 2511 }); 2512 2513 preferences.putBoolean(ID_BOUNDINGBOX_RULES, b); 2514 } 2515 else if (StringUtil.equals(name, PROPERTY_BOUNDINGBOX_LABELS)) 2516 { 2517 final boolean b = ((Boolean) value).booleanValue(); 2518 2519 invokeOnEDT(new Runnable() 2520 { 2521 @Override 2522 public void run() 2523 { 2524 rulerBox.SetXAxisLabelVisibility(b ? 1 : 0); 2525 rulerBox.SetYAxisLabelVisibility(b ? 1 : 0); 2526 rulerBox.SetZAxisLabelVisibility(b ? 1 : 0); 2527 } 2528 }); 2529 2530 preferences.putBoolean(ID_BOUNDINGBOX_LABELS, b); 2531 } 2532 else if (StringUtil.equals(name, VtkSettingPanel.PROPERTY_SHADING)) 2533 { 2534 final boolean b = ((Boolean) value).booleanValue(); 2535 2536 invokeOnEDT(new Runnable() 2537 { 2538 @Override 2539 public void run() 2540 { 2541 imageVolume.setShade(b); 2542 } 2543 }); 2544 2545 preferences.putBoolean(ID_SHADING, b); 2546 } 2547 else if (StringUtil.equals(name, PROPERTY_LUT)) 2548 { 2549 updateLut(); 2550 } 2551 else if (StringUtil.equals(name, PROPERTY_SCALE)) 2552 { 2553 final double[] oldScale = getVolumeScale(); 2554 final double[] newScale = (double[]) value; 2555 2556 if (!Arrays.equals(oldScale, newScale)) 2557 { 2558 invokeOnEDT(new Runnable() 2559 { 2560 @Override 2561 public void run() 2562 { 2563 imageVolume.setScale(newScale); 2564 // need to update bounding box as well 2565 updateBoundingBoxSize(); 2566 } 2567 }); 2568 } 2569 } 2570 else if (StringUtil.equals(name, PROPERTY_DATA)) 2571 { 2572 final vtkImageData data = getImageData(); 2573 2574 invokeOnEDT(new Runnable() 2575 { 2576 @Override 2577 public void run() 2578 { 2579 // set image data 2580 updateImageData(data); 2581 } 2582 }); 2583 } 2584 else if (StringUtil.equals(name, PROPERTY_BOUNDS)) 2585 { 2586 invokeOnEDT(new Runnable() 2587 { 2588 @Override 2589 public void run() 2590 { 2591 updateBoundingBoxSize(); 2592 } 2593 }); 2594 } 2595 else if (StringUtil.equals(name, PROPERTY_LAYERS_VISIBLE)) 2596 { 2597 // force refresh 2598 refresh(); 2599 } 2600 } 2601 2602 @Override 2603 public void run() 2604 { 2605 while (!isInterrupted()) 2606 { 2607 try 2608 { 2609 updateProperty(toUpdate.take()); 2610 } 2611 catch (InterruptedException e) 2612 { 2613 // just end process 2614 break; 2615 } 2616 2617 // need to refresh rendering 2618 if (toUpdate.isEmpty()) 2619 refresh(); 2620 } 2621 2622 // help GC 2623 toUpdate.clear(); 2624 } 2625 } 2626 2627 /** 2628 * VTK overlay updater helper class 2629 */ 2630 protected class VtkOverlayUpdater extends Thread 2631 { 2632 final LinkedList<vtkProp> propToAdd; 2633 final LinkedList<vtkProp> propToRemove; 2634 2635 public VtkOverlayUpdater() 2636 { 2637 super("VTK canvas overlay updater"); 2638 2639 propToAdd = new LinkedList<vtkProp>(); 2640 propToRemove = new LinkedList<vtkProp>(); 2641 } 2642 2643 public void addProp(vtkProp prop) 2644 { 2645 synchronized (propToAdd) 2646 { 2647 propToAdd.add(prop); 2648 } 2649 } 2650 2651 public void removeProp(vtkProp prop) 2652 { 2653 synchronized (propToRemove) 2654 { 2655 propToRemove.add(prop); 2656 } 2657 } 2658 2659 public void addProps(List<vtkProp> props) 2660 { 2661 synchronized (propToAdd) 2662 { 2663 propToAdd.addAll(props); 2664 } 2665 } 2666 2667 public void removeProps(List<vtkProp> props) 2668 { 2669 synchronized (propToRemove) 2670 { 2671 propToRemove.addAll(props); 2672 } 2673 } 2674 2675 public void addProps(vtkProp[] props) 2676 { 2677 synchronized (propToAdd) 2678 { 2679 for (vtkProp prop : props) 2680 propToAdd.add(prop); 2681 } 2682 } 2683 2684 public void removeProps(vtkProp[] props) 2685 { 2686 synchronized (propToAdd) 2687 { 2688 for (vtkProp prop : props) 2689 propToRemove.add(prop); 2690 } 2691 } 2692 2693 @Override 2694 public void run() 2695 { 2696 while (!isInterrupted()) 2697 { 2698 while (!isInterrupted() && !propToAdd.isEmpty()) 2699 { 2700 invokeOnEDTSilent(new Runnable() 2701 { 2702 @Override 2703 public void run() 2704 { 2705 final vtkRenderer r = getRenderer(); 2706 final vtkCamera cam = getCamera(); 2707 int done = 0; 2708 2709 if ((r != null) && (cam != null)) 2710 { 2711 // add actor by packet of 1000 2712 while (!propToAdd.isEmpty() && (done++ < 1000)) 2713 { 2714 final vtkProp prop = propToAdd.removeFirst(); 2715 2716 // actor not yet present in renderer ? 2717 if (r.HasViewProp(prop) == 0) 2718 { 2719 // refresh camera property for this specific kind of actor 2720 if (prop instanceof vtkCubeAxesActor) 2721 ((vtkCubeAxesActor) prop).SetCamera(cam); 2722 2723 // add the actor to the renderer 2724 r.AddViewProp(prop); 2725 } 2726 } 2727 } 2728 } 2729 }); 2730 2731 // sleep a bit to offer a bit of responsiveness 2732 ThreadUtil.sleep(10); 2733 // and refresh 2734 refresh(); 2735 } 2736 2737 while (!isInterrupted() && !propToRemove.isEmpty()) 2738 { 2739 invokeOnEDTSilent(new Runnable() 2740 { 2741 @Override 2742 public void run() 2743 { 2744 final vtkRenderer r = getRenderer(); 2745 final vtkCamera cam = getCamera(); 2746 int done = 0; 2747 2748 if ((r != null) && (cam != null)) 2749 { 2750 // remove actors from renderer by packet of 1000 2751 while (!propToRemove.isEmpty() && (done++ < 1000)) 2752 r.RemoveViewProp(propToRemove.removeFirst()); 2753 } 2754 } 2755 }); 2756 2757 // sleep a bit to offer a bit of responsiveness 2758 ThreadUtil.sleep(10); 2759 // and refresh 2760 refresh(); 2761 } 2762 2763 // sleep a bit 2764 ThreadUtil.sleep(1); 2765 } 2766 2767 // help GC 2768 propToAdd.clear(); 2769 propToRemove.clear(); 2770 } 2771 } 2772}