Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3bf50a4
Add a 3D surface figure beside each contour figure
claude Nov 3, 2026
4d20393
Move 3D figure alt text into chunk options and reword unlinked terms
claude Nov 3, 2026
b40360a
Make 3D figure R code pass the lab linters
claude Nov 3, 2026
b483b15
Break caption lines at clauses and keep the Rosenbrock curve on the p…
claude Nov 3, 2026
0eea9fd
Split the convex-hessian surface caption's comma splice
claude Nov 3, 2026
f85b0a1
Run the check-contour-3d check on PRs
claude Nov 4, 2026
f8bf55d
Merge remote-tracking branch 'origin/claude/project-thread-qgkfho' in…
claude Nov 4, 2026
f23f8d0
Add a 3D bowl figure beside the contour-line example
claude Nov 4, 2026
59c677f
Merge the old branch history (already merged as #619)
claude Nov 4, 2026
d87d622
Merge the old branch history (already merged as #619)
claude Nov 4, 2026
cdf6ae4
Leave the tilt-a-plane figure untouched
claude Nov 4, 2026
3787920
Rename the contour-line 3D figure subfile to the _fig- prefix
claude Nov 4, 2026
3cd35d5
Name intermediate results in the contour-line 3D figure
claude Nov 4, 2026
6dd1481
Add a 3D view beside the gradient explorer
claude Nov 4, 2026
fc66695
Link the terms in the gradient-explorer 3D caption
claude Nov 4, 2026
2f72561
Merge the old branch history (already squash-merged as #622)
claude Nov 4, 2026
ac5a1e5
Break the gradient-explorer 3D caption at the clause
claude Nov 4, 2026
46ebc3f
Exclude the gradient-explorer 3D figure from R linting
claude Nov 4, 2026
bf8b026
Wrap the 3D figure in a content-visible div instead of putting the cl…
claude Nov 4, 2026
65d040e
Link the explorer from the 3D caption instead of cross-referencing it
claude Nov 4, 2026
87a4ba6
Add the non-HTML fallback note after the 3D figure, as the sibling fi…
claude Nov 4, 2026
278d8e5
Hold the 3D figure and its fallback note in one top-level div
claude Nov 4, 2026
ba6cdb0
Bisect the CI build crash: give the 3D figure a plain id instead of a…
claude Nov 4, 2026
56a9d28
Merge main into the gradient explorer 3D branch
claude Nov 4, 2026
53d8c84
Control build: disable the gradient explorer 3D include
claude Nov 4, 2026
a2115f8
Revert "Control build: disable the gradient explorer 3D include"
claude Nov 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .lintr.R
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ exclusions <- list(
),
`_subfiles/linear-algebra/_fig-tilt-a-plane.qmd` = Inf,
`_subfiles/vector-calc/_fig-linear-approx.qmd` = Inf,
`_subfiles/vector-calc/_fig-gradient-explorer.qmd` = Inf
`_subfiles/vector-calc/_fig-gradient-explorer.qmd` = Inf,
`_subfiles/vector-calc/_fig-gradient-explorer-3d.qmd` = Inf
)
4 changes: 4 additions & 0 deletions _subfiles/_sec_vector_calc.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@

{{< slidebreak >}}

{{< include mds/_subfiles/vector-calc/_fig-gradient-explorer-3d.qmd >}}

{{< slidebreak >}}

{{< include mds/_subfiles/vector-calc/_exr-partials-by-hand.qmd >}}

{{< include mds/_subfiles/vector-calc/_sol-partials-by-hand.qmd >}}
Expand Down
85 changes: 85 additions & 0 deletions _subfiles/vector-calc/_fig-gradient-explorer-3d.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
::: {.explorer-3d-pair}

::: {.content-visible when-format="html"}

::: {#gradient-explorer-3d}

```{ojs}
viewof gradTurn = Inputs.range([0, 360], {value: 30, step: 5, label: "turn the view"})
```

```{ojs}
// The graph z = f(w1, w2) of the function chosen in @fig-gradient-explorer, drawn in 3-D:
// turn the square about the vertical axis, then look down on it from 30 degrees.
{
const n = 41, lim = 3, w = 380, h = 320, s = 36, elev = 30 * Math.PI / 180;
const turn = gradTurn * Math.PI / 180;
const grid = d3.range(n).map((i) => d3.range(n).map((j) => {
const a = -lim + 2 * lim * i / (n - 1);
const b = -lim + 2 * lim * j / (n - 1);
return gradF(a, b);
}));
const top = d3.max(grid.flat().map(Math.abs)) || 1;
const sz = 2.5 / top;
const at = (a, b, z) => {
const u = a * Math.cos(turn) - b * Math.sin(turn);
const v = a * Math.sin(turn) + b * Math.cos(turn);
return [w / 2 + s * u, h / 2 - s * Math.sin(elev) * v - s * Math.cos(elev) * z * sz];
};
const path = (pts) => d3.line()(pts.map(([a, b, z]) => at(a, b, z)));
const svg = d3.create("svg").attr("width", w).attr("height", h)
.attr("role", "img")
.attr("aria-label", "The graph of the chosen function drawn in three dimensions over a square of inputs, " +
"with the chosen point lifted onto the surface, " +
"the level curve through it at constant height, " +
"a red arrow along the gradient and a blue arrow in the chosen step direction, " +
"each rising at its rate of change.");
const ticks = d3.range(-lim, lim + 0.01, 1);
const fill = "none";
for (const t of ticks) {
svg.append("path").attr("d", path(d3.range(n).map((j) => [t, -lim + 2 * lim * j / (n - 1), gradF(t, -lim + 2 * lim * j / (n - 1))])))
.attr("stroke", "#1f77b4").attr("stroke-opacity", 0.45).attr("fill", fill);
svg.append("path").attr("d", path(d3.range(n).map((i) => [-lim + 2 * lim * i / (n - 1), t, gradF(-lim + 2 * lim * i / (n - 1), t)])))
.attr("stroke", "#1f77b4").attr("stroke-opacity", 0.45).attr("fill", fill);
}
// The level curve through the chosen point, at the point's height.
const fv = gradF(gradW1, gradW2);
const flat = d3.range(n * n).map((k) => grid[k % n][Math.floor(k / n)]);
const level = d3.contours().size([n, n]).thresholds([fv])(flat)[0];
for (const polygon of level.coordinates) {
for (const ring of polygon) {
const pts = ring.map(([i, j]) => [-lim + 2 * lim * i / (n - 1), -lim + 2 * lim * j / (n - 1), fv]);
svg.append("path").attr("d", path(pts)).attr("stroke", "#222").attr("stroke-width", 2).attr("fill", fill);
}
}
// Arrows from the point: one unit step in the plane, rising by the rate of change along it.
const u = [Math.cos(gradTheta * Math.PI / 180), Math.sin(gradTheta * Math.PI / 180)];
const g = gradLen > 1e-9 ? gradG.map((v) => v / gradLen) : [0, 0];
const arrow = (dir, rise, color) => {
svg.append("path")
.attr("d", path([[gradW1, gradW2, fv], [gradW1 + dir[0], gradW2 + dir[1], fv + rise]]))
.attr("stroke", color).attr("stroke-width", 3).attr("fill", fill);
const [x, y] = at(gradW1 + dir[0], gradW2 + dir[1], fv + rise);
svg.append("circle").attr("cx", x).attr("cy", y).attr("r", 3.5).attr("fill", color);
};
arrow(u, gradRate(gradTheta), "#1f77b4");
arrow(g, gradLen, "#d62728");
const [px, py] = at(gradW1, gradW2, fv);
svg.append("circle").attr("cx", px).attr("cy", py).attr("r", 5).attr("fill", "#222");
return svg.node();
}
```

The [graph](functions.qmd#def-graph) of the [function](functions.qmd#def-function) in the [gradient explorer](#fig-gradient-explorer), with the same point, [level curve](#def-level-set) and arrows.
Each arrow takes one unit step in the plane,
and its height gain is the amount $f$ changes along that step.

:::

:::

::: {.content-visible unless-format="html"}
The course website's version of this page has an interactive 3D plot here.

Check warning on line 82 in _subfiles/vector-calc/_fig-gradient-explorer-3d.qmd

View workflow job for this annotation

GitHub Actions / check / check-definite-antecedents

This div opens with the definite phrase 'The course ...' and no link, so no earlier text in the div can be its antecedent. Fix: name the referent in this div, or link the phrase to where it is introduced, for example [the data](#exm-data)
:::

:::
Loading