console.clear() var width = 900, height = 500 var svg = d3.select('body').html('').append('svg').at({ width, height }) var drag = d3 .drag() .on('drag', d => { d[0] = d3.event.x d[1] = d3.event.y update() }) .subject(d => { return { x: d[0], y: d[1] } }) var a = [200, 300] var b = [400, 400] var path = svg.append('path').at({ stroke: '#cc0', fill: '#eee' }) var pointSel = svg .appendMany([a, b], 'circle') .at({ r: 10, fill: '#ccc', stroke: '#000', cursor: 'pointer' }) .call(drag) var angleLine = svg.append('path').at({ stroke: '#0ff', strokeWidth: 4 }) var normLine = svg.append('path').at({ stroke: '#f0f', strokeWidth: 4 }) update() function update() { pointSel.translate(d => d) var angle = calcAngle(a, b) angleLine.at({ d: ['M100,100l', Math.cos(angle) * 50, Math.sin(angle) * 50].join(' ') }) var norm = angle + Math.PI / 2 normLine.at({ d: ['M100,100l', Math.cos(norm) * 50, Math.sin(norm) * 50].join(' ') }) var dist = calcDist(a, b) path.at({ d: [ 'M', a, 'l', Math.cos(angle + Math.PI / 2) * dist, Math.sin(angle + Math.PI / 2) * dist, 'l', Math.cos(angle + Math.PI) * dist, Math.sin(angle + Math.PI) * dist, 'L', b, 'Z' ].join(' ') }) function calcAngle([x0, y0], [x1, y1]) { return Math.atan2(y0 - y1, x0 - x1) } function calcDist([x0, y0], [x1, y1]) { var dx = x0 - x1, dy = y0 - y1 return Math.sqrt(dx * dx + dy * dy) } }