import { Injectable } from '@angular/core'; import { BorderUnit, CM_PER_INCH, DimensionsInterface, EnclosingQuadrilateral, ImageEditorImageInterface, MaskImageMapInterface, PIGMaskInterface, PIGPrintGuideAreaInterface, } from './../../../models/index'; @Injectable() export class CanvasUtilitiesService { public getScaledEnclosingQuad( containerDimensions: DimensionsInterface, maskImageDimensions: DimensionsInterface, {mask, imageEditorImage}: MaskImageMapInterface, assetSize: DimensionsInterface, dpi: number, ): EnclosingQuadrilateral { const borderInPx = this._getBorderInPx(imageEditorImage, mask, assetSize, dpi); const bleed = mask.bleed_area.points; const xPoints = []; const yPoints = []; bleed.forEach( (point) => { xPoints.push(...(point.map((i) => i.x))); yPoints.push(...(point.map((i) => i.y))); }, ); const minX = Math.min(...xPoints) + borderInPx; const maxX = Math.max(...xPoints) - borderInPx; const minY = Math.min(...yPoints) + borderInPx; const maxY = Math.max(...yPoints) - borderInPx; const scale = this.calculateScale(containerDimensions, maskImageDimensions, mask); const isXOrYLargerThanMask = (maxX - minX) > maskImageDimensions.width || (maxY - minY) > maskImageDimensions.height; // Flip coord system too as pillow in python is opposite to html5 canvas const xoff = ( containerDimensions.width - ( ( isXOrYLargerThanMask ? maxX : maskImageDimensions.width ) * scale ) ) / 2; const yoff = ( containerDimensions.height - ( ( isXOrYLargerThanMask ? maxY : maskImageDimensions.height ) * scale ) ) / 2; const topLeftX = xoff + minX * scale; const topLeftY = yoff + minY * scale; const topRightX = xoff + maxX * scale; const topRightY = yoff + minY * scale; const bottomRightX = xoff + maxX * scale; const bottomRightY = yoff + maxY * scale; const bottomLeftX = xoff + minX * scale; const bottomLeftY = yoff + maxY * scale; return { topLeft: {x: topLeftX, y: topLeftY}, topRight: {x: topRightX, y: topRightY}, bottomRight: {x: bottomRightX, y: bottomRightY}, bottomLeft: {x: bottomLeftX, y: bottomLeftY}, size: { height: Math.abs(bottomRightY - topLeftY), width: Math.abs(bottomRightX - topLeftX), }, borderInPx: borderInPx * scale, }; } private _getBorderInPx = (imageEditorImage: ImageEditorImageInterface, mask: PIGMaskInterface, assetSize: DimensionsInterface, dpi: number): number => { if (!imageEditorImage.borderWidth || imageEditorImage.borderUnit === BorderUnit.None) { return 0; } const scale = assetSize.width / mask.enclosing_quadrilateral.size.width; const borderInInch = this._getBorderInInches(imageEditorImage); return borderInInch * dpi / scale; } private _getBorderInInches = (imageEditorImage: ImageEditorImageInterface): number => { if (imageEditorImage.borderUnit === BorderUnit.Inch) { return imageEditorImage.borderWidth; } else if (imageEditorImage.borderUnit === BorderUnit.Cm) { return imageEditorImage.borderWidth / CM_PER_INCH; } else { throw new Error(`Border unit ${imageEditorImage.borderUnit} is invalid.`); } } public calculateScale(containerDimensions: DimensionsInterface, maskImageDimensions: DimensionsInterface, mask: PIGMaskInterface): number { const isBleedLargerThanMask = mask.bleed_area.widthAdjustedForNegativeBleed > maskImageDimensions.width || mask.bleed_area.heightAdjustedForNegativeBleed > maskImageDimensions.height; const hRatio = containerDimensions.width / ( isBleedLargerThanMask ? mask.bleed_area.widthAdjustedForNegativeBleed : maskImageDimensions.width ); const vRatio = containerDimensions.height / ( isBleedLargerThanMask ? mask.bleed_area.heightAdjustedForNegativeBleed : maskImageDimensions.height ); return Math.min(hRatio, vRatio); } public getImageQuadCoords( containerDimensions: DimensionsInterface, maskImageDimensions: DimensionsInterface, userImageDimensions: DimensionsInterface, assetDimensions: DimensionsInterface, maskImageMap: MaskImageMapInterface, dpi: number, ) { const userImageTranslations = { x: maskImageMap.imageEditorImage.tx, y: maskImageMap.imageEditorImage.ty, }; const containerQuad = this.getScaledEnclosingQuad(containerDimensions, maskImageDimensions, maskImageMap, assetDimensions, dpi); const containerQuadMid = { x: containerQuad.topLeft.x + containerQuad.size.width / 2, y: containerQuad.topLeft.y + containerQuad.size.height / 2, }; const widthWithBorder = (containerQuad.size.width + (containerQuad.borderInPx * 2)); const heightWithBorder = (containerQuad.size.height + (containerQuad.borderInPx * 2)); const scaleX = widthWithBorder / assetDimensions.width; const scaleY = heightWithBorder / assetDimensions.height; const imageRatio = userImageDimensions.width / userImageDimensions.height; const containerRatio = containerQuad.size.width / containerQuad.size.height; const scale = Math.max(scaleX, scaleY); const printCoords = { x: scale * userImageTranslations.x, y: scale * userImageTranslations.y, }; let imageHeight: number; let imageWidth: number; if (imageRatio > containerRatio) { imageHeight = maskImageMap.imageEditorImage.scale * containerQuad.size.height; imageWidth = imageHeight * imageRatio; } else { imageWidth = maskImageMap.imageEditorImage.scale * containerQuad.size.width; imageHeight = imageWidth / imageRatio; } const halfImageWidth = imageWidth / 2; const halfImageHeight = imageHeight / 2; const leftMostX = containerQuadMid.x + printCoords.x - halfImageWidth; const topMostY = containerQuadMid.y + printCoords.y - halfImageHeight; const rightMostX = leftMostX + imageWidth; const bottomMostY = topMostY + imageHeight; return [ { x: leftMostX, y: topMostY, }, { x: rightMostX, y: topMostY, }, { x: rightMostX, y: bottomMostY, }, { x: leftMostX, y: bottomMostY, }, ]; } public toCanvasCoordinateSystem( containerDimensions: DimensionsInterface, coord: number, maskImageMap: MaskImageMapInterface, maskImageDimensions: DimensionsInterface, assetDimensions: DimensionsInterface, dpi: number, ) { const quad = this.getScaledEnclosingQuad(containerDimensions, maskImageDimensions, maskImageMap, assetDimensions, dpi); const widthWithBorder = (quad.size.width + (quad.borderInPx * 2)); const heightWithBorder = (quad.size.height + (quad.borderInPx * 2)); const scaleX = widthWithBorder / assetDimensions.width; const scaleY = heightWithBorder / assetDimensions.height; const scale = Math.max(scaleX, scaleY); return Math.round(scale * coord); } public toPrintImageCoordinateSystem( containerDimensions: DimensionsInterface, coord: number, maskImageMap: MaskImageMapInterface, maskImageDimensions: DimensionsInterface, assetDimensions: DimensionsInterface, dpi: number, ) { const quad = this.getScaledEnclosingQuad(containerDimensions, maskImageDimensions, maskImageMap, assetDimensions, dpi); const widthWithBorder = (quad.size.width + (quad.borderInPx * 2)); const heightWithBorder = (quad.size.height + (quad.borderInPx * 2)); const scaleX = assetDimensions.width / widthWithBorder; const scaleY = assetDimensions.height / heightWithBorder; const scale = Math.min(scaleX, scaleY); return Math.round(scale * coord); } public getAdjustedGuideAreaPoints( printGuideArea: PIGPrintGuideAreaInterface, fitWidth: number, fitHeight: number, totalWidth: number, totalHeight: number, ) { const scaledWidth = fitWidth / totalWidth; const scaledHeight = fitHeight / totalHeight; const scaleFactor = scaledWidth > scaledHeight ? scaledHeight : scaledWidth; const adjustX = (fitWidth - (totalWidth * scaleFactor)) / 2; const adjustY = (fitHeight - (totalHeight * scaleFactor)) / 2; return printGuideArea.points.map((pointsArray) => { return pointsArray.map((point) => { const scaledX = point.x * scaleFactor + adjustX; const scaledY = point.y * scaleFactor + adjustY; return { x: scaledX, y: scaledY, }; }); }); } public getImageWithUrl( allImages: HTMLImageElement[], url: string, ) { return allImages.find((image) => ( image.src === url )); } }