001/* 002 * Copyright 2010-2015 Institut Pasteur. 003 * 004 * This file is part of Icy. 005 * 006 * Icy is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * Icy is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with Icy. If not, see <http://www.gnu.org/licenses/>. 018 */ 019package plugins.kernel.roi.roi2d; 020 021import icy.canvas.IcyCanvas; 022import icy.canvas.IcyCanvas2D; 023import icy.common.CollapsibleEvent; 024import icy.painter.Anchor2D; 025import icy.painter.OverlayEvent; 026import icy.painter.OverlayEvent.OverlayEventType; 027import icy.resource.ResourceUtil; 028import icy.roi.ROI; 029import icy.roi.ROIEvent; 030import icy.sequence.Sequence; 031import icy.type.point.Point5D; 032import icy.util.StringUtil; 033import icy.util.XMLUtil; 034import icy.vtk.IcyVtkPanel; 035 036import java.awt.Color; 037import java.awt.Graphics2D; 038import java.awt.geom.Ellipse2D; 039import java.awt.geom.Line2D; 040import java.awt.geom.Point2D; 041import java.awt.geom.Rectangle2D; 042 043import org.w3c.dom.Node; 044 045import plugins.kernel.canvas.VtkCanvas; 046import vtk.vtkActor; 047import vtk.vtkPolyDataMapper; 048import vtk.vtkSphereSource; 049 050/** 051 * ROI 2D Point class.<br> 052 * Define a single point ROI<br> 053 * 054 * @author Stephane Dallongeville 055 */ 056public class ROI2DPoint extends ROI2DShape 057{ 058 public class ROI2DPointPainter extends ROI2DShapePainter 059 { 060 vtkSphereSource vtkSource; 061 062 @Override 063 protected void finalize() throws Throwable 064 { 065 super.finalize(); 066 067 // release extra VTK objects 068 if (vtkSource != null) 069 vtkSource.Delete(); 070 } 071 072 @Override 073 protected boolean isSmall(Rectangle2D bounds, Graphics2D g, IcyCanvas canvas) 074 { 075 if (isSelected()) 076 return false; 077 078 return true; 079 } 080 081 @Override 082 protected boolean isTiny(Rectangle2D bounds, Graphics2D g, IcyCanvas canvas) 083 { 084 if (isSelected()) 085 return false; 086 087 return true; 088 } 089 090 @Override 091 public void drawROI(Graphics2D g, Sequence sequence, IcyCanvas canvas) 092 { 093 if (canvas instanceof IcyCanvas2D) 094 { 095 final Graphics2D g2 = (Graphics2D) g.create(); 096 097 if (isSelected() && !isReadOnly()) 098 { 099 // draw control point if selected 100 synchronized (controlPoints) 101 { 102 for (Anchor2D pt : controlPoints) 103 pt.paint(g2, sequence, canvas); 104 } 105 } 106 else 107 { 108 final Point2D pos = getPoint(); 109 final double ray = getAdjustedStroke(canvas); 110 final Ellipse2D ellipse = new Ellipse2D.Double(pos.getX() - ray, pos.getY() - ray, ray * 2, 111 ray * 2); 112 113 // draw shape 114 g2.setColor(getDisplayColor()); 115 g2.fill(ellipse); 116 } 117 118 g2.dispose(); 119 } 120 else 121 // just use parent method 122 super.drawROI(g, sequence, canvas); 123 } 124 125 @Override 126 protected void initVtkObjects() 127 { 128 super.initVtkObjects(); 129 130 // init 3D painters stuff 131 vtkSource = new vtkSphereSource(); 132 vtkSource.SetRadius(getStroke()); 133 vtkSource.SetThetaResolution(12); 134 vtkSource.SetPhiResolution(12); 135 136 // delete previously created objects that we will recreate 137 if (actor != null) 138 actor.Delete(); 139 if (polyMapper != null) 140 polyMapper.Delete(); 141 142 polyMapper = new vtkPolyDataMapper(); 143 polyMapper.SetInputConnection((vtkSource).GetOutputPort()); 144 145 actor = new vtkActor(); 146 actor.SetMapper(polyMapper); 147 148 // initialize color 149 final Color col = getColor(); 150 actor.GetProperty().SetColor(col.getRed() / 255d, col.getGreen() / 255d, col.getBlue() / 255d); 151 } 152 153 /** 154 * update 3D painter for 3D canvas (called only when VTK is loaded). 155 */ 156 @Override 157 protected void rebuildVtkObjects() 158 { 159 final VtkCanvas canvas = canvas3d.get(); 160 // canvas was closed 161 if (canvas == null) 162 return; 163 164 final IcyVtkPanel vtkPanel = canvas.getVtkPanel(); 165 // canvas was closed 166 if (vtkPanel == null) 167 return; 168 169 final Sequence seq = canvas.getSequence(); 170 // nothing to update 171 if (seq == null) 172 return; 173 174 final Point2D pos = getPoint(); 175 double curZ = getZ(); 176 177 // all slices ? 178 if (curZ == -1) 179 // set object at middle of the volume 180 curZ = seq.getSizeZ() / 2d; 181 182 // actor can be accessed in canvas3d for rendering so we need to synchronize access 183 vtkPanel.lock(); 184 try 185 { 186 // need to handle scaling on radius and position to keep a "round" sphere (else we obtain ellipsoid) 187 vtkSource.SetRadius(getStroke() * scaling[0]); 188 vtkSource.SetCenter(pos.getX() * scaling[0], pos.getY() * scaling[1], (curZ + 0.5d) * scaling[2]); 189 polyMapper.Update(); 190 191 // vtkSource.SetRadius(getStroke()); 192 // vtkSource.SetCenter(pos.getX(), pos.getY(), curZ); 193 // polyMapper.Update(); 194 // actor.SetScale(scaling); 195 } 196 finally 197 { 198 vtkPanel.unlock(); 199 } 200 201 // need to repaint 202 painterChanged(); 203 } 204 205 @Override 206 protected void updateVtkDisplayProperties() 207 { 208 if (actor == null) 209 return; 210 211 final VtkCanvas cnv = canvas3d.get(); 212 final Color col = getDisplayColor(); 213 final double r = col.getRed() / 255d; 214 final double g = col.getGreen() / 255d; 215 final double b = col.getBlue() / 255d; 216 // final float opacity = getOpacity(); 217 218 final IcyVtkPanel vtkPanel = (cnv != null) ? cnv.getVtkPanel() : null; 219 220 // we need to lock canvas as actor can be accessed during rendering 221 if (vtkPanel != null) 222 vtkPanel.lock(); 223 try 224 { 225 actor.GetProperty().SetColor(r, g, b); 226 } 227 finally 228 { 229 if (vtkPanel != null) 230 vtkPanel.unlock(); 231 } 232 233 // need to repaint 234 painterChanged(); 235 } 236 } 237 238 public static final String ID_POSITION = "position"; 239 240 private final Anchor2D position; 241 242 /** 243 * @deprecated 244 */ 245 @Deprecated 246 public ROI2DPoint(Point2D pt, boolean cm) 247 { 248 this(pt); 249 } 250 251 public ROI2DPoint(Point2D position) 252 { 253 super(new Line2D.Double()); 254 255 this.position = createAnchor(position); 256 this.position.setSelected(true); 257 addPoint(this.position); 258 259 // set icon (default name is defined by getDefaultName()) 260 setIcon(ResourceUtil.ICON_ROI_POINT); 261 } 262 263 /** 264 * Generic constructor for interactive mode 265 */ 266 public ROI2DPoint(Point5D pt) 267 { 268 this(pt.toPoint2D()); 269 // getOverlay().setMousePos(pt); 270 } 271 272 public ROI2DPoint(double x, double y) 273 { 274 this(new Point2D.Double(x, y)); 275 } 276 277 public ROI2DPoint() 278 { 279 this(new Point2D.Double()); 280 } 281 282 @Override 283 public String getDefaultName() 284 { 285 return "Point2D"; 286 } 287 288 @Override 289 protected ROI2DShapePainter createPainter() 290 { 291 return new ROI2DPointPainter(); 292 } 293 294 /** 295 * @deprecated Use {@link #getLine()} instead. 296 */ 297 @Deprecated 298 public Rectangle2D getRectangle() 299 { 300 final Point2D pt = getPoint(); 301 return new Rectangle2D.Double(pt.getX(), pt.getY(), 0d, 0d); 302 } 303 304 public Line2D getLine() 305 { 306 return (Line2D) shape; 307 } 308 309 public Point2D getPoint() 310 { 311 return position.getPosition(); 312 } 313 314 /** 315 * Called when anchor overlay changed 316 */ 317 @Override 318 public void controlPointOverlayChanged(OverlayEvent event) 319 { 320 // we only mind about painter change from anchor... 321 if (event.getType() == OverlayEventType.PAINTER_CHANGED) 322 { 323 // here we want to have ROI focused when point is selected (special case for ROIPoint) 324 if (hasSelectedPoint()) 325 setFocused(true); 326 327 // anchor changed --> ROI painter changed 328 getOverlay().painterChanged(); 329 } 330 } 331 332 @Override 333 public boolean contains(double x, double y) 334 { 335 return false; 336 } 337 338 @Override 339 public boolean contains(Point2D p) 340 { 341 return false; 342 } 343 344 @Override 345 public boolean contains(double x, double y, double w, double h) 346 { 347 return false; 348 } 349 350 @Override 351 public boolean contains(Rectangle2D r) 352 { 353 return false; 354 } 355 356 @Override 357 public boolean contains(ROI roi) 358 { 359 return false; 360 } 361 362 @Override 363 public boolean intersects(ROI r) 364 { 365 // special case of ROI2DPoint 366 if (r instanceof ROI2DPoint) 367 return onSamePos(((ROI2DPoint) r), false) && ((ROI2DPoint) r).getPoint().equals(getPoint()); 368 369 return super.intersects(r); 370 } 371 372 /** 373 * roi changed 374 */ 375 @Override 376 public void onChanged(CollapsibleEvent object) 377 { 378 final ROIEvent event = (ROIEvent) object; 379 380 // do here global process on ROI change 381 switch (event.getType()) 382 { 383 case PROPERTY_CHANGED: 384 final String property = event.getPropertyName(); 385 386 // stroke changed --> rebuild vtk object 387 if (StringUtil.equals(property, PROPERTY_STROKE)) 388 ((ROI2DShapePainter) getOverlay()).needRebuild = true; 389 break; 390 391 case SELECTION_CHANGED: 392 // always select the control point when ROI was just selected 393 if (isSelected()) 394 position.setSelected(true); 395 break; 396 397 default: 398 break; 399 } 400 401 super.onChanged(object); 402 } 403 404 @Override 405 protected void updateShape() 406 { 407 final Point2D pt = getPoint(); 408 final double x = pt.getX(); 409 final double y = pt.getY(); 410 411 getLine().setLine(x, y, x, y); 412 413 // call super method after shape has been updated 414 super.updateShape(); 415 } 416 417 @Override 418 public boolean canAddPoint() 419 { 420 // this ROI doesn't support point add 421 return false; 422 } 423 424 @Override 425 protected boolean removePoint(IcyCanvas canvas, Anchor2D pt) 426 { 427 if (canvas != null) 428 { 429 // remove point on this ROI remove the ROI from current sequence 430 canvas.getSequence().removeROI(this); 431 return true; 432 } 433 434 return false; 435 } 436 437 @Override 438 public double computeNumberOfContourPoints() 439 { 440 return 0d; 441 } 442 443 @Override 444 public double computeNumberOfPoints() 445 { 446 return 0d; 447 } 448 449 @Override 450 public boolean loadFromXML(Node node) 451 { 452 beginUpdate(); 453 try 454 { 455 if (!super.loadFromXML(node)) 456 return false; 457 458 position.loadPositionFromXML(XMLUtil.getElement(node, ID_POSITION)); 459 } 460 finally 461 { 462 endUpdate(); 463 } 464 465 return true; 466 } 467 468 @Override 469 public boolean saveToXML(Node node) 470 { 471 if (!super.saveToXML(node)) 472 return false; 473 474 position.savePositionToXML(XMLUtil.setElement(node, ID_POSITION)); 475 476 return true; 477 } 478}