Message Boards Message Boards

Constructing 2D and 3D Jerusalem Cube Fractal

Posted 6 years ago

enter image description here

Consider constructing a Jerusalem Cube Fractal. It seems the logic is if there's a square of certain size (dependent on the level) available, then punch out the shape. I did not notice a nice pattern for this, so I took a brute force approach which means this solution most likely suboptimal.

First let's get the shape we'd like to punch out. To get the connectivity, I'll import the image and turn it to a mesh. To get the correct coordinates, I'll map the pixel approximations to the appropriate values.

The connectivity:

im = Import["https://i.stack.imgur.com/9uvoz.png"];

mesh = RegionResize[ConnectedMeshComponents[ImageMesh[ColorNegate[im]]][[1]], {{0, 1}, {0, 1}}];

Find the correct coordinates:

s = Last[x /. NSolve[x + x^4 + x^3 + x^2 + x == 1, x, Reals]];

nf = Nearest[Union[#, 1 - #] &[{0., s^4, s^3, s^2, s, s + s^4, s + s^4 + s^3}]];

seed = RegionDifference[
  BoundaryDiscretizeGraphics[Rectangle[]],
  BoundaryMeshRegion[Map[First@*nf, MeshCoordinates[mesh], {2}], MeshCells[mesh, 1]]
]

enter image description here

Now for the code that looks for valid squares of with side lengths s^lev and punches out the shape. Note that I didn't take the time to refine makeRects or to make it readable.

iJerusalemCube[mr_, lev_] := 
  Block[{coords, nn, rects, sub},
    coords = MeshCoordinates[mr];
    nn = Nearest[coords];
    rects = Union @@ makeRects[mr, s^lev] /@ coords;
    sub = RegionUnion @@ (RegionResize[seed, Transpose[#] + s^(4+lev){{1, -1}, {1, -1}}]& /@ rects);
    RegionDifference[mr, sub]
]

makeRects[mr_, lev_][p:{x_, y_}] := 
  First[Reap[
    If[Or[Count[Max[Chop[Abs[First[nn[#]]-#]]]& /@ Tuples[Transpose@{p, p+lev}], 0] > 1, Nor @@ RegionMember[mr, {p + {lev/2, -s^(6+lev)}, p + {-s^(6+lev), lev/2}}]] && RegionWithin[mr, Rectangle[p, p+lev]], Sow[{p, p+lev}]];
    If[Or[Count[Max[Chop[Abs[First[nn[#]]-#]]]& /@ Tuples[Transpose@{{x, y-lev}, {x+lev, y}}], 0] > 1, Nor @@ RegionMember[mr, {p + {lev/2, s^(6+lev)}, p + {-s^(6+lev), -lev/2}}]] && RegionWithin[mr, Rectangle[{x, y-lev}, {x+lev, y}]], Sow[{{x, y-lev}, {x+lev, y}}]];
    If[Or[Count[Max[Chop[Abs[First[nn[#]]-#]]]& /@ Tuples[Transpose@{{x-lev, y}, {x, y+lev}}], 0] > 1, Nor @@ RegionMember[mr, {p + {-lev/2, -s^(6+lev)}, p + {s^(6+lev), lev/2}}]] && RegionWithin[mr, Rectangle[{x-lev, y}, {x, y+lev}]], Sow[{{x-lev, y}, {x, y+lev}}]];
    If[Or[Count[Max[Chop[Abs[First[nn[#]]-#]]]& /@ Tuples[Transpose@{p-lev, p}], 0] > 1, Nor @@ RegionMember[mr, {p + {-lev/2, s^(6+lev)}, p + {s^(6+lev), -lev/2}}]] && RegionWithin[mr, Rectangle[p-lev, p]], Sow[{p-lev, p}]];
  ][[-1]], {}]

Here, iJerusalemCube finds all rectangles that

  • have side lengths s^lev
  • have a corner that's a coordinate of mr
  • either
  • has multiple corners that are a coordinate of mr (the nn part)
  • is tucked into a corner, i.e. does not just meet the boundary at a single point (the Nor @@ RegionMember part)
  • is fully contained within mr (the RegionWithin part).

Then it punches out the shape in all valid rectangles.

The main function will iterate this:

JerusalemCubeList[lev_Integer?NonNegative] := 
  FoldList[
    iJerusalemCube, 
    BoundaryDiscretizeGraphics[Rectangle[]], 
    Range[0, lev-1]
  ]

JerusalemCubeList[4]

enter image description here

Multicolumn[MapIndexed[
  BoundaryMeshRegion[#1, MeshCellStyle -> {1 -> Black, 2 -> ColorData[112][First[#2]]}] &, 
  JerusalemCubeList[5]], 3, Appearance -> "Horizontal"]

enter image description here

And unsurprising that the number of boundary edges grows exponentially:

cnts = MeshCellCount[#, 1] & /@ JerusalemCubeList[5]
{4, 32, 144, 704, 3504, 17504}
FindSequenceFunction[Rest@cnts, n]
4/5 (5 + 7 5^n)

To get a cube in 3D, we can intersect the 2D mesh extruded in 3 different directions:

mr = Last[JerusalemCubeList[3]];

bmr = BoundaryMesh[RegionProduct[mr, Line[{{0.}, {1.}}]]];

RegionIntersection @@ (
  BoundaryMeshRegion[MeshCoordinates[bmr][[All, #]], MeshCells[bmr, 2]] & /@ 
  {{1, 2, 3}, {3, 2, 1}, {1, 3, 2}})

enter image description here


Original post can be found HERE.

POSTED BY: Greg Hurst
7 Replies
Posted 5 years ago

Hear, hear, Eric's description is way better than mine at https://robertdickau.com/jerusalemcube.html and https://robertdickau.com/spongeslices.html#asterisk.

POSTED BY: Robert Dickau
Posted 5 years ago

Hi Chip!

FWIW, your model isn't the "official" Jerusalem Cube, it's a "houndstooth" variant (probably by Robert Dickau).

The "real thing" has crosses, whose arms have the proportions 1:root(2), and it has mirror-symmetry. The problem with the non-mirror-symmetrical variants is that they don't extend cleanly into three dimensions: if you punch a "right-handed" houndstooth through a cube, it comes out on the other side as left-handed. So you end up with three right-handed faces circling around one cube corner and three left-handed faces circling the opposite corner, and then you have another six corners whose three adjacents are either 2+1 or 1+2 left/right. It loses some of the original cube symmetry.

Regards, Eric

POSTED BY: Eric Baird
Posted 6 years ago

Coincidentally, the Demonstration "Cross Menger (Jerusalem) Fractal" was published today, to give more details about the construction. Instead of punching holes through the shape, each iteration is constructed by shrinking and assembling copies of the previous two iterations. (See "Cross Menger (Jerusalem) Cube Fractal" for the difference between using one or two previous iterations.)

POSTED BY: Robert Dickau

Can this method make the Wallis sponge?

POSTED BY: Ed Pegg
Posted 6 years ago

Yes, a similar method can.

iWallis[n_] := With[{k = 2 n - 1},
  BoundaryDiscretizeGraphics[
   Table[Rectangle[{x, y}, {x, y} + 1/(k + 2)!!], 
      {x, (k + 1)/(2 (k + 2)!!), 1 - 1/(k + 2)!!, 1/k!!}, 
      {y, (k + 1)/(2 (k + 2)!!), 1 - 1/(k + 2)!!, 1/k!!}
    ]
  ]
]

init = BoundaryDiscretizeGraphics[Rectangle[]];

wallis2D = Fold[RegionDifference[#1, iWallis[#2]] &, init, Range[3]]

enter image description here

bmr = BoundaryMesh[RegionProduct[wallis2D, Line[{{0.}, {1.}}]]];

RegionIntersection @@ (BoundaryMeshRegion[
     MeshCoordinates[bmr][[All, #]], MeshCells[bmr, 2]] & /@ {{1, 2, 
     3}, {3, 2, 1}, {1, 3, 2}})

enter image description here

POSTED BY: Greg Hurst
Posted 6 years ago

It's kind of neat to see the tunnel structure here:

BoundaryMeshRegion[wallis3D, MeshCellStyle -> {1 -> None, 2 -> Opacity[.5]}, PlotTheme -> "Default"]

enter image description here

POSTED BY: Greg Hurst

enter image description here - Congratulations! This post is now a Staff Pick as distinguished by a badge on your profile! Thank you, keep it coming!

POSTED BY: EDITORIAL BOARD
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract