这个奇怪的面积减法问题的原因是什么?

发布于 2024-11-18 06:11:20 字数 9110 浏览 3 评论 0原文

当我编写碰撞检测算法时,我遇到了这个问题。这很奇怪,超出了我的理解范围。

这里的问题是,如果在我的算法中,在函数 tryMove() 中呈现,我将 pottialArea 添加到 moveLineArea我正在检测 spaceTestArea (由 moveLineArea 创建)中的变化,在减去所有单位占用的区域后,我与一个不存在的单位发生了碰撞甚至接近x=280,y=120,其中移动单元位于 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);
    }
}

这是它产生的输出:

******TEST for method tryMove()******
TEST 1: this will FAIL
No move --- a unit is on the way. Conflicting point is=java.awt.Point[x=280,y=120]; for i=6; where moving unit point is=java.awt.Point[x=1880,y=120]; the unit is moving to=java.awt.Point[x=1914,y=126]
Result=false

TEST 2: this will be OK
Move OK.
Result=true
******TEST for method tryMove2()******
TEST 1: this will be OK
Move OK.
Result=true

TEST 2: this will be OK
Move OK.
Result=true

为了让您更好地看到问题,我有两个图像来展示两个端点,第一个 1914, 126 当方法失败时,第二个 1914 , 127 正常时。

我们遇到了问题。

这里一切都OK。

如果需要更多描述,我会尽快回答。预先感谢大家。

编辑1: 正如 @trashgod 所建议的,我确实尝试并实现了一个使用 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;
}

While I was coding an algorithm of collision detection I have come up at this problem. It is something weird which is beyond my understanding.

The problem here is that if in my algorithm, presented in function tryMove(), I add potentialArea to moveLineArea and I am doing detection of change in the spaceTestArea (which is created from the moveLineArea) after subtracting areas taken by all units, I have a collision with a unit that is not even close x=280,y=120, where moving unit is at x=1880,y=120, and it is moving to x=1914,y=126.

I would like to know what might be the reason of this issue and what to do in order to avoid it in future.

I must say I have a temporary solution (tryMove2()) but please do not let it influence your thinking, i.e. I do not like this solution and I strongly believe that the first solution (tryMove()) should work and it must be me who forgot about something.

Please see below the code presenting the problem.

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);
    }
}

And this is the output it is producing:

******TEST for method tryMove()******
TEST 1: this will FAIL
No move --- a unit is on the way. Conflicting point is=java.awt.Point[x=280,y=120]; for i=6; where moving unit point is=java.awt.Point[x=1880,y=120]; the unit is moving to=java.awt.Point[x=1914,y=126]
Result=false

TEST 2: this will be OK
Move OK.
Result=true
******TEST for method tryMove2()******
TEST 1: this will be OK
Move OK.
Result=true

TEST 2: this will be OK
Move OK.
Result=true

In order for you to see the problem better I have two images presenting it for two endPoints, first 1914, 126 when the method fails, and second 1914, 127 when it is OK.

Here we have the problem.

Here is all OK.

If more description is needed I will answer ASAP. Thank you all in advance.

EDIT1:
As suggested by @trashgod I did tried and implemented a solution which uses intersect() method. I do not like that for every test you must create a new object. Can you suggest some optimization for this algorithm.

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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

初见终念 2024-11-25 06:11:20

我猜您正在与其中一种方式发生冲突 java.awt.geom.Area 可以变为空。如果有帮助的话,我拍了一张照片,如下。或者,您可以使用 createTransformedShape()contains() 吗?

附录:tryMove3() 显示正确。如果它的性能确实更差,我会看到几种可能性:

在此处输入图像描述

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);

I'm guessing you're running afoul of one of the ways java.awt.geom.Area can become empty. If it helps, I took a picture, below. Alternatively, could you use createTransformedShape() and contains()?

Addendum: tryMove3() appears correct. If it really profiles worse, I see several possibilities:

  • Cache any static Area, perhaps those associated with each center point.

  • Do a rough vicinity check based on getBounds() or a quadtree and skip remote pairs.

enter image description here

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);
兮颜 2024-11-25 06:11:20

不知何故,第六次减法改变了 spaceTestArea 的几何形状,使其不等于 moveLineArea,这可以通过对两者进行异或来直观地看到。 Area API 仅声明 equals 测试两个几何图形是否相等,但遗憾的是没有详细说明。例如,如果您将调试代码添加到程序中,您将看到此异或区域仅在第六次减法中弹出。我还没有弄清楚为什么,但也许如果您创建一个 ExclusiveOr 图像的图像,您就会看到。

你的代码和我的调试语句(有些有点多余,抱歉):

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);
   }
}

Somehow the 6th subtraction is changing the geometry of spaceTestArea so that it is not equal to moveLineArea and this may be seen visually by exlusiveOr'ing the two. The Area API states only that equals tests if the two geometries are equal but unfortunately doesn't go into further detail. For instance if you add debug code to your program, you'll see that this exclusiveOr area only pops up in the 6th subtraction. I've not yet figured out why, but perhaps if you create an image of the excluxiveOr image you'll see.

Your code with my debug statements (some a bit redundant, sorry):

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);
   }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文