-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy pathplot_surface.py
More file actions
executable file
·149 lines (130 loc) · 5.04 KB
/
plot_surface.py
File metadata and controls
executable file
·149 lines (130 loc) · 5.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#!/usr/bin/env python3
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D # noqa: F401
# ---------- Parameters ----------
# Domain: [-2, 2] x [-2, 2]
x_min, x_max, y_min, y_max = -2.0, 2.0, -2.0, 2.0
n = 300 # surface resolution
qstep = 32 # stride for 3D gradient vectors (even fewer arrows)
qstep2d = 10 # stride for 2D/top-down quiver (even fewer arrows)
elev, azim = 30, -45 # 3D camera for the first three plots
cmap = plt.get_cmap("viridis")
phi_level = 0.03 # contour level
# Arrow scaling:
# We keep vector components in data units so length == |∇φ|.
# If you want globally longer/shorter arrows while preserving "length ∝ |∇φ|",
# tweak these multipliers (1.0 = true magnitude).
arrow_scale_3d = 1.0
arrow_scale_2d = 0.05
# ---------- Define φ(x,y) and ∇φ ----------
def phi(x, y):
r2 = x**2 + y**2
return np.sin(r2) + 0.5*np.cos(2*x - 2)*np.sin(y) + 0.2*np.cos(y + 1)
def grad_phi(x, y):
r2 = x**2 + y**2
dphidx = 2*x*np.cos(r2) - np.sin(2*x - 2)*np.sin(y)
dphidy = 2*y*np.cos(r2) + 0.5*np.cos(2*x - 2)*np.cos(y) - 0.2*np.sin(y + 1)
return dphidx, dphidy
# ---------- Grid ----------
x = np.linspace(x_min, x_max, n)
y = np.linspace(y_min, y_max, n)
X, Y = np.meshgrid(x, y, indexing="xy")
Z = phi(X, Y)
vmin, vmax = np.min(Z), np.max(Z)
Zrange = vmax - vmin
# ---------- Styling helpers ----------
def style_axes_black_3d(ax):
fig = ax.figure
fig.patch.set_facecolor("black")
ax.set_facecolor("black")
ax.tick_params(colors="white", which="both")
for axis in (ax.xaxis, ax.yaxis, ax.zaxis):
axis.label.set_color("white")
ax.grid(False)
# Try to set axis lines to white (version-dependent)
for line in getattr(ax, "w_xaxis", getattr(ax, "xaxis", None)), \
getattr(ax, "w_yaxis", getattr(ax, "yaxis", None)), \
getattr(ax, "w_zaxis", getattr(ax, "zaxis", None)):
try:
line.line.set_color("white")
except Exception:
pass
try:
ax.xaxis.pane.set_facecolor((0,0,0,1))
ax.yaxis.pane.set_facecolor((0,0,0,1))
ax.zaxis.pane.set_facecolor((0,0,0,1))
ax.xaxis.pane.set_edgecolor("white")
ax.yaxis.pane.set_edgecolor("white")
ax.zaxis.pane.set_edgecolor("white")
except Exception:
pass
fig.tight_layout()
def style_axes_black_2d(ax):
fig = ax.figure
fig.patch.set_facecolor("black")
ax.set_facecolor("black")
ax.tick_params(colors="white", which="both")
ax.xaxis.label.set_color("white")
ax.yaxis.label.set_color("white")
for spine in ax.spines.values():
spine.set_color("white")
ax.grid(False)
fig.tight_layout()
def colorbar_on_black(fig, mappable, label="φ(x, y)"):
cb = fig.colorbar(mappable, shrink=0.75, pad=0.05)
cb.outline.set_edgecolor("white")
cb.ax.yaxis.set_tick_params(color="white")
plt.setp(plt.getp(cb.ax.axes, 'yticklabels'), color="white")
cb.set_label(label, color="white")
return cb
# ---------- Figure 1: surface ----------
fig1 = plt.figure(figsize=(8, 6))
ax1 = fig1.add_subplot(111, projection="3d")
style_axes_black_3d(ax1)
surf = ax1.plot_surface(X, Y, Z, rstride=2, cstride=2, cmap=cmap,
vmin=vmin, vmax=vmax, linewidth=0, antialiased=True)
colorbar_on_black(fig1, surf)
ax1.set_xlabel("x"); ax1.set_ylabel("y"); ax1.set_zlabel("φ(x, y)")
ax1.view_init(elev=elev, azim=azim)
fig1.savefig("surface.pdf", dpi=300, facecolor=fig1.get_facecolor(), edgecolor="none")
plt.close(fig1)
# ---------- Figure 4: TOP-DOWN φ(x,y) (2D) with red contour on top ----------
fig4 = plt.figure(figsize=(7, 7))
ax4 = fig4.add_subplot(111)
style_axes_black_2d(ax4)
im = ax4.pcolormesh(X, Y, Z, shading="auto", cmap=cmap, vmin=vmin, vmax=vmax)
colorbar_on_black(fig4, im, label="φ(x, y)")
# Red level-set overlaid with high zorder so it's not hidden
ax4.contour(X, Y, Z, levels=[phi_level], colors="red", linewidths=2.2, zorder=5)
ax4.set_xlabel("x")
ax4.set_ylabel("y")
ax4.set_aspect("equal", "box")
fig4.savefig("surface_topdown.pdf", dpi=300, facecolor=fig4.get_facecolor(), edgecolor="none")
plt.close(fig4)
# ---------- Figure 5: TOP-DOWN gradient field (2D; fewer points; length = |∇φ|) ----------
fig5 = plt.figure(figsize=(7, 7))
ax5 = fig5.add_subplot(111)
style_axes_black_2d(ax5)
# Background: same φ(x,y) map for context
im2 = ax5.pcolormesh(X, Y, Z, shading="auto", cmap=cmap, vmin=vmin, vmax=vmax)
colorbar_on_black(fig5, im2, label="φ(x, y)")
Xs2 = X[::qstep2d, ::qstep2d]
Ys2 = Y[::qstep2d, ::qstep2d]
Ux2, Uy2 = grad_phi(Xs2, Ys2)
# Keep 2D arrows in data units: scale=1.0, scale_units='xy' -> length equals ||∇φ|| * arrow_scale_2d
ax5.quiver(
Xs2, Ys2,
arrow_scale_2d * Ux2, arrow_scale_2d * Uy2,
color="red",
angles="xy",
scale_units="xy",
scale=1.0,
width=0.004
)
ax5.set_xlabel("x")
ax5.set_ylabel("y")
ax5.set_aspect("equal", "box")
fig5.savefig("surface_topdown_gradient.pdf", dpi=300, facecolor=fig5.get_facecolor(), edgecolor="none")
plt.close(fig5)