2014-02-21 2 views
1

У меня есть форма. Я в основном пытаюсь разделить область на две области, используя сегмент в качестве деления пополам.Формы и сегменты в Java

public Shape divide(Shape a, Point2D p1, Point2D p2) {  

     Shape str = new BasicStroke().createStrokedShape(new Line2D.Double(p1,p2));  
     Shape line = new Shape(str); 
     Shape temp = a;  
     line.intersect(temp);  
     temp.exclusiveOr(line); 
     // temp is the shape with the line intersecting it 

     AffineTransform t = new AffineTransform(); 
     double angle = Math.atan2(p2.getY() - p1.getY(), p2.getX() - p1.getX()); 

     t.rotate(angle, p1.getX(), p1.getY());   
     temp = temp.createTransformedArea(t);  

     return Shape ; 

    } 

Я хочу разрез`ать форму в два с использованием сегмента, но не уверен, как идти об этом, я смотрел на методы пересечения: http://docs.oracle.com/javase/7/docs/api/java/awt/geom/Area.html, но до сих пор не уверен, как получить двух областей от одного. Я надеюсь вернуть что-то вроде:

return firstHalf secondHalf; 

ответ

2

Вот еще https://stackoverflow.com/help/mcve (я начал это "вчера" в ~ 3: 00 утра, очевидно, Эндрю Томпсон находится в другом часовом поясе ;-))

Основная идея вот что:

Две заданные точки определяют линию. То есть бесконечная линия, а не только линия сегмент. На этой линии и ее перпендикуляре проецируются угловые точки ограничивающего прямоугольника объекта. Это дает (верхнюю границу) протяженность объекта вдоль этих линий. Эти верхние границы можно использовать для определения «минимальных полупространств» выше и ниже линии, которые необходимы для покрытия соответствующей половины объекта. Эти полупространства могут быть затем пересекаются с объектом для получения желаемых результатов.

Метод split в этом примере принимает параметр Graphics2D. Это только используется для «отладки», то есть для отображения промежуточных результатов (экстентов, полупространств), которые вычисляются, и предварительного просмотра окончательных результатов. Этот параметр Graphics g (и соответствующий отладочный вывод) можно просто удалить (но это также может помочь показать идею подхода).

import java.awt.Color; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.Shape; 
import java.awt.event.MouseEvent; 
import java.awt.event.MouseMotionListener; 
import java.awt.geom.AffineTransform; 
import java.awt.geom.Area; 
import java.awt.geom.Ellipse2D; 
import java.awt.geom.Line2D; 
import java.awt.geom.Path2D; 
import java.awt.geom.Point2D; 
import java.awt.geom.Rectangle2D; 

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

public class ShapeSplit 
{ 
    public static void main(String[] args) 
    { 
     SwingUtilities.invokeLater(new Runnable() 
     { 
      @Override 
      public void run() 
      { 
       createAndShowGUI(); 
      } 
     }); 
    }  

    private static void createAndShowGUI() 
    { 
     JFrame f = new JFrame(); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.getContentPane().add(new ShapeSplitPanel()); 
     f.setSize(1100,600); 
     f.setLocationRelativeTo(null); 
     f.setVisible(true); 
    } 
} 

class ShapeSplitPanel extends JPanel implements MouseMotionListener 
{ 
    private Shape inputShape = new Ellipse2D.Double(300,200,200,300); 
    private Point2D point0 = new Point2D.Double(200,300); 
    private Point2D point1 = new Point2D.Double(600,400); 

    ShapeSplitPanel() 
    { 
     addMouseMotionListener(this); 
    } 

    @Override 
    protected void paintComponent(Graphics gr) 
    { 
     super.paintComponent(gr); 
     Graphics2D g = (Graphics2D)gr; 
     g.setColor(Color.BLUE); 
     g.fill(inputShape); 

     g.setColor(Color.BLACK); 
     g.draw(new Line2D.Double(point0, point1)); 
     g.fill(new Ellipse2D.Double(
      point0.getX() - 3, point0.getY()-3, 6, 6)); 
     g.fill(new Ellipse2D.Double(
      point1.getX() - 3, point1.getY()-3, 6, 6)); 

     split(new Area(inputShape), point0, point1, g); 

    } 


    private static Area[] split(Area a, Point2D p0, Point2D p1, Graphics2D g) 
    { 
     // Compute the direction of the line (L) 
     // and its perpendicular (P) 
     double dx = p1.getX() - p0.getX(); 
     double dy = p1.getY() - p0.getY(); 
     double length = Math.hypot(dx, dy); 
     double dirLx = dx/length; 
     double dirLy = dy/length; 
     double dirPx = -dirLy; 
     double dirPy = dirLx; 

     // Compute the minimum and maximum of all dot 
     // products that describe the distance of the 
     // projection of the corner points of the 
     // bounding box on on the line (L) and its 
     // perpendicular (P). These are upper limits 
     // for the extents of the object along these 
     // directions 
     double minDotL = Double.MAX_VALUE; 
     double maxDotL = -Double.MAX_VALUE; 
     double minDotP = Double.MAX_VALUE; 
     double maxDotP = -Double.MAX_VALUE; 
     Rectangle2D bounds = a.getBounds2D(); 
     for (int i=0; i<4; i++) 
     { 
      Point2D corner = getCorner(bounds, i); 
      double pdx = corner.getX() - p0.getX(); 
      double pdy = corner.getY() - p0.getY(); 

      double dotL = dirLx * pdx + dirLy * pdy; 
      minDotL = Math.min(minDotL, dotL); 
      maxDotL = Math.max(maxDotL, dotL); 

      double dotP = dirPx * pdx + dirPy * pdy; 
      minDotP = Math.min(minDotP, dotP); 
      maxDotP = Math.max(maxDotP, dotP); 
     } 

     // Compute the start- and end points of 
     // the line segments describing the 
     // extent of the bounds along the line 
     // and the perpendicular 
     Point2D extentLmin = new Point2D.Double(
      p0.getX() + minDotL * dirLx, 
      p0.getY() + minDotL * dirLy); 

     Point2D extentLmax = new Point2D.Double(
      p0.getX() + maxDotL * dirLx, 
      p0.getY() + maxDotL * dirLy); 

     Point2D extentPmin = new Point2D.Double(
      p0.getX() + minDotP * dirPx, 
      p0.getY() + minDotP * dirPy); 

     Point2D extentPmax = new Point2D.Double(
      p0.getX() + maxDotP * dirPx, 
      p0.getY() + maxDotP * dirPy); 

     // Compute the two rectangles that cover 
     // each half of the object based on 
     // the given line 
     Path2D half0 = new Path2D.Double(); 
     half0.moveTo(extentLmin.getX(), extentLmin.getY()); 
     half0.lineTo(
      extentLmin.getX() + minDotP * dirPx, 
      extentLmin.getY() + minDotP * dirPy); 
     half0.lineTo(
      extentLmax.getX() + minDotP * dirPx, 
      extentLmax.getY() + minDotP * dirPy); 
     half0.lineTo(extentLmax.getX(), extentLmax.getY()); 
     half0.closePath(); 

     Path2D half1 = new Path2D.Double(); 
     half1.moveTo(extentLmin.getX(), extentLmin.getY()); 
     half1.lineTo(
      extentLmin.getX() + maxDotP * dirPx, 
      extentLmin.getY() + maxDotP * dirPy); 
     half1.lineTo(
      extentLmax.getX() + maxDotP * dirPx, 
      extentLmax.getY() + maxDotP * dirPy); 
     half1.lineTo(extentLmax.getX(), extentLmax.getY()); 
     half1.closePath(); 

     // Compute the resulting areas by intersecting 
     // the original area with both halves 
     Area a0 = new Area(a); 
     a0.intersect(new Area(half0)); 

     Area a1 = new Area(a); 
     a1.intersect(new Area(half1)); 

     // Debugging output 
     if (g != null) 
     { 
      g.setColor(Color.GRAY); 
      g.draw(bounds); 

      g.setColor(Color.RED); 
      g.draw(new Line2D.Double(extentLmin, extentLmax)); 

      g.setColor(Color.GREEN); 
      g.draw(new Line2D.Double(extentPmin, extentPmax)); 

      g.setColor(Color.YELLOW.darker()); 
      g.draw(half0); 

      g.setColor(Color.MAGENTA); 
      g.draw(half1); 

      g.setColor(Color.BLUE); 
      g.fill(AffineTransform.getTranslateInstance(400, -20). 
       createTransformedShape(a0)); 

      g.setColor(Color.BLUE); 
      g.fill(AffineTransform.getTranslateInstance(400, +20). 
       createTransformedShape(a1)); 
     } 
     return new Area[] { a0, a1 }; 
    } 

    private static Point2D getCorner(Rectangle2D r, int corner) 
    { 
     switch (corner) 
     { 
      case 0: return new Point2D.Double(r.getMinX(), r.getMinY()); 
      case 1: return new Point2D.Double(r.getMinX(), r.getMaxY()); 
      case 2: return new Point2D.Double(r.getMaxX(), r.getMaxY()); 
      case 3: return new Point2D.Double(r.getMaxX(), r.getMinY()); 
     } 
     return null; 
    } 



    @Override 
    public void mouseDragged(MouseEvent e) 
    { 
     point1.setLocation(e.getPoint()); 
     repaint(); 
    } 


    @Override 
    public void mouseMoved(MouseEvent e) 
    { 
    } 
} 

РЕДАКТИРОВАТЬ В сторону: Технически, это может быть проще (или даже более элегантно), чтобы преобразовать первоначальную форму и линию так, чтобы линия соответствует оси х, а затем определение полупространства (который в этом случае может быть простым Rectangle2D с) и преобразование обрезанных результатов обратно в исходную ориентацию. Но я хотел вычислить его «на месте», не создавая много преобразованных фигур.


EDIT2: Еще один фрагмент для комментария, чтобы вставить непосредственно перед тем// Debugging output

AffineTransform t = new AffineTransform(); 
double angle = Math.atan2(p1.getY() - p0.getY(), p1.getX() - p0.getX()); 
t.rotate(-angle, p0.getX(), p0.getY()); 
a0 = a0.createTransformedArea(t); 
a1 = a1.createTransformedArea(t); 

EDIT3 Второй подход, только соответствующий метод этот раз

private static Area[] split(Area a, Point2D p0, Point2D p1, Graphics2D g) 
{ 
    // Compute the angle of the line to the x-axis 
    double dx = p1.getX() - p0.getX(); 
    double dy = p1.getY() - p0.getY(); 
    double angleRadToX = Math.atan2(dy, dx); 

    // Align the area so that the line matches the x-axis 
    AffineTransform at = new AffineTransform(); 
    at.rotate(-angleRadToX); 
    at.translate(-p0.getX(), -p0.getY()); 
    Area aa = a.createTransformedArea(at); 

    // Compute the upper and lower halves that the area 
    // has to be intersected with 
    Rectangle2D bounds = aa.getBounds2D(); 

    double half0minY = Math.min(0, bounds.getMinY()); 
    double half0maxY = Math.min(0, bounds.getMaxY()); 
    Rectangle2D half0 = new Rectangle2D.Double(
     bounds.getX(), half0minY, 
     bounds.getWidth(), half0maxY-half0minY); 

    double half1minY = Math.max(0, bounds.getMinY()); 
    double half1maxY = Math.max(0, bounds.getMaxY()); 
    Rectangle2D half1 = new Rectangle2D.Double(
     bounds.getX(), half1minY, 
     bounds.getWidth(), half1maxY-half1minY); 

    // Compute the resulting areas by intersecting 
    // the original area with both halves, and 
    // transform them back to their initial position 
    Area a0 = new Area(aa); 
    a0.intersect(new Area(half0)); 

    Area a1 = new Area(aa); 
    a1.intersect(new Area(half1)); 

    try 
    { 
     at.invert(); 
    } 
    catch (NoninvertibleTransformException e) 
    { 
     // Always invertible 
    } 
    a0 = a0.createTransformedArea(at); 
    a1 = a1.createTransformedArea(at); 

    // Debugging output 
    if (g != null) 
    { 
     g.setColor(Color.GRAY); 
     g.draw(bounds); 

     g.setColor(Color.RED); 
     g.draw(aa); 

     g.setColor(Color.YELLOW.darker()); 
     g.draw(half0); 

     g.setColor(Color.MAGENTA); 
     g.draw(half1); 

     g.setColor(Color.BLUE.darker()); 
     g.fill(AffineTransform.getTranslateInstance(400, -20). 
      createTransformedShape(a0)); 

     g.setColor(Color.BLUE.brighter()); 
     g.fill(AffineTransform.getTranslateInstance(400, +20). 
      createTransformedShape(a1)); 
    } 

    return new Area[] { a0, a1 }; 
} 
+0

Если бы я попытался использовать метод преобразования. Как бы я мог преобразовать линию и область, чтобы она была повернута и выровнена с осью x? – Thatdude1

+1

Вычислить угол между линией и осью x как 'Math.atan2 (p1.y-p0.y, p1.x-p0.x)', затем создать 'AffineTransfrom.getRotateInstance (angle, p0.x, p0.y) 'и использовать это для создания преобразованной формы, затем вычислить границы этой преобразованной формы и разбить этот ограничивающий прямоугольник на часть выше и ниже оси x, пересечь эти половины с формой и, наконец, преобразовать эти пересечения с использованием обратного аффинного преобразования. – Marco13

+0

Обновлен мой вопрос с вашими предложениями .. но моя форма не вращается должным образом. В зависимости от линии ее вращение и перевод иногда не всегда вращаются по оси x – Thatdude1

0

Интересный вопрос.

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

Общая идея заключается в том, чтобы

  1. Найти ограничительный прямоугольник исходной области: либо getBounds() или getBounds2D().
  2. Вычислите два прямоугольника из вашей линии, которые перекрывают вашу область по обе стороны от линии. При этом вам придется учитывать несколько особых случаев (например, длина линии достаточно длинная, она пересекает область вообще, прямоугольники полностью перекрывают каждую сторону исходной области и т. Д.). Размер прямоугольников должен определяться ограничивающим прямоугольником исходной области.
  3. Получить две области, пересекая каждый из двух прямоугольников с исходной области, т.е. с использованием метода intersect()
+0

Должен ли я иметь AffineTransform в пределах Района, поэтому, когда я создаю te два прямоугольника я могу повернуть прямоугольники на основе линии? – Thatdude1

+0

Возможно, это более простой способ сделать это, но я не уверен в этом. Я думал, что вам нужно всего лишь вычислить углы прямоугольников таким образом, что два из них попадают в линию, а два других достаточно далеко, чтобы убедиться, что прямоугольник охватывает сторону вашей области, которую он должен пересекать. С помощью решения поворота вам нужно убедиться, что сторона прямоугольника соответствует линии instersection после завершения вращения. – Steinar

2

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

enter image description here

import java.awt.*; 
import java.awt.event.MouseAdapter; 
import java.awt.event.MouseEvent; 
import java.awt.event.MouseListener; 
import java.awt.geom.*; 
import java.awt.image.BufferedImage; 
import javax.swing.*; 

class SplitArea { 

    int s = 100; 
    JPanel gui = new JPanel(new BorderLayout()); 
    BufferedImage[] images = new BufferedImage[4]; 
    Point p1 = new Point(s/4, s/4); 
    Point p2 = new Point(s * 3/4, s * 3/4); 
    Ellipse2D ellipse = new Ellipse2D.Float(
      s/5, s/5, s * 3/5, s * 3/5); 
    Rectangle2D bg = new Rectangle2D.Float(0, 0, s, s); 

    SplitArea() { 
     JToolBar tb = new JToolBar(); 
     gui.add(tb, BorderLayout.PAGE_START); 
     final JToggleButton tob = new JToggleButton("Primary Point"); 
     tb.add(tob); 

     JPanel view = new JPanel(new GridLayout(1, 0, 4, 4)); 
     gui.add(view, BorderLayout.CENTER); 
     for (int ii = 0; ii < images.length; ii++) { 
      BufferedImage bi = new BufferedImage(
        s, s, BufferedImage.TYPE_INT_RGB); 
      images[ii] = bi; 
      JLabel l = new JLabel(new ImageIcon(bi)); 
      if (ii == 0) { 
       l.addMouseListener(new MouseAdapter() { 

        @Override 
        public void mouseClicked(MouseEvent e) { 
         if (tob.isSelected()) { 
          p1 = e.getPoint(); 
         } else { 
          p2 = e.getPoint(); 
         } 
         drawImages(); 
        } 
       }); 
      } 
      view.add(l); 
     } 

     drawImages(); 
    } 

    public final void drawImages() { 
     Graphics2D g; 

     // image 0 
     g = images[0].createGraphics(); 
     g.setColor(Color.BLACK); 
     g.fill(bg); 

     g.setColor(Color.CYAN); 
     g.fill(ellipse); 
     g.setColor(Color.WHITE); 
     g.draw(ellipse); 

     g.setColor(Color.red); 
     drawPoint(g, p1); 
     drawPoint(g, p2); 

     g.dispose(); 

     int xDiff = p1.x - p2.x; 
     int yDiff = p1.y - p2.y; 
     Point2D xAxis; 
     Point2D xSAxis; 
     if (xDiff == 0) { 
      xAxis = new Point2D.Double(p1.x, 0); 
      xSAxis = new Point2D.Double(p1.x, s); 
     } else if (yDiff == 0) { 
      xAxis = new Point2D.Double(0, p1.y); 
      xSAxis = new Point2D.Double(s, p1.y); 
     } else { 
      System.out.println("Not vertical or horizontal!"); 
      // will throw a NaN if line is vertical 
      double m = (double) yDiff/(double) xDiff; 
      System.out.println("m: " + m); 

      double b = (double) p1.y - (m * (double) p1.x); 
      System.out.println("b: " + b); 

      // crosses x axis at.. 
      xAxis = new Point2D.Double(0d, b); 
      double pointS = (s - b)/m; 
      xSAxis = new Point2D.Double(pointS, s); 
     } 

     // image 1 
     g = images[1].createGraphics(); 
     g.setColor(Color.BLACK); 
     g.fill(bg); 

     g.setColor(Color.CYAN); 
     g.fill(ellipse); 
     g.setColor(Color.WHITE); 
     g.draw(ellipse); 

     g.setColor(Color.YELLOW); 
     System.out.println(xAxis); 
     System.out.println(xSAxis); 
     g.drawLine(
       (int) xAxis.getX(), (int) xAxis.getY(), 
       (int) xSAxis.getX(), (int) xSAxis.getY()); 

     g.setColor(Color.red); 
     drawPoint(g, p1); 
     drawPoint(g, p2); 

     g.dispose(); 

     // image 2 
     g = images[1].createGraphics(); 
     g.setColor(Color.BLACK); 
     g.fill(bg); 

     g.setColor(Color.CYAN); 
     g.fill(ellipse); 
     g.setColor(Color.WHITE); 
     g.draw(ellipse); 

     g.setColor(Color.YELLOW); 
     System.out.println(xAxis); 
     System.out.println(xSAxis); 
     g.drawLine(
       (int) xAxis.getX(), (int) xAxis.getY(), 
       (int) xSAxis.getX(), (int) xSAxis.getY()); 

     g.setColor(Color.red); 
     drawPoint(g, p1); 
     drawPoint(g, p2); 

     g.dispose(); 

     // split the regions 
     Rectangle2D.Double all = new Rectangle2D.Double(0, 0, s, s); 
     Area a1 = new Area(all); 
     Area a2 = new Area(all); 
     GeneralPath aPart = new GeneralPath(); 
     aPart.moveTo(0, 0); 
     aPart.lineTo(0, s); 
     aPart.lineTo(xSAxis.getX(), xSAxis.getY()); 
     aPart.lineTo(xAxis.getX(), xAxis.getY()); 
     aPart.closePath(); 
     a1.subtract(new Area(aPart)); 
     a2.subtract(a1); 

     Area ellipsePartA = new Area(ellipse); 
     ellipsePartA.subtract(a1); 
     Area ellipsePartB = new Area(ellipse); 
     ellipsePartB.subtract(a2); 

     // image 3 
     g = images[2].createGraphics(); 
     g.setColor(Color.BLACK); 
     g.fill(bg); 

     g.setColor(Color.CYAN); 
     g.fill(ellipsePartA); 
     g.setColor(Color.WHITE); 
     g.draw(ellipsePartA); 

     g.setColor(Color.red); 
     drawPoint(g, p1); 
     drawPoint(g, p2); 

     g.dispose(); 

     // image 4 
     g = images[3].createGraphics(); 
     g.setColor(Color.BLACK); 
     g.fill(bg); 

     g.setColor(Color.CYAN); 
     g.fill(ellipsePartB); 
     g.setColor(Color.WHITE); 
     g.draw(ellipsePartB); 

     g.setColor(Color.red); 
     drawPoint(g, p1); 
     drawPoint(g, p2); 

     g.dispose(); 

     gui.repaint(); 
    } 

    public final void drawPoint(Graphics g, Point2D p) { 
     g.setColor(new Color(255, 0, 0, 128)); 
     int x = (int) p.getX(); 
     int y = (int) p.getY(); 
     g.drawLine(x - 1, y, x - 5, y); 
     g.drawLine(x + 1, y, x + 5, y); 
     g.drawLine(x, y - 1, x, y - 5); 
     g.drawLine(x, y + 1, x, y + 5); 
    } 

    public Area[] split(Area a, Point2D p1, Point2D p2) { 

     Shape str = new BasicStroke().createStrokedShape(new Line2D.Double(p1, p2)); 
     Area line = new Area(str); 
     Area temp = a; 
     line.intersect(temp); 
     temp.exclusiveOr(line); 
     // temp is the shape with the line intersecting it   

     Area[] areas = {new Area(temp)}; 

     return areas; 
    } 

    public JComponent getGui() { 
     return gui; 
    } 

    public static void main(String[] args) { 
     Runnable r = new Runnable() { 

      @Override 
      public void run() { 
       SplitArea sa = new SplitArea(); 
       JOptionPane.showMessageDialog(null, sa.getGui()); 
      } 
     }; 
     // Swing GUIs should be created and updated on the EDT 
     // http://docs.oracle.com/javase/tutorial/uiswing/concurrency 
     SwingUtilities.invokeLater(r); 
    } 
} 
Смежные вопросы