Skip to content

Commit 7721797

Browse files
committed
Show only the learned isosurface in the 3D README animation
Drop the phase where points accumulate (early meshes looked like crude blobs), learn 6000 points for a smoother surface, and sway the view so the cut-away wedge stays visible.
1 parent 9c4b905 commit 7721797

2 files changed

Lines changed: 29 additions & 37 deletions

File tree

‎docs/_static/readme/learnerND.gif‎

506 KB
Loading

‎docs/make_animations.py‎

Lines changed: 29 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
STATIC = Path(__file__).parent / "_static"
3333
DARK, ACCENT, NEW = "#2c3e6b", "#21918c", "#e4572e"
3434
BLUE, YELLOW = "#3b528b", "#fde725"
35+
NPOINTS_3D = 6000
3536
plt.rcParams.update(
3637
{
3738
"font.family": "DejaVu Sans",
@@ -171,24 +172,19 @@ def draw(fig, i):
171172

172173

173174
def learnerND(target: Path) -> None:
174-
points, values = sampled(
175-
adaptive.LearnerND(shell, bounds=[(-1, 1)] * 3), npoints_goal=2500
176-
)
177-
grow, turn = 45, 60
178-
frames = np.concatenate([counts(30, len(points), grow), np.full(turn, len(points))])
179-
surfaces = _isosurfaces(points, values, np.unique(frames))
175+
learner = adaptive.LearnerND(shell, bounds=[(-1, 1)] * 3)
176+
adaptive.runner.simple(learner, npoints_goal=NPOINTS_3D)
177+
triangles = _isosurface(learner)
178+
centers = triangles.mean(axis=1)
179+
inner = np.linalg.norm(centers, axis=1) < 0.75
180+
# Cut an x > 0, y > 0 wedge out of the outer surface to show the inner one.
181+
keep = inner | ~((centers[:, 0] > 0) & (centers[:, 1] > 0))
182+
triangles, inner = triangles[keep], inner[keep]
183+
base = np.array([to_rgba(BLUE), to_rgba(YELLOW)])[inner.astype(int)]
184+
nframes = 90
180185

181186
def draw(fig, i):
182-
n = frames[i]
183-
# Rock back and forth, so the cut-away wedge stays in view.
184-
azim = 45 + 50 * np.sin(2 * np.pi * max(0, i - grow) / turn)
185-
triangles = surfaces[n]
186-
centers = triangles.mean(axis=1)
187-
inner = np.linalg.norm(centers, axis=1) < 0.75
188-
# Cut an x > 0, y > 0 wedge out of the outer surface to show the inner one.
189-
keep = inner | ~((centers[:, 0] > 0) & (centers[:, 1] > 0))
190-
triangles, inner = triangles[keep], inner[keep]
191-
base = np.array([to_rgba(BLUE), to_rgba(YELLOW)])[inner.astype(int)]
187+
azim = 45 + 55 * np.sin(2 * np.pi * i / nframes) # keeps the cut in view
192188
colors = base * _lambert(triangles, azim - 30, 25 + 20)[:, None]
193189
colors[:, 3] = 1
194190
ax = fig.add_subplot(projection="3d")
@@ -201,10 +197,13 @@ def draw(fig, i):
201197
set_lim(-1, 1)
202198
ax.set_box_aspect((1, 1, 1), zoom=1.25)
203199
ax.set_axis_off()
204-
fig.text(0.5, 0.04, f"{n} points", ha="center", color="#555555", fontsize=12)
200+
fig.text(
201+
0.5, 0.04, f"LearnerND, {learner.npoints} points",
202+
ha="center", color="#555555", fontsize=12,
203+
) # fmt: skip
205204
fig.subplots_adjust(left=0, right=1, top=1, bottom=0)
206205

207-
_render_gif(draw, len(frames), (4.5, 4.5), target, hold=0, dpi=90)
206+
_render_gif(draw, nframes, (4.5, 4.5), target, hold=0, dpi=90)
208207

209208

210209
def _lambert(triangles: np.ndarray, azim: float, elev: float) -> np.ndarray:
@@ -220,25 +219,18 @@ def _lambert(triangles: np.ndarray, azim: float, elev: float) -> np.ndarray:
220219
return 0.45 + 0.55 * np.clip(normals @ light, 0, 1)
221220

222221

223-
def _isosurfaces(points, values, counts_) -> dict[int, np.ndarray]:
224-
"""Triangles of the f = 0.5 isosurface after the first n points, for each n."""
225-
learner = adaptive.LearnerND(shell, bounds=[(-1, 1)] * 3)
226-
surfaces, told = {}, 0
227-
for n in counts_:
228-
for point, value in zip(points[told:n], values[told:n]):
229-
learner.tell(tuple(point), value)
230-
told = n
231-
vertices, faces = learner._get_iso(level=0.5)
232-
triangles = np.array(vertices)[np.array(faces)]
233-
# Wind every triangle with its normal pointing away from the center,
234-
# so the shading is consistent.
235-
normals = np.cross(
236-
triangles[:, 1] - triangles[:, 0], triangles[:, 2] - triangles[:, 0]
237-
)
238-
inward = np.einsum("ij,ij->i", normals, triangles.mean(axis=1)) < 0
239-
triangles[inward] = triangles[inward][:, ::-1]
240-
surfaces[n] = triangles
241-
return surfaces
222+
def _isosurface(learner: adaptive.LearnerND) -> np.ndarray:
223+
"""Triangles of the learner's f = 0.5 isosurface, with outward normals."""
224+
vertices, faces = learner._get_iso(level=0.5)
225+
triangles = np.array(vertices)[np.array(faces)]
226+
# Wind every triangle with its normal pointing away from the center,
227+
# so the shading is consistent.
228+
normals = np.cross(
229+
triangles[:, 1] - triangles[:, 0], triangles[:, 2] - triangles[:, 0]
230+
)
231+
inward = np.einsum("ij,ij->i", normals, triangles.mean(axis=1)) < 0
232+
triangles[inward] = triangles[inward][:, ::-1]
233+
return triangles
242234

243235

244236
def _render_gif(draw, nframes, figsize, target, hold=2.5, dpi=100, fps=15):

0 commit comments

Comments
 (0)