2016-03-14 4 views
1

Я, прежде всего, рисую две дуги случайным образом с использованием методов drawArc Graphics и fillArc. Одна дуга, например arc1, больше, чем другая дуга, например arc2. Теперь я хочу увидеть, если arc1, содержит (полностью или частично) arc2. Я пробовал разные способы, но безрезультатно. Например, прежде всего вычисляя расстояния между ними, а затем принимая точечный продукт этих двух и видя, если его больше радиуса первой дуги, умноженной на косинус его ориентации. Еще не успел, любая помощь или предложения, которые будут предложены, будут очень благодарны. Есть ли лучший/другой подход для достижения этого? Можно ли также определить, сколько из arc2 покрыто arc1? спасибо,Определение того, содержит ли дуга/покрывает другую дугу.

+1

drawArc и fillArc могут создавать разные вещи: fillArc будет охватывать относительно центра w hile drawArc просто рисует дугу - так неясно, как вы хотите совпадение - нарисуйте пару вещей и объясните, как вы? – gpasch

+0

@gpasch, как дела? Пойдем с fillarc, потому что это тот, который я использовал для большей части. Вот как я их рисовал, я произвольно генерировал аргументы x, y и startAngle метода fillarc, затем я пытаюсь увидеть, какая из сгенерированных дуг фактически пересекается/перекрывается. Если вам нужны визуальные эффекты, я могу легко предоставить образцы снимков экрана. Кроме того, что вы подразумеваете под fillarc, будет касаться центра, а drawArc будет просто рисовать, пожалуйста, объясните немного больше, если вы не возражаете. Благодарю. – guthik

ответ

1

Я дам вам простое решение, который подсчитывает для любой формы - не только дуги:

public Vector measureArea(int[] pix) { 
    int i; 
    Vector v=new Vector(); 
    for(i=0; i<pix.length; i++) 
     if((pix[i]&0x00ffffff)==0x00000000) v.add(i); 
    return v; 
    } 

Это находит пиксели, которые принадлежат к этой области вы могли бы заполнить дугу следующим образом затем вызвать этот функция:

BufferedImage bim=new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); 
Graphics g=bim.getGraphics(); 
g.setColor(Color.white); 
g.fillRect(0, 0, w, h); 
g.setColor(Color.black); 
g2.fillArc(x, y, 2*w/16, 2*h/16, 270, 250); 
int[] pix=bim.getRGB(0, 0, w, h, null, 0, w); 
Vector v=measureArea(pix); 

Повторите со второй дугой, затем найдите общие точки.

for(i=0; i<v.size(); i++) { 
    int I=((Integer)v.get(i)).intValue(); 
    for(j=0; j<v2.size(); j++) { 
    int J=((Integer)v2.get(j)).intValue(); 
    if(I==J) ..... // do something 
    } 
} 

Если вы хотите больше математического подхода необходимо определить заполненную дугу по окружности (или, может быть, два клиньев) и найти область пересекающая эти формы.

Существует третий подход с использованием областей в java.

Area a=new Area(new Arc2D.Double(x+3*w/4-w/16, y+h/4-h/16, 2*w/16, 2*h/16, 270, 250, Arc2D.OPEN)); 
Area a2=new Area(new Arc2D.Double(x+3*w/4, y+h/4, 2*w/16, 2*h/16, 270, 200, Arc2D.OPEN)); 
Area intrsct=new Area(new Arc2D.Double(x+3*w/4-w/16, y+h/4-h/16, 2*w/16, 2*h/16, 270, 250, Arc2D.OPEN)); 
intrsct.intersect(a2); 

Теперь intrsct имеет пересечение.

Если мы расширим это простые формы мы имеем:

Arc2D.Double a=new Arc2D.Double(x+3*w/4-w/16, y+h/4-h/16, 2*w/16, 2*h/16, 270, 250, Arc2D.OPEN); 
Arc2D.Double a2=new Arc2D.Double(x+3*w/4, y+h/4, 2*w/16, 2*h/16, 270, 200, Arc2D.OPEN); 
Rectangle b=a.getBounds(); 
int intrsct=0; 
for(i=0; i<b.getWidth(); i++) 
for(j=0; j<b.getHeight(); j++) 
    if(a.contains(b.x+i, b.y+j) && a2.contains(b.x+i, b.y+j)) intrsct++; 

Четвертый подход.

-

Если вы хотите дугу заданного цвета вам нужно проверить, что цвет в первом приближении. Таким образом, мы изменяем область измерения следующим образом:

