ij.gui.Roi
(2D Regions Of Interest) implemented in terms of java.awt.Shape.
* A ShapeRoi is constructed from a ij.gui.Roi
object, or as a result of logical operators
* (i.e., union, intersection, exclusive or, and subtraction) provided by this class. These operators use the package
* java.awt.geom
as a backend. true
.
*/
private static final int MAXPOLY = 10; // I hate arbitrary values !!!!
private static final int OR=0, AND=1, XOR=2, NOT=3;
private static final double SHAPE_TO_ROI=-1.0;
/**The java.awt.Shape
encapsulated by this object.*/
private Shape shape;
/**The instance value of the maximum tolerance (MAXERROR) allowed in calculating the
* length of the curve segments of this ROI's shape.
*/
private double maxerror = ShapeRoi.MAXERROR;
/**The instance value of the coefficient (FLATNESS) used to
* obtain a flattened version of this ROI's shape.
*/
private double flatness = ShapeRoi.FLATNESS;
/**The instance value of MAXPOLY.*/
private int maxPoly = ShapeRoi.MAXPOLY;
/**If true then methods that manipulate this ROI's shape will work on
* a flattened version of the shape. */
private boolean flatten;
/**Flag which specifies how Roi objects will be constructed from closed (sub)paths having more than
* MAXPOLY
and composed exclusively of line segments.
* If true
then (sub)path will be parsed into a
* {@link ij.gui.Roi#TRACED_ROI}; else, into a {@link ij.gui.Roi#FREEROI}. */
private boolean forceTrace = false;
/**Flag which specifies if Roi objects constructed from open (sub)paths composed of only two line segments
* will be of type {@link ij.gui.Roi#ANGLE}.
* If true
then (sub)path will be parsed into a {@link ij.gui.Roi#ANGLE};
* else, into a {@link ij.gui.Roi#POLYLINE}. */
private boolean forceAngle = false;
private Vector savedRois;
/** Constructs a ShapeRoi from an Roi. */
public ShapeRoi(Roi r) {
this(r, ShapeRoi.FLATNESS, ShapeRoi.MAXERROR, false, false, false, ShapeRoi.MAXPOLY);
}
/** Constructs a ShapeRoi from a Shape. */
public ShapeRoi(Shape s) {
super(s.getBounds());
AffineTransform at = new AffineTransform();
at.translate(-x, -y);
shape = new GeneralPath(at.createTransformedShape(s));
type = COMPOSITE;
}
/** Constructs a ShapeRoi from a Shape. */
public ShapeRoi(int x, int y, Shape s) {
super(x, y, s.getBounds().width, s.getBounds().height);
shape = new GeneralPath(s);
type = COMPOSITE;
}
/**Creates a ShapeRoi object from a "classical" ImageJ ROI.
* @param r An ij.gui.Roi object
* @param flatness The flatness factor used in convertion of curve segments into line segments.
* @param maxerror Error correction for calculating length of Bezeir curves.
* @param forceAngle flag used in the conversion of Shape objects to Roi objects (see {@link #shapeToRois()}.
* @param forceTrace flag for conversion of Shape objects to Roi objects (see {@link #shapeToRois()}.
* @param flatten if true
then the shape of this ROI will be flattened
* (i.e., curve segments will be aproximated by line segments).
* @param maxPoly Roi objects constructed from shapes composed of linear segments fewer than this
* value will be of type {@link ij.gui.Roi#POLYLINE} or {@link ij.gui.Roi#POLYGON}; conversion of
* shapes with linear segments more than this value will result in Roi objects of type
* {@link ij.gui.Roi#FREELINE} or {@link ij.gui.Roi#FREEROI} (see {@link #shapeToRois()}).
*/
ShapeRoi(Roi r, double flatness, double maxerror, boolean forceAngle, boolean forceTrace, boolean flatten, int maxPoly) {
super(r.startX, r.startY, r.width, r.height);
this.type = COMPOSITE;
this.flatness = flatness;
this.maxerror = maxerror;
this.forceAngle = forceAngle;
this.forceTrace = forceTrace;
this.maxPoly= maxPoly;
this.flatten = flatten;
state = r.getState();
setImage(r.imp);
shape = roiToShape((Roi)r.clone());
//IJ.log("ShapeRoi:"+x+" "+y);
if(ic!=null) {
Graphics g = ic.getGraphics();
draw(g);
g.dispose();
}
}
/** Constructs a ShapeRoi from an array of variable length path segments. Each
segment consists of the segment type followed by 0-3 end points and control
points. Depending on the type, a segment uses from 1 to 7 elements of the array. */
public ShapeRoi(float[] shapeArray) {
super(0,0,null);
shape = makeShapeFromArray(shapeArray);
Rectangle r = shape.getBounds();
x = r.x;
y = r.y;
width = r.width;
height = r.height;
state = NORMAL;
oldX=x; oldY=y; oldWidth=width; oldHeight=height;
AffineTransform at = new AffineTransform();
at.translate(-x, -y);
shape = new GeneralPath(at.createTransformedShape(shape));
flatness = ShapeRoi.FLATNESS;
maxerror = ShapeRoi.MAXERROR;
maxPoly = ShapeRoi.MAXPOLY;
flatten = false;
type = COMPOSITE;
}
/**Returns a deep copy of this. */
public synchronized Object clone() { // the equivalent of "operator=" ?
ShapeRoi sr = (ShapeRoi)super.clone();
sr.type = COMPOSITE;
sr.flatness = flatness;
sr.maxerror = maxerror;
sr.forceAngle = forceAngle;
sr.forceTrace = forceTrace;
//sr.setImage(imp); //wsr
sr.setShape(ShapeRoi.cloneShape(shape));
return sr;
}
/**Returns a deep copy of the argument. */
static Shape cloneShape(Shape rhs) {
if(rhs==null) return null;
if(rhs instanceof Rectangle2D.Double) { return (Rectangle2D.Double)((Rectangle2D.Double)rhs).clone(); }
else if(rhs instanceof Ellipse2D.Double) { return (Ellipse2D.Double)((Ellipse2D.Double)rhs).clone(); }
else if(rhs instanceof Line2D.Double) { return (Line2D.Double)((Line2D.Double)rhs).clone(); }
else if(rhs instanceof Polygon) { return new Polygon(((Polygon)rhs).xpoints, ((Polygon)rhs).ypoints, ((Polygon)rhs).npoints); }
else if(rhs instanceof GeneralPath) { return (GeneralPath)((GeneralPath)rhs).clone(); }
return new GeneralPath(); // dodgy !!!
}
/**********************************************************************************/
/*** Logical operations on shaped rois ****/
/**********************************************************************************/
/**Unary union operator.
* The caller is set to its union with the argument.
* @return the union of this
and sr
*/
public ShapeRoi or(ShapeRoi sr) {return unaryOp(sr, OR);}
/**Unary intersection operator.
* The caller is set to its intersection with the argument (i.e., the overlapping regions between the
* operands).
* @return the overlapping regions between this
and sr
*/
public ShapeRoi and(ShapeRoi sr) {return unaryOp(sr, AND);}
/**Unary exclusive or operator.
* The caller is set to the non-overlapping regions between the operands.
* @return the union of the non-overlapping regions of this
and sr
*/
public ShapeRoi xor(ShapeRoi sr) {return unaryOp(sr, XOR);}
/**Unary subtraction operator.
* The caller is set to the result of the operation between the operands.
* @return this
subtracted from sr
*/
public ShapeRoi not(ShapeRoi sr) {return unaryOp(sr, NOT);}
ShapeRoi unaryOp(ShapeRoi sr, int op) {
AffineTransform at = new AffineTransform();
at.translate(x, y);
Area a1 = new Area(at.createTransformedShape(getShape()));
at = new AffineTransform();
at.translate(sr.x, sr.y);
Area a2 = new Area(at.createTransformedShape(sr.getShape()));
switch (op) {
case OR: a1.add(a2); break;
case AND: a1.intersect(a2); break;
case XOR: a1.exclusiveOr(a2); break;
case NOT: a1.subtract(a2); break;
}
Rectangle r = a1.getBounds();
at = new AffineTransform();
at.translate(-r.x, -r.y);
setShape(new GeneralPath(at.createTransformedShape(a1)));
x = r.x;
y = r.y;
return this;
}
/**********************************************************************************/
/*** Interconversions between "regular" rois and shaped rois ****/
/**********************************************************************************/
/**Converts the Roi argument to an instance of java.awt.Shape.
* Currently, the following conversions are supported:Roi class | Roi type | Shape | Winding rule | Flag forceAngle | Flag forceTrace | Flag complexShape |
---|---|---|---|---|---|---|
ij.gui.Roi | Roi.RECTANGLE | java.awt.geom.Rectangle2D.Double | false | false | false | |
ij.gui.OvalRoi | Roi.OVAL | java.awt.geom.Ellipse2D.Double | false | false | false | |
ij.gui.Line | Roi.LINE | java.awt.geom.Line2D.Double | false | false | false | |
ij.gui.PolygonRoi | Roi.POLYGON | java.awt.Polygon | false | false | false | |
ij.gui.PolygonRoi | Roi.FREEROI | closed java.awt.geom.GeneralPath | GeneralPath.WIND_EVEN_ODD | false | false | false |
ij.gui.PolygonRoi | Roi.TRACED_ROI | closed java.awt.geom.GeneralPath | GeneralPath.WIND_EVEN_ODD | false | true | false |
ij.gui.PolygonRoi | Roi.POLYLINE | open java.awt.geom.GeneralPath | GeneralPath.WIND_NON_ZERO | false | false | false |
ij.gui.PolygonRoi | Roi.FREELINE | open java.awt.geom.GeneralPath | GeneralPath.WIND_NON_ZERO | false | false | false |
ij.gui.PolygonRoi | Roi.ANGLE | open java.awt.geom.GeneralPath | GeneralPath.WIND_NON_ZERO | true | false | false |
ij.gui.ShapeRoi | Roi.COMPOSITE | shape of argument | winding rule of argument | flag of argument | flag of argument | flag of argument |
ij.gui.ShapeRoi | ShapeRoi.NO_TYPE | null | false | false | false |
Shape type | Roi class | Roi type |
---|---|---|
java.awt.geom.Rectangle2D.Double | ij.gui.Roi | Roi.RECTANGLE |
java.awt.geom.Ellipse2D.Double | ij.gui.OvalRoi | Roi.OVAL |
java.awt.geom.Line2D.Double | ij.gui.Line | Roi.LINE |
java.awt.Polygon | ij.gui.PolygonRoi | Roi.POLYGON |
java.awt.geom.GeneralPath
is converted following these rules:
Segment types | Number of segments |
Closed path | Value of forceAngle |
Value of forceTrace | Roi type |
---|---|---|---|---|---|
lines only: | 0 | ShapeRoi.NO_TYPE | |||
1 | ShapeRoi.NO_TYPE | ||||
2 | Y | ShapeRoi.NO_TYPE | |||
N | Roi.LINE | ||||
3 | Y | N | Roi.POLYGON | ||
N | Y | Roi.ANGLE | |||
N | N | Roi.POLYLINE | |||
4 | Y | Roi.RECTANGLE | |||
N | Roi.POLYLINE | ||||
<= MAXPOLY | Y | Roi.POLYGON | |||
N | Roi.POLYLINE | ||||
> MAXPOLY | Y | Y | Roi.TRACED_ROI | ||
N | Roi.FREEROI | ||||
N | Roi.FREELINE | ||||
anything else: | <= 2 | ShapeRoi.NO_TYPE | |||
> 2 | ShapeRoi.SHAPE_ROI |
java.awt.geom.GeneralPath
to ij.gui.Roi
.
* @param segments The number of segments that compose the path
* @param linesOnly Indicates wether the GeneralPath object is composed only of SEG_LINETO segments
* @param curvesOnly Indicates wether the GeneralPath object is composed only of SEG_CUBICTO and SEG_QUADTO segments
* @param closed Indicates a closed GeneralPath
* @see #shapeToRois()
* @return a type flag
*/
private int guessType(int segments, boolean linesOnly, boolean curvesOnly, boolean closed) {
//IJ.log("guessType: "+segments+" "+linesOnly+" "+curvesOnly+" "+closed);
closed = true; // lines currently not supported
int roiType = Roi.RECTANGLE;
if (linesOnly) {
switch(segments) {
case 0: roiType = NO_TYPE; break;
case 1: roiType = NO_TYPE; break;
case 2: roiType = (closed ? NO_TYPE : Roi.LINE); break;
case 3: roiType = (closed ? Roi.POLYGON : (forceAngle ? Roi.ANGLE: Roi.POLYLINE)); break;
case 4: roiType = (closed ? Roi.RECTANGLE : Roi.POLYLINE); break;
default:
if (segments <= MAXPOLY)
roiType = closed ? Roi.POLYGON : Roi.POLYLINE;
else
roiType = closed ? (forceTrace ? Roi.TRACED_ROI: Roi.FREEROI): Roi.FREELINE;
break;
}
}
else roiType = segments >=2 ? Roi.COMPOSITE : NO_TYPE;
return roiType;
}
/**Creates a Roi object based on the arguments.
* @see #shapeToRois()
* @param xCoords the x coordinates
* @param yCoords the y coordinates
* @param type the type flag
* @return a ij.gui.Roi object
*/
private Roi createRoi(Vector xCoords, Vector yCoords, int roiType) {
if (roiType==NO_TYPE) return null;
Roi roi = null;
if(xCoords.size() != yCoords.size() || xCoords.size()==0) { return null; }
int[] xPoints = new int[xCoords.size()];
int[] yPoints = new int[yCoords.size()];
for (int i=0; ijava.awt.geom.CubicCurve2D.Double
.
* Please visit {@link Graphics Gems IV} for
* examples of other possible implementations in C and C++.
*/
double cBezLength(CubicCurve2D.Double c) {
double l = 0.0;
double cl = cclength(c);
double pl = cplength(c);
if((pl-cl)/2.0 > maxerror)
{
CubicCurve2D.Double[] cc = cBezSplit(c);
for(int i=0; i<2; i++) l+=cBezLength(cc[i]);
return l;
}
l = 0.5*pl+0.5*cl;
return l;
}
/**Calculates the length of a quadratic Bézier curve specified in double precision.
* The algorithm is based on the theory presented in paper java.awt.geom.CubicCurve2D.Double
.
* Please visit {@link Graphics Gems IV} for
* examples of other possible implementations in C and C++.
*/
double qBezLength(QuadCurve2D.Double c) {
double l = 0.0;
double cl = qclength(c);
double pl = qplength(c);
if((pl-cl)/2.0 > maxerror)
{
QuadCurve2D.Double[] cc = qBezSplit(c);
for(int i=0; i<2; i++) l+=qBezLength(cc[i]);
return l;
}
l = (2.0*pl+cl)/3.0;
return l;
}
/**Splits a cubic Bézier curve in half.
* @param c A cubic Bézier curve to be divided
* @return an array with the left and right cubic Bézier subcurves
*
*/
CubicCurve2D.Double[] cBezSplit(CubicCurve2D.Double c) {
CubicCurve2D.Double[] cc = new CubicCurve2D.Double[2];
for (int i=0; i<2 ; i++) cc[i] = new CubicCurve2D.Double();
c.subdivide(cc[0],cc[1]);
return cc;
}
/**Splits a quadratic Bézier curve in half.
* @param c A quadratic Bézier curve to be divided
* @return an array with the left and right quadratic Bézier subcurves
*
*/
QuadCurve2D.Double[] qBezSplit(QuadCurve2D.Double c) {
QuadCurve2D.Double[] cc = new QuadCurve2D.Double[2];
for(int i=0; i<2; i++) cc[i] = new QuadCurve2D.Double();
c.subdivide(cc[0],cc[1]);
return cc;
}
// c is an array of even length with x0, y0, x1, y1, ... ,xn, yn coordinate pairs
/**Scales a coordinate array with the size calibration of a 2D image.
* The array is modified in place.
* @param c Array of coordinates in double precision with a fixed structure:x0,y0,x1,y1,....,xn,yn
and with even length of 2*(n+1)
.
* @param pw The x-scale of the image.
* @param ph The y-scale of the image.
*
*/
void scaleCoords(double[] c, double pw, double ph) {
int k = c.length/2;
if (2*k!=c.length) return; // bail out if array has odd length
for(int i=0; i parsePath(pIter, par, null, null) ,
*
where par
is a double array of length one, then get the length as par[0]
* @param pIter the PathIterator to be parsed.
* @param params an array with one elemet that will hold the calculated length of path;
* @param segments a Vector of Integer objects that will hold the types of the segments composing the shape's path
* @param rois a Vector that will hold ij.gui.Roi objects constructed from elements of this path
* (see @link #shapeToRois()} for details;
* @param handles a Vector of Point2D.Double objects representing vertices (segment joinings) and
* control points of the curves segments in the iteration order;
* @return true
if successful.*/
boolean parsePath(PathIterator pIter, double[] params, Vector segments, Vector rois, Vector handles) {
//long start = System.currentTimeMillis();
boolean result = true;
if(pIter==null) return false;
double pw = 1.0, ph = 1.0;
if(imp!=null) {
Calibration cal = imp.getCalibration();
pw = cal.pixelWidth;
ph = cal.pixelHeight;
}
Vector xCoords = new Vector();
Vector yCoords = new Vector();
if(segments==null) segments = new Vector();
if(handles==null) handles = new Vector();
//if(rois==null) rois = new Vector();
if(params == null) params = new double[1];
boolean shapeToRoi = params[0]==SHAPE_TO_ROI;
int subPaths = 0; // the number of subpaths
int count = 0;// the number of segments in each subpath w/o SEG_CLOSE; resets to one after each SEG_MOVETO
int roiType = Roi.RECTANGLE;
int segType;
boolean closed = false;
boolean linesOnly = true;
boolean curvesOnly = true;
//boolean success = false;
double[] coords; // scaled coordinates of the path segment
double[] ucoords; // unscaled coordinates of the path segment
double sX = Double.NaN; // start x of subpath (scaled)
double sY = Double.NaN; // start y of subpath (scaled)
double x0 = Double.NaN; // last x in the subpath (scaled)
double y0 = Double.NaN; // last y in the subpath (scaled)
double usX = Double.NaN;// unscaled versions of the above
double usY = Double.NaN;
double ux0 = Double.NaN;
double uy0 = Double.NaN;
double pathLength = 0.0;
Shape curve; // temporary reference to a curve segment of the path
boolean done = false;
while (!done) {
coords = new double[6];
ucoords = new double[6];
segType = pIter.currentSegment(coords);
segments.add(new Integer(segType));
count++;
System.arraycopy(coords,0,ucoords,0,coords.length);
scaleCoords(coords,pw,ph);
switch(segType) {
case PathIterator.SEG_MOVETO:
if(subPaths>0) {
closed = ((int)ux0==(int)usX && (int)uy0==(int)usY);
if(closed && (int)ux0!=(int)usX && (int)uy0!=(int)usY) { // this may only happen after a SEG_CLOSE
xCoords.add(new Integer(((Integer)xCoords.elementAt(0)).intValue()));
yCoords.add(new Integer(((Integer)yCoords.elementAt(0)).intValue()));
}
if (rois!=null) {
roiType = guessType(count, linesOnly, curvesOnly, closed);
Roi r = createRoi(xCoords, yCoords, roiType);
if (r!=null)
rois.addElement(r);
}
xCoords = new Vector();
yCoords = new Vector();
count = 1;
}
subPaths++;
usX = ucoords[0];
usY = ucoords[1];
ux0 = ucoords[0];
uy0 = ucoords[1];
sX = coords[0];
sY = coords[1];
x0 = coords[0];
y0 = coords[1];
handles.add(new Point2D.Double(ucoords[0],ucoords[1]));
xCoords.add(new Integer((int)ucoords[0]));
yCoords.add(new Integer((int)ucoords[1]));
closed = false;
break;
case PathIterator.SEG_LINETO:
linesOnly = linesOnly & true;
curvesOnly = curvesOnly & false;
pathLength += Math.sqrt(Math.pow((y0-coords[1]),2.0)+Math.pow((x0-coords[0]),2.0));
ux0 = ucoords[0];
uy0 = ucoords[1];
x0 = coords[0];
y0 = coords[1];
handles.add(new Point2D.Double(ucoords[0],ucoords[1]));
xCoords.add(new Integer((int)ucoords[0]));
yCoords.add(new Integer((int)ucoords[1]));
closed = ((int)ux0==(int)usX && (int)uy0==(int)usY);
break;
case PathIterator.SEG_QUADTO:
linesOnly = linesOnly & false;
curvesOnly = curvesOnly & true;
curve = new QuadCurve2D.Double(x0,y0,coords[0],coords[2],coords[2],coords[3]);
pathLength += qBezLength((QuadCurve2D.Double)curve);
ux0 = ucoords[2];
uy0 = ucoords[3];
x0 = coords[2];
y0 = coords[3];
handles.add(new Point2D.Double(ucoords[0],ucoords[1]));
handles.add(new Point2D.Double(ucoords[2],ucoords[3]));
xCoords.add(new Integer((int)ucoords[2]));
yCoords.add(new Integer((int)ucoords[3]));
closed = ((int)ux0==(int)usX && (int)uy0==(int)usY);
break;
case PathIterator.SEG_CUBICTO:
linesOnly = linesOnly & false;
curvesOnly = curvesOnly & true;
curve = new CubicCurve2D.Double(x0,y0,coords[0],coords[1],coords[2],coords[3],coords[4],coords[5]);
pathLength += cBezLength((CubicCurve2D.Double)curve);
ux0 = ucoords[4];
uy0 = ucoords[5];
x0 = coords[4];
y0 = coords[5];
handles.add(new Point2D.Double(ucoords[0],ucoords[1]));
handles.add(new Point2D.Double(ucoords[2],ucoords[3]));
handles.add(new Point2D.Double(ucoords[4],ucoords[5]));
xCoords.add(new Integer((int)ucoords[4]));
yCoords.add(new Integer((int)ucoords[5]));
closed = ((int)ux0==(int)usX && (int)uy0==(int)usY);
break;
case PathIterator.SEG_CLOSE:
if((int)ux0 != (int)usX && (int)uy0 != (int)usY) pathLength += Math.sqrt(Math.pow((x0-sX),2.0) + Math.pow((y0-sY),2.0));
closed = true;
break;
default:
break;
}
pIter.next();
done = pIter.isDone() || (shapeToRoi&&rois!=null&&rois.size()==1);
if (done) {
if(closed && (int)x0!=(int)sX && (int)y0!=(int)sY) { // this may only happen after a SEG_CLOSE
xCoords.add(new Integer(((Integer)xCoords.elementAt(0)).intValue()));
yCoords.add(new Integer(((Integer)yCoords.elementAt(0)).intValue()));
}
if (rois!=null) {
roiType = shapeToRoi?TRACED_ROI:guessType(count+1, linesOnly, curvesOnly, closed);
Roi r = createRoi(xCoords, yCoords, roiType);
if (r!=null)
rois.addElement(r);
}
}
}
params[0] = pathLength;
//IJ.log("parsePath:"+ (System.currentTimeMillis()-start));
return result;
}
/**********************************************************************************/
/*** Drawing and Image routines ****/
/**********************************************************************************/
/** Non-destructively draws the shape of this object on the associated ImagePlus. */
public void draw(Graphics g) {
if(ic==null) return;
AffineTransform aTx = (((Graphics2D)g).getDeviceConfiguration()).getDefaultTransform();
g.setColor(instanceColor!=null?instanceColor:ROIColor);
if (stroke!=null) ((Graphics2D)g).setStroke(stroke);
mag = ic.getMagnification();
Rectangle r = ic.getSrcRect();
aTx.setTransform(mag, 0.0, 0.0, mag, -r.x*mag, -r.y*mag);
aTx.translate(x, y);
((Graphics2D)g).draw(aTx.createTransformedShape(shape));
if (Toolbar.getToolId()==Toolbar.OVAL) drawRoiBrush(g);
showStatus();
if (updateFullWindow)
{updateFullWindow = false; imp.draw();}
}
public void drawRoiBrush(Graphics g) {
int size = Toolbar.getBrushSize();
if (size==0) return;
int flags = ic.getModifiers();
if ((flags&16)==0) return; // exit if mouse button up
size = (int)(size*mag);
Point p = ic.getCursorLoc();
int sx = ic.screenX(p.x);
int sy = ic.screenY(p.y);
g.drawOval(sx-size/2, sy-size/2, size, size);
}
/**Draws the shape of this object onto the specified ImageProcessor.
*
This method will always draw a flattened version of the actual shape
* (i.e., all curve segments will be approximated by line segments).
*/
public void drawPixels(ImageProcessor ip) {
PathIterator pIter = getFlatteningPathIterator(shape,flatness);
float[] coords = new float[6];
float sx=0f, sy=0f;
while (!pIter.isDone()) {
int segType = pIter.currentSegment(coords);
switch(segType) {
case PathIterator.SEG_MOVETO:
sx = coords[0];
sy = coords[1];
ip.moveTo(x+(int)sx, y+(int)sy);
break;
case PathIterator.SEG_LINETO:
ip.lineTo(x+(int)coords[0], y+(int)coords[1]);
break;
case PathIterator.SEG_CLOSE:
ip.lineTo(x+(int)sx, y+(int)sy);
break;
default: break;
}
pIter.next();
}
}
/** Returns this ROI's mask pixels as a ByteProcessor with pixels "in" the mask
set to white (255) and pixels "outside" the mask set to black (0). */
public ImageProcessor getMask() {
if(shape==null) return null;
if (cachedMask!=null && cachedMask.getPixels()!=null)
return cachedMask;
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
Graphics2D g2d = bi.createGraphics();
g2d.setColor(Color.white);
//AffineTransform at = new AffineTransform();
//at.translate(-x, -y);
//g2d.fill(at.createTransformedShape(shape));
g2d.fill(shape);
Raster raster = bi.getRaster();
DataBufferByte buffer = (DataBufferByte)raster.getDataBuffer();
byte[] mask = buffer.getData();
cachedMask = new ByteProcessor(width, height, mask, null);
return cachedMask;
}
/**********************************************************************************/
/*** Field accessors ****/
/**********************************************************************************/
/**Retrieves the value of {@link #forceTrace} flag.*/
//public boolean getForceTrace() { return forceTrace; }
/**Retrieves the value of {@link #forceAngle} flag.*/
//public boolean getForceAngle() { return forceAngle; }
/**Retrieves the value of {@link #flatten} flag.*/
//public boolean getFlatten() { return flatten; }
/**Retrieves the value of {@link #flatness}*/
//public double getFlatness() { return flatness; }
/**Retrieves the value of {@link #maxerror}*/
//public double getMaxError() { return maxerror;}
/**Retrieves the value of {@link #maxPoly}*/
//public int getMaxPoly() { return maxPoly; }
/**@return false
if type equals {@link #NO_TYPE}. */
//public boolean isValid() { return type!=NO_TYPE; }
/**Retrieves a reference to the shape of this
. */
Shape getShape(){ return shape; }
/**Sets the java.awt.Shape
object encapsulated by this
* to the argument.
*
This object will hold a (shallow) copy of the shape argument. If a deep copy
* of the shape argumnt is required, then a clone of the argument should be passed
* in; a possible example is setShape(ShapeRoi.cloneShape(shape))
.
* @return false
if the argument is null.
*/
boolean setShape(Shape rhs) {
boolean result = true;
if (rhs==null) return false;
if(shape.equals(rhs)) return false;
shape = rhs;
type = Roi.COMPOSITE;
Rectangle rect = shape.getBounds();
width = rect.width;
height = rect.height;
return true;
}
/**********************************************************************************/
/*** Other helpers ****/
/**********************************************************************************/
/**Returns the element with the smallest value in the array argument.*/
private int min(int[] array) {
int val = array[0];
for (int i=1; i