Java PathIterator - Как мне точно рассчитать центр объекта Shape?

Я пытаюсь использовать 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 ответа

Решение

Как уже указывалось, 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);
    }

}

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

Я не уверен, что вы используете его неправильно, но вы не учитываете аспект PathIterator. PathIterator представляет собой не столько геометрическую фигуру, сколько путь, который нужно пройти при рисовании. Таким образом, его точки также представляют тип пути, по которому должен идти "перо". Например, для прямоугольника путь состоит из следующих сегментов:

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

Потому что, очевидно, путь должен:

  • Перемещайте, а не рисуйте, где бы ни была ручка.
  • Закройте этот путь от того, что перо рисует дальше.

Тип сегмента - это возвращаемое значение currentSegment, Если вы хотите захватить только точки, которые находятся на многоугольнике, вы можете проверить наличие отрезка 'line to':

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

Это будет работать для простых полигонов, таких как Rectangle. Для данного Rectangle он вернет [0.5, 0.5], то есть, я полагаю, интересующий вас результат.

С другой стороны, есть фигуры, которые не являются полигонами, поэтому я буду осторожен с этим подходом.

Другие вопросы по тегам