# Simplify 2D polyhedral Regions like Interval

I don't think there's a built in "RegionSimplify" for polyhedral Regions and derived akin to how Interval automatically simplifies its argument expression:

Interval[{0, 1}, {1, 2}]


and

Interval[{0, 1}]~IntervalUnion~Interval[{1, 2}]


yield

Interval[{0, 2}]


In contrast, RegionUnion remains unevaluated, even in 2D:

Line[{{0, 0}, {1, 0}}]~ RegionUnion ~ Line[{{1, 0}, {2, 0}}]

(* RegionUnion[Line[{{0, 0}, {1, 0}}], Line[{{1, 0}, {2, 0}}]] *)


From

Rectangle[{0, 0}, {1, 1}] ~ RegionUnion ~
Rectangle[{1, 0}, {2, 1}] // RegionBoundary // DiscretizeRegion


As expected, the Rectangles' common segment is not part of the output.

How to expose or compute the ideal normal form for 2D (at least) polyhedral Regions like these basic examples:

Overlapping lines:

Line[{{0, 0}, {2, 0}}]~ RegionUnion ~ Line[{{1, 0}, {3, 0}}] (* // RegionSimplify *)

(*  Line[{{0,0},{3,0}}] *)


Union of rectangles:

 Rectangle[{0, 0}, {1, 1}] ~ RegionUnion ~
Rectangle[{1, 0}, {2, 1}] (* // RegionSimplify *)

(* Rectangle[{0, 0}, {2, 1}] *)


Boundary:

 Rectangle[{0, 0}, {1, 1}] ~ RegionUnion ~
Rectangle[{1, 0}, {2, 1}] // RegionBoundary  (* // RegionSimplify *)

(* Line[{{0,0},{2,0},{2,1},{0,1},{0,0}}]  *)


The simplification should work for all 2D polyhedral regions including cones (which btw the current language does not support correctly):

ConicHullRegion[{{0, 0}}, {{1, 0}, {1, 1}}] ~RegionUnion ~
ConicHullRegion[{{0, 0}}, {{0, 1}, {1, 1}}] (* // RegionSimplify *)

(* ConicHullRegion[{{0, 0}}, {{1, 0}, {0, 1}}] *)


For convex regions and convex-preserving operations (eg intersection) should suffice to consider the extreme set (vertices and rays), but in general regions aren't convex (and union doens't preserve it).

(Note: RegionIntersection of the same Rectangles hangs the kernel w/ msg: Last::nolast: "{} has a length of zero and no last element")

eps = 0.001;
repl = (z_Symbol == val_) :>
(val - eps/2 <= z <= val + eps/2);

r1 = Line[{{0, 0}, {1, 0}}];
r2 = Line[{{1, 0}, {2, 0}}];

r3 = ImplicitRegion[RegionMember[
RegionUnion[r1, r2], {x, y}] //
Simplify, {x, y}] /. repl

ImplicitRegion[(x | y) \[Element] Reals && -0.0005 <= y <= 0.0005 &&
0 <= x <= 2, {x, y}]

r3 // DiscretizeRegion


r4 = Line[{{0, 0}, {2, 0}}];
r5 = Line[{{1, 0}, {3, 0}}];

r6 = ImplicitRegion[RegionMember[
RegionUnion[r4, r5], {x, y}] //
Simplify, {x, y}] /. repl

ImplicitRegion[(x | y) \[Element] Reals && -0.0005 <= y <= 0.0005 &&
0 <= x <= 3, {x, y}]

r6 // DiscretizeRegion


r7 = Rectangle[{0, 0}, {1, 1}];
r8 = Rectangle[{1, 0}, {2, 1}];
r9 = RegionUnion[r7, r8];

RegionMember[r9, {x, y}] // Simplify

(x | y) \[Element] Reals && 0 <= x <= 2 && 0 <= y <= 1

r9 // DiscretizeRegion


r10 = Rectangle[{0, 0}, {1, 1}];
r11 = Rectangle[{1, 0}, {2, 1}];
r12 = RegionUnion[r10, r11];

RegionMember[r12, {x, y}] // Simplify

(x | y) \[Element] Reals && 0 <= x <= 2 && 0 <= y <= 1

r12 // DiscretizeRegion


n = 5;
rectangles =
Rectangle @@@
Transpose[{RandomInteger[{0, 10}, {n, 2}],
RandomInteger[{11, 20}, {n, 2}]}];

Graphics[{Opacity[.25], rectangles}]


r13 = RegionUnion @@ rectangles;

RegionMember[r13, {x, y}]

(x | y) \[Element]
Reals && ((2 <= x <= 13 && 3 <= y <= 14) || (2 <= x <= 14 &&
4 <= y <= 11) || (3 <= x <= 16 &&
8 <= y <= 19) || (4 <= x <= 13 &&
9 <= y <= 13) || (5 <= x <= 17 && 8 <= y <= 13))

r13 // DiscretizeRegion


EDIT: Example of disconnected regions

r14 = Rectangle[{0, 0}, {1, 1}];
r15 = Rectangle[{1.1, 1.1}, {1.6, 1.7}];
r16 = RegionUnion[r14, r15];

RegionMember[r16, {x, y}] // Simplify

(x | y) \[Element]
Reals && ((0 <= x <= 1 && 0 <= y <= 1) || (1.1 <= x <= 1.6 &&
1.1 <= y <= 1.7))

r16 // DiscretizeRegion


SECOND EDIT: Additional example--regions with holes.

r1 = Rectangle[{0, 0}, {1, 1}];
r2 = Rectangle[{.25, .25}, {.5, .5}];
r3 = RegionDifference[r1, r2];
r4 = Disk[{1.5, 1.25}, .75];
r5 = Rectangle[{1.25, 1.25}, {1.75, 1.35}];
r6 = RegionDifference[r4, r5];
r7 = RegionUnion[r3, r6];

RegionMember[r7, {x, y}]


(x | y) [Element] Reals && (((-1.5 + x)^2 + (-1.25 + y)^2 <= 0.5625 && ! (1.25 <= x <= 1.75 && 1.25 <= y <= 1.35)) || (0 <= x <= 1 && 0 <= y <= 1 && ! (0.25 <= x <= 0.5 && 0.25 <= y <= 0.5)))

DiscretizeRegion[r7]


• Did you try Union of Rectangles that isn't simply-connected? Commented Oct 5, 2014 at 17:40
• Added example of disconnected regions. Commented Oct 5, 2014 at 18:57
• Those components are simply connected. Try something w/ a hole in it. Commented Oct 6, 2014 at 2:54
• Added example with regions with holes. If this is not what you want, provide a specific example of what you do want. Commented Oct 6, 2014 at 5:05
• Your method using RegionMember and Simplify, unlike Junho's, seems robust to the more complex topologies but it doesn't yield the normal form, which is the minimal number of boundary points (for polytopes). Commented Oct 6, 2014 at 17:21

(version 10) I made function lastUnion , firstUnion and RectangleRegionSimplify instead of RegionSimplify. I think you might want to make RectangleRegionSimplify.

lastUnion[coo_] :=
Flatten[Replace[SplitBy [ coo, Last], {f_, ___, l_} -> {f, l}, 1], 1]
firstUnion[coo_] :=
Flatten[Replace[SplitBy [ coo, First], {f_, ___, l_} -> {f, l}, 1], 1]
RectangleRegionSimplify[p__] := Module[{pts, rst},
pts = MeshPrimitives[
BoundaryDiscretizeRegion[
DiscretizeRegion[RegionUnion@p], MaxCellMeasure -> \[Infinity]],
2][[1, 1]];
rst = Append[pts, First[pts]];
rst = N[Rationalize[Chop[rst, 10^-6]], 5];
rst // firstUnion // lastUnion // Rationalize // N // Line
]


And I made some sample Rectangle's as Region. Rectangle is not always Region, so I randomly selected those as Region.

n = 5;
data = Transpose[{RandomInteger[{0, 10}, {n, 2}],
RandomInteger[{11, 20}, {n, 2}]}];
fig1 = {ColorData["Atoms", "ColorList"][[1 ;; n]],
Rectangle @@@ data} // Transpose;
Graphics[{EdgeForm[Gray], Opacity[0.3], fig1}]


I think you can check for your code with following like this.

fig2 = RectangleRegionSimplify[RegionUnion @@ Rectangle @@@ data]


Line[{{4., 8.}, {5., 8.}, {5., 5.}, {7., 5.}, {7., 1.}, {12., 1.}, {12., 3.}, {16., 3.}, {16., 5.}, {19., 5.}, {19., 14.}, {15., 14.}, {15., 19.}, {4., 19.}, {4., 8.}}]

Graphics[{%, PointSize[Large], Red, Point @@ %}]


Caution

RegionQ[Rectangle[{0, 0}, {1, 3}]]


True

RegionQ[Rectangle[{2, 0}, {1, 3}]]


False

• Junho, it doesn't work on derived Regions, eg: Rectangle[{0, 0}, {2, 1}]~RegionUnion~ Rectangle[{1, 0}, {2, 2}]. Derived implies RegionQ[r]==True Commented Oct 1, 2014 at 16:48
• Applying Normal to BoundaryDiscretizeRegion is still a graphic. How to extract the line representation? Also note: ...// BoundaryDiscretizeRegion // RegionBoundary outputs many points that are not corners. Commented Oct 1, 2014 at 17:06
• as per the calendar counterexample, try rectangles whose union is not simply connected. Also, the method should work for all 2D polyhedra and derived regions. Commented Oct 3, 2014 at 2:50
• Your method fails if the union is not simply-connected, eg this "#" shape: data2 = {{{1, 0}, {2, 6}}, {{4, 0}, {5, 1}}, {{0, 1}, {6, 2}}, {{4, 0}, {5, 6}}, {{0, 4}, {6, 5}}} with msg: There is no simple cell representation for the specified cells of the BoundaryMeshRegion... Commented Oct 6, 2014 at 17:15