Skip to content

Instantly share code, notes, and snippets.

@gregtatum
Last active July 21, 2016 03:32
Show Gist options
  • Save gregtatum/2c5bb9cf83cfe7e2f58c29403540df31 to your computer and use it in GitHub Desktop.
Save gregtatum/2c5bb9cf83cfe7e2f58c29403540df31 to your computer and use it in GitHub Desktop.
Round a corner
const {
dot,
length,
add,
scale,
normalize,
divide,
subtract,
cross,
distance,
squaredDistance
} = require('gl-vec2')
function roundAngle(segment, inset, ctx) {
// Convex angles fail, so have something before the real
// function to re-order the segment and result, if convex.
const [a, b, c] = segment
/**
* vectors
*
* • •------>
* v ^ w
* |
* |
* •
*/
const vX = b[0] - a[0]
const vY = b[1] - a[1]
const wX = c[0] - b[0]
const wY = c[1] - b[1]
const theta = Math.atan2(vX, vY)
const rotatedWX = wX * Math.cos(theta) - wY * Math.sin(theta)
// See which way vector W rotates, left or right
if (rotatedWX > 0) {
const [ra, rb, rc] = roundAngle_strict(c, b, a, inset, ctx)
return [rc, rb, ra]
} else {
return roundAngle_strict(a, b, c, inset, ctx)
}
}
function roundAngle_strict(a, b, c, inset, ctx) {
/**
* segment rounded vectors midway
*
* b --- c > ,-- c > • <------• • <--•---•
* | / ^ w ^ mw w
* | | | • mv
* a a | |
* v v v • v •
*/
// Figure out some things about this vector
const v = [b[0] - a[0], b[1] - a[1]]
const w = [b[0] - c[0], b[1] - c[1]]
const unitV = normalize([], v)
const unitW = normalize([], w)
const lengthV = length(v)
const lengthW = length(w)
// Make sure the target distance doesn't bust out of the segments
const targetInset = Math.min(inset, length(v), length(w))
const mv = add([], scale([], unitV, lengthV - targetInset), a)
const mw = add([], scale([], unitW, lengthW - targetInset), c)
const normalV = [-unitV[1], unitV[0]]
const normalW = [unitW[1], -unitW[0]]
// Find intersection of the normals
// Given: mw + t × normalW = mv + u × normalV
// t = (mv − mw) × normalW / (normalW × normalV)
const distanceOnNormalV = (
magCross(subtract([], mv, mw), normalW) /
magCross(normalW, normalV)
)
const arcCenter = add([], mw, scale([], normalW, distanceOnNormalV))
const arcRadius = distance(mv, arcCenter)
const arcStart = Math.PI + Math.atan2(normalV[1], normalV[0])
const arcEnd = Math.PI + Math.atan2(normalW[1], normalW[0])
const arc = {
center: arcCenter,
radius: arcRadius,
start: arcStart,
end: arcEnd
}
// Debug draw if a 2d context is passed in.
if (ctx) {
// Draw sides of triangle
ctx.beginPath()
ctx.strokeStyle = 'rgba(255,255,255,0.8)'
ctx.lineWidth = 2
ctx.moveTo(a[0],a[1])
ctx.lineTo(b[0],b[1])
ctx.lineTo(c[0],c[1])
ctx.stroke()
// Draw midway dots
const dotSize = 10
ctx.fillStyle = 'rgba(255,0,0,0.8)'
ctx.fillRect(mv[0] - dotSize / 2, mv[1] - dotSize / 2, dotSize, dotSize)
ctx.fillStyle = 'rgba(0,255,0,0.8)'
ctx.fillRect(mw[0] - dotSize / 2, mw[1] - dotSize / 2, dotSize, dotSize)
// Draw normals
const normalLength = 40
ctx.beginPath()
ctx.strokeStyle = 'rgba(0,255,255,0.8)'
ctx.lineWidth = 2
ctx.moveTo(mv[0],mv[1])
ctx.lineTo(mv[0] + normalLength * normalV[0], mv[1] + normalLength * normalV[1])
ctx.stroke()
ctx.beginPath()
ctx.strokeStyle = 'rgba(0,255,255,0.8)'
ctx.lineWidth = 2
ctx.moveTo(mw[0],mw[1])
ctx.lineTo(mw[0] + normalLength * normalW[0], mw[1] + normalLength * normalW[1])
ctx.stroke()
// Draw arcCenter dot
ctx.fillStyle = 'rgba(255,255,0,0.8)'
ctx.fillRect(arcCenter[0] - dotSize / 2, arcCenter[1] - dotSize / 2, dotSize, dotSize)
ctx.strokeStyle = 'rgba(255,255,0,0.1)'
ctx.beginPath()
ctx.arc(arc.center[0], arc.center[1], arc.radius, 0, 2 * Math.PI)
ctx.stroke()
// Draw full arc
ctx.beginPath()
ctx.moveTo(a[0], a[1])
ctx.lineTo(mv[0], mv[1])
ctx.arc(arc.center[0], arc.center[1], arc.radius, arc.start, arc.end)
ctx.moveTo(mw[0], mw[1])
ctx.lineTo(c[0], c[1])
ctx.lineWidth = 4
ctx.strokeStyle = 'rgba(255, 0, 255, 0.5)'
ctx.stroke()
}
return [
[a, mv],
arc,
[mw, c]
]
}
function magCross (v, w) {
return v[0] * w[1] - v[1] * w[0]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment