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 };