77 lines
1.6 KiB
TypeScript
77 lines
1.6 KiB
TypeScript
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;
|
|
}
|