# 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? – alancalvitti Oct 5 '14 at 17:40
• Added example of disconnected regions. – Bob Hanlon Oct 5 '14 at 18:57
• Those components are simply connected. Try something w/ a hole in it. – alancalvitti Oct 6 '14 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. – Bob Hanlon Oct 6 '14 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). – alancalvitti Oct 6 '14 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 – alancalvitti Oct 1 '14 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. – alancalvitti Oct 1 '14 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. – alancalvitti Oct 3 '14 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... – alancalvitti Oct 6 '14 at 17:15