【问题标题】:Java PathIterator - How do I accurately calculate center of Shape object?Java PathIterator - 如何准确计算 Shape 对象的中心?
【发布时间】:2014-02-23 20:11:32
【问题描述】:

我正在尝试使用 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 不能解决我的问题怎么办?

【问题讨论】:

    标签: java shapes centroid path-iterator


    【解决方案1】:

    正如已经指出的那样,PathIterator 返回不同类型的段。如果只考虑 SEG_LINETO 中涉及的点,应该已经获得了满意的结果。但是,考虑到其他形状也可能存在 SEG_QUADTO 和 SEG_CUBICTO。使用 flattening 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);
        }
    
    }
    

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

        我不确定您是否错误地使用它,但您没有考虑到PathIterator 的某个方面。 PathIterator 与其说是几何形状,不如说是绘制时应该采用的路径。所以它的点也代表了“笔”应该走的路径类型。例如,对于 Rectangle,路径包含以下部分:

        1. SEG_MOVETO
        2. SEG_LINETO
        3. SEG_LINETO
        4. SEG_LINETO
        5. SEG_LINETO
        6. SEG_CLOSE

        因为显然路径应该:

        • 从笔所在的位置移动,而不是绘图。
        • 从笔接下来绘制的任何内容中关闭这条路径。

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

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

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-11-02
          • 1970-01-01
          • 2015-05-04
          • 2013-11-20
          • 1970-01-01
          • 1970-01-01
          • 2019-02-14
          相关资源
          最近更新 更多