我有一个形状。我基本上试图将一个区域分成两个区域,使用一个区段作为二分区。
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;
答案 0 :(得分:3)
我会这样做的。请注意,代码有一个错误,指示从右上角开始的点到左上角的左边。留作用户的练习。
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)
{
}
}
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)
有趣的问题。
没有任何方法可以直接帮助您,但通过计算边界矩形并与分割线上的两个相对的矩形相交,您应该能够创建这样的方法。
一般的想法是
getBounds()
或getBounds2D()
。intersect()
方法