mirror of
https://github.com/Doodle3D/Doodle3D-Core.git
synced 2024-07-03 00:11:21 +02:00
104 lines
2.7 KiB
JavaScript
104 lines
2.7 KiB
JavaScript
import { applyMatrixOnPath, pathToVectorPath } from './vectorUtils.js';
|
|
import { Matrix } from 'cal';
|
|
import TraceWorker from '../../workers/trace.worker.js';
|
|
import { getPixel } from './colorUtils.js';
|
|
import memoize from 'memoizee';
|
|
|
|
const TRACE_WORKER = new TraceWorker();
|
|
|
|
export const prepareImageData = memoize(prepareImageDataRaw, { max: 1 });
|
|
function prepareImageDataRaw(canvas, x, y) {
|
|
const { width, height } = canvas;
|
|
const imageData = canvas.getContext('2d').getImageData(0, 0, width, height);
|
|
|
|
const compairValue = getPixel(imageData, y * width + x);
|
|
|
|
const data = new Uint8Array(width * height);
|
|
for (let i = 0; i < data.length; i ++) {
|
|
const pixel = getPixel(imageData, i);
|
|
const r = Math.abs(pixel.r - compairValue.r);
|
|
const g = Math.abs(pixel.g - compairValue.g);
|
|
const b = Math.abs(pixel.b - compairValue.b);
|
|
|
|
const value = Math.max(r, g, b);
|
|
|
|
data[i] = value;
|
|
}
|
|
|
|
return { width, height, data };
|
|
}
|
|
|
|
const FLOOD_FILL_MULTIPLIER = 0.4;
|
|
export function calculateTolerance(start, current) {
|
|
const tolerance = start.distanceTo(current) * FLOOD_FILL_MULTIPLIER;
|
|
|
|
return tolerance;
|
|
}
|
|
|
|
export function floodFill(tolerance, image, start) {
|
|
const imageData = prepareImageData(image, start.x, start.y);
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const callback = ({ data }) => {
|
|
switch (data.msg) {
|
|
case 'FLOOD_FILL':
|
|
TRACE_WORKER.removeEventListener('message', callback);
|
|
|
|
if (data.status === 'OK') {
|
|
resolve(data.traceData);
|
|
} else {
|
|
reject(data.error);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
};
|
|
TRACE_WORKER.addEventListener('message', callback);
|
|
|
|
TRACE_WORKER.postMessage({
|
|
msg: 'FLOOD_FILL',
|
|
tolerance, imageData, start
|
|
});
|
|
});
|
|
}
|
|
|
|
export function traceFloodFill(traceData, shapeData) {
|
|
const { fill } = traceData;
|
|
const { width, height } = shapeData.imageData;
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const callback = ({ data }) => {
|
|
switch (data.msg) {
|
|
case 'TRACE':
|
|
TRACE_WORKER.removeEventListener('message', callback);
|
|
|
|
const transform = new Matrix()
|
|
.translate(width / -2, height / -2)
|
|
.multiplyMatrix(shapeData.transform);
|
|
|
|
const paths = data.paths
|
|
.map(pathToVectorPath)
|
|
.map(path => applyMatrixOnPath(path, transform));
|
|
|
|
if (data.status === 'OK') {
|
|
resolve(paths);
|
|
} else {
|
|
reject(data.error);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
};
|
|
TRACE_WORKER.addEventListener('message', callback);
|
|
|
|
TRACE_WORKER.postMessage({
|
|
msg: 'TRACE',
|
|
fill, width, height
|
|
});
|
|
});
|
|
}
|