2025, Dec 31 15:03
Как окрасить 3D‑треугольную сетку в matplotlib по значениям на вершинах
Пошагово показываем, как окрасить 3D‑треугольную сетку в matplotlib по скаляру на вершинах: Poly3DCollection, colormap, нормировка и шкала, пример кода.
Раскрашивание 3D‑треугольной сетки по скалярным значениям на вершинах кажется простой задачей, но распространённые упрощения при построении графиков делают не совсем это. plot_trisurf ориентирован на затенение по z, а tripcolor работает в 2D. Если цель — полноценная трёхмерная поверхность, где цвет грани отражает ваш собственный скаляр, заданный на вершинах, нужно явно построить грани сетки и назначить им цвета, полученные из этих скаляров.
Постановка задачи
Данные включают координаты вершин в 3D (x, y, z), скаляр для каждой вершины и треугольную связность, описывающую, какие вершины образуют каждую грань. Отрисовать одну только геометрию несложно, но недостающий шаг — сопоставить скалярное поле с цветами граней в 3D.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection as Mesh3D
# Положение вершин
xs = np.array([0, 1, 0, 1])
ys = np.array([0, 0, 1, 1])
zs = np.array([0, 0, 0, 1])
vals = np.array([0.1, 0.5, 0.9, 0.6]) # скаляр на вершину
# Связность треугольников по индексам вершин
faces_idx = np.array([
[0, 1, 2],
[1, 2, 3]
])
# Формируем тройки вершин треугольников
poly_pts = [list(zip(xs[t], ys[t], zs[t])) for t in faces_idx]
# Базовый график: только геометрия, без раскраски по скалярам
canvas = plt.figure()
axes3d = canvas.add_subplot(111, projection='3d')
surf = Mesh3D(poly_pts, facecolors='lightgray', edgecolors='k', linewidths=0.5)
axes3d.add_collection3d(surf)
axes3d.auto_scale_xyz(xs, ys, zs)
plt.show()Этот код рисует сетку, но грани не окрашены по скалярному полю на вершинах. Именно это нам и предстоит исправить.
Чего на самом деле не хватает
Одна лишь геометрическая часть не знает, как превратить значения на вершинах в цвета граней. tripcolor умеет раскрашивать треугольники, но только в 2D‑проекции, тогда как plot_trisurf привязан к затенению по z. Чтобы окрасить 3D‑поверхность вашим скаляром, нужно преобразовать скаляры на вершинах в цвет для каждой грани и передать эти цвета в трёхмерную коллекцию полигонов.
Простой и эффективный вариант — вычислить репрезентативное значение для грани, например среднее из трёх скаляров вершин, нормировать эти значения, пропустить через цветовую карту и использовать получившиеся RGBA‑цвета для граней.
Решение: формируем цвета граней из скаляров на вершинах
Следующий код строит треугольники, вычисляет средний скаляр на грань, нормирует его, преобразует в RGBA через цветовую карту и выводит окрашенную поверхность. Для наглядности добавляется и цветовая шкала.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection as Mesh3D
from matplotlib import cm as cms
# Положение вершин
xs = np.array([0, 1, 0, 1])
ys = np.array([0, 0, 1, 1])
zs = np.array([0, 0, 0, 1])
vals = np.array([0.1, 0.5, 0.9, 0.6]) # скаляр на вершину
# Связность треугольников по индексам вершин
faces_idx = np.array([
[0, 1, 2],
[1, 2, 3]
])
# Формируем тройки вершин треугольников
poly_pts = [list(zip(xs[t], ys[t], zs[t])) for t in faces_idx]
# Агрегируем скаляр для грани (среднее трёх значений на вершинах)
face_vals = np.mean(vals[faces_idx], axis=1)
# Нормируем и отображаем в RGBA через цветовую карту
scaler = plt.Normalize(vals.min(), vals.max())
face_rgba = cms.viridis(scaler(face_vals))
# Построение графика
canvas = plt.figure()
axes3d = canvas.add_subplot(111, projection='3d')
surf = Mesh3D(poly_pts, facecolors=face_rgba, edgecolors='k', linewidths=0.5)
axes3d.add_collection3d(surf)
axes3d.auto_scale_xyz(xs, ys, zs)
plt.colorbar(cms.ScalarMappable(norm=scaler, cmap='viridis'), ax=axes3d, label='Scalar value')
plt.show()Здесь цвет грани — это результат применения цветовой карты к скаляру этой грани, а сам скаляр грани определён как среднее трёх скаляров вершин. Отображение задаётся явно и полностью под вашим контролем.
Почему этот подход важен
Явное сопоставление цветов гарантирует, что визуальные кодировки соответствуют смыслу данных. Вместо того чтобы позволять библиотеке определять цвета по z или по 2D‑проекции, вы точно задаёте, как получить цвет грани из значений на вершинах, как их нормировать и отображать. Это убирает неоднозначность и делает 3D‑визуализацию поверхности верной тому скалярному полю, которое для вас действительно важно.
Итоги
Постройте полигоны граней по индексам треугольников, сведите скаляры на вершинах к одному значению на грань, нормируйте в пределах диапазона скаляров и примените цветовую карту, чтобы получить RGBA‑цвета граней. С Poly3DCollection это даёт простой и воспроизводимый способ визуализировать 3D‑сетку, чьи грани отражают поле данных, заданное на вершинах.