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?
The colors are not random at all.
The second generates this:
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()
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()