toosoon-utils
Version:
Utility functions & classes
117 lines (116 loc) • 4.16 kB
JavaScript
import { EPSILON, TWO_PI } from '../../constants';
import { Vector2 } from '../geometry';
import Curve from './Curve';
/**
* Utility class for manipulating ellipses
*
* @exports
* @class EllipseCurve
* @extends Curve
*/
export default class EllipseCurve extends Curve {
type = 'EllipseCurve';
/**
* X-axis coordinate of the center of the ellipse
*/
cx;
/**
* Y-axis coordinate of the center of the ellipse
*/
cy;
/**
* X-radius of the ellipse
*/
rx;
/**
* Y-radius of the ellipse
*/
ry;
/**
* Rotation angle of the ellipse (in radians), counterclockwise from the positive X-axis
*/
rotation;
/**
* Start angle of the arc (in radians)
*/
startAngle;
/**
* End angle of the arc (in radians)
*/
endAngle;
/**
* Flag indicating the direction of the arc
*/
counterclockwise;
/**
* @param {number} cx X-axis coordinate of the center of the ellipse
* @param {number} cy Y-axis coordinate of the center of the ellipse
* @param {number} rx X-radius of the ellipse
* @param {number} ry Y-radius of the ellipse
* @param {number} [rotation=0] Rotation angle of the ellipse (in radians), counterclockwise from the positive X-axis
* @param {number} [startAngle=0] Start angle of the arc (in radians)
* @param {number} [endAngle=2*PI] End angle of the arc (in radians)
* @param {boolean} [counterclockwise=false] Flag indicating the direction of the arc
*/
constructor(cx, cy, rx, ry, rotation = 0, startAngle = 0, endAngle = TWO_PI, counterclockwise = false) {
super();
this.cx = cx;
this.cy = cy;
this.rx = rx;
this.ry = ry;
this.rotation = rotation;
this.startAngle = startAngle;
this.endAngle = endAngle;
this.counterclockwise = counterclockwise;
}
/**
* Interpolate a point on this curve
*
* @abstract
* @param {number} t Normalized time value to interpolate
* @returns {Vector2} Interpolated coordinates on this curve
*/
getPoint(t) {
return new Vector2(...EllipseCurve.interpolate(t, this.cx, this.cy, this.rx, this.ry, this.rotation, this.startAngle, this.endAngle, this.counterclockwise));
}
/**
* Interpolate a point on an elliptical arc
*
* @param {number} t Normalized time value to interpolate
* @param {number} cx X-axis coordinate of the center of the ellipse
* @param {number} cy Y-axis coordinate of the center of the ellipse
* @param {number} rx X-radius of the ellipse
* @param {number} ry Y-radius of the ellipse
* @param {number} [rotation=0] Rotation angle of the ellipse (in radians), counterclockwise from the positive X-axis
* @param {number} [startAngle=0] Start angle of the arc (in radians)
* @param {number} [endAngle=2*PI] End angle of the arc (in radians)
* @param {boolean} [counterclockwise=false] Flag indicating the direction of the arc
* @returns {Point2} Interpolated coordinates on the arc
*/
static interpolate(t, cx, cy, rx, ry, rotation = 0, startAngle = 0, endAngle = TWO_PI, counterclockwise = false) {
let deltaAngle = endAngle - startAngle;
const isEmpty = Math.abs(deltaAngle) <= EPSILON;
while (deltaAngle < 0)
deltaAngle += TWO_PI;
while (deltaAngle > TWO_PI)
deltaAngle -= TWO_PI;
if (deltaAngle <= EPSILON) {
deltaAngle = isEmpty ? 0 : TWO_PI;
}
if (counterclockwise && !isEmpty) {
deltaAngle = deltaAngle === TWO_PI ? -TWO_PI : deltaAngle - TWO_PI;
}
const angle = startAngle + t * deltaAngle;
let x = cx + rx * Math.cos(angle);
let y = cy + ry * Math.sin(angle);
if (Math.abs(rotation) > EPSILON) {
const cos = Math.cos(rotation);
const sin = Math.sin(rotation);
const dx = x - cx;
const dy = y - cy;
x = cx + dx * cos - dy * sin;
y = cy + dx * sin + dy * cos;
}
return [x, y];
}
}