mirror of
https://gitlab.com/skysthelimit.dev/selenite.git
synced 2025-06-21 04:46:45 -05:00
153 lines
5.4 KiB
JavaScript
153 lines
5.4 KiB
JavaScript
define([],
|
|
function () {
|
|
// Adapted from CircumscribedCircle.java @ github.com/itdelatrisu/opsu
|
|
function CircumscribedCircle(hit) {
|
|
|
|
var start = {
|
|
x: hit.x,
|
|
y: hit.y
|
|
};
|
|
var mid = {
|
|
x: hit.keyframes[0].x,
|
|
y: hit.keyframes[0].y
|
|
};
|
|
var end = {
|
|
x: hit.keyframes[1].x,
|
|
y: hit.keyframes[1].y
|
|
};
|
|
|
|
// find the circle center
|
|
var mida = vecmid(start, mid);
|
|
var midb = vecmid(end, mid);
|
|
var nora = nor(vecsub(mid, start));
|
|
var norb = nor(vecsub(mid, end));
|
|
var circleCenter = intersect(mida, nora, midb, norb);
|
|
if (!circleCenter) return [];
|
|
|
|
// find the angles relative to the circle center
|
|
var startAngPoint = vecsub(start, circleCenter);
|
|
var midAngPoint = vecsub(mid, circleCenter);
|
|
var endAngPoint = vecsub(end, circleCenter);
|
|
|
|
var startAng = Math.atan2(startAngPoint.y, startAngPoint.x);
|
|
var midAng = Math.atan2(midAngPoint.y, midAngPoint.x);
|
|
var endAng = Math.atan2(endAngPoint.y, endAngPoint.x);
|
|
|
|
const TWO_PI = Math.PI * 2;
|
|
const HALF_PI = Math.PI / 2;
|
|
|
|
// find the angles that pass through midAng
|
|
if (!isIn(startAng, midAng, endAng)) {
|
|
if (Math.abs(startAng + TWO_PI - endAng) < TWO_PI && isIn(startAng + (TWO_PI), midAng, endAng))
|
|
startAng += TWO_PI;
|
|
else if (Math.abs(startAng - (endAng + TWO_PI)) < TWO_PI && isIn(startAng, midAng, endAng + (TWO_PI)))
|
|
endAng += TWO_PI;
|
|
else if (Math.abs(startAng - TWO_PI - endAng) < TWO_PI && isIn(startAng - (TWO_PI), midAng, endAng))
|
|
startAng -= TWO_PI;
|
|
else if (Math.abs(startAng - (endAng - TWO_PI)) < TWO_PI && isIn(startAng, midAng, endAng - (TWO_PI)))
|
|
endAng -= TWO_PI;
|
|
else
|
|
throw "Cannot find angles between midAng.";
|
|
}
|
|
|
|
// find an angle with an arc length of pixelLength along this circle
|
|
var radius = veclen(startAngPoint);
|
|
var arcAng = Math.abs(startAng - endAng);
|
|
let expectAng = hit.pixelLength / radius;
|
|
if (arcAng > expectAng * 0.97) {
|
|
// console.log("truncating arc to ", expectAng / arcAng);
|
|
arcAng = expectAng; // truncate to given len
|
|
} else {
|
|
console.warn("[curve] P shorter than given", arcAng / expectAng);
|
|
}
|
|
|
|
// now use it for our new end angle
|
|
endAng = (endAng > startAng) ? startAng + arcAng : startAng - arcAng;
|
|
|
|
// calculate points
|
|
var step = Math.floor(hit.pixelLength / CURVE_POINTS_SEPERATION);
|
|
var curve = new Array(step + 1);
|
|
|
|
pointAt = function (t) {
|
|
if (t > 1) t = 1;
|
|
var ang = lerp(startAng, endAng, t);
|
|
return {
|
|
x: Math.cos(ang) * radius + circleCenter.x,
|
|
y: Math.sin(ang) * radius + circleCenter.y,
|
|
t: t,
|
|
};
|
|
};
|
|
for (var i = 0; i < curve.length; i++) {
|
|
curve[i] = pointAt(i / step);
|
|
}
|
|
|
|
// check total distance
|
|
var l = 0;
|
|
for (let i = 1; i < curve.length; ++i) {
|
|
let dx = curve[i].x - curve[i - 1].x;
|
|
let dy = curve[i].y - curve[i - 1].y;
|
|
l += Math.hypot(dx, dy);
|
|
}
|
|
return {
|
|
curve: curve,
|
|
pointAt: pointAt,
|
|
totalDistance: l
|
|
};
|
|
};
|
|
return CircumscribedCircle;
|
|
|
|
function lerp(a, b, t) {
|
|
return a * (1 - t) + b * t;
|
|
}
|
|
|
|
function veclen(a) {
|
|
return Math.sqrt(a.x * a.x + a.y * a.y);
|
|
}
|
|
|
|
function nor(a) {
|
|
return {
|
|
x: -a.y,
|
|
y: a.x
|
|
};
|
|
}
|
|
|
|
function vecsub(a, b) {
|
|
return {
|
|
x: a.x - b.x,
|
|
y: a.y - b.y
|
|
};
|
|
}
|
|
|
|
function vecmid(a, b) {
|
|
return {
|
|
x: (a.x + b.x) / 2,
|
|
y: (a.y + b.y) / 2
|
|
};
|
|
}
|
|
|
|
function isIn(a, b, c) {
|
|
return (b > a && b < c) || (b < a && b > c);
|
|
}
|
|
|
|
function intersect(a, ta, b, tb) {
|
|
// xy = a + ta * t = b + tb * u
|
|
// t =(b + tb*u -a)/ta
|
|
//t(x) == t(y)
|
|
//(b.x + tb.x*u -a.x)/ta.x = (b.y + tb.y*u -a.y)/ta.y
|
|
// b.x*ta.y + tb.x*u*ta.y -a.x*ta.y = b.y*ta.x + tb.y*u*ta.x -a.y*ta.x
|
|
// tb.x*u*ta.y - tb.y*u*ta.x= b.y*ta.x -a.y*ta.x -b.x*ta.y +a.x*ta.y
|
|
//u *(tb.x*ta.y - tb.y*ta.x) = (b.y-a.y)ta.x +(a.x-b.x)ta.y
|
|
//u = ((b.y-a.y)ta.x +(a.x-b.x)ta.y) / (tb.x*ta.y - tb.y*ta.x);
|
|
|
|
var des = tb.x * ta.y - tb.y * ta.x;
|
|
if (Math.abs(des) < 0.00001) {
|
|
console.warn("[curve] encountering straight P slider");
|
|
return undefined;
|
|
}
|
|
var u = ((b.y - a.y) * ta.x + (a.x - b.x) * ta.y) / des;
|
|
return {
|
|
x: b.x + tb.x * u,
|
|
y: b.y + tb.y * u
|
|
};
|
|
}
|
|
}); |