From dd23854e852e1ee56c6592ac975460601538a079 Mon Sep 17 00:00:00 2001 From: monman53 Date: Sat, 22 Jun 2024 16:41:09 +0900 Subject: [PATCH] Vector calculation for segment intersection --- src/Canvas.vue | 12 ++++---- src/math.ts | 78 ++++++++++++++++++++++++++++++++------------------ 2 files changed, 56 insertions(+), 34 deletions(-) diff --git a/src/Canvas.vue b/src/Canvas.vue index e4be8fc..716b8ee 100644 --- a/src/Canvas.vue +++ b/src/Canvas.vue @@ -2,7 +2,7 @@ import { watch, onMounted, ref } from 'vue' import { state, lights, lens, sensor, sensorData, apple, options, style, lensR, lensD, infR } from './globals' -import { Vec, vec, vecRad, getIntersectionY, getIntersectionLens, crossAngle, fGaussian } from './math' +import { Vec, vec, vecRad, getIntersectionLens, crossAngle, fGaussian, intersectionSS } from './math' import { Light } from './type' @@ -56,7 +56,7 @@ const drawRay = (image: Vec, s0: Vec, s: Vec, v: Vec, color: number, sensorDataT // Collision to aperture //-------------------------------- if (options.value.aperture) { - const p = getIntersectionY(s, v, lens.value.x, -lens.value.r, lens.value.r) + const p = intersectionSS(s, s.add(v.normalize().mul(infR.value)), vec(lens.value.x, -lens.value.r), vec(lens.value.x, lens.value.r)) if (p) { const upperHit = p.y > lens.value.aperture * lens.value.r const lowerHit = p.y < -lens.value.aperture * lens.value.r @@ -93,7 +93,7 @@ const drawRay = (image: Vec, s0: Vec, s: Vec, v: Vec, color: number, sensorDataT // Collision to ideal lens //-------------------------------- if (options.value.lensIdeal && options.value.lens) { - const p = getIntersectionY(s, v, lens.value.x, -lens.value.r, lens.value.r) + const p = intersectionSS(s, s.add(v.normalize().mul(infR.value)), vec(lens.value.x, -lens.value.r), vec(lens.value.x, lens.value.r)) if (p) { v = p.sub(s) s = drawSegment(s, v, v.length()) @@ -116,7 +116,7 @@ const drawRay = (image: Vec, s0: Vec, s: Vec, v: Vec, color: number, sensorDataT if (options.value.body) { // Upper { - const p = getIntersectionY(s, v, lens.value.x, lens.value.r, infR.value); + const p = intersectionSS(s, s.add(v.normalize().mul(infR.value)), vec(lens.value.x, -infR.value), vec(lens.value.x, -lens.value.r)) if (p) { v = p.sub(s) drawSegment(s, v, v.length()) @@ -125,7 +125,7 @@ const drawRay = (image: Vec, s0: Vec, s: Vec, v: Vec, color: number, sensorDataT } // Lower { - const p = getIntersectionY(s, v, lens.value.x, -infR.value, -lens.value.r); + const p = intersectionSS(s, s.add(v.normalize().mul(infR.value)), vec(lens.value.x, infR.value), vec(lens.value.x, lens.value.r)) if (p) { v = p.sub(s) drawSegment(s, v, v.length()) @@ -138,7 +138,7 @@ const drawRay = (image: Vec, s0: Vec, s: Vec, v: Vec, color: number, sensorDataT // Collision to sensor //-------------------------------- if (options.value.sensor) { - const p = getIntersectionY(s, v, sensor.value.x, -sensor.value.r, sensor.value.r); + const p = intersectionSS(s, s.add(v.normalize().mul(infR.value)), vec(sensor.value.x, -sensor.value.r), vec(sensor.value.x, sensor.value.r)) if (p) { v = p.sub(s) drawSegment(s, v, v.length()) diff --git a/src/math.ts b/src/math.ts index 642e710..b1012be 100644 --- a/src/math.ts +++ b/src/math.ts @@ -126,32 +126,64 @@ export class Vec { // TODO: toString } +const dot = (p: Vec, q: Vec) => { + return p.x * q.x + p.y * q.y +} + +// export const dotAngle = (x1: number, y1: number, x2: number, y2: number) => { +// const norm1 = Math.sqrt(x1 * x1 + y1 * y1); +// const norm2 = Math.sqrt(x2 * x2 + y2 * y2); +// return Math.acos((x1 * x2 + y1 * y2) / (norm1 * norm2)); +// }; + +const cross = (p: Vec, q: Vec) => { + return p.x * q.y - q.x * p.y +} + +export const crossAngle = (p: Vec, q: Vec) => { + return Math.asin(cross(p, q) / (p.length() * q.length())); +}; + //================================ -// Support functions +// Geometry //================================ -// export const getIntersectionX = (px: number, py: number, theta: number, minX: number, maxX: number, y: number, maxR: number) => { -// const sin = Math.sin(theta); -// const cos = Math.cos(theta); -// const r = (y - py) / sin; -// const x = px + r * cos; -// if (r >= 0 && minX <= x && x <= maxX) { -// return [true, x, y, r]; -// } else { -// return [false, px + maxR * cos, py + maxR * sin, maxR]; -// } -// } -export const getIntersectionY = (s: Vec, v: Vec, x: number, minY: number, maxY: number) => { - const n = v.normalize() - const r = (x - s.x) / n.x; - const y = s.y + r * n.y; - if (r >= 0 && minY <= y && y <= maxY) { - return vec(x, y) +const eps = 1e-9 + +// ccw +const ccw = (a: Vec, b: Vec, c: Vec) => { + b = b.sub(a) + c = c.sub(a) + if (cross(b, c) > eps) return +1 // counter clockwise + if (cross(b, c) < -eps) return -1 // clockwise + if (dot(b, c) < 0) return +2 // cab (back) + if (b.length() < c.length()) return -2 // abc (front) + return 0 // acb (on segment) +} + +const isIntersectedSS = (a1: Vec, a2: Vec, b1: Vec, b2: Vec) => { + return ccw(a1, a2, b1) * ccw(a1, a2, b2) <= 0 && + ccw(b1, b2, a1) * ccw(b1, b2, a2) <= 0 +} + +const intersectionLL = (a1: Vec, a2: Vec, b1: Vec, b2: Vec) => { + const a = a2.sub(a1) + const b = b2.sub(b1) + return a1.add(a.mul(cross(b, b1.sub(a1))).div(cross(b, a))) +} + +export const intersectionSS = (a1: Vec, a2: Vec, b1: Vec, b2: Vec) => { + if (isIntersectedSS(a1, a2, b1, b2)) { + return intersectionLL(a1, a2, b1, b2) } else { return null } } +//================================ +// Support functions +//================================ + export const getIntersectionLens = (s: Vec, v: Vec, cl: Vec, r: number /* lens diameter */, R: number /* lens curvature radius */, select: boolean) => { const n = v.normalize() @@ -175,16 +207,6 @@ export const getIntersectionLens = (s: Vec, v: Vec, cl: Vec, r: number /* lens d } } -// export const dotAngle = (x1: number, y1: number, x2: number, y2: number) => { -// const norm1 = Math.sqrt(x1 * x1 + y1 * y1); -// const norm2 = Math.sqrt(x2 * x2 + y2 * y2); -// return Math.acos((x1 * x2 + y1 * y2) / (norm1 * norm2)); -// }; - -export const crossAngle = (p: Vec, q: Vec) => { - return Math.asin((p.x * q.y - q.x * p.y) / (p.length() * q.length())); -}; - // const getIntersectionBody = (cx, cy, theta, maxR, isInner) => { // // Front // if (!isInner) {