public Vector measureArea(int[] pix, int color) { 
    int i; 
    Vector v=new Vector(); 
    int c=color&0x00ffffff; 
    for(i=0; i<pix.length; i++) 
     if((pix[i]&0x00ffffff)==c) v.add(i); 
    return v; 
    } 

и называют его measureArea (пикс, Color.red.getRGB()), например.

И убедитесь, что вы очистить изображение для каждой формы, чтобы рассчитывать на своем собственном:

public Image init(Graphics g) 
    { 
     bim=new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); 
     g=bim.getGraphics(); 
     g.setColor(Color.yellow); 
     g.fillRect(0, 0, w, h); 
     g.setColor(Color.red); 
     g.fillArc(x, y, 300, 300, 270, 75); // 2*w/16, 2*h/16 
     int[] pix=bim.getRGB(0, 0, w, h, null, 0, w); 
     Vector v1=measureArea(pix, Color.red.getRGB()); 
     g.setColor(Color.yellow); 
     g.fillRect(0, 0, w, h); 
     g.setColor(Color.blue); 
     g.fillArc(x+100, y+100, 150, 150, 270, 45); //2*w/32, 2*h/32, 
     pix=bim.getRGB(0, 0, w, h, null, 0, w); 
     Vector v2=measureArea(pix, Color.blue.getRGB()); 
     System.out.println(intersect(v1, v2)); 
     return bim; 
    } 

Примечание 3: метод с территориями, не зависит от цвета - использовать, что, если он работает. Метод с пикселями можно использовать позже, если у вас сложная форма:

Чтобы нарисовать все фигуры, просто сделайте то, что вы сейчас делаете: держите их на одном изображении. Для измерения области используйте другое изображение bim2, где вы рисуете каждую форму, последовательно вызывайте функцию области измерения, очищайте изображение и т. Д. - его не нужно показывать где-либо - у вас есть другое изображение, чтобы показать все фигуры вместе. Надеюсь, это сработает.

+0

Большое спасибо, я в значительной степени понимаю последние два, но не первый. if (I == J), означает ли это, что они имеют одинаковые пиксели и, таким образом, пересекаются o.O? Я собираюсь попробовать их, когда я вернусь домой. – guthik

+0

Да, они имеют одинаковый пиксель, поэтому вы можете считать пересечение, например, или сделать что-либо - нарисовать пересечение и т. Д. – gpasch

+0

ОК отлично, потому что я планирую считать, сколько из таких дуг пересекается с каждой дугой, итеративно. поэтому я думаю, что этот метод лучше подходит для этой задачи. О, я не могу дождаться, чтобы сесть и попробовать этот выход. – guthik

1

answer by gpash содержит несколько вариантов. Как уже упоминалось в комментарии, я бы рекомендовал оценку Area для общего случая.Хотя площадь вычисления (как computing the intersection, для этого примера) могут быть дорогими, они, вероятно, хороший компромисс между изображением на основе и чисто аналитических подходов:

  • Подход на основе изображений вызывает некоторые вопросы, например, относительно размер изображения. Кроме того, время работы и потребление памяти могут быть большими для «больших» форм (представьте формы, которые охватывают область, скажем, 1000x1000 пикселей).
  • Чисто аналитическое решение может быть довольно математически связано. Можно было бы разбить его на более простые задачи, и это, безусловно, выполнимо, но не тривиально. Возможно, что более важно: этот подход не обобщается для других типов Shape.

С Area на основе решения, вычисление пересечения между двумя произвольными формами s0 и s1 (которые могут быть Arc2D или любой другой формы) довольно тривиальный:

Area a = new Area(s0); 
    a.intersect(new Area(s1)); 

(это все) ,


Примечание стороны: Можно было бы рассмотреть возможность выполнения консервативного теста: Формы могут не пересекаются, если их объемы Ограничивающие сделать не пересекаются. Таким образом, для некоторых сценариев использования, можно было бы рассмотреть возможность сделать что-то вроде этого:

Shape s0 = ...; 
Shape s1 = ...; 
if (!s0.getBounds().intersects(s1.getBounds())) 
{ 
    // The bounds do not intersect. Then the shapes 
    // can not intersect. 
    return ...; 
} 
else 
{ 
    // The bounds DO intesect. Perform the Area-based 
    // intersection computation here: 
    ... 
} 

Что осталось, то есть вычисление площади в Area - то есть, размер области пересечения , Класс Area имеет метод, который может использоваться для проверки области isEmpty. Но у него нет способа вычислить размер области. Однако это можно вычислить, преобразуя полученную область в многоугольник, используя (flattening!) PathIterator, а затем вычисляя площадь многоугольника как, например, в the answers to this question.

