# 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. Jul 20, 2016 at 5:14
• @kirma, interesting, I've tended to use the term "capsule" myself... Jul 20, 2016 at 8:07
• @J.M. Apparently there's StadiumShape for 2D and CapsuleShape for 3D... Jul 20, 2016 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, 2016 at 14:47
• To minimize confusion, consider adding AspectRatio -> Automatic in your ContourPlot[]. Jul 19, 2016 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. Jul 19, 2016 at 15:22
• exellent solution
– Wjx
Jul 19, 2016 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]} Jul 20, 2016 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 Jul 20, 2016 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... Jul 20, 2016 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 