diff --git a/demo/big.jpg b/docs/big.jpg
similarity index 100%
rename from demo/big.jpg
rename to docs/big.jpg
diff --git a/demo/default.css b/docs/default.css
similarity index 100%
rename from demo/default.css
rename to docs/default.css
diff --git a/demo/index.html b/docs/index.html
similarity index 73%
rename from demo/index.html
rename to docs/index.html
index 256909e..4260f44 100644
--- a/demo/index.html
+++ b/docs/index.html
@@ -4,13 +4,13 @@
diff --git a/demo/maze.png b/docs/maze.png
similarity index 100%
rename from demo/maze.png
rename to docs/maze.png
diff --git a/index.js b/index.js
index f995b38..59be585 100644
--- a/index.js
+++ b/index.js
@@ -1,4 +1,4 @@
-function filler(options) {
+function waveFiller(options) {
this.threshold = options.threshold || 20; // maximum deviance in color channell value allowed for a pixel to be considered blank
this.blank = options.blank || [255, 255, 255, 255]; // white - set it to whatever color is considered blank in the image
this.pixel = options.pixel || [255, 0, 0, 50]; // red - set it to whatever fill color you want as RGBA
@@ -8,97 +8,95 @@ function filler(options) {
this.minWorkerLoad = options.minWorkerLoad || 500; // minimum number of shore pixels, if more are available, to be assigned to a web worker
this.computeAhead = options.computeAhead; // set to true to compute upcoming frames before current frame is done for faster overall rendering; warning: wave is no longer an advancing circle when filling large areas
this.libraryPath = options.libraryPath || './' // path to library directory relative to current context
+ this.silent = options.silent // set to true to disable console logs
const frameTime = 1000 / this.fps;
let skipFrame = false;
- this.initialize = async () => {
- this.canvas = document.getElementById(options.canvasId);
- this.context = this.canvas.getContext('2d');
- this.paint(options.fit.width, options.fit.height, options.fit.resize);
- this.canvas.onclick = (event) => {
- this.click(event.clientX, event.clientY);
- }
- }
- this.paint = (width, height, resize) => { // paint image in canvas
- this.image = new Image();
- this.image.src = options.imageSrc;
- this.image.onload = async () => {
+ this.initialize = () => {
+ return new Promise (async (resolve, reject) => {
try {
- let resized;
- if (width) {
- height = this.image.height / this.image.width * width;
- }
- else if (height) {
- width = this.image.width / this.image.height * height;
- }
- else {
- resized = this.fit();
- width = resized.width;
- height = resized.height;
- }
- this.canvas.width = width;
- this.canvas.height = height;
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.context.drawImage(this.image, 0, 0, this.canvas.width, this.canvas.height);
- this.pixels = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
- if (resize) {
- resized = this.fit();
- this.canvas.style.width = resized.width;
- this.canvas.style.height = resized.height;
- }
- if (this.canvas.offsetWidth < window.innerWidth)
- this.canvas.style.left = (window.innerWidth - this.canvas.offsetWidth) / 2 + 'px';
- if (this.canvas.offsetHeight < window.innerHeight)
- this.canvas.style.top = (window.innerHeight - this.canvas.offsetHeight) / 2 + 'px';
- this.canvasScale = this.canvas.width / this.canvas.offsetWidth;
- this.workers = [];
- this.worked = 0;
- const workerBlob = await (await fetch(`${options.libraryPath}worker.js`)).blob();
- for (let index = 0; index < this.workerCount; index++) {
- const worker = new Worker(URL.createObjectURL(workerBlob));
- worker.working = false;
- worker.onmessage = (message) => {
- switch (message.data.status) {
- case 'initDone':
- this.worked++;
- if (this.worked == this.workerCount) {
- console.log(`web worker init done; ${this.worked} web workers ready`);
- }
- break;
- case 'done':
- this.handleWorkerDone(message.data.output);
- break;
- }
+ this.canvas = document.getElementById(options.canvasId);
+ this.context = this.canvas.getContext('2d');
+ let width = options.fit.width;
+ let height = options.fit.height;
+ let resize = options.fit.resize;
+ this.image = new Image();
+ this.image.src = options.imageSrc;
+ this.image.onload = async () => {
+ let resized;
+ if (width) {
+ height = this.image.height / this.image.width * width;
+ }
+ else if (height) {
+ width = this.image.width / this.image.height * height;
}
- worker.onerror = (error) => {
- console.log(`worker ${index} error`);
- console.log(error);
+ else {
+ resized = this.fit();
+ width = resized.width;
+ height = resized.height;
}
- worker.onmessageerror = (error) => {
- console.log(`worker ${index} message error`);
- console.log(error);
+ this.canvas.width = width;
+ this.canvas.height = height;
+ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ this.context.drawImage(this.image, 0, 0, this.canvas.width, this.canvas.height);
+ this.pixels = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
+ if (resize) {
+ resized = this.fit();
+ this.canvas.style.width = resized.width;
+ this.canvas.style.height = resized.height;
}
- worker.postMessage({
- type: 'init',
- input: {
- index,
- threshold: this.threshold,
- blank: this.blank,
- pixel: this.pixel,
- radius: this.radius,
- width: this.canvas.width,
- height: this.canvas.height,
- pixels: this.pixels.data,
- done: {}
+ if (this.canvas.offsetWidth < window.innerWidth)
+ this.canvas.style.left = (window.innerWidth - this.canvas.offsetWidth) / 2 + 'px';
+ if (this.canvas.offsetHeight < window.innerHeight)
+ this.canvas.style.top = (window.innerHeight - this.canvas.offsetHeight) / 2 + 'px';
+ this.canvasScale = this.canvas.width / this.canvas.offsetWidth;
+ this.workers = [];
+ let initialized = 0;
+ const workerBlob = await (await fetch(`${options.libraryPath}worker.js`)).blob();
+ for (let index = 0; index < this.workerCount; index++) {
+ const worker = new Worker(URL.createObjectURL(workerBlob));
+ worker.working = false;
+ worker.onmessage = (message) => {
+ switch (message.data.status) {
+ case 'initDone':
+ initialized++;
+ if (initialized == this.workerCount) {
+ this.log(`web worker init done; ${initialized} web workers ready`);
+ resolve(initialized);
+ }
+ break;
+ case 'done':
+ this.handleWorkerDone(message.data.output);
+ break;
+ }
+ }
+ worker.onerror = (error) => {
+ this.log([`worker ${index} error`, error]);
}
- });
- this.workers.push(worker);
+ worker.onmessageerror = (error) => {
+ this.log([`worker ${index} message error`, error]);
+ }
+ worker.postMessage({
+ type: 'init',
+ input: {
+ index,
+ threshold: this.threshold,
+ blank: this.blank,
+ pixel: this.pixel,
+ radius: this.radius,
+ width: this.canvas.width,
+ height: this.canvas.height,
+ pixels: this.pixels.data,
+ done: {}
+ }
+ });
+ this.workers.push(worker);
+ }
}
}
catch (error) {
- console.log('initialize error');
- console.log(error);
+ this.log(['initialize error', error]);
}
- }
+ });
}
this.fit = () => { // resize image to fit canvas
let width;
@@ -242,7 +240,8 @@ function filler(options) {
currentFrame = this.frames[this.frame];
if (!currentFrame?.shore.length) { // animation done
this.end = window.performance.now();
- console.log(`done in ${this.end - this.start} ms`);
+ this.runTime = this.end - this.start;
+ this.log(`done in ${this.runTime} ms @ ${((Object.keys(this.frames).length - 1) / this.runTime * 1000).toFixed(2)} fps`);
this.locked = false;
}
else {
@@ -256,20 +255,35 @@ function filler(options) {
}
window.requestAnimationFrame(this.checkFrameReady);
}
- this.click = (x, y) => {
+ this.fill = (x, y) => {
if (this.locked) {
- console.log('> nope; locked.');
+ this.log('locked; already running');
return;
}
this.locked = true;
this.frame = 0;
this.frames = {};
- x = Math.floor((x - this.canvas.offsetLeft) * this.canvasScale);
- y = Math.floor((y - this.canvas.offsetTop) * this.canvasScale);
this.createFrame(this.frame);
this.frames[this.frame].shore = [[x, y]];
this.start = window.performance.now();
this.computeNextFrame();
this.assignWork();
}
+ this.click = (x, y) => { // computes x, y click event coordinates relative to canvas pixels
+ x = Math.floor((x - this.canvas.offsetLeft) * this.canvasScale);
+ y = Math.floor((y - this.canvas.offsetTop) * this.canvasScale);
+ this.fill(x, y);
+ }
+ this.log = (input) => {
+ if (options.silent) {
+ return;
+ }
+ const isArray = Array.isArray(input);
+ console.log(`waveFiller: ${!isArray ? input : ''}`);
+ if (isArray) {
+ for (let i = 0; i < input.length; i++) {
+ console.log(input[i]);
+ }
+ }
+ }
}