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
|
||||
*/
|
||||
/**
|
||||
* Represents a simple bounding 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
|
||||
* Represents a simple bouding box
|
||||
*/
|
||||
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
|
||||
|
@ -19,28 +31,34 @@
|
|||
class Observer {
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @param {(msg: T) => void | Promise<void>} callback The function to run when receiving a message
|
||||
* @returns {(msg:T) => void | Promise<void>} The callback we received
|
||||
* @param {(msg: T, state?: any) => void | Promise<void>} callback The function to run when receiving a message
|
||||
* @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) {
|
||||
this._handlers.add(callback);
|
||||
on(callback, priority = 0, wait = false) {
|
||||
this._handlers.push({handler: callback, priority, wait});
|
||||
this._handlers.sort((a, b) => b.priority - a.priority);
|
||||
return callback;
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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
|
||||
|
@ -48,16 +66,23 @@ class Observer {
|
|||
* @param {T} msg The message to send to the observers
|
||||
*/
|
||||
async emit(msg) {
|
||||
return Promise.all(
|
||||
Array.from(this._handlers).map(async (handler) => {
|
||||
const state = {};
|
||||
const promises = [];
|
||||
for (const {handler, wait} of this._handlers) {
|
||||
const run = async () => {
|
||||
try {
|
||||
await handler(msg);
|
||||
await handler(msg, state);
|
||||
} catch (e) {
|
||||
console.warn("Observer failed to run handler");
|
||||
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.y = Math.round(offs.y + cy);
|
||||
|
||||
return {
|
||||
return new BoundingBox({
|
||||
x: Math.floor(box.x - w / 2),
|
||||
y: Math.floor(box.y - h / 2),
|
||||
w: Math.round(w),
|
||||
h: Math.round(h),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
class NoContentError extends Error {}
|
||||
|
@ -236,7 +261,7 @@ function cropCanvas(sourceCanvas, options = {}) {
|
|||
const h = sourceCanvas.height;
|
||||
var imageData = sourceCanvas.getContext("2d").getImageData(0, 0, w, h);
|
||||
/** @type {BoundingBox} */
|
||||
const bb = {x: 0, y: 0, w: 0, h: 0};
|
||||
const bb = new BoundingBox();
|
||||
|
||||
let minx = w;
|
||||
let maxx = -1;
|
||||
|
|
|
@ -291,7 +291,8 @@ const _generate = async (endpoint, request, bb, options = {}) => {
|
|||
fetch(`${host}${url}interrupt`, {method: "POST"});
|
||||
interruptButton.disabled = true;
|
||||
});
|
||||
const stopMarchingAnts = march(bb);
|
||||
const marchingOptions = {};
|
||||
const stopMarchingAnts = march(bb, marchingOptions);
|
||||
|
||||
// First Dream Run
|
||||
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);
|
||||
|
||||
// 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
|
||||
const clean = (removeBrushMask = false) => {
|
||||
if (removeBrushMask) {
|
||||
|
@ -482,6 +539,12 @@ const _generate = async (endpoint, request, bb, options = {}) => {
|
|||
keyboard.listen.onkeyclick.clear(onarrow);
|
||||
// Remove area from no-generate list
|
||||
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();
|
||||
|
@ -1097,7 +1160,7 @@ const dreamTool = () =>
|
|||
};
|
||||
|
||||
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 miny = Math.min(state.selected.now.y, state.selected.start.y);
|
||||
|
@ -1167,10 +1230,12 @@ const dreamTool = () =>
|
|||
state.mousemovecb(state.lastMouseMove);
|
||||
};
|
||||
|
||||
state.wheelcb = (evn) => {
|
||||
state.wheelcb = (evn, estate) => {
|
||||
if (estate.dream_processed) return;
|
||||
_dream_onwheel(evn, state);
|
||||
};
|
||||
state.dreamcb = (evn) => {
|
||||
state.dreamcb = (evn, estate) => {
|
||||
if (estate.dream_processed) return;
|
||||
const bb =
|
||||
(state.selected && state.selected.bb) ||
|
||||
getBoundingBox(
|
||||
|
@ -1187,12 +1252,13 @@ const dreamTool = () =>
|
|||
dream_generate_callback(bb, resolution, state);
|
||||
state.selected = null;
|
||||
};
|
||||
state.erasecb = (evn) => {
|
||||
state.erasecb = (evn, estate) => {
|
||||
if (state.selected) {
|
||||
state.selected = null;
|
||||
state.redraw();
|
||||
return;
|
||||
}
|
||||
if (estate.dream_processed) return;
|
||||
const bb = getBoundingBox(
|
||||
evn.x,
|
||||
evn.y,
|
||||
|
@ -1419,7 +1485,7 @@ const img2imgTool = () =>
|
|||
let request = null;
|
||||
|
||||
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 miny = Math.min(state.selected.now.y, state.selected.start.y);
|
||||
|
@ -1580,10 +1646,12 @@ const img2imgTool = () =>
|
|||
state.mousemovecb(state.lastMouseMove);
|
||||
};
|
||||
|
||||
state.wheelcb = (evn) => {
|
||||
state.wheelcb = (evn, estate) => {
|
||||
if (estate.dream_processed) return;
|
||||
_dream_onwheel(evn, state);
|
||||
};
|
||||
state.dreamcb = (evn) => {
|
||||
state.dreamcb = (evn, estate) => {
|
||||
if (estate.dream_processed) return;
|
||||
const bb =
|
||||
(state.selected && state.selected.bb) ||
|
||||
getBoundingBox(
|
||||
|
@ -1601,7 +1669,8 @@ const img2imgTool = () =>
|
|||
state.selected = null;
|
||||
state.redraw();
|
||||
};
|
||||
state.erasecb = (evn) => {
|
||||
state.erasecb = (evn, estate) => {
|
||||
if (estate.dream_processed) return;
|
||||
if (state.selected) {
|
||||
state.selected = null;
|
||||
state.redraw();
|
||||
|
|
Loading…
Reference in a new issue