2014-02-21 13 views
1

我有一個Shape。我基本上是試圖將一個區域分成兩個區域,並使用一個區段作爲二分區。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) 
    { 
    } 
} 

編輯一旁:從技術上講,它可以是更容易(或甚至更優雅)來轉換原來的形狀和線,使線與x軸一致,則限定所述半空間(在這種情況下可能是簡單的Rectangle2D s),並將剪輯結果轉換回原始方向。但是我想「就地」計算它,而不必創建許多變形的形狀。


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

我應該在Area內有AffineTransform嗎,所以當我crea te兩個矩形,我可以根據線條旋轉矩形? – Thatdude1

+0

也許這是一個更簡單的方法來做到這一點,但我並不相信這一點。我在想,你只需要以這樣一種方式計算矩形的角落,使得其中的兩個碰到線,而另外兩個足夠遠以確保矩形跨越你應該相交的區域的一側。通過旋轉解決方案,您需要確保旋轉完成後,矩形的邊與交線相匹配。 – 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); 
    } 
}