Java 这个奇怪的面积减法问题的原因是什么?
在编写碰撞检测算法的过程中,我遇到了这个问题。这是我无法理解的奇怪的事情 这里的问题是如果在我的算法中,在函数Java 这个奇怪的面积减法问题的原因是什么?,java,collision-detection,area,Java,Collision Detection,Area,在编写碰撞检测算法的过程中,我遇到了这个问题。这是我无法理解的奇怪的事情 这里的问题是如果在我的算法中,在函数tryMove()中,我将potentialArea添加到moveLineArea中,并在减去所有单元的面积后检测spaceTestArea(由moveLineArea创建)中的变化,我与一个甚至不接近的装置发生碰撞,移动装置位于x=1880,y=120,它正在移动到x=1914,y=126 我想知道这一问题的原因可能是什么,以及今后如何避免 我必须说我有一个临时解决方案(tryMove
tryMove()
中,我将potentialArea
添加到moveLineArea
中,并在减去所有单元的面积后检测spaceTestArea
(由moveLineArea
创建)中的变化,我与一个甚至不接近的装置发生碰撞,移动装置位于x=1880,y=120,它正在移动到x=1914,y=126
我想知道这一问题的原因可能是什么,以及今后如何避免
我必须说我有一个临时解决方案(tryMove2()
),但请不要让它影响你的思维,也就是说,我不喜欢这个解决方案,我坚信第一个解决方案(tryMove()
)应该有效,一定是我忘记了什么
请参阅下面显示问题的代码
import java.awt.Point;
import java.awt.Polygon;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.List;
/**
* Test showing some unexpected and weird behaviour of area subtraction.
* @author Konrad Borowiecki
*/
public class TestTryMove {
private static final List<Point> unitCenterPoints = new ArrayList<Point>();
static{
unitCenterPoints.add(new Point(1720, 120));
unitCenterPoints.add(new Point(1880, 120));
unitCenterPoints.add(new Point(1800, 200));
unitCenterPoints.add(new Point(1720, 280));
unitCenterPoints.add(new Point(1880, 280));
unitCenterPoints.add(new Point(120, 120));
unitCenterPoints.add(new Point(280, 120));
unitCenterPoints.add(new Point(200, 200));
unitCenterPoints.add(new Point(120, 280));
unitCenterPoints.add(new Point(280, 280));
unitCenterPoints.add(new Point(120, 1720));
unitCenterPoints.add(new Point(280, 1720));
unitCenterPoints.add(new Point(200, 1800));
unitCenterPoints.add(new Point(120, 1880));
unitCenterPoints.add(new Point(280, 1880));
}
public static void main(String[] args) {
int[] xpointsOK = new int[]{1876, 1884, 1918, 1910};//for Move OK
int[] ypointsOK = new int[]{139, 101, 108, 146};//for Move OK
Polygon lineOK = new Polygon(xpointsOK, ypointsOK, xpointsOK.length);
int[] xpointsFAIL = new int[]{1877, 1883, 1917, 1911};//for problem no move
int[] ypointsFAIL = new int[]{139, 101, 107, 145};//for problem no move
Polygon lineFAIL = new Polygon(xpointsFAIL, ypointsFAIL, xpointsFAIL.length);
Point endPointCPOK = new Point(1914, 127);//Move OK
Point endPointCPFAIL = new Point(1914, 126);//problem no move
//where in both cases it should be move OK
System.out.println("******TEST for method tryMove()******");
System.out.println("TEST 1: this will FAIL");
System.out.println("Result="+tryMove(endPointCPFAIL, lineFAIL));
System.out.println("\nTEST 2: this will be OK");
System.out.println("Result="+tryMove(endPointCPOK, lineOK));
System.out.println("******TEST for method tryMove2()******");
System.out.println("TEST 1: this will be OK");
System.out.println("Result="+tryMove2(endPointCPFAIL, lineFAIL));
System.out.println("\nTEST 2: this will be OK");
System.out.println("Result="+tryMove2(endPointCPOK, lineOK));
}
/**
* Tests if a unit represented by point of index 1 in the list of
* unitCenterPoints (i.e. [1880, 120]) can make a move to the given endPointCP.
* (Please notice we are ignoring this unit in the algorithm
* i.e. if(i != movingUnitIndexInTheArray)).
* @param endPointCP the point where the unit moves to.
* @param line the line of the move of the thickness equal to units width (mod=40),
* drawn between the current unit's center point and the endPointCP,
* represented as a polygon object.
* @return true if move possible; false otherwise.
*/
private static boolean tryMove(Point endPointCP, Polygon line){
Area potentialArea = getArea(endPointCP);
Area moveLineArea = new Area(line);
moveLineArea.add(potentialArea);
//this area is used for testing if nothing stays on the way of the move
Area spaceTestArea = new Area(moveLineArea);
//the index of the unit making the move in the unitCenterPoints list
int movingUnitIndexInTheArray = 1;
//we are subtracting from spaceTestArea all areas of units
for(int i = 0; i < unitCenterPoints.size(); i++)
if(i != movingUnitIndexInTheArray) {
Point p = unitCenterPoints.get(i);
Area uArea = getArea(p);
spaceTestArea.subtract(uArea);
//we have intersection then return false, we cannot make this move
if(spaceTestArea.isEmpty() || !spaceTestArea.equals(moveLineArea)) {
System.out.println("No move --- a unit is on the way. "
+ "Conflicting point is="+p +"; for i="+i);
return false;
}
}
System.out.println("Move OK.");
return true;
}
private static boolean tryMove2(Point endPointCP, Polygon line){
Area potentialArea = getArea(endPointCP);
Area moveLineArea = new Area(line);
//test if unit can move to the new position
Area potentialTestArea = new Area(potentialArea);
//this area is used for testing if nothing stays on the way of the move
Area spaceTestArea = new Area(moveLineArea);
//the index of the unit making the move in the unitCenterPoints list
int movingUnitIndexInTheArray = 1;
//we are subtracting from spaceTestArea all areas of units
for(int i = 0; i < unitCenterPoints.size(); i++)
if(i != movingUnitIndexInTheArray) {
Point p = unitCenterPoints.get(i);
Area uArea = getArea(p);
spaceTestArea.subtract(uArea);
potentialTestArea.subtract(uArea);
//we have intersection then return false, we cannot make this move
if(spaceTestArea.isEmpty() || !spaceTestArea.equals(moveLineArea)
|| potentialTestArea.isEmpty() || !potentialTestArea.equals(potentialArea)) {
System.out.println("No move --- a unit is on the way. "
+ "Conflicting point is="+p +"; for i="+i);
return false;
}
}
System.out.println("Move OK.");
return true;
}
/**
* Gets the area taken by a unit given the unit's center point.
* @param p the center point of a unit.
* @return circle area.
*/
private static Area getArea(Point p) {
int mod = 40;//this is width and height of a unit
Ellipse2D circle = new Ellipse2D.Double(p.x - mod / 2, p.y - mod / 2, mod, mod);
return new Area(circle);
}
}
为了让您更好地看到这个问题,我有两幅图像显示了两个端点,第一幅是方法失败时的1914126
,第二幅是方法正常时的1914127
如果需要更多的描述,我会尽快回答。提前谢谢大家
EDIT1:
根据@trashgood的建议,我尝试并实现了一个使用intersect()
方法的解决方案。我不喜欢每次测试都必须创建一个新对象。你能为这个算法提出一些优化建议吗
private static boolean tryMove3(Point endPointCP, Polygon line){
Area potentialArea = getArea(endPointCP);
Area moveLineArea = new Area(line);
moveLineArea.add(potentialArea);
//this area is used for testing if nothing stays on the way of the move
//the index of the unit making the move in the unitCenterPoints list
int movingUnitIndexInTheArray = 1;
//we are subtracting from spaceTestArea all areas of units
for(int i = 0; i < unitCenterPoints.size(); i++)
if(i != movingUnitIndexInTheArray) {
Point p = unitCenterPoints.get(i);
Area uArea = getArea(p);
Area spaceTestArea = new Area(moveLineArea);
spaceTestArea.intersect(uArea);
//we have intersection then return false, we cannot make this move
if(!spaceTestArea.isEmpty()) {
System.out.println("No move --- a unit is on the way. "
+ "Conflicting point is="+p +"; for i="+i
+ "; where moving unit point is="
+unitCenterPoints.get(movingUnitIndexInTheArray)
+"; the unit is moving to="+endPointCP
+"; spaceTestArea.isEmpty()="+spaceTestArea.isEmpty());
return false;
}
}
System.out.println("Move OK.");
return true;
}
private静态布尔tryMove3(点端点cp,多边形线){
面积电位面积=getArea(endPointCP);
面积移动线面积=新面积(线);
移动线面积增加(潜在面积);
//此区域用于测试移动途中是否没有任何东西停留
//在unitCenterPoints列表中进行移动的单元的索引
int movingingindinexinthearray=1;
//我们从spaceTestArea中减去所有单位面积
对于(int i=0;i
不知何故,第六次减法改变了spaceTestArea的几何结构,使其不等于moveLineArea,这可以通过排除这两种方法直观地看到。Area API仅声明,如果两个几何体相等,则等于测试,但遗憾的是,没有进一步详细说明。例如,如果您将调试代码添加到程序中,您将看到此独占EOR区域仅在第6次减法中弹出。我还不知道为什么,但也许如果你创建一个排他形象的形象,你会看到
您的代码和我的调试语句(有些有点多余,抱歉):
import java.awt.*;
导入java.awt.geom.*;
导入java.util.ArrayList;
导入java.util.Formatter;
导入java.util.List;
公共类TestTryMove{
私有静态最终列表unitCenterPoints=new ArrayList();
静止的{
添加(新点(1720、120));
添加(新点(1880120));
添加(新点(1800200));
添加(新点(1720280));
增加(新点(1880280));
添加(新点(120120));
增加(新的点(280120));
添加(新点(200200));
添加(新点(120280));
添加(新点(280280));
添加(新点(1201720));
增加(新点(2801720));
添加(新点(2001800));
增加(新点(1201880));
增加(新点(2801880));
}
公共静态void main(字符串[]args){
int[]xpointsOK=newint[]{1876188419181910};//用于移动确定
int[]ypointsOK=newint[]{139101108146};//用于移动确定
多边形lineOK=新多边形(xpointsOK、ypointsOK、xpointsOK.length);
int[]xpointsFAIL=new int[]{1877188319171911};//用于问题编号
//移动
int[]ypointsFAIL=newint[]{139,101,107,145};//对于问题,不移动
多边形lineFAIL=新多边形(xpointsFAIL、ypointsFAIL、,
xpointsFAIL.length);
PointEndpointCPOK=新点(1914127);//移动确定
PointEndpointCPFail=新点(1914126);//问题无移动
//在这两种情况下,都应该是移动OK
System.out.println(“******方法tryMove()的测试*******”);
System.out.println(“测试1:这将失败”);
System.out.println(“Result=“+tryMove(endPointCPFAIL,lineFAIL));
System.out.println(“\n测试2:这样就可以了”);
System.out.println(“Result=“+tryMove(endPointCPOK,lineOK));
System.out.println(“******方法tryMove2()*******的测试”);
System.out.println(“测试1:一切正常”);
System.out.println(“Result=“+tryMove2(endPointCPFAIL,lineFAIL))
private static boolean tryMove3(Point endPointCP, Polygon line){
Area potentialArea = getArea(endPointCP);
Area moveLineArea = new Area(line);
moveLineArea.add(potentialArea);
//this area is used for testing if nothing stays on the way of the move
//the index of the unit making the move in the unitCenterPoints list
int movingUnitIndexInTheArray = 1;
//we are subtracting from spaceTestArea all areas of units
for(int i = 0; i < unitCenterPoints.size(); i++)
if(i != movingUnitIndexInTheArray) {
Point p = unitCenterPoints.get(i);
Area uArea = getArea(p);
Area spaceTestArea = new Area(moveLineArea);
spaceTestArea.intersect(uArea);
//we have intersection then return false, we cannot make this move
if(!spaceTestArea.isEmpty()) {
System.out.println("No move --- a unit is on the way. "
+ "Conflicting point is="+p +"; for i="+i
+ "; where moving unit point is="
+unitCenterPoints.get(movingUnitIndexInTheArray)
+"; the unit is moving to="+endPointCP
+"; spaceTestArea.isEmpty()="+spaceTestArea.isEmpty());
return false;
}
}
System.out.println("Move OK.");
return true;
}
import java.awt.*;
import java.awt.geom.*;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;
public class TestTryMove {
private static final List<Point> unitCenterPoints = new ArrayList<Point>();
static {
unitCenterPoints.add(new Point(1720, 120));
unitCenterPoints.add(new Point(1880, 120));
unitCenterPoints.add(new Point(1800, 200));
unitCenterPoints.add(new Point(1720, 280));
unitCenterPoints.add(new Point(1880, 280));
unitCenterPoints.add(new Point(120, 120));
unitCenterPoints.add(new Point(280, 120));
unitCenterPoints.add(new Point(200, 200));
unitCenterPoints.add(new Point(120, 280));
unitCenterPoints.add(new Point(280, 280));
unitCenterPoints.add(new Point(120, 1720));
unitCenterPoints.add(new Point(280, 1720));
unitCenterPoints.add(new Point(200, 1800));
unitCenterPoints.add(new Point(120, 1880));
unitCenterPoints.add(new Point(280, 1880));
}
public static void main(String[] args) {
int[] xpointsOK = new int[]{1876, 1884, 1918, 1910};// for Move OK
int[] ypointsOK = new int[]{139, 101, 108, 146};// for Move OK
Polygon lineOK = new Polygon(xpointsOK, ypointsOK, xpointsOK.length);
int[] xpointsFAIL = new int[]{1877, 1883, 1917, 1911};// for problem no
// move
int[] ypointsFAIL = new int[]{139, 101, 107, 145};// for problem no move
Polygon lineFAIL = new Polygon(xpointsFAIL, ypointsFAIL,
xpointsFAIL.length);
Point endPointCPOK = new Point(1914, 127);// Move OK
Point endPointCPFAIL = new Point(1914, 126);// problem no move
// where in both cases it should be move OK
System.out.println("******TEST for method tryMove()******");
System.out.println("TEST 1: this will FAIL");
System.out.println("Result=" + tryMove(endPointCPFAIL, lineFAIL));
System.out.println("\nTEST 2: this will be OK");
System.out.println("Result=" + tryMove(endPointCPOK, lineOK));
System.out.println("******TEST for method tryMove2()******");
System.out.println("TEST 1: this will be OK");
System.out.println("Result=" + tryMove2(endPointCPFAIL, lineFAIL));
System.out.println("\nTEST 2: this will be OK");
System.out.println("Result=" + tryMove2(endPointCPOK, lineOK));
}
private static boolean tryMove(Point endPointCP, Polygon line) {
Area potentialArea = getArea(endPointCP);
Area moveLineArea = new Area(line);
System.out.println(showBounds("moveLine before add", moveLineArea));
moveLineArea.add(potentialArea);
System.out.println(showBounds("moveLine after add ", moveLineArea));
// this area is used for testing if nothing stays on the way of the move
Area spaceTestArea = new Area(moveLineArea);
System.out.println(showBounds("spaceTest", spaceTestArea));
Area xOr = (Area)spaceTestArea.clone();
xOr.exclusiveOr(moveLineArea);
System.out.printf("Pre %s %s %s%n", showBounds("STA", spaceTestArea), showBounds("MLA", moveLineArea),
showBounds("xOr", xOr));
// the index of the unit making the move in the unitCenterPoints list
int movingUnitIndexInTheArray = 1;
// we are subtracting from spaceTestArea all areas of units
for (int i = 0; i < unitCenterPoints.size(); i++) {
if (i != movingUnitIndexInTheArray) {
Point p = unitCenterPoints.get(i);
Area uArea = getArea(p);
spaceTestArea.subtract(uArea);
xOr = (Area)spaceTestArea.clone();
xOr.exclusiveOr(moveLineArea);
System.out.printf("i: %02d %s %s %s %s%n", i,
showBounds("STA", spaceTestArea),
showBounds("MLA", moveLineArea),
showBounds("uA", uArea),
showBounds("xOr", xOr));
// we have intersection then return false, we cannot make this move
if (spaceTestArea.isEmpty() || !spaceTestArea.equals(moveLineArea)) {
System.out.println("spaceTestArea.isEmpty()? " + spaceTestArea.isEmpty());
System.out.println("!spaceTestArea.equals(moveLineArea)? " + !spaceTestArea.equals(moveLineArea));
System.out.println("moveLineArea bounds: " + moveLineArea.getBounds());
System.out.println("No move --- a unit is on the way. "
+ "Conflicting point is=" + p + "; for i=" + i);
return false;
}
}
}
System.out.println("Move OK.");
return true;
}
public static String showBounds(String name, Area area) {
Rectangle rect = area.getBounds();
StringBuilder resultSB = new StringBuilder();
Formatter formatter = new Formatter(resultSB);
formatter.format("%5s [%04d, %04d, %04d, %04d]", name, rect.x, rect.y, rect.width, rect.height);
return resultSB.toString();
}
private static boolean tryMove2(Point endPointCP, Polygon line) {
Area potentialArea = getArea(endPointCP);
Area moveLineArea = new Area(line);
// test if unit can move to the new position
Area potentialTestArea = new Area(potentialArea);
// this area is used for testing if nothing stays on the way of the move
Area spaceTestArea = new Area(moveLineArea);
// the index of the unit making the move in the unitCenterPoints list
int movingUnitIndexInTheArray = 1;
// we are subtracting from spaceTestArea all areas of units
for (int i = 0; i < unitCenterPoints.size(); i++)
if (i != movingUnitIndexInTheArray) {
Point p = unitCenterPoints.get(i);
Area uArea = getArea(p);
spaceTestArea.subtract(uArea);
potentialTestArea.subtract(uArea);
// we have intersection then return false, we cannot make this move
if (spaceTestArea.isEmpty() || !spaceTestArea.equals(moveLineArea)
|| potentialTestArea.isEmpty()
|| !potentialTestArea.equals(potentialArea)) {
System.out.println("No move --- a unit is on the way. "
+ "Conflicting point is=" + p + "; for i=" + i);
return false;
}
}
System.out.println("Move OK.");
return true;
}
/**
* Gets the area taken by a unit given the unit's center point.
*
* @param p
* the center point of a unit.
* @return circle area.
*/
private static Area getArea(Point p) {
int mod = 40;// this is width and height of a unit
Ellipse2D circle = new Ellipse2D.Double(p.x - mod / 2, p.y - mod / 2,
mod, mod);
return new Area(circle);
}
}
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new JPanel(){
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.scale(10, 10);
g2d.translate(-1875, -100);
g2d.setColor(Color.green);
g2d.draw(lineOK);
g2d.setColor(Color.green.darker());
g2d.drawRect(endPointCPOK.x, endPointCPOK.y, 1, 1);
g2d.setColor(Color.red);
g2d.draw(lineFAIL);
g2d.setColor(Color.red.darker());
g2d.drawRect(endPointCPFAIL.x, endPointCPFAIL.y, 1, 1);
}
});
f.pack();
f.setSize(450, 500);
f.setLocationRelativeTo(null);
f.setVisible(true);