{
const surface = d3.create('svg')
.attr('width', img.width)
.attr('height', img.height)
.style('border', '1px solid black')
.style('touch-action', 'none')
.style('transform-origin', '0px 0px 0px')
const img_plane = d3.create('svg')
.attr('width', img.width)
.attr('height', img.height)
.style('border', '1px solid black')
.style('touch-action', 'none')
.style('transform-origin', '0px 0px 0px')
const image = surface.append("image")
.attr("xlink:href", dataurl)
.attr("width", img.width)
.attr("height", img.height);
const make_draggable = ({element, update_position}) => {
let start = null
let i = 0
element.call(d3.drag()
.on('start', () => null)
.on('drag', () => {
const curr = {x: d3.event.x, y: d3.event.y}
start = i > 1 ? start : curr
const dx = curr.x - start.x
const dy = curr.y - start.y
mutable log = {dy}
update_position({dx, dy})
i++
})
.on('end', () => (start = null, i = 0)))
}
const add_circle = ({x, y}) => surface.append('circle')
.attr('r', 20)
.attr('cx', x)
.attr('cy', y)
.attr('stroke', 'gray')
.attr('stroke-width', 2.5)
.attr('fill', 'transparent')
surface.append('path').datum(state.centers).attr('stroke', 'black').attr('fill', 'none').attr('d', d3.line().x(d => d.x).y(d => d.y).curve(d3.curveLinearClosed))
state.centers.map((c, i) => make_draggable({
element: add_circle(c),
update_position: ({dx, dy}) => {
mutable state =
produce(state, state => {state.centers[i] = {x: c.x + dx, y: c.y + dy}})
}
}))
const sourcePoints = [[0, 0], [img.width, 0], [img.width, img.height], [0, img.height]]
const targetPoints = state.centers.map(({x, y}) => [x, y])
const matrix = get_matrix(sourcePoints, targetPoints)
img_plane.style('transform', "matrix3d(" + matrix + ")")
return surface.node()
}