There are many degrees of freedom regaring the approaches and actual implementation here. Some relevant questions to consider, just from the tip of my head:
- Are the coordinates always integers?
- Are the input shapes always (axis-aligned) rectangles?
- Do the rectangles always overlap?
- Are the points always at different coordinates?
- Are the edges never equal?
- ...
In some cases (if the first three questions are answered with "Yes"), you could probably go for a technically very simple approach: Just consider the whole input as a "grid", where the rectanges define the grid cells that are "filled", and in the end, return the border of the filled area.
If the answer to one of them may be "No", then you're opening a can of worms, with many, many subtleties.
Fortunately, the people at Sun/Oracle have worked this out pretty well, and there may still be a simple solution even for very complex problems: You can convert the input into shapes, and then use the Area#add
method to compute the merged shape.
This is implemented in this example. But note that this is not very efficient, compared to, say, the grid-based approach that could be used for simpler cases. And it still leaves the question open about what should happen for non-overlapping rectangles. However, at least for the given inputs, the result is the expected one:
import java.awt.Point;
import java.awt.Shape;
import java.awt.geom.Area;
import java.awt.geom.Path2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class RectangleMerge {
public static void main(String[] args) {
List<Point2D> pointsA = Arrays.asList(new Point(0, 0), new Point(0, 1), new Point(2, 1), new Point(2, 0));
List<Point2D> pointsB = Arrays.asList(new Point(1, 0), new Point(1, 2), new Point(3, 2), new Point(3, 0));
System.out.println(merge(pointsA, pointsB));
}
private static List<Point2D> merge(List<? extends Point2D> pointsA, List<? extends Point2D> pointsB) {
Area aa = new Area(toShape(pointsA));
aa.add(new Area(toShape(pointsB)));
return toPoints(aa);
}
private static Shape toShape(List<? extends Point2D> points) {
Path2D path = new Path2D.Double();
for (int i = 0; i < points.size(); i++) {
Point2D p = points.get(i);
if (i == 0) {
path.moveTo(p.getX(), p.getY());
} else {
path.lineTo(p.getX(), p.getY());
}
}
path.closePath();
return path;
}
private static List<Point2D> toPoints(Shape shape) {
List<Point2D> result = new ArrayList<Point2D>();
PathIterator pi = shape.getPathIterator(null, 0.0);
double[] coords = new double[6];
while (!pi.isDone()) {
int segment = pi.currentSegment(coords);
switch (segment) {
case PathIterator.SEG_MOVETO:
case PathIterator.SEG_LINETO:
result.add(new Point2D.Double(coords[0], coords[1]));
break;
}
pi.next();
}
return result;
}
}