Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /* camera.js */
- // MAY FAIL FOR POINTS INSIDE ELLIPSE
- function closestPointOnEllipse (c, e, ITERATIONS) {
- ITERATIONS = ITERATIONS || 2 // >= 2 is good enough
- var t = Math.atan2(e.rx * c.y, e.ry * c.x)
- var X = c.x * e.rx
- var Y = c.y * e.ry
- var D = (e.rx * e.rx - e.ry * e.ry)
- for (var i = 0; i < ITERATIONS; i++) {
- // netwon's method: t = t - f(t) / f'(t)
- var cos_t = Math.cos(t)
- var sin_t = Math.sin(t)
- t -= (D * cos_t * sin_t - X * sin_t + Y * cos_t) / (D * (cos_t * cos_t - sin_t * sin_t) - X * cos_t - Y * sin_t)
- }
- return {
- x: e.rx * Math.cos(t),
- y: e.ry * Math.sin(t)
- }
- }
- function softBound (t, pull, push) {
- if (pull >= 1) return 0
- let slope = 1 - pull
- let threshold = (1 + pull) * (1 - push)
- let u
- if (t > threshold) {
- u = 1
- if (push > 0) {
- let c = 1 - slope * threshold
- let k = 1 - c * Math.pow(Math.exp(slope / c), -t + threshold)
- u *= k
- }
- } else {
- u = t * slope
- }
- return u
- }
- function ovalBound (p, {rx, ry}, {pull, push}) {
- let dr = push * ((rx > ry) ? ry : rx)
- rx -= dr
- ry -= dr
- let x = p.x / rx
- let y = p.y / ry
- // (x,y) is point on inner ellipse, t is relative distance from center
- let t = x * x + y * y
- let innerLength, distance, outerDistance, dx, dy
- if (t <= 1) {
- t = Math.sqrt(t)
- x = p.x / t
- y = p.y / t
- innerLength = Math.sqrt(x * x + y * y)
- distance = Math.sqrt(p.x * p.x + p.y * p.y)
- } else {
- ({x, y} = closestPointOnEllipse(p, {rx, ry}))
- dx = p.x - x
- dy = p.y - y
- innerLength = Math.sqrt(x * x + y * y)
- outerDistance = Math.sqrt(dx * dx + dy * dy)
- distance = outerDistance + innerLength
- }
- let totalLength = dr + innerLength
- let pushThreshold = dr / totalLength
- t = distance / totalLength
- let u = softBound(t, pull, pushThreshold)
- if (u <= (1 - pushThreshold)) {
- return {
- x: x * u * totalLength / innerLength,
- y: y * u * totalLength / innerLength
- }
- } else {
- let k = (u - (1 - pushThreshold)) / pushThreshold
- return {
- x: dx * k * dr / outerDistance + x,
- y: dy * k * dr / outerDistance + y
- }
- }
- }
- function rectBound ({x, y}, {rx, ry}, {pull, push}) {
- return {
- x: Math.sign(x) * rx * softBound(Math.abs(x) / rx, pull, push),
- y: Math.sign(y) * ry * softBound(Math.abs(y) / ry, pull, push)
- }
- }
- function smoothBound (pos, {rx, ry}, {squareness, roundness, ...settings}) {
- if (rx > ry) {
- rx = squareness * ry + (1 - squareness) * rx
- } else {
- ry = squareness * rx + (1 - squareness) * ry
- }
- let ovalBounded = ovalBound(pos, {rx, ry}, settings)
- let rectBounded = rectBound(pos, {rx, ry}, settings)
- return {
- x: roundness * ovalBounded.x + (1 - roundness) * rectBounded.x,
- y: roundness * ovalBounded.y + (1 - roundness) * rectBounded.y
- }
- }
- function makeCamera (bound) {
- return (prevPos, {x, y}, z, {w, h}, {area, ...settings}) => {
- let delta = {
- x: x - prevPos.x,
- y: y - prevPos.y
- }
- if (delta.x === 0 && delta.y === 0) {
- return prevPos
- }
- let bounded = bound(delta, {rx: area * z * w / 2, ry: area * z * h / 2}, settings)
- return {
- x: prevPos.x + delta.x - bounded.x,
- y: prevPos.y + delta.y - bounded.y
- }
- }
- }
- export const defaultSettings = {
- area: 0.4,
- pull: 0.01,
- push: 0.8,
- roundness: 0.5,
- squareness: 0
- }
- export const smoothCam = makeCamera(smoothBound)
Add Comment
Please, Sign In to add comment