chart = {
const svg = d3.select(DOM.svg(width, height));
var x = math.zeros(2);
draw_axes(svg, scales);
svg.selectAll("g")
.data(["links", "points", "weights_select", "weights", "weights_text", "x_input"], (d) => d).enter()
.append("g")
.attr("id", (d) => d);
var u = math.zeros(2, 2),
w = math.zeros(2, 2),
all_data = generate_data(u, w, x);
svg.select("#links")
.selectAll("path")
.data(all_data["links"]).enter()
.append("path")
.attrs({
"d": (d) => "M" + scales.h0(d["pre"]._data[0]) + "," + scales.h1(d["pre"]._data[1]) + "L" + scales.h0(d["post"]._data[0]) + "," + scales.h1(d["post"]._data[1]),
"stroke": "#a4a4a4",
"stroke-width": 0.8
});
svg.select("#points")
.selectAll("circle")
.data(all_data["points"]).enter()
.append("circle")
.attrs({
"cx": (d) => scales.h0(d.h._data[0]),
"cy": (d) => scales.h1(d.h._data[1]),
"r": 3,
"fill-opacity": .6,
"fill": (d) => scales.type(d.type)
});
svg.select("#x_input")
.selectAll("circle")
.data([x]).enter()
.append("circle")
.attrs({
"cx": (d) => scales.x0(d._data[0]),
"cy": (d) => scales.x1(d._data[1]),
"r": 10,
"fill": "black"
})
.call(d3.drag().on("drag", update_x))
svg.select("#weights_select")
.selectAll("circle")
.data(rows(w, u)).enter()
.append("circle")
.attrs({
"cx": (d) => scales.wselect_x(d.value._data[0][0]) + scales.wtype(d.weight),
"cy": (d) => scales.wselect_y(d.value._data[0][1]),
"r": 7,
"fill": (d) => scales.weight_fill(d.weight)
})
.call(d3.drag().on("drag", (d) => update_weights(d)));
svg.select("#weights")
.selectAll("rect")
.data(entries(w, u)).enter()
.append("rect")
.attrs({
"x": (d) => scales.wtype(d.weight) + scales.col_offset(d.col),
"y": (d) => scales.row_offset(d.row),
"width": scales.col_offset(1) - scales.col_offset(0),
"height": scales.row_offset(1) - scales.row_offset(0),
"fill": (d) => scales.weight_fill(d.weight),
"fill-opacity": (d) => .5 * scales.weight_opacity(Math.abs(d.value)),
});
svg.select("#weights_text")
.selectAll("text")
.data(entries(w, u)).enter()
.append("text")
.attrs({
"x": (d) => scales.wtype(d.weight) + scales.col_offset(d.col),
"y": (d) => scales.row_offset(d.row),
"fill": (d) => scales.weight_fill(d.weight),
"fill-opacity": (d) => scales.weight_fill(Math.abs(d.value)),
"font-size": 15,
"alignment-baseline": "hanging"
})
.text((d) => Number(d.value).toFixed(3))
function update_x() {
if (d3.event.x > width || d3.event.x < scales.x0.range()[0] + 10) {
return;
}
if (d3.event.y > scales.x1.range()[0] - 10 || d3.event.y < 0) {
return;
}
x = math.matrix(
[scales.x0.invert(d3.event.x),
scales.x1.invert(d3.event.y)]);
all_data = generate_data(u, w, x);
update_figure(svg, all_data["points"], all_data["links"], x, w, u)
}
function update_weights(d) {
var cur = [scales.wselect_x.invert(d3.event.x - scales.wtype(d.weight)),
scales.wselect_y.invert(d3.event.y)];
if (cur[0] < scales.wselect_x.domain()[0] || cur[0] > scales.wselect_x.domain()[1]) {
return;
}
if (cur[1] < scales.wselect_y.domain()[0] || cur[1] > scales.wselect_y.domain()[1]) {
return;
}
if (d.weight == "w") {
w.subset(math.index(d.row, [0, 1]), cur);
} else {
u.subset(math.index(d.row, [0, 1]), cur);
}
all_data = generate_data(u, w, x);
update_figure(svg, all_data["points"], all_data["links"], x, w, u)
}
return svg.node()
}