4

Consider a set of points generated via Lloyd's relaxation algorithm

rel = Function[{pts}, 
   Block[{cells}, 
    cells = MeshPrimitives[VoronoiMesh[pts, {{-1, 1}, {-1, 1}}], 
      "Faces"];
    RegionCentroid /@ 
     cells[[SparseArray[Outer[#2@#1 &, pts, RegionMember /@ cells, 1],
           Automatic, False]["NonzeroPositions"][[All, 2]]]]]];
n = 30;
pts = RandomReal[{-1, 1}, {n, 2}];
vor = VoronoiMesh[Nest[rel, pts, 20]]

enter image description here

Then, if I wanted a more "realistic" mesh and exclude the boundary cells, I could get something like this

vori = MeshCellIndex[vor, {2, "Interior"}];
Graphics[{Gray, EdgeForm[{Thick, White}], 
  Table[MeshPrimitives[vor, 2][[vori[[j, 2]]]], {j, Length[vori]}]}]

enter image description here

Now, the problem with this approach is that I can't get the exact number of interior cells that I want (same as this approach). It will always depend on which cells touch the boundary (or "Frontier", as it's sometimes used) and which don't. Now, an alternative way is to consider a periodic mesh, and in that case we get

ptsi = Nest[rel, pts, 20];
pts2 = Flatten[
   Table[TranslationTransform[{2 i, 2 j}][ptsi], {i, -1, 1}, {j, -1, 
     1}], 2];
vorp = VoronoiMesh[pts2, {{-3, 3}, {-3, 3}}];
vcells = Catenate[NearestMeshCells[{vorp, 2}, #] & /@ ptsi];
pvor = MeshRegion[MeshCoordinates[vorp], MeshCells[vorp, vcells]]

enter image description here

This seems to do the trick (despite some occasional problems with rel), but it still has the problem that it is only considering periodic meshes.

My goal: Given a number n, generate a roughly square mesh of similar "realistic-looking" cells, in the sense of the examples above. For instance, I think it would be enough to simply fix the mean and variance of the cells area and perimeter, such that the tissue has a "uniform" look, and no "spiky" cells appear. I'm sorry for the over-usage of " ", but I'm ok with slightly different mathematical descriptions, as long as I get a mesh with a similar look to the ones presented above.

On top of that, if the mesh moves (as seen here, for example), I want the cells to be able to move accordingly (so that suddenly a cell doesn't become a frontier cell and disappears, which could happen in the first approach). Naturally I could draw the cells, but I want to specifically use VoronoiMesh and avoid periodic meshes.

Any ideas?

J. M.'s missing motivation
  • 124,525
  • 11
  • 401
  • 574
sam wolfe
  • 4,663
  • 7
  • 37
  • 1
    Can you clarify again what is meant by "realistic"? – TumbiSapichu Jul 08 '20 at 12:32
  • Naturally I could draw the cells, but I want to specifically use VoronoiMesh and avoid periodic meshes. you could build up cells without VoronoiMesh and still avoid periodicity by simulating cell division. – flinty Jul 08 '20 at 12:50
  • 1
    @TumbiSapichu Great question, I've edited the "My goal" section, so that hopefully give you an idea of what I want. – sam wolfe Jul 08 '20 at 14:12
  • @flinty Yes, I agree, but I wonder if it's possible to do what I want using VoronoiMesh. – sam wolfe Jul 08 '20 at 14:13
  • 1
    @samwolfe I've thought a bit about the "regular cells" problem recently, and although I still have not tried an answer, here's an idea. I think it's not possible to build cells with a known average area/perimeter without first building the Voronoi mesh (I might be wrong here). But, what you could do is generate random points as you do, then calculate their areas/perimeters, then use use Lloyd's process and check the area/perimeter at every time step, and stop when it is around the desired average. This could be done also applying Loyd's to only some cells (the "irregular ones"), for instance. – TumbiSapichu Jul 08 '20 at 14:19
  • @TumbiSapichu Yes I believe that to be the best way of tracking areas and perimeters. In a moving tissue scenario (see linked question), it's probably better if simulations only start occurring after some iterations of Lloyd's algorithm. Nonetheless, I think irregularity, as you say, is what's being measured in the end. It's the boundary cells that concern me, because there is no obvious way of making them irregular, other than translating the mesh in the four direction, as seen in the periodic case. To determine irregular edges on all four sides seems to be very subjective. – sam wolfe Jul 08 '20 at 14:26
  • Well, actually it just occurred to me that instead of translating the seeds, you could simply add more points, enough so that, upon drawing a disk centred in this new mesh, you simply pick the first n cells which seeds intersect the disk, with increasing radius, until the n threshold is met. This should work, right? In this setting I wouldn't even care about areas or perimeters, Lloyd would cover that for me. Wonder if this is efficient for large n though.. – sam wolfe Jul 08 '20 at 14:28
  • 1
    I see. It's tricky, and I don't see if it will work (but you should try!). Alternatively, you can "fix" cells at the boundary (just like a picture frame), and only generate random points inside the frame, then apply all these processes (Lloyd's and whatnot) to the inner cells only (but the Voronoi tesselation to all points, including the frame), for enough steps that the inner cells become regular without converging to an hexagonal lattice. Another approach that seems relevant to you (or at least the ideas in it) is here. – TumbiSapichu Jul 08 '20 at 14:31
  • Ah I see! And if such outer points were close enough (but not too close) then the Voronoi tessellation would actually seal the interior cells, and avoid any from "escaping", like a frame as you said. Don't see how this wouldn't work, but I can try! Now the question is more how many points should you use for the frame, and how close should they be to each other. Too close could yield weirdly shaped edges of the interior cells, but if you get interior cell divisions, then I suspect you should also adjust the frame accordingly, or maybe not. The paper looks very interesting, I'll take a look! – sam wolfe Jul 08 '20 at 14:42
  • 1
    @TumbiSapichu I've added my answer, please take a look and let me know if you have any comments. – sam wolfe Jul 08 '20 at 16:13

1 Answers1

3

Following the discussion in the comment section with @TumbiSapichu, I've found a possible solution to this problem. As mentioned, instead of translating the seeds, we could simply add more points, enough so that, upon drawing a rectangle centred in this new mesh, you simply pick the first n cells which seeds intersect the rectangle, with increasing size, until the n threshold is met. The following code does what I want, where n = 36 is simply selected so that we get an approximate $6\times 6$ lattice

n = 36;
rel = Function[{pts}, 
   Block[{cells}, 
    cells = MeshPrimitives[VoronoiMesh[pts, {{-1, 1}, {-1, 1}}], 
      "Faces"];
    RegionCentroid /@ 
     cells[[SparseArray[Outer[#2@#1 &, pts, RegionMember /@ cells, 1],
           Automatic, False]["NonzeroPositions"][[All, 2]]]]]];
pts = RandomReal[{-1, 1}, {1 + 2 n, 2}];
ptsr = Nest[rel, pts, 20];
vor = VoronoiMesh[ptsr];
rr = 0.1;
ac = 0;
While[ac < n,
  rt = Rectangle[{-rr, -rr}, {rr, rr}];
  ml = Select[MeshPrimitives[vor, 2], 
    RegionDimension[RegionIntersection[#, rt]] =!= -Infinity &];
  ac = Length[ml];
  rr = rr + .05
  ];
Graphics[{Gray, EdgeForm[{Thick, White}], ml}]

enter image description here

In fact, there is no limitation as to the shape of rt. Considering more cells and taking rt to be, for example, the disk

rt = Disk[{0, 0}, rr];

We get, for n = 400,

enter image description here

Notice that the radius increments become more sensible for higher value of n and therefore should be adjusted accordingly to avoid counting too many cells.

Just as an interesting observation, the disk case can be replicated using the following code

ml2 = Table[
   MeshPrimitives[vor, 
     2][[NearestMeshCells[vor, {0, 0}, n][[j, 2]]]], {j, n}];
Graphics[{Gray, EdgeForm[{Thick, White}], ml2}]

where NearestMeshCells is based on euclidean distance, maybe it's possible to tweak it in such a way that incorporates the rectangle case as well (Manhattan distance, maybe?).

Anyway, this seems to work fine for relatively small n. As suggested by @TumbiSapichu in the comments, fixing a cell frame and letting only interior cells move and update via Lloyd's relaxation algorithm could prove another, and perhaps more efficient, way of doing this. Let me know if you have any comments or improvements.

sam wolfe
  • 4,663
  • 7
  • 37