From 0de9aabb720c4e97184041f002f5e0500ffef365 Mon Sep 17 00:00:00 2001 From: Dariusz L Date: Wed, 30 Jul 2025 09:52:07 +0200 Subject: [PATCH] Hide pending generation areas --- js/BatchPreviewManager.js | 2 + js/CanvasRenderer.js | 85 +++++++++++++++++++++++++--------- src/BatchPreviewManager.ts | 3 ++ src/CanvasRenderer.ts | 93 ++++++++++++++++++++++++++++---------- src/utils/IconLoader.ts | 2 +- 5 files changed, 140 insertions(+), 45 deletions(-) diff --git a/js/BatchPreviewManager.js b/js/BatchPreviewManager.js index c84446f..19f49a6 100644 --- a/js/BatchPreviewManager.js +++ b/js/BatchPreviewManager.js @@ -179,6 +179,8 @@ export class BatchPreviewManager { this.layers.forEach((layer) => { layer.visible = true; }); + // Clear selection - deselect all layers + this.canvas.updateSelection([]); // Update the layers panel to reflect visibility changes if (this.canvas.canvasLayersPanel) { this.canvas.canvasLayersPanel.onLayersChanged(); diff --git a/js/CanvasRenderer.js b/js/CanvasRenderer.js index 1bf07af..49a129a 100644 --- a/js/CanvasRenderer.js +++ b/js/CanvasRenderer.js @@ -261,6 +261,45 @@ export class CanvasRenderer { } ctx.stroke(); } + /** + * Check if custom shape overlaps with any active batch preview areas + */ + isCustomShapeOverlappingWithBatchAreas() { + if (!this.canvas.outputAreaShape || !this.canvas.batchPreviewManagers || this.canvas.batchPreviewManagers.length === 0) { + return false; + } + // Get custom shape bounds + const bounds = this.canvas.outputAreaBounds; + const ext = this.canvas.outputAreaExtensionEnabled ? this.canvas.outputAreaExtensions : { top: 0, bottom: 0, left: 0, right: 0 }; + const shapeOffsetX = bounds.x + ext.left; + const shapeOffsetY = bounds.y + ext.top; + const shape = this.canvas.outputAreaShape; + let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity; + // Calculate shape bounding box + shape.points.forEach((point) => { + const worldX = shapeOffsetX + point.x; + const worldY = shapeOffsetY + point.y; + minX = Math.min(minX, worldX); + maxX = Math.max(maxX, worldX); + minY = Math.min(minY, worldY); + maxY = Math.max(maxY, worldY); + }); + const shapeBounds = { x: minX, y: minY, width: maxX - minX, height: maxY - minY }; + // Check overlap with each active batch preview area + for (const manager of this.canvas.batchPreviewManagers) { + if (manager.generationArea) { + const area = manager.generationArea; + // Check if rectangles overlap + if (!(shapeBounds.x + shapeBounds.width < area.x || + area.x + area.width < shapeBounds.x || + shapeBounds.y + shapeBounds.height < area.y || + area.y + area.height < shapeBounds.y)) { + return true; // Overlap detected + } + } + } + return false; + } drawCanvasOutline(ctx) { ctx.beginPath(); ctx.strokeStyle = 'rgba(255, 255, 255, 0.8)'; @@ -276,7 +315,8 @@ export class CanvasRenderer { const textWorldX = bounds.x + bounds.width / 2; const textWorldY = bounds.y + bounds.height + (20 / this.canvas.viewport.zoom); this.drawTextWithBackground(ctx, dimensionsText, textWorldX, textWorldY); - if (this.canvas.outputAreaShape) { + // Only draw custom shape if it doesn't overlap with batch preview areas + if (this.canvas.outputAreaShape && !this.isCustomShapeOverlappingWithBatchAreas()) { ctx.save(); ctx.strokeStyle = 'rgba(0, 255, 255, 0.9)'; ctx.lineWidth = 2 / this.canvas.viewport.zoom; @@ -351,29 +391,32 @@ export class CanvasRenderer { }); } drawPendingGenerationAreas(ctx) { - const areasToDraw = []; - // 1. Get areas from active managers - if (this.canvas.batchPreviewManagers && this.canvas.batchPreviewManagers.length > 0) { - this.canvas.batchPreviewManagers.forEach((manager) => { - if (manager.generationArea) { - areasToDraw.push(manager.generationArea); - } - }); - } - // 2. Get the area from the pending context (if it exists) + const pendingAreas = []; + // 1. Get all pending generation areas (from pendingBatchContext) if (this.canvas.pendingBatchContext && this.canvas.pendingBatchContext.outputArea) { - areasToDraw.push(this.canvas.pendingBatchContext.outputArea); + pendingAreas.push(this.canvas.pendingBatchContext.outputArea); } - if (areasToDraw.length === 0) { - return; - } - // 3. Draw all collected areas - areasToDraw.forEach(area => { - this.drawStyledRect(ctx, area, { - strokeStyle: 'rgba(0, 150, 255, 0.9)', - lineWidth: 3, - dashPattern: [12, 6] + // 2. Draw only those pending areas, które NIE mają aktywnego batch preview managera dla tego samego obszaru + const isAreaCoveredByBatch = (area) => { + if (!this.canvas.batchPreviewManagers) + return false; + return this.canvas.batchPreviewManagers.some((manager) => { + if (!manager.generationArea) + return false; + // Sprawdź czy obszary się pokrywają (prosty overlap AABB) + const a = area; + const b = manager.generationArea; + return !(a.x + a.width < b.x || b.x + b.width < a.x || a.y + a.height < b.y || b.y + b.height < a.y); }); + }; + pendingAreas.forEach(area => { + if (!isAreaCoveredByBatch(area)) { + this.drawStyledRect(ctx, area, { + strokeStyle: 'rgba(0, 150, 255, 0.9)', + lineWidth: 3, + dashPattern: [12, 6] + }); + } }); } drawMaskAreaBounds(ctx) { diff --git a/src/BatchPreviewManager.ts b/src/BatchPreviewManager.ts index 1236726..ed7cf14 100644 --- a/src/BatchPreviewManager.ts +++ b/src/BatchPreviewManager.ts @@ -234,6 +234,9 @@ export class BatchPreviewManager { layer.visible = true; }); + // Clear selection - deselect all layers + this.canvas.updateSelection([]); + // Update the layers panel to reflect visibility changes if (this.canvas.canvasLayersPanel) { this.canvas.canvasLayersPanel.onLayersChanged(); diff --git a/src/CanvasRenderer.ts b/src/CanvasRenderer.ts index 2c32b88..e8abdba 100644 --- a/src/CanvasRenderer.ts +++ b/src/CanvasRenderer.ts @@ -325,6 +325,52 @@ export class CanvasRenderer { ctx.stroke(); } + /** + * Check if custom shape overlaps with any active batch preview areas + */ + isCustomShapeOverlappingWithBatchAreas(): boolean { + if (!this.canvas.outputAreaShape || !this.canvas.batchPreviewManagers || this.canvas.batchPreviewManagers.length === 0) { + return false; + } + + // Get custom shape bounds + const bounds = this.canvas.outputAreaBounds; + const ext = this.canvas.outputAreaExtensionEnabled ? this.canvas.outputAreaExtensions : { top: 0, bottom: 0, left: 0, right: 0 }; + const shapeOffsetX = bounds.x + ext.left; + const shapeOffsetY = bounds.y + ext.top; + + const shape = this.canvas.outputAreaShape; + let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity; + + // Calculate shape bounding box + shape.points.forEach((point: { x: number; y: number }) => { + const worldX = shapeOffsetX + point.x; + const worldY = shapeOffsetY + point.y; + minX = Math.min(minX, worldX); + maxX = Math.max(maxX, worldX); + minY = Math.min(minY, worldY); + maxY = Math.max(maxY, worldY); + }); + + const shapeBounds = { x: minX, y: minY, width: maxX - minX, height: maxY - minY }; + + // Check overlap with each active batch preview area + for (const manager of this.canvas.batchPreviewManagers) { + if (manager.generationArea) { + const area = manager.generationArea; + // Check if rectangles overlap + if (!(shapeBounds.x + shapeBounds.width < area.x || + area.x + area.width < shapeBounds.x || + shapeBounds.y + shapeBounds.height < area.y || + area.y + area.height < shapeBounds.y)) { + return true; // Overlap detected + } + } + } + + return false; + } + drawCanvasOutline(ctx: any) { ctx.beginPath(); ctx.strokeStyle = 'rgba(255, 255, 255, 0.8)'; @@ -345,7 +391,8 @@ export class CanvasRenderer { this.drawTextWithBackground(ctx, dimensionsText, textWorldX, textWorldY); - if (this.canvas.outputAreaShape) { + // Only draw custom shape if it doesn't overlap with batch preview areas + if (this.canvas.outputAreaShape && !this.isCustomShapeOverlappingWithBatchAreas()) { ctx.save(); ctx.strokeStyle = 'rgba(0, 255, 255, 0.9)'; ctx.lineWidth = 2 / this.canvas.viewport.zoom; @@ -432,33 +479,33 @@ export class CanvasRenderer { } drawPendingGenerationAreas(ctx: any) { - const areasToDraw = []; + const pendingAreas = []; - // 1. Get areas from active managers - if (this.canvas.batchPreviewManagers && this.canvas.batchPreviewManagers.length > 0) { - this.canvas.batchPreviewManagers.forEach((manager: any) => { - if (manager.generationArea) { - areasToDraw.push(manager.generationArea); - } - }); - } - - // 2. Get the area from the pending context (if it exists) + // 1. Get all pending generation areas (from pendingBatchContext) if (this.canvas.pendingBatchContext && this.canvas.pendingBatchContext.outputArea) { - areasToDraw.push(this.canvas.pendingBatchContext.outputArea); + pendingAreas.push(this.canvas.pendingBatchContext.outputArea); } - if (areasToDraw.length === 0) { - return; - } - - // 3. Draw all collected areas - areasToDraw.forEach(area => { - this.drawStyledRect(ctx, area, { - strokeStyle: 'rgba(0, 150, 255, 0.9)', - lineWidth: 3, - dashPattern: [12, 6] + // 2. Draw only those pending areas, które NIE mają aktywnego batch preview managera dla tego samego obszaru + const isAreaCoveredByBatch = (area: any) => { + if (!this.canvas.batchPreviewManagers) return false; + return this.canvas.batchPreviewManagers.some((manager: any) => { + if (!manager.generationArea) return false; + // Sprawdź czy obszary się pokrywają (prosty overlap AABB) + const a = area; + const b = manager.generationArea; + return !(a.x + a.width < b.x || b.x + b.width < a.x || a.y + a.height < b.y || b.y + b.height < a.y); }); + }; + + pendingAreas.forEach(area => { + if (!isAreaCoveredByBatch(area)) { + this.drawStyledRect(ctx, area, { + strokeStyle: 'rgba(0, 150, 255, 0.9)', + lineWidth: 3, + dashPattern: [12, 6] + }); + } }); } diff --git a/src/utils/IconLoader.ts b/src/utils/IconLoader.ts index 45b85b1..b7b78fa 100644 --- a/src/utils/IconLoader.ts +++ b/src/utils/IconLoader.ts @@ -13,7 +13,7 @@ export const LAYERFORGE_TOOLS = { DELETE: 'delete', DUPLICATE: 'duplicate', BLEND_MODE: 'blend_mode', - OPACITY: 'opacity', + OPACITY: 'opacity', MASK: 'mask', BRUSH: 'brush', ERASER: 'eraser',