# Inflate and unite a list of 0D to 2D regions

I've got a List of BoundaryMeshRegions, created via ConvexHullMesh:

hulls0 = ConvexHullMesh /@ RandomReal[{-10, 10}, {3, 1, 2}];
hulls1 = ConvexHullMesh /@ RandomReal[{-10, 10}, {3, 2, 2}];
hulls2 = ConvexHullMesh /@ RandomReal[{-10, 10}, {3, 3, 2}];

hulls = Flatten[List[hulls0, hulls1, hulls2]];
Show[hulls] ## Question

I now want to extend every region, to include also all points within a given distance d. Afterwards I want to obtain the union of all extended regions.

A 0D region (point) will therefore become a circle, a 1D region (line) will become two half circles with a rectangle in between, and so on.

My simple approach using

infReg[d_,regs_] := ImplicitRegion[RegionDistance[#, {x, y}] < d, {x, y}] & /@ regs
RegionUnion[infReg[2,hulls]]


doesn't work...

## Real test case

You can take these hulls to test a solution with one of my real cases: PasteBin - Testcase

## Minimal test case (take d=1)

poly1 = ConvexHullMesh[{{0, 0}, {1, 1}, {2, 0}, {1, -1}}];
poly2 = ConvexHullMesh[{{0, 0}, {2, 2}, {2, 0}, {0, -2}}];
hulls = {poly1, poly2}

• a 1D region (line) will become two half circles with a rectangle in between - that is a stadium, in Mma since v10.2 it's called StadiumShape. – kirma Jul 20 '16 at 5:14
• @kirma, interesting, I've tended to use the term "capsule" myself... – J. M.'s torpor Jul 20 '16 at 8:07
• @J.M. Apparently there's StadiumShape for 2D and CapsuleShape for 3D... – kirma Jul 20 '16 at 8:11

Here we run up against the slowness of ImplicitRegion and RegionPlot when compared to ContourPlot. It's the same thing that led to this fantastic post.

Here is the first instinct,

SeedRandom;
hulls0 = ConvexHullMesh /@ RandomReal[{-10, 10}, {3, 1, 2}];
hulls1 = ConvexHullMesh /@ RandomReal[{-10, 10}, {3, 2, 2}];
hulls2 = ConvexHullMesh /@ RandomReal[{-10, 10}, {3, 3, 2}];
hulls = Flatten[List[hulls0, hulls1, hulls2]];

ImplicitRegion[
RegionDistance[hulls1[], {x, y}] <= 2, {x, y}] // RegionPlot That was really slow and not accurate! Basically, the framework underlying ImplicitRegion are not optimized for what we want to do. But we can take advantage of another function, which is optimized for this task. What we want is the boundary to the region, where the RegionDistance is equal to some number d, and finding this line boundary line can be done easily and quickly by ContourPlot, and the result can be fed directly to BoundaryDiscretizeGraphics to create the MeshRegion

{#, BoundaryDiscretizeGraphics@#} &@
ContourPlot[
RegionDistance[hulls1[], {x, y}] == 2, {x, -7, -1}, {y, -8, 12}, AspectRatio -> Automatic] Now all that's left is to wrap this up in a function, taking care to figure out the bounds for the ContourPlot first.

Now consider

ClearAll[expandedMeshRegion];
expandedMeshRegion[x_MeshRegion | x_BoundaryMeshRegion, d_] :=
Module[{xmin, xmax, ymin, ymax},
{{xmin, xmax}, {ymin, ymax}} =
Plus[#, {-1.1 d, 1.1 d}] & /@
MinMax /@ Transpose[MeshCoordinates[x]];
ContourPlot[RegionDistance[x, {xx, yy}] == d,
{xx, xmin, xmax}, {yy, ymin, ymax}] // BoundaryDiscretizeGraphics]


It works super fast and seems to work with any RegionDimension less than 3. It works on the whole list quickly as well

expandedMeshRegion[#, 2] & /@ hulls You can combine or show the regions however you like

RegionUnion[expandedMeshRegion[#, 2] & /@ hulls] • This is a great solution! Would you mind explaining your thoughts to some extent, so that I have a chance to understand the successive steps? – DPF Jul 19 '16 at 14:47
• To minimize confusion, consider adding AspectRatio -> Automatic in your ContourPlot[]. – J. M.'s torpor Jul 19 '16 at 15:21
• @DPF Thanks, I tried to expand the explanation a bit. Also check out the linked post from the first sentence - it applies to 3D, but it's a great read. – Jason B. Jul 19 '16 at 15:22
• exellent solution – Wjx Jul 19 '16 at 23:30
• @DPF - I think you are moving the goalposts a bit. The code here, and those below, answer the question you posted. Further, the code in my post will work on the pastebin you edited in quite easily and quickly: i.stack.imgur.com/xgtEe.png . What you are asking now is a new question: "Why won't RegionUnion work on these two simple BoundaryMeshRegions?" The code for this question would be very succinct: << "http://pastebin.com/raw/BYvkdnst"; {RegionPlot[expandedHulls], RegionUnion[expandedHulls]} – Jason B. Jul 20 '16 at 13:30

Somewhat dumb method (for instance, every line has both two Disks and a StadiumShape overlapping), but it's not at least very complicated:

hulls0 = ConvexHullMesh /@ RandomReal[{-10, 10}, {3, 1, 2}];
hulls1 = ConvexHullMesh /@ RandomReal[{-10, 10}, {3, 2, 2}];
hulls2 = ConvexHullMesh /@ RandomReal[{-10, 10}, {3, 3, 2}];

hulls = Flatten@{hulls0, hulls1, hulls2};

With[
{r = 1},
BoundaryDiscretizeRegion[RegionUnion@@
(RegionUnion@@
(MeshPrimitives[#, 0 | 1 | 2] /.
{Point[pt_] :> Disk[pt, r],
Line[line_] :> StadiumShape[line, r]}) & /@ hulls)]] • Uncomplicated is good sometimes. +1 – Young Jul 20 '16 at 6:18
• A side note: this method is not applicable to input boundary mesh regions with holes inside them. Of course, that shouldn't be an issue with convex hulls... – kirma Jul 20 '16 at 8:19

This is slightly more compact than Jason's proposal, except that it throws a bunch of Compile::cpw errors that seem to not affect the final result (and thus, you can use Quiet[] if they bother you):

BlockRandom[SeedRandom;
hulls0 = ConvexHullMesh /@ RandomReal[{-10, 10}, {3, 1, 2}];
hulls1 = ConvexHullMesh /@ RandomReal[{-10, 10}, {3, 2, 2}];
hulls2 = ConvexHullMesh /@ RandomReal[{-10, 10}, {3, 3, 2}];]
hulls = Flatten[{hulls0, hulls1, hulls2}];

inflateAndJoinRegions[hulls_List, d_?NumericQ, opts___] :=
RegionUnion[BoundaryDiscretizeRegion[
ImplicitRegion[RegionDistance[
With[{k = RegionDimension[#]},
If[k < 2, First[MeshPrimitives[#, k]], #]],
{\[FormalX], \[FormalY]}]^2 <= d^2,
{\[FormalX], \[FormalY]}], opts,
Method -> "RegionPlot"] & /@ hulls]

Table[inflateAndJoinRegions[hulls, d, MaxCellMeasure -> {"Length" -> 0.2}] // Quiet,
{d, {1/2, 1, 2}}] // GraphicsRow 