Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
ShapeRoi.java
1 package ij.gui;
2 import java.awt.*;
3 import java.awt.image.*;
4 import java.awt.geom.*;
5 import java.awt.event.KeyEvent;
6 import java.util.*;
7 import ij.*;
8 import ij.process.*;
9 import ij.measure.*;
10 import ij.plugin.frame.Recorder;
11 import ij.plugin.filter.Analyzer;
12 
20 public class ShapeRoi extends Roi{
21 
22  /***/
23  static final int NO_TYPE = 128;
24 
26  static final double MAXERROR = 1.0e-3;
27 
30  static final double FLATNESS = 0.1;
31 
38  private static final int MAXPOLY = 10; // I hate arbitrary values !!!!
39 
40  private static final int OR=0, AND=1, XOR=2, NOT=3;
41 
43  private Shape shape;
44 
48  private double maxerror = ShapeRoi.MAXERROR;
49 
53  private double flatness = ShapeRoi.FLATNESS;
54 
56  private int maxPoly = ShapeRoi.MAXPOLY;
57 
60  private boolean flatten;
61 
66  private boolean forceTrace = false;
67 
72  private boolean forceAngle = false;
73 
74  /**********************************************************************************/
75  /*** Constructors ****/
76  /**********************************************************************************/
77 
79  public ShapeRoi(Roi r) {
80  this(r, ShapeRoi.FLATNESS, ShapeRoi.MAXERROR, false, false, false, ShapeRoi.MAXPOLY);
81  }
82 
96  ShapeRoi(Roi r, double flatness, double maxerror, boolean forceAngle, boolean forceTrace, boolean flatten, int maxPoly) {
97  super(r.startX, r.startY, r.width, r.height);
98  if(!IJ.isJava2()) return;
99  this.type = COMPOSITE;
100  this.flatness = flatness;
101  this.maxerror = maxerror;
102  this.forceAngle = forceAngle;
103  this.forceTrace = forceTrace;
104  this.maxPoly= maxPoly;
105  this.flatten = flatten;
106  state = r.getState();
107  setImage(r.imp);
108  shape = roiToShape((Roi)r.clone());
109  //IJ.log("ShapeRoi:"+x+" "+y);
110  if(ic!=null) {
111  Graphics g = ic.getGraphics();
112  draw(g);
113  g.dispose();
114  }
115  }
116 
120  public ShapeRoi(float[] shapeArray) {
121  super(0,0,null);
122  if(!IJ.isJava2()) return;
123  shape = makeShapeFromArray(shapeArray);
124  Rectangle r = shape.getBounds();
125  x = r.x;
126  y = r.y;
127  width = r.width;
128  height = r.height;
129 
130  state = NORMAL;
131  oldX=x; oldY=y; oldWidth=width; oldHeight=height;
132 
133  AffineTransform at = new AffineTransform();
134  at.translate(-x, -y);
135  shape = at.createTransformedShape(shape);
136  flatness = ShapeRoi.FLATNESS;
137  maxerror = ShapeRoi.MAXERROR;
138  maxPoly = ShapeRoi.MAXPOLY;
139  flatten = false;
140  type = COMPOSITE;
141  }
142 
144  public synchronized Object clone() { // the equivalent of "operator=" ?
145  ShapeRoi sr = (ShapeRoi)super.clone();
146  sr.type = COMPOSITE;
147  sr.flatness = flatness;
148  sr.maxerror = maxerror;
149  sr.forceAngle = forceAngle;
150  sr.forceTrace = forceTrace;
151  //sr.setImage(imp); //wsr
152  sr.setShape(ShapeRoi.cloneShape(shape));
153  return sr;
154  }
155 
157  static Shape cloneShape(Shape rhs) {
158  if(rhs==null) return null;
159  if(rhs instanceof Rectangle2D.Double) { return (Rectangle2D.Double)((Rectangle2D.Double)rhs).clone(); }
160  else if(rhs instanceof Ellipse2D.Double) { return (Ellipse2D.Double)((Ellipse2D.Double)rhs).clone(); }
161  else if(rhs instanceof Line2D.Double) { return (Line2D.Double)((Line2D.Double)rhs).clone(); }
162  else if(rhs instanceof Polygon) { return new Polygon(((Polygon)rhs).xpoints, ((Polygon)rhs).ypoints, ((Polygon)rhs).npoints); }
163  else if(rhs instanceof GeneralPath) { return (GeneralPath)((GeneralPath)rhs).clone(); }
164  return new GeneralPath(); // dodgy !!!
165  }
166 
167  /**********************************************************************************/
168  /*** Logical operations on shaped rois ****/
169  /**********************************************************************************/
170 
175  public ShapeRoi or(ShapeRoi sr) {return unaryOp(sr, OR);}
176 
182  public ShapeRoi and(ShapeRoi sr) {return unaryOp(sr, AND);}
183 
188  public ShapeRoi xor(ShapeRoi sr) {return unaryOp(sr, XOR);}
189 
194  public ShapeRoi not(ShapeRoi sr) {return unaryOp(sr, NOT);}
195 
196  ShapeRoi unaryOp(ShapeRoi sr, int op) {
197  AffineTransform at = new AffineTransform();
198  at.translate(x, y);
199  Area a1 = new Area(at.createTransformedShape(getShape()));
200  at = new AffineTransform();
201  at.translate(sr.x, sr.y);
202  Area a2 = new Area(at.createTransformedShape(sr.getShape()));
203  switch (op) {
204  case OR: a1.add(a2); break;
205  case AND: a1.intersect(a2); break;
206  case XOR: a1.exclusiveOr(a2); break;
207  case NOT: a1.subtract(a2); break;
208  }
209  Rectangle r = a1.getBounds();
210  at = new AffineTransform();
211  at.translate(-r.x, -r.y);
212  setShape(new GeneralPath(at.createTransformedShape(a1)));
213  x = r.x;
214  y = r.y;
215  return this;
216  }
217 
218  /**********************************************************************************/
219  /*** Interconversions between "regular" rois and shaped rois ****/
220  /**********************************************************************************/
221 
246  private Shape roiToShape(Roi roi) { //wsr
247  Shape shape = null;
248  Rectangle r = roi.getBounds();
249  int[] xCoords = null;
250  int[] yCoords = null;
251  int nCoords = 0;
252  switch(roi.getType()) {
253  case Roi.LINE:
254  shape = new Line2D.Double ((double)(((Line)roi).x1), (double)(((Line)roi).y1), (double)(((Line)roi).x2), (double)(((Line)roi).y2) );
255  break;
256  case Roi.RECTANGLE:
257  shape = new Rectangle2D.Double(0.0, 0.0, (double)r.width, (double)r.height);
258  break;
259  case Roi.OVAL:
260  Polygon p = roi.getPolygon();
261  for (int i=0; i<p.npoints; i++) {
262  p.xpoints[i] -= r.x;
263  p.ypoints[i] -= r.y;
264  }
265  shape = new Polygon(p.xpoints, p.ypoints, p.npoints);
266  //shape = new Ellipse2D.Double(0.0, 0.0, (double)r.width, (double)r.height);
267  break;
268  case Roi.POLYGON:
269  nCoords =((PolygonRoi)roi).getNCoordinates();
270  xCoords = ((PolygonRoi)roi).getXCoordinates();
271  yCoords = ((PolygonRoi)roi).getYCoordinates();
272  shape = new Polygon(xCoords,yCoords,nCoords);
273  break;
274  case Roi.FREEROI: case Roi.TRACED_ROI:
275  nCoords =((PolygonRoi)roi).getNCoordinates();
276  xCoords = ((PolygonRoi)roi).getXCoordinates();
277  yCoords = ((PolygonRoi)roi).getYCoordinates();
278  shape = new GeneralPath(GeneralPath.WIND_EVEN_ODD,nCoords);
279  ((GeneralPath)shape).moveTo((float)xCoords[0], (float)yCoords[0]);
280  for (int i=1; i<nCoords; i++) ((GeneralPath)shape).lineTo((float)xCoords[i],(float)yCoords[i]);
281  ((GeneralPath)shape).closePath();
282  break;
283  case Roi.POLYLINE: case Roi.FREELINE: case Roi.ANGLE:
284  nCoords =((PolygonRoi)roi).getNCoordinates();
285  xCoords = ((PolygonRoi)roi).getXCoordinates();
286  yCoords = ((PolygonRoi)roi).getYCoordinates();
287  shape = new GeneralPath(GeneralPath.WIND_NON_ZERO,nCoords);
288  ((GeneralPath)shape).moveTo((float)xCoords[0], (float)yCoords[0]);
289  for (int i=1; i<nCoords; i++) ((GeneralPath)shape).lineTo((float)xCoords[i],(float)yCoords[i]);
290  break;
291  case Roi.COMPOSITE: shape = ShapeRoi.cloneShape(((ShapeRoi)roi).getShape()); break;
292  default: type = NO_TYPE; break;
293  }
294 
295  if(shape!=null) {
296  this.x = roi.x;
297  this.y = roi.y;
298  Rectangle bounds = shape.getBounds();
299  this.width = bounds.width;
300  this.height = bounds.height;
301  this.startX = x;
302  this.startY = y;
303  //IJ.log("RoiToShape: "+x+" "+y+" "+width+" "+height+" "+bounds);
304  }
305  return shape;
306  }
307 
309  Shape makeShapeFromArray(float[] array) {
310  if(array==null) return null;
311  Shape s = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
312  int index=0, type, len;
313  float[] seg = new float[7];
314  while (true) {
315  len = getSegment(array, seg, index);
316  if (len<0) break;
317  index += len;
318  type = (int)seg[0];
319  switch(type) {
320  case PathIterator.SEG_MOVETO:
321  ((GeneralPath)s).moveTo(seg[1], seg[2]);
322  break;
323  case PathIterator.SEG_LINETO:
324  ((GeneralPath)s).lineTo(seg[1], seg[2]);
325  break;
326  case PathIterator.SEG_QUADTO:
327  ((GeneralPath)s).quadTo(seg[1], seg[2],seg[3], seg[4]);
328  break;
329  case PathIterator.SEG_CUBICTO:
330  ((GeneralPath)s).curveTo(seg[1], seg[2], seg[3], seg[4], seg[5], seg[6]);
331  break;
332  case PathIterator.SEG_CLOSE:
333  ((GeneralPath)s).closePath();
334  break;
335  default: break;
336  }
337  }
338  return s;
339  }
340 
341  private int getSegment(float[] array, float[] seg, int index) {
342  int len = array.length;
343  if (index>=len) return -1; seg[0]=array[index++];
344  int type = (int)seg[0];
345  if (type==PathIterator.SEG_CLOSE) return 1;
346  if (index>=len) return -1; seg[1]=array[index++];
347  if (index>=len) return -1; seg[2]=array[index++];
348  if (type==PathIterator.SEG_MOVETO||type==PathIterator.SEG_LINETO) return 3;
349  if (index>=len) return -1; seg[3]=array[index++];
350  if (index>=len) return -1; seg[4]=array[index++];
351  if (type==PathIterator.SEG_QUADTO) return 5;
352  if (index>=len) return -1; seg[5]=array[index++];
353  if (index>=len) return -1; seg[6]=array[index++];
354  if (type==PathIterator.SEG_CUBICTO) return 7;
355  return -1;
356  }
357 
399  public Roi[] getRois () {
400  if(shape==null) return new Roi[0];
401  Vector rois = new Vector();
402  if(shape instanceof Rectangle2D.Double) {
403  Roi r = new Roi((int)((Rectangle2D.Double)shape).getX(), (int)((Rectangle2D.Double)shape).getY(), (int)((Rectangle2D.Double)shape).getWidth(), (int)((Rectangle2D.Double)shape).getHeight());
404  rois.addElement(r);
405  } else if(shape instanceof Ellipse2D.Double) {
406  Roi r = new OvalRoi((int)((Ellipse2D.Double)shape).getX(), (int)((Ellipse2D.Double)shape).getY(), (int)((Ellipse2D.Double)shape).getWidth(), (int)((Ellipse2D.Double)shape).getHeight());
407  rois.addElement(r);
408  } else if(shape instanceof Line2D.Double) {
409  Roi r = new ij.gui.Line((int)((Line2D.Double)shape).getX1(), (int)((Line2D.Double)shape).getY1(), (int)((Line2D.Double)shape).getX2(), (int)((Line2D.Double)shape).getY2());
410  rois.addElement(r);
411  } else if(shape instanceof Polygon) {
412  Roi r = new PolygonRoi(((Polygon)shape).xpoints, ((Polygon)shape).ypoints, ((Polygon)shape).npoints, Roi.POLYGON);
413  rois.addElement(r);
414  } else if(shape instanceof GeneralPath) {
415  PathIterator pIter;
416  if (flatten) pIter = getFlatteningPathIterator(shape,flatness);
417  else pIter = shape.getPathIterator(new AffineTransform());
418  parsePath(pIter, null, null, rois, null);
419  }
420  Roi[] array = new Roi[rois.size()];
421  rois.copyInto((Roi[])array);
422  return array;
423  }
424 
433  private int guessType(int segments, boolean linesOnly, boolean curvesOnly, boolean closed) {
434  //IJ.log("guessType: "+segments+" "+linesOnly+" "+curvesOnly+" "+closed);
435  closed = true; // lines currently not supported
436  int roiType = Roi.RECTANGLE;
437  if (linesOnly) {
438  switch(segments) {
439  case 0: roiType = NO_TYPE; break;
440  case 1: roiType = NO_TYPE; break;
441  case 2: roiType = (closed ? NO_TYPE : Roi.LINE); break;
442  case 3: roiType = (closed ? Roi.POLYGON : (forceAngle ? Roi.ANGLE: Roi.POLYLINE)); break;
443  case 4: roiType = (closed ? Roi.RECTANGLE : Roi.POLYLINE); break;
444  default:
445  if (segments <= MAXPOLY)
446  roiType = closed ? Roi.POLYGON : Roi.POLYLINE;
447  else
448  roiType = closed ? (forceTrace ? Roi.TRACED_ROI: Roi.FREEROI): Roi.FREELINE;
449  break;
450  }
451  }
452  else roiType = segments >=2 ? Roi.COMPOSITE : NO_TYPE;
453  return roiType;
454  }
455 
463  private Roi createRoi(Vector xCoords, Vector yCoords, int roiType) {
464  if (roiType==NO_TYPE) return null;
465  Roi roi = null;
466  if(xCoords.size() != yCoords.size() || xCoords.size()==0) { return null; }
467 
468  int[] xPoints = new int[xCoords.size()];
469  int[] yPoints = new int[yCoords.size()];
470 
471  for (int i=0; i<xPoints.length; i++) {
472  xPoints[i] = ((Integer)xCoords.elementAt(i)).intValue() + x;
473  yPoints[i] = ((Integer)yCoords.elementAt(i)).intValue() + y;
474  }
475 
476  int startX = 0;
477  int startY = 0;
478  int width = 0;
479  int height = 0;
480  switch(roiType) {
481  //case NO_TYPE: roi = this; break;
482  case Roi.COMPOSITE: roi = this; break; // hmmm.....!!!???
483  case Roi.OVAL:
484  startX = xPoints[xPoints.length-4];
485  startY = yPoints[yPoints.length-3];
486  width = max(xPoints)-min(xPoints);
487  height = max(yPoints)-min(yPoints);
488  roi = new OvalRoi(startX, startY, width, height);
489  break;
490  case Roi.RECTANGLE:
491  startX = xPoints[0];
492  startY = yPoints[0];
493  width = max(xPoints)-min(xPoints);
494  height = max(yPoints)-min(yPoints);
495  roi = new Roi(startX, startY, width, height);
496  break;
497  case Roi.LINE: roi = new ij.gui.Line(xPoints[0],yPoints[0],xPoints[1],yPoints[1]); break;
498  default:
499  int n = xPoints.length;
500  roi = new PolygonRoi(xPoints, yPoints, n, roiType);
501  if (roiType==FREEROI) {
502  double length = roi.getLength();
503  double mag = ic!=null?ic.getMagnification():1.0;
504  length *= mag;
505  //IJ.log("createRoi: "+length/n+" "+mag);
506  if (length/n>=15.0) {
507  roi = new PolygonRoi(xPoints, yPoints, n, POLYGON);
508  }
509  }
510  break;
511  }
512  //if(roi!=null && imp!=null) roi.setImage(imp);
513  return roi;
514  }
515 
516  /**********************************************************************************/
517  /*** Geometry ****/
518  /**********************************************************************************/
519 
521  public boolean contains(int x, int y) {
522  if(shape==null) return false;
523  return shape.contains(x-this.x, y-this.y); //wsr
524  }
525 
531  public double getFeretsDiameter() {
532  if(shape == null) return 0.0;
533  double result = 0.0;
534  double pw = 1.0, ph = 1.0;
535  if(imp!=null) {
536  Calibration cal = imp.getCalibration();
537  pw = cal.pixelWidth;
538  ph = cal.pixelHeight;
539  }
540  Rectangle2D sr = shape.getBounds2D();
541  double cx = sr.getX() + sr.getWidth()/2;
542  double cy = sr.getY() + sr.getHeight()/2;
543  double alpha = 1.0;
544  Rectangle2D r;
545  Shape s = null;
546  AffineTransform at = new AffineTransform();
547  for (int i=0; i<271; i++) {
548  at.rotate(1.0);
549  s = at.createTransformedShape(shape);
550  r = s.getBounds2D();
551  result = Math.max(result, Math.max(pw*r.getWidth(), ph*r.getHeight()));
552  }
553  return result;
554  }
555 
561  /*
562  public double getMinFeret() {
563  if(shape == null) return 0.0;
564  double result = 0.0;
565  double pw = 1.0, ph = 1.0;
566  if(imp!=null) {
567  Calibration cal = imp.getCalibration();
568  pw = cal.pixelWidth;
569  ph = cal.pixelHeight;
570  }
571  Rectangle2D sr = shape.getBounds2D();
572  double cx = sr.getX() + sr.getWidth()/2;
573  double cy = sr.getY() + sr.getHeight()/2;
574  double alpha = 1.0;
575  double diam = 0.0;
576  Rectangle2D r;
577  Shape s = null;
578  AffineTransform at = new AffineTransform();
579  for (int i=0; i<271; i++) {
580  at.rotate(1.0);
581  s = at.createTransformedShape(shape);
582  r = s.getBounds2D();
583  diam = Math.min(pw*r.getWidth(), ph*r.getHeight());
584  if(i==0) result = diam;
585  else result = Math.min(result,diam);
586  }
587  return result;
588  }
589  */
590 
592  public double getLength() {
593  if(shape==null) return 0.0;
594  Rectangle2D r2d = shape.getBounds2D();
595  double w = r2d.getWidth();
596  double h = r2d.getHeight();
597  if(w==0 && h==0) return 0.0;
598  PathIterator pIter;
599  flatten = true;
600  if(flatten) pIter = getFlatteningPathIterator(shape, flatness);
601  else pIter = shape.getPathIterator(new AffineTransform());
602  double[] par = new double[1];
603  parsePath(pIter, par, null, null, null);
604  flatten = false;
605  return par[0];
606  }
607 
609  FlatteningPathIterator getFlatteningPathIterator(Shape s, double fl)
610  { return (FlatteningPathIterator)s.getPathIterator(new AffineTransform(),fl); }
611 
613  double cplength(CubicCurve2D.Double c) {
614  double result = Math.sqrt(Math.pow((c.ctrlx1-c.x1),2.0)+Math.pow((c.ctrly1-c.y1),2.0));
615  result += Math.sqrt(Math.pow((c.ctrlx2-c.ctrlx1),2.0)+Math.pow((c.ctrly2-c.ctrly1),2.0));
616  result += Math.sqrt(Math.pow((c.x2-c.ctrlx2),2.0)+Math.pow((c.y2-c.ctrly2),2.0));
617  return result;
618  }
619 
621  double qplength(QuadCurve2D.Double c) {
622  double result = Math.sqrt(Math.pow((c.ctrlx-c.x1),2.0)+Math.pow((c.ctrly-c.y1),2.0));
623  result += Math.sqrt(Math.pow((c.x2-c.ctrlx),2.0)+Math.pow((c.y2-c.ctrly),2.0));
624  return result;
625  }
626 
628  double cclength(CubicCurve2D.Double c)
629  { return Math.sqrt(Math.pow((c.x2-c.x1),2.0) + Math.pow((c.y2-c.y1),2.0)); }
630 
632  double qclength(QuadCurve2D.Double c)
633  { return Math.sqrt(Math.pow((c.x2-c.x1),2.0) + Math.pow((c.y2-c.y1),2.0)); }
634 
642  double cBezLength(CubicCurve2D.Double c) {
643  double l = 0.0;
644  double cl = cclength(c);
645  double pl = cplength(c);
646  if((pl-cl)/2.0 > maxerror)
647  {
648  CubicCurve2D.Double[] cc = cBezSplit(c);
649  for(int i=0; i<2; i++) l+=cBezLength(cc[i]);
650  return l;
651  }
652  l = 0.5*pl+0.5*cl;
653  return l;
654  }
655 
663  double qBezLength(QuadCurve2D.Double c) {
664  double l = 0.0;
665  double cl = qclength(c);
666  double pl = qplength(c);
667  if((pl-cl)/2.0 > maxerror)
668  {
669  QuadCurve2D.Double[] cc = qBezSplit(c);
670  for(int i=0; i<2; i++) l+=qBezLength(cc[i]);
671  return l;
672  }
673  l = (2.0*pl+cl)/3.0;
674  return l;
675  }
676 
682  CubicCurve2D.Double[] cBezSplit(CubicCurve2D.Double c) {
683  CubicCurve2D.Double[] cc = new CubicCurve2D.Double[2];
684  for (int i=0; i<2 ; i++) cc[i] = new CubicCurve2D.Double();
685  c.subdivide(cc[0],cc[1]);
686  return cc;
687  }
688 
694  QuadCurve2D.Double[] qBezSplit(QuadCurve2D.Double c) {
695  QuadCurve2D.Double[] cc = new QuadCurve2D.Double[2];
696  for(int i=0; i<2; i++) cc[i] = new QuadCurve2D.Double();
697  c.subdivide(cc[0],cc[1]);
698  return cc;
699  }
700 
701  // c is an array of even length with x0, y0, x1, y1, ... ,xn, yn coordinate pairs
710  void scaleCoords(double[] c, double pw, double ph) {
711  int k = c.length/2;
712  if (2*k!=c.length) return; // bail out if array has odd length
713  for(int i=0; i<c.length; i+=2)
714  {
715  c[i]*=pw;
716  c[i+1]*=ph;
717  }
718  }
719 
720  Vector parseSegments(PathIterator pI) {
721  Vector v = new Vector();
722  if(parsePath(pI, null, v, null, null)) return v;
723  return null;
724  }
725 
731  public float[] getShapeAsArray() {
732  if(shape==null) return null;
733  PathIterator pIt = shape.getPathIterator(new AffineTransform());
734  Vector h = new Vector(); // handles
735  Vector s = new Vector(); // segment types
736  if(!(parsePath(pIt, null, s, null, h))) return null;
737  float[] result = new float[7*s.size()];
738  Point2D.Double p;
739  int segType;
740  int k=0, j=0;
741  int index = 0;
742  for (int i=0; i<s.size(); i++) {
743  segType = ((Integer)s.elementAt(i)).intValue();
744  switch(segType) {
745  case PathIterator.SEG_MOVETO: case PathIterator.SEG_LINETO:
746  result[index++] = segType;
747  p = (Point2D.Double)h.elementAt(j++);
748  result[index++]=(float)p.getX()+x; result[index++]=(float)p.getY()+y;
749  break;
750  case PathIterator.SEG_QUADTO:
751  result[index++] = segType;
752  p = (Point2D.Double)h.elementAt(j++);
753  result[index++]=(float)p.getX()+x; result[index++]=(float)p.getY()+y;
754  p = (Point2D.Double)h.elementAt(j++);
755  result[index++]=(float)p.getX()+x; result[index++]=(float)p.getY()+y;
756  break;
757  case PathIterator.SEG_CUBICTO:
758  result[index++] = segType;
759  p = (Point2D.Double)h.elementAt(j++);
760  result[index++]=(float)p.getX()+x; result[index++]=(float)p.getY()+y;
761  p = (Point2D.Double)h.elementAt(j++);
762  result[index++]=(float)p.getX()+x; result[index++]=(float)p.getY()+y;
763  p = (Point2D.Double)h.elementAt(j++);
764  result[index++]=(float)p.getX()+x; result[index++]=(float)p.getY()+y;
765  break;
766  case PathIterator.SEG_CLOSE:
767  result[index++] = segType;
768  break;
769  default: break;
770  }
771  }
772  float[] result2 = new float[index];
773  System.arraycopy(result, 0, result2, 0, result2.length);
774  return result2;
775  }
776 
792  boolean parsePath(PathIterator pIter, double[] params, Vector segments, Vector rois, Vector handles) {
793  //long start = System.currentTimeMillis();
794  boolean result = true;
795  if(pIter==null) return false;
796  double pw = 1.0, ph = 1.0;
797  if(imp!=null) {
798  Calibration cal = imp.getCalibration();
799  pw = cal.pixelWidth;
800  ph = cal.pixelHeight;
801  }
802  Vector xCoords = new Vector();
803  Vector yCoords = new Vector();
804  if(segments==null) segments = new Vector();
805  if(handles==null) handles = new Vector();
806  //if(rois==null) rois = new Vector();
807  if(params == null) params = new double[1];
808  int subPaths = 0; // the number of subpaths
809  int count = 0;// the number of segments in each subpath w/o SEG_CLOSE; resets to one after each SEG_MOVETO
810  int roiType = Roi.RECTANGLE;
811  int segType;
812  boolean closed = false;
813  boolean linesOnly = true;
814  boolean curvesOnly = true;
815  //boolean success = false;
816  double[] coords; // scaled coordinates of the path segment
817  double[] ucoords; // unscaled coordinates of the path segment
818  double sX = Double.NaN; // start x of subpath (scaled)
819  double sY = Double.NaN; // start y of subpath (scaled)
820  double x0 = Double.NaN; // last x in the subpath (scaled)
821  double y0 = Double.NaN; // last y in the subpath (scaled)
822  double usX = Double.NaN;// unscaled versions of the above
823  double usY = Double.NaN;
824  double ux0 = Double.NaN;
825  double uy0 = Double.NaN;
826  double pathLength = 0.0;
827  Shape curve; // temporary reference to a curve segment of the path
828  for(;!pIter.isDone();) {
829  coords = new double[6];
830  ucoords = new double[6];
831  segType = pIter.currentSegment(coords);
832  segments.add(new Integer(segType));
833  count++;
834  System.arraycopy(coords,0,ucoords,0,coords.length);
835  scaleCoords(coords,pw,ph);
836  switch(segType) {
837  case PathIterator.SEG_MOVETO:
838  if(subPaths>0) {
839  closed = ((int)ux0==(int)usX && (int)uy0==(int)usY);
840  if(closed && (int)ux0!=(int)usX && (int)uy0!=(int)usY) { // this may only happen after a SEG_CLOSE
841  xCoords.add(new Integer(((Integer)xCoords.elementAt(0)).intValue()));
842  yCoords.add(new Integer(((Integer)yCoords.elementAt(0)).intValue()));
843  }
844  if (rois!=null) {
845  roiType = guessType(count, linesOnly, curvesOnly, closed);
846  Roi r = createRoi(xCoords, yCoords, roiType);
847  if (r!=null)
848  rois.addElement(r);
849  }
850  xCoords = new Vector();
851  yCoords = new Vector();
852  count = 1;
853  }
854  subPaths++;
855  usX = ucoords[0];
856  usY = ucoords[1];
857  ux0 = ucoords[0];
858  uy0 = ucoords[1];
859  sX = coords[0];
860  sY = coords[1];
861  x0 = coords[0];
862  y0 = coords[1];
863  handles.add(new Point2D.Double(ucoords[0],ucoords[1]));
864  xCoords.add(new Integer((int)ucoords[0]));
865  yCoords.add(new Integer((int)ucoords[1]));
866  closed = false;
867  break;
868  case PathIterator.SEG_LINETO:
869  linesOnly = linesOnly & true;
870  curvesOnly = curvesOnly & false;
871  pathLength += Math.sqrt(Math.pow((y0-coords[1]),2.0)+Math.pow((x0-coords[0]),2.0));
872  ux0 = ucoords[0];
873  uy0 = ucoords[1];
874  x0 = coords[0];
875  y0 = coords[1];
876  handles.add(new Point2D.Double(ucoords[0],ucoords[1]));
877  xCoords.add(new Integer((int)ucoords[0]));
878  yCoords.add(new Integer((int)ucoords[1]));
879  closed = ((int)ux0==(int)usX && (int)uy0==(int)usY);
880  break;
881  case PathIterator.SEG_QUADTO:
882  linesOnly = linesOnly & false;
883  curvesOnly = curvesOnly & true;
884  curve = new QuadCurve2D.Double(x0,y0,coords[0],coords[2],coords[2],coords[3]);
885  pathLength += qBezLength((QuadCurve2D.Double)curve);
886  ux0 = ucoords[2];
887  uy0 = ucoords[3];
888  x0 = coords[2];
889  y0 = coords[3];
890  handles.add(new Point2D.Double(ucoords[0],ucoords[1]));
891  handles.add(new Point2D.Double(ucoords[2],ucoords[3]));
892  xCoords.add(new Integer((int)ucoords[2]));
893  yCoords.add(new Integer((int)ucoords[3]));
894  closed = ((int)ux0==(int)usX && (int)uy0==(int)usY);
895  break;
896  case PathIterator.SEG_CUBICTO:
897  linesOnly = linesOnly & false;
898  curvesOnly = curvesOnly & true;
899  curve = new CubicCurve2D.Double(x0,y0,coords[0],coords[1],coords[2],coords[3],coords[4],coords[5]);
900  pathLength += cBezLength((CubicCurve2D.Double)curve);
901  ux0 = ucoords[4];
902  uy0 = ucoords[5];
903  x0 = coords[4];
904  y0 = coords[5];
905  handles.add(new Point2D.Double(ucoords[0],ucoords[1]));
906  handles.add(new Point2D.Double(ucoords[2],ucoords[3]));
907  handles.add(new Point2D.Double(ucoords[4],ucoords[5]));
908  xCoords.add(new Integer((int)ucoords[4]));
909  yCoords.add(new Integer((int)ucoords[5]));
910  closed = ((int)ux0==(int)usX && (int)uy0==(int)usY);
911  break;
912  case PathIterator.SEG_CLOSE:
913  if((int)ux0 != (int)usX && (int)uy0 != (int)usY) pathLength += Math.sqrt(Math.pow((x0-sX),2.0) + Math.pow((y0-sY),2.0));
914  closed = true;
915  break;
916  default:
917  break;
918  }
919  pIter.next();
920  if(pIter.isDone()) {
921  if(closed && (int)x0!=(int)sX && (int)y0!=(int)sY) { // this may only happen after a SEG_CLOSE
922  xCoords.add(new Integer(((Integer)xCoords.elementAt(0)).intValue()));
923  yCoords.add(new Integer(((Integer)yCoords.elementAt(0)).intValue()));
924  }
925  if (rois!=null) {
926  roiType = guessType(count, linesOnly, curvesOnly, closed);
927  Roi r = createRoi(xCoords, yCoords, roiType);
928  if (r!=null)
929  rois.addElement(r);
930  }
931  }
932  }
933  params[0] = pathLength;
934  //IJ.log("parsePath:"+ (System.currentTimeMillis()-start));
935  return result;
936  }
937 
938  /**********************************************************************************/
939  /*** Drawing and Image routines ****/
940  /**********************************************************************************/
941 
943  public void draw(Graphics g) {
944  if(ic==null) return;
945  AffineTransform aTx = (((Graphics2D)g).getDeviceConfiguration()).getDefaultTransform();
946  g.setColor(ROIColor);
947  mag = ic.getMagnification();
948  Rectangle r = ic.getSrcRect();
949  aTx.setTransform(mag,0.0,0.0,mag,-r.x*mag,-r.y*mag);
950  aTx.translate(x, y); //wsr
951  //IJ.log("draw:"+x+" "+y);
952  //((Graphics2D)g).transform(aTx); //wsr
953  ((Graphics2D)g).draw(aTx.createTransformedShape(shape)); //wsr
954  /* //wsr
955  if(state!=CONSTRUCTING && clipboard==null) {
956  PathIterator pIter;
957  if(flatten) pIter = getFlatteningPathIterator(shape, flatness);
958  else pIter = shape.getPathIterator(new AffineTransform());
959  Vector handles = new Vector();
960  parsePath(pIter, null, null, null, handles);
961  if(handles==null || handles.size()==0) return;
962  int size2 = HANDLE_SIZE/2;
963  int cnt=0;
964  for(Enumeration e = handles.elements(); e.hasMoreElements();)
965  {
966  Point2D.Double pt = (Point2D.Double)e.nextElement();
967  cnt++;
968  int hx = (int)pt.getX();
969  int hy = (int)pt.getY();
970  drawHandle(g, hx-size2, hy-size2);
971  }
972  }
973  */
974  showStatus();
975  if (updateFullWindow) { updateFullWindow = false; imp.draw(); }
976  }
977 
982  public void drawPixels() {
983  ImageProcessor ip = imp.getProcessor();
984  PathIterator pIter = getFlatteningPathIterator(shape,flatness);
985  double[] coords;
986  double x0 = Double.NaN;
987  double y0 = Double.NaN;
988  double sX = Double.NaN;
989  double sY = Double.NaN;
990  for(;!pIter.isDone();)
991  {
992  coords = new double[6];
993  int segType = pIter.currentSegment(coords);
994  switch(segType)
995  {
996  case PathIterator.SEG_MOVETO:
997  x0 = coords[0];
998  y0 = coords[1];
999  sX = coords[0];
1000  sY = coords[1];
1001  ip.moveTo(x+(int)coords[0], y+(int)coords[1]);
1002  break;
1003  case PathIterator.SEG_LINETO:
1004  x0 = coords[0];
1005  y0 = coords[1];
1006  ip.lineTo(x+(int)coords[0], y+(int)coords[1]);
1007  break;
1008  case PathIterator.SEG_CLOSE:
1009  if(x0 != sX && y0 != sY) ip.lineTo(x+(int)sX, y+(int)sY);
1010  break;
1011  default: break;
1012  }
1013  pIter.next();
1014  }
1015  }
1016 
1020  if(shape==null) return null;
1021  if (cachedMask!=null) return cachedMask;
1022  BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
1023  Graphics2D g2d = bi.createGraphics();
1024  g2d.setColor(Color.white);
1025  //AffineTransform at = new AffineTransform();
1026  //at.translate(-x, -y);
1027  //g2d.fill(at.createTransformedShape(shape));
1028  g2d.fill(shape);
1029  Raster raster = bi.getRaster();
1030  DataBufferByte buffer = (DataBufferByte)raster.getDataBuffer();
1031  byte[] mask = buffer.getData();
1032  cachedMask = new ByteProcessor(width, height, mask, null);
1033  return cachedMask;
1034  }
1035 
1036  /**********************************************************************************/
1037  /*** Field accessors ****/
1038  /**********************************************************************************/
1039 
1041  //public boolean getForceTrace() { return forceTrace; }
1042 
1044  //public boolean getForceAngle() { return forceAngle; }
1045 
1047  //public boolean getFlatten() { return flatten; }
1048 
1050  //public double getFlatness() { return flatness; }
1051 
1053  //public double getMaxError() { return maxerror;}
1054 
1056  //public int getMaxPoly() { return maxPoly; }
1057 
1059  //public boolean isValid() { return type!=NO_TYPE; }
1060 
1062  Shape getShape(){ return shape; }
1063 
1071  boolean setShape(Shape rhs) {
1072  boolean result = true;
1073  if (rhs==null) {IJ.write("rhs null!"); return false;}
1074  if(shape.equals(rhs)) {IJ.write("shouldn't set it to itself"); return false;}
1075  shape = rhs;
1076  type = Roi.COMPOSITE;
1077  Rectangle rect = shape.getBounds();
1078  //x = rect.x; //wsr
1079  //y = rect.y;
1080  width = rect.width;
1081  height = rect.height;
1082  return true;
1083  }
1084 
1085  /**********************************************************************************/
1086  /*** Other helpers ****/
1087  /**********************************************************************************/
1088 
1090  private int min(int[] array) {
1091  int val = array[0];
1092  for (int i=1; i<array.length; i++) val = Math.min(val,array[i]);
1093  return val;
1094  }
1095 
1097  private int max(int[] array) {
1098  int val = array[0];
1099  for (int i=1; i<array.length; i++) val = Math.max(val,array[i]);
1100  return val;
1101  }
1102 
1103 }