Should mostly solve #94

Signed-off-by: Victor Seiji Hariki <victorseijih@gmail.com>
This commit is contained in:
Victor Seiji Hariki 2022-12-14 15:12:44 -03:00
parent 0c5ed4a17e
commit 5e6b18503b
2 changed files with 127 additions and 33 deletions

View file

@ -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;

View file

@ -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();