From 7819edb1d9e1006b82ff4257839f186bbbb5abbd Mon Sep 17 00:00:00 2001 From: Urabewe Date: Wed, 15 Oct 2025 21:42:53 -0500 Subject: [PATCH 01/18] Uploading shape tool PR for review --- .../js/genpage/helpers/image_editor.js | 243 ++++++++++++++++++ 1 file changed, 243 insertions(+) diff --git a/src/wwwroot/js/genpage/helpers/image_editor.js b/src/wwwroot/js/genpage/helpers/image_editor.js index 04148f3c8..eec2cad2c 100644 --- a/src/wwwroot/js/genpage/helpers/image_editor.js +++ b/src/wwwroot/js/genpage/helpers/image_editor.js @@ -837,6 +837,225 @@ class ImageEditorToolBucket extends ImageEditorTool { } } +/** + * The Shape tool. + */ +class ImageEditorToolShape extends ImageEditorTool { + constructor(editor) { + super(editor, 'shape', 'shape', 'Shape', 'Create different shapes for AI editing.\nRectangle: Click and drag\nCircle: Click and drag\nHotKey: X', 'x'); + this.cursor = 'crosshair'; + this.color = '#ff0000'; + this.strokeWidth = 2; + this.shape = 'rectangle'; + this.isDrawing = false; + this.startX = 0; + this.startY = 0; + this.currentX = 0; + this.currentY = 0; + + let colorHTML = ` +
+ + + + +
`; + + let shapeHTML = ` +
+ + +
`; + + let strokeHTML = ` +
+ + +
+ +
+
`; + + let controlsHTML = ` +
+ +
`; + + this.configDiv.innerHTML = colorHTML + shapeHTML + strokeHTML + controlsHTML; + + this.colorText = this.configDiv.querySelector('.id-col1'); + this.colorSelector = this.configDiv.querySelector('.id-col2'); + this.colorPickButton = this.configDiv.querySelector('.id-col3'); + this.shapeSelect = this.configDiv.querySelector('.id-shape'); + this.strokeNumber = this.configDiv.querySelector('.id-stroke1'); + this.strokeSelector = this.configDiv.querySelector('.id-stroke2'); + this.clearButton = this.configDiv.querySelector('.id-clear'); + + this.colorText.addEventListener('input', () => { + this.colorSelector.value = this.colorText.value; + this.onConfigChange(); + }); + this.colorSelector.addEventListener('change', () => { + this.colorText.value = this.colorSelector.value; + this.onConfigChange(); + }); + this.colorPickButton.addEventListener('click', () => { + if (this.colorPickButton.classList.contains('interrupt-button')) { + this.colorPickButton.classList.remove('interrupt-button'); + this.editor.activateTool(this.id); + } else { + this.colorPickButton.classList.add('interrupt-button'); + this.editor.pickerTool.toolFor = this; + this.editor.activateTool('picker'); + } + }); + + this.shapeSelect.addEventListener('change', () => { + this.shape = this.shapeSelect.value; + this.updateUI(); + this.editor.redraw(); + }); + + enableSliderForBox(this.configDiv.querySelector('.id-stroke-block')); + this.strokeNumber.addEventListener('change', () => { this.onConfigChange(); }); + + this.clearButton.addEventListener('click', () => { + this.clearAllShapes(); + }); + + this.shapes = []; + } + + setColor(col) { + this.color = col; + this.colorText.value = col; + this.colorSelector.value = col; + this.colorPickButton.classList.remove('interrupt-button'); + } + + onConfigChange() { + this.color = this.colorText.value; + this.strokeWidth = parseInt(this.strokeNumber.value); + this.editor.redraw(); + } + + clearAllShapes() { + this.shapes = []; + this.isDrawing = false; + this.editor.redraw(); + } + + + draw() { + if (this.isDrawing && (this.shape === 'rectangle' || this.shape === 'circle')) { + this.drawShape({ + type: this.shape, + x: this.startX, + y: this.startY, + width: this.currentX - this.startX, + height: this.currentY - this.startY, + color: this.color, + strokeWidth: this.strokeWidth + }); + } + } + + drawShape(shape) { + this.editor.ctx.save(); + this.editor.ctx.strokeStyle = shape.color; + this.editor.ctx.lineWidth = shape.strokeWidth; + this.editor.ctx.setLineDash([]); + this.editor.ctx.beginPath(); + + if (shape.type === 'rectangle') { + let [x, y] = this.editor.imageCoordToCanvasCoord(shape.x, shape.y); + let [w, h] = [shape.width * this.editor.zoomLevel, shape.height * this.editor.zoomLevel]; + this.editor.ctx.rect(x, y, w, h); + } else if (shape.type === 'circle') { + let [cx, cy] = this.editor.imageCoordToCanvasCoord(shape.x + shape.width / 2, shape.y + shape.height / 2); + let radius = Math.sqrt(shape.width * shape.width + shape.height * shape.height) / 2 * this.editor.zoomLevel; + this.editor.ctx.arc(cx, cy, radius, 0, 2 * Math.PI); + } + + this.editor.ctx.stroke(); + this.editor.ctx.restore(); + } + + drawShapeToCanvas(ctx, shape, zoom, offsetX = 0, offsetY = 0) { + ctx.save(); + ctx.strokeStyle = shape.color; + ctx.lineWidth = shape.strokeWidth; + ctx.setLineDash([]); + ctx.beginPath(); + + if (shape.type === 'rectangle') { + let x = (shape.x + offsetX) * zoom; + let y = (shape.y + offsetY) * zoom; + let w = shape.width * zoom; + let h = shape.height * zoom; + ctx.rect(x, y, w, h); + } else if (shape.type === 'circle') { + let cx = (shape.x + shape.width / 2 + offsetX) * zoom; + let cy = (shape.y + shape.height / 2 + offsetY) * zoom; + let radius = Math.sqrt(shape.width * shape.width + shape.height * shape.height) / 2 * zoom; + ctx.arc(cx, cy, radius, 0, 2 * Math.PI); + } + + ctx.stroke(); + ctx.restore(); + } + + + onMouseDown(e) { + let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); + + this.isDrawing = true; + this.startX = mouseX; + this.startY = mouseY; + this.currentX = mouseX; + this.currentY = mouseY; + } + + onMouseUp(e) { + if (this.isDrawing && (this.shape === 'rectangle' || this.shape === 'circle')) { + let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); + this.currentX = mouseX; + this.currentY = mouseY; + + // Only add if there's actual size + if (Math.abs(this.currentX - this.startX) > 1 || Math.abs(this.currentY - this.startY) > 1) { + this.shapes.push({ + type: this.shape, + x: Math.min(this.startX, this.currentX), + y: Math.min(this.startY, this.currentY), + width: Math.abs(this.currentX - this.startX), + height: Math.abs(this.currentY - this.startY), + color: this.color, + strokeWidth: this.strokeWidth + }); + this.editor.markChanged(); + } + + this.isDrawing = false; + this.editor.redraw(); + } + } + + onGlobalMouseMove(e) { + if (this.isDrawing) { + let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); + this.currentX = mouseX; + this.currentY = mouseY; + this.editor.redraw(); + return true; + } + return false; + } +} + /** * The Color Picker tool, a special hidden sub-tool. */ @@ -1243,6 +1462,7 @@ class ImageEditor { this.addTool(new ImageEditorToolBrush(this, 'brush', 'paintbrush', 'Paintbrush', 'Draw on the image.\nHotKey: B', false, 'b')); this.addTool(new ImageEditorToolBrush(this, 'eraser', 'eraser', 'Eraser', 'Erase parts of the image.\nHotKey: E', true, 'e')); this.addTool(new ImageEditorToolBucket(this)); + this.addTool(new ImageEditorToolShape(this)); this.pickerTool = new ImageEditorToolPicker(this, 'picker', 'paintbrush', 'Color Picker', 'Pick a color from the image.'); this.addTool(this.pickerTool); this.activateTool('brush'); @@ -1813,6 +2033,7 @@ class ImageEditor { this.ctx.restore(); } + redraw() { if (!this.canvas) { return; @@ -1860,6 +2081,14 @@ class ImageEditor { let [selectX, selectY] = this.imageCoordToCanvasCoord(this.selectX, this.selectY); this.drawSelectionBox(selectX, selectY, this.selectWidth * this.zoomLevel, this.selectHeight * this.zoomLevel, this.uiColor, 8 * this.zoomLevel, 0); } + + // Always draw shapes regardless of active tool + if (this.tools.shape && this.tools.shape.shapes) { + for (let shape of this.tools.shape.shapes) { + this.tools.shape.drawShape(shape); + } + } + this.activeTool.draw(); this.ctx.restore(); } @@ -1874,6 +2103,13 @@ class ImageEditor { layer.drawToBack(ctx, this.finalOffsetX, this.finalOffsetY, 1); } } + + if (this.tools.shape && this.tools.shape.shapes) { + for (let shape of this.tools.shape.shapes) { + this.tools.shape.drawShapeToCanvas(ctx, shape, 1); + } + } + return canvas.toDataURL(format); } @@ -1899,6 +2135,13 @@ class ImageEditor { layer.drawToBack(ctx, -minX, -minY, 1); } } + + if (this.tools.shape && this.tools.shape.shapes) { + for (let shape of this.tools.shape.shapes) { + this.tools.shape.drawShapeToCanvas(ctx, shape, 1, -minX, -minY); + } + } + return canvas.toDataURL(format); } From fc02142b15e9e1d7a93901925f0cf85b31fdff7f Mon Sep 17 00:00:00 2001 From: Urabewe Date: Wed, 15 Oct 2025 21:45:16 -0500 Subject: [PATCH 02/18] Update image_editor.js --- src/wwwroot/js/genpage/helpers/image_editor.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/wwwroot/js/genpage/helpers/image_editor.js b/src/wwwroot/js/genpage/helpers/image_editor.js index eec2cad2c..9032d19bd 100644 --- a/src/wwwroot/js/genpage/helpers/image_editor.js +++ b/src/wwwroot/js/genpage/helpers/image_editor.js @@ -2082,7 +2082,6 @@ class ImageEditor { this.drawSelectionBox(selectX, selectY, this.selectWidth * this.zoomLevel, this.selectHeight * this.zoomLevel, this.uiColor, 8 * this.zoomLevel, 0); } - // Always draw shapes regardless of active tool if (this.tools.shape && this.tools.shape.shapes) { for (let shape of this.tools.shape.shapes) { this.tools.shape.drawShape(shape); From 95fc127fc537a2a7b9dc87862da0a4457c366c97 Mon Sep 17 00:00:00 2001 From: Urabewe Date: Wed, 15 Oct 2025 23:12:18 -0500 Subject: [PATCH 03/18] Update image_editor.js --- .../js/genpage/helpers/image_editor.js | 140 +++++++++--------- 1 file changed, 72 insertions(+), 68 deletions(-) diff --git a/src/wwwroot/js/genpage/helpers/image_editor.js b/src/wwwroot/js/genpage/helpers/image_editor.js index 9032d19bd..d34340298 100644 --- a/src/wwwroot/js/genpage/helpers/image_editor.js +++ b/src/wwwroot/js/genpage/helpers/image_editor.js @@ -845,13 +845,14 @@ class ImageEditorToolShape extends ImageEditorTool { super(editor, 'shape', 'shape', 'Shape', 'Create different shapes for AI editing.\nRectangle: Click and drag\nCircle: Click and drag\nHotKey: X', 'x'); this.cursor = 'crosshair'; this.color = '#ff0000'; - this.strokeWidth = 2; + this.strokeWidth = 4; this.shape = 'rectangle'; this.isDrawing = false; this.startX = 0; this.startY = 0; this.currentX = 0; this.currentY = 0; + this.shapesLayer = null; let colorHTML = `
@@ -872,10 +873,10 @@ class ImageEditorToolShape extends ImageEditorTool { let strokeHTML = `
- - -
- + + +
+
`; @@ -915,7 +916,6 @@ class ImageEditorToolShape extends ImageEditorTool { this.shapeSelect.addEventListener('change', () => { this.shape = this.shapeSelect.value; - this.updateUI(); this.editor.redraw(); }); @@ -925,8 +925,6 @@ class ImageEditorToolShape extends ImageEditorTool { this.clearButton.addEventListener('click', () => { this.clearAllShapes(); }); - - this.shapes = []; } setColor(col) { @@ -943,12 +941,51 @@ class ImageEditorToolShape extends ImageEditorTool { } clearAllShapes() { - this.shapes = []; + if (this.shapesLayer && this.shapesLayer.hasAnyContent) { + this.editor.activeLayer.saveBeforeEdit(); + + this.shapesLayer.ctx.clearRect(0, 0, this.shapesLayer.canvas.width, this.shapesLayer.canvas.height); + this.shapesLayer.hasAnyContent = false; + this.editor.markChanged(); + } this.isDrawing = false; this.editor.redraw(); } - + drawShapeToSubLayer(shape) { + if (!this.shapesLayer) return; + + let [canvasX1, canvasY1] = this.editor.imageCoordToCanvasCoord(shape.x, shape.y); + let [canvasX2, canvasY2] = this.editor.imageCoordToCanvasCoord(shape.x + shape.width, shape.y + shape.height); + + let [layerX1, layerY1] = this.shapesLayer.canvasCoordToLayerCoord(canvasX1, canvasY1); + let [layerX2, layerY2] = this.shapesLayer.canvasCoordToLayerCoord(canvasX2, canvasY2); + + this.shapesLayer.ctx.save(); + this.shapesLayer.ctx.strokeStyle = shape.color; + this.shapesLayer.ctx.lineWidth = shape.strokeWidth; + this.shapesLayer.ctx.setLineDash([]); + this.shapesLayer.ctx.beginPath(); + + if (shape.type === 'rectangle') { + let x = Math.min(layerX1, layerX2); + let y = Math.min(layerY1, layerY2); + let w = Math.abs(layerX2 - layerX1); + let h = Math.abs(layerY2 - layerY1); + this.shapesLayer.ctx.rect(x, y, w, h); + } + else if (shape.type === 'circle') { + let cx = (layerX1 + layerX2) / 2; + let cy = (layerY1 + layerY2) / 2; + let radius = Math.sqrt(Math.pow(layerX2 - layerX1, 2) + Math.pow(layerY2 - layerY1, 2)) / 2; + this.shapesLayer.ctx.arc(cx, cy, radius, 0, 2 * Math.PI); + } + + this.shapesLayer.ctx.stroke(); + this.shapesLayer.ctx.restore(); + this.shapesLayer.hasAnyContent = true; + } + draw() { if (this.isDrawing && (this.shape === 'rectangle' || this.shape === 'circle')) { this.drawShape({ @@ -966,7 +1003,7 @@ class ImageEditorToolShape extends ImageEditorTool { drawShape(shape) { this.editor.ctx.save(); this.editor.ctx.strokeStyle = shape.color; - this.editor.ctx.lineWidth = shape.strokeWidth; + this.editor.ctx.lineWidth = shape.strokeWidth * this.editor.zoomLevel; this.editor.ctx.setLineDash([]); this.editor.ctx.beginPath(); @@ -974,7 +1011,8 @@ class ImageEditorToolShape extends ImageEditorTool { let [x, y] = this.editor.imageCoordToCanvasCoord(shape.x, shape.y); let [w, h] = [shape.width * this.editor.zoomLevel, shape.height * this.editor.zoomLevel]; this.editor.ctx.rect(x, y, w, h); - } else if (shape.type === 'circle') { + } + else if (shape.type === 'circle') { let [cx, cy] = this.editor.imageCoordToCanvasCoord(shape.x + shape.width / 2, shape.y + shape.height / 2); let radius = Math.sqrt(shape.width * shape.width + shape.height * shape.height) / 2 * this.editor.zoomLevel; this.editor.ctx.arc(cx, cy, radius, 0, 2 * Math.PI); @@ -983,32 +1021,7 @@ class ImageEditorToolShape extends ImageEditorTool { this.editor.ctx.stroke(); this.editor.ctx.restore(); } - - drawShapeToCanvas(ctx, shape, zoom, offsetX = 0, offsetY = 0) { - ctx.save(); - ctx.strokeStyle = shape.color; - ctx.lineWidth = shape.strokeWidth; - ctx.setLineDash([]); - ctx.beginPath(); - - if (shape.type === 'rectangle') { - let x = (shape.x + offsetX) * zoom; - let y = (shape.y + offsetY) * zoom; - let w = shape.width * zoom; - let h = shape.height * zoom; - ctx.rect(x, y, w, h); - } else if (shape.type === 'circle') { - let cx = (shape.x + shape.width / 2 + offsetX) * zoom; - let cy = (shape.y + shape.height / 2 + offsetY) * zoom; - let radius = Math.sqrt(shape.width * shape.width + shape.height * shape.height) / 2 * zoom; - ctx.arc(cx, cy, radius, 0, 2 * Math.PI); - } - - ctx.stroke(); - ctx.restore(); - } - - + onMouseDown(e) { let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); @@ -1020,23 +1033,32 @@ class ImageEditorToolShape extends ImageEditorTool { } onMouseUp(e) { - if (this.isDrawing && (this.shape === 'rectangle' || this.shape === 'circle')) { + if (this.isDrawing) { let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); this.currentX = mouseX; this.currentY = mouseY; - // Only add if there's actual size if (Math.abs(this.currentX - this.startX) > 1 || Math.abs(this.currentY - this.startY) > 1) { - this.shapes.push({ - type: this.shape, - x: Math.min(this.startX, this.currentX), - y: Math.min(this.startY, this.currentY), - width: Math.abs(this.currentX - this.startX), - height: Math.abs(this.currentY - this.startY), - color: this.color, - strokeWidth: this.strokeWidth - }); - this.editor.markChanged(); + // Create shapes sub-layer if it doesn't exist + if (!this.shapesLayer) { + this.shapesLayer = new ImageEditorLayer(this.editor, this.editor.activeLayer.canvas.width, this.editor.activeLayer.canvas.height, this.editor.activeLayer); + this.editor.activeLayer.childLayers.push(this.shapesLayer); + } + + this.editor.activeLayer.saveBeforeEdit(); + + let shape = { + type: this.shape, + x: Math.min(this.startX, this.currentX), + y: Math.min(this.startY, this.currentY), + width: Math.abs(this.currentX - this.startX), + height: Math.abs(this.currentY - this.startY), + color: this.color, + strokeWidth: this.strokeWidth + }; + + this.drawShapeToSubLayer(shape); + this.editor.markChanged(); } this.isDrawing = false; @@ -2082,12 +2104,6 @@ class ImageEditor { this.drawSelectionBox(selectX, selectY, this.selectWidth * this.zoomLevel, this.selectHeight * this.zoomLevel, this.uiColor, 8 * this.zoomLevel, 0); } - if (this.tools.shape && this.tools.shape.shapes) { - for (let shape of this.tools.shape.shapes) { - this.tools.shape.drawShape(shape); - } - } - this.activeTool.draw(); this.ctx.restore(); } @@ -2103,12 +2119,6 @@ class ImageEditor { } } - if (this.tools.shape && this.tools.shape.shapes) { - for (let shape of this.tools.shape.shapes) { - this.tools.shape.drawShapeToCanvas(ctx, shape, 1); - } - } - return canvas.toDataURL(format); } @@ -2135,12 +2145,6 @@ class ImageEditor { } } - if (this.tools.shape && this.tools.shape.shapes) { - for (let shape of this.tools.shape.shapes) { - this.tools.shape.drawShapeToCanvas(ctx, shape, 1, -minX, -minY); - } - } - return canvas.toDataURL(format); } From 55f461b0b8c801b5ea8018c65d17dfdb041a9915 Mon Sep 17 00:00:00 2001 From: Urabewe Date: Wed, 15 Oct 2025 23:19:05 -0500 Subject: [PATCH 04/18] whoops --- src/wwwroot/js/genpage/helpers/image_editor.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/wwwroot/js/genpage/helpers/image_editor.js b/src/wwwroot/js/genpage/helpers/image_editor.js index d34340298..9981c7b76 100644 --- a/src/wwwroot/js/genpage/helpers/image_editor.js +++ b/src/wwwroot/js/genpage/helpers/image_editor.js @@ -852,6 +852,7 @@ class ImageEditorToolShape extends ImageEditorTool { this.startY = 0; this.currentX = 0; this.currentY = 0; + this.shapes = []; this.shapesLayer = null; let colorHTML = ` @@ -941,11 +942,14 @@ class ImageEditorToolShape extends ImageEditorTool { } clearAllShapes() { - if (this.shapesLayer && this.shapesLayer.hasAnyContent) { + if (this.shapesLayer && this.shapes.length > 0) { + // Save before edit for undo functionality this.editor.activeLayer.saveBeforeEdit(); + // Clear only the shapes sub-layer this.shapesLayer.ctx.clearRect(0, 0, this.shapesLayer.canvas.width, this.shapesLayer.canvas.height); this.shapesLayer.hasAnyContent = false; + this.shapes = []; this.editor.markChanged(); } this.isDrawing = false; @@ -1021,7 +1025,7 @@ class ImageEditorToolShape extends ImageEditorTool { this.editor.ctx.stroke(); this.editor.ctx.restore(); } - + onMouseDown(e) { let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); @@ -1039,7 +1043,6 @@ class ImageEditorToolShape extends ImageEditorTool { this.currentY = mouseY; if (Math.abs(this.currentX - this.startX) > 1 || Math.abs(this.currentY - this.startY) > 1) { - // Create shapes sub-layer if it doesn't exist if (!this.shapesLayer) { this.shapesLayer = new ImageEditorLayer(this.editor, this.editor.activeLayer.canvas.width, this.editor.activeLayer.canvas.height, this.editor.activeLayer); this.editor.activeLayer.childLayers.push(this.shapesLayer); @@ -1057,6 +1060,7 @@ class ImageEditorToolShape extends ImageEditorTool { strokeWidth: this.strokeWidth }; + this.shapes.push(shape); this.drawShapeToSubLayer(shape); this.editor.markChanged(); } From ebed25a5ffdee1340279eac2c1d8851b343ffea8 Mon Sep 17 00:00:00 2001 From: Urabewe Date: Wed, 15 Oct 2025 23:37:38 -0500 Subject: [PATCH 05/18] Fix my mistakes? last commit for tonight lol too tired --- .../js/genpage/helpers/image_editor.js | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/src/wwwroot/js/genpage/helpers/image_editor.js b/src/wwwroot/js/genpage/helpers/image_editor.js index 9981c7b76..bf414a9b0 100644 --- a/src/wwwroot/js/genpage/helpers/image_editor.js +++ b/src/wwwroot/js/genpage/helpers/image_editor.js @@ -852,8 +852,8 @@ class ImageEditorToolShape extends ImageEditorTool { this.startY = 0; this.currentX = 0; this.currentY = 0; - this.shapes = []; - this.shapesLayer = null; + this.shapes = []; // Track shapes for undo functionality + this.shapesLayer = null; // Sub-layer for storing shapes let colorHTML = `
@@ -874,7 +874,7 @@ class ImageEditorToolShape extends ImageEditorTool { let strokeHTML = `
- +
@@ -959,9 +959,11 @@ class ImageEditorToolShape extends ImageEditorTool { drawShapeToSubLayer(shape) { if (!this.shapesLayer) return; + // Convert image coordinates to canvas coordinates first let [canvasX1, canvasY1] = this.editor.imageCoordToCanvasCoord(shape.x, shape.y); let [canvasX2, canvasY2] = this.editor.imageCoordToCanvasCoord(shape.x + shape.width, shape.y + shape.height); + // Then convert to sub-layer coordinates let [layerX1, layerY1] = this.shapesLayer.canvasCoordToLayerCoord(canvasX1, canvasY1); let [layerX2, layerY2] = this.shapesLayer.canvasCoordToLayerCoord(canvasX2, canvasY2); @@ -988,8 +990,10 @@ class ImageEditorToolShape extends ImageEditorTool { this.shapesLayer.ctx.stroke(); this.shapesLayer.ctx.restore(); this.shapesLayer.hasAnyContent = true; + console.log('Drew shape to sub-layer:', shape); } - + + draw() { if (this.isDrawing && (this.shape === 'rectangle' || this.shape === 'circle')) { this.drawShape({ @@ -1025,7 +1029,33 @@ class ImageEditorToolShape extends ImageEditorTool { this.editor.ctx.stroke(); this.editor.ctx.restore(); } - + + drawShapeToCanvas(ctx, shape, zoom, offsetX = 0, offsetY = 0) { + ctx.save(); + ctx.strokeStyle = shape.color; + ctx.lineWidth = shape.strokeWidth; + ctx.setLineDash([]); + ctx.beginPath(); + + if (shape.type === 'rectangle') { + let x = (shape.x + offsetX) * zoom; + let y = (shape.y + offsetY) * zoom; + let w = shape.width * zoom; + let h = shape.height * zoom; + ctx.rect(x, y, w, h); + } + else if (shape.type === 'circle') { + let cx = (shape.x + shape.width / 2 + offsetX) * zoom; + let cy = (shape.y + shape.height / 2 + offsetY) * zoom; + let radius = Math.sqrt(shape.width * shape.width + shape.height * shape.height) / 2 * zoom; + ctx.arc(cx, cy, radius, 0, 2 * Math.PI); + } + + ctx.stroke(); + ctx.restore(); + } + + onMouseDown(e) { let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); @@ -1043,11 +1073,14 @@ class ImageEditorToolShape extends ImageEditorTool { this.currentY = mouseY; if (Math.abs(this.currentX - this.startX) > 1 || Math.abs(this.currentY - this.startY) > 1) { + // Create shapes sub-layer if it doesn't exist if (!this.shapesLayer) { this.shapesLayer = new ImageEditorLayer(this.editor, this.editor.activeLayer.canvas.width, this.editor.activeLayer.canvas.height, this.editor.activeLayer); this.editor.activeLayer.childLayers.push(this.shapesLayer); + console.log('Created shapes sub-layer, childLayers count:', this.editor.activeLayer.childLayers.length); } + // Save before edit for undo functionality this.editor.activeLayer.saveBeforeEdit(); let shape = { From 6cb790848e1edbe25143d4f254d6a37f2dd31d32 Mon Sep 17 00:00:00 2001 From: Urabewe Date: Sat, 1 Nov 2025 11:03:50 -0500 Subject: [PATCH 06/18] Update image_editor.js --- .../js/genpage/helpers/image_editor.js | 302 +++++++++++------- 1 file changed, 195 insertions(+), 107 deletions(-) diff --git a/src/wwwroot/js/genpage/helpers/image_editor.js b/src/wwwroot/js/genpage/helpers/image_editor.js index bf414a9b0..13359758c 100644 --- a/src/wwwroot/js/genpage/helpers/image_editor.js +++ b/src/wwwroot/js/genpage/helpers/image_editor.js @@ -852,8 +852,12 @@ class ImageEditorToolShape extends ImageEditorTool { this.startY = 0; this.currentX = 0; this.currentY = 0; - this.shapes = []; // Track shapes for undo functionality - this.shapesLayer = null; // Sub-layer for storing shapes + this.startLayerX = 0; + this.startLayerY = 0; + this.currentLayerX = 0; + this.currentLayerY = 0; + this.bufferLayer = null; + this.hasDrawn = false; let colorHTML = `
@@ -881,12 +885,7 @@ class ImageEditorToolShape extends ImageEditorTool {
`; - let controlsHTML = ` -
- -
`; - - this.configDiv.innerHTML = colorHTML + shapeHTML + strokeHTML + controlsHTML; + this.configDiv.innerHTML = colorHTML + shapeHTML + strokeHTML; this.colorText = this.configDiv.querySelector('.id-col1'); this.colorSelector = this.configDiv.querySelector('.id-col2'); @@ -894,7 +893,6 @@ class ImageEditorToolShape extends ImageEditorTool { this.shapeSelect = this.configDiv.querySelector('.id-shape'); this.strokeNumber = this.configDiv.querySelector('.id-stroke1'); this.strokeSelector = this.configDiv.querySelector('.id-stroke2'); - this.clearButton = this.configDiv.querySelector('.id-clear'); this.colorText.addEventListener('input', () => { this.colorSelector.value = this.colorText.value; @@ -922,10 +920,6 @@ class ImageEditorToolShape extends ImageEditorTool { enableSliderForBox(this.configDiv.querySelector('.id-stroke-block')); this.strokeNumber.addEventListener('change', () => { this.onConfigChange(); }); - - this.clearButton.addEventListener('click', () => { - this.clearAllShapes(); - }); } setColor(col) { @@ -941,70 +935,29 @@ class ImageEditorToolShape extends ImageEditorTool { this.editor.redraw(); } - clearAllShapes() { - if (this.shapesLayer && this.shapes.length > 0) { - // Save before edit for undo functionality - this.editor.activeLayer.saveBeforeEdit(); - - // Clear only the shapes sub-layer - this.shapesLayer.ctx.clearRect(0, 0, this.shapesLayer.canvas.width, this.shapesLayer.canvas.height); - this.shapesLayer.hasAnyContent = false; - this.shapes = []; - this.editor.markChanged(); - } - this.isDrawing = false; - this.editor.redraw(); - } - - drawShapeToSubLayer(shape) { - if (!this.shapesLayer) return; - - // Convert image coordinates to canvas coordinates first - let [canvasX1, canvasY1] = this.editor.imageCoordToCanvasCoord(shape.x, shape.y); - let [canvasX2, canvasY2] = this.editor.imageCoordToCanvasCoord(shape.x + shape.width, shape.y + shape.height); - - // Then convert to sub-layer coordinates - let [layerX1, layerY1] = this.shapesLayer.canvasCoordToLayerCoord(canvasX1, canvasY1); - let [layerX2, layerY2] = this.shapesLayer.canvasCoordToLayerCoord(canvasX2, canvasY2); - - this.shapesLayer.ctx.save(); - this.shapesLayer.ctx.strokeStyle = shape.color; - this.shapesLayer.ctx.lineWidth = shape.strokeWidth; - this.shapesLayer.ctx.setLineDash([]); - this.shapesLayer.ctx.beginPath(); - - if (shape.type === 'rectangle') { - let x = Math.min(layerX1, layerX2); - let y = Math.min(layerY1, layerY2); - let w = Math.abs(layerX2 - layerX1); - let h = Math.abs(layerY2 - layerY1); - this.shapesLayer.ctx.rect(x, y, w, h); - } - else if (shape.type === 'circle') { - let cx = (layerX1 + layerX2) / 2; - let cy = (layerY1 + layerY2) / 2; - let radius = Math.sqrt(Math.pow(layerX2 - layerX1, 2) + Math.pow(layerY2 - layerY1, 2)) / 2; - this.shapesLayer.ctx.arc(cx, cy, radius, 0, 2 * Math.PI); - } - - this.shapesLayer.ctx.stroke(); - this.shapesLayer.ctx.restore(); - this.shapesLayer.hasAnyContent = true; - console.log('Drew shape to sub-layer:', shape); - } draw() { - if (this.isDrawing && (this.shape === 'rectangle' || this.shape === 'circle')) { - this.drawShape({ - type: this.shape, - x: this.startX, - y: this.startY, - width: this.currentX - this.startX, - height: this.currentY - this.startY, - color: this.color, - strokeWidth: this.strokeWidth - }); + if (this.isDrawing) { + this.editor.ctx.save(); + this.editor.ctx.strokeStyle = this.color; + this.editor.ctx.lineWidth = this.strokeWidth * this.editor.zoomLevel; + this.editor.ctx.setLineDash([]); + this.editor.ctx.beginPath(); + + if (this.shape === 'rectangle') { + let [x, y] = this.editor.imageCoordToCanvasCoord(Math.min(this.startX, this.currentX), Math.min(this.startY, this.currentY)); + let [w, h] = [Math.abs(this.currentX - this.startX) * this.editor.zoomLevel, Math.abs(this.currentY - this.startY) * this.editor.zoomLevel]; + this.editor.ctx.rect(x, y, w, h); + } + else if (this.shape === 'circle') { + let [cx, cy] = this.editor.imageCoordToCanvasCoord((this.startX + this.currentX) / 2, (this.startY + this.currentY) / 2); + let radius = Math.sqrt(Math.pow(this.currentX - this.startX, 2) + Math.pow(this.currentY - this.startY, 2)) / 2 * this.editor.zoomLevel; + this.editor.ctx.arc(cx, cy, radius, 0, 2 * Math.PI); + } + + this.editor.ctx.stroke(); + this.editor.ctx.restore(); } } @@ -1054,9 +1007,15 @@ class ImageEditorToolShape extends ImageEditorTool { ctx.stroke(); ctx.restore(); } - - + onMouseDown(e) { + if (e.button !== 0 && e.button !== undefined) { + return; + } + if (this.isDrawing) { + this.finishDrawing(); + } + this.editor.updateMousePosFrom(e); let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); this.isDrawing = true; @@ -1064,55 +1023,181 @@ class ImageEditorToolShape extends ImageEditorTool { this.startY = mouseY; this.currentX = mouseX; this.currentY = mouseY; + this.hasDrawn = false; + + let target = this.editor.activeLayer; + if (!target) { + this.bufferLayer = null; + this.isDrawing = false; + return; + } + let [layerX, layerY] = target.canvasCoordToLayerCoord(this.editor.mouseX, this.editor.mouseY); + this.startLayerX = layerX; + this.startLayerY = layerY; + this.currentLayerX = layerX; + this.currentLayerY = layerY; + this.bufferLayer = new ImageEditorLayer(this.editor, target.canvas.width, target.canvas.height, target); + this.bufferLayer.opacity = 1; + target.childLayers.push(this.bufferLayer); } - onMouseUp(e) { - if (this.isDrawing) { - let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); - this.currentX = mouseX; - this.currentY = mouseY; - - if (Math.abs(this.currentX - this.startX) > 1 || Math.abs(this.currentY - this.startY) > 1) { - // Create shapes sub-layer if it doesn't exist - if (!this.shapesLayer) { - this.shapesLayer = new ImageEditorLayer(this.editor, this.editor.activeLayer.canvas.width, this.editor.activeLayer.canvas.height, this.editor.activeLayer); - this.editor.activeLayer.childLayers.push(this.shapesLayer); - console.log('Created shapes sub-layer, childLayers count:', this.editor.activeLayer.childLayers.length); + finishDrawing() { + if (this.isDrawing && this.bufferLayer) { + const parent = this.editor.activeLayer; + if (!parent) { + this.bufferLayer = null; + this.isDrawing = false; + this.hasDrawn = false; + this.editor.redraw(); + return; + } + + if (!this.hasDrawn) { + const idx = parent.childLayers.indexOf(this.bufferLayer); + if (idx !== -1) { + parent.childLayers.splice(idx, 1); } - - // Save before edit for undo functionality - this.editor.activeLayer.saveBeforeEdit(); - - let shape = { - type: this.shape, - x: Math.min(this.startX, this.currentX), - y: Math.min(this.startY, this.currentY), - width: Math.abs(this.currentX - this.startX), - height: Math.abs(this.currentY - this.startY), - color: this.color, - strokeWidth: this.strokeWidth - }; - - this.shapes.push(shape); - this.drawShapeToSubLayer(shape); - this.editor.markChanged(); + this.bufferLayer = null; + this.isDrawing = false; + this.hasDrawn = false; + this.editor.redraw(); + return; } - + + this.drawShape(); + + const idx = parent.childLayers.indexOf(this.bufferLayer); + if (idx !== -1) { + parent.childLayers.splice(idx, 1); + } + const offset = parent.getOffset(); + parent.saveBeforeEdit(); + this.bufferLayer.drawToBackDirect(parent.ctx, -offset[0], -offset[1], 1); + parent.hasAnyContent = true; + this.bufferLayer = null; this.isDrawing = false; + this.hasDrawn = false; + this.editor.markChanged(); this.editor.redraw(); } } + onMouseMove(e) { + if (this.isDrawing) { + this.editor.updateMousePosFrom(e); + let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); + this.currentX = mouseX; + this.currentY = mouseY; + let target = this.editor.activeLayer; + if (target) { + let [layerX, layerY] = target.canvasCoordToLayerCoord(this.editor.mouseX, this.editor.mouseY); + this.currentLayerX = layerX; + this.currentLayerY = layerY; + } + this.drawShape(); + return true; + } + return false; + } + onGlobalMouseMove(e) { if (this.isDrawing) { + this.editor.updateMousePosFrom(e); let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); this.currentX = mouseX; this.currentY = mouseY; - this.editor.redraw(); + let target = this.editor.activeLayer; + if (target) { + let [layerX, layerY] = target.canvasCoordToLayerCoord(this.editor.mouseX, this.editor.mouseY); + this.currentLayerX = layerX; + this.currentLayerY = layerY; + } + this.drawShape(); return true; } return false; } + + onMouseUp(e) { + if (e.button !== 0 && e.button !== undefined) { + return; + } + if (!this.isDrawing) { + return; + } + this.editor.updateMousePosFrom(e); + let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); + this.currentX = mouseX; + this.currentY = mouseY; + let target = this.editor.activeLayer; + if (target) { + let [layerX, layerY] = target.canvasCoordToLayerCoord(this.editor.mouseX, this.editor.mouseY); + this.currentLayerX = layerX; + this.currentLayerY = layerY; + } + this.finishDrawing(); + } + + onGlobalMouseUp(e) { + if (e.button !== 0 && e.button !== undefined) { + return false; + } + if (this.isDrawing) { + this.editor.updateMousePosFrom(e); + this.finishDrawing(); + return true; + } + return false; + } + + drawShape() { + if (!this.isDrawing || !this.bufferLayer) { + return; + } + + const parent = this.editor.activeLayer; + if (!parent) { + return; + } + + this.bufferLayer.ctx.clearRect(0, 0, this.bufferLayer.canvas.width, this.bufferLayer.canvas.height); + + const layerX1 = Math.min(this.startLayerX, this.currentLayerX); + const layerY1 = Math.min(this.startLayerY, this.currentLayerY); + const width = Math.abs(this.currentLayerX - this.startLayerX); + const height = Math.abs(this.currentLayerY - this.startLayerY); + + if (width < 0.5 && height < 0.5) { + this.bufferLayer.hasAnyContent = false; + this.hasDrawn = false; + this.editor.redraw(); + return; + } + + this.bufferLayer.ctx.save(); + this.bufferLayer.ctx.strokeStyle = this.color; + this.bufferLayer.ctx.lineWidth = this.strokeWidth; + this.bufferLayer.ctx.setLineDash([]); + this.bufferLayer.ctx.beginPath(); + + if (this.shape === 'rectangle') { + this.bufferLayer.ctx.rect(layerX1, layerY1, width, height); + } + else if (this.shape === 'circle') { + const cx = layerX1 + width / 2; + const cy = layerY1 + height / 2; + const radius = Math.sqrt(width * width + height * height) / 2; + this.bufferLayer.ctx.arc(cx, cy, radius, 0, 2 * Math.PI); + } + + this.bufferLayer.ctx.stroke(); + this.bufferLayer.ctx.restore(); + this.bufferLayer.hasAnyContent = true; + this.hasDrawn = true; + this.editor.markChanged(); + this.editor.redraw(); + } + } /** @@ -2156,6 +2241,7 @@ class ImageEditor { } } + return canvas.toDataURL(format); } @@ -2182,9 +2268,11 @@ class ImageEditor { } } + return canvas.toDataURL(format); } + getFinalMaskData(format = 'image/png') { let canvas = document.createElement('canvas'); canvas.width = this.realWidth; From 7e0b3bef535de6463822ce1688ef4f67878d6f8f Mon Sep 17 00:00:00 2001 From: Urabewe Date: Sat, 1 Nov 2025 11:19:19 -0500 Subject: [PATCH 07/18] Update image_editor.js --- src/wwwroot/js/genpage/helpers/image_editor.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/wwwroot/js/genpage/helpers/image_editor.js b/src/wwwroot/js/genpage/helpers/image_editor.js index 13359758c..6a0ff120e 100644 --- a/src/wwwroot/js/genpage/helpers/image_editor.js +++ b/src/wwwroot/js/genpage/helpers/image_editor.js @@ -934,9 +934,7 @@ class ImageEditorToolShape extends ImageEditorTool { this.strokeWidth = parseInt(this.strokeNumber.value); this.editor.redraw(); } - - - + draw() { if (this.isDrawing) { this.editor.ctx.save(); @@ -2225,7 +2223,6 @@ class ImageEditor { let [selectX, selectY] = this.imageCoordToCanvasCoord(this.selectX, this.selectY); this.drawSelectionBox(selectX, selectY, this.selectWidth * this.zoomLevel, this.selectHeight * this.zoomLevel, this.uiColor, 8 * this.zoomLevel, 0); } - this.activeTool.draw(); this.ctx.restore(); } @@ -2240,8 +2237,6 @@ class ImageEditor { layer.drawToBack(ctx, this.finalOffsetX, this.finalOffsetY, 1); } } - - return canvas.toDataURL(format); } @@ -2267,12 +2262,9 @@ class ImageEditor { layer.drawToBack(ctx, -minX, -minY, 1); } } - - return canvas.toDataURL(format); } - getFinalMaskData(format = 'image/png') { let canvas = document.createElement('canvas'); canvas.width = this.realWidth; From 80df440b39e58d3deb13b20f77e7e887707dba8e Mon Sep 17 00:00:00 2001 From: Urabewe Date: Sat, 1 Nov 2025 11:31:49 -0500 Subject: [PATCH 08/18] So much extra spaces BALEETED --- .../js/genpage/helpers/image_editor.js | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/src/wwwroot/js/genpage/helpers/image_editor.js b/src/wwwroot/js/genpage/helpers/image_editor.js index 6a0ff120e..7a5d2798a 100644 --- a/src/wwwroot/js/genpage/helpers/image_editor.js +++ b/src/wwwroot/js/genpage/helpers/image_editor.js @@ -886,14 +886,12 @@ class ImageEditorToolShape extends ImageEditorTool {
`; this.configDiv.innerHTML = colorHTML + shapeHTML + strokeHTML; - this.colorText = this.configDiv.querySelector('.id-col1'); this.colorSelector = this.configDiv.querySelector('.id-col2'); this.colorPickButton = this.configDiv.querySelector('.id-col3'); this.shapeSelect = this.configDiv.querySelector('.id-shape'); this.strokeNumber = this.configDiv.querySelector('.id-stroke1'); this.strokeSelector = this.configDiv.querySelector('.id-stroke2'); - this.colorText.addEventListener('input', () => { this.colorSelector.value = this.colorText.value; this.onConfigChange(); @@ -912,12 +910,10 @@ class ImageEditorToolShape extends ImageEditorTool { this.editor.activateTool('picker'); } }); - this.shapeSelect.addEventListener('change', () => { this.shape = this.shapeSelect.value; this.editor.redraw(); }); - enableSliderForBox(this.configDiv.querySelector('.id-stroke-block')); this.strokeNumber.addEventListener('change', () => { this.onConfigChange(); }); } @@ -942,7 +938,6 @@ class ImageEditorToolShape extends ImageEditorTool { this.editor.ctx.lineWidth = this.strokeWidth * this.editor.zoomLevel; this.editor.ctx.setLineDash([]); this.editor.ctx.beginPath(); - if (this.shape === 'rectangle') { let [x, y] = this.editor.imageCoordToCanvasCoord(Math.min(this.startX, this.currentX), Math.min(this.startY, this.currentY)); let [w, h] = [Math.abs(this.currentX - this.startX) * this.editor.zoomLevel, Math.abs(this.currentY - this.startY) * this.editor.zoomLevel]; @@ -953,7 +948,6 @@ class ImageEditorToolShape extends ImageEditorTool { let radius = Math.sqrt(Math.pow(this.currentX - this.startX, 2) + Math.pow(this.currentY - this.startY, 2)) / 2 * this.editor.zoomLevel; this.editor.ctx.arc(cx, cy, radius, 0, 2 * Math.PI); } - this.editor.ctx.stroke(); this.editor.ctx.restore(); } @@ -965,7 +959,6 @@ class ImageEditorToolShape extends ImageEditorTool { this.editor.ctx.lineWidth = shape.strokeWidth * this.editor.zoomLevel; this.editor.ctx.setLineDash([]); this.editor.ctx.beginPath(); - if (shape.type === 'rectangle') { let [x, y] = this.editor.imageCoordToCanvasCoord(shape.x, shape.y); let [w, h] = [shape.width * this.editor.zoomLevel, shape.height * this.editor.zoomLevel]; @@ -976,7 +969,6 @@ class ImageEditorToolShape extends ImageEditorTool { let radius = Math.sqrt(shape.width * shape.width + shape.height * shape.height) / 2 * this.editor.zoomLevel; this.editor.ctx.arc(cx, cy, radius, 0, 2 * Math.PI); } - this.editor.ctx.stroke(); this.editor.ctx.restore(); } @@ -987,7 +979,6 @@ class ImageEditorToolShape extends ImageEditorTool { ctx.lineWidth = shape.strokeWidth; ctx.setLineDash([]); ctx.beginPath(); - if (shape.type === 'rectangle') { let x = (shape.x + offsetX) * zoom; let y = (shape.y + offsetY) * zoom; @@ -1001,7 +992,6 @@ class ImageEditorToolShape extends ImageEditorTool { let radius = Math.sqrt(shape.width * shape.width + shape.height * shape.height) / 2 * zoom; ctx.arc(cx, cy, radius, 0, 2 * Math.PI); } - ctx.stroke(); ctx.restore(); } @@ -1015,14 +1005,12 @@ class ImageEditorToolShape extends ImageEditorTool { } this.editor.updateMousePosFrom(e); let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); - this.isDrawing = true; this.startX = mouseX; this.startY = mouseY; this.currentX = mouseX; this.currentY = mouseY; this.hasDrawn = false; - let target = this.editor.activeLayer; if (!target) { this.bufferLayer = null; @@ -1049,7 +1037,6 @@ class ImageEditorToolShape extends ImageEditorTool { this.editor.redraw(); return; } - if (!this.hasDrawn) { const idx = parent.childLayers.indexOf(this.bufferLayer); if (idx !== -1) { @@ -1061,9 +1048,7 @@ class ImageEditorToolShape extends ImageEditorTool { this.editor.redraw(); return; } - this.drawShape(); - const idx = parent.childLayers.indexOf(this.bufferLayer); if (idx !== -1) { parent.childLayers.splice(idx, 1); @@ -1152,32 +1137,26 @@ class ImageEditorToolShape extends ImageEditorTool { if (!this.isDrawing || !this.bufferLayer) { return; } - const parent = this.editor.activeLayer; if (!parent) { return; } - this.bufferLayer.ctx.clearRect(0, 0, this.bufferLayer.canvas.width, this.bufferLayer.canvas.height); - const layerX1 = Math.min(this.startLayerX, this.currentLayerX); const layerY1 = Math.min(this.startLayerY, this.currentLayerY); const width = Math.abs(this.currentLayerX - this.startLayerX); const height = Math.abs(this.currentLayerY - this.startLayerY); - if (width < 0.5 && height < 0.5) { this.bufferLayer.hasAnyContent = false; this.hasDrawn = false; this.editor.redraw(); return; } - this.bufferLayer.ctx.save(); this.bufferLayer.ctx.strokeStyle = this.color; this.bufferLayer.ctx.lineWidth = this.strokeWidth; this.bufferLayer.ctx.setLineDash([]); this.bufferLayer.ctx.beginPath(); - if (this.shape === 'rectangle') { this.bufferLayer.ctx.rect(layerX1, layerY1, width, height); } @@ -1187,7 +1166,6 @@ class ImageEditorToolShape extends ImageEditorTool { const radius = Math.sqrt(width * width + height * height) / 2; this.bufferLayer.ctx.arc(cx, cy, radius, 0, 2 * Math.PI); } - this.bufferLayer.ctx.stroke(); this.bufferLayer.ctx.restore(); this.bufferLayer.hasAnyContent = true; @@ -1195,7 +1173,6 @@ class ImageEditorToolShape extends ImageEditorTool { this.editor.markChanged(); this.editor.redraw(); } - } /** From 44867b722c2ce5b4e1fe3d0b7dfb07a70c6fd479 Mon Sep 17 00:00:00 2001 From: Urabewe Date: Sat, 1 Nov 2025 11:57:21 -0500 Subject: [PATCH 09/18] Update image_editor.js --- .../js/genpage/helpers/image_editor.js | 92 ++++++++----------- 1 file changed, 39 insertions(+), 53 deletions(-) diff --git a/src/wwwroot/js/genpage/helpers/image_editor.js b/src/wwwroot/js/genpage/helpers/image_editor.js index 7a5d2798a..d59f337bf 100644 --- a/src/wwwroot/js/genpage/helpers/image_editor.js +++ b/src/wwwroot/js/genpage/helpers/image_editor.js @@ -931,23 +931,27 @@ class ImageEditorToolShape extends ImageEditorTool { this.editor.redraw(); } + drawShapeToCanvas(ctx, type, x, y, width, height) { + ctx.beginPath(); + if (type === 'rectangle') { + ctx.rect(x, y, width, height); + } + else if (type === 'circle') { + let radius = Math.sqrt(width * width + height * height) / 2; + ctx.arc(x + width / 2, y + height / 2, radius, 0, 2 * Math.PI); + } + } + draw() { if (this.isDrawing) { this.editor.ctx.save(); this.editor.ctx.strokeStyle = this.color; this.editor.ctx.lineWidth = this.strokeWidth * this.editor.zoomLevel; this.editor.ctx.setLineDash([]); - this.editor.ctx.beginPath(); - if (this.shape === 'rectangle') { - let [x, y] = this.editor.imageCoordToCanvasCoord(Math.min(this.startX, this.currentX), Math.min(this.startY, this.currentY)); - let [w, h] = [Math.abs(this.currentX - this.startX) * this.editor.zoomLevel, Math.abs(this.currentY - this.startY) * this.editor.zoomLevel]; - this.editor.ctx.rect(x, y, w, h); - } - else if (this.shape === 'circle') { - let [cx, cy] = this.editor.imageCoordToCanvasCoord((this.startX + this.currentX) / 2, (this.startY + this.currentY) / 2); - let radius = Math.sqrt(Math.pow(this.currentX - this.startX, 2) + Math.pow(this.currentY - this.startY, 2)) / 2 * this.editor.zoomLevel; - this.editor.ctx.arc(cx, cy, radius, 0, 2 * Math.PI); - } + let [x, y] = this.editor.imageCoordToCanvasCoord(Math.min(this.startX, this.currentX), Math.min(this.startY, this.currentY)); + let width = Math.abs(this.currentX - this.startX) * this.editor.zoomLevel; + let height = Math.abs(this.currentY - this.startY) * this.editor.zoomLevel; + this.drawShapeToCanvas(this.editor.ctx, this.shape, x, y, width, height); this.editor.ctx.stroke(); this.editor.ctx.restore(); } @@ -958,46 +962,30 @@ class ImageEditorToolShape extends ImageEditorTool { this.editor.ctx.strokeStyle = shape.color; this.editor.ctx.lineWidth = shape.strokeWidth * this.editor.zoomLevel; this.editor.ctx.setLineDash([]); - this.editor.ctx.beginPath(); - if (shape.type === 'rectangle') { - let [x, y] = this.editor.imageCoordToCanvasCoord(shape.x, shape.y); - let [w, h] = [shape.width * this.editor.zoomLevel, shape.height * this.editor.zoomLevel]; - this.editor.ctx.rect(x, y, w, h); - } - else if (shape.type === 'circle') { - let [cx, cy] = this.editor.imageCoordToCanvasCoord(shape.x + shape.width / 2, shape.y + shape.height / 2); - let radius = Math.sqrt(shape.width * shape.width + shape.height * shape.height) / 2 * this.editor.zoomLevel; - this.editor.ctx.arc(cx, cy, radius, 0, 2 * Math.PI); - } + let [x, y] = this.editor.imageCoordToCanvasCoord(shape.x, shape.y); + let width = Math.abs(shape.width) * this.editor.zoomLevel; + let height = Math.abs(shape.height) * this.editor.zoomLevel; + this.drawShapeToCanvas(this.editor.ctx, shape.type, x, y, width, height); this.editor.ctx.stroke(); this.editor.ctx.restore(); } - drawShapeToCanvas(ctx, shape, zoom, offsetX = 0, offsetY = 0) { + drawShapeObjectToCanvas(ctx, shape, zoom, offsetX = 0, offsetY = 0) { ctx.save(); ctx.strokeStyle = shape.color; ctx.lineWidth = shape.strokeWidth; ctx.setLineDash([]); - ctx.beginPath(); - if (shape.type === 'rectangle') { - let x = (shape.x + offsetX) * zoom; - let y = (shape.y + offsetY) * zoom; - let w = shape.width * zoom; - let h = shape.height * zoom; - ctx.rect(x, y, w, h); - } - else if (shape.type === 'circle') { - let cx = (shape.x + shape.width / 2 + offsetX) * zoom; - let cy = (shape.y + shape.height / 2 + offsetY) * zoom; - let radius = Math.sqrt(shape.width * shape.width + shape.height * shape.height) / 2 * zoom; - ctx.arc(cx, cy, radius, 0, 2 * Math.PI); - } + let x = (shape.x + offsetX) * zoom; + let y = (shape.y + offsetY) * zoom; + let width = Math.abs(shape.width) * zoom; + let height = Math.abs(shape.height) * zoom; + this.drawShapeToCanvas(ctx, shape.type, x, y, width, height); ctx.stroke(); ctx.restore(); } onMouseDown(e) { - if (e.button !== 0 && e.button !== undefined) { + if (e.button != 0) { return; } if (this.isDrawing) { @@ -1066,21 +1054,20 @@ class ImageEditorToolShape extends ImageEditorTool { } onMouseMove(e) { - if (this.isDrawing) { - this.editor.updateMousePosFrom(e); - let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); - this.currentX = mouseX; - this.currentY = mouseY; - let target = this.editor.activeLayer; - if (target) { - let [layerX, layerY] = target.canvasCoordToLayerCoord(this.editor.mouseX, this.editor.mouseY); - this.currentLayerX = layerX; - this.currentLayerY = layerY; - } - this.drawShape(); - return true; + if (!this.isDrawing) { + return; } - return false; + this.editor.updateMousePosFrom(e); + let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); + this.currentX = mouseX; + this.currentY = mouseY; + let target = this.editor.activeLayer; + if (target) { + let [layerX, layerY] = target.canvasCoordToLayerCoord(this.editor.mouseX, this.editor.mouseY); + this.currentLayerX = layerX; + this.currentLayerY = layerY; + } + this.drawShape(); } onGlobalMouseMove(e) { @@ -2152,7 +2139,6 @@ class ImageEditor { this.ctx.restore(); } - redraw() { if (!this.canvas) { return; From 35713ae76e4d98a57f10d0a335b84421f1ed3fc6 Mon Sep 17 00:00:00 2001 From: "Alex \"mcmonkey\" Goodwin" Date: Sun, 2 Nov 2025 11:20:20 -0800 Subject: [PATCH 10/18] basic code deborks --- .../js/genpage/helpers/image_editor.js | 98 ++++++++----------- 1 file changed, 43 insertions(+), 55 deletions(-) diff --git a/src/wwwroot/js/genpage/helpers/image_editor.js b/src/wwwroot/js/genpage/helpers/image_editor.js index d59f337bf..fb3d4003c 100644 --- a/src/wwwroot/js/genpage/helpers/image_editor.js +++ b/src/wwwroot/js/genpage/helpers/image_editor.js @@ -991,7 +991,6 @@ class ImageEditorToolShape extends ImageEditorTool { if (this.isDrawing) { this.finishDrawing(); } - this.editor.updateMousePosFrom(e); let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); this.isDrawing = true; this.startX = mouseX; @@ -1016,48 +1015,48 @@ class ImageEditorToolShape extends ImageEditorTool { } finishDrawing() { - if (this.isDrawing && this.bufferLayer) { - const parent = this.editor.activeLayer; - if (!parent) { - this.bufferLayer = null; - this.isDrawing = false; - this.hasDrawn = false; - this.editor.redraw(); - return; - } - if (!this.hasDrawn) { - const idx = parent.childLayers.indexOf(this.bufferLayer); - if (idx !== -1) { - parent.childLayers.splice(idx, 1); - } - this.bufferLayer = null; - this.isDrawing = false; - this.hasDrawn = false; - this.editor.redraw(); - return; - } - this.drawShape(); + if (!this.isDrawing || !this.bufferLayer) { + return; + } + const parent = this.editor.activeLayer; + if (!parent) { + this.bufferLayer = null; + this.isDrawing = false; + this.hasDrawn = false; + this.editor.redraw(); + return; + } + if (!this.hasDrawn) { const idx = parent.childLayers.indexOf(this.bufferLayer); if (idx !== -1) { parent.childLayers.splice(idx, 1); } - const offset = parent.getOffset(); - parent.saveBeforeEdit(); - this.bufferLayer.drawToBackDirect(parent.ctx, -offset[0], -offset[1], 1); - parent.hasAnyContent = true; this.bufferLayer = null; this.isDrawing = false; this.hasDrawn = false; - this.editor.markChanged(); this.editor.redraw(); + return; } + this.drawShape(); + const idx = parent.childLayers.indexOf(this.bufferLayer); + if (idx !== -1) { + parent.childLayers.splice(idx, 1); + } + const offset = parent.getOffset(); + parent.saveBeforeEdit(); + this.bufferLayer.drawToBackDirect(parent.ctx, -offset[0], -offset[1], 1); + parent.hasAnyContent = true; + this.bufferLayer = null; + this.isDrawing = false; + this.hasDrawn = false; + this.editor.markChanged(); + this.editor.redraw(); } onMouseMove(e) { if (!this.isDrawing) { return; } - this.editor.updateMousePosFrom(e); let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); this.currentX = mouseX; this.currentY = mouseY; @@ -1071,31 +1070,25 @@ class ImageEditorToolShape extends ImageEditorTool { } onGlobalMouseMove(e) { - if (this.isDrawing) { - this.editor.updateMousePosFrom(e); - let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); - this.currentX = mouseX; - this.currentY = mouseY; - let target = this.editor.activeLayer; - if (target) { - let [layerX, layerY] = target.canvasCoordToLayerCoord(this.editor.mouseX, this.editor.mouseY); - this.currentLayerX = layerX; - this.currentLayerY = layerY; - } - this.drawShape(); - return true; + if (!this.isDrawing) { + return; } - return false; + let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); + this.currentX = mouseX; + this.currentY = mouseY; + let target = this.editor.activeLayer; + if (target) { + let [layerX, layerY] = target.canvasCoordToLayerCoord(this.editor.mouseX, this.editor.mouseY); + this.currentLayerX = layerX; + this.currentLayerY = layerY; + } + this.drawShape(); } onMouseUp(e) { - if (e.button !== 0 && e.button !== undefined) { + if (e.button != 0 || !this.isDrawing) { return; } - if (!this.isDrawing) { - return; - } - this.editor.updateMousePosFrom(e); let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); this.currentX = mouseX; this.currentY = mouseY; @@ -1109,15 +1102,10 @@ class ImageEditorToolShape extends ImageEditorTool { } onGlobalMouseUp(e) { - if (e.button !== 0 && e.button !== undefined) { - return false; - } - if (this.isDrawing) { - this.editor.updateMousePosFrom(e); - this.finishDrawing(); - return true; + if (e.button != 0 || !this.isDrawing) { + return; } - return false; + this.finishDrawing(); } drawShape() { From 7fff663e34838bf8ae2b9236883de6cc9288fcf6 Mon Sep 17 00:00:00 2001 From: "Alex \"mcmonkey\" Goodwin" Date: Sun, 2 Nov 2025 11:26:53 -0800 Subject: [PATCH 11/18] more basic code bits --- .../js/genpage/helpers/image_editor.js | 76 +++++++++---------- 1 file changed, 35 insertions(+), 41 deletions(-) diff --git a/src/wwwroot/js/genpage/helpers/image_editor.js b/src/wwwroot/js/genpage/helpers/image_editor.js index fb3d4003c..9a78ed32f 100644 --- a/src/wwwroot/js/genpage/helpers/image_editor.js +++ b/src/wwwroot/js/genpage/helpers/image_editor.js @@ -842,7 +842,7 @@ class ImageEditorToolBucket extends ImageEditorTool { */ class ImageEditorToolShape extends ImageEditorTool { constructor(editor) { - super(editor, 'shape', 'shape', 'Shape', 'Create different shapes for AI editing.\nRectangle: Click and drag\nCircle: Click and drag\nHotKey: X', 'x'); + super(editor, 'shape', 'shape', 'Shape', 'Create basic colored shape outlines.\nClick and drag to draw a shape.\nHotKey: X', 'x'); this.cursor = 'crosshair'; this.color = '#ff0000'; this.strokeWidth = 4; @@ -858,7 +858,6 @@ class ImageEditorToolShape extends ImageEditorTool { this.currentLayerY = 0; this.bufferLayer = null; this.hasDrawn = false; - let colorHTML = `
@@ -866,7 +865,6 @@ class ImageEditorToolShape extends ImageEditorTool {
`; - let shapeHTML = `
@@ -875,16 +873,14 @@ class ImageEditorToolShape extends ImageEditorTool {
`; - let strokeHTML = `
- +
`; - this.configDiv.innerHTML = colorHTML + shapeHTML + strokeHTML; this.colorText = this.configDiv.querySelector('.id-col1'); this.colorSelector = this.configDiv.querySelector('.id-col2'); @@ -904,7 +900,8 @@ class ImageEditorToolShape extends ImageEditorTool { if (this.colorPickButton.classList.contains('interrupt-button')) { this.colorPickButton.classList.remove('interrupt-button'); this.editor.activateTool(this.id); - } else { + } + else { this.colorPickButton.classList.add('interrupt-button'); this.editor.pickerTool.toolFor = this; this.editor.activateTool('picker'); @@ -933,35 +930,34 @@ class ImageEditorToolShape extends ImageEditorTool { drawShapeToCanvas(ctx, type, x, y, width, height) { ctx.beginPath(); - if (type === 'rectangle') { + if (type == 'rectangle') { ctx.rect(x, y, width, height); } - else if (type === 'circle') { + else if (type == 'circle') { let radius = Math.sqrt(width * width + height * height) / 2; ctx.arc(x + width / 2, y + height / 2, radius, 0, 2 * Math.PI); } } draw() { - if (this.isDrawing) { - this.editor.ctx.save(); - this.editor.ctx.strokeStyle = this.color; - this.editor.ctx.lineWidth = this.strokeWidth * this.editor.zoomLevel; - this.editor.ctx.setLineDash([]); - let [x, y] = this.editor.imageCoordToCanvasCoord(Math.min(this.startX, this.currentX), Math.min(this.startY, this.currentY)); - let width = Math.abs(this.currentX - this.startX) * this.editor.zoomLevel; - let height = Math.abs(this.currentY - this.startY) * this.editor.zoomLevel; - this.drawShapeToCanvas(this.editor.ctx, this.shape, x, y, width, height); - this.editor.ctx.stroke(); - this.editor.ctx.restore(); + if (!this.isDrawing) { + return; } + this.editor.ctx.save(); + this.editor.ctx.strokeStyle = this.color; + this.editor.ctx.lineWidth = this.strokeWidth * this.editor.zoomLevel; + let [x, y] = this.editor.imageCoordToCanvasCoord(Math.min(this.startX, this.currentX), Math.min(this.startY, this.currentY)); + let width = Math.abs(this.currentX - this.startX) * this.editor.zoomLevel; + let height = Math.abs(this.currentY - this.startY) * this.editor.zoomLevel; + this.drawShapeToCanvas(this.editor.ctx, this.shape, x, y, width, height); + this.editor.ctx.stroke(); + this.editor.ctx.restore(); } drawShape(shape) { this.editor.ctx.save(); this.editor.ctx.strokeStyle = shape.color; this.editor.ctx.lineWidth = shape.strokeWidth * this.editor.zoomLevel; - this.editor.ctx.setLineDash([]); let [x, y] = this.editor.imageCoordToCanvasCoord(shape.x, shape.y); let width = Math.abs(shape.width) * this.editor.zoomLevel; let height = Math.abs(shape.height) * this.editor.zoomLevel; @@ -974,7 +970,6 @@ class ImageEditorToolShape extends ImageEditorTool { ctx.save(); ctx.strokeStyle = shape.color; ctx.lineWidth = shape.strokeWidth; - ctx.setLineDash([]); let x = (shape.x + offsetX) * zoom; let y = (shape.y + offsetY) * zoom; let width = Math.abs(shape.width) * zoom; @@ -1018,7 +1013,7 @@ class ImageEditorToolShape extends ImageEditorTool { if (!this.isDrawing || !this.bufferLayer) { return; } - const parent = this.editor.activeLayer; + let parent = this.editor.activeLayer; if (!parent) { this.bufferLayer = null; this.isDrawing = false; @@ -1027,8 +1022,8 @@ class ImageEditorToolShape extends ImageEditorTool { return; } if (!this.hasDrawn) { - const idx = parent.childLayers.indexOf(this.bufferLayer); - if (idx !== -1) { + let idx = parent.childLayers.indexOf(this.bufferLayer); + if (idx != -1) { parent.childLayers.splice(idx, 1); } this.bufferLayer = null; @@ -1038,11 +1033,11 @@ class ImageEditorToolShape extends ImageEditorTool { return; } this.drawShape(); - const idx = parent.childLayers.indexOf(this.bufferLayer); - if (idx !== -1) { + let idx = parent.childLayers.indexOf(this.bufferLayer); + if (idx != -1) { parent.childLayers.splice(idx, 1); } - const offset = parent.getOffset(); + let offset = parent.getOffset(); parent.saveBeforeEdit(); this.bufferLayer.drawToBackDirect(parent.ctx, -offset[0], -offset[1], 1); parent.hasAnyContent = true; @@ -1112,15 +1107,15 @@ class ImageEditorToolShape extends ImageEditorTool { if (!this.isDrawing || !this.bufferLayer) { return; } - const parent = this.editor.activeLayer; + let parent = this.editor.activeLayer; if (!parent) { return; } this.bufferLayer.ctx.clearRect(0, 0, this.bufferLayer.canvas.width, this.bufferLayer.canvas.height); - const layerX1 = Math.min(this.startLayerX, this.currentLayerX); - const layerY1 = Math.min(this.startLayerY, this.currentLayerY); - const width = Math.abs(this.currentLayerX - this.startLayerX); - const height = Math.abs(this.currentLayerY - this.startLayerY); + let layerX1 = Math.min(this.startLayerX, this.currentLayerX); + let layerY1 = Math.min(this.startLayerY, this.currentLayerY); + let width = Math.abs(this.currentLayerX - this.startLayerX); + let height = Math.abs(this.currentLayerY - this.startLayerY); if (width < 0.5 && height < 0.5) { this.bufferLayer.hasAnyContent = false; this.hasDrawn = false; @@ -1130,15 +1125,14 @@ class ImageEditorToolShape extends ImageEditorTool { this.bufferLayer.ctx.save(); this.bufferLayer.ctx.strokeStyle = this.color; this.bufferLayer.ctx.lineWidth = this.strokeWidth; - this.bufferLayer.ctx.setLineDash([]); this.bufferLayer.ctx.beginPath(); - if (this.shape === 'rectangle') { + if (this.shape == 'rectangle') { this.bufferLayer.ctx.rect(layerX1, layerY1, width, height); } - else if (this.shape === 'circle') { - const cx = layerX1 + width / 2; - const cy = layerY1 + height / 2; - const radius = Math.sqrt(width * width + height * height) / 2; + else if (this.shape == 'circle') { + let cx = layerX1 + width / 2; + let cy = layerY1 + height / 2; + let radius = Math.sqrt(width * width + height * height) / 2; this.bufferLayer.ctx.arc(cx, cy, radius, 0, 2 * Math.PI); } this.bufferLayer.ctx.stroke(); @@ -1699,7 +1693,7 @@ class ImageEditor { } onKeyDown(e) { - if (e.key === 'Alt') { + if (e.key == 'Alt') { e.preventDefault(); this.handleAltDown(); } @@ -1725,7 +1719,7 @@ class ImageEditor { } onGlobalKeyUp(e) { - if (e.key === 'Alt') { + if (e.key == 'Alt') { this.altDown = false; this.handleAltUp(); } From 0ea4d712aa600067e7166e7e51878eefc52c647a Mon Sep 17 00:00:00 2001 From: "Alex \"mcmonkey\" Goodwin" Date: Sun, 2 Nov 2025 11:31:35 -0800 Subject: [PATCH 12/18] final basic code fix --- src/wwwroot/js/genpage/helpers/image_editor.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/wwwroot/js/genpage/helpers/image_editor.js b/src/wwwroot/js/genpage/helpers/image_editor.js index 9a78ed32f..04f90feb5 100644 --- a/src/wwwroot/js/genpage/helpers/image_editor.js +++ b/src/wwwroot/js/genpage/helpers/image_editor.js @@ -931,12 +931,13 @@ class ImageEditorToolShape extends ImageEditorTool { drawShapeToCanvas(ctx, type, x, y, width, height) { ctx.beginPath(); if (type == 'rectangle') { - ctx.rect(x, y, width, height); + ctx.rect(Math.round(x), Math.round(y), Math.round(width), Math.round(height)); } else if (type == 'circle') { let radius = Math.sqrt(width * width + height * height) / 2; - ctx.arc(x + width / 2, y + height / 2, radius, 0, 2 * Math.PI); + ctx.arc(Math.round(x + width / 2), Math.round(y + height / 2), Math.round(radius), 0, 2 * Math.PI); } + ctx.stroke(); } draw() { @@ -950,7 +951,6 @@ class ImageEditorToolShape extends ImageEditorTool { let width = Math.abs(this.currentX - this.startX) * this.editor.zoomLevel; let height = Math.abs(this.currentY - this.startY) * this.editor.zoomLevel; this.drawShapeToCanvas(this.editor.ctx, this.shape, x, y, width, height); - this.editor.ctx.stroke(); this.editor.ctx.restore(); } @@ -962,7 +962,6 @@ class ImageEditorToolShape extends ImageEditorTool { let width = Math.abs(shape.width) * this.editor.zoomLevel; let height = Math.abs(shape.height) * this.editor.zoomLevel; this.drawShapeToCanvas(this.editor.ctx, shape.type, x, y, width, height); - this.editor.ctx.stroke(); this.editor.ctx.restore(); } @@ -975,7 +974,6 @@ class ImageEditorToolShape extends ImageEditorTool { let width = Math.abs(shape.width) * zoom; let height = Math.abs(shape.height) * zoom; this.drawShapeToCanvas(ctx, shape.type, x, y, width, height); - ctx.stroke(); ctx.restore(); } From 8bc1afc20aa3311022cf822f6266d862c192a855 Mon Sep 17 00:00:00 2001 From: Urabewe Date: Tue, 4 Nov 2025 10:42:25 -0600 Subject: [PATCH 13/18] Update image_editor.js --- .../js/genpage/helpers/image_editor.js | 204 ++++++++++++------ 1 file changed, 137 insertions(+), 67 deletions(-) diff --git a/src/wwwroot/js/genpage/helpers/image_editor.js b/src/wwwroot/js/genpage/helpers/image_editor.js index d59f337bf..6dd8c1ccd 100644 --- a/src/wwwroot/js/genpage/helpers/image_editor.js +++ b/src/wwwroot/js/genpage/helpers/image_editor.js @@ -931,6 +931,20 @@ class ImageEditorToolShape extends ImageEditorTool { this.editor.redraw(); } + drawRectangleBorder(ctx, x, y, width, height, thickness) { + width = Math.max(1, Math.floor(width)); + height = Math.max(1, Math.floor(height)); + thickness = Math.max(1, Math.floor(thickness)); + thickness = Math.min(thickness, width, height); + ctx.fillRect(x, y, width, thickness); + ctx.fillRect(x, y + height - thickness, width, thickness); + let verticalHeight = height - thickness * 2; + if (verticalHeight > 0) { + ctx.fillRect(x, y + thickness, thickness, verticalHeight); + ctx.fillRect(x + width - thickness, y + thickness, thickness, verticalHeight); + } + } + drawShapeToCanvas(ctx, type, x, y, width, height) { ctx.beginPath(); if (type === 'rectangle') { @@ -943,47 +957,47 @@ class ImageEditorToolShape extends ImageEditorTool { } draw() { - if (this.isDrawing) { - this.editor.ctx.save(); - this.editor.ctx.strokeStyle = this.color; - this.editor.ctx.lineWidth = this.strokeWidth * this.editor.zoomLevel; - this.editor.ctx.setLineDash([]); - let [x, y] = this.editor.imageCoordToCanvasCoord(Math.min(this.startX, this.currentX), Math.min(this.startY, this.currentY)); - let width = Math.abs(this.currentX - this.startX) * this.editor.zoomLevel; - let height = Math.abs(this.currentY - this.startY) * this.editor.zoomLevel; - this.drawShapeToCanvas(this.editor.ctx, this.shape, x, y, width, height); - this.editor.ctx.stroke(); - this.editor.ctx.restore(); + if (!this.isDrawing) { + return; } - } - - drawShape(shape) { + const target = this.editor.activeLayer; + if (!target) { + return; + } + const startX = Math.min(this.startLayerX, this.currentLayerX); + const startY = Math.min(this.startLayerY, this.currentLayerY); + const endX = Math.max(this.startLayerX, this.currentLayerX); + const endY = Math.max(this.startLayerY, this.currentLayerY); + const width = endX - startX; + const height = endY - startY; + if (width === 0 && height === 0) { + return; + } + const [imageX1, imageY1] = target.layerCoordToImageCoord(startX, startY); + const [imageX2, imageY2] = target.layerCoordToImageCoord(endX, endY); + const [canvasX1, canvasY1] = this.editor.imageCoordToCanvasCoord(imageX1, imageY1); + const [canvasX2, canvasY2] = this.editor.imageCoordToCanvasCoord(imageX2, imageY2); + const canvasWidth = canvasX2 - canvasX1; + const canvasHeight = canvasY2 - canvasY1; this.editor.ctx.save(); - this.editor.ctx.strokeStyle = shape.color; - this.editor.ctx.lineWidth = shape.strokeWidth * this.editor.zoomLevel; + const prevSmoothing = this.editor.ctx.imageSmoothingEnabled; + this.editor.ctx.imageSmoothingEnabled = false; this.editor.ctx.setLineDash([]); - let [x, y] = this.editor.imageCoordToCanvasCoord(shape.x, shape.y); - let width = Math.abs(shape.width) * this.editor.zoomLevel; - let height = Math.abs(shape.height) * this.editor.zoomLevel; - this.drawShapeToCanvas(this.editor.ctx, shape.type, x, y, width, height); - this.editor.ctx.stroke(); + if (this.shape === 'rectangle') { + const thickness = Math.max(1, Math.round(this.strokeWidth * this.editor.zoomLevel)); + this.editor.ctx.fillStyle = this.color; + this.drawRectangleBorder(this.editor.ctx, Math.round(canvasX1), Math.round(canvasY1), Math.round(canvasWidth), Math.round(canvasHeight), thickness); + } + else { + this.editor.ctx.strokeStyle = this.color; + this.editor.ctx.lineWidth = Math.max(1, Math.round(this.strokeWidth * this.editor.zoomLevel)); + this.drawShapeToCanvas(this.editor.ctx, this.shape, canvasX1, canvasY1, canvasWidth, canvasHeight); + this.editor.ctx.stroke(); + } + this.editor.ctx.imageSmoothingEnabled = prevSmoothing; this.editor.ctx.restore(); } - drawShapeObjectToCanvas(ctx, shape, zoom, offsetX = 0, offsetY = 0) { - ctx.save(); - ctx.strokeStyle = shape.color; - ctx.lineWidth = shape.strokeWidth; - ctx.setLineDash([]); - let x = (shape.x + offsetX) * zoom; - let y = (shape.y + offsetY) * zoom; - let width = Math.abs(shape.width) * zoom; - let height = Math.abs(shape.height) * zoom; - this.drawShapeToCanvas(ctx, shape.type, x, y, width, height); - ctx.stroke(); - ctx.restore(); - } - onMouseDown(e) { if (e.button != 0) { return; @@ -993,6 +1007,8 @@ class ImageEditorToolShape extends ImageEditorTool { } this.editor.updateMousePosFrom(e); let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); + mouseX = Math.round(mouseX); + mouseY = Math.round(mouseY); this.isDrawing = true; this.startX = mouseX; this.startY = mouseY; @@ -1005,7 +1021,9 @@ class ImageEditorToolShape extends ImageEditorTool { this.isDrawing = false; return; } - let [layerX, layerY] = target.canvasCoordToLayerCoord(this.editor.mouseX, this.editor.mouseY); + let [layerX, layerY] = target.imageCoordToLayerCoord(mouseX, mouseY); + layerX = Math.round(layerX); + layerY = Math.round(layerY); this.startLayerX = layerX; this.startLayerY = layerY; this.currentLayerX = layerX; @@ -1059,29 +1077,33 @@ class ImageEditorToolShape extends ImageEditorTool { } this.editor.updateMousePosFrom(e); let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); + mouseX = Math.round(mouseX); + mouseY = Math.round(mouseY); this.currentX = mouseX; this.currentY = mouseY; let target = this.editor.activeLayer; if (target) { - let [layerX, layerY] = target.canvasCoordToLayerCoord(this.editor.mouseX, this.editor.mouseY); - this.currentLayerX = layerX; - this.currentLayerY = layerY; + let [layerX, layerY] = target.imageCoordToLayerCoord(mouseX, mouseY); + this.currentLayerX = Math.round(layerX); + this.currentLayerY = Math.round(layerY); } this.drawShape(); } onGlobalMouseMove(e) { if (this.isDrawing) { - this.editor.updateMousePosFrom(e); - let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); - this.currentX = mouseX; - this.currentY = mouseY; - let target = this.editor.activeLayer; - if (target) { - let [layerX, layerY] = target.canvasCoordToLayerCoord(this.editor.mouseX, this.editor.mouseY); - this.currentLayerX = layerX; - this.currentLayerY = layerY; - } + this.editor.updateMousePosFrom(e); + let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); + mouseX = Math.round(mouseX); + mouseY = Math.round(mouseY); + this.currentX = mouseX; + this.currentY = mouseY; + let target = this.editor.activeLayer; + if (target) { + let [layerX, layerY] = target.imageCoordToLayerCoord(mouseX, mouseY); + this.currentLayerX = Math.round(layerX); + this.currentLayerY = Math.round(layerY); + } this.drawShape(); return true; } @@ -1097,13 +1119,15 @@ class ImageEditorToolShape extends ImageEditorTool { } this.editor.updateMousePosFrom(e); let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); + mouseX = Math.round(mouseX); + mouseY = Math.round(mouseY); this.currentX = mouseX; this.currentY = mouseY; let target = this.editor.activeLayer; if (target) { - let [layerX, layerY] = target.canvasCoordToLayerCoord(this.editor.mouseX, this.editor.mouseY); - this.currentLayerX = layerX; - this.currentLayerY = layerY; + let [layerX, layerY] = target.imageCoordToLayerCoord(mouseX, mouseY); + this.currentLayerX = Math.round(layerX); + this.currentLayerY = Math.round(layerY); } this.finishDrawing(); } @@ -1114,6 +1138,17 @@ class ImageEditorToolShape extends ImageEditorTool { } if (this.isDrawing) { this.editor.updateMousePosFrom(e); + let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); + mouseX = Math.round(mouseX); + mouseY = Math.round(mouseY); + this.currentX = mouseX; + this.currentY = mouseY; + let target = this.editor.activeLayer; + if (target) { + let [layerX, layerY] = target.imageCoordToLayerCoord(mouseX, mouseY); + this.currentLayerX = Math.round(layerX); + this.currentLayerY = Math.round(layerY); + } this.finishDrawing(); return true; } @@ -1129,31 +1164,34 @@ class ImageEditorToolShape extends ImageEditorTool { return; } this.bufferLayer.ctx.clearRect(0, 0, this.bufferLayer.canvas.width, this.bufferLayer.canvas.height); - const layerX1 = Math.min(this.startLayerX, this.currentLayerX); - const layerY1 = Math.min(this.startLayerY, this.currentLayerY); - const width = Math.abs(this.currentLayerX - this.startLayerX); - const height = Math.abs(this.currentLayerY - this.startLayerY); - if (width < 0.5 && height < 0.5) { + const startX = Math.round(Math.min(this.startLayerX, this.currentLayerX)); + const startY = Math.round(Math.min(this.startLayerY, this.currentLayerY)); + const endX = Math.round(Math.max(this.startLayerX, this.currentLayerX)); + const endY = Math.round(Math.max(this.startLayerY, this.currentLayerY)); + const width = endX - startX; + const height = endY - startY; + if (width === 0 && height === 0) { this.bufferLayer.hasAnyContent = false; this.hasDrawn = false; this.editor.redraw(); return; } this.bufferLayer.ctx.save(); - this.bufferLayer.ctx.strokeStyle = this.color; - this.bufferLayer.ctx.lineWidth = this.strokeWidth; + const prevSmoothing = this.bufferLayer.ctx.imageSmoothingEnabled; + this.bufferLayer.ctx.imageSmoothingEnabled = false; this.bufferLayer.ctx.setLineDash([]); - this.bufferLayer.ctx.beginPath(); if (this.shape === 'rectangle') { - this.bufferLayer.ctx.rect(layerX1, layerY1, width, height); + const thickness = Math.max(1, Math.round(this.strokeWidth)); + this.bufferLayer.ctx.fillStyle = this.color; + this.drawRectangleBorder(this.bufferLayer.ctx, startX, startY, width, height, thickness); } - else if (this.shape === 'circle') { - const cx = layerX1 + width / 2; - const cy = layerY1 + height / 2; - const radius = Math.sqrt(width * width + height * height) / 2; - this.bufferLayer.ctx.arc(cx, cy, radius, 0, 2 * Math.PI); + else { + this.bufferLayer.ctx.strokeStyle = this.color; + this.bufferLayer.ctx.lineWidth = Math.max(1, Math.round(this.strokeWidth)); + this.drawShapeToCanvas(this.bufferLayer.ctx, this.shape, startX, startY, width, height); + this.bufferLayer.ctx.stroke(); } - this.bufferLayer.ctx.stroke(); + this.bufferLayer.ctx.imageSmoothingEnabled = prevSmoothing; this.bufferLayer.ctx.restore(); this.bufferLayer.hasAnyContent = true; this.hasDrawn = true; @@ -1351,6 +1389,22 @@ class ImageEditorLayer { return [x2, y2]; } + imageCoordToLayerCoord(x, y) { + let [offsetX, offsetY] = this.getOffset(); + let relWidth = this.width / this.canvas.width; + let relHeight = this.height / this.canvas.height; + [x, y] = [x - offsetX, y - offsetY]; + let angle = -this.rotation; + let [cx, cy] = [this.width / 2, this.height / 2]; + let [x2, y2] = [x - cx, y - cy]; + let cos = Math.cos(angle), sin = Math.sin(angle); + let xRot = x2 * cos - y2 * sin; + let yRot = x2 * sin + y2 * cos; + xRot += cx; + yRot += cy; + return [xRot / relWidth, yRot / relHeight]; + } + layerCoordToCanvasCoord(x, y) { let [x2, y2] = this.editor.imageCoordToCanvasCoord(x, y); let [offsetX, offsetY] = this.getOffset(); @@ -1360,6 +1414,22 @@ class ImageEditorLayer { return [x2, y2]; } + layerCoordToImageCoord(x, y) { + let relWidth = this.width / this.canvas.width; + let relHeight = this.height / this.canvas.height; + [x, y] = [x * relWidth, y * relHeight]; + let angle = this.rotation; + let [cx, cy] = [this.width / 2, this.height / 2]; + let [x2, y2] = [x - cx, y - cy]; + let cos = Math.cos(angle), sin = Math.sin(angle); + let xRot = x2 * cos - y2 * sin; + let yRot = x2 * sin + y2 * cos; + xRot += cx; + yRot += cy; + let [offsetX, offsetY] = this.getOffset(); + return [xRot + offsetX, yRot + offsetY]; + } + drawFilledCircle(x, y, radius, color) { this.ctx.fillStyle = color; this.ctx.beginPath(); From ae577c85e0e5929d87028e1a4adb9c1b379724d4 Mon Sep 17 00:00:00 2001 From: Urabewe Date: Tue, 4 Nov 2025 11:12:54 -0600 Subject: [PATCH 14/18] Update image_editor.js --- .../js/genpage/helpers/image_editor.js | 267 ++++++++++++------ 1 file changed, 177 insertions(+), 90 deletions(-) diff --git a/src/wwwroot/js/genpage/helpers/image_editor.js b/src/wwwroot/js/genpage/helpers/image_editor.js index 04f90feb5..4acf6edf7 100644 --- a/src/wwwroot/js/genpage/helpers/image_editor.js +++ b/src/wwwroot/js/genpage/helpers/image_editor.js @@ -865,6 +865,7 @@ class ImageEditorToolShape extends ImageEditorTool {
`; + let shapeHTML = `
@@ -873,14 +874,16 @@ class ImageEditorToolShape extends ImageEditorTool {
`; + let strokeHTML = `
- +
`; + this.configDiv.innerHTML = colorHTML + shapeHTML + strokeHTML; this.colorText = this.configDiv.querySelector('.id-col1'); this.colorSelector = this.configDiv.querySelector('.id-col2'); @@ -928,6 +931,20 @@ class ImageEditorToolShape extends ImageEditorTool { this.editor.redraw(); } + drawRectangleBorder(ctx, x, y, width, height, thickness) { + width = Math.max(1, Math.floor(width)); + height = Math.max(1, Math.floor(height)); + thickness = Math.max(1, Math.floor(thickness)); + thickness = Math.min(thickness, width, height); + ctx.fillRect(x, y, width, thickness); + ctx.fillRect(x, y + height - thickness, width, thickness); + let verticalHeight = height - thickness * 2; + if (verticalHeight > 0) { + ctx.fillRect(x, y + thickness, thickness, verticalHeight); + ctx.fillRect(x + width - thickness, y + thickness, thickness, verticalHeight); + } + } + drawShapeToCanvas(ctx, type, x, y, width, height) { ctx.beginPath(); if (type == 'rectangle') { @@ -937,46 +954,50 @@ class ImageEditorToolShape extends ImageEditorTool { let radius = Math.sqrt(width * width + height * height) / 2; ctx.arc(Math.round(x + width / 2), Math.round(y + height / 2), Math.round(radius), 0, 2 * Math.PI); } - ctx.stroke(); } draw() { if (!this.isDrawing) { return; } + const target = this.editor.activeLayer; + if (!target) { + return; + } + const startX = Math.min(this.startLayerX, this.currentLayerX); + const startY = Math.min(this.startLayerY, this.currentLayerY); + const endX = Math.max(this.startLayerX, this.currentLayerX); + const endY = Math.max(this.startLayerY, this.currentLayerY); + const width = endX - startX; + const height = endY - startY; + if (width == 0 && height == 0) { + return; + } + const [imageX1, imageY1] = target.layerCoordToImageCoord(startX, startY); + const [imageX2, imageY2] = target.layerCoordToImageCoord(endX, endY); + const [canvasX1, canvasY1] = this.editor.imageCoordToCanvasCoord(imageX1, imageY1); + const [canvasX2, canvasY2] = this.editor.imageCoordToCanvasCoord(imageX2, imageY2); + const canvasWidth = canvasX2 - canvasX1; + const canvasHeight = canvasY2 - canvasY1; this.editor.ctx.save(); - this.editor.ctx.strokeStyle = this.color; - this.editor.ctx.lineWidth = this.strokeWidth * this.editor.zoomLevel; - let [x, y] = this.editor.imageCoordToCanvasCoord(Math.min(this.startX, this.currentX), Math.min(this.startY, this.currentY)); - let width = Math.abs(this.currentX - this.startX) * this.editor.zoomLevel; - let height = Math.abs(this.currentY - this.startY) * this.editor.zoomLevel; - this.drawShapeToCanvas(this.editor.ctx, this.shape, x, y, width, height); - this.editor.ctx.restore(); - } - - drawShape(shape) { - this.editor.ctx.save(); - this.editor.ctx.strokeStyle = shape.color; - this.editor.ctx.lineWidth = shape.strokeWidth * this.editor.zoomLevel; - let [x, y] = this.editor.imageCoordToCanvasCoord(shape.x, shape.y); - let width = Math.abs(shape.width) * this.editor.zoomLevel; - let height = Math.abs(shape.height) * this.editor.zoomLevel; - this.drawShapeToCanvas(this.editor.ctx, shape.type, x, y, width, height); + const prevSmoothing = this.editor.ctx.imageSmoothingEnabled; + this.editor.ctx.imageSmoothingEnabled = false; + this.editor.ctx.setLineDash([]); + if (this.shape == 'rectangle') { + const thickness = Math.max(1, Math.round(this.strokeWidth * this.editor.zoomLevel)); + this.editor.ctx.fillStyle = this.color; + this.drawRectangleBorder(this.editor.ctx, Math.round(canvasX1), Math.round(canvasY1), Math.round(canvasWidth), Math.round(canvasHeight), thickness); + } + else { + this.editor.ctx.strokeStyle = this.color; + this.editor.ctx.lineWidth = Math.max(1, Math.round(this.strokeWidth * this.editor.zoomLevel)); + this.drawShapeToCanvas(this.editor.ctx, this.shape, canvasX1, canvasY1, canvasWidth, canvasHeight); + this.editor.ctx.stroke(); + } + this.editor.ctx.imageSmoothingEnabled = prevSmoothing; this.editor.ctx.restore(); } - drawShapeObjectToCanvas(ctx, shape, zoom, offsetX = 0, offsetY = 0) { - ctx.save(); - ctx.strokeStyle = shape.color; - ctx.lineWidth = shape.strokeWidth; - let x = (shape.x + offsetX) * zoom; - let y = (shape.y + offsetY) * zoom; - let width = Math.abs(shape.width) * zoom; - let height = Math.abs(shape.height) * zoom; - this.drawShapeToCanvas(ctx, shape.type, x, y, width, height); - ctx.restore(); - } - onMouseDown(e) { if (e.button != 0) { return; @@ -984,7 +1005,10 @@ class ImageEditorToolShape extends ImageEditorTool { if (this.isDrawing) { this.finishDrawing(); } + this.editor.updateMousePosFrom(e); let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); + mouseX = Math.round(mouseX); + mouseY = Math.round(mouseY); this.isDrawing = true; this.startX = mouseX; this.startY = mouseY; @@ -997,7 +1021,9 @@ class ImageEditorToolShape extends ImageEditorTool { this.isDrawing = false; return; } - let [layerX, layerY] = target.canvasCoordToLayerCoord(this.editor.mouseX, this.editor.mouseY); + let [layerX, layerY] = target.imageCoordToLayerCoord(mouseX, mouseY); + layerX = Math.round(layerX); + layerY = Math.round(layerY); this.startLayerX = layerX; this.startLayerY = layerY; this.currentLayerX = layerX; @@ -1008,72 +1034,75 @@ class ImageEditorToolShape extends ImageEditorTool { } finishDrawing() { - if (!this.isDrawing || !this.bufferLayer) { - return; - } - let parent = this.editor.activeLayer; - if (!parent) { - this.bufferLayer = null; - this.isDrawing = false; - this.hasDrawn = false; - this.editor.redraw(); - return; - } - if (!this.hasDrawn) { - let idx = parent.childLayers.indexOf(this.bufferLayer); - if (idx != -1) { + if (this.isDrawing && this.bufferLayer) { + const parent = this.editor.activeLayer; + if (!parent) { + this.bufferLayer = null; + this.isDrawing = false; + this.hasDrawn = false; + this.editor.redraw(); + return; + } + if (!this.hasDrawn) { + const idx = parent.childLayers.indexOf(this.bufferLayer); + if (idx !== -1) { + parent.childLayers.splice(idx, 1); + } + this.bufferLayer = null; + this.isDrawing = false; + this.hasDrawn = false; + this.editor.redraw(); + return; + } + this.drawShape(); + const idx = parent.childLayers.indexOf(this.bufferLayer); + if (idx !== -1) { parent.childLayers.splice(idx, 1); } + const offset = parent.getOffset(); + parent.saveBeforeEdit(); + this.bufferLayer.drawToBackDirect(parent.ctx, -offset[0], -offset[1], 1); + parent.hasAnyContent = true; this.bufferLayer = null; this.isDrawing = false; this.hasDrawn = false; + this.editor.markChanged(); this.editor.redraw(); - return; } - this.drawShape(); - let idx = parent.childLayers.indexOf(this.bufferLayer); - if (idx != -1) { - parent.childLayers.splice(idx, 1); - } - let offset = parent.getOffset(); - parent.saveBeforeEdit(); - this.bufferLayer.drawToBackDirect(parent.ctx, -offset[0], -offset[1], 1); - parent.hasAnyContent = true; - this.bufferLayer = null; - this.isDrawing = false; - this.hasDrawn = false; - this.editor.markChanged(); - this.editor.redraw(); } onMouseMove(e) { if (!this.isDrawing) { return; } + this.editor.updateMousePosFrom(e); let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); + mouseX = Math.round(mouseX); + mouseY = Math.round(mouseY); this.currentX = mouseX; this.currentY = mouseY; let target = this.editor.activeLayer; if (target) { - let [layerX, layerY] = target.canvasCoordToLayerCoord(this.editor.mouseX, this.editor.mouseY); - this.currentLayerX = layerX; - this.currentLayerY = layerY; + let [layerX, layerY] = target.imageCoordToLayerCoord(mouseX, mouseY); + this.currentLayerX = Math.round(layerX); + this.currentLayerY = Math.round(layerY); } this.drawShape(); } onGlobalMouseMove(e) { - if (!this.isDrawing) { - return; - } + if (this.isDrawing) { + this.editor.updateMousePosFrom(e); let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); + mouseX = Math.round(mouseX); + mouseY = Math.round(mouseY); this.currentX = mouseX; this.currentY = mouseY; let target = this.editor.activeLayer; if (target) { - let [layerX, layerY] = target.canvasCoordToLayerCoord(this.editor.mouseX, this.editor.mouseY); - this.currentLayerX = layerX; - this.currentLayerY = layerY; + let [layerX, layerY] = target.imageCoordToLayerCoord(mouseX, mouseY); + this.currentLayerX = Math.round(layerX); + this.currentLayerY = Math.round(layerY); } this.drawShape(); } @@ -1082,14 +1111,20 @@ class ImageEditorToolShape extends ImageEditorTool { if (e.button != 0 || !this.isDrawing) { return; } + if (!this.isDrawing) { + return; + } + this.editor.updateMousePosFrom(e); let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); + mouseX = Math.round(mouseX); + mouseY = Math.round(mouseY); this.currentX = mouseX; this.currentY = mouseY; let target = this.editor.activeLayer; if (target) { - let [layerX, layerY] = target.canvasCoordToLayerCoord(this.editor.mouseX, this.editor.mouseY); - this.currentLayerX = layerX; - this.currentLayerY = layerY; + let [layerX, layerY] = target.imageCoordToLayerCoord(mouseX, mouseY); + this.currentLayerX = Math.round(layerX); + this.currentLayerY = Math.round(layerY); } this.finishDrawing(); } @@ -1098,42 +1133,62 @@ class ImageEditorToolShape extends ImageEditorTool { if (e.button != 0 || !this.isDrawing) { return; } - this.finishDrawing(); + if (this.isDrawing) { + this.editor.updateMousePosFrom(e); + let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); + mouseX = Math.round(mouseX); + mouseY = Math.round(mouseY); + this.currentX = mouseX; + this.currentY = mouseY; + let target = this.editor.activeLayer; + if (target) { + let [layerX, layerY] = target.imageCoordToLayerCoord(mouseX, mouseY); + this.currentLayerX = Math.round(layerX); + this.currentLayerY = Math.round(layerY); + } + this.finishDrawing(); + return true; + } + return false; } drawShape() { if (!this.isDrawing || !this.bufferLayer) { return; } - let parent = this.editor.activeLayer; + const parent = this.editor.activeLayer; if (!parent) { return; } this.bufferLayer.ctx.clearRect(0, 0, this.bufferLayer.canvas.width, this.bufferLayer.canvas.height); - let layerX1 = Math.min(this.startLayerX, this.currentLayerX); - let layerY1 = Math.min(this.startLayerY, this.currentLayerY); - let width = Math.abs(this.currentLayerX - this.startLayerX); - let height = Math.abs(this.currentLayerY - this.startLayerY); - if (width < 0.5 && height < 0.5) { + const startX = Math.round(Math.min(this.startLayerX, this.currentLayerX)); + const startY = Math.round(Math.min(this.startLayerY, this.currentLayerY)); + const endX = Math.round(Math.max(this.startLayerX, this.currentLayerX)); + const endY = Math.round(Math.max(this.startLayerY, this.currentLayerY)); + const width = endX - startX; + const height = endY - startY; + if (width == 0 && height == 0) { this.bufferLayer.hasAnyContent = false; this.hasDrawn = false; this.editor.redraw(); return; } this.bufferLayer.ctx.save(); - this.bufferLayer.ctx.strokeStyle = this.color; - this.bufferLayer.ctx.lineWidth = this.strokeWidth; - this.bufferLayer.ctx.beginPath(); + const prevSmoothing = this.bufferLayer.ctx.imageSmoothingEnabled; + this.bufferLayer.ctx.imageSmoothingEnabled = false; + this.bufferLayer.ctx.setLineDash([]); if (this.shape == 'rectangle') { - this.bufferLayer.ctx.rect(layerX1, layerY1, width, height); + const thickness = Math.max(1, Math.round(this.strokeWidth)); + this.bufferLayer.ctx.fillStyle = this.color; + this.drawRectangleBorder(this.bufferLayer.ctx, startX, startY, width, height, thickness); } - else if (this.shape == 'circle') { - let cx = layerX1 + width / 2; - let cy = layerY1 + height / 2; - let radius = Math.sqrt(width * width + height * height) / 2; - this.bufferLayer.ctx.arc(cx, cy, radius, 0, 2 * Math.PI); + else { + this.bufferLayer.ctx.strokeStyle = this.color; + this.bufferLayer.ctx.lineWidth = Math.max(1, Math.round(this.strokeWidth)); + this.drawShapeToCanvas(this.bufferLayer.ctx, this.shape, startX, startY, width, height); + this.bufferLayer.ctx.stroke(); } - this.bufferLayer.ctx.stroke(); + this.bufferLayer.ctx.imageSmoothingEnabled = prevSmoothing; this.bufferLayer.ctx.restore(); this.bufferLayer.hasAnyContent = true; this.hasDrawn = true; @@ -1331,6 +1386,22 @@ class ImageEditorLayer { return [x2, y2]; } + imageCoordToLayerCoord(x, y) { + let [offsetX, offsetY] = this.getOffset(); + let relWidth = this.width / this.canvas.width; + let relHeight = this.height / this.canvas.height; + [x, y] = [x - offsetX, y - offsetY]; + let angle = -this.rotation; + let [cx, cy] = [this.width / 2, this.height / 2]; + let [x2, y2] = [x - cx, y - cy]; + let cos = Math.cos(angle), sin = Math.sin(angle); + let xRot = x2 * cos - y2 * sin; + let yRot = x2 * sin + y2 * cos; + xRot += cx; + yRot += cy; + return [xRot / relWidth, yRot / relHeight]; + } + layerCoordToCanvasCoord(x, y) { let [x2, y2] = this.editor.imageCoordToCanvasCoord(x, y); let [offsetX, offsetY] = this.getOffset(); @@ -1340,6 +1411,22 @@ class ImageEditorLayer { return [x2, y2]; } + layerCoordToImageCoord(x, y) { + let relWidth = this.width / this.canvas.width; + let relHeight = this.height / this.canvas.height; + [x, y] = [x * relWidth, y * relHeight]; + let angle = this.rotation; + let [cx, cy] = [this.width / 2, this.height / 2]; + let [x2, y2] = [x - cx, y - cy]; + let cos = Math.cos(angle), sin = Math.sin(angle); + let xRot = x2 * cos - y2 * sin; + let yRot = x2 * sin + y2 * cos; + xRot += cx; + yRot += cy; + let [offsetX, offsetY] = this.getOffset(); + return [xRot + offsetX, yRot + offsetY]; + } + drawFilledCircle(x, y, radius, color) { this.ctx.fillStyle = color; this.ctx.beginPath(); From 54b2ce0c92cc148bed11d9fe7c762e45a33a8478 Mon Sep 17 00:00:00 2001 From: Urabewe Date: Tue, 4 Nov 2025 11:22:30 -0600 Subject: [PATCH 15/18] Update image_editor.js --- src/wwwroot/js/genpage/helpers/image_editor.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/wwwroot/js/genpage/helpers/image_editor.js b/src/wwwroot/js/genpage/helpers/image_editor.js index 4acf6edf7..0b3e7fded 100644 --- a/src/wwwroot/js/genpage/helpers/image_editor.js +++ b/src/wwwroot/js/genpage/helpers/image_editor.js @@ -865,7 +865,6 @@ class ImageEditorToolShape extends ImageEditorTool {
`; - let shapeHTML = `
@@ -874,7 +873,6 @@ class ImageEditorToolShape extends ImageEditorTool {
`; - let strokeHTML = `
@@ -883,7 +881,6 @@ class ImageEditorToolShape extends ImageEditorTool {
`; - this.configDiv.innerHTML = colorHTML + shapeHTML + strokeHTML; this.colorText = this.configDiv.querySelector('.id-col1'); this.colorSelector = this.configDiv.querySelector('.id-col2'); From 1a0b799ec42e46b01d4b4c375edf9bed5e15a2c7 Mon Sep 17 00:00:00 2001 From: "Alex \"mcmonkey\" Goodwin" Date: Mon, 10 Nov 2025 20:11:43 -0800 Subject: [PATCH 16/18] re-fix this PR. --- .../js/genpage/helpers/image_editor.js | 144 +++++++----------- 1 file changed, 52 insertions(+), 92 deletions(-) diff --git a/src/wwwroot/js/genpage/helpers/image_editor.js b/src/wwwroot/js/genpage/helpers/image_editor.js index 0b3e7fded..36542d047 100644 --- a/src/wwwroot/js/genpage/helpers/image_editor.js +++ b/src/wwwroot/js/genpage/helpers/image_editor.js @@ -951,37 +951,37 @@ class ImageEditorToolShape extends ImageEditorTool { let radius = Math.sqrt(width * width + height * height) / 2; ctx.arc(Math.round(x + width / 2), Math.round(y + height / 2), Math.round(radius), 0, 2 * Math.PI); } + ctx.stroke(); } draw() { if (!this.isDrawing) { return; } - const target = this.editor.activeLayer; + let target = this.editor.activeLayer; if (!target) { return; } - const startX = Math.min(this.startLayerX, this.currentLayerX); - const startY = Math.min(this.startLayerY, this.currentLayerY); - const endX = Math.max(this.startLayerX, this.currentLayerX); - const endY = Math.max(this.startLayerY, this.currentLayerY); - const width = endX - startX; - const height = endY - startY; + let startX = Math.min(this.startLayerX, this.currentLayerX); + let startY = Math.min(this.startLayerY, this.currentLayerY); + let endX = Math.max(this.startLayerX, this.currentLayerX); + let endY = Math.max(this.startLayerY, this.currentLayerY); + let width = endX - startX; + let height = endY - startY; if (width == 0 && height == 0) { return; } - const [imageX1, imageY1] = target.layerCoordToImageCoord(startX, startY); - const [imageX2, imageY2] = target.layerCoordToImageCoord(endX, endY); - const [canvasX1, canvasY1] = this.editor.imageCoordToCanvasCoord(imageX1, imageY1); - const [canvasX2, canvasY2] = this.editor.imageCoordToCanvasCoord(imageX2, imageY2); - const canvasWidth = canvasX2 - canvasX1; - const canvasHeight = canvasY2 - canvasY1; + let [canvasX1, canvasY1] = target.layerCoordToCanvasCoord(startX, startY); + let [canvasX2, canvasY2] = target.layerCoordToCanvasCoord(endX, endY); + let [imageX1, imageY1] = target.editor.canvasCoordToImageCoord(canvasX1, canvasY1); + let [imageX2, imageY2] = target.editor.canvasCoordToImageCoord(canvasX2, canvasY2); + let canvasWidth = canvasX2 - canvasX1; + let canvasHeight = canvasY2 - canvasY1; this.editor.ctx.save(); - const prevSmoothing = this.editor.ctx.imageSmoothingEnabled; this.editor.ctx.imageSmoothingEnabled = false; this.editor.ctx.setLineDash([]); if (this.shape == 'rectangle') { - const thickness = Math.max(1, Math.round(this.strokeWidth * this.editor.zoomLevel)); + let thickness = Math.max(1, Math.round(this.strokeWidth * this.editor.zoomLevel)); this.editor.ctx.fillStyle = this.color; this.drawRectangleBorder(this.editor.ctx, Math.round(canvasX1), Math.round(canvasY1), Math.round(canvasWidth), Math.round(canvasHeight), thickness); } @@ -989,9 +989,7 @@ class ImageEditorToolShape extends ImageEditorTool { this.editor.ctx.strokeStyle = this.color; this.editor.ctx.lineWidth = Math.max(1, Math.round(this.strokeWidth * this.editor.zoomLevel)); this.drawShapeToCanvas(this.editor.ctx, this.shape, canvasX1, canvasY1, canvasWidth, canvasHeight); - this.editor.ctx.stroke(); } - this.editor.ctx.imageSmoothingEnabled = prevSmoothing; this.editor.ctx.restore(); } @@ -1018,7 +1016,8 @@ class ImageEditorToolShape extends ImageEditorTool { this.isDrawing = false; return; } - let [layerX, layerY] = target.imageCoordToLayerCoord(mouseX, mouseY); + let [canvasX, canvasY] = target.editor.imageCoordToCanvasCoord(mouseX, mouseY); + let [layerX, layerY] = target.canvasCoordToLayerCoord(canvasX, canvasY); layerX = Math.round(layerX); layerY = Math.round(layerY); this.startLayerX = layerX; @@ -1032,7 +1031,7 @@ class ImageEditorToolShape extends ImageEditorTool { finishDrawing() { if (this.isDrawing && this.bufferLayer) { - const parent = this.editor.activeLayer; + let parent = this.editor.activeLayer; if (!parent) { this.bufferLayer = null; this.isDrawing = false; @@ -1041,8 +1040,8 @@ class ImageEditorToolShape extends ImageEditorTool { return; } if (!this.hasDrawn) { - const idx = parent.childLayers.indexOf(this.bufferLayer); - if (idx !== -1) { + let idx = parent.childLayers.indexOf(this.bufferLayer); + if (idx != -1) { parent.childLayers.splice(idx, 1); } this.bufferLayer = null; @@ -1052,11 +1051,11 @@ class ImageEditorToolShape extends ImageEditorTool { return; } this.drawShape(); - const idx = parent.childLayers.indexOf(this.bufferLayer); - if (idx !== -1) { + let idx = parent.childLayers.indexOf(this.bufferLayer); + if (idx != -1) { parent.childLayers.splice(idx, 1); } - const offset = parent.getOffset(); + let offset = parent.getOffset(); parent.saveBeforeEdit(); this.bufferLayer.drawToBackDirect(parent.ctx, -offset[0], -offset[1], 1); parent.hasAnyContent = true; @@ -1072,7 +1071,6 @@ class ImageEditorToolShape extends ImageEditorTool { if (!this.isDrawing) { return; } - this.editor.updateMousePosFrom(e); let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); mouseX = Math.round(mouseX); mouseY = Math.round(mouseY); @@ -1080,7 +1078,8 @@ class ImageEditorToolShape extends ImageEditorTool { this.currentY = mouseY; let target = this.editor.activeLayer; if (target) { - let [layerX, layerY] = target.imageCoordToLayerCoord(mouseX, mouseY); + let [canvasX, canvasY] = target.editor.imageCoordToCanvasCoord(mouseX, mouseY); + let [layerX, layerY] = target.canvasCoordToLayerCoord(canvasX, canvasY); this.currentLayerX = Math.round(layerX); this.currentLayerY = Math.round(layerY); } @@ -1088,7 +1087,9 @@ class ImageEditorToolShape extends ImageEditorTool { } onGlobalMouseMove(e) { - if (this.isDrawing) { + if (!this.isDrawing) { + return; + } this.editor.updateMousePosFrom(e); let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); mouseX = Math.round(mouseX); @@ -1097,7 +1098,8 @@ class ImageEditorToolShape extends ImageEditorTool { this.currentY = mouseY; let target = this.editor.activeLayer; if (target) { - let [layerX, layerY] = target.imageCoordToLayerCoord(mouseX, mouseY); + let [canvasX, canvasY] = target.editor.imageCoordToCanvasCoord(mouseX, mouseY); + let [layerX, layerY] = target.canvasCoordToLayerCoord(canvasX, canvasY); this.currentLayerX = Math.round(layerX); this.currentLayerY = Math.round(layerY); } @@ -1108,10 +1110,6 @@ class ImageEditorToolShape extends ImageEditorTool { if (e.button != 0 || !this.isDrawing) { return; } - if (!this.isDrawing) { - return; - } - this.editor.updateMousePosFrom(e); let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); mouseX = Math.round(mouseX); mouseY = Math.round(mouseY); @@ -1119,7 +1117,8 @@ class ImageEditorToolShape extends ImageEditorTool { this.currentY = mouseY; let target = this.editor.activeLayer; if (target) { - let [layerX, layerY] = target.imageCoordToLayerCoord(mouseX, mouseY); + let [canvasX, canvasY] = target.editor.imageCoordToCanvasCoord(mouseX, mouseY); + let [layerX, layerY] = target.canvasCoordToLayerCoord(canvasX, canvasY); this.currentLayerX = Math.round(layerX); this.currentLayerY = Math.round(layerY); } @@ -1130,40 +1129,36 @@ class ImageEditorToolShape extends ImageEditorTool { if (e.button != 0 || !this.isDrawing) { return; } - if (this.isDrawing) { - this.editor.updateMousePosFrom(e); - let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); - mouseX = Math.round(mouseX); - mouseY = Math.round(mouseY); - this.currentX = mouseX; - this.currentY = mouseY; - let target = this.editor.activeLayer; - if (target) { - let [layerX, layerY] = target.imageCoordToLayerCoord(mouseX, mouseY); - this.currentLayerX = Math.round(layerX); - this.currentLayerY = Math.round(layerY); - } - this.finishDrawing(); - return true; + let [mouseX, mouseY] = this.editor.canvasCoordToImageCoord(this.editor.mouseX, this.editor.mouseY); + mouseX = Math.round(mouseX); + mouseY = Math.round(mouseY); + this.currentX = mouseX; + this.currentY = mouseY; + let target = this.editor.activeLayer; + if (target) { + let [canvasX, canvasY] = target.editor.imageCoordToCanvasCoord(mouseX, mouseY); + let [layerX, layerY] = target.canvasCoordToLayerCoord(canvasX, canvasY); + this.currentLayerX = Math.round(layerX); + this.currentLayerY = Math.round(layerY); } - return false; + this.finishDrawing(); } drawShape() { if (!this.isDrawing || !this.bufferLayer) { return; } - const parent = this.editor.activeLayer; + let parent = this.editor.activeLayer; if (!parent) { return; } this.bufferLayer.ctx.clearRect(0, 0, this.bufferLayer.canvas.width, this.bufferLayer.canvas.height); - const startX = Math.round(Math.min(this.startLayerX, this.currentLayerX)); - const startY = Math.round(Math.min(this.startLayerY, this.currentLayerY)); - const endX = Math.round(Math.max(this.startLayerX, this.currentLayerX)); - const endY = Math.round(Math.max(this.startLayerY, this.currentLayerY)); - const width = endX - startX; - const height = endY - startY; + let startX = Math.round(Math.min(this.startLayerX, this.currentLayerX)); + let startY = Math.round(Math.min(this.startLayerY, this.currentLayerY)); + let endX = Math.round(Math.max(this.startLayerX, this.currentLayerX)); + let endY = Math.round(Math.max(this.startLayerY, this.currentLayerY)); + let width = endX - startX; + let height = endY - startY; if (width == 0 && height == 0) { this.bufferLayer.hasAnyContent = false; this.hasDrawn = false; @@ -1171,11 +1166,10 @@ class ImageEditorToolShape extends ImageEditorTool { return; } this.bufferLayer.ctx.save(); - const prevSmoothing = this.bufferLayer.ctx.imageSmoothingEnabled; this.bufferLayer.ctx.imageSmoothingEnabled = false; this.bufferLayer.ctx.setLineDash([]); if (this.shape == 'rectangle') { - const thickness = Math.max(1, Math.round(this.strokeWidth)); + let thickness = Math.max(1, Math.round(this.strokeWidth)); this.bufferLayer.ctx.fillStyle = this.color; this.drawRectangleBorder(this.bufferLayer.ctx, startX, startY, width, height, thickness); } @@ -1183,9 +1177,7 @@ class ImageEditorToolShape extends ImageEditorTool { this.bufferLayer.ctx.strokeStyle = this.color; this.bufferLayer.ctx.lineWidth = Math.max(1, Math.round(this.strokeWidth)); this.drawShapeToCanvas(this.bufferLayer.ctx, this.shape, startX, startY, width, height); - this.bufferLayer.ctx.stroke(); } - this.bufferLayer.ctx.imageSmoothingEnabled = prevSmoothing; this.bufferLayer.ctx.restore(); this.bufferLayer.hasAnyContent = true; this.hasDrawn = true; @@ -1383,22 +1375,6 @@ class ImageEditorLayer { return [x2, y2]; } - imageCoordToLayerCoord(x, y) { - let [offsetX, offsetY] = this.getOffset(); - let relWidth = this.width / this.canvas.width; - let relHeight = this.height / this.canvas.height; - [x, y] = [x - offsetX, y - offsetY]; - let angle = -this.rotation; - let [cx, cy] = [this.width / 2, this.height / 2]; - let [x2, y2] = [x - cx, y - cy]; - let cos = Math.cos(angle), sin = Math.sin(angle); - let xRot = x2 * cos - y2 * sin; - let yRot = x2 * sin + y2 * cos; - xRot += cx; - yRot += cy; - return [xRot / relWidth, yRot / relHeight]; - } - layerCoordToCanvasCoord(x, y) { let [x2, y2] = this.editor.imageCoordToCanvasCoord(x, y); let [offsetX, offsetY] = this.getOffset(); @@ -1408,22 +1384,6 @@ class ImageEditorLayer { return [x2, y2]; } - layerCoordToImageCoord(x, y) { - let relWidth = this.width / this.canvas.width; - let relHeight = this.height / this.canvas.height; - [x, y] = [x * relWidth, y * relHeight]; - let angle = this.rotation; - let [cx, cy] = [this.width / 2, this.height / 2]; - let [x2, y2] = [x - cx, y - cy]; - let cos = Math.cos(angle), sin = Math.sin(angle); - let xRot = x2 * cos - y2 * sin; - let yRot = x2 * sin + y2 * cos; - xRot += cx; - yRot += cy; - let [offsetX, offsetY] = this.getOffset(); - return [xRot + offsetX, yRot + offsetY]; - } - drawFilledCircle(x, y, radius, color) { this.ctx.fillStyle = color; this.ctx.beginPath(); From 7cf68d3a717bc498d4e667b063bbeb7170ebdeb7 Mon Sep 17 00:00:00 2001 From: "Alex \"mcmonkey\" Goodwin" Date: Mon, 10 Nov 2025 20:11:49 -0800 Subject: [PATCH 17/18] add an icon --- src/wwwroot/imgs/shape.png | Bin 0 -> 6694 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/wwwroot/imgs/shape.png diff --git a/src/wwwroot/imgs/shape.png b/src/wwwroot/imgs/shape.png new file mode 100644 index 0000000000000000000000000000000000000000..3cc7205747a3acc2293ad27e9306725003983bbb GIT binary patch literal 6694 zcmeHMX;>54whc0+ARW-e2aULY;H=UT3d$_CBY+ z$~tdPS1nBgO&kuVi~YA#UK2F1?@B@RT^vqhX1sp@>I=*95~-Lk5F&UqPJ-YO zg@BL4DQ-Ot3_Q|pKKbo+i};EB)Q3RH^Vlmt_e~8myDZtCHne(RU388vlB*_lc+qaV zVW_SwDZp*kBhi7_y0T<#sN)Q|GyR=~-ktsLuK1<&ok+Z;HZ?@E${_Sq@$1N%87CVjvreu`O!0SJ-fGt0>A1Q- z-aR3fj*#0AC%??7vtD$eG9^fodqD@6VVmloV)sYnGADVX*}aulGt{(SJ=pzpg5A~M zc6Nja&R&Q$vw7Jve|eDS^cNkx6NhKNaWD8~FM8efZ1y&}w7tFU*+L@u?jx_rrlUg_ z6&ZVo!JFIa|0{36*OX8hkT+Fx3O|DCMgP$`P;jo^tJ!1QS z<@(9#K}$Dj(|!3B@w=CKYHY15@-FoHb*E~Ya~x1;f5KsZ+ktc&Q2_MfrZt~5uF|)* zpf;z)tk~#^!R{p8_3f+GgMAPFq>{X~bNC7<<^fK$FVpy*n@$p;)MFPiPPKk!AkKH? zt_8>XH|;*7Yj(VnI`vLU;sSnHz4?!Z$L#agE~wJ#I4j7Wnc<=@%B()4FHTywG*GnR zaBJX-X*>KpH&ZS%ADx^;ihp>RemDNnDIFUP{b_paZ-kP%G+j>P4^#3?TwKi?3wXZn zW{&4g^H*!mhdQiV;-BJs?VA7ckQcP8H*{CBeOfM2 zZwEgo9y(TSDM(o8b->g#@*t%tBZ{ka>T;E-8rdwBp$cz0gzL~H4F=ZxQRv5k25+!G`5?-sJgtHr$D;-g-Br{7JLh6{Pd z3uAoF*_`_^yV><_&)uNcrdGFyXy{FEiw$v=1hwlc4^7vfChGfXZY94;parVK^D^F? zH*Hv7;!-}4mA9mQ4!V}OMZ0a8M{|^>cN#inhD`(>(wb)$5wzxBQ3t>C+TZ~|>q^Yz zRTEI7hHuA(oIP-TNB7JD-N(#SmfG?3I{&=|0hElNL*{QJ0qc;on%`G8Z#t6b^Gb-e z)F*rBghi?rd3ZiQ=+C5<X9I2MfMm z&vApQCua5R&v;t&DtyxX3CEK=0<(Kd6|LJBnwPwIJV7%BmuHplYQ=f8prCZTz1h*U zIlG$FGkLMq+Ie}$g3fAG?De?h)Rnhvv5Vh!cDg0k^-#6xz$<+bE34V$IxlkVy){MnahSoBjc)PHv zVQqeIz2}arXddp@%$|K!F1!2d1$n#P#~$ozr?a3WaVi>ke6?Gs@L8yO`yV&d?lknT zv41JCzSzPt)On8ToNcfk?Ggg_g#Y*>e@Xjh^L1A@8Fmv~W;B+B7=&k5kY*(M9X^*m z_mxm4qdkd{go|d?Z0P##M4~}(wZseCVRbo6$2bo_8eU?;;52^(2%4R$kA+skt^mKyfpJ95gH>FPdHTq`(J7Hm1< z@T0bA{*e9V)HzK}J`$qU;76e%PaBb0s!@cK2XI_Y$FWA>&;9}_EhMzbET4}yx+$5h{ zG~dNLA6s|bpDlgsEGQH<^GRHbEm?-Pqlvec^7u?&CznwOti>Wkps0jNCd=h=lAJ;kOT)<^gTWvJ z5E+7qm<3T57lpzKVwB882{D4hm?MrsSp)($j{gv!NWx~1!AHqP zRlxKhD_{v3BmrcRi2SLC40VphKt==lQxBOx_UuFUMP%X_DHm~$MWRrXPa$~RF@H&n zR5(nB$0Z{|M1+~juvx*+rgU{@dyjc2B?uRYB*R{q*q>QSBE+az79p05nSU1G*zhp1 zPeVcl{EsSs=A#_>Cv$AxKcC@)Xv5f;6`Rd;5_4meyxg5w1gwX|6Y!W2Xi4MHVVKCF za(P59gG$Hls9Yj!32|v$I^WU?p??D59wkHJC@%5=LV!Wg5ePuI2uP%W5JIGaAcM%@ zT4GIr4pA(*JPrULpFns?1y~xx!v9w^JT8+jmWp6({RJX893e}h!iVQjt`O7S+nq&# zNWjM)Zy}5-9k8MiMDfIO*~cM&fe7(KVWo~B-4e2*P(g+z9e}JDl#fPB5vdGIUL`6B zkRYq!k+DT&I*Ns2DO=1#ScDNL<*z)0w0)?%r@SS(}_lxa8x*g?a8B?!2iXU#^*4w0$~uX_#i^$@ToMS6-{|Z<@2}z zVg+&d@aIAQJ$$je0IB{Eg$dD^RK{pt{1^CIa$$&$ARHo<16nEha)}%Y0uVVoE{{v2 z0G1$aq-g%;_5IU2_%HAUcnm(o0J%g41)>qD5Cjkz0M(Mn;ld0M0XZCyKT^N{imxkn zs0>&6UnVh)2!ccq@Q1K1gTbU(5=QA+la;6JhdQ+;|5GK87<_UpV`)FqhaLCW(*t?z z@nN(cuvq_-pV1xUKRE>+|8x&flBIB>w^|#5T`SAq= z5`{hC%CXlAdBsvH_G&_d;;sYyOX^?_G`pe(6hl#*SVcXeC*(yOTn2<@HdyAo4Y3`M|Zl> z%hLOLwd#*clQJf$?ZR);*VKbA^aP#AdmDP#@5Z+^TW|91CK#vaI!rKjNq=bF(+lg` z!mWBU?)6f_CibX=PM(*da$r${Pm7;}_94d6{ye#E zLd@-yjV3Bn@qh8xS@y(-mp-d#YRc)1e3P(SjM=Ko-tIlOD6Ce%R#CC~tx-0lmrDz{ zeoWP7JalG%ZOCsN%L>(t!p`1&uuZ*Wu!cmgn^vGg=MUsjJkB3`<7s^)w{rGHVX~4k e#ykD#phiF|E6Jhc`eRIHoV&B9(>{l=r2hfHsPj+& literal 0 HcmV?d00001 From f4871b90c7dbb52fe7e387be9ddf1ccb01fa1f94 Mon Sep 17 00:00:00 2001 From: "Alex \"mcmonkey\" Goodwin" Date: Mon, 10 Nov 2025 20:15:37 -0800 Subject: [PATCH 18/18] fix up css on tool block a bit --- src/wwwroot/css/genpage.css | 8 ++++++++ src/wwwroot/js/genpage/helpers/image_editor.js | 8 ++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/wwwroot/css/genpage.css b/src/wwwroot/css/genpage.css index 28072ac07..06dc1ca7d 100644 --- a/src/wwwroot/css/genpage.css +++ b/src/wwwroot/css/genpage.css @@ -1239,6 +1239,14 @@ body { border: 1px solid var(--light-border); height: 32px; } +.image-editor-tool-block input[type="color"] { + height: 1.6rem; +} +.tool-block-nogrow { + flex-grow: 0; + padding-left: 5px; + padding-right: 5px; +} .image_editor_canvaslist { display: block; margin: 0; diff --git a/src/wwwroot/js/genpage/helpers/image_editor.js b/src/wwwroot/js/genpage/helpers/image_editor.js index 36542d047..b583683e5 100644 --- a/src/wwwroot/js/genpage/helpers/image_editor.js +++ b/src/wwwroot/js/genpage/helpers/image_editor.js @@ -526,7 +526,7 @@ class ImageEditorToolBrush extends ImageEditorTool { this.brushing = false; this.isEraser = isEraser; let colorHTML = ` -
+
@@ -699,7 +699,7 @@ class ImageEditorToolBucket extends ImageEditorTool { this.threshold = 10; this.opacity = 1; let colorHTML = ` -
+
@@ -859,14 +859,14 @@ class ImageEditorToolShape extends ImageEditorTool { this.bufferLayer = null; this.hasDrawn = false; let colorHTML = ` -
+
`; let shapeHTML = ` -
+