function make_gradient_ascent_pic() {
const svg = d3
.create("svg")
.attr("class", "contour_plot")
.attr("viewBox", [0, 0, svg_width, svg_height])
.style("max-width", `${svg_width}px`);
svg
.append("g")
.attr("stroke", "#000")
.attr("stroke-opacity", 0.8)
.selectAll("path")
.data(contours)
.join("path")
.attr("fill", (d) => color(-d.value))
.attr("d", d3.geoPath(null));
let x_scale = d3.scaleLinear().domain([grid.a, grid.b]).range([0, svg_width]);
let y_scale = d3
.scaleLinear()
.domain([grid.c, grid.d])
.range([svg_height, 0]);
let pts_to_path = d3
.line()
.x((d) => x_scale(d[0]))
.y((d) => y_scale(d[1]));
let ascent = svg.append("g");
svg
.on("pointerenter", function (evt) {
let [w, h] = d3.pointer(evt);
let x = x_scale.invert(w);
let y = y_scale.invert(h);
let path = compute_path([x, y]);
ascent
.selectAll("path")
.data([path])
.enter()
.append("path")
.attr("d", pts_to_path)
.attr("stroke", "black")
.attr("stroke-width", 4)
.attr("fill", "none");
ascent
.append("circle")
.attr("id", "start")
.attr("cx", x_scale(path[0][0]))
.attr("cy", y_scale(path[0][1]))
.attr("r", 5)
.attr("fill", "#5f5");
ascent
.append("circle")
.attr("id", "fin")
.attr("cx", x_scale(path[path.length - 1][0]))
.attr("cy", y_scale(path[path.length - 1][1]))
.attr("r", 5)
.attr("fill", "#f55");
})
.on("touchstart", (e) => e.preventDefault())
.on("pointermove", function (evt) {
let [w, h] = d3.pointer(evt);
let x = x_scale.invert(w);
let y = y_scale.invert(h);
let path = compute_path([x, y]);
ascent.select("path").data([path]).join("path").attr("d", pts_to_path);
ascent
.selectAll("circle#start")
.data([path])
.join("circle")
.attr("cx", (d) => x_scale(d[0][0]))
.attr("cy", (d) => y_scale(d[0][1]));
ascent
.selectAll("circle#fin")
.data([path])
.join("circle")
.attr("cx", (d) => x_scale(d[d.length - 1][0]))
.attr("cy", (d) => y_scale(d[d.length - 1][1]));
})
.on("pointerleave", function () {
ascent.selectAll("*").remove();
});
return svg.node();
}