feat(ui): implement polygon tool

This commit is contained in:
max_richter 2021-03-16 13:31:55 +01:00
parent 0e461edd38
commit b96f820971
9 changed files with 1549 additions and 4993 deletions

3597
view/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -35,10 +35,12 @@
}, },
"dependencies": { "dependencies": {
"@tensorflow-models/deeplab": "^0.2.1", "@tensorflow-models/deeplab": "^0.2.1",
"@tensorflow/tfjs-backend-cpu": "^3.3.0",
"@tensorflow/tfjs-backend-webgl": "^3.3.0", "@tensorflow/tfjs-backend-webgl": "^3.3.0",
"@tensorflow/tfjs-converter": "^3.3.0", "@tensorflow/tfjs-converter": "^3.3.0",
"@tensorflow/tfjs-core": "^3.3.0", "@tensorflow/tfjs-core": "^3.3.0",
"file-selector": "^0.2.4", "file-selector": "^0.2.4",
"lodash": "^4.17.21",
"ogl": "^0.0.65" "ogl": "^0.0.65"
} }
} }

1411
view/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -6,8 +6,18 @@
export let image: Image; export let image: Image;
let activeTool = "brush"; let activeTool = "brush";
const _activeTool = localStorage.getItem("activeTool");
if (_activeTool) {
activeTool = _activeTool;
}
let brushRadius = 20; let brushRadius = 20;
let activeColor = "ff0000"; let activeColor = "ff0000";
let _activeColor = localStorage.getItem("activeColor");
if (_activeColor) {
activeColor = _activeColor;
}
let layerOpacity = 50; let layerOpacity = 50;
</script> </script>
@ -26,7 +36,12 @@
<ToolBox bind:activeTool /> <ToolBox bind:activeTool />
</div> </div>
<div class="top"> <div class="top">
<TopBar bind:layerOpacity bind:brushRadius bind:activeColor /> <TopBar
bind:layerOpacity
bind:brushRadius
bind:activeColor
bind:activeTool
/>
</div> </div>
<div class="back"> <div class="back">
<button on:click={() => ($currentRoute = "list")}>exit</button> <button on:click={() => ($currentRoute = "list")}>exit</button>

View File

