Home > Blockchain >  matplotlib.pyplot.tripcolor how to fill triangles with random RGB colors?
matplotlib.pyplot.tripcolor how to fill triangles with random RGB colors?

Time:05-25

Say I have a bunch of triangles, I know how to draw them using matplotlib.pyplot.tripcolor, I want to know how to fill the individual triangles with completely random RGB colors from the entire RGB color space (all 16777216 colors) unrelated to x, y whatsoever, how to get this done?

I have this: enter image description here

The colors are not random at all.

The second generates this:

enter image description here

There is little variation of colors in the second image.

I have tried a number of methods, all of them failed:

colors = np.random.random((l, 3))
plt.tripcolor(pts[:,0], pts[:,1], tri.simplices.copy(), facecolors=colors, edgecolors='k')
plt.gca().set_aspect('equal')
plt.show()

colors = [tuple(randbytes(3)) for i in range(l)]
plt.tripcolor(pts[:,0], pts[:,1], tri.simplices.copy(), facecolors=colors, edgecolors='k')
plt.gca().set_aspect('equal')
plt.show()

colors = [tuple(randbytes(4)) for i in range(l)]
plt.tripcolor(pts[:,0], pts[:,1], tri.simplices.copy(), facecolors=colors, edgecolors='k')
plt.gca().set_aspect('equal')
plt.show()

colors = [tuple([random() for i in range(3)]) for j in range(l)]
plt.tripcolor(pts[:,0], pts[:,1], tri.simplices.copy(), facecolors=colors, edgecolors='k')
plt.gca().set_aspect('equal')
plt.show()

colors = [tuple([random() for i in range(4)]) for j in range(l)]
plt.tripcolor(pts[:,0], pts[:,1], tri.simplices.copy(), facecolors=colors, edgecolors='k')
plt.gca().set_aspect('equal')
plt.show()

colors = [randbytes(3).hex() for j in range(l)]
plt.tripcolor(pts[:,0], pts[:,1], tri.simplices.copy(), facecolors=colors, edgecolors='k')
plt.gca().set_aspect('equal')
plt.show()

colors = [randbytes(4).hex() for j in range(l)]
plt.tripcolor(pts[:,0], pts[:,1], tri.simplices.copy(), facecolors=colors, edgecolors='k')
plt.gca().set_aspect('equal')
plt.show()

colors = ['#' randbytes(3).hex() for j in range(l)]
plt.tripcolor(pts[:,0], pts[:,1], tri.simplices.copy(), facecolors=colors, edgecolors='k')
plt.gca().set_aspect('equal')
plt.show()

colors = ['#' randbytes(4).hex() for j in range(l)]
plt.tripcolor(pts[:,0], pts[:,1], tri.simplices.copy(), facecolors=colors, edgecolors='k')
plt.gca().set_aspect('equal')
plt.show()

The first seven raise ValueError: Collections can only map rank 1 arrays.

The last two raise TypeError: Image data of dtype <U6 cannot be converted to float.

How to properly do this? Exactly what should be put into facecolors? The official documentation is extremely vague about what it should be.


For completeness' sake, my intention is to:

from PIL import Image

fig = plt.figure(frameon=False, figsize=(19.2,10.8), dpi=100)
ax = fig.add_subplot(111)
ax.set_axis_off()
...
fig.canvas.draw()
fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0)
plt.axis('scaled')
plt.box(False)
Image.frombytes('RGB', fig.canvas.get_width_height(), fig.canvas.tostring_rgb())

I was able to get this:

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Polygon
from PIL import Image
from scipy.spatial import Delaunay
from random import random, randbytes

plt.style.use('_mpl-gallery-nogrid')

points = np.zeros((360,2))
points[:,0] = np.random.randint(0,1920,360)
points[:,1] = np.random.randint(0,1080,360)
triangles = points[Delaunay(points).simplices]

fig = plt.figure(frameon=False, figsize=(19.2,10.8), dpi=100)
ax = fig.add_subplot(111)
ax.set_axis_off()
for triangle in triangles:
    ax.add_patch(Polygon(triangle, edgecolor='#c0c0c0', facecolor='#' randbytes(3).hex(), fill=True))

plt.xlim(0, 1920)
plt.ylim(0, 1080)
fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0)
plt.axis('scaled')
plt.box(False)
fig.canvas.draw()
Image.frombytes('RGB', fig.canvas.get_width_height(), fig.canvas.tostring_rgb()).show()

But my code is obviously much less efficient than tripcolor.

CodePudding user response:

It's probably easiest to avoid using tripcolor alltogether, unless you need some of it's specific functionality? You can create your own PolyCollection from the Delauny triangulation, which is a lot more flexible regarding formatting.

from matplotlib.collections import PolyCollection
import matplotlib.pyplot as plt
from scipy.spatial import Delaunay
from copy import copy

n_points = 360
pts = np.random.randint(0, 1920, (n_points, 2)).astype(np.float32)

tri = Delaunay(pts)

vertices = np.stack((
    tri.points[tri.simplices, 0], # x
    tri.points[tri.simplices, 1], # y
), axis=-1)


collection = PolyCollection(vertices, edgecolor="k")
collection.set_facecolor(np.random.rand(len(vertices), 3))

fig, ax = plt.subplots(figsize=(8,8), facecolor="w")

ax.add_collection(copy(collection))
ax.autoscale_view()

enter image description here

CodePudding user response:

One way to work around it is to create a colormap that generates random colors, independently of the values you have assigned to facecolors or C. For example:

import matplotlib.pyplot as plt
from matplotlib.colors import Colormap
import numpy as np
from scipy.spatial import Delaunay

class RandomColors(Colormap):
    def __init__(self):
        pass
    def __call__(self, X, alpha=None, bytes=False):
        # randomly generate an RGBA [N x 4] matrix of colors
        # where N is the number of elements of X
        # X is what we assigne to `facecolors` or `C`
        X = np.atleast_1d(X)
        col = np.random.random((X.shape[0], 4))
        # set alpha=1
        col[:, -1] = 1
        return col


plt.figure()
pts = np.zeros((360,2))
pts[:,0] = np.random.randint(0,1920,360)
pts[:,1] = np.random.randint(0,1080,360)
tri = Delaunay(pts)
plt.xlim(0, 1920)
plt.ylim(0, 1080)
centers = np.sum(pts[tri.simplices], axis=1, dtype='int')/3.0

# Don't really care about what's in this array: our custom colormap
# is going to ignore it!
colors = np.ones(centers.shape[0])
# instantiate the colormap
cmap = RandomColors()
plt.tripcolor(pts[:,0], pts[:,1], tri.simplices.copy(), facecolors=colors, cmap=cmap, edgecolors='k')
plt.gca().set_aspect('equal')
plt.show()

enter image description here

  • Related