Allow consecutive scans without refresh by resetting trackers and counting vision rate limits once per card. Add a fixed card guide, widen detection bounds, and correct object-cover overlay math. Co-authored-by: Cursor <cursoragent@cursor.com>
98 lines
2.8 KiB
JavaScript
98 lines
2.8 KiB
JavaScript
/** Trading card aspect ratio (width / height). */
|
|
export const CARD_ASPECT_RATIO = 5 / 7;
|
|
|
|
/**
|
|
* Layout math for a video element using CSS object-cover inside a container.
|
|
*/
|
|
export function computeObjectCoverLayout(videoWidth, videoHeight, containerWidth, containerHeight) {
|
|
if (!videoWidth || !videoHeight || !containerWidth || !containerHeight) {
|
|
return null;
|
|
}
|
|
|
|
const videoAspect = videoWidth / videoHeight;
|
|
const containerAspect = containerWidth / containerHeight;
|
|
|
|
let renderedWidth;
|
|
let renderedHeight;
|
|
let offsetX;
|
|
let offsetY;
|
|
|
|
if (videoAspect > containerAspect) {
|
|
renderedHeight = containerHeight;
|
|
renderedWidth = containerHeight * videoAspect;
|
|
offsetX = (containerWidth - renderedWidth) / 2;
|
|
offsetY = 0;
|
|
} else {
|
|
renderedWidth = containerWidth;
|
|
renderedHeight = containerWidth / videoAspect;
|
|
offsetX = 0;
|
|
offsetY = (containerHeight - renderedHeight) / 2;
|
|
}
|
|
|
|
return {
|
|
videoWidth,
|
|
videoHeight,
|
|
containerWidth,
|
|
containerHeight,
|
|
renderedWidth,
|
|
renderedHeight,
|
|
offsetX,
|
|
offsetY,
|
|
};
|
|
}
|
|
|
|
/** Map video-pixel bounds to percentage styles within an object-cover container. */
|
|
export function videoBoundsToContainerStyle(bounds, layout) {
|
|
const {
|
|
videoWidth,
|
|
videoHeight,
|
|
containerWidth,
|
|
containerHeight,
|
|
renderedWidth,
|
|
renderedHeight,
|
|
offsetX,
|
|
offsetY,
|
|
} = layout;
|
|
|
|
const left = offsetX + (bounds.x / videoWidth) * renderedWidth;
|
|
const top = offsetY + (bounds.y / videoHeight) * renderedHeight;
|
|
const width = (bounds.width / videoWidth) * renderedWidth;
|
|
const height = (bounds.height / videoHeight) * renderedHeight;
|
|
|
|
return {
|
|
left: `${(left / containerWidth) * 100}%`,
|
|
top: `${(top / containerHeight) * 100}%`,
|
|
width: `${(width / containerWidth) * 100}%`,
|
|
height: `${(height / containerHeight) * 100}%`,
|
|
};
|
|
}
|
|
|
|
/** Centered card guide frame in container pixel coordinates. */
|
|
export function computeCardGuideBounds(containerWidth, containerHeight) {
|
|
let width = containerWidth * 0.78;
|
|
let height = width / CARD_ASPECT_RATIO;
|
|
|
|
const maxHeight = containerHeight * 0.72;
|
|
if (height > maxHeight) {
|
|
height = maxHeight;
|
|
width = height * CARD_ASPECT_RATIO;
|
|
}
|
|
|
|
return {
|
|
left: (containerWidth - width) / 2,
|
|
top: (containerHeight - height) / 2,
|
|
width,
|
|
height,
|
|
};
|
|
}
|
|
|
|
/** Card guide as percentage styles for an object-cover container. */
|
|
export function cardGuideToContainerStyle(layout) {
|
|
const guide = computeCardGuideBounds(layout.containerWidth, layout.containerHeight);
|
|
return {
|
|
left: `${(guide.left / layout.containerWidth) * 100}%`,
|
|
top: `${(guide.top / layout.containerHeight) * 100}%`,
|
|
width: `${(guide.width / layout.containerWidth) * 100}%`,
|
|
height: `${(guide.height / layout.containerHeight) * 100}%`,
|
|
};
|
|
}
|