Rocky_Mountain_Vending/.pnpm-store/v10/files/0b/6726157824a79a8c20ebf3be119212ac2bd7b3e1973c75b7ca7f45a556bde2dfe8a25a41d7cf1b66d6b7167b3f92d65f334df2961018a738a667edf0d67c47
DMleadgen 46d973904b
Initial commit: Rocky Mountain Vending website
Next.js website for Rocky Mountain Vending company featuring:
- Product catalog with Stripe integration
- Service areas and parts pages
- Admin dashboard with Clerk authentication
- SEO optimized pages with JSON-LD structured data

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 16:22:15 -07:00

270 lines
8.2 KiB
Text

'use strict';
const imageSSIM = require('image-ssim');
/* BEGIN FAST MODE CONSTANTS - See function doc for explanation */
const fastModeAllowableChangeMax = 5;
const fastModeAllowableChangeMedian = 3;
const fastModeAllowableChangeMin = -1;
const fastModeConstant = fastModeAllowableChangeMin;
const fastModeMultiplier = fastModeAllowableChangeMax - fastModeConstant;
const fastModeExponentiationCoefficient = Math.log((fastModeAllowableChangeMedian - fastModeConstant) / fastModeMultiplier);
/* END FAST MODE CONSTANTS - See function doc for explanation */
/** @typedef {import('../speedline').Output['frames'][number]} Frame */
/**
* This computes the allowed percentage of change between two frames in fast mode where we won't examine the frames in between them.
* It follows an exponential function such that:
* - We allow up to FAST_MODE_ALLOWABLE_CHANGE_MAX percent difference when the frames are ~0s apart.
* - We allow up to FAST_MODE_ALLOWABLE_CHANGE_MEDIAN percent difference when the frames are ~1s apart.
* - We allow up to FAST_MODE_ALLOWABLE_CHANGE_MIN percent difference when the frames are very far apart.
*
* f(t) = FAST_MODE_MULTIPLIER * e^(FAST_MODE_EXPONENTIATION_COEFFICIENT * t) + FAST_MODE_CONSTANT
* @param {number} elapsedTime
*/
function calculateFastModeAllowableChange(elapsedTime) {
const elapsedTimeInSeconds = elapsedTime / 1000;
const allowableChange = fastModeMultiplier * Math.exp(fastModeExponentiationCoefficient * elapsedTimeInSeconds) + fastModeConstant;
return allowableChange;
}
/**
* @param {Frame} current
* @param {Frame} initial
* @param {Frame} target
*/
function calculateFrameProgress(current, initial, target) {
let total = 0;
let match = 0;
const currentHist = current.getHistogram();
const initialHist = initial.getHistogram();
const targetHist = target.getHistogram();
for (let channel = 0; channel < 3; channel++) {
for (let pixelVal = 0; pixelVal < 256; pixelVal++) {
const currentCount = currentHist[channel][pixelVal];
const initialCount = initialHist[channel][pixelVal];
const targetCount = targetHist[channel][pixelVal];
const currentDiff = Math.abs(currentCount - initialCount);
const targetDiff = Math.abs(targetCount - initialCount);
match += Math.min(currentDiff, targetDiff);
total += targetDiff;
}
}
let progress;
if (match === 0 && total === 0) { // All images are the same
progress = 100;
} else { // When images differs
progress = Math.floor(match / total * 100);
}
return progress;
}
/**
* @param {Array<Frame>} frames
* @param {number} lowerBound
* @param {number} upperBound
* @param {boolean} isFastMode
* @param {function(Frame): number} getProgress
* @param {function(Frame, number, boolean): void} setProgress
*/
function calculateProgressBetweenFrames(frames, lowerBound, upperBound, isFastMode, getProgress, setProgress) {
if (!isFastMode) {
frames.forEach(frame => setProgress(frame, getProgress(frame), false));
return;
}
const lowerFrame = frames[lowerBound];
const upperFrame = frames[upperBound];
const elapsedTime = upperFrame.getTimeStamp() - lowerFrame.getTimeStamp();
const lowerProgress = getProgress(lowerFrame);
const upperProgress = getProgress(upperFrame);
setProgress(lowerFrame, lowerProgress, false);
setProgress(upperFrame, upperProgress, false);
if (Math.abs(lowerProgress - upperProgress) < calculateFastModeAllowableChange(elapsedTime)) {
for (let i = lowerBound + 1; i < upperBound; i++) {
setProgress(frames[i], lowerProgress, true);
}
} else if (upperBound - lowerBound > 1) {
const midpoint = Math.floor((lowerBound + upperBound) / 2);
calculateProgressBetweenFrames(frames, lowerBound, midpoint, isFastMode, getProgress, setProgress);
calculateProgressBetweenFrames(frames, midpoint, upperBound, isFastMode, getProgress, setProgress);
}
}
/**
* @param {Array<Frame>} frames
* @param {{fastMode?: boolean}} opts
*/
function calculateVisualProgress(frames, opts) {
const initial = frames[0];
const target = frames[frames.length - 1];
/** @param {Frame} frame */
function getProgress(frame) {
if (typeof frame.getProgress() === 'number') {
return frame.getProgress();
}
return calculateFrameProgress(frame, initial, target);
}
/**
* @param {Frame} frame
* @param {number} progress
* @param {boolean} isInterpolated
*/
function setProgress(frame, progress, isInterpolated) {
return frame.setProgress(progress, isInterpolated);
}
calculateProgressBetweenFrames(
frames,
0,
frames.length - 1,
opts && opts.fastMode,
getProgress,
setProgress
);
return frames;
}
/**
* @param {Frame} frame
* @param {Frame} target
* @return {number}
*/
function calculateFrameSimilarity(frame, target) {
const defaultImageConfig = {
// image-ssim uses this to interpret the arraybuffer NOT the desired channels to consider
// jpeg-js encodes each pixel with an alpha channel set to 0xFF, so 4 channel interpretation is required
channels: 4
};
const frameData = Object.assign(frame.getParsedImage(), defaultImageConfig);
const targetData = Object.assign(target.getParsedImage(), defaultImageConfig);
const diff = imageSSIM.compare(frameData, targetData);
return diff.ssim;
}
/**
* @param {Array<Frame>} frames
* @param {{fastMode?: boolean}} opts
*/
function calculatePerceptualProgress(frames, opts) {
const initial = frames[0];
const target = frames[frames.length - 1];
const initialSimilarity = calculateFrameSimilarity(initial, target);
/** @param {Frame} frame */
function getProgress(frame) {
if (typeof frame.getPerceptualProgress() === 'number') {
return frame.getPerceptualProgress();
}
const ssim = calculateFrameSimilarity(frame, target);
return Math.max(100 * (ssim - initialSimilarity) / (1 - initialSimilarity), 0);
}
/**
* @param {Frame} frame
* @param {number} progress
* @param {boolean} isInterpolated
*/
function setProgress(frame, progress, isInterpolated) {
return frame.setPerceptualProgress(progress, isInterpolated);
}
calculateProgressBetweenFrames(
frames,
0,
frames.length - 1,
opts && opts.fastMode,
getProgress,
setProgress
);
return frames;
}
/**
* @param {Array<Frame>} frames
* @param {{startTs: number}} data
* @return {{firstPaintTs: number, visuallyCompleteTs: number, speedIndex?: number, perceptualSpeedIndex?: number}}
*/
function calculateSpeedIndexes(frames, data) {
const hasVisualProgress = typeof frames[0].getProgress() === 'number';
const hasPerceptualProgress = typeof frames[0].getPerceptualProgress() === 'number';
const progressToUse = hasVisualProgress ? 'getProgress' : 'getPerceptualProgress';
const startTs = data.startTs;
let visuallyCompleteTs;
/** @type {number|undefined} */
let firstPaintTs;
// find first paint
for (let i = 0; i < frames.length && !firstPaintTs; i++) {
if (frames[i][progressToUse]() > 0) {
firstPaintTs = frames[i].getTimeStamp();
}
}
// find visually complete
for (let i = 0; i < frames.length && !visuallyCompleteTs; i++) {
if (frames[i][progressToUse]() >= 100) {
visuallyCompleteTs = frames[i].getTimeStamp();
}
}
let prevFrameTs = frames[0].getTimeStamp();
let prevProgress = frames[0].getProgress();
let prevPerceptualProgress = frames[0].getPerceptualProgress();
// SI = firstPaint + sum(fP to VC){1-VC%}
// github.com/pmdartus/speedline/issues/28#issuecomment-244127192
/** @type {number|undefined} */
let speedIndex = firstPaintTs - startTs;
/** @type {number|undefined} */
let perceptualSpeedIndex = firstPaintTs - startTs;
frames.forEach(function (frame) {
// skip frames from 0 to fP
if (frame.getTimeStamp() > firstPaintTs) {
const elapsed = frame.getTimeStamp() - prevFrameTs;
speedIndex += elapsed * (1 - prevProgress);
perceptualSpeedIndex += elapsed * (1 - prevPerceptualProgress);
}
prevFrameTs = frame.getTimeStamp();
prevProgress = frame.getProgress() / 100;
prevPerceptualProgress = frame.getPerceptualProgress() / 100;
});
speedIndex = hasVisualProgress ? speedIndex : undefined;
perceptualSpeedIndex = hasPerceptualProgress ? perceptualSpeedIndex : undefined;
return {
firstPaintTs,
visuallyCompleteTs,
speedIndex,
perceptualSpeedIndex
};
}
module.exports = {
calculateFastModeAllowableChange,
calculateFrameSimilarity,
calculateVisualProgress,
calculatePerceptualProgress,
calculateSpeedIndexes
};