feat(ui): implement fill tool
This commit is contained in:
parent
b96f820971
commit
312d15a296
@ -19,6 +19,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
let layerOpacity = 50;
|
let layerOpacity = 50;
|
||||||
|
let _layerOpacity = localStorage.getItem("layerOpacity");
|
||||||
|
if (_layerOpacity) {
|
||||||
|
layerOpacity = _layerOpacity;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class={`wrapper tool-${activeTool}`}>
|
<div class={`wrapper tool-${activeTool}`}>
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { AI, bufToImageUrl } from "helpers";
|
import {
|
||||||
|
AI,
|
||||||
|
bufToImageUrl,
|
||||||
|
createFloodMap,
|
||||||
|
hexToRGB,
|
||||||
|
imageToArray,
|
||||||
|
} from "helpers";
|
||||||
import { images as imageStore } from "stores";
|
import { images as imageStore } from "stores";
|
||||||
import OrbView from "./OrbView";
|
import OrbView from "./OrbView";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
@ -53,6 +59,7 @@
|
|||||||
let topLeftY = 0;
|
let topLeftY = 0;
|
||||||
let wrapperHeightRatio = 1;
|
let wrapperHeightRatio = 1;
|
||||||
let wrapperWidth = 0;
|
let wrapperWidth = 0;
|
||||||
|
const pixelAmount = image.width * image.height;
|
||||||
|
|
||||||
let xOffset = 0;
|
let xOffset = 0;
|
||||||
|
|
||||||
@ -160,6 +167,56 @@
|
|||||||
prePolygonImage.src = canvas.toDataURL();
|
prePolygonImage.src = canvas.toDataURL();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let preFloodMx, floodMap, preFloodImage: ArrayBuffer, floodImage: ImageData;
|
||||||
|
function handleFloodFill() {
|
||||||
|
if (!floodMap) return;
|
||||||
|
|
||||||
|
const amount = 255 - Math.floor(Math.abs(mx - preFloodMx) / 4);
|
||||||
|
|
||||||
|
const [r, g, b] = hexToRGB(activeColor);
|
||||||
|
|
||||||
|
for (let i = 0; i < pixelAmount; i++) {
|
||||||
|
if (floodMap[i] > amount) {
|
||||||
|
floodImage.data[i * 4 + 0] = r;
|
||||||
|
floodImage.data[i * 4 + 1] = g;
|
||||||
|
floodImage.data[i * 4 + 2] = b;
|
||||||
|
floodImage.data[i * 4 + 3] = 255;
|
||||||
|
} else {
|
||||||
|
floodImage.data[i * 4 + 0] = preFloodImage[i * 4 + 0];
|
||||||
|
floodImage.data[i * 4 + 1] = preFloodImage[i * 4 + 1];
|
||||||
|
floodImage.data[i * 4 + 2] = preFloodImage[i * 4 + 2];
|
||||||
|
floodImage.data[i * 4 + 3] = preFloodImage[i * 4 + 3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cx1.putImageData(floodImage, 0, 0);
|
||||||
|
}
|
||||||
|
async function savePreFlood() {
|
||||||
|
preFloodMx = mx;
|
||||||
|
prePolygonImage.src = canvas.toDataURL();
|
||||||
|
floodImage = cx1.getImageData(0, 0, image.width, image.height);
|
||||||
|
preFloodImage = cx1
|
||||||
|
.getImageData(0, 0, image.width, image.height)
|
||||||
|
.data.slice(0);
|
||||||
|
|
||||||
|
const x = Math.floor(
|
||||||
|
mx * wrapperHeightRatio - xOffset * wrapperHeightRatio
|
||||||
|
);
|
||||||
|
const y = Math.floor(my * wrapperHeightRatio);
|
||||||
|
|
||||||
|
floodMap = await createFloodMap(image, x, y);
|
||||||
|
|
||||||
|
// const pixelAmount = image.width * image.height;
|
||||||
|
// const withAlpha = new Uint8ClampedArray(pixelAmount * 4);
|
||||||
|
// for (let i = 0; i < pixelAmount; i++) {
|
||||||
|
// withAlpha[i * 4 + 0] = floodMap[i];
|
||||||
|
// withAlpha[i * 4 + 1] = floodMap[i];
|
||||||
|
// withAlpha[i * 4 + 2] = floodMap[i];
|
||||||
|
// withAlpha[i * 4 + 3] = 255;
|
||||||
|
// }
|
||||||
|
// cx1.putImageData(new ImageData(withAlpha, image.width, image.height), 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
function handlePolygonMouseDown() {
|
function handlePolygonMouseDown() {
|
||||||
if (polygonPoints.length < 2) savePrePolygon();
|
if (polygonPoints.length < 2) savePrePolygon();
|
||||||
|
|
||||||
@ -242,6 +299,9 @@
|
|||||||
if (activeTool === "smooth_polygon") {
|
if (activeTool === "smooth_polygon") {
|
||||||
savePrePolygon();
|
savePrePolygon();
|
||||||
}
|
}
|
||||||
|
if (activeTool === "flood_fill") {
|
||||||
|
savePreFlood();
|
||||||
|
}
|
||||||
if (activeTool === "polygon") {
|
if (activeTool === "polygon") {
|
||||||
if (isDbClick) {
|
if (isDbClick) {
|
||||||
handleFinishPolygon();
|
handleFinishPolygon();
|
||||||
@ -274,6 +334,10 @@
|
|||||||
(downOffset + e.clientX - downX) % (image.width / wrapperHeightRatio);
|
(downOffset + e.clientX - downX) % (image.width / wrapperHeightRatio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (activeTool === "flood_fill") {
|
||||||
|
handleFloodFill();
|
||||||
|
}
|
||||||
|
|
||||||
if (activeTool === "erasor") {
|
if (activeTool === "erasor") {
|
||||||
cx1.globalCompositeOperation = "destination-out";
|
cx1.globalCompositeOperation = "destination-out";
|
||||||
cx1.fillStyle = "#" + activeColor;
|
cx1.fillStyle = "#" + activeColor;
|
||||||
|
@ -6,7 +6,14 @@
|
|||||||
|
|
||||||
export let activeTool = "pan";
|
export let activeTool = "pan";
|
||||||
|
|
||||||
const tools = ["pan", "brush", "erasor", "smooth_polygon", "polygon"];
|
const tools = [
|
||||||
|
"pan",
|
||||||
|
"brush",
|
||||||
|
"erasor",
|
||||||
|
"smooth_polygon",
|
||||||
|
"polygon",
|
||||||
|
"flood_fill",
|
||||||
|
];
|
||||||
|
|
||||||
$: if (activeTool) {
|
$: if (activeTool) {
|
||||||
if (!tools.includes(activeTool)) activeTool = "brush";
|
if (!tools.includes(activeTool)) activeTool = "brush";
|
||||||
@ -41,11 +48,13 @@
|
|||||||
<style>
|
<style>
|
||||||
button.active {
|
button.active {
|
||||||
background-color: black;
|
background-color: black;
|
||||||
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
|
color: black;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
const minRadius = 1;
|
const minRadius = 1;
|
||||||
const maxRadius = 100;
|
const maxRadius = 100;
|
||||||
export let brushRadius = 10;
|
export let brushRadius = 10;
|
||||||
export const layerOpacity = 50;
|
export let layerOpacity = 50;
|
||||||
|
|
||||||
const colors = ["ff0000", "00ff00", "0000ff", "ffff00", "00ffff", "ff00ff"];
|
const colors = ["ff0000", "00ff00", "0000ff", "ffff00", "00ffff", "ff00ff"];
|
||||||
|
|
||||||
@ -14,6 +14,10 @@
|
|||||||
localStorage.setItem("activeColor", activeColor);
|
localStorage.setItem("activeColor", activeColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$: if (layerOpacity) {
|
||||||
|
localStorage.setItem("layerOpacity", layerOpacity);
|
||||||
|
}
|
||||||
|
|
||||||
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),
|
||||||
@ -47,15 +51,15 @@
|
|||||||
bind:value={brushRadius}
|
bind:value={brushRadius}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
<!-- <label for="brush-radius">LayerOpacity</label>
|
<label for="layer-opacity">LayerOpacity</label>
|
||||||
<input
|
<input
|
||||||
id="brush-radius"
|
id="layer-opacity"
|
||||||
type="range"
|
type="range"
|
||||||
min="0"
|
min="0"
|
||||||
max="100"
|
max="100"
|
||||||
steps="1"
|
steps="1"
|
||||||
bind:value={layerOpacity}
|
bind:value={layerOpacity}
|
||||||
/> -->
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { bufToImageUrl } from ".";
|
import { bufToImageUrl, imageToArray } from ".";
|
||||||
|
|
||||||
const worker = new Worker("build/workers/ai-worker.js");
|
const worker = new Worker("build/workers/ai-worker.js");
|
||||||
|
|
||||||
@ -14,26 +14,13 @@ worker.addEventListener("message", ev => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const analyze = (img: Image) => new Promise((res, rej) => {
|
const analyze = (img: Image) => new Promise(async (res, rej) => {
|
||||||
i++;
|
i++;
|
||||||
const _i = i;
|
const _i = i;
|
||||||
|
|
||||||
const canvas = document.createElement("canvas");
|
const pixels = await imageToArray(img);
|
||||||
canvas.width = img.width;
|
|
||||||
canvas.height = img.height;
|
|
||||||
|
|
||||||
const cx = canvas.getContext("2d");
|
|
||||||
const image = document.createElement("img");
|
|
||||||
image.onload = () => {
|
|
||||||
|
|
||||||
cx.drawImage(image, 0, 0);
|
|
||||||
const pixels = cx.getImageData(0, 0, image.width, image.height).data
|
|
||||||
|
|
||||||
worker.postMessage({ i: _i, pixels, width: img.width, height: img.height });
|
worker.postMessage({ i: _i, pixels, width: img.width, height: img.height });
|
||||||
}
|
|
||||||
|
|
||||||
image.src = bufToImageUrl(img.data, img.type);
|
|
||||||
|
|
||||||
|
|
||||||
cb[_i] = res;
|
cb[_i] = res;
|
||||||
});
|
});
|
||||||
|
@ -1,16 +1,4 @@
|
|||||||
import { images } from "../stores";
|
import _pixelWorker from "./_pixelWorker";
|
||||||
|
|
||||||
const worker = new Worker("build/workers/pixel-worker.js");
|
|
||||||
|
|
||||||
let i = 0;
|
|
||||||
|
|
||||||
let cb = {};
|
|
||||||
|
|
||||||
worker.addEventListener("message", ev => {
|
|
||||||
if (ev.data.i in cb) {
|
|
||||||
cb[ev.data.i](ev.data.result);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
interface res {
|
interface res {
|
||||||
color: string
|
color: string
|
||||||
@ -19,9 +7,6 @@ interface res {
|
|||||||
value: number
|
value: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (img: Image, correctDistortion: boolean): Promise<res[]> => new Promise((res, rej) => {
|
export default async (img: Image, correctDistortion: boolean): Promise<res[]> => {
|
||||||
i++;
|
return await _pixelWorker({ pixels: img.overlayData, width: img.width, height: img.height, correctDistortion, type: "count" })
|
||||||
const _i = i;
|
};
|
||||||
worker.postMessage({ i: _i, pixels: img.overlayData, width: img.width, height: img.height, correctDistortion });
|
|
||||||
cb[_i] = res;
|
|
||||||
});
|
|
6
view/src/helpers/FloodFill.ts
Normal file
6
view/src/helpers/FloodFill.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import hexToRgb from "./hexToRGB"
|
||||||
|
import _pixelWorker from "./_pixelWorker";
|
||||||
|
|
||||||
|
export default async (floodMap: Uint8ClampedArray, image: Uint8ClampedArray, color: string, amount: number) => {
|
||||||
|
return await _pixelWorker({ type: "flood_fill", floodMap, image, color, amount });
|
||||||
|
}
|
6
view/src/helpers/FloodFillMap.ts
Normal file
6
view/src/helpers/FloodFillMap.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { imageToArray } from "helpers";
|
||||||
|
import _pixelWorker from "./_pixelWorker"
|
||||||
|
|
||||||
|
export default async (image: Image, x: number, y: number): Promise<Uint8ClampedArray> => {
|
||||||
|
return await _pixelWorker({ type: "flood_map", pixels: await imageToArray(image, false), x, y, width: image.width, height: image.height });
|
||||||
|
}
|
34
view/src/helpers/ImageToArray.ts
Normal file
34
view/src/helpers/ImageToArray.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import BuffToImg from "./BuffToImg";
|
||||||
|
|
||||||
|
export default (img: Image, withAlpha = true) => new Promise((resolve) => {
|
||||||
|
|
||||||
|
const c = document.createElement("canvas");
|
||||||
|
c.width = img.width;
|
||||||
|
c.height = img.height;
|
||||||
|
|
||||||
|
const ctx = c.getContext("2d");
|
||||||
|
|
||||||
|
const imgEl = new Image(img.width, img.height);
|
||||||
|
imgEl.src = BuffToImg(img.data, img.type)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
imgEl.onload = () => {
|
||||||
|
ctx.drawImage(imgEl, 0, 0);
|
||||||
|
const alphaPixels = ctx.getImageData(0, 0, img.width, img.height).data;
|
||||||
|
|
||||||
|
if (withAlpha) return resolve(alphaPixels);
|
||||||
|
|
||||||
|
const pixelAmount = img.width * img.height;
|
||||||
|
const pixels = new Uint8ClampedArray(pixelAmount * 3);
|
||||||
|
|
||||||
|
for (let i = 0; i < pixelAmount; i++) {
|
||||||
|
pixels[i * 3 + 0] = alphaPixels[i * 4 + 0];
|
||||||
|
pixels[i * 3 + 1] = alphaPixels[i * 4 + 1];
|
||||||
|
pixels[i * 3 + 2] = alphaPixels[i * 4 + 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(pixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
18
view/src/helpers/_pixelWorker.ts
Normal file
18
view/src/helpers/_pixelWorker.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
const worker = new Worker("build/workers/pixel-worker.js");
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
let cb = {};
|
||||||
|
|
||||||
|
worker.addEventListener("message", ev => {
|
||||||
|
if (ev.data.i in cb) {
|
||||||
|
cb[ev.data.i](ev.data.result);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default (msg: any): Promise<any> => new Promise((res, rej) => {
|
||||||
|
i++;
|
||||||
|
const _i = i;
|
||||||
|
worker.postMessage({ i: _i, ...msg });
|
||||||
|
cb[_i] = res;
|
||||||
|
});
|
8
view/src/helpers/hexToRGB.ts
Normal file
8
view/src/helpers/hexToRGB.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export default function hexToRgb(hex) {
|
||||||
|
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||||
|
return result ? [
|
||||||
|
parseInt(result[1], 16),
|
||||||
|
parseInt(result[2], 16),
|
||||||
|
parseInt(result[3], 16)
|
||||||
|
] : null;
|
||||||
|
}
|
@ -2,4 +2,7 @@ export { default as bufToImageUrl } from "./BuffToImg";
|
|||||||
export { default as fileToImage } from "./FileToImage";
|
export { default as fileToImage } from "./FileToImage";
|
||||||
export { default as countPixels } from "./CountPixels";
|
export { default as countPixels } from "./CountPixels";
|
||||||
export { default as downloadImage } from "./DownloadImage";
|
export { default as downloadImage } from "./DownloadImage";
|
||||||
|
export { default as imageToArray } from "./ImageToArray";
|
||||||
|
export { default as createFloodMap } from "./FloodFillMap";
|
||||||
|
export { default as hexToRGB } from "./hexToRGB";
|
||||||
export { default as AI } from "./AI";
|
export { default as AI } from "./AI";
|
37
view/src/icons/Fill.svelte
Normal file
37
view/src/icons/Fill.svelte
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<script>
|
||||||
|
export let color = "black";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="100"
|
||||||
|
height="100"
|
||||||
|
viewBox="0 0 100 100"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M88.0216 51.8241L45.4864 94.3593L11.9785 60.8514L54.5137 18.3162L88.0216 51.8241Z"
|
||||||
|
stroke={color}
|
||||||
|
stroke-width="5"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M68.5713 64.4131L22.767 64.4129L45.7679 87.2165L68.5713 64.4131Z"
|
||||||
|
fill={color}
|
||||||
|
/>
|
||||||
|
<path d="M62.069 45.4335L66.4193 5.31409" stroke={color} stroke-width="5" />
|
||||||
|
<circle
|
||||||
|
cx="62.069"
|
||||||
|
cy="44.7038"
|
||||||
|
r="6.92501"
|
||||||
|
transform="rotate(45 62.069 44.7038)"
|
||||||
|
stroke={color}
|
||||||
|
stroke-width="5"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
svg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
@ -9,6 +9,7 @@ export { default as brush } from "./Brush.svelte"
|
|||||||
export { default as clear } from "./Clear.svelte"
|
export { default as clear } from "./Clear.svelte"
|
||||||
export { default as cross } from "./Cross.svelte"
|
export { default as cross } from "./Cross.svelte"
|
||||||
export { default as erasor } from "./Erasor.svelte"
|
export { default as erasor } from "./Erasor.svelte"
|
||||||
|
export { default as flood_fill } from "./Fill.svelte"
|
||||||
export { default as pan } from "./Pan.svelte"
|
export { default as pan } from "./Pan.svelte"
|
||||||
export { default as polygon } from "./Polygon.svelte"
|
export { default as polygon } from "./Polygon.svelte"
|
||||||
export { default as smooth_polygon } from "./SmoothPolygon.svelte"
|
export { default as smooth_polygon } from "./SmoothPolygon.svelte"
|
@ -1,10 +1,11 @@
|
|||||||
|
import hexToRGB from "helpers/hexToRGB";
|
||||||
|
|
||||||
|
|
||||||
export { };
|
export { };
|
||||||
|
|
||||||
self.addEventListener('message', function (e) {
|
const countColors = ({ pixels, width, height }) => {
|
||||||
|
|
||||||
|
|
||||||
const { data: { i, pixels, correctDistortion = true, width, height } } = e;
|
|
||||||
|
|
||||||
// This is the old method just by counting pixels
|
// This is the old method just by counting pixels
|
||||||
const oldStore = {};
|
const oldStore = {};
|
||||||
|
|
||||||
@ -80,6 +81,114 @@ self.addEventListener('message', function (e) {
|
|||||||
}
|
}
|
||||||
}).sort((a, b) => a.value > b.value ? -1 : 1)
|
}).sort((a, b) => a.value > b.value ? -1 : 1)
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const distance2D = (x1, y1, x2, y2) => {
|
||||||
|
const dx = x1 - x2;
|
||||||
|
const dy = y1 - y2;
|
||||||
|
return Math.sqrt(dx * dx + dy * dy);
|
||||||
|
}
|
||||||
|
const distance3D = (x1, y1, z1, x2, y2, z2) => {
|
||||||
|
const dx = x1 - x2;
|
||||||
|
const dy = y1 - y2;
|
||||||
|
const dz = z1 - z2;
|
||||||
|
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||||
|
}
|
||||||
|
|
||||||
|
const createFloodFillMap = ({ pixels, x, y, width, height }: { pixels: Uint8ClampedArray, x: number, y: number, width: number, height: number }) => {
|
||||||
|
|
||||||
|
const pixelAmount = width * height;
|
||||||
|
const index = (y * width + x) * 3;
|
||||||
|
|
||||||
|
const _R = pixels[index + 0];
|
||||||
|
const _G = pixels[index + 1];
|
||||||
|
const _B = pixels[index + 2];
|
||||||
|
|
||||||
|
let distanceWeight = 0.7;
|
||||||
|
|
||||||
|
// This is the distance to the pixel that is furthest away from xy
|
||||||
|
const maxDistance = distance2D((x < (width / 2)) ? width : 0, (y < (height / 2)) ? height : 0, x, y);
|
||||||
|
|
||||||
|
console.log(maxDistance)
|
||||||
|
|
||||||
|
// This the distance to the color that is furthest away from _R_G_B;
|
||||||
|
let maxColorDistance = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < pixelAmount; i++) {
|
||||||
|
const r = pixels[i * 3 + 0];
|
||||||
|
const g = pixels[i * 3 + 1];
|
||||||
|
const b = pixels[i * 3 + 2];
|
||||||
|
const dist = distance3D(r, g, b, _R, _G, _B);
|
||||||
|
if (dist > maxColorDistance) maxColorDistance = dist;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const floodFillMap = new Uint8ClampedArray(pixelAmount);
|
||||||
|
|
||||||
|
|
||||||
|
const maxWeight = (maxDistance * distanceWeight) + maxColorDistance * (1 - distanceWeight);
|
||||||
|
|
||||||
|
for (let i = 0; i < pixelAmount; i++) {
|
||||||
|
|
||||||
|
const _x = i % width;
|
||||||
|
const _y = Math.floor(i / width);
|
||||||
|
|
||||||
|
const r = pixels[i * 3 + 0];
|
||||||
|
const g = pixels[i * 3 + 1];
|
||||||
|
const b = pixels[i * 3 + 2];
|
||||||
|
|
||||||
|
let dist = distance2D(_x, _y, x, y);
|
||||||
|
let colDist = distance3D(r, g, b, _R, _G, _B);
|
||||||
|
|
||||||
|
let weight = 1 - ((dist * distanceWeight) + (colDist * (1 - distanceWeight))) / maxWeight;
|
||||||
|
|
||||||
|
floodFillMap[i] = Math.floor(weight * 255);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return floodFillMap;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const drawFloodFill = ({ floodMap, image, color, amount }: { floodMap: Uint8ClampedArray, image: Uint8ClampedArray, color: string, amount: number }) => {
|
||||||
|
|
||||||
|
const [r, g, b] = hexToRGB(color);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return image;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const send = (result, i) => {
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
self.postMessage({ result, i });
|
self.postMessage({ result, i });
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addEventListener('message', function (e) {
|
||||||
|
|
||||||
|
const data = e.data;
|
||||||
|
|
||||||
|
const { i, type = "count" } = data;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case "count":
|
||||||
|
send(countColors(data), i);
|
||||||
|
break;
|
||||||
|
case "flood_map":
|
||||||
|
send(createFloodFillMap(data), i);
|
||||||
|
break;
|
||||||
|
case "flood_fill":
|
||||||
|
send(drawFloodFill(data), i);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error("ERROR IN POXEL WORKER")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}, false);
|
}, false);
|
Loading…
Reference in New Issue
Block a user