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 较大矩阵的边,以确保返回一个矩阵,其中模式完全传播,而不是缺少三角形。

推荐

我还没有尝试过多色图案。目前只支持单行颜色模式。但是,如果使用正弦曲线、旋转角度和原始矩阵的长度来计算旋转的截止位置,则无论颜色模式如何,都可以进行截止。