import { Bezier } from 'bezier-js' type TToPercMap = { t: number perc: number }[] const distortedBezier = false as boolean export function createBezierTToPercMap(bezier: Bezier, numPoints: number) { const tToPercMap: TToPercMap = [] let prev = bezier.get(0) const totalLength = bezier.length() let lengthOnCursor = 0 for (let i = 0; i < numPoints; i++) { const t = i / (numPoints - 1) const pt = bezier.get(t) const diff = Math.sqrt((prev.x - pt.x) ** 2 + (prev.y - pt.y) ** 2) lengthOnCursor += diff tToPercMap.push({ // t: distortedBezier ? easingEffects.easeInOutSine(t) : t, t, perc: lengthOnCursor / totalLength, }) prev = pt } return tToPercMap } export function findBestTFromMap(tToPercMap: TToPercMap, percentage: number) { const count = tToPercMap.length if (count === 0) { throw new Error('Unable to sample array - it has no elements') } if (percentage === 0) return 0 if (percentage === 1) return 1 let firstAbovePercIndex = tToPercMap.findIndex(({ perc }) => perc >= percentage) if (firstAbovePercIndex === -1) { firstAbovePercIndex = count - 1 } const firstAbovePerc = tToPercMap[firstAbovePercIndex] const firstBelowPerc = firstAbovePercIndex === 0 ? tToPercMap[0] : tToPercMap[firstAbovePercIndex - 1] const diff = firstAbovePerc.perc - firstBelowPerc.perc const progressBetween = (percentage - firstBelowPerc.perc) / diff return firstBelowPerc.t + (firstAbovePerc.t - firstBelowPerc.t) * progressBetween } const findBestPercFromMap = (tToPercMap: TToPercMap, t: number): number => { const count = tToPercMap.length if (count === 0) { throw new Error('Unable to sample array - it has no elements') } if (t === 0) return 0 if (t === 1) return 1 let firstAboveTIndex = tToPercMap.findIndex(({ t: currT }) => currT >= t) if (firstAboveTIndex === -1) { firstAboveTIndex = count - 1 } const firstAboveT = tToPercMap[firstAboveTIndex] const firstBelowT = firstAboveTIndex === 0 ? tToPercMap[0] : tToPercMap[firstAboveTIndex - 1] const diff = firstAboveT.t - firstBelowT.t const progressBetween = (t - firstBelowT.t) / diff return firstBelowT.perc + (firstAboveT.perc - firstBelowT.perc) * progressBetween } export const BEZIER_PRECISION = 100 export const getAdjustedT = (bezier: Bezier, percentage: number) => { const numPoints = BEZIER_PRECISION const tToPercMap = createBezierTToPercMap(bezier, numPoints) const adjustedT = findBestTFromMap(tToPercMap, percentage) return adjustedT } export const reverseBezier = (bezier: Bezier) => { return new Bezier(bezier.points[3], bezier.points[2], bezier.points[1], bezier.points[0]) } export const getAdjustedPerc = (bezier: Bezier, t: number) => { const numPoints = BEZIER_PRECISION const tToPercMap = createBezierTToPercMap(bezier, numPoints) const adjustedPerc = findBestPercFromMap(tToPercMap, t) return adjustedPerc }