import type { AgentPsdTemplateImageContentBounds, AgentPsdTemplateReplaceMode, } from "@/lib/agents/psd-template-config"; export interface AgentPsdImagePlacement { scale: number; offsetX: number; offsetY: number; } export interface AgentPsdImageSize { width: number; height: number; } export interface AgentPsdImageFrame { width: number; height: number; left: number; top: number; centerX: number; centerY: number; } export function getDefaultAgentPsdImagePlacement(): AgentPsdImagePlacement { return { scale: 1, offsetX: 0, offsetY: 0, }; } export function normalizeAgentPsdImagePlacement( value: Partial | null | undefined ): AgentPsdImagePlacement { const scale = Number(value?.scale); const offsetX = Number(value?.offsetX); const offsetY = Number(value?.offsetY); return { scale: Number.isFinite(scale) && scale > 0 ? Math.max(0.1, Math.min(8, scale)) : 1, offsetX: Number.isFinite(offsetX) ? offsetX : 0, offsetY: Number.isFinite(offsetY) ? offsetY : 0, }; } function resolveBaseFittedSize(params: { containerWidth: number; containerHeight: number; imageWidth: number; imageHeight: number; replaceMode: AgentPsdTemplateReplaceMode; }) { const { containerWidth, containerHeight, imageWidth, imageHeight, replaceMode } = params; const safeImageWidth = Math.max(1, imageWidth); const safeImageHeight = Math.max(1, imageHeight); if (replaceMode === "stretch") { return { width: containerWidth, height: containerHeight, left: 0, top: 0, }; } const fitRatio = replaceMode === "contain" ? Math.min(containerWidth / safeImageWidth, containerHeight / safeImageHeight) : Math.max(containerWidth / safeImageWidth, containerHeight / safeImageHeight); const width = safeImageWidth * fitRatio; const height = safeImageHeight * fitRatio; return { width, height, left: (containerWidth - width) / 2, top: (containerHeight - height) / 2, }; } function normalizeContainerBounds(params: { containerWidth: number; containerHeight: number; contentBounds?: AgentPsdTemplateImageContentBounds; }) { const { containerWidth, containerHeight, contentBounds } = params; if (!contentBounds) { return { left: 0, top: 0, width: containerWidth, height: containerHeight, }; } const left = Math.max(0, Math.min(containerWidth - 1, Math.round(contentBounds.left))); const top = Math.max(0, Math.min(containerHeight - 1, Math.round(contentBounds.top))); const width = Math.max( 1, Math.min(Math.round(contentBounds.width), Math.max(1, containerWidth - left)) ); const height = Math.max( 1, Math.min(Math.round(contentBounds.height), Math.max(1, containerHeight - top)) ); return { left, top, width, height, }; } export function resolveAgentPsdImageBaseFrame(params: { containerWidth: number; containerHeight: number; imageWidth: number; imageHeight: number; replaceMode: AgentPsdTemplateReplaceMode; contentBounds?: AgentPsdTemplateImageContentBounds; }): AgentPsdImageFrame { const { containerWidth, containerHeight, imageWidth, imageHeight, replaceMode } = params; const contentBounds = normalizeContainerBounds({ containerWidth, containerHeight, contentBounds: params.contentBounds, }); const baseFrame = resolveBaseFittedSize({ containerWidth, containerHeight, imageWidth, imageHeight, replaceMode, }); const anchorCenterX = contentBounds.left + contentBounds.width / 2; const anchorCenterY = contentBounds.top + contentBounds.height / 2; return { width: baseFrame.width, height: baseFrame.height, left: anchorCenterX - baseFrame.width / 2, top: anchorCenterY - baseFrame.height / 2, centerX: anchorCenterX, centerY: anchorCenterY, }; } export function resolveAgentPsdImagePlacementFrame(params: { containerWidth: number; containerHeight: number; imageWidth: number; imageHeight: number; replaceMode: AgentPsdTemplateReplaceMode; contentBounds?: AgentPsdTemplateImageContentBounds; placement?: Partial | null; }): AgentPsdImageFrame { const placement = normalizeAgentPsdImagePlacement(params.placement); const baseFrame = resolveAgentPsdImageBaseFrame(params); const width = baseFrame.width * placement.scale; const height = baseFrame.height * placement.scale; return { width, height, left: baseFrame.centerX + placement.offsetX - width / 2, top: baseFrame.centerY + placement.offsetY - height / 2, centerX: baseFrame.centerX + placement.offsetX, centerY: baseFrame.centerY + placement.offsetY, }; }