12
$\begingroup$

I'm looking for a general method of packing any set of 2D glyphs. For example, say I had 30 randomly transformed english characters:

$letters = 
  Table[First[
    First[ImportString[
      ExportString[
       Style[c, Italic, FontSize -> 24, FontFamily -> "Times"], 
       "PDF"], "PDF", "TextMode" -> "Outlines"]]], {c, Alphabet[]}];
n = 30;
toPack = Table[{Hue[RandomReal[]], 
    Translate[
     Rotate[Scale[RandomChoice[$letters], RandomReal[5]], 
      RandomReal[2 Pi]], RandomReal[20, {2}]]}, {n}];
Graphics@toPack

enter image description here

How would I pack them so they are touching on their edges? Doesn't have to be optimal, just a random layout where they are touching compactly. Basically, assume they are all magnets that attract each other in 2D.

For example, with 5 shapes, here's an output I would expect:

enter image description here

Update:

I played with @ChipHurst's idea of using WordCloud, but it doesn't make valid tight packings, most of them aren't touching and some of them even overlap.

enter image description here

$\endgroup$
  • 2
    $\begingroup$ I assume your picture is an infeasible packing? $\endgroup$ – MikeY Mar 28 at 23:23
  • $\begingroup$ Sure, just starter code for generating random shapes that I'd like to layout in a packing $\endgroup$ – M.R. Mar 30 at 2:48
  • 1
    $\begingroup$ You can get close to what you want with WordCloud using random weights and WordSpacings -> 0. If you need them touching, perhaps you could iterate over some force field until they're close enough. $\endgroup$ – Chip Hurst Apr 10 at 21:08
  • $\begingroup$ Though to avoid overlap, I had to rasterize your toPack. I'm not sure why. SeedRandom[1234]; WordCloud[ AssociationThread[Rasterize[#, Background -> None] & /@ Graphics /@ toPack, RandomReal[5, Length[toPack]]], WordSpacings -> 0] $\endgroup$ – Chip Hurst Apr 10 at 21:12
  • $\begingroup$ Ah nice trick, but the shapes might not be just letters (I picked letters arbitrarily) but arbitrary polygon shapes $\endgroup$ – M.R. Apr 10 at 21:22
14
+200
$\begingroup$

Perhaps a start:

We can extract information from WordCloud in order to translate a collection of regions so they pack nicely.

First I'll create some BoundaryMeshRegions similar to how the glyphs were created by OP:

$letters = Table[BoundaryDiscretizeGraphics[
  Text[Style[c, Italic, FontFamily -> "Times"]], _Text], {c, Alphabet[]}];

n = 30;

BlockRandom[
  glyphs = RandomChoice[$letters, n];
  scales = RandomReal[5, n],
  RandomSeeding -> 1234
];

Plot the word cloud using random orientations:

wc = WordCloud[AssociationThread[glyphs, scales], WordSpacings -> 0,
  WordOrientation -> "Random", RandomSeeding -> 1234]

Notice that the objects aren't quite touching. Luckily when we convert this scene back to a collection of regions, they will seem to be touching. I think this has to do with padding within Inset. Using regions in the beginning rather then just graphics makes it easier to convert the insets into explicit coordinates and avoid padding.

insets = Cases[wc2, _Inset, ∞];

insetToReg[mr_, c_, p_, s_] := 
  BoundaryMeshRegion[TransformedRegion[#, 
   TranslationTransform[c - RegionCentroid[BoundingRegion[#]]]], 
    MeshCellStyle -> {1 -> Black, 2 -> RandomColor[Hue[_]]}]& @ RegionResize[mr[[1]], s]

BlockRandom[Show[insetToReg @@@ insets], RandomSeeding -> 1234]

Or if you prefer a region instead of just a visualization:

RegionUnion[insetToReg @@@ insets]

We can do this for polygons too:

BlockRandom[
  polys = 
   Table[BoundaryMeshRegion[#[[FindShortestTour[#][[2]]]], 
       Line[Mod[Range[16], 15, 1]]] &[RandomReal[{0, 1}, {15, 2}]], n];
  scales = RandomReal[{0, 1}, n],
  RandomSeeding -> 1234
  ];
wc = WordCloud[AssociationThread[polys, scales], WordSpacings -> 0, 
   WordOrientation -> "Random", RandomSeeding -> 1234];
BlockRandom[Show[insetToReg @@@ Cases[wc, _Inset, ∞]], 
 RandomSeeding -> 1234]

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.