Что может быть сложно об этом заключается в том, что в целом, области могут быть подписаны (то есть, они могут быть положительным или отрицательный, в зависимости от того, вершины многоугольника приведены в против часовой стрелки или или по часовой стрелке порядок, соответственно). Кроме того, пересечение между двумя формами не обязательно приводит к единой, связной форме, но может привести к различным замкнутых областей, как показано на этом изображении:

IntersectionAreas

Изображение представляет собой кадр из следующих MCVE который позволяет перетаскивать вокруг данных фигуры с помощью мыши и печатает область фигур и их пересечение.

Это использует некоторые служебные методы для вычисления площади, которые взяты из набора утилитами для геометрии в целом, и shapes, в частности, которые я начал собирать некоторое время назад)

import java.awt.Color; 
import java.awt.Font; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.Point; 
import java.awt.RenderingHints; 
import java.awt.Shape; 
import java.awt.event.MouseEvent; 
import java.awt.event.MouseListener; 
import java.awt.event.MouseMotionListener; 
import java.awt.geom.AffineTransform; 
import java.awt.geom.Arc2D; 
import java.awt.geom.Area; 
import java.awt.geom.PathIterator; 
import java.awt.geom.Point2D; 
import java.util.ArrayList; 
import java.util.Collections; 
import java.util.List; 

import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.SwingUtilities; 


public class ShapeIntersectionAreaTest 
{ 
    public static void main(String[] args) 
    { 
     SwingUtilities.invokeLater(() -> createAndShowGUI()); 
    } 

    private static void createAndShowGUI() 
    { 
     JFrame f = new JFrame(); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

     f.getContentPane().add(new ShapeIntersectionAreaTestPanel()); 

     f.setSize(800,800); 
     f.setLocationRelativeTo(null); 
     f.setVisible(true); 
    } 
} 


class ShapeIntersectionAreaTestPanel extends JPanel 
    implements MouseListener, MouseMotionListener 
{ 
    private Shape shape0; 
    private Shape shape1; 
    private Shape draggedShape; 
    private Point previousMousePosition; 

    ShapeIntersectionAreaTestPanel() 
    { 
     shape0 = new Arc2D.Double(100, 160, 200, 200, 90, 120, Arc2D.PIE); 
     shape1 = new Arc2D.Double(300, 400, 100, 150, 220, 260, Arc2D.PIE); 

     addMouseListener(this); 
     addMouseMotionListener(this); 
    } 

    @Override 
    protected void paintComponent(Graphics gr) 
    { 
     super.paintComponent(gr); 
     Graphics2D g = (Graphics2D)gr; 
     g.setRenderingHint(
      RenderingHints.KEY_ANTIALIASING, 
      RenderingHints.VALUE_ANTIALIAS_ON); 

     g.setColor(Color.RED); 
     g.fill(shape0); 
     g.setColor(Color.BLUE); 
     g.fill(shape1); 

     Shape intersection = 
      ShapeIntersectionAreaUtils.computeIntersection(shape0, shape1); 
     g.setColor(Color.MAGENTA); 
     g.fill(intersection); 

     double area0 = Math.abs( 
      ShapeIntersectionAreaUtils.computeSignedArea(shape0, 1.0)); 
     double area1 = Math.abs( 
      ShapeIntersectionAreaUtils.computeSignedArea(shape1, 1.0)); 
     double areaIntersection = Math.abs( 
      ShapeIntersectionAreaUtils.computeSignedArea(intersection, 1.0)); 
     g.setColor(Color.BLACK); 
     g.setFont(new Font("Monospaced", Font.PLAIN, 12)); 
     g.drawString(String.format("Red area   : %10.3f", area0), 10, 20); 
     g.drawString(String.format("Blue area  : %10.3f", area1), 10, 40); 
     g.drawString(String.format("Intersection area: %10.3f", areaIntersection), 10, 60); 
    } 


    @Override 
    public void mouseDragged(MouseEvent e) 
    { 
     int dx = e.getX() - previousMousePosition.x; 
     int dy = e.getY() - previousMousePosition.y; 
     AffineTransform at = 
      AffineTransform.getTranslateInstance(dx, dy); 
     if (draggedShape == shape0) 
     { 
      shape0 = at.createTransformedShape(draggedShape); 
      draggedShape = shape0; 
     } 
     if (draggedShape == shape1) 
     { 
      shape1 = at.createTransformedShape(draggedShape); 
      draggedShape = shape1; 
     } 
     repaint(); 
     previousMousePosition = e.getPoint(); 
    } 

    @Override 
    public void mouseMoved(MouseEvent e) 
    { 
    } 

    @Override 
    public void mouseClicked(MouseEvent e) 
    { 
    } 

    @Override 
    public void mousePressed(MouseEvent e) 
    { 
     draggedShape = null; 
     if (shape0.contains(e.getPoint())) 
     { 
      draggedShape = shape0; 
     } 
     if (shape1.contains(e.getPoint())) 
     { 
      draggedShape = shape1; 
     } 
     previousMousePosition = e.getPoint(); 
    } 

    @Override 
    public void mouseReleased(MouseEvent e) 
    { 
     draggedShape = null; 
    } 

    @Override 
    public void mouseEntered(MouseEvent e) 
    { 
    } 

    @Override 
    public void mouseExited(MouseEvent e) 
    { 
    } 

} 

// Utility methods related to shape and shape area computations, mostly taken from 
// https://github.com/javagl/Geom/blob/master/src/main/java/de/javagl/geom/Shapes.java 
class ShapeIntersectionAreaUtils 
{ 
    public static Shape computeIntersection(Shape s0, Shape s1) 
    { 
     Area a = new Area(s0); 
     a.intersect(new Area(s1)); 
     return a; 
    } 


    /** 
    * Compute all closed regions that occur in the given shape, as 
    * lists of points, each describing one polygon 
    * 
    * @param shape The shape 
    * @param flatness The flatness for the shape path iterator 
    * @return The regions 
    */ 
    static List<List<Point2D>> computeRegions(
     Shape shape, double flatness) 
    { 
     List<List<Point2D>> regions = new ArrayList<List<Point2D>>(); 
     PathIterator pi = shape.getPathIterator(null, flatness); 
     double coords[] = new double[6]; 
     List<Point2D> region = Collections.emptyList(); 
     while (!pi.isDone()) 
     { 
      switch (pi.currentSegment(coords)) 
      { 
       case PathIterator.SEG_MOVETO: 
        region = new ArrayList<Point2D>(); 
        region.add(new Point2D.Double(coords[0], coords[1])); 
        break; 

       case PathIterator.SEG_LINETO: 
        region.add(new Point2D.Double(coords[0], coords[1])); 
        break; 

       case PathIterator.SEG_CLOSE: 
        regions.add(region); 
        break; 

       case PathIterator.SEG_CUBICTO: 
       case PathIterator.SEG_QUADTO: 
       default: 
        throw new AssertionError(
         "Invalid segment in flattened path"); 
      } 
      pi.next(); 
     } 
     return regions; 
    } 

    /** 
    * Computes the (signed) area enclosed by the given point list. 
    * The area will be positive if the points are ordered 
    * counterclockwise, and and negative if the points are ordered 
    * clockwise. 
    * 
    * @param points The points 
    * @return The signed area 
    */ 
    static double computeSignedArea(List<? extends Point2D> points) 
    { 
     double sum0 = 0; 
     double sum1 = 0; 
     for (int i=0; i<points.size()-1; i++) 
     { 
      int i0 = i; 
      int i1 = i + 1; 
      Point2D p0 = points.get(i0); 
      Point2D p1 = points.get(i1); 
      double x0 = p0.getX(); 
      double y0 = p0.getY(); 
      double x1 = p1.getX(); 
      double y1 = p1.getY(); 
      sum0 += x0 * y1; 
      sum1 += x1 * y0; 
     } 
     Point2D p0 = points.get(0); 
     Point2D pn = points.get(points.size()-1); 
     double x0 = p0.getX(); 
     double y0 = p0.getY(); 
     double xn = pn.getX(); 
     double yn = pn.getY(); 
     sum0 += xn * y0; 
     sum1 += x0 * yn; 
     double area = 0.5 * (sum0 - sum1); 
     return area; 
    } 

    /** 
    * Compute the (signed) area that is covered by the given shape.<br> 
    * <br> 
    * The area will be positive for regions where the points are 
    * ordered counterclockwise, and and negative for regions where 
    * the points are ordered clockwise. 
    * 
    * @param shape The shape 
    * @param flatness The flatness for the path iterator 
    * @return The signed area 
    */ 
    public static double computeSignedArea(Shape shape, double flatness) 
    { 
     double area = 0; 
     List<List<Point2D>> regions = computeRegions(shape, flatness); 
     for (List<Point2D> region : regions) 
     { 
      double signedArea = computeSignedArea(region); 
      area += signedArea; 
     } 
     return area; 
    } 
} 

(Примечание : Механизмы для перетаскивания фигур не особенно элегантны. В реальном приложении это нужно решать по-другому - это только для демонстрации методов вычисления области).