diff --git a/404.html b/404.html new file mode 100644 index 0000000..7f9f4a7 --- /dev/null +++ b/404.html @@ -0,0 +1,14 @@ + + + + + PolygonZone has moved to Roboflow Workflows + + + + + + +

PolygonZone is now part of Roboflow Workflows. If you are not redirected, go to Draw Polygon Zones and Lines.

+ + diff --git a/README.md b/README.md index 17f3866..8296387 100644 --- a/README.md +++ b/README.md @@ -1,88 +1,5 @@ -

- - - -

-
-
- - - - - - - - - - - - - - - - - - - - - - - -
-
+# PolygonZone -## About PolygonZone 📐 +PolygonZone now lives in [Roboflow Workflows](https://roboflow.com/workflows). Draw polygon zones and lines and copy their coordinates in the Workflows editor: see [Draw Polygon Zones and Lines](https://docs.roboflow.com/workflows/build/draw-polygon-zones). -PolygonZone lets you draw arbitrary polygons on an image and retrieve the coordinates of the points you have drawn. - -This application makes it easy to retrieve coordinates for Regions of Interest in computer vision applications. - -PolygonZone accompanies [Roboflow Supervision](https://github.com/roboflow/supervision), a Python library with a range of utilities that are useful in computer vision projects. - -Please note that PolygonZone is not an annotation tool. It is a tool for retrieving coordinates of polygons that you have drawn on an image. - -This application is designed for desktop use. - -## Demo - -https://user-images.githubusercontent.com/37276661/218796838-3a66a61c-ac9d-40f5-97ff-0030a8bdc60d.mov - -## Getting Started 🚀 - -To use PolygonZone, open up the [PolygonZone web application](https://roboflow.github.io/polygonzone/). Then: - -1. Upload an image onto which you want to draw a polygon. -2. Click on the points where you want to draw the polygon. -3. Click on the intial point or press "Enter" to save a polygon. -4. Continue to draw as many polygons as you need. -5. Copy the NumPy array or JSON object that contains the coordinates of the polygons you have drawn. - -## Functionalities -- You can zoom in and out of an image using the mouse wheel or a laptop track pad. -- You can undo the last point pressing Ctrl/Cmd+Z -- You can discard the last unsaved polygon pressing Esc - -## Contributing 🤝 - -We welcome contributions to the PolygonZone project. Please see the [Contributing Guidelines](CONTRIBUTING.md) for more information on how you can help to improve this project. +polygonzone.roboflow.com redirects to that page. The code for the old standalone web tool is in this repository's git history. diff --git a/index.html b/index.html index 4e0655e..7f9f4a7 100644 --- a/index.html +++ b/index.html @@ -1,149 +1,14 @@ - - - - - - - PolygonZone by Roboflow - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-

PolygonZone

-

PolygonZone lets you calculate polygon points in an image.

-

How to use

-
    -
  1. Drop an image to the indicated area
  2. -
  3. Select the desired mode: Press L to draw a line, or P to draw a polygon
  4. -
  5. Click to draw polygon points. Press enter to finish the polygon.
  6. -
-

Coordinates

-

Copy the points below, formatted as NumPy arrays, into your Python code.

- Copy Python to Clipboard - - -
-                            
-                            
-                        
-
- View JSON Points -

JSON Points

- Copy JSON to Clipboard -
-                                
-                                
-                            
-
-
-
-
-
-
-
-
- - - Polygon Mode - (P) - -
-
- - - Line Mode - (L) - -
-
- - - Edit Mode - (E) - -
-
-
- - - Undo - (Ctrl/⌘-Z) - -
-
- - - Discard current - (Esc) - -
-
- - - Clear all polygons - (Ctrl/⌘-E) - -
-
-
- - - Save image - (Ctrl/⌘-S) - -
-
- - - Fullscreen - (F) - -
-
-
- Coordinates - - x: --- | - y: --- - -
-
-
-
- -
-
-
-
- - + + + + PolygonZone has moved to Roboflow Workflows + + + + + + +

PolygonZone is now part of Roboflow Workflows. If you are not redirected, go to Draw Polygon Zones and Lines.