@ -4,6 +4,7 @@
import OrbView from "./OrbView"; import OrbView from "./OrbView";
import { onMount } from "svelte"; import { onMount } from "svelte";
import Toast from "components/Toast"; import Toast from "components/Toast";
import { throttle } from "lodash";
export let image: Image; export let image: Image;
export let activeTool = "pan"; export let activeTool = "pan";
@ -21,16 +22,27 @@
let cx2: CanvasRenderingContext2D; let cx2: CanvasRenderingContext2D;
let orb: ReturnType<typeof OrbView>; let orb: ReturnType<typeof OrbView>;
$: if (activeTool) { let _lastActiveTool;
function handleToolChange(t) {
if (_lastActiveTool === t) return;
if (orb) { if (orb) {
orb.setTool(activeTool); orb.setTool(t);
} }
if (activeTool === "clear") { if (t === "clear") {
cx1.clearRect(0, 0, image.width, image.height); cx1.clearRect(0, 0, image.width, image.height);
cx2.clearRect(0, 0, image.width, image.height); cx2.clearRect(0, 0, image.width, image.height);
saveToImage(); saveToImage();
activeTool = "brush"; activeTool = "brush";
} }
if (_lastActiveTool === "polygon") {
handleFinishPolygon();
}
_lastActiveTool = t;
}
$: if (activeTool) {
handleToolChange(activeTool);
} }
$: if (layerOpacity && orb) orb.setOpacity(layerOpacity); $: if (layerOpacity && orb) orb.setOpacity(layerOpacity);
@ -59,8 +71,6 @@
let debugValue = 0; let debugValue = 0;
let debugY = 0; let debugY = 0;
let isStrPressed = false;
let isSpacePressed = false;
let lastActiveTool; let lastActiveTool;
function scaleImageData(imageData, scale) { function scaleImageData(imageData, scale) {
@ -117,7 +127,7 @@
let prePolygonImage = new Image(image.width, image.height); let prePolygonImage = new Image(image.width, image.height);
let lastPolyX; let lastPolyX;
let lastPolyY; let lastPolyY;
function drawPolygon() { function drawSmoothPolygon() {
const x = Math.floor( const x = Math.floor(
mx * wrapperHeightRatio - xOffset * wrapperHeightRatio mx * wrapperHeightRatio - xOffset * wrapperHeightRatio
); );
@ -150,7 +160,53 @@
prePolygonImage.src = canvas.toDataURL(); prePolygonImage.src = canvas.toDataURL();
} }
function switchMode(e: MouseEvent) { function handlePolygonMouseDown() {
if (polygonPoints.length < 2) savePrePolygon();
const x = Math.floor(
mx * wrapperHeightRatio - xOffset * wrapperHeightRatio
);
const y = Math.floor(my * wrapperHeightRatio);
polygonPoints.push(x, y);
drawPolygon();
}
function handleFinishPolygon() {
// console.trace("BAD");
drawPolygon(false);
if (polygonPoints.length) {
polygonPoints = [];
lastPolyX = undefined;
lastPolyY = undefined;
}
}
function drawPolygon(showMouse = true) {
if (polygonPoints.length < 2) return;
cx1.clearRect(0, 0, image.width, image.height);
cx1.drawImage(prePolygonImage, 0, 0, image.width, image.height);
cx1.beginPath();
cx1.moveTo(polygonPoints[0], polygonPoints[1]);
for (let i = 2; i < polygonPoints.length; i += 2) {
cx1.lineTo(polygonPoints[i], polygonPoints[i + 1]);
}
if (showMouse) {
const x = Math.floor(
mx * wrapperHeightRatio - xOffset * wrapperHeightRatio
);
const y = Math.floor(my * wrapperHeightRatio);
cx1.lineTo(x, y);
}
cx1.fillStyle = cx1.strokeStyle = "#" + activeColor;
cx1.closePath();
cx1.lineWidth = 1;
polygonPoints.length === 2 ? cx1.stroke() : cx1.fill();
}
function switchViewMode(e: MouseEvent) {
e.stopPropagation(); e.stopPropagation();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
e.preventDefault(); e.preventDefault();
@ -163,9 +219,17 @@
} }
mode === "2d" ? orb.stop() : orb.start(); mode === "2d" ? orb.stop() : orb.start();
handleFinishPolygon();
} }
let lastMouseDown;
function handleMouseDown(e: MouseEvent) { function handleMouseDown(e: MouseEvent) {
// Double Click detection
if (!lastMouseDown) lastMouseDown = 0;
let t = Date.now();
let isDbClick = t - lastMouseDown < 200;
lastMouseDown = t;
if (e.button === 1) { if (e.button === 1) {
lastActiveTool = activeTool; lastActiveTool = activeTool;
activeTool = "pan"; activeTool = "pan";
@ -178,6 +242,13 @@
if (activeTool === "smooth_polygon") { if (activeTool === "smooth_polygon") {
savePrePolygon(); savePrePolygon();
} }
if (activeTool === "polygon") {
if (isDbClick) {
handleFinishPolygon();
} else {
handlePolygonMouseDown();
}
}
} }
function handleMouseUp(e) { function handleMouseUp(e) {
@ -190,27 +261,12 @@
saveToImage(); saveToImage();
} }
function handleMouseMove(e) { const handleMouseMove = throttle((e) => {
mx = Math.floor(e.clientX - topLeftX); mx = Math.floor(e.clientX - topLeftX);
my = Math.floor(e.clientY - topLeftY); my = Math.floor(e.clientY - topLeftY);
isOriginal = e.target.id === "cx1"; isOriginal = e.target.id === "cx1";
//Caclulate y position of pixel
const y = Math.floor(my * wrapperHeightRatio);
debugY = y;
const height = image.height;
// KarlKilian Formel
//debugValue = (2 * Math.sqrt(y * (image.height - y))) / image.height;
// New new formel
debugValue = Math.cos(
(((360 / height ** 2) * y ** 2 + (-360 / height) * y + 90) / 360) *
2 *
Math.PI
);
if (isDown) { if (isDown) {
if (activeTool === "pan") { if (activeTool === "pan") {
// TODO fix overflowiung // TODO fix overflowiung
@ -237,19 +293,22 @@
cx1.globalCompositeOperation = "source-over"; cx1.globalCompositeOperation = "source-over";
} }
if (activeTool === "smooth_polygon") drawPolygon(); if (activeTool === "smooth_polygon") drawSmoothPolygon();
if (activeTool === "brush") drawBrush(); if (activeTool === "brush") drawBrush();
} }
}
if (activeTool === "polygon") {
drawPolygon(true);
}
}, 50);
function handleKeyDown(e) { function handleKeyDown(e) {
if (e.keyCode === 69) activeTool = "erasor"; if (e.keyCode === 69) activeTool = "erasor";
if (e.keyCode === 66) activeTool = "brush"; if (e.keyCode === 66) activeTool = "brush";
if (e.keyCode === 17) isStrPressed = true; if (e.key === "Enter") handleFinishPolygon();
//SPACE //SPACE
if (e.keyCode === 32) { if (e.keyCode === 32) {
isSpacePressed = true;
if (!lastActiveTool) { if (!lastActiveTool) {
lastActiveTool = activeTool; lastActiveTool = activeTool;
activeTool = "pan"; activeTool = "pan";
@ -258,7 +317,6 @@
} }
function handleKeyUp(e) { function handleKeyUp(e) {
if (e.keyCode === 17) isStrPressed = false;
//SPACE //SPACE
if (e.keyCode === 32) { if (e.keyCode === 32) {
activeTool = lastActiveTool; activeTool = lastActiveTool;
@ -347,7 +405,7 @@
class:is-down={isDown} class:is-down={isDown}
style={`background-image: url(${imageUrl}); background-position: ${xOffset}px ${0}px`} style={`background-image: url(${imageUrl}); background-position: ${xOffset}px ${0}px`}
> >
<button id="mode" on:click={switchMode}> <button id="mode" on:click={switchViewMode}>
{mode} {mode}
</button> </button>
@ -360,7 +418,9 @@
id="cursor" id="cursor"
style={`width: ${brushRadius * 2}px; height: ${ style={`width: ${brushRadius * 2}px; height: ${
brushRadius * 2 brushRadius * 2
}px; background-color: #${activeColor}; top: ${my}px; left: ${mx}px`} }px; background-color: #${activeColor}; transform: translate(${
mx - brushRadius
}px, ${my - brushRadius}px);`}
/> />
{/if} {/if}
@ -382,19 +442,10 @@
}%)); opacity: ${(layerOpacity / 100) * 0.5};`} }%)); opacity: ${(layerOpacity / 100) * 0.5};`}
/> />
<p>h:{image.height} | y:{debugY} | value:{debugValue}</p>
<canvas class:visible={mode === "3d"} bind:this={canvas3D} /> <canvas class:visible={mode === "3d"} bind:this={canvas3D} />
</div> </div>
<style> <style>
p {
position: absolute;
top: 0px;
left: 0px;
z-index: 1001;
}
#ai { #ai {
position: absolute; position: absolute;
top: 60px; top: 60px;
@ -424,7 +475,6 @@
opacity: 0.5; opacity: 0.5;
z-index: 99; z-index: 99;
border-radius: 100%; border-radius: 100%;
transform: translateX(-50%) translateY(-50%);
} }
.wrapper.tool-pan { .wrapper.tool-pan {

View File

@ -6,7 +6,12 @@
export let activeTool = "pan"; export let activeTool = "pan";
const tools = ["pan", "brush", "erasor", "smooth_polygon"]; const tools = ["pan", "brush", "erasor", "smooth_polygon", "polygon"];
$: if (activeTool) {
if (!tools.includes(activeTool)) activeTool = "brush";
localStorage.setItem("activeTool", activeTool);
}
</script> </script>
{#each tools as t} {#each tools as t}

View File

@ -1,5 +1,6 @@
<script> <script>
export let activeColor = "ff0000"; export let activeColor = "ff0000";
export let activeTool;
const minRadius = 1; const minRadius = 1;
const maxRadius = 100; const maxRadius = 100;
@ -8,6 +9,11 @@
const colors = ["ff0000", "00ff00", "0000ff", "ffff00", "00ffff", "ff00ff"]; const colors = ["ff0000", "00ff00", "0000ff", "ffff00", "00ffff", "ff00ff"];
$: if (activeColor) {
if (!colors.includes(activeColor)) activeColor = colors[0];
localStorage.setItem("activeColor", activeColor);
}
function handleMouseWheel(e) { function handleMouseWheel(e) {
brushRadius = Math.min( brushRadius = Math.min(
Math.max(brushRadius - e.deltaY / 10, minRadius), Math.max(brushRadius - e.deltaY / 10, minRadius),
@ -31,14 +37,16 @@
</div> </div>
<div class="settings-wrapper"> <div class="settings-wrapper">
<label for="brush-radius">Brush Radius</label> {#if activeTool === "brush" || activeTool === "erasor"}
<input <label for="brush-radius">Brush Radius</label>
id="brush-radius" <input
type="range" id="brush-radius"
min={minRadius} type="range"
max={maxRadius} min={minRadius}
bind:value={brushRadius} max={maxRadius}
/> bind:value={brushRadius}
/>
{/if}
<!-- <label for="brush-radius">LayerOpacity</label> <!-- <label for="brush-radius">LayerOpacity</label>
<input <input
id="brush-radius" id="brush-radius"

File diff suppressed because it is too large Load Diff

8
yarn.lock Normal file
View File

@ -0,0 +1,8 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==