UNPKG

@prodbirdy/mockup-generator

Version:

Serverless-optimized TypeScript SDK for generating high-quality product mockups from PSD templates

168 lines (147 loc) 5.11 kB
import type { Psd } from "ag-psd"; import type { Guide, GuideConstraints } from "../constants"; import { printInfo, printWarning } from "./formatting"; /** * Extract guides from PSD image resources */ export function extractGuides(psd: Psd): Guide[] { const guides: Guide[] = []; if (psd.imageResources?.gridAndGuidesInformation?.guides) { return psd.imageResources.gridAndGuidesInformation.guides; } return guides; } /** * Find constraint area defined by guides within smart object bounds * Assumes guides define a rectangular constraint area */ export function findGuideConstraints( guides: Guide[], smartObjectBounds: { left: number; top: number; right: number; bottom: number; }, verbose: boolean = false ): GuideConstraints | null { if (guides.length === 0) { if (verbose) printWarning("No guides found in PSD"); return null; } // Convert smart object bounds to absolute coordinates if needed const absoluteBounds = { left: smartObjectBounds.left, top: smartObjectBounds.top, right: smartObjectBounds.right, bottom: smartObjectBounds.bottom, }; // Find guides that intersect with smart object area const relevantGuides = guides.filter((guide) => { if (guide.direction === "vertical") { return ( guide.location >= absoluteBounds.left && guide.location <= absoluteBounds.right ); } else { return ( guide.location >= absoluteBounds.top && guide.location <= absoluteBounds.bottom ); } }); if (relevantGuides.length === 0) { if (verbose) printWarning("No guides found within smart object bounds"); return null; } // Extract constraint boundaries const verticalGuides = relevantGuides .filter((g) => g.direction === "vertical") .map((g) => g.location) .sort((a, b) => a - b); const horizontalGuides = relevantGuides .filter((g) => g.direction === "horizontal") .map((g) => g.location) .sort((a, b) => a - b); const constraints: GuideConstraints = {}; // Find leftmost and rightmost vertical guides if (verticalGuides.length >= 2) { constraints.left = Math.max(verticalGuides[0], absoluteBounds.left) - absoluteBounds.left; constraints.right = Math.min( verticalGuides[verticalGuides.length - 1], absoluteBounds.right ) - absoluteBounds.left; } else if (verticalGuides.length === 1) { // Single vertical guide - use it as center reference const guidePos = verticalGuides[0] - absoluteBounds.left; const width = absoluteBounds.right - absoluteBounds.left; constraints.left = Math.max(0, guidePos - width * 0.25); constraints.right = Math.min(width, guidePos + width * 0.25); } // Find topmost and bottommost horizontal guides if (horizontalGuides.length >= 2) { constraints.top = Math.max(horizontalGuides[0], absoluteBounds.top) - absoluteBounds.top; constraints.bottom = Math.min( horizontalGuides[horizontalGuides.length - 1], absoluteBounds.bottom ) - absoluteBounds.top; } else if (horizontalGuides.length === 1) { // Single horizontal guide - use it as center reference const guidePos = horizontalGuides[0] - absoluteBounds.top; const height = absoluteBounds.bottom - absoluteBounds.top; constraints.top = Math.max(0, guidePos - height * 0.25); constraints.bottom = Math.min(height, guidePos + height * 0.25); } if (verbose && Object.keys(constraints).length > 0) { printInfo(`Found guide constraints: ${JSON.stringify(constraints)}`); } return Object.keys(constraints).length > 0 ? constraints : null; } /** * Calculate constrained dimensions and position for image placement */ export function calculateConstrainedPlacement( imageWidth: number, imageHeight: number, constraints: GuideConstraints, smartObjectWidth: number, smartObjectHeight: number ): { width: number; height: number; offsetX: number; offsetY: number; constraintWidth: number; constraintHeight: number; } { // Default to full smart object if no constraints const constraintLeft = constraints.left ?? 0; const constraintTop = constraints.top ?? 0; const constraintRight = constraints.right ?? smartObjectWidth; const constraintBottom = constraints.bottom ?? smartObjectHeight; const constraintWidth = constraintRight - constraintLeft; const constraintHeight = constraintBottom - constraintTop; // Calculate scale to fit within constraints const scaleX = constraintWidth / imageWidth; const scaleY = constraintHeight / imageHeight; const scale = Math.min(scaleX, scaleY); const scaledWidth = Math.round(imageWidth * scale); const scaledHeight = Math.round(imageHeight * scale); // Center within constraint area const offsetX = constraintLeft + Math.round((constraintWidth - scaledWidth) / 2); const offsetY = constraintTop + Math.round((constraintHeight - scaledHeight) / 2); return { width: scaledWidth, height: scaledHeight, offsetX, offsetY, constraintWidth, constraintHeight, }; }