package ij.gui; import java.awt.*; import java.awt.image.*; import java.awt.geom.*; import java.awt.event.KeyEvent; import java.util.*; import ij.*; import ij.process.*; import ij.measure.*; import ij.plugin.filter.Analyzer; import ij.util.Tools; /**A subclass oftrue 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 *ij.gui.Roi(2D Regions Of Interest) implemented in terms of java.awt.Shape. * A ShapeRoi is constructed from aij.gui.Roiobject, 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.geomas a backend.
* This code is in the public domain. * @author Cezar M.Tigaret*/ public class ShapeRoi extends Roi { /***/ static final int NO_TYPE = 128; /**The maximum tolerance allowed in calculating the length of the curve segments of this ROI's shape.*/ static final double MAXERROR = 1.0e-3; /**Coefficient used to obtain a flattened version of this ROI's shape. A flattened shape is the * closest approximation of the original shape's curve segments with line segments.*/ static final double FLATNESS = 0.1; /**Parsing a shape composed of linear segments less than this value will result in Roi objects of type * {@link ij.gui.Roi#POLYLINE} and {@link ij.gui.Roi#POLYGON} for open and closed shapes, respectively. * Conversion of shapes open and closed with more than MAXPOLY line segments will result, * respectively, in {@link ij.gui.Roi#FREELINE} and {@link ij.gui.Roi#FREEROI} (or * {@link ij.gui.Roi#TRACED_ROI} if {@link #forceTrace} flag is 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; /**Thejava.awt.Shapeencapsulated 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; /**IfMAXPOLYand composed exclusively of line segments. * Iftruethen (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}. * Iftruethen (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; private static Stroke defaultStroke = new BasicStroke(); /** 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 iftruethen 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; shape = roiToShape((Roi)r.clone()); } /** 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 ofthisandsr*/ 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 betweenthisandsr*/ 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 ofthisandsr*/ 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. * @returnthissubtracted fromsr*/ 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())); try { 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; } } catch(Exception e) {} 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; cachedMask = null; 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) {
if (pIter==null || pIter.isDone())
return false;
boolean result = true;
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;
return result;
}
/** Non-destructively draws the shape of this object on the associated ImagePlus. */
public void draw(Graphics g) {
Color color = strokeColor!=null? strokeColor:ROIColor;
boolean isActiveOverlayRoi = !overlay && isActiveOverlayRoi();
//IJ.log("draw: "+overlay+" "+isActiveOverlayRoi);
if (isActiveOverlayRoi)
color = Color.cyan;
if (fillColor!=null) color = fillColor;
g.setColor(color);
AffineTransform aTx = (((Graphics2D)g).getDeviceConfiguration()).getDefaultTransform();
Graphics2D g2d = (Graphics2D)g;
if (stroke!=null && !isActiveOverlayRoi)
g2d.setStroke((ic!=null&&ic.getCustomRoi())||isCursor()?stroke:getScaledStroke());
mag = getMagnification();
int basex=0, basey=0;
if (ic!=null) {
Rectangle r = ic.getSrcRect();
basex=r.x; basey=r.y;
}
aTx.setTransform(mag, 0.0, 0.0, mag, -basex*mag, -basey*mag);
aTx.translate(x, y);
if (fillColor!=null) {
if (isActiveOverlayRoi) {
g2d.setColor(Color.cyan);
g2d.draw(aTx.createTransformedShape(shape));
} else
g2d.fill(aTx.createTransformedShape(shape));
} else
g2d.draw(aTx.createTransformedShape(shape));
if (stroke!=null) g2d.setStroke(defaultStroke);
if (Toolbar.getToolId()==Toolbar.OVAL)
drawRoiBrush(g);
if (state!=NORMAL && imp!=null && imp.getRoi()!=null)
showStatus();
if (updateFullWindow)
{updateFullWindow = false; imp.draw();}
}
public void drawRoiBrush(Graphics g) {
g.setColor(ROIColor);
int size = Toolbar.getBrushSize();
if (size==0 || ic==null)
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 = shape.getPathIterator(new AffineTransform(), 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);
g2d.fill(shape);
Raster raster = bi.getRaster();
DataBufferByte buffer = (DataBufferByte)raster.getDataBuffer();
byte[] mask = buffer.getData();
cachedMask = new ByteProcessor(width, height, mask, null);
cachedMask.setThreshold(255,255,ImageProcessor.NO_LUT_UPDATE);
return cachedMask;
}
/**Returns a reference to the Shape object encapsulated by this ShapeRoi. */
public 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;
}
/**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
Ads help maintain this website.