Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save schester44/16be0720bbe7b2d01a38eb1baa3a2d89 to your computer and use it in GitHub Desktop.
Save schester44/16be0720bbe7b2d01a38eb1baa3a2d89 to your computer and use it in GitHub Desktop.
find the shortest distance between a point and a line segment.
import { render } from "react-dom";
import React from "react";
import { Stage, Text, Layer, Line, Circle } from "react-konva";
const originalPoints = [
{ x: 294, y: 344 },
{ x: 322, y: 451 },
{ x: 547, y: 379 },
{ x: 505, y: 251 },
{ x: 417, y: 317 }
];
const x = 100;
const y = 100;
// source: http://csharphelper.com/blog/2016/09/find-the-shortest-distance-between-a-point-and-a-line-segment-in-c/
const getDistanceToUse = (p1, p2, pt) => {
let dx = p2.x - p1.x;
let dy = p2.y - p1.y;
// See if this represents one of the segment's
// end points or a point in the middle.
let closest = null;
if (dx === 0 && dy === 0) {
// It's a point not a line segment.
closest = p1;
dx = pt.x - p1.x;
dy = pt.y - p1.y;
return Math.sqrt(dx * dx + dy * dy);
}
// Calculate the t that minimizes the distance.
let t = ((pt.x - p1.x) * dx + (pt.y - p1.y) * dy) / (dx * dx + dy * dy);
if (t < 0) {
closest = { x: p1.x, y: p1.y };
dx = pt.x - p1.x;
dy = pt.y - p1.y;
} else if (t > 1) {
closest = { x: p2.x, y: p2.y };
dx = pt.x - p2.x;
dy = pt.y - p2.y;
} else {
closest = { x: p1.x + t * dx, y: p1.y + t * dy };
dx = pt.x - closest.x;
dy = pt.y - closest.y;
}
return Math.sqrt(dx * dx + dy * dy);
};
const getIndexOfClosestLine = (points, pt) => {
let index = undefined
let distance = Infinity
for (let i = 0; i < points.length; i++) {
const first = points[i];
const next = points[i + 1] ? points[i + 1] : points[0];
const distanceToLine = getDistanceToUse(first, next, pt);
if (distanceToLine < distance) {
index = i
distance = distanceToLine
}
}
return index
}
const App = () => {
const [allPoints, setPoints] = React.useState(originalPoints)
const currentIndex = React.useRef(undefined)
const inserted = React.useRef(false)
const handleDrag = (evt) => {
const pt = {
x: evt.target.x(),
y: evt.target.y(),
ITS_ME: true
}
const index = getIndexOfClosestLine(originalPoints, pt)
if (index !== currentIndex.current) {
let points = allPoints.filter((c, i) => !c.ITS_ME)
inserted.current = true
currentIndex.current = index
if (index === allPoints.length - 1) {
points.push(pt);
} else {
points.splice(index + 1, 0, pt)
}
setPoints(points)
} else {
setPoints(points => {
return points.map((t, i) => i === index + 1 ? pt : t)
})
}
}
const points = allPoints.reduce((acc, point) => {
acc = acc.concat([point.x, point.y]);
return acc;
}, [])
const handleOtherDrag = (e, index) => {
console.log(e, index)
setPoints(points => {
return points.map((t, i) => i === index ? {
x: e.target.x(),
y: e.target.y()
} : t)
})
}
return <div>
<Stage width={window.innerWidth} height={window.innerHeight}>
<Layer>
<Circle
draggable
onDragMove={handleDrag}
x={x} y={y} radius={10} fill="rgba(255,0,244, 1)" />
{allPoints.map((point, index) => {
return (
<Text
x={point.x + 10}
y={point.y}
key={index}
text={`${point.x + "," + point.y}`}
fontSize={15}
/>
);
})}
{allPoints.map((point, index) => {
return (
<Circle
key={index}
x={point.x}
y={point.y}
draggable
onDragMove={e => handleOtherDrag(e, index)}
radius={5}
fill={
index === 0
? "red"
: index === allPoints.length - 1
? "green"
: "rgba(0,0,255,1)"
}
/>
);
})}
<Line
x={0}
y={0}
points={points}
tension={0}
closed
stroke={"rgba(223, 108, 106, 1.0)"}
fill={"rgba(223, 108, 106, 0.6)"}
/>
</Layer>
</Stage>
</div>
}
render(<App />, document.getElementById("root"));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment