feat(ui): implement polygon tool
This commit is contained in:
parent
0e461edd38
commit
b96f820971
3597
view/package-lock.json
generated
3597
view/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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
1411
view/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -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>
|
||||||
|
@ -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 {
|
||||||
|
@ -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}
|
||||||
|
@ -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,6 +37,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="settings-wrapper">
|
<div class="settings-wrapper">
|
||||||
|
{#if activeTool === "brush" || activeTool === "erasor"}
|
||||||
<label for="brush-radius">Brush Radius</label>
|
<label for="brush-radius">Brush Radius</label>
|
||||||
<input
|
<input
|
||||||
id="brush-radius"
|
id="brush-radius"
|
||||||
@ -39,6 +46,7 @@
|
|||||||
max={maxRadius}
|
max={maxRadius}
|
||||||
bind:value={brushRadius}
|
bind:value={brushRadius}
|
||||||
/>
|
/>
|
||||||
|
{/if}
|
||||||
<!-- <label for="brush-radius">LayerOpacity</label>
|
<!-- <label for="brush-radius">LayerOpacity</label>
|
||||||
<input
|
<input
|
||||||
id="brush-radius"
|
id="brush-radius"
|
||||||
|
1346
view/yarn.lock
1346
view/yarn.lock
File diff suppressed because it is too large
Load Diff
8
yarn.lock
Normal file
8
yarn.lock
Normal 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==
|
Loading…
x
Reference in New Issue
Block a user