From 8a40c9610cc424bc1747fb53c2f485bd75102398 Mon Sep 17 00:00:00 2001 From: Michael Womick Date: Thu, 22 Aug 2024 23:25:00 -0500 Subject: [PATCH 1/8] allow user to constrain the angle to 45deg increments --- script.js | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/script.js b/script.js index fe20ded..7a24a5e 100644 --- a/script.js +++ b/script.js @@ -12,6 +12,8 @@ var color_choices = [ "#CCCCCC", ]; +var radiansPer45Degrees = Math.PI / 4; + var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); var img = new Image(); @@ -28,7 +30,7 @@ var masterColors = []; var drawMode setDrawMode('polygon') - +var constrainAngles = false; var showNormalized = false; var modeMessage = document.querySelector('#mode'); @@ -166,6 +168,11 @@ function getParentPoints () { return parentPoints; } +window.addEventListener('keyup', function(e) { + if (e.key === 'Shift') { + constrainAngles = false; + } +}); document.querySelector('#clipboard').addEventListener('click', function(e) { e.preventDefault(); @@ -198,6 +205,20 @@ canvas.addEventListener('mousemove', function(e) { // update x y coords var xcoord = document.querySelector('#x'); var ycoord = document.querySelector('#y'); + + if(constrainAngles) { + var lastPoint = points[points.length - 1]; + var dx = x - lastPoint[0]; + var dy = y - lastPoint[1]; + var angle = Math.atan2(dy, dx); + var length = Math.sqrt(dx * dx + dy * dy); + const snappedAngle = Math.round(angle / radiansPer45Degrees) * radiansPer45Degrees; + var new_x = lastPoint[0] + length * Math.cos(snappedAngle); + var new_y = lastPoint[1] + length * Math.sin(snappedAngle); + x = Math.round(new_x); + y = Math.round(new_y); + } + xcoord.innerHTML = x; ycoord.innerHTML = y; @@ -309,6 +330,19 @@ canvas.addEventListener('click', function(e) { x = Math.round(x); y = Math.round(y); + if(constrainAngles) { + var lastPoint = points[points.length - 1]; + var dx = x - lastPoint[0]; + var dy = y - lastPoint[1]; + var angle = Math.atan2(dy, dx); + var length = Math.sqrt(dx * dx + dy * dy); + const snappedAngle = Math.round(angle / radiansPer45Degrees) * radiansPer45Degrees; + var new_x = lastPoint[0] + length * Math.cos(snappedAngle); + var new_y = lastPoint[1] + length * Math.sin(snappedAngle); + x = Math.round(new_x); + y = Math.round(new_y); + } + // If starting point os clicked, we complete the polygon if (drawMode === 'polygon' && points.length > 1) { const [firstPointX, firstPointY] = points[0] @@ -466,6 +500,10 @@ window.addEventListener('keydown', function(e) { undo() } + if (e.key === 'Shift') { + constrainAngles = true; + } + if (e.key === 'Escape') { discardCurrentPolygon() } From 78647cebf52bd4fc10d206a08fa88defc950b12a Mon Sep 17 00:00:00 2001 From: Michael Womick Date: Thu, 22 Aug 2024 23:30:56 -0500 Subject: [PATCH 2/8] fixed bug where 'Clear all polygons' causes polygon colors to change unexpectedly upon closure --- script.js | 1 + 1 file changed, 1 insertion(+) diff --git a/script.js b/script.js index 7a24a5e..6f2e575 100644 --- a/script.js +++ b/script.js @@ -449,6 +449,7 @@ function clearAll() { points = [] masterPoints = [] + masterColors = [] document.querySelector('#json').innerHTML = '' document.querySelector('#python').innerHTML = '' } From d6951a0b08ebe9fc390d1029bb0f4372454d52f5 Mon Sep 17 00:00:00 2001 From: Michael Womick Date: Fri, 23 Aug 2024 00:00:10 -0500 Subject: [PATCH 3/8] fix bug where lines are all the same color (although different depending on poly/line switching history) and also change the closing criteria from L1 dist to L2 dist --- script.js | 68 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 23 deletions(-) diff --git a/script.js b/script.js index 6f2e575..6aa66b6 100644 --- a/script.js +++ b/script.js @@ -36,7 +36,6 @@ var showNormalized = false; var modeMessage = document.querySelector('#mode'); var coords = document.querySelector('#coords'); - function clipboard(selector) { var copyText = document.querySelector(selector).innerText; navigator.clipboard.writeText(copyText); @@ -55,6 +54,28 @@ function zoom(clicks) { canvas.style.height = h + 'px'; } +function closePath() { + canvas.style.cursor = 'default'; + masterPoints.push(points); + masterColors.push(rgb_color); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(img, 0, 0); + + drawAllPolygons(); + points = []; + + // dont choose a color that has already been chosen + var remaining_choices = color_choices.filter(function(x) { + return !masterColors.includes(x); + }); + + if (remaining_choices.length == 0) { + remaining_choices = color_choices; + } + + rgb_color = remaining_choices[Math.floor(Math.random() * remaining_choices.length)]; +} + // placeholder image img.src = 'https://assets.website-files.com/5f6bc60e665f54545a1e52a5/63d3f236a6f0dae14cdf0063_drag-image-here.png'; img.onload = function() { @@ -343,37 +364,38 @@ canvas.addEventListener('click', function(e) { y = Math.round(new_y); } - // If starting point os clicked, we complete the polygon - if (drawMode === 'polygon' && points.length > 1) { - const [firstPointX, firstPointY] = points[0] - const overlapDistance = 15 - const isXOverlaped = Math.abs(x - firstPointX) <= overlapDistance - const isYOverlaped = Math.abs(y - firstPointY) <= overlapDistance - if (isXOverlaped && isYOverlaped) { - completeCurrentPolygon() - return + if (points.length > 2 && drawMode == "polygon") { + distX = x - points[0][0]; + distY = y - points[0][1]; + // stroke is 3px and centered on the circle (i.e. 1/2 * 3px) and arc radius is 5px + if(Math.sqrt(distX * distX + distY * distY) <= 6.5) { + closePath(); + return; } } points.push([x, y]); - // if line mode and two points have been drawn, add to masterPoints - if (drawMode == 'line' && points.length == 2) { - masterPoints.push(points); - points = []; + if(drawMode == "line" && points.length == 2) { + closePath(); } - - ctx.beginPath(); - ctx.strokeStyle = rgb_color; - // add rgb_color to masterColors - - if (masterColors.length == 0) { - masterColors.push(rgb_color); + else { + ctx.beginPath(); + ctx.strokeStyle = rgb_color; } + + // ctx.arc(x, y, 155, 0, 2 * Math.PI); + // concat all points into one array + var parentPoints = []; - ctx.arc(x, y, 155, 0, 2 * Math.PI); + for (var i = 0; i < masterPoints.length; i++) { + parentPoints.push(masterPoints[i]); + } + // add "points" + if(points.length > 0) { + parentPoints.push(points); + } - var parentPoints = getParentPoints(); writePoints(parentPoints); }); From 83ed2e335bccfc2266bfed048d41d3c30e84a46b Mon Sep 17 00:00:00 2001 From: Michael Womick Date: Fri, 23 Aug 2024 00:10:47 -0500 Subject: [PATCH 4/8] change ordering of poly/lines so that while drawing, lines appear above the previous drawings instead of behind --- script.js | 75 +++++++++++++++++++++---------------------------------- 1 file changed, 28 insertions(+), 47 deletions(-) diff --git a/script.js b/script.js index 6aa66b6..e08d73b 100644 --- a/script.js +++ b/script.js @@ -103,50 +103,6 @@ function getScaledCoords(e) { return [x / scaleFactor, y / scaleFactor]; } -function drawCurrentPolygon (cursorX, cursorY) { - //ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.drawImage(img, 0, 0); - for (var i = 0; i < points.length - 1; i++) { - // draw arc around each point - ctx.beginPath(); - ctx.strokeStyle = rgb_color; - ctx.arc(points[i][0], points[i][1], 5, 0, 2 * Math.PI); - // fill with white - ctx.fillStyle = 'white'; - ctx.fill(); - ctx.stroke(); - drawLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1]); - } - - if ((points.length > 0 && drawMode == "polygon") || (points.length > 0 && points.length < 2 && drawMode == "line")) { - ctx.beginPath(); - ctx.strokeStyle = rgb_color; - ctx.arc(points[i][0], points[i][1], 5, 0, 2 * Math.PI); - // fill with white - ctx.fillStyle = 'white'; - ctx.fill(); - ctx.stroke(); - - if (cursorX && cursorY) { - drawLine(points[points.length - 1][0], points[points.length - 1][1], cursorX, cursorY); - } - - if (points.length == 2 && drawMode == "line") { - console.log("line"); - // draw arc around each point - ctx.beginPath(); - ctx.strokeStyle = rgb_color; - ctx.arc(points[0][0], points[0][1], 5, 0, 2 * Math.PI); - // fill with white - ctx.fillStyle = 'white'; - ctx.fill(); - ctx.stroke(); - masterPoints.push(points); - points = []; - } - } -} - function drawAllPolygons () { // draw all points for previous regions for (var i = 0; i < masterPoints.length; i++) { @@ -244,8 +200,32 @@ canvas.addEventListener('mousemove', function(e) { ycoord.innerHTML = y; if (canvas.style.cursor == 'crosshair') { - drawCurrentPolygon(x, y) + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(img, 0, 0); + drawAllPolygons(); + + for (var i = 0; i < points.length - 1; i++) { + // draw arc around each point + ctx.beginPath(); + ctx.strokeStyle = rgb_color; + ctx.arc(points[i][0], points[i][1], 5, 0, 2 * Math.PI); + // fill with white + ctx.fillStyle = 'white'; + ctx.fill(); + ctx.stroke(); + drawLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1]); + } + if ((points.length > 0 && drawMode == "polygon") || (points.length > 0 && points.length < 2 && drawMode == "line")) { + ctx.beginPath(); + ctx.strokeStyle = rgb_color; + ctx.arc(points[i][0], points[i][1], 5, 0, 2 * Math.PI); + // fill with white + ctx.fillStyle = 'white'; + ctx.fill(); + ctx.stroke(); + drawLine(points[points.length - 1][0], points[points.length - 1][1], x, y); + } } }); @@ -367,7 +347,7 @@ canvas.addEventListener('click', function(e) { if (points.length > 2 && drawMode == "polygon") { distX = x - points[0][0]; distY = y - points[0][1]; - // stroke is 3px and centered on the circle (i.e. 1/2 * 3px) and arc radius is 5px + // stroke is 3px and centered on the circle (i.e. 1/2 * 3px) and arc radius is if(Math.sqrt(distX * distX + distY * distY) <= 6.5) { closePath(); return; @@ -430,8 +410,9 @@ document.addEventListener('keydown', function(e) { }) function draw () { - drawCurrentPolygon() + drawAllPolygons() + drawCurrentPolygon() var parentPoints = getParentPoints() writePoints(parentPoints) } From 36824b9aa74b9b96734830d0112faf7c184cf6ed Mon Sep 17 00:00:00 2001 From: Michael Womick Date: Fri, 23 Aug 2024 00:23:30 -0500 Subject: [PATCH 5/8] add a node--i.e., ctx.arc(...)-- immediately upon click--previously only added node on mousemove --- script.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/script.js b/script.js index e08d73b..e80a265 100644 --- a/script.js +++ b/script.js @@ -356,6 +356,14 @@ canvas.addEventListener('click', function(e) { points.push([x, y]); + ctx.beginPath(); + ctx.strokeStyle = rgb_color; + ctx.arc(x, y, 5, 0, 2 * Math.PI); + // fill with white + ctx.fillStyle = 'white'; + ctx.fill(); + ctx.stroke(); + if(drawMode == "line" && points.length == 2) { closePath(); } From a4dfa23d22e58163ad0cb25db331581316dea653 Mon Sep 17 00:00:00 2001 From: Michael Womick Date: Fri, 23 Aug 2024 00:27:50 -0500 Subject: [PATCH 6/8] draw lines *before* the nodes so that nodes are not painted over --- script.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script.js b/script.js index e80a265..b7c4a41 100644 --- a/script.js +++ b/script.js @@ -206,6 +206,7 @@ canvas.addEventListener('mousemove', function(e) { drawAllPolygons(); for (var i = 0; i < points.length - 1; i++) { + drawLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1]); // draw arc around each point ctx.beginPath(); ctx.strokeStyle = rgb_color; @@ -214,9 +215,9 @@ canvas.addEventListener('mousemove', function(e) { ctx.fillStyle = 'white'; ctx.fill(); ctx.stroke(); - drawLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1]); } if ((points.length > 0 && drawMode == "polygon") || (points.length > 0 && points.length < 2 && drawMode == "line")) { + drawLine(points[points.length - 1][0], points[points.length - 1][1], x, y); ctx.beginPath(); ctx.strokeStyle = rgb_color; ctx.arc(points[i][0], points[i][1], 5, 0, 2 * Math.PI); @@ -224,7 +225,6 @@ canvas.addEventListener('mousemove', function(e) { ctx.fillStyle = 'white'; ctx.fill(); ctx.stroke(); - drawLine(points[points.length - 1][0], points[points.length - 1][1], x, y); } } }); From 8bcf4fe123c8637e9448bb5c1b2489c4f5b0121f Mon Sep 17 00:00:00 2001 From: Michael Womick Date: Fri, 23 Aug 2024 00:41:15 -0500 Subject: [PATCH 7/8] fill before stroking --- script.js | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/script.js b/script.js index b7c4a41..e314e34 100644 --- a/script.js +++ b/script.js @@ -93,7 +93,7 @@ function drawLine(x1, y1, x2, y2) { ctx.lineWidth = 5; ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); - ctx.stroke(); + // ctx.stroke(); } function getScaledCoords(e) { @@ -112,6 +112,17 @@ function drawAllPolygons () { for (var j = 1; j < newpoints.length; j++) { // draw all lines drawLine(newpoints[j - 1][0], newpoints[j - 1][1], newpoints[j][0], newpoints[j][1]); + + // fill + ctx.beginPath(); + ctx.fillStyle = opaque_color; + ctx.moveTo(newpoints[0][0], newpoints[0][1]); + for (var j = 1; j < newpoints.length; j++) { + ctx.lineTo(newpoints[j][0], newpoints[j][1]); + } + ctx.closePath(); + ctx.fill(); + ctx.stroke(); } drawLine(newpoints[newpoints.length - 1][0], newpoints[newpoints.length - 1][1], newpoints[0][0], newpoints[0][1]); // draw arc around each point @@ -124,15 +135,7 @@ function drawAllPolygons () { ctx.fill(); ctx.stroke(); } - // fill - ctx.beginPath(); - ctx.fillStyle = opaque_color; - ctx.moveTo(newpoints[0][0], newpoints[0][1]); - for (var j = 1; j < newpoints.length; j++) { - ctx.lineTo(newpoints[j][0], newpoints[j][1]); - } - ctx.closePath(); - ctx.fill(); + } } @@ -206,20 +209,24 @@ canvas.addEventListener('mousemove', function(e) { drawAllPolygons(); for (var i = 0; i < points.length - 1; i++) { + ctx.strokeStyle = rgb_color; drawLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1]); + ctx.stroke(); // draw arc around each point ctx.beginPath(); - ctx.strokeStyle = rgb_color; ctx.arc(points[i][0], points[i][1], 5, 0, 2 * Math.PI); // fill with white ctx.fillStyle = 'white'; ctx.fill(); ctx.stroke(); } + + if ((points.length > 0 && drawMode == "polygon") || (points.length > 0 && points.length < 2 && drawMode == "line")) { + ctx.strokeStyle = rgb_color; drawLine(points[points.length - 1][0], points[points.length - 1][1], x, y); + ctx.stroke(); // new ctx.beginPath(); - ctx.strokeStyle = rgb_color; ctx.arc(points[i][0], points[i][1], 5, 0, 2 * Math.PI); // fill with white ctx.fillStyle = 'white'; From dcfd15ab7ed758394adb22306d139384d689af7e Mon Sep 17 00:00:00 2001 From: Michael Womick Date: Fri, 23 Aug 2024 00:46:18 -0500 Subject: [PATCH 8/8] bevel instead of miter to reduce sharp joints for small angles --- script.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/script.js b/script.js index e314e34..135bb23 100644 --- a/script.js +++ b/script.js @@ -16,6 +16,8 @@ var radiansPer45Degrees = Math.PI / 4; var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); +ctx.lineJoin = 'bevel'; + var img = new Image(); var rgb_color = color_choices[Math.floor(Math.random() * color_choices.length)] var opaque_color = 'rgba(0,0,0,0.5)';