From 587304ccf63fe8a56bcbde950865ef7b2771c8dd Mon Sep 17 00:00:00 2001 From: Nicola <47360416+unsettledgames@users.noreply.github.com> Date: Mon, 27 Dec 2021 10:54:37 +0100 Subject: [PATCH 01/43] Edited contribution guidelines --- README.md | 3 ++- README.md.bak | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 README.md.bak diff --git a/README.md b/README.md index b49a328..0537fdb 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ The tool can be viewed online here: https://lospec.com/pixel-editor ## How to contribute -Please do not submit pull requests with new features or core changes. Instead, please file an issue first for discussion. +Before starting to work, please open an issue for discussion so that we can organize the work without creating too many conflicts. If your contribution is going to fix a bug, please + make a fork and use the bug-fixes branch. If you want to work on a new feature, please use the new-feature branch instead. ## What to Contribute diff --git a/README.md.bak b/README.md.bak new file mode 100644 index 0000000..b49a328 --- /dev/null +++ b/README.md.bak @@ -0,0 +1,74 @@ +# Lospec Pixel Editor + +This is a browser based software for creating pixel art + +The tool can be viewed online here: https://lospec.com/pixel-editor + +## How to contribute + +Please do not submit pull requests with new features or core changes. Instead, please file an issue first for discussion. + +## What to Contribute + +Any changes that fix bugs or add features are welcome. Check out the issues if you don't know where to start: if +you're new to the editor, we suggest you check out the Wiki first. + +The next version is mostly focused on adding missing essential features and porting to mobile. + +Suggestions / Planned features: + +- Documentation +- Possibility to hide and resize menus (layers, palette) +- Tiled mode +- Load palette from LPE file +- Symmetry options (currently being worked on) +- Make a palette grid instead of having a huge stack on the right when colours are too many +- Possibly add collaborate function + +- Mobile + - Touch equivalent for mouse clicks + - Hide or scale ui + - Maybe rearrange UI on portrait + - Fix popups + +- Polish: + - CTRL+A to select everything / selection -> all, same for deselection + - Warning windows for wrong inputs + - Palette option remove unused colors + - Move selection with arrows + - Update borders by dragging the canvas' edges with the mouse when resizing canvas + - Move the canvases so they're centered after resizing the canvas (maybe a .center() method in layer class) + - Scale / rotate selection + +## How to Contribute + +### Requirements + +You must have node.js and git installed. + +You also need `npm` in version 7 (because of 2nd version of lockfile which was introduced there) which comes with Node.js 15 or newer. To simplify installation of proper versions you can make use of [nvm](https://github.com/nvm-sh/nvm#installing-and-updating) and run `nvm install` – it will activate proper Node.js version in your current command prompt session. + +### Contribution Workflow + +1. Click **Fork** above. It will automatically create a copy of this repository and add it to your account. +2. Clone the repository to your computer. +3. Open the folder in command prompt and run **`npm install`** +4. Make any changes you would like to suggest. +5. In command prompt run **`npm run hot`** which will compile app to the `/build` folder, serve under [http://localhost:3000](http://localhost:3000), then open in your browser. Moreover, it restarts server every time you save your changes in a codebase. You can go even further by running `npm run hot:reload`, which will also trigger webpage reloads. +6. Add, Commit and Push your changes to your fork. +7. On the github page for your fork, click **New Pull Request** above the file list. +8. Change the **head repository** dropdown to your fork. +9. Add a title and description explaining your changes. +10. Click create pull request. + +If you have any trouble, see this page: https://help.github.com/en/articles/creating-a-pull-request-from-a-fork + +### Feature Toggles + +Some feature might be hidden by default. Functions to enable/disable them are available inside global `featureToggles` and operate on a `window.localStorage`. + +For example use `featureToggles.enableEllipseTool()` to make ellipse tool button visible. Then `featureToggles.disableEllipseTool()` to hide it. + +## License + +This software may not be resold, redistributed, rehosted or otherwise conveyed to a third party. From a52fc647e33e338e00ef2ecd1db8c9ee87216352 Mon Sep 17 00:00:00 2001 From: Nicola <47360416+unsettledgames@users.noreply.github.com> Date: Mon, 27 Dec 2021 19:11:59 +0100 Subject: [PATCH 02/43] Reduced tool size Moved tool size buttons on the right, started lasso tool --- css/_canvas.scss | 4 +- css/_tools-menu.scss | 79 ++++++++++++++++------------------ js/tools/LassoSelectionTool.js | 29 +++++++++++++ views/tools-menu.hbs | 52 +++++++++++++--------- 4 files changed, 98 insertions(+), 66 deletions(-) create mode 100644 js/tools/LassoSelectionTool.js diff --git a/css/_canvas.scss b/css/_canvas.scss index 5d02f4c..46f6b74 100644 --- a/css/_canvas.scss +++ b/css/_canvas.scss @@ -85,7 +85,7 @@ #canvas-view { bottom: 0px; - left: 64px; + left: 48px; right: 48px; top: 48px; cursor: default; @@ -97,7 +97,7 @@ box-shadow: inset 0px 0px 4px 0px rgba(0, 0, 0, 0.4); position: fixed; bottom: 0px; - left: 64px; + left: 48px; right: 48px; top: 48px; display: block; diff --git a/css/_tools-menu.scss b/css/_tools-menu.scss index de7dc48..894527e 100644 --- a/css/_tools-menu.scss +++ b/css/_tools-menu.scss @@ -1,7 +1,7 @@ #tools-menu { left: 0; - width: 64px; + width: 48px; list-style-type: none; top: 48px; bottom: 0; @@ -13,14 +13,10 @@ z-index: 1120; } - - #tools-menu li { position: relative; } - - #tools-menu li button:first-child { text-align: center; border: none; @@ -28,7 +24,8 @@ width: 100%; padding: 0; cursor: pointer; - height: 64px; + height: 48px; + z-index:0; } #tools-menu li button path { @@ -48,23 +45,17 @@ fill: $baseselectedicon; } -#tools-menu li.selected.expanded { - padding-bottom: 10px; -} - - .tools-menu-sub-button { text-align: center; border: none; background: none; cursor: pointer; - width: 50%; - height: 22px; - display: none; + width: 22px !important; + height:24px !important; + display: inline-block; line-height: 0; overflow: hidden; - position: absolute; - bottom: 0; + position: relative; path { fill: $baseselectedicon !important; @@ -77,37 +68,39 @@ } } -#tools-menu li button#brush-bigger-button, -#tools-menu li button#zoom-in-button, -#tools-menu li button#eraser-bigger-button, -#tools-menu li button#rectangle-bigger-button, -#tools-menu li button#ellipse-bigger-button, -#tools-menu li button#line-bigger-button { - left: 0; +#tools-menu .size-buttons { + position:absolute; + display: none; + height:48px; + left:8px; + z-index:-1; + background-color: $basecolor !important; } -#tools-menu li button#brush-smaller-button, -#tools-menu li button#zoom-out-button, -#tools-menu li button#eraser-smaller-button, -#tools-menu li button#rectangle-smaller-button, -#tools-menu li button#ellipse-smaller-button, -#tools-menu li button#line-smaller-button { - right: 0; +#tools-menu .size-buttons button { + text-align: center; + border: none; + padding: 0; + position: relative; + height:24px; + background-color: $baseselected !important; + cursor: pointer; + display:inline-block; + line-height: 0; + overflow: hidden; + width:22px; } -#tools-menu li.selected button#brush-bigger-button, -#tools-menu li.selected button#brush-smaller-button, -#tools-menu li.selected button#zoom-in-button, -#tools-menu li.selected button#zoom-out-button, -#tools-menu li.selected button#eraser-bigger-button, -#tools-menu li.selected button#eraser-smaller-button, -#tools-menu li.selected button#rectangle-bigger-button, -#tools-menu li.selected button#rectangle-smaller-button, -#tools-menu li.selected button#ellipse-bigger-button, -#tools-menu li.selected button#ellipse-smaller-button, -#tools-menu li.selected button#line-bigger-button, -#tools-menu li.selected button#line-smaller-button { - display: block; +#tools-menu .size-buttons button:hover { + background:$baseselected !important; +} + +#tools-menu .size-buttons button:active { + background:$basehovericon !important; +} + +#tools-menu li.selected .size-buttons { + display:inline-block; } diff --git a/js/tools/LassoSelectionTool.js b/js/tools/LassoSelectionTool.js new file mode 100644 index 0000000..adc16aa --- /dev/null +++ b/js/tools/LassoSelectionTool.js @@ -0,0 +1,29 @@ +class LassoSelectionTool extends SelectionTool { + constructor (name, options, switchFunc, moveTool) { + + } + + onStart(mousePos) { + super.onStart(mousePos); + } + + onDrag(mousePos) { + super.onDrag(mousePos); + } + + onEnd(mousePos) { + super.onEnd(mousePos); + } + + onSelect(mousePos) { + super.onSelect(mousePos); + } + + onDeselect(mousePos) { + super.onDeselect(mousePos); + } + + drawSelection(mousePos) { + + } +} \ No newline at end of file diff --git a/views/tools-menu.hbs b/views/tools-menu.hbs index 3af1966..3d25c66 100644 --- a/views/tools-menu.hbs +++ b/views/tools-menu.hbs @@ -1,21 +1,27 @@ \ No newline at end of file From fd3daecba583c5cc4a1877d5d05c5fbdbf6e1971 Mon Sep 17 00:00:00 2001 From: Nicola <47360416+unsettledgames@users.noreply.github.com> Date: Mon, 27 Dec 2021 19:40:23 +0100 Subject: [PATCH 03/43] Restored square templates in splash page --- css/_splash-page.scss | 7 ++----- js/ToolManager.js | 2 ++ js/pixel-editor.js | 1 + js/tools/LassoSelectionTool.js | 2 +- svg/lasso.svg | 19 +++++++++++++++++++ views/tools-menu.hbs | 5 ++++- 6 files changed, 29 insertions(+), 7 deletions(-) create mode 100644 svg/lasso.svg diff --git a/css/_splash-page.scss b/css/_splash-page.scss index 46939e3..fd03cdc 100644 --- a/css/_splash-page.scss +++ b/css/_splash-page.scss @@ -155,9 +155,7 @@ .sp-template { display: flex; align-items: center; - height: 5em; - min-width: 5em; - + text-transform: uppercase; width:16%; border-radius:5%; @@ -188,12 +186,11 @@ } } - /* .sp-template:before { content:''; float:left; padding-top:100%; - }*/ + } #sp-newpixel { -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */ diff --git a/js/ToolManager.js b/js/ToolManager.js index b5eaa8b..65bcd5e 100644 --- a/js/ToolManager.js +++ b/js/ToolManager.js @@ -16,6 +16,8 @@ const ToolManager = (() => { {type:'cursor', style:'crosshair'}, switchTool, tools["brush"]); tools["rectselect"] = new RectangularSelectionTool("rectselect", {type: 'cursor', style:'crosshair'}, switchTool, tools["moveselection"]); + tools["lassoselect"] = new LassoSelectionTool("lassoselect", + {type: 'cursor', style:'crosshair'}, switchTool, tools["moveselection"]); currTool = tools["brush"]; currTool.onSelect(); diff --git a/js/pixel-editor.js b/js/pixel-editor.js index 29a8087..4e7606b 100644 --- a/js/pixel-editor.js +++ b/js/pixel-editor.js @@ -42,6 +42,7 @@ //=include tools/PanTool.js //=include tools/ZoomTool.js //=include tools/RectangularSelectionTool.js +//=include tools/LassoSelectionTool.js //=include tools/MoveSelectionTool.js /** MODULES AND MENUS **/ diff --git a/js/tools/LassoSelectionTool.js b/js/tools/LassoSelectionTool.js index adc16aa..9d926db 100644 --- a/js/tools/LassoSelectionTool.js +++ b/js/tools/LassoSelectionTool.js @@ -1,6 +1,6 @@ class LassoSelectionTool extends SelectionTool { constructor (name, options, switchFunc, moveTool) { - + super(name, options, switchFunc, moveTool); } onStart(mousePos) { diff --git a/svg/lasso.svg b/svg/lasso.svg new file mode 100644 index 0000000..1e653da --- /dev/null +++ b/svg/lasso.svg @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/views/tools-menu.hbs b/views/tools-menu.hbs index 3d25c66..1b87fa7 100644 --- a/views/tools-menu.hbs +++ b/views/tools-menu.hbs @@ -48,9 +48,12 @@
  • +
  • + +
  • +
  • -
  • \ No newline at end of file From 8df8b3ac54b3782c09165579a05113d24bb5bda2 Mon Sep 17 00:00:00 2001 From: Nicola <47360416+unsettledgames@users.noreply.github.com> Date: Tue, 28 Dec 2021 22:51:18 +0100 Subject: [PATCH 04/43] Started lasso selection preview --- css/_splash-page.scss | 4 +-- js/ToolManager.js | 5 ++- js/layers/Layer.js | 8 +++-- js/layers/PixelGrid.js | 2 -- js/tools/LassoSelectionTool.js | 57 +++++++++++++++++++++++++++++++--- views/tools-menu.hbs | 2 +- 6 files changed, 62 insertions(+), 16 deletions(-) diff --git a/css/_splash-page.scss b/css/_splash-page.scss index fd03cdc..1bf440a 100644 --- a/css/_splash-page.scss +++ b/css/_splash-page.scss @@ -155,9 +155,9 @@ .sp-template { display: flex; align-items: center; - + text-transform: uppercase; - width:16%; + width:5.5em; border-radius:5%; margin-right:4%; margin-top:4%; diff --git a/js/ToolManager.js b/js/ToolManager.js index 65bcd5e..1b07d2d 100644 --- a/js/ToolManager.js +++ b/js/ToolManager.js @@ -38,7 +38,6 @@ const ToolManager = (() => { } function onMouseWheel(mouseEvent) { - console.log("MOUSE WHEEL"); if (!EditorState.documentCreated || Dialogue.isOpen()) return; @@ -125,11 +124,11 @@ const ToolManager = (() => { tools["eyedropper"].onEnd(mousePos, mouseEvent.target); } else if (!currFile.currentLayer.isLocked || !((Object.getPrototypeOf(currTool) instanceof DrawingTool))) { - currTool.onEnd(mousePos); + currTool.onEnd(mousePos, mouseEvent.target); } break; case 2: - tools["pan"].onEnd(mousePos); + tools["pan"].onEnd(mousePos, mouseEvent.target); break; case 3: currTool.onRightEnd(mousePos, mouseEvent.target); diff --git a/js/layers/Layer.js b/js/layers/Layer.js index 466086c..3922f5c 100644 --- a/js/layers/Layer.js +++ b/js/layers/Layer.js @@ -293,12 +293,14 @@ class Layer { while (true) { //set pixel // If the current tool is the brush - if (ToolManager.currentTool().name == 'brush' || ToolManager.currentTool().name == 'rectangle' || ToolManager.currentTool().name == 'ellipse') { + // REFACTOR: this is terrible + if (ToolManager.currentTool().name == 'brush' || ToolManager.currentTool().name == 'rectangle' || ToolManager.currentTool().name == 'ellipse' + || ToolManager.currentTool().name == 'lassoselect') { // I fill the rect - currFile.currentLayer.context.fillRect(x0-Math.floor(brushSize/2), y0-Math.floor(brushSize/2), brushSize, brushSize); + this.context.fillRect(x0-Math.floor(brushSize/2), y0-Math.floor(brushSize/2), brushSize, brushSize); } else if (ToolManager.currentTool().name == 'eraser') { // In case I'm using the eraser I must clear the rect - currFile.currentLayer.context.clearRect(x0-Math.floor(brushSize/2), y0-Math.floor(brushSize/2), brushSize, brushSize); + this.context.clearRect(x0-Math.floor(brushSize/2), y0-Math.floor(brushSize/2), brushSize, brushSize); } //if we've reached the end goal, exit the loop diff --git a/js/layers/PixelGrid.js b/js/layers/PixelGrid.js index 5401ccb..8b9c5fb 100644 --- a/js/layers/PixelGrid.js +++ b/js/layers/PixelGrid.js @@ -82,8 +82,6 @@ class PixelGrid extends Layer { this.context.strokeStyle = Settings.getCurrSettings().pixelGridColour; - console.log("Line ditance: " + this.lineDistance) - // OPTIMIZABLE, could probably be a bit more elegant // Draw horizontal lines for (let i=0; i
  • -
  • +
  • From 69d7f127903ca5695765401463c4fc76aaf6ddd9 Mon Sep 17 00:00:00 2001 From: Nicola <47360416+unsettledgames@users.noreply.github.com> Date: Tue, 28 Dec 2021 23:14:37 +0100 Subject: [PATCH 05/43] Slightly refactored selection tool Now the move tool doesn't depend on how the selection has been made anymore --- js/tools/LassoSelectionTool.js | 8 +++- js/tools/MoveSelectionTool.js | 44 +--------------------- js/tools/RectangularSelectionTool.js | 55 ++++++++++++++++++++++++++++ js/tools/SelectionTool.js | 4 ++ 4 files changed, 68 insertions(+), 43 deletions(-) diff --git a/js/tools/LassoSelectionTool.js b/js/tools/LassoSelectionTool.js index 1380f7e..7cf2477 100644 --- a/js/tools/LassoSelectionTool.js +++ b/js/tools/LassoSelectionTool.js @@ -29,6 +29,13 @@ class LassoSelectionTool extends SelectionTool { onEnd(mousePos) { super.onEnd(mousePos); + new HistoryState().EditCanvas(); + + this.currentPixels.push[this.startMousePos[0] / currFile.zoom, this.startMousePos[1] / currFile.zoom]; + this.getSelection(); + + // Switch to the move tool so that the user can move the selection + this.switchFunc(this.moveTool); } onSelect() { @@ -46,7 +53,6 @@ class LassoSelectionTool extends SelectionTool { let prevPoint = []; currFile.VFXLayer.context.clearRect(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]); - //currFile.VFXLayer.context.setLineDash([2, 2]); currFile.VFXLayer.context.strokeStyle = 'rgba(0,0,0,1)'; currFile.VFXLayer.context.fillStyle = 'rgba(0,0,0,1)'; currFile.VFXLayer.context.lineWidth = 1; diff --git a/js/tools/MoveSelectionTool.js b/js/tools/MoveSelectionTool.js index 3c81145..7fdc34a 100644 --- a/js/tools/MoveSelectionTool.js +++ b/js/tools/MoveSelectionTool.js @@ -32,8 +32,7 @@ class MoveSelectionTool extends DrawingTool { this.endSelection(); this.currSelection = this.lastCopiedSelection; // Cut the data - currFile.currentLayer.context.clearRect(this.currSelection.left-0.5, this.currSelection.top-0.5, - this.currSelection.width, this.currSelection.height); + this.selectionTool.cutSelection(); } pasteSelection() { @@ -127,47 +126,8 @@ class MoveSelectionTool extends DrawingTool { endSelection() { if (this.currSelection == undefined) return; - // Clearing the tmp (move preview) and vfx (ants) layers - currFile.TMPLayer.context.clearRect(0, 0, currFile.TMPLayer.canvas.width, currFile.TMPLayer.canvas.height); - currFile.VFXLayer.context.clearRect(0, 0, currFile.VFXLayer.canvas.width, currFile.VFXLayer.canvas.height); - - // I have to save the underlying data, so that the transparent pixels in the clipboard - // don't override the coloured pixels in the canvas - let underlyingImageData = currFile.currentLayer.context.getImageData( - this.currSelection.left, this.currSelection.top, - this.currSelection.width+1, this.currSelection.height+1 - ); - let pasteData = this.currSelection.data.data.slice(); - - for (let i=0; i Date: Tue, 28 Dec 2021 23:38:28 +0100 Subject: [PATCH 06/43] Moved cursor in selection check to selection tool --- js/Tool.js | 1 + js/tools/LassoSelectionTool.js | 6 +++++- js/tools/MoveSelectionTool.js | 18 ++---------------- js/tools/RectangularSelectionTool.js | 21 +++++++++++++++------ js/tools/SelectionTool.js | 14 ++++++++++++-- 5 files changed, 35 insertions(+), 25 deletions(-) diff --git a/js/Tool.js b/js/Tool.js index 0a9e43e..c3af458 100644 --- a/js/Tool.js +++ b/js/Tool.js @@ -6,6 +6,7 @@ var tool = {}; class Tool { name = "AbstractTool"; isSelected = false; + switchFunction = undefined; // Cursor and brush size cursorType = {}; diff --git a/js/tools/LassoSelectionTool.js b/js/tools/LassoSelectionTool.js index 7cf2477..02ade4b 100644 --- a/js/tools/LassoSelectionTool.js +++ b/js/tools/LassoSelectionTool.js @@ -1,9 +1,9 @@ class LassoSelectionTool extends SelectionTool { currentPixels = [] + currSelection = {} constructor (name, options, switchFunc, moveTool) { super(name, options, switchFunc, moveTool); - Events.on('click', this.mainButton, switchFunc, this); } @@ -79,4 +79,8 @@ class LassoSelectionTool extends SelectionTool { currFile.VFXLayer.context.fill(); currFile.VFXLayer.context.closePath(); } + + getSelection() { + + } } \ No newline at end of file diff --git a/js/tools/MoveSelectionTool.js b/js/tools/MoveSelectionTool.js index 7fdc34a..93bc297 100644 --- a/js/tools/MoveSelectionTool.js +++ b/js/tools/MoveSelectionTool.js @@ -59,7 +59,7 @@ class MoveSelectionTool extends DrawingTool { onStart(mousePos, mouseTarget) { super.onStart(mousePos, mouseTarget); - if (!this.cursorInSelectedArea(mousePos) && + if (!this.selectionTool.cursorInSelectedArea(mousePos) && !Util.isChildOfByClass(mouseTarget, "editor-top-menu")) { this.endSelection(); } @@ -101,7 +101,7 @@ class MoveSelectionTool extends DrawingTool { onHover(mousePos) { super.onHover(mousePos); - if (this.cursorInSelectedArea(mousePos)) { + if (this.selectionTool.cursorInSelectedArea(mousePos)) { currFile.canvasView.style.cursor = 'move'; } else { @@ -109,20 +109,6 @@ class MoveSelectionTool extends DrawingTool { } } - cursorInSelectedArea(cursorPos) { - // Getting the coordinates relatively to the canvas - let x = cursorPos[0] / currFile.zoom; - let y = cursorPos[1] / currFile.zoom; - - if (this.currSelection.left <= x && x <= this.currSelection.right) { - if (y <= this.currSelection.bottom && y >= this.currSelection.top) { - return true; - } - return false; - } - return false; - } - endSelection() { if (this.currSelection == undefined) return; diff --git a/js/tools/RectangularSelectionTool.js b/js/tools/RectangularSelectionTool.js index 1be6c8c..deffbdf 100644 --- a/js/tools/RectangularSelectionTool.js +++ b/js/tools/RectangularSelectionTool.js @@ -1,13 +1,8 @@ class RectangularSelectionTool extends SelectionTool { - switchFunc = undefined; - moveTool = undefined; currSelection = {}; constructor (name, options, switchFunc, moveTool) { - super(name, options, switchFunc); - - this.switchFunc = switchFunc; - this.moveTool = moveTool; + super(name, options, switchFunc, moveTool); Events.on('click', this.mainButton, switchFunc, this); } @@ -209,4 +204,18 @@ class RectangularSelectionTool extends SelectionTool { return ret; } + + cursorInSelectedArea(cursorPos) { + // Getting the coordinates relatively to the canvas + let x = cursorPos[0] / currFile.zoom; + let y = cursorPos[1] / currFile.zoom; + + if (this.currSelection.left <= x && x <= this.currSelection.right) { + if (y <= this.currSelection.bottom && y >= this.currSelection.top) { + return true; + } + return false; + } + return false; + } } \ No newline at end of file diff --git a/js/tools/SelectionTool.js b/js/tools/SelectionTool.js index ab8e544..23a080c 100644 --- a/js/tools/SelectionTool.js +++ b/js/tools/SelectionTool.js @@ -1,9 +1,19 @@ class SelectionTool extends Tool { - constructor(name, options, switchFunc) { - super(name, options, switchFunc); + switchFunc = undefined; + moveTool = undefined; + + constructor(name, options, switchFunc, moveTool) { + super(name, options); + + this.moveTool = moveTool; + this.switchFunc = switchFunc; } cutSelection() {} + pasteSelection(){} + copySelection(){} + + cursorInSelectedArea(){} } \ No newline at end of file From 908cc1b17c02f705820b08aa39d6574ed9a2f2f1 Mon Sep 17 00:00:00 2001 From: Nicola <47360416+unsettledgames@users.noreply.github.com> Date: Wed, 29 Dec 2021 23:16:16 +0100 Subject: [PATCH 07/43] Started lasso selection computing --- js/tools/BrushTool.js | 4 +- js/tools/LassoSelectionTool.js | 86 ++++++++++++++++++++++++++-- js/tools/RectangularSelectionTool.js | 1 - 3 files changed, 83 insertions(+), 8 deletions(-) diff --git a/js/tools/BrushTool.js b/js/tools/BrushTool.js index 70776b9..4ba6508 100644 --- a/js/tools/BrushTool.js +++ b/js/tools/BrushTool.js @@ -22,11 +22,11 @@ class BrushTool extends ResizableTool { currFile.currentLayer.drawLine(Math.floor(this.prevMousePos[0]/currFile.zoom), Math.floor(this.prevMousePos[1]/currFile.zoom), Math.floor(this.currMousePos[0]/currFile.zoom), - Math.floor(this.currMousePos[1]/currFile.zoom), + Math.floor(this.currMousePos[1]/currFile.zoom), this.currSize ); } - + currFile.currentLayer.updateLayerPreview(); } diff --git a/js/tools/LassoSelectionTool.js b/js/tools/LassoSelectionTool.js index 02ade4b..f98b181 100644 --- a/js/tools/LassoSelectionTool.js +++ b/js/tools/LassoSelectionTool.js @@ -1,6 +1,7 @@ class LassoSelectionTool extends SelectionTool { - currentPixels = [] - currSelection = {} + currentPixels = []; + currSelection = {}; + boundingBox = {minX: 9999999, maxX: -1, minY: 9999999, maxY: -1}; constructor (name, options, switchFunc, moveTool) { super(name, options, switchFunc, moveTool); @@ -21,21 +22,33 @@ class LassoSelectionTool extends SelectionTool { onDrag(mousePos) { super.onDrag(mousePos); + let mouseX = mousePos[0] / currFile.zoom; + let mouseY = mousePos[1] / currFile.zoom; + if (this.currentPixels[this.currentPixels.length - 1] != mousePos) this.currentPixels.push([mousePos[0] / currFile.zoom, mousePos[1] / currFile.zoom]); this.drawSelection(); - console.log("here selection"); + + if (mouseX > this.boundingBox.maxX) + this.boundingBox.maxX = Math.floor(mouseX); + if (mouseX < this.boundingBox.minX) + this.boundingBox.minX = Math.floor(mouseX); + if (mouseY < this.boundingBox.minY) + this.boundingBox.minY = Math.floor(mouseY); + if (mouseY > this.boundingBox.maxY) + this.boundingBox.maxY = Math.floor(mouseY); } onEnd(mousePos) { super.onEnd(mousePos); new HistoryState().EditCanvas(); - this.currentPixels.push[this.startMousePos[0] / currFile.zoom, this.startMousePos[1] / currFile.zoom]; + this.currentPixels.push([this.startMousePos[0] / currFile.zoom, this.startMousePos[1] / currFile.zoom]); this.getSelection(); // Switch to the move tool so that the user can move the selection this.switchFunc(this.moveTool); + this.moveTool.setSelectionData(null, this); } onSelect() { @@ -80,7 +93,70 @@ class LassoSelectionTool extends SelectionTool { currFile.VFXLayer.context.closePath(); } - getSelection() { + cursorInSelectedArea(mousePos) { } + + getSelection() { + let selected = []; + if (this.currentPixels.length <= 1){ + return; + } + + for (let x=this.boundingBox.minX; x 1) { + continue; + } + if (isNaN(T1)) { + continue; + } + + intersectionCount++; + } + + if (intersectionCount & 1) + selected.push(toCheck); + } + } + } + + for (let i=0; i Date: Sun, 2 Jan 2022 21:57:20 +0100 Subject: [PATCH 08/43] Commit before dropping the fill tool idea and reverting to the first lasso version --- js/Util.js | 14 +++ js/layers/Layer.js | 2 +- js/tools/LassoSelectionTool.js | 153 +++++++++++++++++++-------------- views/canvases.hbs | 11 +++ 4 files changed, 115 insertions(+), 65 deletions(-) diff --git a/js/Util.js b/js/Util.js index 78d6cd9..7b1bb5b 100644 --- a/js/Util.js +++ b/js/Util.js @@ -1,6 +1,20 @@ // Acts as a public static class class Util { + /** Colors a pixel in an image data + * + * @param {*} tempImage The imagedata to paint + * @param {*} pixelPos Index of the pixel + * @param {*} fillColor Color to use to paint + */ + static colorPixel(tempImage, pixelPos, fillColor) { + //console.log('colorPixel:',pixelPos); + tempImage.data[pixelPos] = fillColor.r; + tempImage.data[pixelPos + 1] = fillColor.g; + tempImage.data[pixelPos + 2] = fillColor.b; + tempImage.data[pixelPos + 3] = 255; + } + /** Tells if a pixel is empty (has alpha = 0) * * @param {*} pixel diff --git a/js/layers/Layer.js b/js/layers/Layer.js index 3922f5c..16f68cc 100644 --- a/js/layers/Layer.js +++ b/js/layers/Layer.js @@ -66,7 +66,7 @@ class Layer { Events.onCustom("del", this.tryDelete.bind(this)); - this.menuEntry.getElementsByTagName("canvas")[0].getContext('2d').imageSmoothingEnabled = false; + this.context.imageSmoothingEnabled = false; } this.initialize(); diff --git a/js/tools/LassoSelectionTool.js b/js/tools/LassoSelectionTool.js index f98b181..f9876a5 100644 --- a/js/tools/LassoSelectionTool.js +++ b/js/tools/LassoSelectionTool.js @@ -1,7 +1,7 @@ class LassoSelectionTool extends SelectionTool { currentPixels = []; currSelection = {}; - boundingBox = {minX: 9999999, maxX: -1, minY: 9999999, maxY: -1}; + boundingBox = {minX: 99999, maxX: -1, minY: 99999, maxY: -1}; constructor (name, options, switchFunc, moveTool) { super(name, options, switchFunc, moveTool); @@ -11,39 +11,41 @@ class LassoSelectionTool extends SelectionTool { onStart(mousePos) { super.onStart(mousePos); + let mouseX = Math.floor(mousePos[0] / currFile.zoom); + let mouseY = Math.floor(mousePos[1] / currFile.zoom); + // Putting the vfx layer on top of everything currFile.VFXLayer.canvas.style.zIndex = MAX_Z_INDEX; // clearSelection(); this.currentPixels = []; this.drawSelection(); - this.currentPixels.push([mousePos[0] / currFile.zoom, mousePos[1] / currFile.zoom]); + this.currentPixels.push([mouseX, mouseY]); + + this.boundingBox = {minX: 99999, maxX: -1, minY: 99999, maxY: -1}; + this.checkBoundingBox(mouseX, mouseY); } onDrag(mousePos) { super.onDrag(mousePos); - let mouseX = mousePos[0] / currFile.zoom; - let mouseY = mousePos[1] / currFile.zoom; + let mouseX = Math.floor(mousePos[0] / currFile.zoom); + let mouseY = Math.floor(mousePos[1] / currFile.zoom); if (this.currentPixels[this.currentPixels.length - 1] != mousePos) - this.currentPixels.push([mousePos[0] / currFile.zoom, mousePos[1] / currFile.zoom]); + this.currentPixels.push([mouseX, mouseY]); this.drawSelection(); - - if (mouseX > this.boundingBox.maxX) - this.boundingBox.maxX = Math.floor(mouseX); - if (mouseX < this.boundingBox.minX) - this.boundingBox.minX = Math.floor(mouseX); - if (mouseY < this.boundingBox.minY) - this.boundingBox.minY = Math.floor(mouseY); - if (mouseY > this.boundingBox.maxY) - this.boundingBox.maxY = Math.floor(mouseY); + this.checkBoundingBox(mouseX, mouseY); } onEnd(mousePos) { super.onEnd(mousePos); new HistoryState().EditCanvas(); - this.currentPixels.push([this.startMousePos[0] / currFile.zoom, this.startMousePos[1] / currFile.zoom]); + let mouseX = Math.floor(mousePos[0] / currFile.zoom); + let mouseY = Math.floor(mousePos[1] / currFile.zoom); + + this.currentPixels.push(this.currentPixels[0]); + this.checkBoundingBox(mouseX, mouseY); this.getSelection(); // Switch to the move tool so that the user can move the selection @@ -51,6 +53,18 @@ class LassoSelectionTool extends SelectionTool { this.moveTool.setSelectionData(null, this); } + checkBoundingBox(mouseX, mouseY) { + if (mouseX > this.boundingBox.maxX) + this.boundingBox.maxX = mouseX; + else if (mouseX < this.boundingBox.minX) + this.boundingBox.minX = mouseX; + + if (mouseY < this.boundingBox.minY) + this.boundingBox.minY = mouseY; + else if (mouseY > this.boundingBox.maxY) + this.boundingBox.maxY = mouseY; + } + onSelect() { super.onSelect(); } @@ -63,7 +77,6 @@ class LassoSelectionTool extends SelectionTool { if (this.currentPixels.length <= 1) return; let point = []; - let prevPoint = []; currFile.VFXLayer.context.clearRect(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]); currFile.VFXLayer.context.strokeStyle = 'rgba(0,0,0,1)'; @@ -77,17 +90,15 @@ class LassoSelectionTool extends SelectionTool { if (index == 0) currFile.VFXLayer.context.moveTo(point[0], point[1]); else { - prevPoint = this.currentPixels[index- 1]; currFile.VFXLayer.context.lineTo(point[0], point[1]); - currFile.VFXLayer.drawLine(Math.floor(prevPoint[0]), Math.floor(prevPoint[1]), - Math.floor(point[0]), Math.floor(point[1]), 1); } } - - currFile.VFXLayer.drawLine(Math.floor(prevPoint[0]), Math.floor(prevPoint[1]), - Math.floor(this.startMousePos[0] / currFile.zoom), Math.floor(this.startMousePos[1] / currFile.zoom), 1); - currFile.VFXLayer.context.lineTo(this.startMousePos[0] / currFile.zoom, this.startMousePos[1] / currFile.zoom); - + + currFile.VFXLayer.context.filter = "url(#remove-alpha)" + currFile.VFXLayer.context.lineTo(this.currentPixels[0][0], this.currentPixels[0][1]); + currFile.VFXLayer.context.stroke(); + + currFile.VFXLayer.context.filter = "none"; currFile.VFXLayer.context.fillStyle = 'rgba(0,0,0,0.3)'; currFile.VFXLayer.context.fill(); currFile.VFXLayer.context.closePath(); @@ -103,56 +114,70 @@ class LassoSelectionTool extends SelectionTool { return; } - for (let x=this.boundingBox.minX; x 1) { - continue; - } - if (isNaN(T1)) { - continue; - } - - intersectionCount++; + currPos[1]++; + prevColor = pixel[3]; } + // If the number of intersections is even (0 or 2), then the pixel is outside the + // seleted region if (intersectionCount & 1) selected.push(toCheck); + } } - } + }*/ for (let i=0; i
    + + + + + + + + + + + From bd1a806f11c9af5c3618a7fea5c5478891969548 Mon Sep 17 00:00:00 2001 From: Nicola <47360416+unsettledgames@users.noreply.github.com> Date: Sun, 2 Jan 2022 21:57:35 +0100 Subject: [PATCH 09/43] Revert "Commit before dropping the fill tool idea and reverting to the first lasso version" This reverts commit 5b6b3d33bc9e0e137a366841c63c381010e0d772. --- js/Util.js | 14 --- js/layers/Layer.js | 2 +- js/tools/LassoSelectionTool.js | 153 ++++++++++++++------------------- views/canvases.hbs | 11 --- 4 files changed, 65 insertions(+), 115 deletions(-) diff --git a/js/Util.js b/js/Util.js index 7b1bb5b..78d6cd9 100644 --- a/js/Util.js +++ b/js/Util.js @@ -1,20 +1,6 @@ // Acts as a public static class class Util { - /** Colors a pixel in an image data - * - * @param {*} tempImage The imagedata to paint - * @param {*} pixelPos Index of the pixel - * @param {*} fillColor Color to use to paint - */ - static colorPixel(tempImage, pixelPos, fillColor) { - //console.log('colorPixel:',pixelPos); - tempImage.data[pixelPos] = fillColor.r; - tempImage.data[pixelPos + 1] = fillColor.g; - tempImage.data[pixelPos + 2] = fillColor.b; - tempImage.data[pixelPos + 3] = 255; - } - /** Tells if a pixel is empty (has alpha = 0) * * @param {*} pixel diff --git a/js/layers/Layer.js b/js/layers/Layer.js index 16f68cc..3922f5c 100644 --- a/js/layers/Layer.js +++ b/js/layers/Layer.js @@ -66,7 +66,7 @@ class Layer { Events.onCustom("del", this.tryDelete.bind(this)); - this.context.imageSmoothingEnabled = false; + this.menuEntry.getElementsByTagName("canvas")[0].getContext('2d').imageSmoothingEnabled = false; } this.initialize(); diff --git a/js/tools/LassoSelectionTool.js b/js/tools/LassoSelectionTool.js index f9876a5..f98b181 100644 --- a/js/tools/LassoSelectionTool.js +++ b/js/tools/LassoSelectionTool.js @@ -1,7 +1,7 @@ class LassoSelectionTool extends SelectionTool { currentPixels = []; currSelection = {}; - boundingBox = {minX: 99999, maxX: -1, minY: 99999, maxY: -1}; + boundingBox = {minX: 9999999, maxX: -1, minY: 9999999, maxY: -1}; constructor (name, options, switchFunc, moveTool) { super(name, options, switchFunc, moveTool); @@ -11,41 +11,39 @@ class LassoSelectionTool extends SelectionTool { onStart(mousePos) { super.onStart(mousePos); - let mouseX = Math.floor(mousePos[0] / currFile.zoom); - let mouseY = Math.floor(mousePos[1] / currFile.zoom); - // Putting the vfx layer on top of everything currFile.VFXLayer.canvas.style.zIndex = MAX_Z_INDEX; // clearSelection(); this.currentPixels = []; this.drawSelection(); - this.currentPixels.push([mouseX, mouseY]); - - this.boundingBox = {minX: 99999, maxX: -1, minY: 99999, maxY: -1}; - this.checkBoundingBox(mouseX, mouseY); + this.currentPixels.push([mousePos[0] / currFile.zoom, mousePos[1] / currFile.zoom]); } onDrag(mousePos) { super.onDrag(mousePos); - let mouseX = Math.floor(mousePos[0] / currFile.zoom); - let mouseY = Math.floor(mousePos[1] / currFile.zoom); + let mouseX = mousePos[0] / currFile.zoom; + let mouseY = mousePos[1] / currFile.zoom; if (this.currentPixels[this.currentPixels.length - 1] != mousePos) - this.currentPixels.push([mouseX, mouseY]); + this.currentPixels.push([mousePos[0] / currFile.zoom, mousePos[1] / currFile.zoom]); this.drawSelection(); - this.checkBoundingBox(mouseX, mouseY); + + if (mouseX > this.boundingBox.maxX) + this.boundingBox.maxX = Math.floor(mouseX); + if (mouseX < this.boundingBox.minX) + this.boundingBox.minX = Math.floor(mouseX); + if (mouseY < this.boundingBox.minY) + this.boundingBox.minY = Math.floor(mouseY); + if (mouseY > this.boundingBox.maxY) + this.boundingBox.maxY = Math.floor(mouseY); } onEnd(mousePos) { super.onEnd(mousePos); new HistoryState().EditCanvas(); - let mouseX = Math.floor(mousePos[0] / currFile.zoom); - let mouseY = Math.floor(mousePos[1] / currFile.zoom); - - this.currentPixels.push(this.currentPixels[0]); - this.checkBoundingBox(mouseX, mouseY); + this.currentPixels.push([this.startMousePos[0] / currFile.zoom, this.startMousePos[1] / currFile.zoom]); this.getSelection(); // Switch to the move tool so that the user can move the selection @@ -53,18 +51,6 @@ class LassoSelectionTool extends SelectionTool { this.moveTool.setSelectionData(null, this); } - checkBoundingBox(mouseX, mouseY) { - if (mouseX > this.boundingBox.maxX) - this.boundingBox.maxX = mouseX; - else if (mouseX < this.boundingBox.minX) - this.boundingBox.minX = mouseX; - - if (mouseY < this.boundingBox.minY) - this.boundingBox.minY = mouseY; - else if (mouseY > this.boundingBox.maxY) - this.boundingBox.maxY = mouseY; - } - onSelect() { super.onSelect(); } @@ -77,6 +63,7 @@ class LassoSelectionTool extends SelectionTool { if (this.currentPixels.length <= 1) return; let point = []; + let prevPoint = []; currFile.VFXLayer.context.clearRect(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]); currFile.VFXLayer.context.strokeStyle = 'rgba(0,0,0,1)'; @@ -90,15 +77,17 @@ class LassoSelectionTool extends SelectionTool { if (index == 0) currFile.VFXLayer.context.moveTo(point[0], point[1]); else { + prevPoint = this.currentPixels[index- 1]; currFile.VFXLayer.context.lineTo(point[0], point[1]); + currFile.VFXLayer.drawLine(Math.floor(prevPoint[0]), Math.floor(prevPoint[1]), + Math.floor(point[0]), Math.floor(point[1]), 1); } } - - currFile.VFXLayer.context.filter = "url(#remove-alpha)" - currFile.VFXLayer.context.lineTo(this.currentPixels[0][0], this.currentPixels[0][1]); - currFile.VFXLayer.context.stroke(); - - currFile.VFXLayer.context.filter = "none"; + + currFile.VFXLayer.drawLine(Math.floor(prevPoint[0]), Math.floor(prevPoint[1]), + Math.floor(this.startMousePos[0] / currFile.zoom), Math.floor(this.startMousePos[1] / currFile.zoom), 1); + currFile.VFXLayer.context.lineTo(this.startMousePos[0] / currFile.zoom, this.startMousePos[1] / currFile.zoom); + currFile.VFXLayer.context.fillStyle = 'rgba(0,0,0,0.3)'; currFile.VFXLayer.context.fill(); currFile.VFXLayer.context.closePath(); @@ -114,70 +103,56 @@ class LassoSelectionTool extends SelectionTool { return; } - currFile.VFXLayer.context.fillStyle = "blue"; - currFile.VFXLayer.context.fillRect(this.boundingBox.minX, this.boundingBox.minY, 1, 1); - currFile.VFXLayer.context.fillRect(this.boundingBox.minX, this.boundingBox.maxY, 1, 1); - currFile.VFXLayer.context.fillRect(this.boundingBox.maxX, this.boundingBox.maxY, 1, 1); - currFile.VFXLayer.context.fillRect(this.boundingBox.maxX, this.boundingBox.minY, 1, 1); - - let fillPoint = []; - let found = false; - // Find a point inside the selection - while (!found) { - let nIntersections = 0; - let prevPoint = -1; - fillPoint = [Math.round(Math.random() * (this.boundingBox.maxX - this.boundingBox.minX)), - Math.round(Math.random() * (this.boundingBox.maxY - this.boundingBox.minY))]; - - currFile.VFXLayer.context.fillStyle = "green"; - currFile.VFXLayer.context.fillRect(fillPoint[0], fillPoint[1], 1, 1); - // Count the number of intersections with the shape - for (let i=0; i 1) { + continue; + } + if (isNaN(T1)) { + continue; + } + + intersectionCount++; } - // If the number of intersections is even (0 or 2), then the pixel is outside the - // seleted region if (intersectionCount & 1) selected.push(toCheck); - } } - }*/ + } for (let i=0; i
    - - - - - - - - - - - From bb657976e82c2eb1a382bff026b1150f79d8561e Mon Sep 17 00:00:00 2001 From: Nicola <47360416+unsettledgames@users.noreply.github.com> Date: Wed, 5 Jan 2022 00:10:02 +0100 Subject: [PATCH 10/43] Got back to BFS --- js/tools/LassoSelectionTool.js | 116 ++++++++++++++++++++------------- 1 file changed, 72 insertions(+), 44 deletions(-) diff --git a/js/tools/LassoSelectionTool.js b/js/tools/LassoSelectionTool.js index f98b181..a59937c 100644 --- a/js/tools/LassoSelectionTool.js +++ b/js/tools/LassoSelectionTool.js @@ -97,59 +97,87 @@ class LassoSelectionTool extends SelectionTool { } + isBorderOfBox(pixel) { + return pixel[0] == this.boundingBox.minX || pixel[0] == this.boundingBox.maxX || + pixel[1] == this.boundingBox.minY || pixel[1] == this.boundingBox.maxY; + } + + visit(pixel, visited) { + let toVisit = [pixel]; + let selected = []; + + while (toVisit.length > 0) { + pixel = toVisit.pop(); + selected.push(pixel); + visited.push(pixel); + + let col = currFile.VFXLayer.context.getImageData(pixel[0], pixel[1], 1, 1).data; + if (col[3] == 255) + continue; + + let top, bottom, left, right; + if (pixel[1] > 0) + top = [pixel[0], pixel[1] - 1]; + else + top = undefined; + + if (pixel[0] > 0) + left = [pixel[0] - 1, pixel[1]]; + else + left = undefined; + + if (pixel[1] < currFile.canvasSize[1]) + bottom = [pixel[0], pixel[1] + 1]; + else + bottom = undefined; + + if (pixel[0] < currFile.canvasSize[0]) + right = [pixel[0] + 1, pixel[1]]; + else + right = undefined; + + if ((right != undefined && this.isBorderOfBox(right)) || (left != undefined && this.isBorderOfBox(left)) + || (top != undefined && this.isBorderOfBox(top)) || (bottom != undefined && this.isBorderOfBox(bottom))) + return []; + + // The include problem: https://stackoverflow.com/questions/19543514/check-whether-an-array-exists-in-an-array-of-arrays + if (right != undefined && !visited.includes(right)) + toVisit.push(right); + if (left != undefined && !visited.includes(left)) + toVisit.push(left); + if (top != undefined && !visited.includes(top)) + toVisit.push(top); + if (bottom != undefined && !visited.includes(bottom)) + toVisit.push(bottom); + } + + return selected; + } + getSelection() { let selected = []; + let visited = []; if (this.currentPixels.length <= 1){ return; } + /** I'm once again asking you to make a BFS + * - This time since I'm dumb check all pixels in the bounding box + * - Start a BFS: stop when you reach the border of the bounding box: + * - In that case all the pixels you visited aren't part of the selection + * - Also stop when you touch black: + * - If you haven't found the border of the bounding box, then all the pixels you visited + * are inside the selection + */ + for (let x=this.boundingBox.minX; x 1) { - continue; - } - if (isNaN(T1)) { - continue; - } - - intersectionCount++; - } + if (!visited.includes([x, y])) { + let insidePixels = this.visit([x,y], visited); - if (intersectionCount & 1) - selected.push(toCheck); + for (let i=0; i Date: Wed, 5 Jan 2022 12:02:35 +0100 Subject: [PATCH 11/43] Committing right algorithm before optimizing it --- js/tools/LassoSelectionTool.js | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/js/tools/LassoSelectionTool.js b/js/tools/LassoSelectionTool.js index a59937c..08c72ca 100644 --- a/js/tools/LassoSelectionTool.js +++ b/js/tools/LassoSelectionTool.js @@ -14,6 +14,7 @@ class LassoSelectionTool extends SelectionTool { // Putting the vfx layer on top of everything currFile.VFXLayer.canvas.style.zIndex = MAX_Z_INDEX; // clearSelection(); + this.boundingBox = {minX: 9999999, maxX: -1, minY: 9999999, maxY: -1}; this.currentPixels = []; this.drawSelection(); this.currentPixels.push([mousePos[0] / currFile.zoom, mousePos[1] / currFile.zoom]); @@ -105,15 +106,20 @@ class LassoSelectionTool extends SelectionTool { visit(pixel, visited) { let toVisit = [pixel]; let selected = []; + let currVisited = {}; while (toVisit.length > 0) { pixel = toVisit.pop(); selected.push(pixel); - visited.push(pixel); + + visited[pixel] = true; + currVisited[pixel] = true; let col = currFile.VFXLayer.context.getImageData(pixel[0], pixel[1], 1, 1).data; if (col[3] == 255) continue; + if (this.isBorderOfBox(pixel)) + return []; let top, bottom, left, right; if (pixel[1] > 0) @@ -135,19 +141,15 @@ class LassoSelectionTool extends SelectionTool { right = [pixel[0] + 1, pixel[1]]; else right = undefined; - - if ((right != undefined && this.isBorderOfBox(right)) || (left != undefined && this.isBorderOfBox(left)) - || (top != undefined && this.isBorderOfBox(top)) || (bottom != undefined && this.isBorderOfBox(bottom))) - return []; // The include problem: https://stackoverflow.com/questions/19543514/check-whether-an-array-exists-in-an-array-of-arrays - if (right != undefined && !visited.includes(right)) + if (right != undefined && currVisited[right] == undefined) toVisit.push(right); - if (left != undefined && !visited.includes(left)) + if (left != undefined && currVisited[left] == undefined) toVisit.push(left); - if (top != undefined && !visited.includes(top)) + if (top != undefined && currVisited[top] == undefined) toVisit.push(top); - if (bottom != undefined && !visited.includes(bottom)) + if (bottom != undefined && currVisited[bottom] == undefined) toVisit.push(bottom); } @@ -156,7 +158,7 @@ class LassoSelectionTool extends SelectionTool { getSelection() { let selected = []; - let visited = []; + let visited = {}; if (this.currentPixels.length <= 1){ return; } @@ -170,12 +172,12 @@ class LassoSelectionTool extends SelectionTool { * are inside the selection */ - for (let x=this.boundingBox.minX; x Date: Wed, 5 Jan 2022 12:20:51 +0100 Subject: [PATCH 12/43] Optimized lasso tool selection --- js/Util.js | 9 +++++++++ js/tools/LassoSelectionTool.js | 18 +++++------------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/js/Util.js b/js/Util.js index 78d6cd9..6c06d9e 100644 --- a/js/Util.js +++ b/js/Util.js @@ -86,4 +86,13 @@ class Util { static toggle(elementId) { Util.getElement(elementId).classList.toggle('selected'); } + + static getPixelColor(data, x, y, dataWidth) { + let pos = (y * dataWidth + x) * 4; + return [data[pos], data[pos+1], data[pos+2], data[pos + 3]]; + } + + static isPixelTransparent(data, x, y, dataWidth) { + return this.getPixelColor(data, x, y, dataWidth)[3] == 255; + } } \ No newline at end of file diff --git a/js/tools/LassoSelectionTool.js b/js/tools/LassoSelectionTool.js index 08c72ca..b99e82e 100644 --- a/js/tools/LassoSelectionTool.js +++ b/js/tools/LassoSelectionTool.js @@ -103,7 +103,7 @@ class LassoSelectionTool extends SelectionTool { pixel[1] == this.boundingBox.minY || pixel[1] == this.boundingBox.maxY; } - visit(pixel, visited) { + visit(pixel, visited, data) { let toVisit = [pixel]; let selected = []; let currVisited = {}; @@ -115,7 +115,7 @@ class LassoSelectionTool extends SelectionTool { visited[pixel] = true; currVisited[pixel] = true; - let col = currFile.VFXLayer.context.getImageData(pixel[0], pixel[1], 1, 1).data; + let col = Util.getPixelColor(data, pixel[0], pixel[1], currFile.canvasSize[0]); if (col[3] == 255) continue; if (this.isBorderOfBox(pixel)) @@ -159,23 +159,15 @@ class LassoSelectionTool extends SelectionTool { getSelection() { let selected = []; let visited = {}; + let data = currFile.VFXLayer.context.getImageData(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]).data; if (this.currentPixels.length <= 1){ return; } - /** I'm once again asking you to make a BFS - * - This time since I'm dumb check all pixels in the bounding box - * - Start a BFS: stop when you reach the border of the bounding box: - * - In that case all the pixels you visited aren't part of the selection - * - Also stop when you touch black: - * - If you haven't found the border of the bounding box, then all the pixels you visited - * are inside the selection - */ - for (let x=this.boundingBox.minX; x<=this.boundingBox.maxX; x++) { for (let y=this.boundingBox.minY; y<=this.boundingBox.maxY; y++) { - if (visited[x, y] == undefined) { - let insidePixels = this.visit([x,y], visited); + if (visited[[x, y]] == undefined) { + let insidePixels = this.visit([x,y], visited, data); for (let i=0; i Date: Wed, 5 Jan 2022 17:41:13 +0100 Subject: [PATCH 13/43] Fixed canvas resizing bug Every time the user opened the window, the events were rebinded and added multiple times. So every popup added a canvas resizing operation. --- js/File.js | 10 ++++++++-- js/ToolManager.js | 1 - 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/js/File.js b/js/File.js index 6d2cf2b..b20a652 100644 --- a/js/File.js +++ b/js/File.js @@ -3,6 +3,7 @@ class File { canvasSize = []; zoom = 7; canvasView = document.getElementById("canvas-view"); + inited = false; // Layers layers = []; @@ -34,11 +35,13 @@ class File { // Start resize data startData = {width: 0, height:0, widthPercentage: 100, heightPercentage: 100}; - // Sprite scaling attributes openResizeCanvasWindow() { + if (!this.inited) { + this.initResizeCanvasInputs(); + this.inited = true; + } // Initializes the inputs - this.initResizeCanvasInputs(); Dialogue.showDialogue('resize-canvas'); } @@ -151,6 +154,9 @@ class File { currFile.canvasSize[1] = parseInt(currFile.canvasSize[1]) + this.rcBorders.top + this.rcBorders.bottom; + console.trace(); + console.log(currFile.canvasSize); + // Resize the canvases for (let i=0; i { } function onMouseWheel(mouseEvent) { - console.log("MOUSE WHEEL"); if (!EditorState.documentCreated || Dialogue.isOpen()) return; From de2479b00777f90f390f929900459ea18e25bf29 Mon Sep 17 00:00:00 2001 From: Nicola <47360416+unsettledgames@users.noreply.github.com> Date: Wed, 5 Jan 2022 23:54:29 +0100 Subject: [PATCH 14/43] Started making selection tools more generic Will also hopefully remove MoveSelection.js and put its functions in SelectionTool --- js/layers/Layer.js | 7 +- js/tools/EraserTool.js | 2 +- js/tools/LassoSelectionTool.js | 117 +--------------------- js/tools/MoveSelectionTool.js | 4 +- js/tools/RectangularSelectionTool.js | 73 +++++++------- js/tools/SelectionTool.js | 143 ++++++++++++++++++++++++++- 6 files changed, 187 insertions(+), 159 deletions(-) diff --git a/js/layers/Layer.js b/js/layers/Layer.js index 3922f5c..501953b 100644 --- a/js/layers/Layer.js +++ b/js/layers/Layer.js @@ -283,7 +283,7 @@ class Layer { previewWidth, previewHeight); } - drawLine(x0,y0,x1,y1, brushSize) { + drawLine(x0,y0,x1,y1, brushSize, clear=false) { var dx = Math.abs(x1-x0); var dy = Math.abs(y1-y0); var sx = (x0 < x1 ? 1 : -1); @@ -294,11 +294,10 @@ class Layer { //set pixel // If the current tool is the brush // REFACTOR: this is terrible - if (ToolManager.currentTool().name == 'brush' || ToolManager.currentTool().name == 'rectangle' || ToolManager.currentTool().name == 'ellipse' - || ToolManager.currentTool().name == 'lassoselect') { + if (!clear) { // I fill the rect this.context.fillRect(x0-Math.floor(brushSize/2), y0-Math.floor(brushSize/2), brushSize, brushSize); - } else if (ToolManager.currentTool().name == 'eraser') { + } else { // In case I'm using the eraser I must clear the rect this.context.clearRect(x0-Math.floor(brushSize/2), y0-Math.floor(brushSize/2), brushSize, brushSize); } diff --git a/js/tools/EraserTool.js b/js/tools/EraserTool.js index 7a296c2..58ef57a 100644 --- a/js/tools/EraserTool.js +++ b/js/tools/EraserTool.js @@ -23,7 +23,7 @@ class EraserTool extends ResizableTool { Math.floor(this.prevMousePos[1]/currFile.zoom), Math.floor(this.currMousePos[0]/currFile.zoom), Math.floor(this.currMousePos[1]/currFile.zoom), - this.currSize + this.currSize, true ); } diff --git a/js/tools/LassoSelectionTool.js b/js/tools/LassoSelectionTool.js index b99e82e..de369c0 100644 --- a/js/tools/LassoSelectionTool.js +++ b/js/tools/LassoSelectionTool.js @@ -1,7 +1,5 @@ class LassoSelectionTool extends SelectionTool { currentPixels = []; - currSelection = {}; - boundingBox = {minX: 9999999, maxX: -1, minY: 9999999, maxY: -1}; constructor (name, options, switchFunc, moveTool) { super(name, options, switchFunc, moveTool); @@ -14,30 +12,19 @@ class LassoSelectionTool extends SelectionTool { // Putting the vfx layer on top of everything currFile.VFXLayer.canvas.style.zIndex = MAX_Z_INDEX; // clearSelection(); - this.boundingBox = {minX: 9999999, maxX: -1, minY: 9999999, maxY: -1}; + this.currentPixels = []; this.drawSelection(); this.currentPixels.push([mousePos[0] / currFile.zoom, mousePos[1] / currFile.zoom]); } onDrag(mousePos) { - super.onDrag(mousePos); - - let mouseX = mousePos[0] / currFile.zoom; - let mouseY = mousePos[1] / currFile.zoom; + super.onDrag(mousePos); if (this.currentPixels[this.currentPixels.length - 1] != mousePos) this.currentPixels.push([mousePos[0] / currFile.zoom, mousePos[1] / currFile.zoom]); + this.drawSelection(); - - if (mouseX > this.boundingBox.maxX) - this.boundingBox.maxX = Math.floor(mouseX); - if (mouseX < this.boundingBox.minX) - this.boundingBox.minX = Math.floor(mouseX); - if (mouseY < this.boundingBox.minY) - this.boundingBox.minY = Math.floor(mouseY); - if (mouseY > this.boundingBox.maxY) - this.boundingBox.maxY = Math.floor(mouseY); } onEnd(mousePos) { @@ -79,106 +66,12 @@ class LassoSelectionTool extends SelectionTool { currFile.VFXLayer.context.moveTo(point[0], point[1]); else { prevPoint = this.currentPixels[index- 1]; - currFile.VFXLayer.context.lineTo(point[0], point[1]); currFile.VFXLayer.drawLine(Math.floor(prevPoint[0]), Math.floor(prevPoint[1]), Math.floor(point[0]), Math.floor(point[1]), 1); } } - currFile.VFXLayer.drawLine(Math.floor(prevPoint[0]), Math.floor(prevPoint[1]), - Math.floor(this.startMousePos[0] / currFile.zoom), Math.floor(this.startMousePos[1] / currFile.zoom), 1); - currFile.VFXLayer.context.lineTo(this.startMousePos[0] / currFile.zoom, this.startMousePos[1] / currFile.zoom); - - currFile.VFXLayer.context.fillStyle = 'rgba(0,0,0,0.3)'; - currFile.VFXLayer.context.fill(); - currFile.VFXLayer.context.closePath(); - } - - cursorInSelectedArea(mousePos) { - - } - - isBorderOfBox(pixel) { - return pixel[0] == this.boundingBox.minX || pixel[0] == this.boundingBox.maxX || - pixel[1] == this.boundingBox.minY || pixel[1] == this.boundingBox.maxY; - } - - visit(pixel, visited, data) { - let toVisit = [pixel]; - let selected = []; - let currVisited = {}; - - while (toVisit.length > 0) { - pixel = toVisit.pop(); - selected.push(pixel); - - visited[pixel] = true; - currVisited[pixel] = true; - - let col = Util.getPixelColor(data, pixel[0], pixel[1], currFile.canvasSize[0]); - if (col[3] == 255) - continue; - if (this.isBorderOfBox(pixel)) - return []; - - let top, bottom, left, right; - if (pixel[1] > 0) - top = [pixel[0], pixel[1] - 1]; - else - top = undefined; - - if (pixel[0] > 0) - left = [pixel[0] - 1, pixel[1]]; - else - left = undefined; - - if (pixel[1] < currFile.canvasSize[1]) - bottom = [pixel[0], pixel[1] + 1]; - else - bottom = undefined; - - if (pixel[0] < currFile.canvasSize[0]) - right = [pixel[0] + 1, pixel[1]]; - else - right = undefined; - - // The include problem: https://stackoverflow.com/questions/19543514/check-whether-an-array-exists-in-an-array-of-arrays - if (right != undefined && currVisited[right] == undefined) - toVisit.push(right); - if (left != undefined && currVisited[left] == undefined) - toVisit.push(left); - if (top != undefined && currVisited[top] == undefined) - toVisit.push(top); - if (bottom != undefined && currVisited[bottom] == undefined) - toVisit.push(bottom); - } - - return selected; - } - - getSelection() { - let selected = []; - let visited = {}; - let data = currFile.VFXLayer.context.getImageData(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]).data; - if (this.currentPixels.length <= 1){ - return; - } - - for (let x=this.boundingBox.minX; x<=this.boundingBox.maxX; x++) { - for (let y=this.boundingBox.minY; y<=this.boundingBox.maxY; y++) { - if (visited[[x, y]] == undefined) { - let insidePixels = this.visit([x,y], visited, data); - - for (let i=0; i= this.currSelection.top) { - return true; - } - return false; - } - return false; - } } \ No newline at end of file diff --git a/js/tools/SelectionTool.js b/js/tools/SelectionTool.js index 23a080c..693eab8 100644 --- a/js/tools/SelectionTool.js +++ b/js/tools/SelectionTool.js @@ -1,7 +1,21 @@ +/** TODO + * - Once the selected pixels have been obtained, save the selection outline in an image data + * - At the same time, create another image data and put the selected pixels in it + * - The move tool will then move those image datas and they'll be pasted on the right layer + * at the end of the selection + * + */ + class SelectionTool extends Tool { switchFunc = undefined; moveTool = undefined; + boundingBox = {minX: 9999999, maxX: -1, minY: 9999999, maxY: -1}; + currSelection = {}; + + previewData = undefined; + selectedPixel = undefined; + constructor(name, options, switchFunc, moveTool) { super(name, options); @@ -9,11 +23,138 @@ class SelectionTool extends Tool { this.switchFunc = switchFunc; } + onStart(mousePos) { + super.onStart(mousePos); + + let mouseX = mousePos[0] / currFile.zoom; + let mouseY = mousePos[1] / currFile.zoom; + + this.boundingBox = {minX: 9999999, maxX: -1, minY: 9999999, maxY: -1}; + this.currSelection = {}; + + this.updateBoundingBox(mouseX, mouseY); + } + + onDrag(mousePos) { + let mouseX = mousePos[0] / currFile.zoom; + let mouseY = mousePos[1] / currFile.zoom; + + this.updateBoundingBox(mouseX, mouseY); + } + cutSelection() {} pasteSelection(){} copySelection(){} - cursorInSelectedArea(){} + cursorInSelectedArea(mousePos) { + let floored = [Math.floor(mousePos[0] / currFile.zoom), Math.floor(mousePos[1] / currFile.zoom)]; + + if (this.currSelection[floored] != undefined) + return true; + return false; + } + + visit(pixel, visited, data) { + let toVisit = [pixel]; + let selected = []; + let currVisited = {}; + + currFile.TMPLayer.context.clearRect(0, 0, 512, 512); + + while (toVisit.length > 0) { + pixel = toVisit.pop(); + selected.push(pixel); + + visited[pixel] = true; + currVisited[pixel] = true; + + let col = Util.getPixelColor(data, pixel[0], pixel[1], currFile.canvasSize[0]); + if (col[3] == 255) + continue; + if (this.isBorderOfBox(pixel)) + return []; + + let top, bottom, left, right; + if (pixel[1] > 0) + top = [pixel[0], pixel[1] - 1]; + else + top = undefined; + + if (pixel[0] > 0) + left = [pixel[0] - 1, pixel[1]]; + else + left = undefined; + + if (pixel[1] < currFile.canvasSize[1]) + bottom = [pixel[0], pixel[1] + 1]; + else + bottom = undefined; + + if (pixel[0] < currFile.canvasSize[0]) + right = [pixel[0] + 1, pixel[1]]; + else + right = undefined; + + if (right != undefined && currVisited[right] == undefined) + toVisit.push(right); + if (left != undefined && currVisited[left] == undefined) + toVisit.push(left); + if (top != undefined && currVisited[top] == undefined) + toVisit.push(top); + if (bottom != undefined && currVisited[bottom] == undefined) + toVisit.push(bottom); + } + + return selected; + } + + getSelection() { + let selected = []; + let visited = {}; + let data = currFile.VFXLayer.context.getImageData(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]).data; + + /* + currFile.VFXLayer.context.fillStyle = "red"; + currFile.VFXLayer.context.fillRect(this.boundingBox.minX, this.boundingBox.minY, 1, 1); + currFile.VFXLayer.context.fillRect(this.boundingBox.maxX, this.boundingBox.minY, 1, 1); + currFile.VFXLayer.context.fillRect(this.boundingBox.minX, this.boundingBox.maxY, 1, 1); + currFile.VFXLayer.context.fillRect(this.boundingBox.maxX, this.boundingBox.maxY, 1, 1); + */ + + for (let x=this.boundingBox.minX-1; x<=this.boundingBox.maxX+1; x++) { + for (let y=this.boundingBox.minY-1; y<=this.boundingBox.maxY+1; y++) { + if (visited[[x, y]] == undefined) { + let insidePixels = this.visit([x,y], visited, data); + + for (let i=0; i this.boundingBox.maxX) + this.boundingBox.maxX = Math.floor(mouseX); + if (mouseX < this.boundingBox.minX) + this.boundingBox.minX = Math.floor(mouseX); + if (mouseY < this.boundingBox.minY) + this.boundingBox.minY = Math.floor(mouseY); + if (mouseY > this.boundingBox.maxY) + this.boundingBox.maxY = Math.floor(mouseY); + } } \ No newline at end of file From 06f534e38e657a06be880009e9296afb0f9116d4 Mon Sep 17 00:00:00 2001 From: Nicola <47360416+unsettledgames@users.noreply.github.com> Date: Thu, 6 Jan 2022 23:12:09 +0100 Subject: [PATCH 15/43] Started selection dragging --- js/tools/MoveSelectionTool.js | 15 ++++---- js/tools/RectangularSelectionTool.js | 38 +++----------------- js/tools/SelectionTool.js | 53 ++++++++++++++++++++++------ 3 files changed, 53 insertions(+), 53 deletions(-) diff --git a/js/tools/MoveSelectionTool.js b/js/tools/MoveSelectionTool.js index bf6a216..4befb2c 100644 --- a/js/tools/MoveSelectionTool.js +++ b/js/tools/MoveSelectionTool.js @@ -68,16 +68,15 @@ class MoveSelectionTool extends DrawingTool { onDrag(mousePos) { super.onDrag(mousePos); - this.currSelection = this.selectionTool.moveAnts(mousePos[0]/currFile.zoom, - mousePos[1]/currFile.zoom, this.currSelection.width, this.currSelection.height); - + // TODO: add (or subtract?) vector (boundingBoxCenter - canvasCenter); + this.selectionTool.moveOffset = [Math.floor(mousePos[0] / currFile.zoom - currFile.canvasSize[0] / 2), + Math.floor(mousePos[1] / currFile.zoom - currFile.canvasSize[1] / 2)]; // clear the entire tmp layer - currFile.TMPLayer.context.clearRect(0, 0, currFile.TMPLayer.canvas.width, currFile.TMPLayer.canvas.height); + currFile.TMPLayer.context.clearRect(0, 0, currFile.TMPLayer.canvas.width, currFile.TMPLayer.canvas.height); // put the image data on the tmp layer with offset - currFile.TMPLayer.context.putImageData( - this.currSelection.data, - Math.floor(mousePos[0] / currFile.zoom - this.currSelection.width / 2), - Math.floor(mousePos[1] / currFile.zoom - this.currSelection.height / 2)); + currFile.TMPLayer.context.putImageData(this.currSelection, + this.selectionTool.moveOffset[0], this.selectionTool.moveOffset[1]); + this.selectionTool.drawSelectedArea(); } onEnd(mousePos) { diff --git a/js/tools/RectangularSelectionTool.js b/js/tools/RectangularSelectionTool.js index e56fc51..2cba72a 100644 --- a/js/tools/RectangularSelectionTool.js +++ b/js/tools/RectangularSelectionTool.js @@ -1,5 +1,4 @@ class RectangularSelectionTool extends SelectionTool { - currSelection = {}; constructor (name, options, switchFunc, moveTool) { super(name, options, switchFunc, moveTool); @@ -46,6 +45,7 @@ class RectangularSelectionTool extends SelectionTool { onEnd(mousePos) { super.onEnd(mousePos); + new HistoryState().EditCanvas(); // Getting the end position @@ -69,40 +69,10 @@ class RectangularSelectionTool extends SelectionTool { this.boundingBox.minY = this.startMousePos[1] - 1; this.boundingBox.maxY = this.endMousePos[1] + 1; + // Switch to the move tool so that the user can move the selection + this.switchFunc(this.moveTool); // Obtain the selected pixels - this.getSelection(); - // Switch to the move tool so that the user can move the selection - this.switchFunc(this.moveTool); - this.moveTool.setSelectionData(null, this); - - /* - // Switch to the move tool so that the user can move the selection - this.switchFunc(this.moveTool); - // Preparing data for the move tool - let dataWidth = this.endMousePos[0] - this.startMousePos[0]; - let dataHeight = this.endMousePos[1] - this.startMousePos[1]; - - this.currSelection = { - left: this.startMousePos[0], right: this.endMousePos[0], - top: this.startMousePos[1], bottom: this.endMousePos[1], - - width: dataWidth, - height: dataHeight, - - data: currFile.currentLayer.context.getImageData( - this.startMousePos[0], this.startMousePos[1], - dataWidth + 1, dataHeight + 1) - }; - - // Moving the selection to the TMP layer. It will be moved back to the original - // layer if the user will cancel or end the selection - currFile.currentLayer.context.clearRect(this.startMousePos[0], this.startMousePos[1], - dataWidth + 1, dataHeight + 1); - // Moving those pixels from the current layer to the tmp layer - currFile.TMPLayer.context.putImageData(this.currSelection.data, this.startMousePos[0], this.startMousePos[1]); - - this.moveTool.setSelectionData(this.currSelection, this);*/ - + this.moveTool.setSelectionData(this.getSelection(), this); } copySelection() { diff --git a/js/tools/SelectionTool.js b/js/tools/SelectionTool.js index 693eab8..47d90c8 100644 --- a/js/tools/SelectionTool.js +++ b/js/tools/SelectionTool.js @@ -13,9 +13,12 @@ class SelectionTool extends Tool { boundingBox = {minX: 9999999, maxX: -1, minY: 9999999, maxY: -1}; currSelection = {}; + outlineData = undefined; previewData = undefined; selectedPixel = undefined; + moveOffset = [0, 0]; + constructor(name, options, switchFunc, moveTool) { super(name, options); @@ -31,6 +34,7 @@ class SelectionTool extends Tool { this.boundingBox = {minX: 9999999, maxX: -1, minY: 9999999, maxY: -1}; this.currSelection = {}; + this.moveOffset = [0, 0]; this.updateBoundingBox(mouseX, mouseY); } @@ -49,7 +53,8 @@ class SelectionTool extends Tool { copySelection(){} cursorInSelectedArea(mousePos) { - let floored = [Math.floor(mousePos[0] / currFile.zoom), Math.floor(mousePos[1] / currFile.zoom)]; + let floored = [Math.floor(mousePos[0] / currFile.zoom) + this.moveOffset[0], + Math.floor(mousePos[1] / currFile.zoom) + this.moveOffset[1]]; if (this.currSelection[floored] != undefined) return true; @@ -114,15 +119,11 @@ class SelectionTool extends Tool { let selected = []; let visited = {}; let data = currFile.VFXLayer.context.getImageData(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]).data; - - /* - currFile.VFXLayer.context.fillStyle = "red"; - currFile.VFXLayer.context.fillRect(this.boundingBox.minX, this.boundingBox.minY, 1, 1); - currFile.VFXLayer.context.fillRect(this.boundingBox.maxX, this.boundingBox.minY, 1, 1); - currFile.VFXLayer.context.fillRect(this.boundingBox.minX, this.boundingBox.maxY, 1, 1); - currFile.VFXLayer.context.fillRect(this.boundingBox.maxX, this.boundingBox.maxY, 1, 1); - */ + let currLayerData = currFile.currentLayer.context.getImageData(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]).data; + // BFS: a pixel that causes the algorithm to visit a pixel of the bounding box is outside the + // selection. Otherwise, since the algorithm stops visiting when it reaches the outline, + // the pixel is inside the selection (and so are all the ones that have been visited) for (let x=this.boundingBox.minX-1; x<=this.boundingBox.maxX+1; x++) { for (let y=this.boundingBox.minY-1; y<=this.boundingBox.maxY+1; y++) { if (visited[[x, y]] == undefined) { @@ -136,9 +137,39 @@ class SelectionTool extends Tool { } } + // Save the selection outline + this.outlineData = currFile.VFXLayer.context.getImageData(this.boundingBox.minX, + this.boundingBox.minY, this.boundingBox.maxX - this.boundingBox.minX, + this.boundingBox.maxY - this.boundingBox.minY); + // Create the image data containing the selected pixels + this.previewData = new ImageData(currFile.canvasSize[0], currFile.canvasSize[1]); + + // Save the selected pixels so that they can be moved and pasted back in the right place for (const key in this.currSelection) { - currFile.VFXLayer.context.fillStyle = "blue"; - currFile.VFXLayer.context.fillRect(parseInt(key.split(",")[0]), parseInt(key.split(",")[1]), 1, 1); + let x = parseInt(key.split(",")[0]); + let y = parseInt(key.split(",")[1]); + let index = (y * currFile.canvasSize[1] + x) * 4; + + for (let i=0; i<4; i++) { + // Save the pixel + this.previewData.data[index + i] = currLayerData[index + i]; + // Delete the data below + currLayerData[index + i] = 0; + } + } + + this.drawSelectedArea(); + + return this.previewData; + } + + drawSelectedArea() { + for (const key in this.currSelection) { + let x = parseInt(key.split(",")[0]); + let y = parseInt(key.split(",")[1]); + + currFile.TMPLayer.context.fillStyle = "rgba(10, 0, 40, 0.3)"; + currFile.TMPLayer.context.fillRect(x + this.moveOffset[0], y + this.moveOffset[1], 1, 1); } } From afff386e5fa4dc5685ec9be89615c96a20e6ad65 Mon Sep 17 00:00:00 2001 From: Nicola <47360416+unsettledgames@users.noreply.github.com> Date: Fri, 7 Jan 2022 23:10:50 +0100 Subject: [PATCH 16/43] Added back selection pasting and end of selection --- js/tools/MoveSelectionTool.js | 35 ++++++++----- js/tools/RectangularSelectionTool.js | 53 ------------------- js/tools/SelectionTool.js | 77 +++++++++++++++++++++++++--- 3 files changed, 94 insertions(+), 71 deletions(-) diff --git a/js/tools/MoveSelectionTool.js b/js/tools/MoveSelectionTool.js index 4befb2c..53fffc4 100644 --- a/js/tools/MoveSelectionTool.js +++ b/js/tools/MoveSelectionTool.js @@ -1,3 +1,10 @@ +/** TODO: + * - Clear cut selection + * - Clear copy selection + * - Clear paste selection + * + */ + class MoveSelectionTool extends DrawingTool { currSelection = undefined; selectionTool = undefined; @@ -58,29 +65,35 @@ class MoveSelectionTool extends DrawingTool { onStart(mousePos, mouseTarget) { super.onStart(mousePos, mouseTarget); - - if (!this.selectionTool.cursorInSelectedArea(mousePos) && - !Util.isChildOfByClass(mouseTarget, "editor-top-menu")) { - this.endSelection(); - } } onDrag(mousePos) { super.onDrag(mousePos); // TODO: add (or subtract?) vector (boundingBoxCenter - canvasCenter); - this.selectionTool.moveOffset = [Math.floor(mousePos[0] / currFile.zoom - currFile.canvasSize[0] / 2), - Math.floor(mousePos[1] / currFile.zoom - currFile.canvasSize[1] / 2)]; + this.selectionTool.moveOffset = + [Math.floor(mousePos[0] / currFile.zoom - currFile.canvasSize[0] / 2 - (this.selectionTool.boundingBoxCenter[0] - currFile.canvasSize[0]/2)), + Math.floor(mousePos[1] / currFile.zoom - currFile.canvasSize[1] / 2- (this.selectionTool.boundingBoxCenter[1] - currFile.canvasSize[1]/2))]; // clear the entire tmp layer currFile.TMPLayer.context.clearRect(0, 0, currFile.TMPLayer.canvas.width, currFile.TMPLayer.canvas.height); // put the image data on the tmp layer with offset currFile.TMPLayer.context.putImageData(this.currSelection, this.selectionTool.moveOffset[0], this.selectionTool.moveOffset[1]); - this.selectionTool.drawSelectedArea(); + + // Draw the selection area and outline + this.selectionTool.drawOutline(); + this.selectionTool.drawSelectedArea(); } - onEnd(mousePos) { - super.onEnd(mousePos); + onEnd(mousePos, mouseTarget) { + super.onEnd(mousePos, mouseTarget); + + if (!this.selectionTool.cursorInSelectedArea(mousePos) && + !Util.isChildOfByClass(mouseTarget, "editor-top-menu")) { + this.endSelection(); + // Switch to selection tool + this.switchFunc(this.selectionTool); + } } onSelect() { @@ -113,7 +126,5 @@ class MoveSelectionTool extends DrawingTool { return; this.currSelection = undefined; this.selectionTool.pasteSelection(); - // Switch to brush - this.switchFunc(this.endTool); } } \ No newline at end of file diff --git a/js/tools/RectangularSelectionTool.js b/js/tools/RectangularSelectionTool.js index 2cba72a..02cfed7 100644 --- a/js/tools/RectangularSelectionTool.js +++ b/js/tools/RectangularSelectionTool.js @@ -8,14 +8,6 @@ class RectangularSelectionTool extends SelectionTool { onStart(mousePos) { super.onStart(mousePos); - // Putting the vfx layer on top of everything - currFile.VFXLayer.canvas.style.zIndex = MAX_Z_INDEX; - - // Saving the start coords of the rect - this.startMousePos = [Math.floor(mousePos[0] / currFile.zoom), - Math.floor(mousePos[1] / currFile.zoom)]; - this.endMousePos = [this.startMousePos[0], this.startMousePos[1]]; - // Avoiding external selections if (this.startMousePos[0] < 0) { this.startMousePos[0] = 0; @@ -85,51 +77,6 @@ class RectangularSelectionTool extends SelectionTool { this.currSelection.width, this.currSelection.height); } - pasteSelection() { - super.pasteSelection(); - if (this.currSelection == undefined) - return; - // Clearing the tmp (move preview) and vfx (ants) layers - currFile.TMPLayer.context.clearRect(0, 0, currFile.TMPLayer.canvas.width, currFile.TMPLayer.canvas.height); - currFile.VFXLayer.context.clearRect(0, 0, currFile.VFXLayer.canvas.width, currFile.VFXLayer.canvas.height); - - // I have to save the underlying data, so that the transparent pixels in the clipboard - // don't override the coloured pixels in the canvas - let underlyingImageData = currFile.currentLayer.context.getImageData( - this.currSelection.left, this.currSelection.top, - this.currSelection.width+1, this.currSelection.height+1 - ); - let pasteData = this.currSelection.data.data.slice(); - - for (let i=0; i Date: Fri, 7 Jan 2022 23:22:42 +0100 Subject: [PATCH 17/43] Added back copy cut paste --- js/tools/SelectionTool.js | 44 ++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/js/tools/SelectionTool.js b/js/tools/SelectionTool.js index eacc707..b744128 100644 --- a/js/tools/SelectionTool.js +++ b/js/tools/SelectionTool.js @@ -63,7 +63,25 @@ class SelectionTool extends Tool { this.boundingBox.minY + (this.boundingBox.maxY - this.boundingBox.minY) / 2]; } - cutSelection() {} + cutSelection() { + let currLayerData = currFile.currentLayer.context.getImageData(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]).data; + + // Save the selected pixels so that they can be moved and pasted back in the right place + for (const key in this.currSelection) { + let x = parseInt(key.split(",")[0]); + let y = parseInt(key.split(",")[1]); + let index = (y * currFile.canvasSize[1] + x) * 4; + + for (let i=0; i<4; i++) { + // Save the pixel + this.previewData.data[index + i] = currLayerData[index + i]; + // Delete the data below + currLayerData[index + i] = 0; + } + } + + currFile.currentLayer.context.putImageData(new ImageData(currLayerData, currFile.canvasSize[0]), 0, 0); + } pasteSelection(){ if (this.currSelection == undefined) @@ -106,7 +124,9 @@ class SelectionTool extends Tool { currFile.VFXLayer.canvas.style.zIndex = MIN_Z_INDEX; } - copySelection(){} + copySelection() { + + } cursorInSelectedArea(mousePos) { let floored = [Math.floor(mousePos[0] / currFile.zoom) - this.moveOffset[0], @@ -175,7 +195,6 @@ class SelectionTool extends Tool { let selected = []; let visited = {}; let data = currFile.VFXLayer.context.getImageData(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]).data; - let currLayerData = currFile.currentLayer.context.getImageData(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]).data; // BFS: a pixel that causes the algorithm to visit a pixel of the bounding box is outside the // selection. Otherwise, since the algorithm stops visiting when it reaches the outline, @@ -200,23 +219,10 @@ class SelectionTool extends Tool { // Create the image data containing the selected pixels this.previewData = new ImageData(currFile.canvasSize[0], currFile.canvasSize[1]); - // Save the selected pixels so that they can be moved and pasted back in the right place - for (const key in this.currSelection) { - let x = parseInt(key.split(",")[0]); - let y = parseInt(key.split(",")[1]); - let index = (y * currFile.canvasSize[1] + x) * 4; - - for (let i=0; i<4; i++) { - // Save the pixel - this.previewData.data[index + i] = currLayerData[index + i]; - // Delete the data below - currLayerData[index + i] = 0; - } - } - - currFile.currentLayer.context.putImageData(new ImageData(currLayerData, currFile.canvasSize[0]), 0, 0); + // Cut the selection + this.cutSelection(); + // Put it on the TMP layer currFile.TMPLayer.context.putImageData(this.previewData, 0, 0); - this.drawSelectedArea(); return this.previewData; From ddb882faaef40984ef26f8f5d207869f529498b2 Mon Sep 17 00:00:00 2001 From: Joao Vitor Date: Sat, 8 Jan 2022 00:45:35 -0300 Subject: [PATCH 18/43] Fixed EyeDropperTool bug The EyeDropperTool was not working properly, the reason was the class name. --- js/ToolManager.js | 2 +- js/pixel-editor.js | 2 +- js/tools/EyeDropperTool.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/js/ToolManager.js b/js/ToolManager.js index 9a52d36..2244789 100644 --- a/js/ToolManager.js +++ b/js/ToolManager.js @@ -8,7 +8,7 @@ const ToolManager = (() => { tools["line"] = new LineTool("line", {type: 'html'}, switchTool); tools["fill"] = new FillTool("fill", {type: 'cursor', style: 'crosshair'}, switchTool); - tools["eyedropper"] = new EyedropperTool("eyedropper", {type: 'cursor', style: 'crosshair'}, switchTool); + tools["eyedropper"] = new EyeDropperTool("eyedropper", {type: 'cursor', style: 'crosshair'}, switchTool); tools["pan"] = new PanTool("pan", {type: 'custom'}, switchTool); tools["zoom"] = new ZoomTool("zoom", {type:'custom'}); diff --git a/js/pixel-editor.js b/js/pixel-editor.js index 29a8087..a560cbd 100644 --- a/js/pixel-editor.js +++ b/js/pixel-editor.js @@ -38,7 +38,7 @@ //=include tools/LineTool.js //=include tools/RectangleTool.js //=include tools/FillTool.js -//=include tools/EyedropperTool.js +//=include tools/EyeDropperTool.js //=include tools/PanTool.js //=include tools/ZoomTool.js //=include tools/RectangularSelectionTool.js diff --git a/js/tools/EyeDropperTool.js b/js/tools/EyeDropperTool.js index bacd62f..d28f829 100644 --- a/js/tools/EyeDropperTool.js +++ b/js/tools/EyeDropperTool.js @@ -1,4 +1,4 @@ -class EyedropperTool extends Tool { +class EyeDropperTool extends Tool { eyedropperPreview = document.getElementById("eyedropper-preview"); selectedColor = {r:0, g:0, b:0}; From d92a2cd1cd3eb39d01c274444104d08498b9edb8 Mon Sep 17 00:00:00 2001 From: Nicola <47360416+unsettledgames@users.noreply.github.com> Date: Sat, 8 Jan 2022 19:16:15 +0100 Subject: [PATCH 19/43] Fixed lasso selection --- js/tools/LassoSelectionTool.js | 15 ++++++--------- js/tools/MoveSelectionTool.js | 6 +----- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/js/tools/LassoSelectionTool.js b/js/tools/LassoSelectionTool.js index de369c0..5d85aa3 100644 --- a/js/tools/LassoSelectionTool.js +++ b/js/tools/LassoSelectionTool.js @@ -8,9 +8,6 @@ class LassoSelectionTool extends SelectionTool { onStart(mousePos) { super.onStart(mousePos); - - // Putting the vfx layer on top of everything - currFile.VFXLayer.canvas.style.zIndex = MAX_Z_INDEX; // clearSelection(); this.currentPixels = []; @@ -32,11 +29,14 @@ class LassoSelectionTool extends SelectionTool { new HistoryState().EditCanvas(); this.currentPixels.push([this.startMousePos[0] / currFile.zoom, this.startMousePos[1] / currFile.zoom]); - this.getSelection(); - // Switch to the move tool so that the user can move the selection + // Include extreme borders + this.boundingBox.maxX++; + this.boundingBox.maxY++; + + // Switch to the move tool so that the user can move the selection this.switchFunc(this.moveTool); - this.moveTool.setSelectionData(null, this); + this.moveTool.setSelectionData(this.getSelection(), this); } onSelect() { @@ -54,10 +54,7 @@ class LassoSelectionTool extends SelectionTool { let prevPoint = []; currFile.VFXLayer.context.clearRect(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]); - currFile.VFXLayer.context.strokeStyle = 'rgba(0,0,0,1)'; currFile.VFXLayer.context.fillStyle = 'rgba(0,0,0,1)'; - currFile.VFXLayer.context.lineWidth = 1; - currFile.VFXLayer.context.beginPath(); for (var index = 0; index < this.currentPixels.length; index ++) { point = this.currentPixels[index]; diff --git a/js/tools/MoveSelectionTool.js b/js/tools/MoveSelectionTool.js index 53fffc4..bd5fa54 100644 --- a/js/tools/MoveSelectionTool.js +++ b/js/tools/MoveSelectionTool.js @@ -1,8 +1,5 @@ /** TODO: - * - Clear cut selection - * - Clear copy selection - * - Clear paste selection - * + * - Test with lasso */ class MoveSelectionTool extends DrawingTool { @@ -70,7 +67,6 @@ class MoveSelectionTool extends DrawingTool { onDrag(mousePos) { super.onDrag(mousePos); - // TODO: add (or subtract?) vector (boundingBoxCenter - canvasCenter); this.selectionTool.moveOffset = [Math.floor(mousePos[0] / currFile.zoom - currFile.canvasSize[0] / 2 - (this.selectionTool.boundingBoxCenter[0] - currFile.canvasSize[0]/2)), Math.floor(mousePos[1] / currFile.zoom - currFile.canvasSize[1] / 2- (this.selectionTool.boundingBoxCenter[1] - currFile.canvasSize[1]/2))]; From 8eebb35df6e36489b0ecbc8ba7a5d07256406bc4 Mon Sep 17 00:00:00 2001 From: Nicola <47360416+unsettledgames@users.noreply.github.com> Date: Sun, 9 Jan 2022 23:42:37 +0100 Subject: [PATCH 20/43] Refined partially external selections --- js/tools/SelectionTool.js | 131 +++++++++++++++++++++++++++++++++++++- 1 file changed, 129 insertions(+), 2 deletions(-) diff --git a/js/tools/SelectionTool.js b/js/tools/SelectionTool.js index b744128..6a836dc 100644 --- a/js/tools/SelectionTool.js +++ b/js/tools/SelectionTool.js @@ -18,6 +18,7 @@ class SelectionTool extends Tool { moveOffset = [0, 0]; boundingBoxCenter = [0,0]; + reconstruct = {left:false, right:false, top:false, bottom:false}; constructor(name, options, switchFunc, moveTool) { super(name, options); @@ -41,10 +42,13 @@ class SelectionTool extends Tool { let mouseY = mousePos[1] / currFile.zoom; this.boundingBox = {minX: 9999999, maxX: -1, minY: 9999999, maxY: -1}; + this.reconstruct = {left:false, right:false, top:false, bottom:false}; + this.currSelection = {}; this.moveOffset = [0, 0]; - this.updateBoundingBox(mouseX, mouseY); + this.updateBoundingBox(Math.min(Math.max(mouseX, 0), currFile.canvasSize[0]-1), + Math.min(Math.max(mouseY, 0), currFile.canvasSize[1]-1)); } onDrag(mousePos) { @@ -53,12 +57,30 @@ class SelectionTool extends Tool { let mouseX = mousePos[0] / currFile.zoom; let mouseY = mousePos[1] / currFile.zoom; - this.updateBoundingBox(mouseX, mouseY); + if (mouseX > currFile.canvasSize[0]) + this.reconstruct.right = true; + else if (mouseX < 0) + this.reconstruct.left = true; + + if (mouseY > currFile.canvasSize[1]) + this.reconstruct.bottom = true; + else if (mouseY < 0) + this.reconstruct.top = true; + + + this.updateBoundingBox(Math.min(Math.max(mouseX, 0), currFile.canvasSize[0]-1), + Math.min(Math.max(mouseY, 0), currFile.canvasSize[1]-1)); } onEnd(mousePos) { super.onEnd(mousePos); + let mouseX = mousePos[0] / currFile.zoom; + let mouseY = mousePos[1] / currFile.zoom; + + this.updateBoundingBox(Math.min(Math.max(mouseX, 0), currFile.canvasSize[0]-1), + Math.min(Math.max(mouseY, 0), currFile.canvasSize[1]-1)); + this.boundingBoxCenter = [this.boundingBox.minX + (this.boundingBox.maxX - this.boundingBox.minX) / 2, this.boundingBox.minY + (this.boundingBox.maxY - this.boundingBox.minY) / 2]; } @@ -196,6 +218,8 @@ class SelectionTool extends Tool { let visited = {}; let data = currFile.VFXLayer.context.getImageData(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]).data; + data = this.reconstructSelection(data); + // BFS: a pixel that causes the algorithm to visit a pixel of the bounding box is outside the // selection. Otherwise, since the algorithm stops visiting when it reaches the outline, // the pixel is inside the selection (and so are all the ones that have been visited) @@ -228,6 +252,109 @@ class SelectionTool extends Tool { return this.previewData; } + reconstructSelection(data) { + let x, y, index; + let fill = false; + + if (this.reconstruct.top) { + y = Math.max(this.boundingBox.minY, 0); + this.boundingBox.minY = y; + + for (x=this.boundingBox.minX; x Date: Sun, 9 Jan 2022 23:53:28 +0100 Subject: [PATCH 21/43] Started magic wand --- js/ToolManager.js | 2 ++ js/pixel-editor.js | 3 ++- js/tools/MagicWandTool.js | 17 +++++++++++++++++ svg/magicwand.svg | 8 ++++++++ views/tools-menu.hbs | 2 ++ 5 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 js/tools/MagicWandTool.js create mode 100644 svg/magicwand.svg diff --git a/js/ToolManager.js b/js/ToolManager.js index 021e5d4..9b1714b 100644 --- a/js/ToolManager.js +++ b/js/ToolManager.js @@ -18,6 +18,8 @@ const ToolManager = (() => { {type: 'cursor', style:'crosshair'}, switchTool, tools["moveselection"]); tools["lassoselect"] = new LassoSelectionTool("lassoselect", {type: 'cursor', style:'crosshair'}, switchTool, tools["moveselection"]); + tools["magicwand"] = new MagicWandTool("magicwand", + {type: 'cursor', style:'crosshair'}, switchTool, tools["movetool"]); currTool = tools["brush"]; currTool.onSelect(); diff --git a/js/pixel-editor.js b/js/pixel-editor.js index ad3fca5..f02d734 100644 --- a/js/pixel-editor.js +++ b/js/pixel-editor.js @@ -41,9 +41,10 @@ //=include tools/EyeDropperTool.js //=include tools/PanTool.js //=include tools/ZoomTool.js +//=include tools/MoveSelectionTool.js //=include tools/RectangularSelectionTool.js //=include tools/LassoSelectionTool.js -//=include tools/MoveSelectionTool.js +//=include tools/MagicWandTool.js /** MODULES AND MENUS **/ //=include SplashPage.js diff --git a/js/tools/MagicWandTool.js b/js/tools/MagicWandTool.js new file mode 100644 index 0000000..b73d46b --- /dev/null +++ b/js/tools/MagicWandTool.js @@ -0,0 +1,17 @@ +class MagicWandTool extends SelectionTool { + constructor (name, options, switchFunc, moveTool) { + super(name, options, switchFunc, moveTool); + Events.on('click', this.mainButton, switchFunc, this); + } + + onEnd(mousePos) { + super.onStart(mousePos); + } + + getSelection() { + // this.currSelection should be filled + + this.drawSelectedArea(); + } + +} \ No newline at end of file diff --git a/svg/magicwand.svg b/svg/magicwand.svg new file mode 100644 index 0000000..dd8f263 --- /dev/null +++ b/svg/magicwand.svg @@ -0,0 +1,8 @@ + + + + diff --git a/views/tools-menu.hbs b/views/tools-menu.hbs index a50b534..4608518 100644 --- a/views/tools-menu.hbs +++ b/views/tools-menu.hbs @@ -52,6 +52,8 @@
  • +
  • +
  • From eada0f7ab1500241b10858a6a395947f787a26c5 Mon Sep 17 00:00:00 2001 From: Nicola <47360416+unsettledgames@users.noreply.github.com> Date: Mon, 10 Jan 2022 23:37:21 +0100 Subject: [PATCH 22/43] Continued MagicWand Only missing moving the selected pixels --- js/ToolManager.js | 2 +- js/tools/MagicWandTool.js | 80 +++++++++++++++++++++++++++- js/tools/RectangularSelectionTool.js | 34 +----------- js/tools/SelectionTool.js | 1 - 4 files changed, 80 insertions(+), 37 deletions(-) diff --git a/js/ToolManager.js b/js/ToolManager.js index 9b1714b..e5dc6ab 100644 --- a/js/ToolManager.js +++ b/js/ToolManager.js @@ -19,7 +19,7 @@ const ToolManager = (() => { tools["lassoselect"] = new LassoSelectionTool("lassoselect", {type: 'cursor', style:'crosshair'}, switchTool, tools["moveselection"]); tools["magicwand"] = new MagicWandTool("magicwand", - {type: 'cursor', style:'crosshair'}, switchTool, tools["movetool"]); + {type: 'cursor', style:'crosshair'}, switchTool, tools["moveselection"]); currTool = tools["brush"]; currTool.onSelect(); diff --git a/js/tools/MagicWandTool.js b/js/tools/MagicWandTool.js index b73d46b..ab2fc6b 100644 --- a/js/tools/MagicWandTool.js +++ b/js/tools/MagicWandTool.js @@ -1,4 +1,5 @@ class MagicWandTool extends SelectionTool { + constructor (name, options, switchFunc, moveTool) { super(name, options, switchFunc, moveTool); Events.on('click', this.mainButton, switchFunc, this); @@ -6,12 +7,87 @@ class MagicWandTool extends SelectionTool { onEnd(mousePos) { super.onStart(mousePos); + + this.switchFunc(this.moveTool); + this.moveTool.setSelectionData(this.getSelection(), this); } getSelection() { - // this.currSelection should be filled + let coords = [Math.floor(this.endMousePos[0]), Math.floor(this.endMousePos[1])]; + let data = currFile.currentLayer.context.getImageData(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]).data; + let index = (coords[1] * currFile.canvasSize[1] + coords[0]) * 4; + let color = [data[index], data[index+1], data[index+2], data[index+3]]; + let selectedData = new ImageData(currFile.canvasSize[0], currFile.canvasSize[1]); + this.visit([Math.floor(this.endMousePos[0]), Math.floor(this.endMousePos[1])], + this.currSelection, data, color); + + for (const pixel in this.currSelection) { + let coords = [parseInt(pixel.split(",")[0]), parseInt(pixel.split(",")[1])]; + let index = (currFile.canvasSize[1] * coords[1] + coords[0]) * 4; + + selectedData[index] = color[0]; + selectedData[index+1] = color[1]; + selectedData[index+2] = color[2]; + selectedData[index+3] = color[3]; + + this.updateBoundingBox(coords[0], coords[1]); + } + + this.outlineData = new ImageData(currFile.canvasSize[0], currFile.canvasSize[1]); + this.previewData = selectedData; this.drawSelectedArea(); + this.boundingBoxCenter = [this.boundingBox.minX + (this.boundingBox.maxX - this.boundingBox.minX) / 2, + this.boundingBox.minY + (this.boundingBox.maxY - this.boundingBox.minY) / 2]; + + return selectedData; + } + + visit(pixel, selected, data, color) { + let toVisit = [pixel]; + let visited = []; + + while (toVisit.length > 0) { + pixel = toVisit.pop(); + visited[pixel] = true; + + let col = Util.getPixelColor(data, pixel[0], pixel[1], currFile.canvasSize[0]); + if (col[0] == color[0] && col[1] == color[1] && col[2] == color[2] && col[3] == color[3]) + selected[pixel] = true; + else + continue; + + let top, bottom, left, right; + if (pixel[1] > 0) + top = [pixel[0], pixel[1] - 1]; + else + top = undefined; + + if (pixel[0] > 0) + left = [pixel[0] - 1, pixel[1]]; + else + left = undefined; + + if (pixel[1] < currFile.canvasSize[1]) + bottom = [pixel[0], pixel[1] + 1]; + else + bottom = undefined; + + if (pixel[0] < currFile.canvasSize[0]) + right = [pixel[0] + 1, pixel[1]]; + else + right = undefined; + + if (right != undefined && visited[right] == undefined) + toVisit.push(right); + if (left != undefined && visited[left] == undefined) + toVisit.push(left); + if (top != undefined && visited[top] == undefined) + toVisit.push(top); + if (bottom != undefined && visited[bottom] == undefined) + toVisit.push(bottom); + } + + return selected; } - } \ No newline at end of file diff --git a/js/tools/RectangularSelectionTool.js b/js/tools/RectangularSelectionTool.js index 02cfed7..6484de0 100644 --- a/js/tools/RectangularSelectionTool.js +++ b/js/tools/RectangularSelectionTool.js @@ -85,7 +85,7 @@ class RectangularSelectionTool extends SelectionTool { super.onDeselect(); } - drawSelection(x, y) { + drawSelection() { // Getting the vfx context let vfxContext = currFile.VFXLayer.context; @@ -97,36 +97,4 @@ class RectangularSelectionTool extends SelectionTool { currFile.VFXLayer.drawLine(this.endMousePos[0], this.endMousePos[1], this.startMousePos[0], this.endMousePos[1], 1); currFile.VFXLayer.drawLine(this.startMousePos[0], this.endMousePos[1], this.startMousePos[0], this.startMousePos[1], 1); } - - /** Moves the rect ants to the specified position - * - * @param {*} x X coordinate of the rect ants - * @param {*} y Y coordinat of the rect ants - * @param {*} width Width of the selection - * @param {*} height Height of the selectione - * - * @return The data regarding the current position and size of the selection - */ - moveAnts(x, y, width, height) { - // Getting the vfx context - let vfxContext = currFile.VFXLayer.context; - let ret = this.currSelection; - - // Clearing the vfx canvas - vfxContext.clearRect(0, 0, currFile.VFXLayer.canvas.width, currFile.VFXLayer.canvas.height); - - // Fixing the coordinates - this.currSelection.left = Math.floor(x - (width / 2)); - this.currSelection.top = Math.floor(y - (height / 2)); - this.currSelection.right = this.currSelection.left + Math.floor(width); - this.currSelection.bottom = this.currSelection.top + Math.floor(height); - - // Drawing the rect - currFile.VFXLayer.drawLine(this.currSelection.left, this.currSelection.top, this.currSelection.right, this.currSelection.top, 1); - currFile.VFXLayer.drawLine(this.currSelection.right, this.currSelection.top, this.currSelection.right, this.currSelection.bottom, 1); - currFile.VFXLayer.drawLine(this.currSelection.right, this.currSelection.bottom, this.currSelection.left, this.currSelection.bottom, 1); - currFile.VFXLayer.drawLine(this.currSelection.left, this.currSelection.bottom, this.currSelection.left, this.currSelection.top, 1); - - return ret; - } } \ No newline at end of file diff --git a/js/tools/SelectionTool.js b/js/tools/SelectionTool.js index 6a836dc..c35c2e8 100644 --- a/js/tools/SelectionTool.js +++ b/js/tools/SelectionTool.js @@ -139,7 +139,6 @@ class SelectionTool extends Tool { } } - console.log(currFile.canvasSize[0]); currFile.currentLayer.context.putImageData(pasteData, 0, 0); currFile.currentLayer.updateLayerPreview(); From 37d21f5decb19f3467516d97aa4a60ab5d1d4e45 Mon Sep 17 00:00:00 2001 From: Nicola <47360416+unsettledgames@users.noreply.github.com> Date: Wed, 12 Jan 2022 23:13:53 +0100 Subject: [PATCH 23/43] Fixed pasting selection with cusor out of canvas --- js/tools/MoveSelectionTool.js | 4 ++++ js/tools/RectangularSelectionTool.js | 4 ---- js/tools/SelectionTool.js | 11 ----------- 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/js/tools/MoveSelectionTool.js b/js/tools/MoveSelectionTool.js index bd5fa54..8290e29 100644 --- a/js/tools/MoveSelectionTool.js +++ b/js/tools/MoveSelectionTool.js @@ -44,6 +44,10 @@ class MoveSelectionTool extends DrawingTool { return; if (this.lastCopiedSelection === undefined) return; + if (!(this.currMousePos[0]/currFile.zoom >= 0 && this.currMousePos[1]/currFile.zoom >= 0 && + this.currMousePos[0]/currFile.zoom < currFile.canvasSize[0] && this.currMousePos[1]/currFile.zoom < currFile.canvasSize[1])) + this.currMousePos = [currFile.canvasSize[0]*currFile.zoom / 2, currFile.canvasSize[1]*currFile.zoom /2]; + // Finish the current selection and start a new one with the same data if (!this.cutting) { this.endSelection(); diff --git a/js/tools/RectangularSelectionTool.js b/js/tools/RectangularSelectionTool.js index 6484de0..ab3a774 100644 --- a/js/tools/RectangularSelectionTool.js +++ b/js/tools/RectangularSelectionTool.js @@ -67,10 +67,6 @@ class RectangularSelectionTool extends SelectionTool { this.moveTool.setSelectionData(this.getSelection(), this); } - copySelection() { - super.copySelection(); - } - cutSelection() { super.cutSelection(); currFile.currentLayer.context.clearRect(this.currSelection.left-0.5, this.currSelection.top-0.5, diff --git a/js/tools/SelectionTool.js b/js/tools/SelectionTool.js index c35c2e8..b083017 100644 --- a/js/tools/SelectionTool.js +++ b/js/tools/SelectionTool.js @@ -1,10 +1,3 @@ -/** TODO - * - Once the selected pixels have been obtained, save the selection outline in an image data - * - The move tool will then move those image datas and they'll be pasted on the right layer - * at the end of the selection - * - */ - class SelectionTool extends Tool { switchFunc = undefined; moveTool = undefined; @@ -144,10 +137,6 @@ class SelectionTool extends Tool { currFile.VFXLayer.canvas.style.zIndex = MIN_Z_INDEX; } - - copySelection() { - - } cursorInSelectedArea(mousePos) { let floored = [Math.floor(mousePos[0] / currFile.zoom) - this.moveOffset[0], From c135e2838d2ea76539a073c27d9ad8a6bb70f296 Mon Sep 17 00:00:00 2001 From: Sam Keddy Date: Thu, 13 Jan 2022 14:40:35 -0500 Subject: [PATCH 24/43] fixed urls changed all urls to be relative (no starting slash) and without /pixel-editor/. Fixed the testing server to deal with these requests (and also include some better logging). --- build.js | 2 ++ css/_canvas.scss | 2 +- css/_colors-menu.scss | 2 +- css/_popup-container.scss | 4 +-- images/favicon.ico | Bin 0 -> 318 bytes js/tools/PanTool.js | 6 ++-- server.js | 65 ++++++++++++++++++++++++----------- views/index.hbs | 6 ++-- views/latestLog.hbs | 2 +- views/logs/splash-update.hbs | 10 +++--- views/popups/splash-page.hbs | 2 +- views/preload.hbs | 22 ++++++------ 12 files changed, 74 insertions(+), 49 deletions(-) create mode 100644 images/favicon.ico diff --git a/build.js b/build.js index 5b4b4d0..3a2b332 100644 --- a/build.js +++ b/build.js @@ -17,6 +17,8 @@ console.log('Building Pixel Editor'); function copy_images(){ // Icons gulp.src('./images/*.png').pipe(gulp.dest(BUILDDIR)); + //favicon + gulp.src('./images/*.ico').pipe(gulp.dest(BUILDDIR)); // Splash images gulp.src('./images/Splash images/*.png').pipe(gulp.dest(BUILDDIR)); // Logs images diff --git a/css/_canvas.scss b/css/_canvas.scss index 5d02f4c..8354543 100644 --- a/css/_canvas.scss +++ b/css/_canvas.scss @@ -1,7 +1,7 @@ .drawingCanvas { - cursor: url('/pixel-editor/pencil-tool-cursor.png'); + cursor: url('pencil-tool-cursor.png'); border: solid 1px #fff; image-rendering: optimizeSpeed; diff --git a/css/_colors-menu.scss b/css/_colors-menu.scss index ec70b43..a19277d 100644 --- a/css/_colors-menu.scss +++ b/css/_colors-menu.scss @@ -112,7 +112,7 @@ button { border: none; width: 100%; - cursor: url('/pixel-editor/eyedropper.png'), auto; + cursor: url('eyedropper.png'), auto; } //white outline &.selected button::before { content: ""; diff --git a/css/_popup-container.scss b/css/_popup-container.scss index b81cf3e..5e05ad3 100644 --- a/css/_popup-container.scss +++ b/css/_popup-container.scss @@ -116,7 +116,7 @@ } .dropdown-button { - background: $basehover url('/pixel-editor/dropdown-arrow.png') right center no-repeat; + background: $basehover url('dropdown-arrow.png') right center no-repeat; border: none; border-radius: 4px; color: $basehovertext; @@ -126,7 +126,7 @@ width: 200px; text-align: left; &:hover { - background: $baseselected url('/pixel-editor/dropdown-arrow-hover.png') right center no-repeat; + background: $baseselected url('dropdown-arrow-hover.png') right center no-repeat; color: $baseselectedtext; } &.selected { diff --git a/images/favicon.ico b/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..0c0b66baac28de389409732f33ecf039d419ee42 GIT binary patch literal 318 zcmb`AF$#b%5Cq3yXA=;tL#lKZ;{W_;K9E literal 0 HcmV?d00001 diff --git a/js/tools/PanTool.js b/js/tools/PanTool.js index af54a5e..628de71 100644 --- a/js/tools/PanTool.js +++ b/js/tools/PanTool.js @@ -10,7 +10,7 @@ class PanTool extends Tool { super.onStart(mousePos); if (target.className != 'drawingCanvas') return; - currFile.canvasView.style.cursor = "url(\'/pixel-editor/pan-held.png\'), auto"; + currFile.canvasView.style.cursor = "url(\'pan-held.png\'), auto"; } onDrag(mousePos, target) { @@ -31,12 +31,12 @@ class PanTool extends Tool { if (target.className != 'drawingCanvas') return; - currFile.canvasView.style.cursor = "url(\'/pixel-editor/pan.png\'), auto"; + currFile.canvasView.style.cursor = "url(\'pan.png\'), auto"; } onSelect() { super.onSelect(); - currFile.canvasView.style.cursor = "url(\'/pixel-editor/pan.png\'), auto"; + currFile.canvasView.style.cursor = "url(\'pan.png\'), auto"; } onDeselect() { diff --git a/server.js b/server.js index 3bb5c2e..3fcb6b0 100644 --- a/server.js +++ b/server.js @@ -5,12 +5,46 @@ const app = express(); const BUILDDIR = process.argv[2] || './build'; const PORT = process.argv[3] || 3000; +const FULLBUILDPATH = path.join(__dirname, BUILDDIR) +//LOGGING +app.use((req, res, next)=> { + //console.log('REQUEST', req.method+' '+req.originalUrl, res.statusCode); + next(); +}); + + +//apply to every request +app.use((req, res, next) => { + //disable caching + res.set("Cache-Control", "no-store"); + + //enabled/disable reload module + if (process.env.RELOAD === "yes") res.locals.reload = true; + + return next(); +}); //ROUTE - other files -app.use('/pixel-editor', express.static(path.join(__dirname, BUILDDIR))); +app.use('/', express.static(FULLBUILDPATH, { + //custom function required for logging static files + setHeaders: (res, filePath, fileStats) => { + console.info('GET', '/'+path.relative(FULLBUILDPATH, filePath), res.statusCode); + } +})); + +//ROUTE - match / or any route with just numbers letters and dashes, and return index.htm (all other routes should have been handled already) +app.get('/', (req, res, next) => { + console.log('root') + res.sendFile(path.join(__dirname, BUILDDIR, 'index.htm'), {}, function (err) { + console.log('sent file'); + return next(); + }); +}); + +//HOT RELOADING // "reload" module allows us to trigger webpage reload automatically on file changes, but inside pixel editor it also // makes browser steal focus from any other window in order to ask user about unsaved changes. It might be quite // intrusive so we decided to give option to choose preferred workflow. @@ -19,6 +53,7 @@ if (process.env.RELOAD === "yes") { //start server app.server = app.listen(PORT, () => { console.log(`Web server listening on port ${PORT} (with reload module)`); + }) }); } else { @@ -27,25 +62,13 @@ if (process.env.RELOAD === "yes") { }) } -// Better to show landing page rather than 404 on editor page reload -app.get('/', (req, res) => { - res.redirect('/pixel-editor'); -}) - - -//ROUTE - match / or any route with just numbers letters and dashes, and return index.htm (all other routes should have been handled already) -app.get(['/pixel-editor', /^\/pixel-editor\/[\/a-z0-9-]+$/gi ], (req, res) => { - res.sendFile(path.join(__dirname, BUILDDIR, 'index.htm'), {}, function (err) { - if (err) { - console.log('error sending file', err); - } else { - console.log("Server: Successfully served index.html", req.originalUrl); - } - }); +app.use(function(req, res, next) { + res.status(404); + res.type('txt').send('The requested resource does not exist. Did you spell it right? Did you remember to build the app? It\'s probably your fault somehow.'); + return next(); }); -// Better to show landing page rather than 404 on editor page reload -app.get('/pixel-editor/app', (req, res) => { - res.redirect('/'); -}) - +//LOGGING +app.use((req, res, next)=> { + console.log(req.method+' '+req.originalUrl, res.statusCode); +}); diff --git a/views/index.hbs b/views/index.hbs index afc1e25..af4a57e 100644 --- a/views/index.hbs +++ b/views/index.hbs @@ -6,7 +6,7 @@ {{title}} - + {{{google-analytics}}} {{{favicons}}} @@ -40,7 +40,7 @@ {{> save-project}}
    - - + + {{#reload}}{{/reload}} \ No newline at end of file diff --git a/views/latestLog.hbs b/views/latestLog.hbs index 8c45377..ea85b0e 100644 --- a/views/latestLog.hbs +++ b/views/latestLog.hbs @@ -7,7 +7,7 @@ We have some good news for users as well! I've worked a bit on the pixel grid to make it look a bit better and less intrusive when zooming in. You can see the difference in behaviour between the new grid (left) and the old grid (right) in the image below. - + In addition, the pixel grid will now automatically be hidden when the zoom level becomes too low: in that way looking at big sprites becomes a lot less performance-heavy and it doesn't cause lag. diff --git a/views/logs/splash-update.hbs b/views/logs/splash-update.hbs index 6cdd02a..612057f 100644 --- a/views/logs/splash-update.hbs +++ b/views/logs/splash-update.hbs @@ -7,7 +7,7 @@ The editor now has a splash page! Besides a fancy cover image with beautiful art left of the page you'll be able to create a new custom pixel. You can also use the quickstart menu to quickly select a preset or load an existing file. It was designed by Skeddles himself! - + Pro tip: once you've created a new project, you can go back to the splash page by clicking on Editor -> Splash page @@ -19,7 +19,7 @@ You can now click on Edit -> Resize canvas to decrease the size drew an ant on a 1024x1024 canvas, just go to Edit -> Resize canvas and decrease the dimensions. - +

    Sprite scaling

    @@ -29,14 +29,14 @@ to scale up or down your work. With the nearest-neighbour algorithm you'll be ab in a pixel-perfect manner, while with bilinear interpolation it's possible to add (or remove, if you're scaling down a sprite) antialiasing. - +

    Line tool

    Our contributor Liam added a new line tool! Quality of life improvement are planned for it, the rectangle and the rectangular selection tools. - +

    Advanced mode: colour picker and palette block

    @@ -46,7 +46,7 @@ you'll find the new palette block, which lets you arrange your colours however y remove multiple colours at once. Changes made in the palette block will update the palette list you've always been familiar with. - +

    Other changes:

      diff --git a/views/popups/splash-page.hbs b/views/popups/splash-page.hbs index c4d67d5..7b3b169 100644 --- a/views/popups/splash-page.hbs +++ b/views/popups/splash-page.hbs @@ -12,7 +12,7 @@
    - -
  • + +
  • +
      +
    • +
    • +
    +
  • \ No newline at end of file diff --git a/views/tools-menu.hbs b/views/tools-menu.hbs index 77b22e6..c8ac9ca 100644 --- a/views/tools-menu.hbs +++ b/views/tools-menu.hbs @@ -1,6 +1,6 @@