Introduce ellipse tool (which draws rectangle for now)

This commit is contained in:
nkoder 2021-04-28 22:35:26 +02:00
parent 07f36cf7cb
commit 6cc60c00e8
12 changed files with 269 additions and 17 deletions

6
_ext/svg/ellipse.svg Normal file
View File

@ -0,0 +1,6 @@
<svg width="512" height="512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
<g>
<ellipse stroke="#000" stroke-width="32" fill="none" cx="255.50001" cy="255.5" id="svg_20" rx="239" ry="187.5"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 221 B

View File

@ -0,0 +1,22 @@
<svg width="512" height="512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
<g>
<title>Layer 1</title>
<g id="svg_4"/>
<g id="svg_5"/>
<g id="svg_6"/>
<g id="svg_7"/>
<g id="svg_8"/>
<g id="svg_9"/>
<g id="svg_10"/>
<g id="svg_11"/>
<g id="svg_12"/>
<g id="svg_13"/>
<g id="svg_14"/>
<g id="svg_15"/>
<g id="svg_16"/>
<g id="svg_17"/>
<g id="svg_18"/>
<ellipse stroke="#000" stroke-width="32" fill="#000000" cx="255.50001" cy="255.5" id="svg_20" rx="239" ry="187.5"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 528 B

View File

