Java PathIterator - 如何准确计算Shape对象的中心?

时间:2014-02-23 20:11:32

标签: java shapes centroid path-iterator

我正在尝试使用PathIterator来计算任何Shape对象的中心,以便可以考虑弯曲路径,但是在找到标准1x1矩形的中心后,我的getCenter()方法返回点:

Point2D.Double[0.3333333333333333, 0.3333333333333333]

我的getCenter()方法:

shape = new Rectangle2D.Double(0, 0, 1, 1);


public Point2D.Double getCenter()
        {
            ArrayList<Point2D.Double> points = new ArrayList<Point2D.Double>();
            double[] arr = new double[6];
            for(PathIterator pi = shape.getPathIterator(null); !pi.isDone(); pi.next())
            {
                pi.currentSegment(arr);
                points.add(new Point2D.Double(arr[0], arr[1]));
            }

            double cX = 0;
            double cY = 0;
            for(Point2D.Double p : points)
            {
                cX += p.x;
                cY += p.y;
            }
                    System.out.println(points.toString());
            return new Point2D.Double(cX / points.size(), cY / points.size());
        }

我发现在打印points.toString()时,我在控制台中得到了这个:

[Point2D.Double[0.0, 0.0], Point2D.Double[1.0, 0.0], Point2D.Double[1.0, 1.0], Point2D.Double[0.0, 1.0], Point2D.Double[0.0, 0.0], Point2D.Double[0.0, 0.0]]

我注意到点数组中有六个条目,而不是我期望的四个条目,因为输入的Shape对象是Rectangle2D.Double(0,0,1,1)。显然,它比我想要的多两倍(0,0),我很困惑为什么会这样。它是PathIterator.isDone()方法的结果吗?我使用不正确吗?如果PathIterator不能解决我的问题会怎样?

3 个答案:

答案 0 :(得分:0)

PathIterator定义了不同类型的细分,你应该注意这个事实。您的示例中有6个段,因为它还会返回SEG_MOVETO段(定义子路径的起点)和SEG_CLOSE(子路径末尾)。如果你只想获得形状线的端点,你应该改变你的代码:

    for(PathIterator pi = shape.getPathIterator(null); !pi.isDone(); pi.next())
    {
        if(pi.currentSegment(arr) == PathIterator.SEG_LINETO) {
            points.add(new Point2D.Double(arr[0], arr[1]));
        }
    }

答案 1 :(得分:0)

我不确定你是否正在使用 ,但你没有考虑PathIterator的一个方面。 PathIterator并不代表几何形状,而是绘制时应该采用的路径。所以它的要点也代表了“笔”应该采用的路径类型。例如,对于Rectangle,路径将生成以下段:

  1. SEG_MOVETO
  2. SEG_LINETO
  3. SEG_LINETO
  4. SEG_LINETO
  5. SEG_LINETO
  6. SEG_CLOSE
  7. 因为显然道路应该:

    • 从笔前的任何地方移动,而不是画画。
    • 关闭此路径,无论笔下一步画什么。

    段的类型是currentSegment的返回值。如果您只想捕获多边形上的点,则可以检查“行到”段:

    if(pi.currentSegment(arr) == PathIterator.SEG_LINETO) {
        points.add(new Point2D.Double(arr[0], arr[1]));
    }
    

    这适用于像Rectangle这样的简单多边形。对于给定的Rectangle,它将返回[0.5,0.5],这是我假设您感兴趣的结果。

    另一方面,有些形状不是多边形,所以我会小心这种方法。

答案 2 :(得分:0)

正如已经指出的那样,PathIterator返回不同类型的段。只考虑SEG_LINETO中涉及的要点时,您应该已经获得了满意的结果。但是,请考虑其他形状中也可能存在SEG_QUADTO和SEG_CUBICTO。使用展平 PathIterator可以轻松避免这些问题:使用

创建PathIterator时
PathIterator pi = shape.getPathIterator(null, flatness);

具有适当的平整度,那么它只包含直线段。

import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

public class ShapeCenter
{
    public static void main(String[] args)
    {
        System.out.println(computeCenter(new Ellipse2D.Double(-10,-10,20,20)));
        System.out.println(computeCenter(new Rectangle2D.Double(0,0,1,1)));
    }

    public static Point2D computeCenter(Shape shape)
    {
        final double flatness = 0.1;
        PathIterator pi = shape.getPathIterator(null, flatness);
        double coords[] = new double[6];
        double sumX = 0;
        double sumY = 0;
        int numPoints = 0;
        while (!pi.isDone())
        {
            int s = pi.currentSegment(coords);
            switch (s)
            {
                case PathIterator.SEG_MOVETO:
                    // Ignore
                    break;

                case PathIterator.SEG_LINETO:
                    sumX += coords[0]; 
                    sumY += coords[1]; 
                    numPoints++;
                    break;

                case PathIterator.SEG_CLOSE:
                    // Ignore
                    break;

                case PathIterator.SEG_QUADTO:
                    throw new AssertionError(
                        "SEG_QUADTO in flattening path iterator");
                case PathIterator.SEG_CUBICTO:
                    throw new AssertionError(
                        "SEG_CUBICTO in flattening path iterator");
            }
            pi.next();
        }
        double x = sumX / numPoints;
        double y = sumY / numPoints;
        return new Point2D.Double(x,y);
    }

}