Makie equivalent of matplotlib's `PolyCollection`

I’m looking to reproduce something like the following in Makie:

using PyPlot

const PolyCollection = PyPlot.matplotlib.collections.PolyCollection
polygon = [[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]
# force new axes
ax = PyPlot.axes(label=string(Base.UUID(rand(UInt128))))
ax.set_aspect(1.0)

# quite clumbsy
grid(xs, ys) = collect(reinterpret(reshape, Int, reshape(collect(Iterators.product(xs, ys)), :))')

offsets = grid(1:10, 1:15)

# based off https://github.com/matplotlib/matplotlib/blob/40862a32e86ed3a9a67790dc249da13971f1a37d/lib/matplotlib/axes/_axes.py#L4886-L4892
poly_collection = PolyCollection(
    [polygon],
    offsets = offsets,
    edgecolors = "face",
    facecolors = rand(n, 4),
    transOffset = matplotlib.transforms.AffineDeltaTransform(ax.transData),
)

(xmin, xmax) = extrema(offsets[:, 1])
(ymin, ymax) = extrema(offsets[:, 2])
corners = ((xmin, ymin), (xmax, ymax))
ax.update_datalim(corners)
ax._request_autoscale_view(tight=true)

ax.add_collection(poly_collection, autolim=false)

display(ax.figure)
ax.figure.savefig("poly_collection.svg")

image

Extra bonus if the SVG output can use an href instead of inlining the polygon path repeatedly.

This is how matplotlib implements e.g. hexbin.

This would be one way although I can’t claim the SVG href bonus, that would require much more optimization in CairoMakie. In my approach you also have to shift each polygon where you want it as a whole, which is a bit more work (especially with rotations), but the example is still only a couple lines, so :man_shrugging:

using CairoMakie

pol = Point2f0[(0, 0), (0, 1), (1, 0)]

offsets = vec(Point2f0.(1:10, (1:15)'))

poly([pol .+ o for o in offsets],
    color = rand(RGBf0, length(offsets)),
    axis = (aspect = DataAspect(),))

grafik

2 Likes

Thanks! That’s certainly an option.

I need to update the colors of each of the polygons, and I think it would not work well if each were an Observable, instead of having one Observable array.

I’m not sure I understand. You do want to use an observable with an array of colors? That should already work with the method I showed, no?

Ah, yes, indeed!