@ -688,6 +688,7 @@ svg {
#tools-menu li button#zoom-in-button, #tools-menu li button#zoom-in-button,
#tools-menu li button#eraser-bigger-button, #tools-menu li button#eraser-bigger-button,
#tools-menu li button#rectangle-bigger-button, #tools-menu li button#rectangle-bigger-button,
#tools-menu li button#ellipse-bigger-button,
#tools-menu li button#line-bigger-button { #tools-menu li button#line-bigger-button {
left: 0; left: 0;
} }
@ -696,6 +697,7 @@ svg {
#tools-menu li button#zoom-out-button, #tools-menu li button#zoom-out-button,
#tools-menu li button#eraser-smaller-button, #tools-menu li button#eraser-smaller-button,
#tools-menu li button#rectangle-smaller-button, #tools-menu li button#rectangle-smaller-button,
#tools-menu li button#ellipse-smaller-button,
#tools-menu li button#line-smaller-button { #tools-menu li button#line-smaller-button {
right: 0; right: 0;
} }
@ -708,6 +710,8 @@ svg {
#tools-menu li.selected button#eraser-smaller-button, #tools-menu li.selected button#eraser-smaller-button,
#tools-menu li.selected button#rectangle-bigger-button, #tools-menu li.selected button#rectangle-bigger-button,
#tools-menu li.selected button#rectangle-smaller-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-bigger-button,
#tools-menu li.selected button#line-smaller-button { #tools-menu li.selected button#line-smaller-button {
display: block; display: block;

View File

@ -9,7 +9,7 @@ function line(x0,y0,x1,y1, brushSize) {
while (true) { while (true) {
//set pixel //set pixel
// If the current tool is the brush // If the current tool is the brush
if (currentTool.name == 'pencil' || currentTool.name == 'rectangle') { if (currentTool.name == 'pencil' || currentTool.name == 'rectangle' || currentTool.name == 'ellipse') {
// I fill the rect // I fill the rect
currentLayer.context.fillRect(x0-Math.floor(brushSize/2), y0-Math.floor(brushSize/2), brushSize, brushSize); currentLayer.context.fillRect(x0-Math.floor(brushSize/2), y0-Math.floor(brushSize/2), brushSize, brushSize);
} else if (currentTool.name == 'eraser') { } else if (currentTool.name == 'eraser') {

146
js/_ellipse.js Normal file
View File

@ -0,0 +1,146 @@
// Saving the empty rect svg
var emptyEllipseSVG = document.getElementById("ellipse-empty-button-svg");
// and the full rect svg so that I can change them when the user changes rect modes
var fullEllipseSVG = document.getElementById("ellipse-full-button-svg");
// The start mode is empty ellipse
var ellipseDrawMode = 'empty';
// I'm not drawing a ellipse at the beginning
var isDrawingEllipse = false;
// Ellipse coordinates
let startEllipseX;
let startEllipseY;
let endEllipseX;
let endEllipseY;
// TODO: [ELLIPSE] Make it draw ellipse instead of copy-pasted rectangle
/** Starts drawing the ellipse, saves the start coordinates
*
* @param {*} mouseEvent
*/
function startEllipseDrawing(mouseEvent) {
// Putting the vfx layer on top of everything
VFXCanvas.style.zIndex = parseInt(currentLayer.canvas.style.zIndex, 10) + 1;;
// Updating flag
isDrawingEllipse = true;
// Saving the start coords of the ellipse
let cursorPos = getCursorPosition(mouseEvent);
startEllipseX = Math.floor(cursorPos[0] / zoom) + 0.5;
startEllipseY = Math.floor(cursorPos[1] / zoom) + 0.5;
drawEllipse(startEllipseX, startEllipseY);
}
// TODO: [ELLIPSE] Make it draw ellipse instead of copy-pasted rectangle
/** Updates the ellipse preview depending on the position of the mouse
*
* @param {*} mouseEvent The mouseEvent from which we'll get the mouse position
*/
function updateEllipseDrawing(mouseEvent) {
let pos = getCursorPosition(mouseEvent);
// Drawing the ellipse at the right position
drawEllipse(Math.round(pos[0] / zoom) + 0.5, Math.round(pos[1] / zoom) + 0.5);
}
// TODO: [ELLIPSE] Make it draw ellipse instead of copy-pasted rectangle
/** Finishes drawing the ellipse, decides the end coordinates and moves the preview ellipse to the
* current layer
*
* @param {*} mouseEvent event from which we'll get the mouse position
*/
function endEllipseDrawing(mouseEvent) {
// Getting the end position
let currentPos = getCursorPosition(mouseEvent);
let vfxContext = VFXCanvas.getContext("2d");
endEllipseX = Math.round(currentPos[0] / zoom) + 0.5;
endEllipseY = Math.round(currentPos[1] / zoom) + 0.5;
// Inverting end and start (start must always be the top left corner)
if (endEllipseX < startEllipseX) {
let tmp = endEllipseX;
endEllipseX = startEllipseX;
startEllipseX = tmp;
}
// Same for the y
if (endEllipseY < startEllipseY) {
let tmp = endEllipseY;
endEllipseY = startEllipseY;
startEllipseY = tmp;
}
// Resetting this
isDrawingEllipse = false;
// Drawing the ellipse
startEllipseY -= 0.5;
endEllipseY -= 0.5;
endEllipseX -= 0.5;
startEllipseX -= 0.5;
// Setting the correct linewidth and colour
currentLayer.context.lineWidth = tool.ellipse.brushSize;
currentLayer.context.fillStyle = currentGlobalColor;
// Drawing the ellipse using 4 lines
line(startEllipseX, startEllipseY, endEllipseX, startEllipseY, tool.ellipse.brushSize);
line(endEllipseX, startEllipseY, endEllipseX, endEllipseY, tool.ellipse.brushSize);
line(endEllipseX, endEllipseY, startEllipseX, endEllipseY, tool.ellipse.brushSize);
line(startEllipseX, endEllipseY, startEllipseX, startEllipseY, tool.ellipse.brushSize);
// If I have to fill it, I do so
if (ellipseDrawMode == 'fill') {
currentLayer.context.fillRect(startEllipseX, startEllipseY, endEllipseX - startEllipseX, endEllipseY - startEllipseY);
}
// Clearing the vfx canvas
vfxContext.clearRect(0, 0, VFXCanvas.width, VFXCanvas.height);
}
// TODO: [ELLIPSE] Make it draw ellipse instead of copy-pasted rectangle
/** Draws a ellipse with end coordinates given by x and y on the VFX layer (draws
* the preview for the ellipse tool)
*
* @param {*} x The current end x of the ellipse
* @param {*} y The current end y of the ellipse
*/
function drawEllipse(x, y) {
// Getting the vfx context
let vfxContext = VFXCanvas.getContext("2d");
// Clearing the vfx canvas
vfxContext.clearRect(0, 0, VFXCanvas.width, VFXCanvas.height);
// Drawing the ellipse
vfxContext.lineWidth = tool.ellipse.brushSize;
vfxContext.strokeStyle = currentGlobalColor;
// Drawing the ellipse
vfxContext.beginPath();
if ((tool.ellipse.brushSize % 2 ) == 0) {
vfxContext.rect(startEllipseX - 0.5, startEllipseY - 0.5, x - startEllipseX, y - startEllipseY);
}
else {
vfxContext.rect(startEllipseX, startEllipseY, x - startEllipseX, y - startEllipseY);
}
vfxContext.setLineDash([]);
vfxContext.stroke();
}
/** Sets the correct tool icon depending on its mode
*
*/
function setEllipseToolSvg() {
console.log("set eilipse svg");
if (ellipseDrawMode == 'empty') {
emptyEllipseSVG.setAttribute('display', 'visible');
fullEllipseSVG.setAttribute('display', 'none');
}
else {
emptyEllipseSVG.setAttribute('display', 'none');
fullEllipseSVG.setAttribute('display', 'visible');
}
}

View File

@ -65,6 +65,11 @@ function KeyPress(e) {
case 77: case 109: case 77: case 109:
tool.rectselect.switchTo() tool.rectselect.switchTo()
break; break;
// TODO: [ELLIPSE] Decide on a shortcut to use. "s" was chosen without any in-team consultation.
// ellipse tool, s
case 83:
tool.ellipse.switchTo()
break;
// rectangle tool, u // rectangle tool, u
case 85: case 85:
tool.rectangle.switchTo() tool.rectangle.switchTo()

View File

@ -22,7 +22,7 @@ window.addEventListener("mousedown", function (mouseEvent) {
else if (mouseEvent.altKey) else if (mouseEvent.altKey)
currentTool = tool.eyedropper; currentTool = tool.eyedropper;
else if (mouseEvent.target.className == 'drawingCanvas' && else if (mouseEvent.target.className == 'drawingCanvas' &&
(currentTool.name == 'pencil' || currentTool.name == 'eraser' || currentTool.name == 'rectangle' || currentTool.name === 'line')) (currentTool.name == 'pencil' || currentTool.name == 'eraser' || currentTool.name == 'rectangle' || currentTool.name == 'ellipse' || currentTool.name === 'line'))
new HistoryStateEditCanvas(); new HistoryStateEditCanvas();
else if (currentTool.name == 'moveselection') { else if (currentTool.name == 'moveselection') {
if (!cursorInSelectedArea() && if (!cursorInSelectedArea() &&
@ -46,6 +46,7 @@ window.addEventListener("mousedown", function (mouseEvent) {
currentTool = tool.resizeeraser; currentTool = tool.resizeeraser;
tool.eraser.previousBrushSize = tool.eraser.brushSize; tool.eraser.previousBrushSize = tool.eraser.brushSize;
} }
// TODO: [ELLIPSE] Do we need similar logic related to ellipse?
else if (currentTool.name == 'rectangle' && mouseEvent.which == 3) { else if (currentTool.name == 'rectangle' && mouseEvent.which == 3) {
currentTool = tool.resizerectangle; currentTool = tool.resizerectangle;
tool.rectangle.previousBrushSize = tool.rectangle.brushSize; tool.rectangle.previousBrushSize = tool.rectangle.brushSize;
@ -160,6 +161,10 @@ window.addEventListener("mouseup", function (mouseEvent) {
endRectDrawing(mouseEvent); endRectDrawing(mouseEvent);
currentLayer.updateLayerPreview(); currentLayer.updateLayerPreview();
} }
else if (currentTool.name == 'ellipse' && isDrawingEllipse) {
endEllipseDrawing(mouseEvent);
currentLayer.updateLayerPreview();
}
dragging = false; dragging = false;
currentTool = currentToolTemp; currentTool = currentToolTemp;
@ -269,6 +274,21 @@ function draw (mouseEvent) {
updateRectDrawing(mouseEvent); updateRectDrawing(mouseEvent);
} }
} }
else if (currentTool.name == 'ellipse')
{
//hide brush preview outside of canvas / canvas view
if (mouseEvent.target.className == 'drawingCanvas'|| mouseEvent.target.className == 'drawingCanvas')
brushPreview.style.visibility = 'visible';
else
brushPreview.style.visibility = 'hidden';
if (!isDrawingEllipse && dragging) {
startEllipseDrawing(mouseEvent);
}
else if (dragging){
updateEllipseDrawing(mouseEvent);
}
}
else if (currentTool.name == 'pan' && dragging) { else if (currentTool.name == 'pan' && dragging) {
// Setting first layer position // Setting first layer position
layers[0].setCanvasOffset(layers[0].canvas.offsetLeft + (cursorLocation[0] - lastMouseClickPos[0]), layers[0].canvas.offsetTop + (cursorLocation[1] - lastMouseClickPos[1])); layers[0].setCanvasOffset(layers[0].canvas.offsetLeft + (cursorLocation[0] - lastMouseClickPos[0]), layers[0].canvas.offsetTop + (cursorLocation[1] - lastMouseClickPos[1]));
@ -328,12 +348,15 @@ function draw (mouseEvent) {
//var roundingAmount = 20 - Math.round(distanceFromClick/10); //var roundingAmount = 20 - Math.round(distanceFromClick/10);
//this doesnt work in reverse... because... it's not basing it off of the brush size which it should be //this doesnt work in reverse... because... it's not basing it off of the brush size which it should be
var rectangleSizeChange = Math.round(distanceFromClick/10); var rectangleSizeChange = Math.round(distanceFromClick/10);
// TODO: [ELLIPSE] Do we need similar logic related to ellipse?
var newRectangleSize = tool.rectangle.previousBrushSize + rectangleSizeChange; var newRectangleSize = tool.rectangle.previousBrushSize + rectangleSizeChange;
//set the brush to the new size as long as its bigger than 1 //set the brush to the new size as long as its bigger than 1
// TODO: [ELLIPSE] Do we need similar logic related to ellipse?
tool.rectangle.brushSize = Math.max(1,newRectangleSize); tool.rectangle.brushSize = Math.max(1,newRectangleSize);
//fix offset so the cursor stays centered //fix offset so the cursor stays centered
// TODO: [ELLIPSE] Do we need similar logic related to ellipse?
tool.rectangle.moveBrushPreview(lastMouseClickPos); tool.rectangle.moveBrushPreview(lastMouseClickPos);
currentTool.updateCursor(); currentTool.updateCursor();
} }

View File

@ -1,10 +1,10 @@
// Saving the empty rect svg // Saving the empty rect svg
var emptySVG = document.getElementById("empty-button-svg"); var emptyRectangleSVG = document.getElementById("rectangle-empty-button-svg");
// and the full rect svg so that I can change them when the user changes rect modes // and the full rect svg so that I can change them when the user changes rect modes
var fullSVG = document.getElementById("full-button-svg"); var fullRectangleSVG = document.getElementById("rectangle-full-button-svg");
// The start mode is empty rectangle // The start mode is empty rectangle
var drawMode = 'empty'; var rectangleDrawMode = 'empty';
// I'm not drawing a rectangle at the beginning // I'm not drawing a rectangle at the beginning
var isDrawingRect = false; var isDrawingRect = false;
@ -88,7 +88,7 @@ function endRectDrawing(mouseEvent) {
line(startRectX, endRectY, startRectX, startRectY, tool.rectangle.brushSize); line(startRectX, endRectY, startRectX, startRectY, tool.rectangle.brushSize);
// If I have to fill it, I do so // If I have to fill it, I do so
if (drawMode == 'fill') { if (rectangleDrawMode == 'fill') {
currentLayer.context.fillRect(startRectX, startRectY, endRectX - startRectX, endRectY - startRectY); currentLayer.context.fillRect(startRectX, startRectY, endRectX - startRectX, endRectY - startRectY);
} }
@ -130,13 +130,13 @@ function drawRectangle(x, y) {
* *
*/ */
function setRectToolSvg() { function setRectToolSvg() {
if (drawMode == 'empty') { if (rectangleDrawMode == 'empty') {
emptySVG.setAttribute('display', 'visible'); emptyRectangleSVG.setAttribute('display', 'visible');
fullSVG.setAttribute('display', 'none'); fullRectangleSVG.setAttribute('display', 'none');
} }
else { else {
emptySVG.setAttribute('display', 'none'); emptyRectangleSVG.setAttribute('display', 'none');
fullSVG.setAttribute('display', 'visible'); fullRectangleSVG.setAttribute('display', 'visible');
} }
} }

View File

@ -35,12 +35,12 @@ on('click',"eraser-smaller-button", function(e){
on('click','rectangle-button', function(e){ on('click','rectangle-button', function(e){
// If the user clicks twice on the button, they change the draw mode // If the user clicks twice on the button, they change the draw mode
if (currentTool.name == 'rectangle') { if (currentTool.name == 'rectangle') {
if (drawMode == 'empty') { if (rectangleDrawMode == 'empty') {
drawMode = 'fill'; rectangleDrawMode = 'fill';
setRectToolSvg(); setRectToolSvg();
} }
else { else {
drawMode = 'empty'; rectangleDrawMode = 'empty';
setRectToolSvg(); setRectToolSvg();
} }
} }
@ -49,6 +49,24 @@ on('click','rectangle-button', function(e){
} }
}, false); }, false);
// ellipse
on('click','ellipse-button', function(e){
// If the user clicks twice on the button, they change the draw mode
if (currentTool.name == 'ellipse') {
if (ellipseDrawMode == 'empty') {
ellipseDrawMode = 'fill';
setEllipseToolSvg();
}
else {
ellipseDrawMode = 'empty';
setEllipseToolSvg();
}
}
else {
tool.ellipse.switchTo();
}
}, false);
// rectangle bigger // rectangle bigger
on('click',"rectangle-bigger-button", function(){ on('click',"rectangle-bigger-button", function(){
tool.rectangle.brushSize++; tool.rectangle.brushSize++;
@ -60,6 +78,17 @@ on('click',"rectangle-smaller-button", function(e){
tool.rectangle.brushSize--; tool.rectangle.brushSize--;
}, false); }, false);
// ellipse bigger
on('click',"ellipse-bigger-button", function(){
tool.ellipse.brushSize++;
}, false);
// ellipse smaller
on('click',"ellipse-smaller-button", function(e){
if(tool.ellipse.brushSize > 1)
tool.ellipse.brushSize--;
}, false);
//fill //fill
on('click',"fill-button", function(){ on('click',"fill-button", function(){
tool.fill.switchTo(); tool.fill.switchTo();

View File

@ -73,6 +73,7 @@
//=include _rectSelect.js //=include _rectSelect.js
//=include _move.js //=include _move.js
//=include _rectangle.js //=include _rectangle.js
//=include _ellipse.js
/**onload**/ /**onload**/
//=include _onLoad.js //=include _onLoad.js

View File

@ -41,6 +41,10 @@ new Tool('rectangle', {
cursor: 'crosshair', cursor: 'crosshair',
brushPreview: true, brushPreview: true,
}); });
new Tool('ellipse', {
cursor: 'crosshair',
brushPreview: true,
});
new Tool('resizerectangle', { new Tool('resizerectangle', {
cursor: 'default', cursor: 'default',
}); });

View File

@ -33,6 +33,7 @@
<img src="/pixel-editor/zoom-in.png" /> <img src="/pixel-editor/zoom-in.png" />
<img src = "/pixel-editor/eraser.png"/> <img src = "/pixel-editor/eraser.png"/>
<img src = "/pixel-editor/rectselect.png"/> <img src = "/pixel-editor/rectselect.png"/>
<!-- TODO: [ELLIPSE] Where is this icon used? Do we need similar one for ellipsis? -->
<img src= "/pixel-editor/rectangle.png"> <img src= "/pixel-editor/rectangle.png">
</div> </div>
@ -119,12 +120,23 @@
</li> </li>
<li class="expanded"> <li class="expanded">
<button title="Rectangle Tool (U)" id="rectangle-button">{{svg "rectangle.svg" width="32" height="32" id = "empty-button-svg"}} <button title="Rectangle Tool (U)" id="rectangle-button">{{svg "rectangle.svg" width="32" height="32" id = "rectangle-empty-button-svg"}}
{{svg "fullrect.svg" width="32" height="32" id = "full-button-svg" display = "none"}}</button> {{svg "fullrect.svg" width="32" height="32" id = "rectangle-full-button-svg" display = "none"}}</button>
<button title="Increase Rectangle Size" id="rectangle-bigger-button" class="tools-menu-sub-button">{{svg "plus.svg" width="12" height="12"}}</button> <button title="Increase Rectangle Size" id="rectangle-bigger-button" class="tools-menu-sub-button">{{svg "plus.svg" width="12" height="12"}}</button>
<button title="Decrease Rectangle Size" id="rectangle-smaller-button" class="tools-menu-sub-button">{{svg "minus.svg" width="12" height="12"}}</button> <button title="Decrease Rectangle Size" id="rectangle-smaller-button" class="tools-menu-sub-button">{{svg "minus.svg" width="12" height="12"}}</button>
</li> </li>
<li class="expanded">
<!-- TODO: [ELLIPSE] Decide on a shortcut to use. "S" was chosen without any in-team consultation. -->
<!-- TODO: [ELLIPSE] Decide on icons to use. Current ones are quickly prepared drafts and display with incorrect color. -->
<button title="Ellipse Tool (S)" id="ellipse-button">
{{svg "ellipse.svg" width="32" height="32" id = "ellipse-empty-button-svg"}}
{{svg "filledellipse.svg" width="32" height="32" id = "ellipse-full-button-svg" display = "none"}}
</button>
<button title="Increase Ellipse Size" id="ellipse-bigger-button" class="tools-menu-sub-button">{{svg "plus.svg" width="12" height="12"}}</button>
<button title="Decrease Ellipse Size" id="ellipse-smaller-button" class="tools-menu-sub-button">{{svg "minus.svg" width="12" height="12"}}</button>
</li>
<li><button title="Fill Tool (F)" id="fill-button">{{svg "fill.svg" width="32" height="32"}}</button></li> <li><button title="Fill Tool (F)" id="fill-button">{{svg "fill.svg" width="32" height="32"}}</button></li>
<li><button title="Eyedropper Tool (E)" id="eyedropper-button">{{svg "eyedropper.svg" width="32" height="32"}}</button></li> <li><button title="Eyedropper Tool (E)" id="eyedropper-button">{{svg "eyedropper.svg" width="32" height="32"}}</button></li>