Last active
October 28, 2019 02:17
-
-
Save kapulkin/7679e559242f3bc8f4fd2e9c4c427f08 to your computer and use it in GitHub Desktop.
Intersection of ray with rounded box
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Circle { | |
private x: number | |
private y: number | |
private radius: number | |
/** | |
* constructor | |
*/ | |
public constructor(x, y, radius) { | |
this.x = x | |
this.y = y | |
this.radius = radius | |
} | |
private sqr(x) { | |
return x * x | |
} | |
private rayPoint(x, y, dx, dy, t) { | |
return { | |
x: x + t * dx, | |
y: y + t * dy | |
} | |
} | |
/** | |
* instersectWithRay | |
*/ | |
public instersectWithRay(x, y, dx, dy) { | |
const a = dx * dx + dy * dy | |
const halfB= (dx * (x - this.x) + dy * (y - this.y)) | |
const c = this.sqr(x - this.x) + this.sqr(y - this.y) - this.radius * this.radius | |
const D = halfB * halfB - a * c | |
if (D < 0) { | |
return [] | |
} | |
if (D === 0) { | |
const t = - halfB / a | |
return t > 0 | |
? [{ | |
t, | |
x: x + t * dx, | |
y: y + t * dy | |
}] | |
: [] | |
} | |
else { | |
const sqrtD = Math.sqrt(D) | |
const ts = [(- halfB - sqrtD) / a, (- halfB + sqrtD) / a] | |
return ts.filter(t => t >= 0).map(t => { | |
const p = this.rayPoint(x, y, dx, dy, t) | |
return { t, x: p.x, y: p.y } | |
}) | |
} | |
} | |
} | |
export { Circle } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Circle } from '~/utils/Circle' | |
class RoundedBox { | |
private x: number | |
private y: number | |
private width: number | |
private height: number | |
private hLines: [] | |
private vLines: [] | |
private circles: [] | |
/** | |
* constructor | |
*/ | |
public constructor(x, y, width, height, radius) { | |
this.x = x | |
this.y = y | |
this.width = width | |
this.height = height | |
this.hLines = [y - height / 2, y + height / 2] | |
this.vLines = [x - width / 2, x + width / 2] | |
this.circles.push(new Circle( | |
x: x + width / 2 - radius, | |
y: y + height / 2 - radius, | |
radius | |
)) | |
this.circles.push(new Circle( | |
x: x - width / 2 - radius, | |
y: y + height / 2 - radius, | |
radius | |
)) | |
this.circles.push(new Circle( | |
x: x - width / 2 - radius, | |
y: y - height / 2 - radius, | |
radius | |
)) | |
this.circles.push(new Circle( | |
x: x + width / 2 - radius, | |
y: y - height / 2 - radius, | |
radius | |
)) | |
} | |
private intersectAxisLineWithRay(v, dv, a) { | |
return t = (a - v) / dv | |
} | |
public instersectWithRay(x, y, dx, dy) { | |
// intersect with circles | |
const points = this.circles | |
.map(circle => { return circle.instersectWithRay(x, y, dx, dy) }) | |
.reduce((a, b) => { return a.concat(b); }); | |
if (points.length > 0) { | |
let maxT = points[0].t | |
let iPoint = points[0] | |
points.forEach(point => { | |
if (point.t > maxT) { | |
maxT = point.t | |
iPoint = point | |
} | |
}); | |
return iPoint | |
} | |
// TODO: | |
// intersect with segments | |
let boxPoints = [] | |
if (dx !== 0) { | |
const vPoints = this.vLines | |
.map(coord => { | |
const t = this.intersectAxisLineWithRay(x, dx, coord) | |
return { t, coord } | |
}) | |
.filter(intersection => intersection.t >= 0) | |
.map({ t, coord } => { | |
return { | |
t, x: coord, y: y + dy * t | |
} | |
}) | |
boxPoints = boxPoints.concat(vPoints) | |
} | |
if (dy !== 0) { | |
const hPoints = this.hLines | |
.map(coord => { | |
const t = this.intersectAxisLineWithRay(y, dy, coord) | |
return { t, coord } | |
}) | |
.filter(intersection => intersection.t >= 0) | |
.map({ t, coord } => { | |
return { | |
t, x: x + dx * t, y: coord | |
} | |
}) | |
boxPoints = boxPoints.concat(hPoints) | |
} | |
if (boxPoints.length > 0) { | |
let minT = points[0].t | |
let iPoint = points[0] | |
points.forEach(point => { | |
if (point.t < minT) { | |
minT = point.t | |
iPoint = point | |
} | |
}); | |
return iPoint | |
} | |
return null | |
} | |
} | |
export { RoundedBox } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment