react-native-reanimated
Version:
More powerful alternative to Animated library for React Native.
127 lines (125 loc) • 5.17 kB
JavaScript
'use strict';
export const PATH_COMMAND_LENGTHS = {
a: 7,
c: 6,
h: 1,
l: 2,
m: 2,
q: 4,
s: 4,
t: 2,
v: 1,
z: 0
};
export const SEGMENT_PATTERN = /([achlmqstvz])([^achlmqstvz]*)/gi;
export const NUMBER_PATTERN = /-?[0-9]*\.?[0-9]+(?:e[-+]?\d+)?/gi;
export function reflectControlPoint(curX, curY, oldX, oldY) {
return [2 * curX - oldX, 2 * curY - oldY];
}
export function lineToCubic(x1, y1, x2, y2) {
return [x1 + (x2 - x1) / 3, y1 + (y2 - y1) / 3, x1 + 2 * (x2 - x1) / 3, y1 + 2 * (y2 - y1) / 3, x2, y2];
}
export function quadraticToCubic(curX, curY, qcx, qcy, x, y) {
const cp1x = curX + 2 / 3 * (qcx - curX);
const cp1y = curY + 2 / 3 * (qcy - curY);
const cp2x = x + 2 / 3 * (qcx - x);
const cp2y = y + 2 / 3 * (qcy - y);
return [cp1x, cp1y, cp2x, cp2y, x, y];
}
export function arcToCubic(startX, startY, radiusX, radiusY, xAxisRotation, largeArcFlag, sweepFlag, endX, endY) {
// Identical endpoints -- omit the segment
if (startX === endX && startY === endY) {
return [];
}
radiusX = Math.abs(radiusX);
radiusY = Math.abs(radiusY);
// Degenerate case:
if (radiusX === 0 || radiusY === 0) {
return [lineToCubic(startX, startY, endX, endY)];
}
const fA = largeArcFlag !== 0 ? 1 : 0;
const fS = sweepFlag !== 0 ? 1 : 0;
const phi = xAxisRotation % 360 * Math.PI / 180;
const sinPhi = Math.sin(phi);
const cosPhi = Math.cos(phi);
const {
centerX,
centerY,
startAngle,
deltaAngle,
correctedRadiusX,
correctedRadiusY
} = getCenterParameterization(startX, startY, radiusX, radiusY, fA, fS, endX, endY, sinPhi, cosPhi);
return splitArcIntoSegments(centerX, centerY, startAngle, deltaAngle, correctedRadiusX, correctedRadiusY, sinPhi, cosPhi);
}
function getCenterParameterization(startX, startY, radiusX, radiusY, largeArcFlag, sweepFlag, endX, endY, sinRotation, cosRotation) {
const xPrime = cosRotation * (startX - endX) / 2 + sinRotation * (startY - endY) / 2;
const yPrime = -sinRotation * (startX - endX) / 2 + cosRotation * (startY - endY) / 2;
const xPrimeSq = xPrime * xPrime;
const yPrimeSq = yPrime * yPrime;
let correctedRadiusX = radiusX;
let correctedRadiusY = radiusY;
const radCheck = xPrimeSq / (correctedRadiusX * correctedRadiusX) + yPrimeSq / (correctedRadiusY * correctedRadiusY);
if (radCheck > 1) {
const scale = Math.sqrt(radCheck);
correctedRadiusX *= scale;
correctedRadiusY *= scale;
}
const crxSq = correctedRadiusX * correctedRadiusX;
const crySq = correctedRadiusY * correctedRadiusY;
const sign = largeArcFlag === sweepFlag ? -1 : 1;
const numerator = Math.max(0, crxSq * crySq - crxSq * yPrimeSq - crySq * xPrimeSq);
const denominator = crxSq * yPrimeSq + crySq * xPrimeSq;
const coef = sign * Math.sqrt(numerator / denominator);
const cxp = coef * (correctedRadiusX * yPrime / correctedRadiusY);
const cyp = coef * (-(correctedRadiusY * xPrime) / correctedRadiusX);
const centerX = cosRotation * cxp - sinRotation * cyp + (startX + endX) / 2;
const centerY = sinRotation * cxp + cosRotation * cyp + (startY + endY) / 2;
const startVectorX = (xPrime - cxp) / correctedRadiusX;
const startVectorY = (yPrime - cyp) / correctedRadiusY;
const endVectorX = (-xPrime - cxp) / correctedRadiusX;
const endVectorY = (-yPrime - cyp) / correctedRadiusY;
const startAngle = calculateAngle(1, 0, startVectorX, startVectorY);
let deltaAngle = calculateAngle(startVectorX, startVectorY, endVectorX, endVectorY);
if (sweepFlag === 0 && deltaAngle > 0) deltaAngle -= 2 * Math.PI;
if (sweepFlag === 1 && deltaAngle < 0) deltaAngle += 2 * Math.PI;
return {
centerX,
centerY,
startAngle,
deltaAngle,
correctedRadiusX,
correctedRadiusY
};
}
function calculateAngle(uX, uY, vX, vY) {
const dot = uX * vX + uY * vY;
const mag = Math.sqrt(uX * uX + uY * uY) * Math.sqrt(vX * vX + vY * vY);
const sign = uX * vY - uY * vX < 0 ? -1 : 1;
return sign * Math.acos(Math.max(-1, Math.min(1, dot / mag)));
}
function splitArcIntoSegments(centerX, centerY, startAngle, deltaAngle, radiusX, radiusY, sinRotation, cosRotation) {
const segments = Math.ceil(Math.abs(deltaAngle) / (Math.PI / 2));
const segmentAngle = deltaAngle / segments;
const k = 4 / 3 * Math.tan(segmentAngle / 4);
const cubics = [];
for (let i = 0; i < segments; i++) {
const a1 = startAngle + i * segmentAngle;
const a2 = startAngle + (i + 1) * segmentAngle;
const cos1 = Math.cos(a1),
sin1 = Math.sin(a1);
const cos2 = Math.cos(a2),
sin2 = Math.sin(a2);
const unitX1 = cos1 - k * sin1;
const unitY1 = sin1 + k * cos1;
const unitX2 = cos2 + k * sin2;
const unitY2 = sin2 - k * cos2;
const transformToEllipse = (uX, uY) => [centerX + cosRotation * uX * radiusX - sinRotation * uY * radiusY, centerY + sinRotation * uX * radiusX + cosRotation * uY * radiusY];
const [cp1x, cp1y] = transformToEllipse(unitX1, unitY1);
const [cp2x, cp2y] = transformToEllipse(unitX2, unitY2);
const [dstX, dstY] = transformToEllipse(cos2, sin2);
cubics.push([cp1x, cp1y, cp2x, cp2y, dstX, dstY]);
}
return cubics;
}
//# sourceMappingURL=path.js.map