viewof result = {
const svg = d3
.create("svg")
.attr("viewBox", [0, 0, width, 500])
.style("overflow", "visible")
.property("value", subject);
const subjectPath = svg
.append("path")
.attr("fill", "blue")
.attr("fill-opacity", 0.1)
.attr("stroke", "blue");
const clipPath = svg
.append("path")
.attr("fill", "red")
.attr("fill-opacity", 0.1)
.attr("stroke", "red");
const clippedPath = svg
.append("path")
.attr("fill", "lightgray")
.attr("stroke", "black")
.attr("stroke-width", 1.5);
const point = svg
.append("g")
.attr("cursor", "move")
.attr("pointer-events", "all")
.attr("stroke", "transparent")
.attr("stroke-width", 30)
.selectAll("circle")
.data(subject.concat(mask))
.join("circle")
.attr("fill", (_, i) => (i < subject.length ? "blue" : "red"))
.attr("r", 4)
.call(
d3
.drag()
.subject((event, [x, y]) => ({ x, y }))
.on("drag", dragged)
);
update();
function dragged(event, d) {
d[0] = event.x;
d[1] = event.y;
update();
svg.dispatch("input");
}
function update() {
const clipped = polygonClip(mask, subject);
point.attr("cx", (d) => d[0]).attr("cy", (d) => d[1]);
subjectPath.attr("d", `M${subject.join("L")}Z`);
clipPath.attr("d", `M${mask.join("L")}Z`);
clippedPath.attr("d", clipped && `M${clipped.join("L")}Z`);
}
return svg.node();
}