Should mostly solve #94
Signed-off-by: Victor Seiji Hariki <victorseijih@gmail.com>
This commit is contained in:
parent
0c5ed4a17e
commit
5e6b18503b
2 changed files with 127 additions and 33 deletions
|
@ -2,15 +2,27 @@
|
||||||
* Some type definitions before the actual code
|
* Some type definitions before the actual code
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Represents a simple bounding box
|
* Represents a simple bouding box
|
||||||
*
|
|
||||||
* @typedef BoundingBox
|
|
||||||
* @type {Object}
|
|
||||||
* @property {number} x - Leftmost coordinate of the box
|
|
||||||
* @property {number} y - Topmost coordinate of the box
|
|
||||||
* @property {number} w - The bounding box Width
|
|
||||||
* @property {number} h - The bounding box Height
|
|
||||||
*/
|
*/
|
||||||
|
class BoundingBox {
|
||||||
|
x = 0;
|
||||||
|
y = 0;
|
||||||
|
w = 0;
|
||||||
|
h = 0;
|
||||||
|
|
||||||
|
constructor({x, y, w, h} = {x: 0, y: 0, w: 0, h: 0}) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.w = w;
|
||||||
|
this.h = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
contains(x, y) {
|
||||||
|
return (
|
||||||
|
this.x < x && this.y < y && x < this.x + this.w && y < this.y + this.h
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple implementation of the Observer programming pattern
|
* A simple implementation of the Observer programming pattern
|
||||||
|
@ -19,28 +31,34 @@
|
||||||
class Observer {
|
class Observer {
|
||||||
/**
|
/**
|
||||||
* List of handlers
|
* List of handlers
|
||||||
* @type {Set<(msg: T) => void | Promise<void>>}
|
* @type {Array<{handler: (msg: T) => void | Promise<void>, priority: number}>}
|
||||||
*/
|
*/
|
||||||
_handlers = new Set();
|
_handlers = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a observer to the events
|
* Adds a observer to the events
|
||||||
*
|
*
|
||||||
* @param {(msg: T) => void | Promise<void>} callback The function to run when receiving a message
|
* @param {(msg: T, state?: any) => void | Promise<void>} callback The function to run when receiving a message
|
||||||
* @returns {(msg:T) => void | Promise<void>} The callback we received
|
* @param {number} priority The priority level of the observer
|
||||||
|
* @param {boolean} wait If the handler must be waited for before continuing
|
||||||
|
* @returns {(msg:T, state?: any) => void | Promise<void>} The callback we received
|
||||||
*/
|
*/
|
||||||
on(callback) {
|
on(callback, priority = 0, wait = false) {
|
||||||
this._handlers.add(callback);
|
this._handlers.push({handler: callback, priority, wait});
|
||||||
|
this._handlers.sort((a, b) => b.priority - a.priority);
|
||||||
return callback;
|
return callback;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Removes a observer
|
* Removes a observer
|
||||||
*
|
*
|
||||||
* @param {(msg: T) => void | Promise<void>} callback The function used to register the callback
|
* @param {(msg: T, state?: any) => void | Promise<void>} callback The function used to register the callback
|
||||||
* @returns {boolean} Whether the handler existed
|
* @returns {boolean} Whether the handler existed
|
||||||
*/
|
*/
|
||||||
clear(callback) {
|
clear(callback) {
|
||||||
return this._handlers.delete(callback);
|
const index = this._handlers.findIndex((v) => v.handler === callback);
|
||||||
|
if (index === -1) return false;
|
||||||
|
this._handlers.splice(index, 1);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Sends a message to all observers
|
* Sends a message to all observers
|
||||||
|
@ -48,16 +66,23 @@ class Observer {
|
||||||
* @param {T} msg The message to send to the observers
|
* @param {T} msg The message to send to the observers
|
||||||
*/
|
*/
|
||||||
async emit(msg) {
|
async emit(msg) {
|
||||||
return Promise.all(
|
const state = {};
|
||||||
Array.from(this._handlers).map(async (handler) => {
|
const promises = [];
|
||||||
|
for (const {handler, wait} of this._handlers) {
|
||||||
|
const run = async () => {
|
||||||
try {
|
try {
|
||||||
await handler(msg);
|
await handler(msg, state);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("Observer failed to run handler");
|
console.warn("Observer failed to run handler");
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
}
|
}
|
||||||
})
|
};
|
||||||
);
|
|
||||||
|
if (wait) await run();
|
||||||
|
else promises.push(run());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,12 +236,12 @@ function getBoundingBox(cx, cy, w, h, gridSnap = null, offset = 0) {
|
||||||
box.x = Math.round(offs.x + cx);
|
box.x = Math.round(offs.x + cx);
|
||||||
box.y = Math.round(offs.y + cy);
|
box.y = Math.round(offs.y + cy);
|
||||||
|
|
||||||
return {
|
return new BoundingBox({
|
||||||
x: Math.floor(box.x - w / 2),
|
x: Math.floor(box.x - w / 2),
|
||||||
y: Math.floor(box.y - h / 2),
|
y: Math.floor(box.y - h / 2),
|
||||||
w: Math.round(w),
|
w: Math.round(w),
|
||||||
h: Math.round(h),
|
h: Math.round(h),
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class NoContentError extends Error {}
|
class NoContentError extends Error {}
|
||||||
|
@ -236,7 +261,7 @@ function cropCanvas(sourceCanvas, options = {}) {
|
||||||
const h = sourceCanvas.height;
|
const h = sourceCanvas.height;
|
||||||
var imageData = sourceCanvas.getContext("2d").getImageData(0, 0, w, h);
|
var imageData = sourceCanvas.getContext("2d").getImageData(0, 0, w, h);
|
||||||
/** @type {BoundingBox} */
|
/** @type {BoundingBox} */
|
||||||
const bb = {x: 0, y: 0, w: 0, h: 0};
|
const bb = new BoundingBox();
|
||||||
|
|
||||||
let minx = w;
|
let minx = w;
|
||||||
let maxx = -1;
|
let maxx = -1;
|
||||||
|
|
|
@ -291,7 +291,8 @@ const _generate = async (endpoint, request, bb, options = {}) => {
|
||||||
fetch(`${host}${url}interrupt`, {method: "POST"});
|
fetch(`${host}${url}interrupt`, {method: "POST"});
|
||||||
interruptButton.disabled = true;
|
interruptButton.disabled = true;
|
||||||
});
|
});
|
||||||
const stopMarchingAnts = march(bb);
|
const marchingOptions = {};
|
||||||
|
const stopMarchingAnts = march(bb, marchingOptions);
|
||||||
|
|
||||||
// First Dream Run
|
// First Dream Run
|
||||||
console.info(`[dream] Generating images for prompt '${request.prompt}'`);
|
console.info(`[dream] Generating images for prompt '${request.prompt}'`);
|
||||||
|
@ -471,6 +472,62 @@ const _generate = async (endpoint, request, bb, options = {}) => {
|
||||||
|
|
||||||
keyboard.listen.onkeyclick.on(onarrow);
|
keyboard.listen.onkeyclick.on(onarrow);
|
||||||
|
|
||||||
|
// For handling mouse events for navigation
|
||||||
|
const onmovehandler = mouse.listen.world.onmousemove.on(
|
||||||
|
(evn, state) => {
|
||||||
|
const contains = bb.contains(evn.x, evn.y);
|
||||||
|
|
||||||
|
if (!contains && !state.dream_processed)
|
||||||
|
imageCollection.inputElement.style.cursor = "auto";
|
||||||
|
if (!contains || state.dream_processed) marchingOptions.style = "#FFF";
|
||||||
|
|
||||||
|
if (!state.dream_processed && contains) {
|
||||||
|
marchingOptions.style = "#F55";
|
||||||
|
|
||||||
|
imageCollection.inputElement.style.cursor = "pointer";
|
||||||
|
|
||||||
|
state.dream_processed = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
const onclickhandler = mouse.listen.world.btn.left.onclick.on(
|
||||||
|
(evn, state) => {
|
||||||
|
if (!state.dream_processed && bb.contains(evn.x, evn.y)) {
|
||||||
|
applyImg();
|
||||||
|
imageCollection.inputElement.style.cursor = "auto";
|
||||||
|
state.dream_processed = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
const oncancelhandler = mouse.listen.world.btn.right.onclick.on(
|
||||||
|
(evn, state) => {
|
||||||
|
if (!state.dream_processed && bb.contains(evn.x, evn.y)) {
|
||||||
|
discardImg();
|
||||||
|
imageCollection.inputElement.style.cursor = "auto";
|
||||||
|
state.dream_processed = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
const onwheelhandler = mouse.listen.world.onwheel.on(
|
||||||
|
(evn, state) => {
|
||||||
|
console.debug(evn, state);
|
||||||
|
if (!state.dream_processed && bb.contains(evn.x, evn.y)) {
|
||||||
|
if (evn.delta < 0) nextImg();
|
||||||
|
else prevImg();
|
||||||
|
state.dream_processed = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
// Cleans up
|
// Cleans up
|
||||||
const clean = (removeBrushMask = false) => {
|
const clean = (removeBrushMask = false) => {
|
||||||
if (removeBrushMask) {
|
if (removeBrushMask) {
|
||||||
|
@ -482,6 +539,12 @@ const _generate = async (endpoint, request, bb, options = {}) => {
|
||||||
keyboard.listen.onkeyclick.clear(onarrow);
|
keyboard.listen.onkeyclick.clear(onarrow);
|
||||||
// Remove area from no-generate list
|
// Remove area from no-generate list
|
||||||
generationAreas.delete(areaid);
|
generationAreas.delete(areaid);
|
||||||
|
|
||||||
|
// Stop handling inputs
|
||||||
|
mouse.listen.world.onmousemove.clear(onmovehandler);
|
||||||
|
mouse.listen.world.onwheel.clear(onwheelhandler);
|
||||||
|
mouse.listen.world.btn.left.onclick.clear(onclickhandler);
|
||||||
|
mouse.listen.world.btn.right.onclick.clear(oncancelhandler);
|
||||||
};
|
};
|
||||||
|
|
||||||
redraw();
|
redraw();
|
||||||
|
@ -1097,7 +1160,7 @@ const dreamTool = () =>
|
||||||
};
|
};
|
||||||
|
|
||||||
if (state.selected) {
|
if (state.selected) {
|
||||||
const bb = {x: 0, y: 0, w: 0, h: 0};
|
const bb = new BoundingBox();
|
||||||
|
|
||||||
const minx = Math.min(state.selected.now.x, state.selected.start.x);
|
const minx = Math.min(state.selected.now.x, state.selected.start.x);
|
||||||
const miny = Math.min(state.selected.now.y, state.selected.start.y);
|
const miny = Math.min(state.selected.now.y, state.selected.start.y);
|
||||||
|
@ -1167,10 +1230,12 @@ const dreamTool = () =>
|
||||||
state.mousemovecb(state.lastMouseMove);
|
state.mousemovecb(state.lastMouseMove);
|
||||||
};
|
};
|
||||||
|
|
||||||
state.wheelcb = (evn) => {
|
state.wheelcb = (evn, estate) => {
|
||||||
|
if (estate.dream_processed) return;
|
||||||
_dream_onwheel(evn, state);
|
_dream_onwheel(evn, state);
|
||||||
};
|
};
|
||||||
state.dreamcb = (evn) => {
|
state.dreamcb = (evn, estate) => {
|
||||||
|
if (estate.dream_processed) return;
|
||||||
const bb =
|
const bb =
|
||||||
(state.selected && state.selected.bb) ||
|
(state.selected && state.selected.bb) ||
|
||||||
getBoundingBox(
|
getBoundingBox(
|
||||||
|
@ -1187,12 +1252,13 @@ const dreamTool = () =>
|
||||||
dream_generate_callback(bb, resolution, state);
|
dream_generate_callback(bb, resolution, state);
|
||||||
state.selected = null;
|
state.selected = null;
|
||||||
};
|
};
|
||||||
state.erasecb = (evn) => {
|
state.erasecb = (evn, estate) => {
|
||||||
if (state.selected) {
|
if (state.selected) {
|
||||||
state.selected = null;
|
state.selected = null;
|
||||||
state.redraw();
|
state.redraw();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (estate.dream_processed) return;
|
||||||
const bb = getBoundingBox(
|
const bb = getBoundingBox(
|
||||||
evn.x,
|
evn.x,
|
||||||
evn.y,
|
evn.y,
|
||||||
|
@ -1419,7 +1485,7 @@ const img2imgTool = () =>
|
||||||
let request = null;
|
let request = null;
|
||||||
|
|
||||||
if (state.selected) {
|
if (state.selected) {
|
||||||
bb = {x: 0, y: 0, w: 0, h: 0};
|
bb = new BoundingBox();
|
||||||
|
|
||||||
const minx = Math.min(state.selected.now.x, state.selected.start.x);
|
const minx = Math.min(state.selected.now.x, state.selected.start.x);
|
||||||
const miny = Math.min(state.selected.now.y, state.selected.start.y);
|
const miny = Math.min(state.selected.now.y, state.selected.start.y);
|
||||||
|
@ -1580,10 +1646,12 @@ const img2imgTool = () =>
|
||||||
state.mousemovecb(state.lastMouseMove);
|
state.mousemovecb(state.lastMouseMove);
|
||||||
};
|
};
|
||||||
|
|
||||||
state.wheelcb = (evn) => {
|
state.wheelcb = (evn, estate) => {
|
||||||
|
if (estate.dream_processed) return;
|
||||||
_dream_onwheel(evn, state);
|
_dream_onwheel(evn, state);
|
||||||
};
|
};
|
||||||
state.dreamcb = (evn) => {
|
state.dreamcb = (evn, estate) => {
|
||||||
|
if (estate.dream_processed) return;
|
||||||
const bb =
|
const bb =
|
||||||
(state.selected && state.selected.bb) ||
|
(state.selected && state.selected.bb) ||
|
||||||
getBoundingBox(
|
getBoundingBox(
|
||||||
|
@ -1601,7 +1669,8 @@ const img2imgTool = () =>
|
||||||
state.selected = null;
|
state.selected = null;
|
||||||
state.redraw();
|
state.redraw();
|
||||||
};
|
};
|
||||||
state.erasecb = (evn) => {
|
state.erasecb = (evn, estate) => {
|
||||||
|
if (estate.dream_processed) return;
|
||||||
if (state.selected) {
|
if (state.selected) {
|
||||||
state.selected = null;
|
state.selected = null;
|
||||||
state.redraw();
|
state.redraw();
|
||||||
|
|
Loading…
Reference in a new issue