5

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]

regions example

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}
DPF
  • 1,067
  • 8
  • 19

3 Answers3

7

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[42];
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[[1]], {x, y}] <= 2, {x, y}] // RegionPlot

Mathematica graphics

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[[1]], {x, y}] == 2, {x, -7, -1}, {y, -8, 12}, AspectRatio -> Automatic]

Mathematica graphics

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

Mathematica graphics

You can combine or show the regions however you like

RegionUnion[expandedMeshRegion[#, 2] & /@ hulls]

Mathematica graphics

Jason B.
  • 68,381
  • 3
  • 139
  • 286
  • 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
  • 1
    To minimize confusion, consider adding AspectRatio -> Automatic in your ContourPlot[]. – J. M.'s missing motivation 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
  • I found a case, where this method fails. Just copy and paste this hulls definition: PasteBin – DPF Jul 20 '16 at 07:37
  • Do you think, it is possible to make the union already inside the ContourPlot? This might speed up the whole thing a little, when you've got many regions... – DPF Jul 20 '16 at 07:49
  • I added the test case to my question – DPF Jul 20 '16 at 08:14
  • @DPF - your pastebin doesn't work: http://i.stack.imgur.com/p4xwW.png – Jason B. Jul 20 '16 at 11:23
  • @JasonB strange. Here (Mma 10.4) it works. How can I provide you the testcase? – DPF Jul 20 '16 at 11:40
  • @JasonB you can try that: link (I removed all occurences of the TJunction option) – DPF Jul 20 '16 at 11:45
  • @JasonB I added a minimal failing example above. It fails for RegionUnion[expandedMeshRegion[#, 1] & /@ hulls] - when d=1, but not for d=2... – DPF Jul 20 '16 at 13:18
  • 2
    @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: http://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
  • @DPF - my point above is that the methods here work to inflate even your failing example, but it uncovers a flaw in RegionUnion for some reason, and that would seem to be a separate issue – Jason B. Jul 20 '16 at 13:35
  • @JasonB I guess, you're right. You have answered the question. I'll decide soon whether or not to open a new question. – DPF Jul 20 '16 at 13:35
  • @DPF - it's interesting, it seems to work for RegionUnion[expandedMeshRegion[#, 1.00000001] & /@ hulls], but not when you add one more zero. Definitely a bug of some kind in RegionUnion having to do with the fact that the two regions share a border. To answer your question above, you can in this case do the RegionUnion before expanding the hulls and it works expandedMeshRegion[#, 1] & /@ hulls, but it won't work when the hulls contain lines and points as well. – Jason B. Jul 20 '16 at 13:44
  • @JasonB Interestingly, here it does not work with my edited pastebin: http://i.stack.imgur.com/w4qrL.png – DPF Jul 20 '16 at 13:46
  • @DPF - not sure about why, works for me - but I have a lot of RAM. Try quitting the kernel. Or try doing the region union one at a time with list = expandedMeshRegion[#, 1] & /@ hulls; Fold[RegionUnion, list] – Jason B. Jul 20 '16 at 13:52
  • @JasonB which version are you using? The failure must have sth. to do with die polygons themselves. other cases with much more polygons work. – DPF Jul 20 '16 at 14:40
  • I'm on Version 10.3.1 – Jason B. Jul 20 '16 at 14:41
  • Version 10.4.0.0 here – DPF Jul 20 '16 at 14:42
  • JasonB and @DPF I just got an automatically generated moderator flag notifying me of an excessive number of comments. I would like to remind you that [Chat] is available for extended discussions and is really a more appropriate place for this. – Mr.Wizard Jul 20 '16 at 14:52
5

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)]]

enter image description here

kirma
  • 19,056
  • 1
  • 51
  • 93
  • Uncomplicated is good sometimes. +1 – Young Jul 20 '16 at 06:18
  • 2
    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 08:19
1

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[42];
            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

inflated regions

J. M.'s missing motivation
  • 124,525
  • 11
  • 401
  • 574