From f329a6ded509639e24a69748d5ac88a057155350 Mon Sep 17 00:00:00 2001 From: Dariusz L Date: Sat, 26 Jul 2025 21:20:18 +0200 Subject: [PATCH] Refactor output area bounds handling for custom shapes Output area bounds are now positioned relative to the world, not always at (0,0), and are updated to match custom shape placement. Rendering and extension logic have been updated to respect the new bounds, and the mask tool now adjusts to the output area position. Also sets log level to DEBUG for development. --- js/Canvas.js | 68 +++++++++++++++++---------------------- js/CanvasRenderer.js | 6 ++-- js/CustomShapeMenu.js | 15 +++++---- js/config.js | 2 +- src/Canvas.ts | 73 ++++++++++++++++++------------------------ src/CanvasRenderer.ts | 7 ++-- src/CustomShapeMenu.ts | 17 ++++++---- 7 files changed, 90 insertions(+), 98 deletions(-) diff --git a/js/Canvas.js b/js/Canvas.js index 490afcc..e89f526 100644 --- a/js/Canvas.js +++ b/js/Canvas.js @@ -73,7 +73,13 @@ export class Canvas { this.outputAreaExtensionEnabled = false; this.outputAreaExtensionPreview = null; this.originalCanvasSize = { width: this.width, height: this.height }; - this.outputAreaBounds = { x: 0, y: 0, width: this.width, height: this.height }; + // Initialize outputAreaBounds centered in viewport, similar to how canvas resize/move work + this.outputAreaBounds = { + x: -(this.width / 4), + y: -(this.height / 4), + width: this.width, + height: this.height + }; this.maskTool = new MaskTool(this, { onStateChange: this.onStateChange }); this.shapeTool = new ShapeTool(this); this.customShapeMenu = new CustomShapeMenu(this); @@ -323,36 +329,22 @@ export class Canvas { }; const newWidth = Math.round(boundingBox.width); const newHeight = Math.round(boundingBox.height); - const finalX = boundingBox.x; - const finalY = boundingBox.y; + const newX = Math.round(boundingBox.x); + const newY = Math.round(boundingBox.y); + // Store the original canvas size for extension calculations + this.originalCanvasSize = { width: newWidth, height: newHeight }; + // Update canvas size but don't change outputAreaBounds yet this.updateOutputAreaSize(newWidth, newHeight, false); - this.layers.forEach((layer) => { - layer.x -= finalX; - layer.y -= finalY; - }); - this.maskTool.updatePosition(-finalX, -finalY); - // Update batch preview managers like in canvas resize/move operations - if (this.pendingBatchContext) { - this.pendingBatchContext.outputArea.x -= finalX; - this.pendingBatchContext.outputArea.y -= finalY; - // Also update the menu spawn position to keep it relative - this.pendingBatchContext.spawnPosition.x -= finalX; - this.pendingBatchContext.spawnPosition.y -= finalY; - log.debug("Updated pending batch context during shape definition:", this.pendingBatchContext); - } - // Also move any active batch preview menus - if (this.batchPreviewManagers && this.batchPreviewManagers.length > 0) { - this.batchPreviewManagers.forEach((manager) => { - manager.worldX -= finalX; - manager.worldY -= finalY; - if (manager.generationArea) { - manager.generationArea.x -= finalX; - manager.generationArea.y -= finalY; - } - }); - } - this.viewport.x -= finalX; - this.viewport.y -= finalY; + // Set outputAreaBounds to where the custom shape was drawn in the world + // Similar to finalizeCanvasMove - just update outputAreaBounds position + this.outputAreaBounds = { + x: newX, + y: newY, + width: newWidth, + height: newHeight + }; + // Update mask canvas to ensure it covers the new output area position + this.maskTool.updateMaskCanvasForOutputArea(); this.saveState(); this.render(); } @@ -399,17 +391,17 @@ export class Canvas { lastExecutionStartTime = Date.now(); // Store a snapshot of the context for the upcoming batch this.pendingBatchContext = { - // For the menu position + // For the menu position - position relative to outputAreaBounds, not canvas center spawnPosition: { - x: this.width / 2, - y: this.height + x: this.outputAreaBounds.x + this.outputAreaBounds.width / 2, + y: this.outputAreaBounds.y + this.outputAreaBounds.height }, - // For the image placement + // For the image placement - use actual outputAreaBounds instead of hardcoded (0,0) outputArea: { - x: 0, - y: 0, - width: this.width, - height: this.height + x: this.outputAreaBounds.x, + y: this.outputAreaBounds.y, + width: this.outputAreaBounds.width, + height: this.outputAreaBounds.height } }; log.debug(`Execution started, pending batch context captured:`, this.pendingBatchContext); diff --git a/js/CanvasRenderer.js b/js/CanvasRenderer.js index fb71568..33d6b69 100644 --- a/js/CanvasRenderer.js +++ b/js/CanvasRenderer.js @@ -272,10 +272,12 @@ export class CanvasRenderer { ctx.lineWidth = 2 / this.canvas.viewport.zoom; ctx.setLineDash([]); const shape = this.canvas.outputAreaShape; + const bounds = this.canvas.outputAreaBounds; ctx.beginPath(); - ctx.moveTo(shape.points[0].x, shape.points[0].y); + // Render custom shape relative to outputAreaBounds, not (0,0) + ctx.moveTo(bounds.x + shape.points[0].x, bounds.y + shape.points[0].y); for (let i = 1; i < shape.points.length; i++) { - ctx.lineTo(shape.points[i].x, shape.points[i].y); + ctx.lineTo(bounds.x + shape.points[i].x, bounds.y + shape.points[i].y); } ctx.closePath(); ctx.stroke(); diff --git a/js/CustomShapeMenu.js b/js/CustomShapeMenu.js index 5ffd335..3ae90a3 100644 --- a/js/CustomShapeMenu.js +++ b/js/CustomShapeMenu.js @@ -610,10 +610,12 @@ export class CustomShapeMenu { } _updateCanvasSize() { if (!this.canvas.outputAreaExtensionEnabled) { - // Reset to original bounds when disabled + // When extensions are disabled, preserve the current outputAreaBounds position + // Only update the size to match originalCanvasSize + const currentBounds = this.canvas.outputAreaBounds; this.canvas.outputAreaBounds = { - x: 0, - y: 0, + x: currentBounds.x, // ✅ Preserve current position + y: currentBounds.y, // ✅ Preserve current position width: this.canvas.originalCanvasSize.width, height: this.canvas.originalCanvasSize.height }; @@ -623,10 +625,11 @@ export class CustomShapeMenu { const ext = this.canvas.outputAreaExtensions; const newWidth = this.canvas.originalCanvasSize.width + ext.left + ext.right; const newHeight = this.canvas.originalCanvasSize.height + ext.top + ext.bottom; - // Aktualizuj outputAreaBounds - "okno" w świecie które zostanie wyrenderowane + // When extensions are enabled, calculate new bounds relative to current position + const currentBounds = this.canvas.outputAreaBounds; this.canvas.outputAreaBounds = { - x: -ext.left, // Może być ujemne - wycinamy fragment świata - y: -ext.top, // Może być ujemne - wycinamy fragment świata + x: currentBounds.x - ext.left, // Adjust position by left extension + y: currentBounds.y - ext.top, // Adjust position by top extension width: newWidth, height: newHeight }; diff --git a/js/config.js b/js/config.js index 472d3c8..af79d03 100644 --- a/js/config.js +++ b/js/config.js @@ -1,3 +1,3 @@ // Log level for development. // Possible values: 'DEBUG', 'INFO', 'WARN', 'ERROR', 'NONE' -export const LOG_LEVEL = 'NONE'; +export const LOG_LEVEL = 'DEBUG'; diff --git a/src/Canvas.ts b/src/Canvas.ts index 9f9c47d..4e561f3 100644 --- a/src/Canvas.ts +++ b/src/Canvas.ts @@ -132,7 +132,13 @@ export class Canvas { this.outputAreaExtensionEnabled = false; this.outputAreaExtensionPreview = null; this.originalCanvasSize = { width: this.width, height: this.height }; - this.outputAreaBounds = { x: 0, y: 0, width: this.width, height: this.height }; + // Initialize outputAreaBounds centered in viewport, similar to how canvas resize/move work + this.outputAreaBounds = { + x: -(this.width / 4), + y: -(this.height / 4), + width: this.width, + height: this.height + }; this.maskTool = new MaskTool(this, {onStateChange: this.onStateChange}); this.shapeTool = new ShapeTool(this); this.customShapeMenu = new CustomShapeMenu(this); @@ -424,43 +430,26 @@ export class Canvas { const newWidth = Math.round(boundingBox.width); const newHeight = Math.round(boundingBox.height); - const finalX = boundingBox.x; - const finalY = boundingBox.y; + const newX = Math.round(boundingBox.x); + const newY = Math.round(boundingBox.y); + // Store the original canvas size for extension calculations + this.originalCanvasSize = { width: newWidth, height: newHeight }; + + // Update canvas size but don't change outputAreaBounds yet this.updateOutputAreaSize(newWidth, newHeight, false); - this.layers.forEach((layer: Layer) => { - layer.x -= finalX; - layer.y -= finalY; - }); + // Set outputAreaBounds to where the custom shape was drawn in the world + // Similar to finalizeCanvasMove - just update outputAreaBounds position + this.outputAreaBounds = { + x: newX, + y: newY, + width: newWidth, + height: newHeight + }; - this.maskTool.updatePosition(-finalX, -finalY); - - // Update batch preview managers like in canvas resize/move operations - if (this.pendingBatchContext) { - this.pendingBatchContext.outputArea.x -= finalX; - this.pendingBatchContext.outputArea.y -= finalY; - - // Also update the menu spawn position to keep it relative - this.pendingBatchContext.spawnPosition.x -= finalX; - this.pendingBatchContext.spawnPosition.y -= finalY; - log.debug("Updated pending batch context during shape definition:", this.pendingBatchContext); - } - - // Also move any active batch preview menus - if (this.batchPreviewManagers && this.batchPreviewManagers.length > 0) { - this.batchPreviewManagers.forEach((manager: any) => { - manager.worldX -= finalX; - manager.worldY -= finalY; - if (manager.generationArea) { - manager.generationArea.x -= finalX; - manager.generationArea.y -= finalY; - } - }); - } - - this.viewport.x -= finalX; - this.viewport.y -= finalY; + // Update mask canvas to ensure it covers the new output area position + this.maskTool.updateMaskCanvasForOutputArea(); this.saveState(); this.render(); @@ -517,17 +506,17 @@ export class Canvas { lastExecutionStartTime = Date.now(); // Store a snapshot of the context for the upcoming batch this.pendingBatchContext = { - // For the menu position + // For the menu position - position relative to outputAreaBounds, not canvas center spawnPosition: { - x: this.width / 2, - y: this.height + x: this.outputAreaBounds.x + this.outputAreaBounds.width / 2, + y: this.outputAreaBounds.y + this.outputAreaBounds.height }, - // For the image placement + // For the image placement - use actual outputAreaBounds instead of hardcoded (0,0) outputArea: { - x: 0, - y: 0, - width: this.width, - height: this.height + x: this.outputAreaBounds.x, + y: this.outputAreaBounds.y, + width: this.outputAreaBounds.width, + height: this.outputAreaBounds.height } }; log.debug(`Execution started, pending batch context captured:`, this.pendingBatchContext); diff --git a/src/CanvasRenderer.ts b/src/CanvasRenderer.ts index e4478a8..c46d9de 100644 --- a/src/CanvasRenderer.ts +++ b/src/CanvasRenderer.ts @@ -321,10 +321,13 @@ export class CanvasRenderer { ctx.lineWidth = 2 / this.canvas.viewport.zoom; ctx.setLineDash([]); const shape = this.canvas.outputAreaShape; + const bounds = this.canvas.outputAreaBounds; + ctx.beginPath(); - ctx.moveTo(shape.points[0].x, shape.points[0].y); + // Render custom shape relative to outputAreaBounds, not (0,0) + ctx.moveTo(bounds.x + shape.points[0].x, bounds.y + shape.points[0].y); for (let i = 1; i < shape.points.length; i++) { - ctx.lineTo(shape.points[i].x, shape.points[i].y); + ctx.lineTo(bounds.x + shape.points[i].x, bounds.y + shape.points[i].y); } ctx.closePath(); ctx.stroke(); diff --git a/src/CustomShapeMenu.ts b/src/CustomShapeMenu.ts index 9160ce7..f01bc90 100644 --- a/src/CustomShapeMenu.ts +++ b/src/CustomShapeMenu.ts @@ -727,12 +727,14 @@ export class CustomShapeMenu { } } - private _updateCanvasSize(): void { + public _updateCanvasSize(): void { if (!this.canvas.outputAreaExtensionEnabled) { - // Reset to original bounds when disabled + // When extensions are disabled, preserve the current outputAreaBounds position + // Only update the size to match originalCanvasSize + const currentBounds = this.canvas.outputAreaBounds; this.canvas.outputAreaBounds = { - x: 0, - y: 0, + x: currentBounds.x, // ✅ Preserve current position + y: currentBounds.y, // ✅ Preserve current position width: this.canvas.originalCanvasSize.width, height: this.canvas.originalCanvasSize.height }; @@ -748,10 +750,11 @@ export class CustomShapeMenu { const newWidth = this.canvas.originalCanvasSize.width + ext.left + ext.right; const newHeight = this.canvas.originalCanvasSize.height + ext.top + ext.bottom; - // Aktualizuj outputAreaBounds - "okno" w świecie które zostanie wyrenderowane + // When extensions are enabled, calculate new bounds relative to current position + const currentBounds = this.canvas.outputAreaBounds; this.canvas.outputAreaBounds = { - x: -ext.left, // Może być ujemne - wycinamy fragment świata - y: -ext.top, // Może być ujemne - wycinamy fragment świata + x: currentBounds.x - ext.left, // Adjust position by left extension + y: currentBounds.y - ext.top, // Adjust position by top extension width: newWidth, height: newHeight };