Matplotlib 绘制带渐变填充的三角形
Mathplotlib draw triangle with gradient fill
我必须使用 mathplotlib 在 Python 中绘制一个三角形。
This 最终应该是这样的:
我的 objective 是,一旦绘制了三角形,就在其上绘制一些点。
目前我可以很好地绘制三角形:
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal')
ax.add_patch(Polygon([[0,0],[0,1],[1,0]], closed=True,fill=True))
ax.set_xlim((0,1))
ax.set_ylim((0,1))
plt.show()
但我只能用纯色填充。如何添加如图所示的渐变?
有人能帮帮我吗?
有一个 example on the matplotlib page 展示了如何使用图像的剪辑路径。
根据您的情况进行调整会得到:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.path import Path
from matplotlib.patches import PathPatch
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal')
path = Path([[0,0],[0,1],[1,0],[0,0]])
patch = PathPatch(path, facecolor='none')
ax.add_patch(patch)
Z, Z2 = np.meshgrid(np.linspace(0,1), np.linspace(0,1))
im = plt.imshow(Z-Z2, interpolation='bilinear', cmap=plt.cm.RdYlGn,
origin='lower', extent=[0, 1, 0, 1],
clip_path=patch, clip_on=True)
im.set_clip_path(patch)
ax.set_xlim((0,1))
ax.set_ylim((0,1))
plt.show()
为了回应 Stücke 的评论,这里有一个任意封闭几何图形的 2 色图案旋转(以度为单位)的示例:
MWE
这是一个具有 2 种颜色图案的几何图形逆时针 (ccw) 旋转 10 度的示例。
def create_gradient_rectangle():
"""Creates a gradient in arbitrary direction in the shape of a
rectangle."""
fig = plt.figure()
ax = fig.add_subplot(111, aspect="equal")
path = Path([[1, 1], [3, 1], [3, 5], [1, 6], [1, 1]])
patch = PathPatch(path, facecolor="none")
ax.add_patch(patch)
# Create a grid that specifies the grid pattern from 0 to 1 for
# red to blue. Resolution = 50 pixels
resolution = 400
arr = np.zeros([resolution, resolution])
for row in range(resolution):
for col in range(resolution):
arr[row][col] = row / resolution
# TODO: verify the entries start at 0 for the first rows
# Ensure the matrix can be plotted at once.
np.set_printoptions(threshold=np.inf)
np.set_printoptions(linewidth=2000) # default = 75
# Rotate the colour gradient matrix.
angle_ccw_deg = -10 # degrees
arr = rotate(arr, angle=angle_ccw_deg)
if angle_ccw_deg > 90 or angle_ccw_deg < -90:
raise Exception(
"Rotation error too large, swap the colour pattern instead please."
)
# Trim the rotated matrix to remove blank triangles that are generated.
colour_resolution = 4 # 10^5=10.000 different colours.
rounded_flipped_arr = np.flip(np.around(arr, colour_resolution), axis=1)
arr = trim_rotated_square(rounded_flipped_arr, resolution, angle_ccw_deg)
im = plt.imshow(
arr,
interpolation="bilinear",
origin="lower",
cmap=plt.cm.RdYlGn,
extent=[1, 3, 1, 6],
clip_path=patch,
clip_on=True,
)
im.set_clip_path(patch)
ax.set_xlim((0, 10))
ax.set_ylim((0, 10))
plt.show()
plt.cla()
plt.clf()
plt.close()
def trim_rotated_square(arr, resolution, angle_ccw_deg):
"""Removes the right and left sides of the colour gradient matrix because
it contains triangles on which the pattern is not extended due to the
rotation.
:param arr: The original rotated and rounded array.
:param resolution: The resolution of the colour gradient pattern/original
unrotated matrix size.
:param angle_ccw_deg: The angle at which the pattern is rotated.
"""
# Assumes the rotated matrix is a square matrix.
width = arr.shape[0]
# If the rotation is to the ccw, then the top right triangle will move up
# into the edge of the larger matrix that encapsulates the rotated matrix.
if angle_ccw_deg < 0:
# Get the most right column on which the pattern is uninterrupted.
max_col = get_max_col(arr, resolution)
# Get the most left column on which the pattern is uninterrupted.
min_col = width - max_col
# If the rotation is to the cw, then the top left triangle will move up
# into the edge of the larger matrix that encapsulates the rotated matrix.
elif angle_ccw_deg > 0:
# Get the most left column on which the pattern is uninterrupted.
min_col = get_max_col(arr, resolution)
# Get the most right column on which the pattern is uninterrupted.
max_col = width - min_col
cut = arr[:, min_col:max_col]
return cut
def get_max_col(arr, resolution):
"""Returns the maximum column number for which the rotated matrix shows an
uninterrupted pattern.
:param arr: The original rotated and rounded array.
:param resolution: The resolution of the colour gradient pattern/original
unrotated matrix size.
"""
# Loop through the rows from top to bottom until the rotated left or right
# edge is encountered.
for row in range(resolution):
# Scan the columns horizontally until an edge is encountered. Assumes
# the matrix stars with zeros on top for the first colour pattern.
for col in range(resolution):
# Since the matrix is rounded to some digits, the first 0.000x will
# be rounded down to 0, and when the colour value becomes larger,
# it will exceed 0, this indicates the first top edge is found.
# Print the arr to see how this happens.
if arr[row][col] > 0:
# Return the column for which the rotated edge is found.
print(f"row={row},col={col},arr[row][col]={arr[row][col]}")
return col
raise Exception("Did not find rotated corner.")
它产生:
逆时针旋转 -10 度会产生:
效率低下
相当in-efficient,因为它首先将梯度模式四舍五入到一定数量的变量,然后通过将方阵放入更大的方阵来旋转方阵,然后我开始循环遍历更大的旋转矩阵找到第一个顶部边缘位置。然后我再次 trim 较大矩阵的边,以确保返回一个矩阵,其中模式完全传播,而不是缺少三角形。
推荐
我还没有尝试过多色图案。目前只支持单行颜色模式。但是,如果使用正弦曲线、旋转角度和原始矩阵的长度来计算旋转的截止位置,则无论颜色模式如何,都可以进行截止。
我必须使用 mathplotlib 在 Python 中绘制一个三角形。
This 最终应该是这样的:
我的 objective 是,一旦绘制了三角形,就在其上绘制一些点。
目前我可以很好地绘制三角形:
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal')
ax.add_patch(Polygon([[0,0],[0,1],[1,0]], closed=True,fill=True))
ax.set_xlim((0,1))
ax.set_ylim((0,1))
plt.show()
但我只能用纯色填充。如何添加如图所示的渐变?
有人能帮帮我吗?
有一个 example on the matplotlib page 展示了如何使用图像的剪辑路径。
根据您的情况进行调整会得到:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.path import Path
from matplotlib.patches import PathPatch
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal')
path = Path([[0,0],[0,1],[1,0],[0,0]])
patch = PathPatch(path, facecolor='none')
ax.add_patch(patch)
Z, Z2 = np.meshgrid(np.linspace(0,1), np.linspace(0,1))
im = plt.imshow(Z-Z2, interpolation='bilinear', cmap=plt.cm.RdYlGn,
origin='lower', extent=[0, 1, 0, 1],
clip_path=patch, clip_on=True)
im.set_clip_path(patch)
ax.set_xlim((0,1))
ax.set_ylim((0,1))
plt.show()
为了回应 Stücke 的评论,这里有一个任意封闭几何图形的 2 色图案旋转(以度为单位)的示例:
MWE
这是一个具有 2 种颜色图案的几何图形逆时针 (ccw) 旋转 10 度的示例。
def create_gradient_rectangle():
"""Creates a gradient in arbitrary direction in the shape of a
rectangle."""
fig = plt.figure()
ax = fig.add_subplot(111, aspect="equal")
path = Path([[1, 1], [3, 1], [3, 5], [1, 6], [1, 1]])
patch = PathPatch(path, facecolor="none")
ax.add_patch(patch)
# Create a grid that specifies the grid pattern from 0 to 1 for
# red to blue. Resolution = 50 pixels
resolution = 400
arr = np.zeros([resolution, resolution])
for row in range(resolution):
for col in range(resolution):
arr[row][col] = row / resolution
# TODO: verify the entries start at 0 for the first rows
# Ensure the matrix can be plotted at once.
np.set_printoptions(threshold=np.inf)
np.set_printoptions(linewidth=2000) # default = 75
# Rotate the colour gradient matrix.
angle_ccw_deg = -10 # degrees
arr = rotate(arr, angle=angle_ccw_deg)
if angle_ccw_deg > 90 or angle_ccw_deg < -90:
raise Exception(
"Rotation error too large, swap the colour pattern instead please."
)
# Trim the rotated matrix to remove blank triangles that are generated.
colour_resolution = 4 # 10^5=10.000 different colours.
rounded_flipped_arr = np.flip(np.around(arr, colour_resolution), axis=1)
arr = trim_rotated_square(rounded_flipped_arr, resolution, angle_ccw_deg)
im = plt.imshow(
arr,
interpolation="bilinear",
origin="lower",
cmap=plt.cm.RdYlGn,
extent=[1, 3, 1, 6],
clip_path=patch,
clip_on=True,
)
im.set_clip_path(patch)
ax.set_xlim((0, 10))
ax.set_ylim((0, 10))
plt.show()
plt.cla()
plt.clf()
plt.close()
def trim_rotated_square(arr, resolution, angle_ccw_deg):
"""Removes the right and left sides of the colour gradient matrix because
it contains triangles on which the pattern is not extended due to the
rotation.
:param arr: The original rotated and rounded array.
:param resolution: The resolution of the colour gradient pattern/original
unrotated matrix size.
:param angle_ccw_deg: The angle at which the pattern is rotated.
"""
# Assumes the rotated matrix is a square matrix.
width = arr.shape[0]
# If the rotation is to the ccw, then the top right triangle will move up
# into the edge of the larger matrix that encapsulates the rotated matrix.
if angle_ccw_deg < 0:
# Get the most right column on which the pattern is uninterrupted.
max_col = get_max_col(arr, resolution)
# Get the most left column on which the pattern is uninterrupted.
min_col = width - max_col
# If the rotation is to the cw, then the top left triangle will move up
# into the edge of the larger matrix that encapsulates the rotated matrix.
elif angle_ccw_deg > 0:
# Get the most left column on which the pattern is uninterrupted.
min_col = get_max_col(arr, resolution)
# Get the most right column on which the pattern is uninterrupted.
max_col = width - min_col
cut = arr[:, min_col:max_col]
return cut
def get_max_col(arr, resolution):
"""Returns the maximum column number for which the rotated matrix shows an
uninterrupted pattern.
:param arr: The original rotated and rounded array.
:param resolution: The resolution of the colour gradient pattern/original
unrotated matrix size.
"""
# Loop through the rows from top to bottom until the rotated left or right
# edge is encountered.
for row in range(resolution):
# Scan the columns horizontally until an edge is encountered. Assumes
# the matrix stars with zeros on top for the first colour pattern.
for col in range(resolution):
# Since the matrix is rounded to some digits, the first 0.000x will
# be rounded down to 0, and when the colour value becomes larger,
# it will exceed 0, this indicates the first top edge is found.
# Print the arr to see how this happens.
if arr[row][col] > 0:
# Return the column for which the rotated edge is found.
print(f"row={row},col={col},arr[row][col]={arr[row][col]}")
return col
raise Exception("Did not find rotated corner.")
它产生:
逆时针旋转 -10 度会产生:
效率低下
相当in-efficient,因为它首先将梯度模式四舍五入到一定数量的变量,然后通过将方阵放入更大的方阵来旋转方阵,然后我开始循环遍历更大的旋转矩阵找到第一个顶部边缘位置。然后我再次 trim 较大矩阵的边,以确保返回一个矩阵,其中模式完全传播,而不是缺少三角形。
推荐
我还没有尝试过多色图案。目前只支持单行颜色模式。但是,如果使用正弦曲线、旋转角度和原始矩阵的长度来计算旋转的截止位置,则无论颜色模式如何,都可以进行截止。