+ diff --git a/script.js b/script.js deleted file mode 100644 index 5e068b3..0000000 --- a/script.js +++ /dev/null @@ -1,715 +0,0 @@ -var color_choices = [ - "#FF00FF", - "#8622FF", - "#FE0056", - "#00FFCE", - "#FF8000", - "#00B7EB", - "#FFFF00", - "#0E7AFE", - "#FFABAB", - "#0000FF", - "#CCCCCC", -]; -// if you want choices to be kind of random, shuffle the array once here - -var radiansPer45Degrees = Math.PI / 4; - -var imageContainer = document.querySelector('.image-container'); -var canvas = document.getElementById('canvas'); -var mainCtx = canvas.getContext('2d'); -var offScreenCanvas = document.createElement('canvas'); -var offScreenCtx = offScreenCanvas.getContext('2d'); -offScreenCanvas.width = canvas.width; -offScreenCanvas.height = canvas.height; - -var img = new Image(); -var rgb_color = color_choices[0]; -var fill_color = 'rgba(0,0,0,0.35)'; - -var scaleFactor = 1; -var scaleSpeed = 0.01; - -var points = []; -var masterPoints = []; -var masterColors = []; - -var drawMode; -setDrawMode('polygon'); -var constrainAngles = false; -var showNormalized = false; - -function resetState() { - points = []; - masterPoints = []; - masterColors = []; - rgb_color = color_choices[0]; - document.querySelector('#json').innerHTML = ''; - document.querySelector('#python').innerHTML = ''; -} - - -var isFullscreen = false; -var taskbarAndCanvas = document.querySelector('.right'); - -var editMode = false; -var selectedPointIndex = -1; -var selectedPolygonIndex = -1; - - -function blitCachedCanvas() { - mainCtx.clearRect(0, 0, canvas.width, canvas.height); - mainCtx.drawImage(offScreenCanvas, 0, 0); -} - -function clipboard(selector) { - var copyText = document.querySelector(selector).innerText; - // strip whitespace on left and right - copyText = copyText.replace(/^\s+|\s+$/g, ''); - navigator.clipboard.writeText(copyText); -} - -function clearDrawings() { - offScreenCtx.clearRect(0, 0, offScreenCanvas.width, offScreenCanvas.height); - blitCachedCanvas(); -} - -function isClockwise(vertices) { - let sum = 0; - for (let i = 0; i < vertices.length; i++) { - const [x1, y1] = vertices[i]; - const [x2, y2] = vertices[(i + 1) % vertices.length]; - sum += (x2 - x1) * (y2 + y1); - } - return sum > 0; -} - -function zoom(clicks) { - var newScaleFactor = scaleFactor + clicks * scaleSpeed; - newScaleFactor = Math.max(0.1, Math.min(newScaleFactor, 1)); - - const maxWidth = imageContainer.offsetWidth * 0.95; - const maxHeight = imageContainer.offsetHeight * 0.95; - - let newWidth = img.width * newScaleFactor; - let newHeight = img.height * newScaleFactor; - - if (newWidth > maxWidth) { - newHeight = (maxWidth / newWidth) * newHeight; - newWidth = maxWidth; - newScaleFactor = newWidth / img.width; - } - - if (newHeight > maxHeight) { - newWidth = (maxHeight / newHeight) * newWidth; - newHeight = maxHeight; - newScaleFactor = newHeight / img.height; - } - - scaleFactor = newScaleFactor; - canvas.style.width = newWidth + 'px'; - canvas.style.height = newHeight + 'px'; -} - -function onPathClose() { - canvas.style.cursor = 'default'; - // we do this to avoid clearing overlapping polygons - if (isClockwise(points)) { - points = points.reverse(); - } - masterPoints.push(points); - points = []; - masterColors.push(rgb_color); - drawAllPolygons(offScreenCtx); - blitCachedCanvas(); - rgb_color = color_choices[(masterColors.length) % (color_choices.length)]; -} - -// placeholder image -img.src = 'https://assets.website-files.com/5f6bc60e665f54545a1e52a5/63d3f236a6f0dae14cdf0063_drag-image-here.png'; -img.onload = function() { - scaleFactor = 0.69; - canvas.style.width = img.width * scaleFactor + 'px'; - canvas.style.height = img.height * scaleFactor + 'px'; - canvas.width = img.width; - canvas.height = img.height; - offScreenCanvas.width = img.width; - offScreenCanvas.height = img.height; - offScreenCtx.drawImage(img, 0, 0); - blitCachedCanvas(); -}; - -function makeLine(ctx, x1, y1, x2, y2) { - ctx.moveTo(x1, y1); - ctx.lineTo(x2, y2); -} - -function drawNode(ctx, x, y, stroke = null) { - if (stroke) { - ctx.strokeStyle = stroke; - } - ctx.beginPath(); - ctx.arc(x, y, 5, 0, 2 * Math.PI); - ctx.closePath(); - ctx.fillStyle = 'white'; - ctx.fill(); - ctx.stroke(); -} - -function getScaledCoords(e) { - var rect = canvas.getBoundingClientRect(); - var x = e.clientX - rect.left; - var y = e.clientY - rect.top; - return [x / scaleFactor, y / scaleFactor]; -} - -function drawAllPolygons(ctx) { - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.drawImage(img, 0, 0); - // draw polygons as subpaths and fill all at once - // we do this to avoid overlapping polygons becoming opaque - ctx.beginPath(); - ctx.fillStyle = fill_color; - for (var i = 0; i < masterPoints.length; i++) { - var newpoints = masterPoints[i]; - for (var j = 1; j < newpoints.length; j++) { - makeLine(ctx, newpoints[j - 1][0], newpoints[j - 1][1], newpoints[j][0], newpoints[j][1]); - ctx.moveTo(newpoints[0][0], newpoints[0][1]); - for (var j = 1; j < newpoints.length; j++) { - ctx.lineTo(newpoints[j][0], newpoints[j][1]); - } - makeLine(ctx, newpoints[newpoints.length - 1][0], newpoints[newpoints.length - 1][1], newpoints[0][0], newpoints[0][1]); - } - } - ctx.fill(); - - ctx.lineWidth = 5; - ctx.lineJoin = 'bevel'; - for (var i = 0; i < masterPoints.length; i++) { - var newpoints = masterPoints[i]; - ctx.strokeStyle = masterColors[i]; - - ctx.beginPath(); - for (var j = 1; j < newpoints.length; j++) { - makeLine(ctx, newpoints[j - 1][0], newpoints[j - 1][1], newpoints[j][0], newpoints[j][1]); - 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.stroke(); - - // draw arc around each point - for (var j = 0; j < newpoints.length; j++) { - drawNode(ctx, newpoints[j][0], newpoints[j][1]); - } - } -} - -function getParentPoints() { - var parentPoints = []; - for (var i = 0; i < masterPoints.length; i++) { - parentPoints.push(masterPoints[i]); - } - parentPoints.push(points); - return parentPoints; -} - -function findClosestPoint(x, y) { - let minDist = Infinity; - let closestPoint = null; - let polygonIndex = -1; - let pointIndex = -1; - - for (let i = 0; i < masterPoints.length; i++) { - for (let j = 0; j < masterPoints[i].length; j++) { - const [px, py] = masterPoints[i][j]; - const dist = Math.sqrt((x - px) ** 2 + (y - py) ** 2); - if (dist < minDist && dist < 10 / scaleFactor) { // grab radius - minDist = dist; - closestPoint = [px, py]; - polygonIndex = i; - pointIndex = j; - } - } - } - - return { point: closestPoint, polygonIndex, pointIndex }; -} - -window.addEventListener('keyup', function(e) { - if (e.key === 'Shift') { - constrainAngles = false; - } -}); - -document.querySelector('#copyPythonButton').addEventListener('click', function(e) { - e.preventDefault(); - clipboard("#python"); -}); - -document.querySelector('#copyJSONButton').addEventListener('click', function(e) { - e.preventDefault(); - clipboard("#json"); -}); - -canvas.addEventListener('dragover', function(e) { - e.preventDefault(); -}); - -canvas.addEventListener('wheel', function(e) { - e.preventDefault() - var delta = Math.sign(e.deltaY); - zoom(delta); -}); - -canvas.addEventListener('mouseleave', function(e) { - var xcoord = document.querySelector('#x'); - var ycoord = document.querySelector('#y'); - xcoord.innerHTML = ''; - ycoord.innerHTML = ''; -}); - -canvas.addEventListener('mousemove', function(e) { - var [x, y] = getScaledCoords(e); - x = Math.round(x); - y = Math.round(y); - - // update x y coords - var xcoord = document.querySelector('#x'); - var ycoord = document.querySelector('#y'); - - if(constrainAngles && points.length > 0) { - 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); - } - - // sometimes, a mousemove event is leaving the canvas and has coordinates outside the canvas - // however, due to the cursors being used, we do not need to check if it is larger than canvas.width or canvas.height - if (x < 0 || y < 0 ){ - xcoord.innerHTML = ''; - ycoord.innerHTML = ''; - return; - } - xcoord.innerHTML = x; - ycoord.innerHTML = y; - - var ctx = mainCtx; - ctx.lineWidth = 5; - ctx.lineJoin = 'bevel'; - ctx.fillStyle = 'white'; - - // if cursor is crosshair, draw line from last point to cursor - if (canvas.style.cursor == 'crosshair') { - blitCachedCanvas(); - - for (var i = 0; i < points.length - 1; i++) { - ctx.strokeStyle = rgb_color; - ctx.beginPath(); - ctx.lineJoin = 'bevel'; - makeLine(ctx, points[i][0], points[i][1], points[i + 1][0], points[i + 1][1]); - ctx.closePath(); - ctx.stroke(); - - drawNode(ctx, points[i][0], points[i][1]); - } - - - if ((points.length > 0 && drawMode == "polygon") || (points.length > 0 && points.length < 2 && drawMode == "line")) { - ctx.beginPath(); - ctx.lineJoin = 'bevel'; - ctx.strokeStyle = rgb_color; - makeLine(ctx, points[points.length - 1][0], points[points.length - 1][1], x, y); - ctx.closePath(); - ctx.stroke(); - - drawNode(ctx, points[i][0], points[i][1]); - } - } - - if (editMode && selectedPointIndex !== -1) { - masterPoints[selectedPolygonIndex][selectedPointIndex] = [x, y]; - drawAllPolygons(offScreenCtx); - blitCachedCanvas(); - writePoints(getParentPoints()); - } -}); - -canvas.addEventListener('drop', function(e) { - e.preventDefault(); - var file = e.dataTransfer.files[0]; - - // only allow image files - var supportedImageTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/webp']; - if (!supportedImageTypes.includes(file.type)) { - alert('Only PNG, JPEG, JPG, and WebP files are allowed.'); - return; - } - - var reader = new FileReader(); - reader.onload = function(event) { - img.src = event.target.result; - }; - reader.readAsDataURL(file); - - img.onload = function() { - // reset state to initial values - resetState(); - - // draw loaded image on canvas - scaleFactor = 0.25; - canvas.style.width = img.width * scaleFactor + 'px'; - canvas.style.height = img.height * scaleFactor + 'px'; - canvas.width = img.width; - canvas.height = img.height; - offScreenCanvas.width = img.width; - offScreenCanvas.height = img.height; - - const maxWidth = imageContainer.offsetWidth * 0.95; - const maxHeight = imageContainer.offsetHeight * 0.95; - - let newWidth = img.width; - let newHeight = img.height; - - if (newWidth > maxWidth) { - newHeight = (maxWidth / newWidth) * newHeight; - newWidth = maxWidth; - } - - if (newHeight > maxHeight) { - newWidth = (maxHeight / newHeight) * newWidth; - newHeight = maxHeight; - } - - scaleFactor = newWidth / img.width; - - canvas.style.width = newWidth + 'px'; - canvas.style.height = newHeight + 'px'; - canvas.style.borderRadius = '10px'; - offScreenCtx.drawImage(img, 0, 0); - blitCachedCanvas(); - }; -}); - -function writePoints(parentPoints) { - var normalized = []; - - // if normalized is true, normalize all points - var imgHeight = img.height; - var imgWidth = img.width; - if (showNormalized) { - for (var i = 0; i < parentPoints.length; i++) { - var normalizedPoints = []; - for (var j = 0; j < parentPoints[i].length; j++) { - normalizedPoints.push([ - Math.round(parentPoints[i][j][0] / imgWidth * 100) / 100, - Math.round(parentPoints[i][j][1] / imgHeight * 100) / 100 - ]); - } - normalized.push(normalizedPoints); - } - parentPoints = normalized; - } - - // clean empty points - parentPoints = parentPoints.filter(points => !!points.length); - - if (!parentPoints.length) { - document.querySelector('#python').innerHTML = ''; - document.querySelector('#json').innerHTML; - return; - } - - // create np.array list - var code_template = `[\n${parentPoints.map(function(points) { - return ` np.array([${points.map(function(point) { - return `[${point[0]}, ${point[1]}]`;}).join(', ')}])`; - }).join(',\n')}\n]`; - - document.querySelector('#python').innerHTML = code_template; - - var json_template = `{\n${parentPoints.map(function(points) { - return ` [${points.map(function(point) { - return `{"x": ${point[0]}, "y": ${point[1]}}`;}).join(', ')}]`; - }).join(',\n')}\n}`; - - document.querySelector('#json').innerHTML = json_template; -} - -canvas.addEventListener('mousedown', function(e) { - var [x, y] = getScaledCoords(e); - x = Math.round(x); - y = Math.round(y); - - if (editMode) { - const { point, polygonIndex, pointIndex } = findClosestPoint(x, y); - if (point) { - selectedPointIndex = pointIndex; - selectedPolygonIndex = polygonIndex; - canvas.style.cursor = 'grabbing'; - } - } else { - // click handling for drawing mode - canvas.style.cursor = 'crosshair'; - - if(constrainAngles && points.length > 0) { - 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 (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 - if(Math.sqrt(distX * distX + distY * distY) <= 6.5) { - onPathClose(); - return; - } - } - - points.push([x, y]); - - drawNode(mainCtx, x, y, rgb_color); - - if(drawMode == "line" && points.length == 2) { - onPathClose(); - } - - // concat all points into one array - var parentPoints = []; - - for (var i = 0; i < masterPoints.length; i++) { - parentPoints.push(masterPoints[i]); - } - // add "points" - if(points.length > 0) { - parentPoints.push(points); - } - - writePoints(parentPoints); - } -}); - -canvas.addEventListener('mouseup', function(e) { - if (editMode) { - selectedPointIndex = -1; - selectedPolygonIndex = -1; - canvas.style.cursor = 'move'; - } -}); - -document.querySelector('#normalize-checkbox').addEventListener('change', function(e) { - showNormalized = e.target.checked; - var parentPoints = getParentPoints(); - writePoints(parentPoints); -}); - -function setDrawMode(mode) { - drawMode = mode; - setEditMode(false); - canvas.style.cursor = 'crosshair'; - document.querySelectorAll('.t-mode').forEach(el => el.classList.remove('active')); - document.querySelector(`#mode-${mode}`).classList.add('active'); -} - -function setEditMode(editEnabled) { - editMode = editEnabled; - canvas.style.cursor = editEnabled ? 'move' : 'crosshair'; - document.querySelectorAll('.t-mode').forEach(el => el.classList.remove('active')); - document.querySelector(`#mode-${editEnabled ? 'edit' : drawMode}`).classList.add('active'); -} - -document.querySelector('#mode-polygon').addEventListener('click', function(e) { - setDrawMode('polygon'); -}) - -document.querySelector('#mode-line').addEventListener('click', function(e) { - setDrawMode('line'); -}) - -document.querySelector('#mode-edit').addEventListener('click', function(e) { - setEditMode(true); -}); - -document.addEventListener('keydown', function(e) { - if (e.key == 'l' || e.key == 'L') { - setDrawMode('line'); - } - if (e.key == 'p' || e.key == 'P') { - setDrawMode('polygon'); - } - if (e.key == 'e' || e.key == 'E') { - setEditMode(true); - } -}); - -function rewritePoints() { - var parentPoints = getParentPoints(); - writePoints(parentPoints); -} - -function highlightButtonInteraction (buttonId) { - document.querySelector(buttonId).classList.add('active'); - setTimeout(() => document.querySelector(buttonId).classList.remove('active'), 100); -} - -function undo() { - highlightButtonInteraction('#undo'); - - if (points.length > 0) { - points.pop(); - blitCachedCanvas(); - rewritePoints(); - - if(points.length === 0){ - return; - } - - var ctx = mainCtx; - ctx.strokeStyle = rgb_color; - ctx.fillStyle = 'white'; - if (points.length === 1) { - drawNode(ctx, points[0][0], points[0][1]); - } - else { - drawNode(ctx, points[0][0], points[0][1]); - for (var i = 0; i < points.length - 1; i++) { - makeLine(ctx, points[i][0], points[i][1], points[i + 1][0], points[i + 1][1]); - ctx.stroke(); - drawNode(ctx, points[i + 1][0], points[i + 1][1]); - } - } - } -} - -document.querySelector('#undo').addEventListener('click', function(e) { - undo(); -}) - -function discardCurrentPolygon () { - highlightButtonInteraction('#discard-current'); - points = []; - blitCachedCanvas(); - rewritePoints(); -} - -document.querySelector('#discard-current').addEventListener('click', function(e) { - discardCurrentPolygon(); -}) - -function clearAll() { - highlightButtonInteraction('#clear') - resetState(); - // reset main and offscreen canvases - mainCtx.clearRect(0, 0, canvas.width, canvas.height); - offScreenCtx.clearRect(0, 0, offScreenCanvas.width, offScreenCanvas.height); - mainCtx.drawImage(img, 0, 0); - offScreenCtx.drawImage(img, 0, 0); - points = []; - masterPoints = []; - masterColors = []; - rgb_color = color_choices[0]; - document.querySelector('#jsonCode').innerHTML = ''; - document.querySelector('#pythonCode').innerHTML = ''; -} - -document.querySelector('#clear').addEventListener('click', function(e) { - clearAll(); -}) - -function saveImage () { - highlightButtonInteraction('#save-image'); - - var link = document.createElement('a'); - link.download = 'image.png'; - link.href = canvas.toDataURL(); - link.click(); -} - -document.querySelector('#save-image').addEventListener('click', function(e) { - saveImage(); -}) - -function toggleFullscreen() { - highlightButtonInteraction('#fullscreen'); - - if (!isFullscreen) { - if (taskbarAndCanvas.requestFullscreen) { - taskbarAndCanvas.requestFullscreen(); - } else if (taskbarAndCanvas.webkitRequestFullscreen) { // Safari - taskbarAndCanvas.webkitRequestFullscreen(); - } else if (taskbarAndCanvas.msRequestFullscreen) { // IE/Edge - taskbarAndCanvas.msRequestFullscreen(); - } - } else { - if (document.exitFullscreen) { - document.exitFullscreen(); - } else if (document.webkitExitFullscreen) { // Safari - document.webkitExitFullscreen(); - } else if (document.msExitFullscreen) { // IE/Edge - document.msExitFullscreen(); - } - } -} - -document.addEventListener('fullscreenchange', function() { - isFullscreen = document.fullscreenElement !== null; -}); - -document.querySelector('#fullscreen').addEventListener('click', function(e) { - toggleFullscreen(); -}); - -window.addEventListener('keydown', function(e) { - if (e.key === 'z' && (e.ctrlKey || e.metaKey)) { - e.preventDefault(); - e.stopImmediatePropagation(); - undo(); - } - - if (e.key === 'Shift') { - constrainAngles = true; - } - - if (e.key === 'Escape') { - discardCurrentPolygon(); - } - - if (e.key === 'e' && (e.ctrlKey || e.metaKey)) { - clearAll(); - } - - if (e.key === 's' && (e.ctrlKey || e.metaKey)) { - e.preventDefault(); - e.stopImmediatePropagation(); - - saveImage(); - } - - if (e.key === 'Enter') { - if(points.length > 2) { - onPathClose(); - } - } - - if (e.key === 'f' || e.key === 'F') { - toggleFullscreen(); - } -}) \ No newline at end of file diff --git a/styles.css b/styles.css deleted file mode 100644 index 2df8a20..0000000 --- a/styles.css +++ /dev/null @@ -1,312 +0,0 @@ -canvas { - display: block; - margin: 0 auto; -} -* { - font-family: Tahoma, Arial, Helvetica, sans-serif; -} -pre { - max-height: 200px; - overflow: auto; - height: 200px; - background-color:rgba(17,24,39,0.1); - padding: 10px; - max-width: 100%; - overflow-y: scroll; - text-wrap: wrap; - text-align: left; -} -body { - background: white; - height: 100vh; - margin: 0; - padding: 0; -} -main { - display: flex; - flex-direction: column; - height: 100vh; - margin: 0; - padding: 0; -} -h1 { - font-size: 28px; -} -h1, h2 { - color: rgb(103, 6, 206); -} -details { - width: 100%; - margin: 0; - text-align: left; -} -nav { - border-bottom: 1px solid lightgrey; - display: flex; - justify-content: space-between; -} -nav ul { - list-style-type: none; - padding: 0; - padding-left: 1em; -} -nav ul li { - display: inline; - margin-right: 10px; -} -summary { - border: 2px solid #00ffce; - color: #000; - padding: 10px; - margin-bottom: 10px; - border-radius: 10px; -} -a { - color: #5400ec; - text-decoration: none; -} -.content { - display: flex; - flex-grow: 1; -} -.left { - flex: 40%; - padding: 0 1em; - border-right: 1px solid #e5e7eb; - box-sizing: border-box; -} -.right { - flex: 60%; - padding: 0 2em; - box-sizing: border-box; - display: flex; - flex-direction: column; -} -.image-container { - flex-grow: 1; -} -@media screen and (max-width: 900px) { - .content { - display: block; - } - .left, .right { - padding: 0; - } - .left { - border-right: 0; - } - - canvas { - margin-bottom: 50px; - } - .right { - width: 45em; - margin: auto; - } -} -.controls { - text-align: left; -} -.space { - padding-left: 50px; -} -.widgetButton { - display: inline-block; - margin-right: 8px; - margin-left: 8px; - padding: 12px 24px; - -webkit-align-self: center; - -ms-flex-item-align: center; - -ms-grid-row-align: center; - align-self: center; - border-radius: 4px; - background-color: #7733f4; - box-shadow: 0 3px 3px 0 rgb(55 65 81 / 20%); - font-family: proxima-nova,sans-serif; - color: #fff; - font-weight: 700; - text-align: center; - letter-spacing: .5px; -} -.banner { - max-width: 100%; - margin-bottom: 20px; - max-height: 150px; -} -.grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); - grid-gap: 20px; - margin-top: 30px; - max-width: 40em; - margin: auto; - margin-bottom: 30px; -} -.card { - background: #fff; - border-radius: 5px; - box-shadow: 0 2px 3px 1px rgb(0 0 0 / 10%); - overflow: hidden; - text-align: left; - padding: 10px; -} -.card img { - width: 100%; -} -.card h4 { - margin-bottom: 20px; - color: #1f2937; - font-size: 24px; - line-height: 1.2; - font-weight: 600; -} -.card a { - color: inherit; - text-decoration: none; -} -.card p { - margin-bottom: 20px; - padding-top: 10px; - border-top: 1px solid #e5e7eb; - color: #666; - line-height: 1.4; - font-weight: 400; -} - -footer { - margin-top: 50px; -} - -.section { - margin-bottom: 50px; -} - -li { - margin-bottom: 4px; -} - -.show-normalized { - background-color: #5400ec; - opacity: 0.8; - color: white; - font-weight: 600; - display: inline-block; - padding: 15px; - border-radius: 10px; -} - -/* Taskbar */ -.taskbar-container { - display: flex; - width: 100%; - align-items: center; - flex-direction: column; - margin-bottom: 1em; - margin-top: 1em; -} - -.taskbar { - display: flex; - background-color: #7733f4; - color: #f1edf8; - - border-radius: 5px; - padding: 6px; - - justify-content:space-around; - box-shadow: 0 2px 3px 1px rgb(0 0 0 / 20%); -} - -.taskbar .t-icon { - display: flex; - flex-direction: column; - padding: 8px 24px; - border-radius: 5px; - transition: all .15s ease-in-out; -} - -.taskbar .t-icon:hover { - cursor: pointer; - background-color: #8f54fd; -} - -.taskbar .t-icon.active { - background-color: #f4efff; - color: #7733f4; -} - -.taskbar .t-icon .ti-caption { - font-size: 11px; - margin-top: 8px; - display: flex; - height: 100%; - flex-direction: column; - justify-content: center; -} - -.taskbar .t-divider { - background-color: #8145f1; - width: 1px; -} - -/* Coordinates */ -.coordinates { - display: flex; - flex-direction: column; - text-align: left; - - width: 100%; - box-sizing: border-box; - text-align: center; - padding: 8px 16px; - border-radius: 5px; - margin-top: 8px; - - font-size: 1em; - box-shadow: 0 2px 3px 1px rgb(0 0 0 / 20%); - background-color: #fbf9ff; -} - -.coordinates .cc-title { - margin-bottom: 4px; - color: #5400ec; -} - -.coordinates .cc-coord { - margin-bottom: 2px; - color: #989898; -} - -.coordinates .cc-value { - color: #000; -} - - -/* Helpers */ -.ta-left { - text-align: left; -} - -.mb-1 { - margin-bottom: 8px; -} - -.mb-2 { - margin-bottom: 16px; -} - -.mb-3 { - margin-bottom: 24px; -} - -.mt-1 { - margin-top: 8px; -} - -.mt-2 { - margin-top: 16px; -} - -.mt-3 { - margin-top: 24px; -} \ No newline at end of file