chart = {
let selected = mutable points[0];
const svg = d3.select(DOM.svg(width, height))
.attr("tabindex", 1)
.attr("pointer-events", "all")
.call(d3.drag()
.subject(dragsubject)
.on("start", dragstarted)
.on("drag", dragged));
svg.append("style").text(`
svg[tabindex] {
display: block;
border: solid 1px #ddd;
}
svg[tabindex]:focus {
outline: none;
border: solid 1px lightblue;
}
`);
svg.append("rect")
.attr("fill", "none")
.attr("width", width)
.attr("height", height);
svg.append("path")
.datum(mutable points)
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-width", 1.5)
.call(update);
d3.select(window)
.on("keydown", keydown);
function update() {
svg.select("path").attr("d", d => {
let coords = chaikin(d, cuts);
if (type === 'closed') {
const start = [coords[0]];
coords = coords.concat(start);
} else if (type === 'open' && cuts > 0) {
coords = [d[0]].concat(coords.slice(0,-trim(cuts)),[d[d.length-1]]);
}
return `M ${coords.join(" L ")}`
});
const circle = svg.selectAll("g")
.data(mutable points, d => d)
circle.enter().append("g")
.call(g => g.append("circle")
.attr("r", 30)
.attr("fill", "none"))
.call(g => g.append("circle")
.attr("r", 0)
.attr("stroke", "black")
.attr("stroke-width", 1.5)
.transition()
.duration(750)
.ease(d3.easeElastic)
.attr("r", 5))
.merge(circle)
.attr("transform", d => `translate(${d})`)
.select("circle:last-child")
.attr("fill", d => d === selected ? "lightblue" : "black");
circle.exit().remove();
}
function dragsubject() {
let subject = d3.event.sourceEvent.target.__data__;
if (!subject) {
mutable points.push(subject = [d3.event.x, d3.event.y]);
mutable points = mutable points;
update();
}
return subject;
}
function dragstarted() {
selected = d3.event.subject;
update();
}
function dragged() {
d3.event.subject[0] = Math.max(0, Math.min(width, d3.event.x));
d3.event.subject[1] = Math.max(0, Math.min(height, d3.event.y));
mutable points = mutable points;
update();
}
function keydown() {
if (!selected) return;
switch (d3.event.key) {
case "Backspace":
case "Delete": {
const i = mutable points.indexOf(selected);
mutable points.splice(i, 1);
mutable points = mutable points;
selected = mutable points.length ? mutable points[i > 0 ? i - 1 : 0] : null;
update();
break;
}
}
}
return svg.node();
}