001/** 002 * 003 */ 004package plugins.kernel.roi.roi3d; 005 006import icy.canvas.IcyCanvas; 007import icy.math.Line3DIterator; 008import icy.painter.Anchor3D; 009import icy.resource.ResourceUtil; 010import icy.roi.ROI; 011import icy.sequence.Sequence; 012import icy.type.geom.Line3D; 013import icy.type.point.Point3D; 014import icy.type.point.Point5D; 015import icy.type.rectangle.Rectangle3D; 016import icy.util.XMLUtil; 017import icy.vtk.IcyVtkPanel; 018 019import java.awt.Graphics2D; 020import java.awt.Rectangle; 021import java.awt.geom.Line2D; 022import java.awt.geom.Rectangle2D; 023import java.util.List; 024 025import org.w3c.dom.Node; 026 027import plugins.kernel.canvas.VtkCanvas; 028import vtk.vtkTubeFilter; 029 030/** 031 * ROI 3D Line. 032 * 033 * @author Stephane Dallongeville 034 */ 035public class ROI3DLine extends ROI3DShape 036{ 037 public class ROI3DLinePainter extends ROI3DShapePainter 038 { 039 // extra VTK 3D objects 040 protected vtkTubeFilter tubeFilter; 041 042 public ROI3DLinePainter() 043 { 044 super(); 045 046 // don't create VTK object on constructor 047 tubeFilter = null; 048 } 049 050 @Override 051 protected void finalize() throws Throwable 052 { 053 super.finalize(); 054 055 // release allocated VTK resources 056 if (tubeFilter != null) 057 tubeFilter.Delete(); 058 }; 059 060 @Override 061 protected void initVtkObjects() 062 { 063 super.initVtkObjects(); 064 065 // init specific tube filter 066 tubeFilter = new vtkTubeFilter(); 067 tubeFilter.SetInputData(polyData); 068 tubeFilter.SetRadius(1d); 069 tubeFilter.CappingOn(); 070 tubeFilter.SetNumberOfSides(8); 071 // tubeFilter.SidesShareVerticesOff(); 072 polyMapper.SetInputConnection(tubeFilter.GetOutputPort()); 073 } 074 075 @Override 076 protected boolean isTiny(Rectangle2D bounds, Graphics2D g, IcyCanvas canvas) 077 { 078 if (isSelected()) 079 return false; 080 081 return super.isTiny(bounds, g, canvas); 082 } 083 084 /** 085 * update 3D painter for 3D canvas (called only when VTK is loaded). 086 */ 087 @Override 088 protected void rebuildVtkObjects() 089 { 090 super.rebuildVtkObjects(); 091 092 final VtkCanvas canvas = canvas3d.get(); 093 // canvas was closed 094 if (canvas == null) 095 return; 096 097 final IcyVtkPanel vtkPanel = canvas.getVtkPanel(); 098 // canvas was closed 099 if (vtkPanel == null) 100 return; 101 102 // sub VTK object not yet initialized (it can happen, have to check why ??) 103 if (tubeFilter == null) 104 return; 105 106 // actor can be accessed in canvas3d for rendering so we need to synchronize access 107 vtkPanel.lock(); 108 try 109 { 110 // just be sure the tube filter is also up to date 111 tubeFilter.Update(); 112 } 113 finally 114 { 115 vtkPanel.unlock(); 116 } 117 } 118 119 protected void updateVtkTubeRadius() 120 { 121 final VtkCanvas canvas = canvas3d.get(); 122 // canvas was closed 123 if (canvas == null) 124 return; 125 126 final IcyVtkPanel vtkPanel = canvas.getVtkPanel(); 127 // canvas was closed 128 if (vtkPanel == null) 129 return; 130 131 // sub VTK object not yet initialized (it can happen, have to check why ??) 132 if (tubeFilter == null) 133 return; 134 135 // update tube radius base on canvas scale X and image scale X 136 final double radius = canvas.canvasToImageLogDeltaX((int) getStroke()) * scaling[0]; 137 138 if (tubeFilter.GetRadius() != radius) 139 { 140 // actor can be accessed in canvas3d for rendering so we need to synchronize access 141 vtkPanel.lock(); 142 try 143 { 144 tubeFilter.SetRadius(radius); 145 tubeFilter.Update(); 146 } 147 finally 148 { 149 vtkPanel.unlock(); 150 } 151 152 // need to repaint 153 painterChanged(); 154 } 155 } 156 157 @Override 158 public void drawROI(Graphics2D g, Sequence sequence, IcyCanvas canvas) 159 { 160 super.drawROI(g, sequence, canvas); 161 162 // update VTK tube radius if needed 163 if (canvas instanceof VtkCanvas) 164 updateVtkTubeRadius(); 165 } 166 167 @Override 168 protected void drawShape(Graphics2D g, Sequence sequence, IcyCanvas canvas, boolean simplified) 169 { 170 drawShape(g, sequence, canvas, simplified, false); 171 } 172 } 173 174 public static final String ID_PT1 = "pt1"; 175 public static final String ID_PT2 = "pt2"; 176 177 protected final Anchor3D pt1; 178 protected final Anchor3D pt2; 179 180 public ROI3DLine(Point3D pt1, Point3D pt2) 181 { 182 super(new Line3D()); 183 184 this.pt1 = createAnchor(pt1); 185 this.pt2 = createAnchor(pt2); 186 // keep pt2 selected to size the line for "interactive mode" 187 this.pt2.setSelected(true); 188 189 addPoint(this.pt1); 190 addPoint(this.pt2); 191 192 // set icon 193 setIcon(ResourceUtil.ICON_ROI_LINE); 194 } 195 196 public ROI3DLine(Line3D line) 197 { 198 this(line.getP1(), line.getP2()); 199 } 200 201 public ROI3DLine(Point3D pt) 202 { 203 this(new Point3D.Double(pt.getX(), pt.getY(), pt.getZ()), pt); 204 } 205 206 /** 207 * Generic constructor for interactive mode 208 */ 209 public ROI3DLine(Point5D pt) 210 { 211 this(pt.toPoint3D()); 212 // getOverlay().setMousePos(pt); 213 } 214 215 public ROI3DLine(double x1, double y1, double z1, double x2, double y2, double z2) 216 { 217 this(new Point3D.Double(x1, y1, z1), new Point3D.Double(x2, y2, z2)); 218 } 219 220 public ROI3DLine() 221 { 222 this(new Point3D.Double(), new Point3D.Double()); 223 } 224 225 @Override 226 public String getDefaultName() 227 { 228 return "Line3D"; 229 } 230 231 @Override 232 protected ROI3DShapePainter createPainter() 233 { 234 return new ROI3DLinePainter(); 235 } 236 237 public Line3D getLine() 238 { 239 return (Line3D) shape; 240 } 241 242 @Override 243 protected void updateShape() 244 { 245 getLine().setLine(pt1.getPosition(), pt2.getPosition()); 246 247 // call super method after shape has been updated 248 super.updateShape(); 249 } 250 251 @Override 252 public boolean canAddPoint() 253 { 254 // this ROI doesn't support point add 255 return false; 256 } 257 258 @Override 259 public boolean canRemovePoint() 260 { 261 // this ROI doesn't support point remove 262 return false; 263 } 264 265 @Override 266 protected boolean removePoint(IcyCanvas canvas, Anchor3D pt) 267 { 268 // this ROI doesn't support point remove 269 return false; 270 } 271 272 @Override 273 public boolean canSetBounds() 274 { 275 return true; 276 } 277 278 @Override 279 public void setBounds3D(Rectangle3D bounds) 280 { 281 beginUpdate(); 282 try 283 { 284 pt1.setPosition(bounds.getMinX(), bounds.getMinY(), bounds.getMinZ()); 285 pt2.setPosition(bounds.getMaxX(), bounds.getMaxY(), bounds.getMaxZ()); 286 } 287 finally 288 { 289 endUpdate(); 290 } 291 } 292 293 public void setLine(Line3D line) 294 { 295 setBounds3D(line.getBounds()); 296 } 297 298 @Override 299 protected double getTotalDistance(List<Point3D> points, double factorX, double factorY, double factorZ) 300 { 301 // for line the total length don't need last point connection 302 return Point3D.getTotalDistance(points, factorX, factorY, factorZ, false); 303 } 304 305 @Override 306 public boolean[] getBooleanMask2D(int x, int y, int width, int height, int z, boolean inclusive) 307 { 308 if ((width <= 0) || (height <= 0)) 309 return new boolean[0]; 310 311 final boolean[] result = new boolean[width * height]; 312 // 2D bounds 313 final Rectangle bounds2d = new Rectangle(x, y, width, height); 314 315 drawLine3DInBooleanMask2D(bounds2d, result, z, pt1.getPositionInternal(), pt2.getPositionInternal()); 316 317 return result; 318 } 319 320 public static void drawLine3DInBooleanMask2D(Rectangle bounds2d, boolean[] result, int z, Point3D p1, Point3D p2) 321 { 322 final Line2D l = new Line2D.Double(p1.getX(), p1.getY(), p2.getX(), p2.getY()); 323 324 // 2D intersection ? 325 if (l.intersects(bounds2d)) 326 { 327 // 3D intersection ? 328 if (((p1.getZ() <= z) && (p2.getZ() >= z)) || ((p2.getZ() <= z) && (p1.getZ() >= z))) 329 { 330 final int bx = bounds2d.x; 331 final int by = bounds2d.y; 332 final int pitch = bounds2d.width; 333 final Line3DIterator it = new Line3DIterator(new Line3D(p1, p2), 1d); 334 335 while (it.hasNext()) 336 { 337 final Point3D pt = it.next(); 338 339 // same Z ? 340 if (Math.floor(pt.getZ()) == z) 341 { 342 final int x = (int) Math.floor(pt.getX()); 343 final int y = (int) Math.floor(pt.getY()); 344 345 // draw inside the mask 346 if (bounds2d.contains(x, y)) 347 result[(x - bx) + ((y - by) * pitch)] = true; 348 } 349 } 350 } 351 } 352 } 353 354 @Override 355 public double computeNumberOfPoints() 356 { 357 return 0d; 358 } 359 360 @Override 361 public boolean contains(ROI roi) 362 { 363 return false; 364 } 365 366 @Override 367 public boolean intersects(ROI r) 368 { 369 // special case of ROI3DLine 370 if (r instanceof ROI3DLine) 371 return onSamePos(((ROI3DLine) r), false) && ((ROI3DLine) r).getLine().intersectsLine(getLine()); 372 373 return super.intersects(r); 374 } 375 376 @Override 377 public boolean loadFromXML(Node node) 378 { 379 beginUpdate(); 380 try 381 { 382 if (!super.loadFromXML(node)) 383 return false; 384 385 pt1.loadPositionFromXML(XMLUtil.getElement(node, ID_PT1)); 386 pt2.loadPositionFromXML(XMLUtil.getElement(node, ID_PT2)); 387 } 388 finally 389 { 390 endUpdate(); 391 } 392 393 return true; 394 } 395 396 @Override 397 public boolean saveToXML(Node node) 398 { 399 if (!super.saveToXML(node)) 400 return false; 401 402 pt1.savePositionToXML(XMLUtil.setElement(node, ID_PT1)); 403 pt2.savePositionToXML(XMLUtil.setElement(node, ID_PT2)); 404 405 return true; 406 } 407}