Java中的形状和段

时间:2014-02-21 17:28:58

标签: 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;

3 个答案:

答案 0 :(得分:3)

我会这样做的。请注意,代码有一个错误,指示从右上角开始的点到左上角的左边。留作用户的练习。

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

答案 1 :(得分:2)

这是另一个https://stackoverflow.com/help/mcve(我昨天凌晨3点开始这个“昨天”,显然Andrew Thompson在不同的时区; - )

这里的基本想法如下:

两个给定点定义一条线。也就是说,一条无限的线,而不仅仅是一条线 segment 。物体边界框的角点投影在该线及其垂直线上。这给出了沿这些线的对象范围(的上限)。这些上限可用于定义覆盖对象的相应一半所需的线上方和下方的“最小半空间”。然后这些半空间可以与物体相交以获得所需的结果。

此示例中的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 };
}

答案 2 :(得分:0)

有趣的问题。

没有任何方法可以直接帮助您,但通过计算边界矩形并与分割线上的两个相对的矩形相交,您应该能够创建这样的方法。

一般的想法是

  1. 找到原始区域的边界矩形:getBounds()getBounds2D()
  2. 计算线条两侧的两个矩形,这两个矩形与线条两侧的区域重叠。执行此操作时,您必须考虑几个特殊情况(如线条足够长,它是否与区域相交,矩形是否与原始区域的每一侧完全重叠等)。矩形的大小应由原始区域的边界矩形决定。
  3. 通过将两个矩形中的每一个与原始区域相交来获取这两个区域,即使用intersect()方法