确定一条弧线是否 contains/covers 另一条弧线
Determining if an arc contains/covers another arc
首先,我使用 Graphics drawArc 和 fillArc 方法随机绘制两条弧线。一个弧,比如 arc1,比另一个弧,比如 arc2 大。
现在我想看看 arc1 是否包含(全部或部分)arc2。我尝试了各种方法但无济于事。例如,首先计算它们之间的距离,然后取这两者的点积,看看它是否大于第一个弧的半径乘以其方向的余弦。
仍然没有成功,将不胜感激提供的任何帮助或建议。
是否有 better/another 方法来实现此目的?
是否也可以估计 arc1 覆盖了 arc2 的多少?谢谢,
我会给你一个简单的解决方案,它适用于任何形状 - 不仅是弧形:
public Vector measureArea(int[] pix) {
int i;
Vector v=new Vector();
for(i=0; i<pix.length; i++)
if((pix[i]&0x00ffffff)==0x00000000) v.add(i);
return v;
}
这会找到属于该区域的像素:您可以按如下方式填充弧线,然后调用此函数:
BufferedImage bim=new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics g=bim.getGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, w, h);
g.setColor(Color.black);
g2.fillArc(x, y, 2*w/16, 2*h/16, 270, 250);
int[] pix=bim.getRGB(0, 0, w, h, null, 0, w);
Vector v=measureArea(pix);
重复第二条弧,然后找到共同点。
for(i=0; i<v.size(); i++) {
int I=((Integer)v.get(i)).intValue();
for(j=0; j<v2.size(); j++) {
int J=((Integer)v2.get(j)).intValue();
if(I==J) ..... // do something
}
}
如果您想要更多的数学方法,您必须根据圆(或可能是两个楔形)来定义实心圆弧,并找到这些形状相交的面积。
java 中还有第三种使用区域的方法。
Area a=new Area(new Arc2D.Double(x+3*w/4-w/16, y+h/4-h/16, 2*w/16, 2*h/16, 270, 250, Arc2D.OPEN));
Area a2=new Area(new Arc2D.Double(x+3*w/4, y+h/4, 2*w/16, 2*h/16, 270, 200, Arc2D.OPEN));
Area intrsct=new Area(new Arc2D.Double(x+3*w/4-w/16, y+h/4-h/16, 2*w/16, 2*h/16, 270, 250, Arc2D.OPEN));
intrsct.intersect(a2);
现在 intrsct 有了交集。
如果我们将其扩展为简单的形状,我们有:
Arc2D.Double a=new Arc2D.Double(x+3*w/4-w/16, y+h/4-h/16, 2*w/16, 2*h/16, 270, 250, Arc2D.OPEN);
Arc2D.Double a2=new Arc2D.Double(x+3*w/4, y+h/4, 2*w/16, 2*h/16, 270, 200, Arc2D.OPEN);
Rectangle b=a.getBounds();
int intrsct=0;
for(i=0; i<b.getWidth(); i++)
for(j=0; j<b.getHeight(); j++)
if(a.contains(b.x+i, b.y+j) && a2.contains(b.x+i, b.y+j)) intrsct++;
第四种方法。
--
如果您想要给定颜色的圆弧,您需要在第一种方法中检查该颜色。所以我们改变测量面积如下:
public Vector measureArea(int[] pix, int color) {
int i;
Vector v=new Vector();
int c=color&0x00ffffff;
for(i=0; i<pix.length; i++)
if((pix[i]&0x00ffffff)==c) v.add(i);
return v;
}
并称之为 measureArea(pix, Color.red.getRGB()) 例如。
并确保清除要单独计算的每个形状的图像:
public Image init( Graphics g )
{
bim=new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
g=bim.getGraphics();
g.setColor(Color.yellow);
g.fillRect(0, 0, w, h);
g.setColor(Color.red);
g.fillArc(x, y, 300, 300, 270, 75); // 2*w/16, 2*h/16
int[] pix=bim.getRGB(0, 0, w, h, null, 0, w);
Vector v1=measureArea(pix, Color.red.getRGB());
g.setColor(Color.yellow);
g.fillRect(0, 0, w, h);
g.setColor(Color.blue);
g.fillArc(x+100, y+100, 150, 150, 270, 45); //2*w/32, 2*h/32,
pix=bim.getRGB(0, 0, w, h, null, 0, w);
Vector v2=measureArea(pix, Color.blue.getRGB());
System.out.println( intersect(v1, v2) );
return bim;
}
注意事项 3:使用 Areas 的方法与颜色无关 - 如果可行,请使用它。
后面如果形状比较复杂可以用带像素的方法:
要将所有形状画在一起,只需按照您现在所做的操作:将它们放在一张图片中。要测量面积,请使用另一个图像 bim2,在其中连续绘制每个形状调用测量面积函数清除图像等 - 它不必在任何地方显示 - 你有另一个图像可以一起显示所有形状。我希望这有效。
lists several options. As mentioned in a comment, I'd recommend Area
-based apprach for the generic case. Although area computations (like computing the intersection,对于这个例子)可能很昂贵,它们可能是基于图像和纯分析方法之间的一个很好的权衡:
- 基于图像的方法提出了一些问题,例如关于图像大小。此外,"large" 形状的运行时和内存消耗可能很大(想象一下覆盖 1000x1000 像素区域的形状)。
- 纯解析解可能在数学上相当复杂。可以考虑将其分解为更简单的任务,这当然是可行的,但并非微不足道。也许更重要的是:这种方法不能推广到其他
Shape
类型。
使用基于 Area
的解决方案,计算两个任意形状 s0
和 s1
之间的交集(可以是 Arc2D
,或 any 其他形状)相当简单:
Area a = new Area(s0);
a.intersect(new Area(s1));
(就是这样)。
旁注:可以考虑进行保守测试:如果边界体积不相交,则形状可以不相交。所以对于某些用例,可以考虑做这样的事情:
Shape s0 = ...;
Shape s1 = ...;
if (!s0.getBounds().intersects(s1.getBounds()))
{
// The bounds do not intersect. Then the shapes
// can not intersect.
return ...;
}
else
{
// The bounds DO intesect. Perform the Area-based
// intersection computation here:
...
}
剩下的就是计算一个Area
的面积——即交集面积的大小。 Area
class有个方法可以用来检查isEmpty
. But it does not have a method to compute the size of the area. However, this can be computed by converting the resulting area into a polygon using a (flattening!) PathIterator
, and then computing the polygon area as, for example in the answers to this question.
区域是否
可能的棘手之处在于,一般来说,区域可以签名(也就是说,它们可以是positive或negative,取决于多边形的顶点是否在counterclockwise或中给出分别顺时针 顺序)。此外,两个形状之间的交集不一定会产生一个单一的、连接的形状,但可能会产生不同的闭合区域,如图所示:
该图像是来自以下 MCVE 的屏幕截图,它允许使用鼠标在给定形状周围拖动,并打印形状的面积及其交叉点。
这使用了一些用于面积计算的实用方法,这些方法通常取自一组通用的几何实用程序,特别是 shapes,我刚开始收集这些方法)
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Area;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ShapeIntersectionAreaTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new ShapeIntersectionAreaTestPanel());
f.setSize(800,800);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class ShapeIntersectionAreaTestPanel extends JPanel
implements MouseListener, MouseMotionListener
{
private Shape shape0;
private Shape shape1;
private Shape draggedShape;
private Point previousMousePosition;
ShapeIntersectionAreaTestPanel()
{
shape0 = new Arc2D.Double(100, 160, 200, 200, 90, 120, Arc2D.PIE);
shape1 = new Arc2D.Double(300, 400, 100, 150, 220, 260, Arc2D.PIE);
addMouseListener(this);
addMouseMotionListener(this);
}
@Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.RED);
g.fill(shape0);
g.setColor(Color.BLUE);
g.fill(shape1);
Shape intersection =
ShapeIntersectionAreaUtils.computeIntersection(shape0, shape1);
g.setColor(Color.MAGENTA);
g.fill(intersection);
double area0 = Math.abs(
ShapeIntersectionAreaUtils.computeSignedArea(shape0, 1.0));
double area1 = Math.abs(
ShapeIntersectionAreaUtils.computeSignedArea(shape1, 1.0));
double areaIntersection = Math.abs(
ShapeIntersectionAreaUtils.computeSignedArea(intersection, 1.0));
g.setColor(Color.BLACK);
g.setFont(new Font("Monospaced", Font.PLAIN, 12));
g.drawString(String.format("Red area : %10.3f", area0), 10, 20);
g.drawString(String.format("Blue area : %10.3f", area1), 10, 40);
g.drawString(String.format("Intersection area: %10.3f", areaIntersection), 10, 60);
}
@Override
public void mouseDragged(MouseEvent e)
{
int dx = e.getX() - previousMousePosition.x;
int dy = e.getY() - previousMousePosition.y;
AffineTransform at =
AffineTransform.getTranslateInstance(dx, dy);
if (draggedShape == shape0)
{
shape0 = at.createTransformedShape(draggedShape);
draggedShape = shape0;
}
if (draggedShape == shape1)
{
shape1 = at.createTransformedShape(draggedShape);
draggedShape = shape1;
}
repaint();
previousMousePosition = e.getPoint();
}
@Override
public void mouseMoved(MouseEvent e)
{
}
@Override
public void mouseClicked(MouseEvent e)
{
}
@Override
public void mousePressed(MouseEvent e)
{
draggedShape = null;
if (shape0.contains(e.getPoint()))
{
draggedShape = shape0;
}
if (shape1.contains(e.getPoint()))
{
draggedShape = shape1;
}
previousMousePosition = e.getPoint();
}
@Override
public void mouseReleased(MouseEvent e)
{
draggedShape = null;
}
@Override
public void mouseEntered(MouseEvent e)
{
}
@Override
public void mouseExited(MouseEvent e)
{
}
}
// Utility methods related to shape and shape area computations, mostly taken from
// https://github.com/javagl/Geom/blob/master/src/main/java/de/javagl/geom/Shapes.java
class ShapeIntersectionAreaUtils
{
public static Shape computeIntersection(Shape s0, Shape s1)
{
Area a = new Area(s0);
a.intersect(new Area(s1));
return a;
}
/**
* Compute all closed regions that occur in the given shape, as
* lists of points, each describing one polygon
*
* @param shape The shape
* @param flatness The flatness for the shape path iterator
* @return The regions
*/
static List<List<Point2D>> computeRegions(
Shape shape, double flatness)
{
List<List<Point2D>> regions = new ArrayList<List<Point2D>>();
PathIterator pi = shape.getPathIterator(null, flatness);
double coords[] = new double[6];
List<Point2D> region = Collections.emptyList();
while (!pi.isDone())
{
switch (pi.currentSegment(coords))
{
case PathIterator.SEG_MOVETO:
region = new ArrayList<Point2D>();
region.add(new Point2D.Double(coords[0], coords[1]));
break;
case PathIterator.SEG_LINETO:
region.add(new Point2D.Double(coords[0], coords[1]));
break;
case PathIterator.SEG_CLOSE:
regions.add(region);
break;
case PathIterator.SEG_CUBICTO:
case PathIterator.SEG_QUADTO:
default:
throw new AssertionError(
"Invalid segment in flattened path");
}
pi.next();
}
return regions;
}
/**
* Computes the (signed) area enclosed by the given point list.
* The area will be positive if the points are ordered
* counterclockwise, and and negative if the points are ordered
* clockwise.
*
* @param points The points
* @return The signed area
*/
static double computeSignedArea(List<? extends Point2D> points)
{
double sum0 = 0;
double sum1 = 0;
for (int i=0; i<points.size()-1; i++)
{
int i0 = i;
int i1 = i + 1;
Point2D p0 = points.get(i0);
Point2D p1 = points.get(i1);
double x0 = p0.getX();
double y0 = p0.getY();
double x1 = p1.getX();
double y1 = p1.getY();
sum0 += x0 * y1;
sum1 += x1 * y0;
}
Point2D p0 = points.get(0);
Point2D pn = points.get(points.size()-1);
double x0 = p0.getX();
double y0 = p0.getY();
double xn = pn.getX();
double yn = pn.getY();
sum0 += xn * y0;
sum1 += x0 * yn;
double area = 0.5 * (sum0 - sum1);
return area;
}
/**
* Compute the (signed) area that is covered by the given shape.<br>
* <br>
* The area will be positive for regions where the points are
* ordered counterclockwise, and and negative for regions where
* the points are ordered clockwise.
*
* @param shape The shape
* @param flatness The flatness for the path iterator
* @return The signed area
*/
public static double computeSignedArea(Shape shape, double flatness)
{
double area = 0;
List<List<Point2D>> regions = computeRegions(shape, flatness);
for (List<Point2D> region : regions)
{
double signedArea = computeSignedArea(region);
area += signedArea;
}
return area;
}
}
(注:拖动形状的机制不是特别优雅。在实际应用中,这应该是不同的解决方式——这只是为了演示面积计算方法)
首先,我使用 Graphics drawArc 和 fillArc 方法随机绘制两条弧线。一个弧,比如 arc1,比另一个弧,比如 arc2 大。 现在我想看看 arc1 是否包含(全部或部分)arc2。我尝试了各种方法但无济于事。例如,首先计算它们之间的距离,然后取这两者的点积,看看它是否大于第一个弧的半径乘以其方向的余弦。 仍然没有成功,将不胜感激提供的任何帮助或建议。 是否有 better/another 方法来实现此目的? 是否也可以估计 arc1 覆盖了 arc2 的多少?谢谢,
我会给你一个简单的解决方案,它适用于任何形状 - 不仅是弧形:
public Vector measureArea(int[] pix) {
int i;
Vector v=new Vector();
for(i=0; i<pix.length; i++)
if((pix[i]&0x00ffffff)==0x00000000) v.add(i);
return v;
}
这会找到属于该区域的像素:您可以按如下方式填充弧线,然后调用此函数:
BufferedImage bim=new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics g=bim.getGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, w, h);
g.setColor(Color.black);
g2.fillArc(x, y, 2*w/16, 2*h/16, 270, 250);
int[] pix=bim.getRGB(0, 0, w, h, null, 0, w);
Vector v=measureArea(pix);
重复第二条弧,然后找到共同点。
for(i=0; i<v.size(); i++) {
int I=((Integer)v.get(i)).intValue();
for(j=0; j<v2.size(); j++) {
int J=((Integer)v2.get(j)).intValue();
if(I==J) ..... // do something
}
}
如果您想要更多的数学方法,您必须根据圆(或可能是两个楔形)来定义实心圆弧,并找到这些形状相交的面积。
java 中还有第三种使用区域的方法。
Area a=new Area(new Arc2D.Double(x+3*w/4-w/16, y+h/4-h/16, 2*w/16, 2*h/16, 270, 250, Arc2D.OPEN));
Area a2=new Area(new Arc2D.Double(x+3*w/4, y+h/4, 2*w/16, 2*h/16, 270, 200, Arc2D.OPEN));
Area intrsct=new Area(new Arc2D.Double(x+3*w/4-w/16, y+h/4-h/16, 2*w/16, 2*h/16, 270, 250, Arc2D.OPEN));
intrsct.intersect(a2);
现在 intrsct 有了交集。
如果我们将其扩展为简单的形状,我们有:
Arc2D.Double a=new Arc2D.Double(x+3*w/4-w/16, y+h/4-h/16, 2*w/16, 2*h/16, 270, 250, Arc2D.OPEN);
Arc2D.Double a2=new Arc2D.Double(x+3*w/4, y+h/4, 2*w/16, 2*h/16, 270, 200, Arc2D.OPEN);
Rectangle b=a.getBounds();
int intrsct=0;
for(i=0; i<b.getWidth(); i++)
for(j=0; j<b.getHeight(); j++)
if(a.contains(b.x+i, b.y+j) && a2.contains(b.x+i, b.y+j)) intrsct++;
第四种方法。
--
如果您想要给定颜色的圆弧,您需要在第一种方法中检查该颜色。所以我们改变测量面积如下:
public Vector measureArea(int[] pix, int color) {
int i;
Vector v=new Vector();
int c=color&0x00ffffff;
for(i=0; i<pix.length; i++)
if((pix[i]&0x00ffffff)==c) v.add(i);
return v;
}
并称之为 measureArea(pix, Color.red.getRGB()) 例如。
并确保清除要单独计算的每个形状的图像:
public Image init( Graphics g )
{
bim=new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
g=bim.getGraphics();
g.setColor(Color.yellow);
g.fillRect(0, 0, w, h);
g.setColor(Color.red);
g.fillArc(x, y, 300, 300, 270, 75); // 2*w/16, 2*h/16
int[] pix=bim.getRGB(0, 0, w, h, null, 0, w);
Vector v1=measureArea(pix, Color.red.getRGB());
g.setColor(Color.yellow);
g.fillRect(0, 0, w, h);
g.setColor(Color.blue);
g.fillArc(x+100, y+100, 150, 150, 270, 45); //2*w/32, 2*h/32,
pix=bim.getRGB(0, 0, w, h, null, 0, w);
Vector v2=measureArea(pix, Color.blue.getRGB());
System.out.println( intersect(v1, v2) );
return bim;
}
注意事项 3:使用 Areas 的方法与颜色无关 - 如果可行,请使用它。 后面如果形状比较复杂可以用带像素的方法:
要将所有形状画在一起,只需按照您现在所做的操作:将它们放在一张图片中。要测量面积,请使用另一个图像 bim2,在其中连续绘制每个形状调用测量面积函数清除图像等 - 它不必在任何地方显示 - 你有另一个图像可以一起显示所有形状。我希望这有效。
Area
-based apprach for the generic case. Although area computations (like computing the intersection,对于这个例子)可能很昂贵,它们可能是基于图像和纯分析方法之间的一个很好的权衡:
- 基于图像的方法提出了一些问题,例如关于图像大小。此外,"large" 形状的运行时和内存消耗可能很大(想象一下覆盖 1000x1000 像素区域的形状)。
- 纯解析解可能在数学上相当复杂。可以考虑将其分解为更简单的任务,这当然是可行的,但并非微不足道。也许更重要的是:这种方法不能推广到其他
Shape
类型。
使用基于 Area
的解决方案,计算两个任意形状 s0
和 s1
之间的交集(可以是 Arc2D
,或 any 其他形状)相当简单:
Area a = new Area(s0);
a.intersect(new Area(s1));
(就是这样)。
旁注:可以考虑进行保守测试:如果边界体积不相交,则形状可以不相交。所以对于某些用例,可以考虑做这样的事情:
Shape s0 = ...;
Shape s1 = ...;
if (!s0.getBounds().intersects(s1.getBounds()))
{
// The bounds do not intersect. Then the shapes
// can not intersect.
return ...;
}
else
{
// The bounds DO intesect. Perform the Area-based
// intersection computation here:
...
}
剩下的就是计算一个Area
的面积——即交集面积的大小。 Area
class有个方法可以用来检查isEmpty
. But it does not have a method to compute the size of the area. However, this can be computed by converting the resulting area into a polygon using a (flattening!) PathIterator
, and then computing the polygon area as, for example in the answers to this question.
可能的棘手之处在于,一般来说,区域可以签名(也就是说,它们可以是positive或negative,取决于多边形的顶点是否在counterclockwise或中给出分别顺时针 顺序)。此外,两个形状之间的交集不一定会产生一个单一的、连接的形状,但可能会产生不同的闭合区域,如图所示:
该图像是来自以下 MCVE 的屏幕截图,它允许使用鼠标在给定形状周围拖动,并打印形状的面积及其交叉点。
这使用了一些用于面积计算的实用方法,这些方法通常取自一组通用的几何实用程序,特别是 shapes,我刚开始收集这些方法)
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Area;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ShapeIntersectionAreaTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new ShapeIntersectionAreaTestPanel());
f.setSize(800,800);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class ShapeIntersectionAreaTestPanel extends JPanel
implements MouseListener, MouseMotionListener
{
private Shape shape0;
private Shape shape1;
private Shape draggedShape;
private Point previousMousePosition;
ShapeIntersectionAreaTestPanel()
{
shape0 = new Arc2D.Double(100, 160, 200, 200, 90, 120, Arc2D.PIE);
shape1 = new Arc2D.Double(300, 400, 100, 150, 220, 260, Arc2D.PIE);
addMouseListener(this);
addMouseMotionListener(this);
}
@Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.RED);
g.fill(shape0);
g.setColor(Color.BLUE);
g.fill(shape1);
Shape intersection =
ShapeIntersectionAreaUtils.computeIntersection(shape0, shape1);
g.setColor(Color.MAGENTA);
g.fill(intersection);
double area0 = Math.abs(
ShapeIntersectionAreaUtils.computeSignedArea(shape0, 1.0));
double area1 = Math.abs(
ShapeIntersectionAreaUtils.computeSignedArea(shape1, 1.0));
double areaIntersection = Math.abs(
ShapeIntersectionAreaUtils.computeSignedArea(intersection, 1.0));
g.setColor(Color.BLACK);
g.setFont(new Font("Monospaced", Font.PLAIN, 12));
g.drawString(String.format("Red area : %10.3f", area0), 10, 20);
g.drawString(String.format("Blue area : %10.3f", area1), 10, 40);
g.drawString(String.format("Intersection area: %10.3f", areaIntersection), 10, 60);
}
@Override
public void mouseDragged(MouseEvent e)
{
int dx = e.getX() - previousMousePosition.x;
int dy = e.getY() - previousMousePosition.y;
AffineTransform at =
AffineTransform.getTranslateInstance(dx, dy);
if (draggedShape == shape0)
{
shape0 = at.createTransformedShape(draggedShape);
draggedShape = shape0;
}
if (draggedShape == shape1)
{
shape1 = at.createTransformedShape(draggedShape);
draggedShape = shape1;
}
repaint();
previousMousePosition = e.getPoint();
}
@Override
public void mouseMoved(MouseEvent e)
{
}
@Override
public void mouseClicked(MouseEvent e)
{
}
@Override
public void mousePressed(MouseEvent e)
{
draggedShape = null;
if (shape0.contains(e.getPoint()))
{
draggedShape = shape0;
}
if (shape1.contains(e.getPoint()))
{
draggedShape = shape1;
}
previousMousePosition = e.getPoint();
}
@Override
public void mouseReleased(MouseEvent e)
{
draggedShape = null;
}
@Override
public void mouseEntered(MouseEvent e)
{
}
@Override
public void mouseExited(MouseEvent e)
{
}
}
// Utility methods related to shape and shape area computations, mostly taken from
// https://github.com/javagl/Geom/blob/master/src/main/java/de/javagl/geom/Shapes.java
class ShapeIntersectionAreaUtils
{
public static Shape computeIntersection(Shape s0, Shape s1)
{
Area a = new Area(s0);
a.intersect(new Area(s1));
return a;
}
/**
* Compute all closed regions that occur in the given shape, as
* lists of points, each describing one polygon
*
* @param shape The shape
* @param flatness The flatness for the shape path iterator
* @return The regions
*/
static List<List<Point2D>> computeRegions(
Shape shape, double flatness)
{
List<List<Point2D>> regions = new ArrayList<List<Point2D>>();
PathIterator pi = shape.getPathIterator(null, flatness);
double coords[] = new double[6];
List<Point2D> region = Collections.emptyList();
while (!pi.isDone())
{
switch (pi.currentSegment(coords))
{
case PathIterator.SEG_MOVETO:
region = new ArrayList<Point2D>();
region.add(new Point2D.Double(coords[0], coords[1]));
break;
case PathIterator.SEG_LINETO:
region.add(new Point2D.Double(coords[0], coords[1]));
break;
case PathIterator.SEG_CLOSE:
regions.add(region);
break;
case PathIterator.SEG_CUBICTO:
case PathIterator.SEG_QUADTO:
default:
throw new AssertionError(
"Invalid segment in flattened path");
}
pi.next();
}
return regions;
}
/**
* Computes the (signed) area enclosed by the given point list.
* The area will be positive if the points are ordered
* counterclockwise, and and negative if the points are ordered
* clockwise.
*
* @param points The points
* @return The signed area
*/
static double computeSignedArea(List<? extends Point2D> points)
{
double sum0 = 0;
double sum1 = 0;
for (int i=0; i<points.size()-1; i++)
{
int i0 = i;
int i1 = i + 1;
Point2D p0 = points.get(i0);
Point2D p1 = points.get(i1);
double x0 = p0.getX();
double y0 = p0.getY();
double x1 = p1.getX();
double y1 = p1.getY();
sum0 += x0 * y1;
sum1 += x1 * y0;
}
Point2D p0 = points.get(0);
Point2D pn = points.get(points.size()-1);
double x0 = p0.getX();
double y0 = p0.getY();
double xn = pn.getX();
double yn = pn.getY();
sum0 += xn * y0;
sum1 += x0 * yn;
double area = 0.5 * (sum0 - sum1);
return area;
}
/**
* Compute the (signed) area that is covered by the given shape.<br>
* <br>
* The area will be positive for regions where the points are
* ordered counterclockwise, and and negative for regions where
* the points are ordered clockwise.
*
* @param shape The shape
* @param flatness The flatness for the path iterator
* @return The signed area
*/
public static double computeSignedArea(Shape shape, double flatness)
{
double area = 0;
List<List<Point2D>> regions = computeRegions(shape, flatness);
for (List<Point2D> region : regions)
{
double signedArea = computeSignedArea(region);
area += signedArea;
}
return area;
}
}
(注:拖动形状的机制不是特别优雅。在实际应用中,这应该是不同的解决方式——这只是为了演示面积计算方法)