import type Phaser from 'phaser'; const defaultFaceCropRatio = 0.6; const defaultFaceCropTopRatio = 0.035; export type DialogueFacePortraitLayout = { cropX: number; cropY: number; cropSize: number; originX: number; originY: number; scale: number; }; export function calculateDialogueFacePortraitLayout( sourceWidth: number, sourceHeight: number, displaySize: number ): DialogueFacePortraitLayout { if ( !Number.isFinite(sourceWidth) || !Number.isFinite(sourceHeight) || !Number.isFinite(displaySize) || sourceWidth <= 0 || sourceHeight <= 0 || displaySize <= 0 ) { throw new RangeError('Dialogue portrait dimensions must be finite positive numbers.'); } const cropSize = Math.max( 1, Math.round( Math.min(sourceWidth, sourceHeight) * defaultFaceCropRatio ) ); const cropX = Math.round((sourceWidth - cropSize) / 2); const cropY = Math.round( Math.min( Math.max(0, sourceHeight * defaultFaceCropTopRatio), sourceHeight - cropSize ) ); return { cropX, cropY, cropSize, originX: (cropX + cropSize / 2) / sourceWidth, originY: (cropY + cropSize / 2) / sourceHeight, scale: displaySize / cropSize }; } export function fitDialogueFacePortrait( portrait: Phaser.GameObjects.Image, displaySize: number ) { const layout = calculateDialogueFacePortraitLayout( portrait.frame.realWidth, portrait.frame.realHeight, displaySize ); portrait .setCrop( layout.cropX, layout.cropY, layout.cropSize, layout.cropSize ) .setOrigin(layout.originX, layout.originY) .setScale(layout.scale); return layout; }