How to calculate the area of a java.awt.geom.Area?
I am looking for a way to calculat开发者_JAVA技巧e the area, in pixels, of an arbitrary instance of java.awt.geom.Area
.
The background: I have Shape
s in my applications that may overlap. I want to know how much one Shape
overlaps another. The Shape
s may be skewed, rotated, etc. If I had a function area(Shape)
(or Area
), I could use the intersection of two Shape
s like so:
double fractionObscured(Shape bottom, Shape top) {
Area intersection = new Area(bottom);
intersection.intersect(new Area(top));
return area(intersection) / area(bottom);
}
To find the area of a polygon using the following snippet:
int sum = 0;
for (int i = 0; i < n -1; i++)
{
sum = sum + x[i]*y[i+1] - y[i]*x[i+1];
}
// (sum / 2) is your area.
System.out.println("The area is : " + (sum / 2));
Here n is the total number of vertices and x[i] and y[i] are the x and y coordinates of a vertex i. Note that for this algorithm to work, the polygon must be closed. It doesent work on open polygons.
You can find mathematical alogrithms related to polygons here. You need to convert it to code yourself:)
I've used this class to approximate the area of a shape in one of my projects. It's slow but at high resolution it may still be faster than counting pixels (because the cost of counting pixels grows quadratically with resolution, but the number of line segments on the perimeter grows linearly.)
import static java.lang.Double.NaN;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.FlatteningPathIterator;
import java.awt.geom.Line2D;
import java.awt.geom.PathIterator;
public abstract class Areas {
public static double approxArea(Area area, double flatness, int limit) {
PathIterator i =
new FlatteningPathIterator(area.getPathIterator(identity),
flatness,
limit);
return approxArea(i);
}
public static double approxArea(Area area, double flatness) {
PathIterator i = area.getPathIterator(identity, flatness);
return approxArea(i);
}
public static double approxArea(PathIterator i) {
double a = 0.0;
double[] coords = new double[6];
double startX = NaN, startY = NaN;
Line2D segment = new Line2D.Double(NaN, NaN, NaN, NaN);
while (! i.isDone()) {
int segType = i.currentSegment(coords);
double x = coords[0], y = coords[1];
switch (segType) {
case PathIterator.SEG_CLOSE:
segment.setLine(segment.getX2(), segment.getY2(), startX, startY);
a += hexArea(segment);
startX = startY = NaN;
segment.setLine(NaN, NaN, NaN, NaN);
break;
case PathIterator.SEG_LINETO:
segment.setLine(segment.getX2(), segment.getY2(), x, y);
a += hexArea(segment);
break;
case PathIterator.SEG_MOVETO:
startX = x;
startY = y;
segment.setLine(NaN, NaN, x, y);
break;
default:
throw new IllegalArgumentException("PathIterator contains curved segments");
}
i.next();
}
if (Double.isNaN(a)) {
throw new IllegalArgumentException("PathIterator contains an open path");
} else {
return 0.5 * Math.abs(a);
}
}
private static double hexArea(Line2D seg) {
return seg.getX1() * seg.getY2() - seg.getX2() * seg.getY1();
}
private static final AffineTransform identity =
AffineTransform.getQuadrantRotateInstance(0);
}
One approach would be to fill()
each scaled and transformed Shape
with a different color using a suitable AlphaComposite
and count the overlapping pixels in the underlying Raster
.
Addendum 1: Using this calculator to see the effect of AlphaComposite.Xor
shows that the intersetion of any two opaque colors is zero.
Addendum 2: Counting pixels may have performance problems; sampling may help. If each Shape
is reasonably convex, it may be possible to estimate the overlap from the ratio of the intersect()
area to the sum of the areas of the Shape
s' getBounds2D()
. For example,
Shape s1, s2 ...
Rectangle2D r1 = s1.getBounds2D();
Rectangle2D r2 = s2.getBounds2D();
Rectangle2D r3 = new Rectangle2D.Double();
Rectangle2D.intersect(r1, r2, r3);
double overlap = area(r3) / (area(r1) + area(r2));
...
private double area(Rectangle2D r) {
return r.getWidth() * r.getHeight();
}
You may need to validate the results empirically.
I would comment if I could. Suraj, your algorithm is correct, but the code should be
int sum = 0;
for (int i = 0; i < npoints ; i++)
{
sum = sum + Xs[i]*Ys[(i+1)%npoints] - Ys[i]*Xs[(i+1)%npoints];
}
return Math.abs(sum / 2);
In your code last vertice is not taken into account. Just a small edit :)
The given answer is not accurate , I have found that following solution gives much better results
private int calcAreaSize(Area area){
int sum = 0;
float xBegin=0, yBegin=0, xPrev=0, yPrev=0, coords[] = new float[6];
for (PathIterator iterator1 = area.getPathIterator(null, 0.1); !iterator1.isDone(); iterator1.next()){
switch (iterator1.currentSegment(coords))
{
case PathIterator.SEG_MOVETO:
xBegin = coords[0]; yBegin = coords[1];
break;
case PathIterator.SEG_LINETO:
// the well-known trapez-formula
sum += (coords[0] - xPrev) * (coords[1] + yPrev) / 2.0;
break;
case PathIterator.SEG_CLOSE:
sum += (xBegin - xPrev) * (yBegin + yPrev) / 2.0;
break;
default:
// curved segments cannot occur, because we have a flattened ath
throw new InternalError();
}
xPrev = coords[0]; yPrev = coords[1];
}
return sum;
}
精彩评论