better intersection detection
This commit is contained in:
parent
9c76d34a5b
commit
0b4a442f5e
62
archive/line-segment-intersection-try.ts
Normal file
62
archive/line-segment-intersection-try.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import * as mathjs from 'mathjs';
|
||||
|
||||
interface Range {
|
||||
x0: number;
|
||||
y0: number;
|
||||
x1: number;
|
||||
y1: number;
|
||||
}
|
||||
|
||||
// my first attempt at this
|
||||
function lineSegmentIntersection(l0: Range, l1: Range): boolean {
|
||||
// convert to y = mx + b
|
||||
const m0 = mathjs.fraction(l0.y1 - l0.y0, l0.x1 - l0.x0);
|
||||
const m1 = mathjs.fraction(l1.y1 - l1.y0, l1.x1 - l1.x0);
|
||||
|
||||
const b0 = mathjs.subtract(l0.y0, mathjs.multiply(m0, l0.x0));
|
||||
const b1 = mathjs.subtract(l1.y1, mathjs.multiply(m1, l1.x1));
|
||||
|
||||
if (mathjs.equal(m0, m1)) {
|
||||
if (mathjs.equal(b0, b1)) {
|
||||
// parallel lines with equal intersect
|
||||
return (
|
||||
(l1.x0 >= l0.x0 && l1.x0 <= l0.x1) ||
|
||||
(l1.x1 >= l1.x0 && l1.x1 <= l1.x1)
|
||||
);
|
||||
} else {
|
||||
// parallel lines with different y-intersect will never intersect
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// solve for l0 = l1
|
||||
const M = mathjs.subtract(m0, m1);
|
||||
const B = mathjs.subtract(b0, b1);
|
||||
|
||||
// MX + B = 0 -> X = -B/M
|
||||
const X = mathjs.divide(mathjs.unaryMinus(B), M);
|
||||
const X_num = mathjs.number(X as mathjs.Fraction);
|
||||
|
||||
const intersects =
|
||||
X_num >= Math.min(l0.x0, l0.x1) &&
|
||||
X_num <= Math.max(l0.x0, l0.x1) &&
|
||||
X_num >= Math.min(l1.x0, l1.x1) &&
|
||||
X_num <= Math.max(l1.x0, l1.x1);
|
||||
|
||||
// const Y = mathjs.add(mathjs.multiply(m0, X), b0);
|
||||
// const Y_num = mathjs.number(Y as mathjs.Fraction);
|
||||
// if (intersects) {
|
||||
// console.log('intersects at', X_num, Y_num);
|
||||
// } else {
|
||||
// console.log('would have intersected at', X_num, Y_num, l0, l1);
|
||||
// }
|
||||
|
||||
// to test
|
||||
// useEffect(() => {
|
||||
// const l0 = { x0: 2, y0: 8, x1: 6, y1: 5, stroke: '' };
|
||||
// const l1 = { x0: 3, y0: 9, x1: 7, y1: 8, stroke: '' };
|
||||
// console.log('intersection:', l0, l1, lineSegmentIntersection(l0, l1));
|
||||
// }, []);
|
||||
|
||||
return intersects;
|
||||
}
|
@ -11,8 +11,6 @@ import {
|
||||
|
||||
import './grid.scss';
|
||||
|
||||
import * as mathjs from 'mathjs';
|
||||
|
||||
import * as lodash from 'lodash';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { drawModeState, lineColorState } from '../../atoms';
|
||||
@ -37,57 +35,24 @@ interface Line {
|
||||
stroke: string;
|
||||
}
|
||||
|
||||
// https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
|
||||
// https://stackoverflow.com/questions/9043805/test-if-two-lines-intersect-javascript-function
|
||||
function lineSegmentIntersection(l0: Range, l1: Range): boolean {
|
||||
// convert to y = mx + b
|
||||
const m0 = mathjs.fraction(l0.y1 - l0.y0, l0.x1 - l0.x0);
|
||||
const m1 = mathjs.fraction(l1.y1 - l1.y0, l1.x1 - l1.x0);
|
||||
|
||||
const b0 = mathjs.subtract(l0.y0, mathjs.multiply(m0, l0.x0));
|
||||
const b1 = mathjs.subtract(l1.y1, mathjs.multiply(m1, l1.x1));
|
||||
|
||||
if (mathjs.equal(m0, m1)) {
|
||||
if (mathjs.equal(b0, b1)) {
|
||||
// parallel lines with equal intersect
|
||||
return (
|
||||
(l1.x0 >= l0.x0 && l1.x0 <= l0.x1) ||
|
||||
(l1.x1 >= l1.x0 && l1.x1 <= l1.x1)
|
||||
);
|
||||
} else {
|
||||
// parallel lines with different y-intersect will never intersect
|
||||
return false;
|
||||
}
|
||||
const determinant =
|
||||
(l0.x1 - l0.x0) * (l1.y1 - l1.y0) - (l1.x1 - l1.x0) * (l0.y1 - l0.y0);
|
||||
if (determinant === 0) {
|
||||
// full lines have no intersection (they must be parallel)
|
||||
return false;
|
||||
}
|
||||
|
||||
// solve for l0 = l1
|
||||
const M = mathjs.subtract(m0, m1);
|
||||
const B = mathjs.subtract(b0, b1);
|
||||
|
||||
// MX + B = 0 -> X = -B/M
|
||||
const X = mathjs.divide(mathjs.unaryMinus(B), M);
|
||||
const X_num = mathjs.number(X as mathjs.Fraction);
|
||||
|
||||
const intersects =
|
||||
X_num >= Math.min(l0.x0, l0.x1) &&
|
||||
X_num <= Math.max(l0.x0, l0.x1) &&
|
||||
X_num >= Math.min(l1.x0, l1.x1) &&
|
||||
X_num <= Math.max(l1.x0, l1.x1);
|
||||
|
||||
// const Y = mathjs.add(mathjs.multiply(m0, X), b0);
|
||||
// const Y_num = mathjs.number(Y as mathjs.Fraction);
|
||||
// if (intersects) {
|
||||
// console.log('intersects at', X_num, Y_num);
|
||||
// } else {
|
||||
// console.log('would have intersected at', X_num, Y_num, l0, l1);
|
||||
// }
|
||||
|
||||
// to test
|
||||
// useEffect(() => {
|
||||
// const l0 = { x0: 2, y0: 8, x1: 6, y1: 5, stroke: '' };
|
||||
// const l1 = { x0: 3, y0: 9, x1: 7, y1: 8, stroke: '' };
|
||||
// console.log('intersection:', l0, l1, lineSegmentIntersection(l0, l1));
|
||||
// }, []);
|
||||
|
||||
return intersects;
|
||||
const lambda =
|
||||
((l1.y1 - l1.y0) * (l1.x1 - l0.x0) +
|
||||
(l1.x0 - l1.x1) * (l1.y1 - l0.y0)) /
|
||||
determinant;
|
||||
const gamma =
|
||||
((l0.y0 - l0.y1) * (l1.x1 - l0.x0) +
|
||||
(l0.x1 - l0.x0) * (l1.y1 - l0.y0)) /
|
||||
determinant;
|
||||
return 0 < lambda && lambda < 1 && 0 < gamma && gamma < 1;
|
||||
}
|
||||
|
||||
function pointsToRange(p0: Point, p1: Point): Range {
|
||||
@ -198,7 +163,10 @@ const Grid: FC = () => {
|
||||
|
||||
setUserRedoLines([]);
|
||||
|
||||
if (lodash.isEqual(userStartPoint, point)) return;
|
||||
if (lodash.isEqual(userStartPoint, point)) {
|
||||
setUserStartPoint(null);
|
||||
return;
|
||||
}
|
||||
if (lodash.isNull(userStartPoint)) {
|
||||
setUserStartPoint(point);
|
||||
return;
|
||||
@ -295,16 +263,31 @@ const Grid: FC = () => {
|
||||
const userLineIndicatorElement = useMemo(() => {
|
||||
if (lodash.isNull(userStartPoint)) return null;
|
||||
const point = snapToGrid(mousePoint, GAP);
|
||||
return (
|
||||
<line
|
||||
x1={userStartPoint.x}
|
||||
y1={userStartPoint.y}
|
||||
x2={point.x}
|
||||
y2={point.y}
|
||||
stroke={userLineColor}
|
||||
strokeWidth="2"
|
||||
/>
|
||||
);
|
||||
if (userDrawMode === 'line') {
|
||||
return (
|
||||
<line
|
||||
x1={userStartPoint.x}
|
||||
y1={userStartPoint.y}
|
||||
x2={point.x}
|
||||
y2={point.y}
|
||||
stroke={userLineColor}
|
||||
strokeWidth="2"
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
console.assert(userDrawMode === 'trash');
|
||||
return (
|
||||
<line
|
||||
x1={userStartPoint.x}
|
||||
y1={userStartPoint.y}
|
||||
x2={point.x}
|
||||
y2={point.y}
|
||||
stroke="#ff0000"
|
||||
strokeDasharray="20,10,5,5,5,10"
|
||||
strokeWidth="2"
|
||||
/>
|
||||
);
|
||||
}
|
||||
}, [userStartPoint, mousePoint]);
|
||||
const userPointIndicatorElement = useMemo(() => {
|
||||
const point = snapToGrid(mousePoint, GAP);
|
||||
|
Loading…
Reference in New Issue
Block a user