Skip to content

Instantly share code, notes, and snippets.

@kapulkin
Last active October 28, 2019 02:17
Show Gist options
  • Save kapulkin/7679e559242f3bc8f4fd2e9c4c427f08 to your computer and use it in GitHub Desktop.
Save kapulkin/7679e559242f3bc8f4fd2e9c4c427f08 to your computer and use it in GitHub Desktop.
Intersection of ray with rounded box
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 }
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