One solution can be to use SVG.
Here is a sample implementation.
Grid Boxes table with a child table that contains cell rows. The cell row contains a ref to the item it stores.
Click the image to enlarge it.
The item has a copy of the image of the grid box that it is housed in.
This is the code I had ChatGPT generate, modified and used in a VC of type image (you can search the community for SVG usage tips).
CONCATENATE("data:image/svg+xml;utf8,
<svg width=""700"" height=""700"" xmlns=""http://www.w3.org/2000/svg"">
<!-- Draw the grid lines -->
<rect width=""100%"" height=""100%"" fill=""white"" />
<line x1=""25%"" y1=""0"" x2=""25%"" y2=""100%"" stroke=""black"" stroke-width=""2"" />
<line x1=""50%"" y1=""0"" x2=""50%"" y2=""100%"" stroke=""black"" stroke-width=""2"" />
<line x1=""75%"" y1=""0"" x2=""75%"" y2=""100%"" stroke=""black"" stroke-width=""2"" />
<line x1=""0"" y1=""25%"" x2=""100%"" y2=""25%"" stroke=""black"" stroke-width=""2"" />
<line x1=""0"" y1=""50%"" x2=""100%"" y2=""50%"" stroke=""black"" stroke-width=""2"" />
<line x1=""0"" y1=""75%"" x2=""100%"" y2=""75%"" stroke=""black"" stroke-width=""2"" />
<!-- Add black lines around the outer edges of the grid -->
<rect x=""0"" y=""0"" width=""100%"" height=""100%"" fill=""transparent"" stroke=""black"" stroke-width=""2"" />
<!-- Add text with cell coordinates -->
<text x=""5%"" y=""5%"" fill=""black"" font-size=""12"" text-anchor=""start"" alignment-baseline=""middle"">A,0</text>
<text x=""30%"" y=""5%"" fill=""black"" font-size=""12"" text-anchor=""start"" alignment-baseline=""middle"">A,1</text>
<text x=""55%"" y=""5%"" fill=""black"" font-size=""12"" text-anchor=""start"" alignment-baseline=""middle"">A,2</text>
<text x=""80%"" y=""5%"" fill=""black"" font-size=""12"" text-anchor=""start"" alignment-baseline=""middle"">A,3</text>
<text x=""5%"" y=""30%"" fill=""black"" font-size=""12"" text-anchor=""start"" alignment-baseline=""middle"">B,0</text>
<text x=""30%"" y=""30%"" fill=""black"" font-size=""12"" text-anchor=""start"" alignment-baseline=""middle"">B,1</text>
<text x=""55%"" y=""30%"" fill=""black"" font-size=""12"" text-anchor=""start"" alignment-baseline=""middle"">B,2</text>
<text x=""80%"" y=""30%"" fill=""black"" font-size=""12"" text-anchor=""start"" alignment-baseline=""middle"">B,3</text>
<text x=""5%"" y=""55%"" fill=""black"" font-size=""12"" text-anchor=""start"" alignment-baseline=""middle"">C,0</text>
<text x=""30%"" y=""55%"" fill=""black"" font-size=""12"" text-anchor=""start"" alignment-baseline=""middle"">C,1</text>
<text x=""55%"" y=""55%"" fill=""black"" font-size=""12"" text-anchor=""start"" alignment-baseline=""middle"">C,2</text>
<text x=""80%"" y=""55%"" fill=""black"" font-size=""12"" text-anchor=""start"" alignment-baseline=""middle"">C,3</text>
<text x=""5%"" y=""80%"" fill=""black"" font-size=""12"" text-anchor=""start"" alignment-baseline=""middle"">D,0</text>
<text x=""30%"" y=""80%"" fill=""black"" font-size=""12"" text-anchor=""start"" alignment-baseline=""middle"">D,1</text>
<text x=""55%"" y=""80%"" fill=""black"" font-size=""12"" text-anchor=""start"" alignment-baseline=""middle"">D,2</text>
<text x=""80%"" y=""80%"" fill=""black"" font-size=""12"" text-anchor=""start"" alignment-baseline=""middle"">D,3</text>
<!-- Add text with the concatenation of y and x coordinates at the center of each cell -->
<text x=""12.5%"" y=""15%"" fill=""black"" font-size=""12"" text-anchor=""middle"" alignment-baseline=""middle"">",LOOKUP(INDEX(SELECT(gb_cells[item],AND([gb_id]=[_THISROW],[cell]="A1")),1),"items","iten_id","item_name"),"</text>
<text x=""37.5%"" y=""15%"" fill=""black"" font-size=""12"" text-anchor=""middle"" alignment-baseline=""middle"">",LOOKUP(INDEX(SELECT(gb_cells[item],AND([gb_id]=[_THISROW],[cell]="A2")),1),"items","iten_id","item_name"),"</text>
<text x=""62.5%"" y=""15%"" fill=""black"" font-size=""12"" text-anchor=""middle"" alignment-baseline=""middle"">A2</text>
<text x=""87.5%"" y=""15%"" fill=""black"" font-size=""12"" text-anchor=""middle"" alignment-baseline=""middle"">A3</text>
<text x=""12.5%"" y=""40%"" fill=""black"" font-size=""12"" text-anchor=""middle"" alignment-baseline=""middle"">",LOOKUP(INDEX(SELECT(gb_cells[item],AND([gb_id]=[_THISROW],[cell]="B1")),1),"items","iten_id","item_name"),"</text>
<text x=""37.5%"" y=""40%"" fill=""black"" font-size=""12"" text-anchor=""middle"" alignment-baseline=""middle"">B1</text>
<text x=""62.5%"" y=""40%"" fill=""black"" font-size=""12"" text-anchor=""middle"" alignment-baseline=""middle"">B2</text>
<text x=""87.5%"" y=""40%"" fill=""black"" font-size=""12"" text-anchor=""middle"" alignment-baseline=""middle"">B3</text>
<text x=""12.5%"" y=""65%"" fill=""black"" font-size=""12"" text-anchor=""middle"" alignment-baseline=""middle"">C0</text>
<text x=""37.5%"" y=""65%"" fill=""black"" font-size=""12"" text-anchor=""middle"" alignment-baseline=""middle"">C1</text>
<text x=""62.5%"" y=""65%"" fill=""black"" font-size=""12"" text-anchor=""middle"" alignment-baseline=""middle"">C2</text>
<text x=""87.5%"" y=""65%"" fill=""black"" font-size=""12"" text-anchor=""middle"" alignment-baseline=""middle"">C3</text>
<text x=""12.5%"" y=""90%"" fill=""black"" font-size=""12"" text-anchor=""middle"" alignment-baseline=""middle"">D0</text>
<text x=""37.5%"" y=""90%"" fill=""black"" font-size=""12"" text-anchor=""middle"" alignment-baseline=""middle"">D1</text>
<text x=""62.5%"" y=""90%"" fill=""black"" font-size=""12"" text-anchor=""middle"" alignment-baseline=""middle"">D2</text>
<text x=""87.5%"" y=""90%"" fill=""black"" font-size=""12"" text-anchor=""middle"" alignment-baseline=""middle"">D3</text>
</svg>
")
LOOKUP(INDEX(SELECT(gb_cells[item],AND([gb_id]=[_THISROW],[cell]=“A1”)),1) needs to be modified to fit your table structure at the least.
I am no expert at SVG. But if you decide to use it and have questions, you can certainly ask them here for guidance from experts.