Merge pull request #100 from zero01101/testing

2022-12-13 Testing Pull
This commit is contained in:
Victor Seiji Hariki 2022-12-15 22:47:33 -03:00 committed by GitHub
commit b038059ffb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 1275 additions and 364 deletions

View file

@ -325,6 +325,8 @@
<script src="js/ui/floating/layers.js" type="text/javascript"></script>
<!-- Load Tools -->
<script src="js/ui/tool/generic.js" type="text/javascript"></script>
<script src="js/ui/tool/dream.js" type="text/javascript"></script>
<script src="js/ui/tool/maskbrush.js" type="text/javascript"></script>
<script src="js/ui/tool/colorbrush.js" type="text/javascript"></script>

View file

@ -890,30 +890,22 @@ async function upscaleAndDownload() {
function loadSettings() {
// set default values if not set
var _prompt =
localStorage.getItem("prompt") == null
? "ocean floor scientific expedition, underwater wildlife"
: localStorage.getItem("prompt");
var _negprompt =
localStorage.getItem("neg_prompt") == null
? "people, person, humans, human, divers, diver, glitch, error, text, watermark, bad quality, blurry"
: localStorage.getItem("neg_prompt");
var _mask_blur =
localStorage.getItem("mask_blur") == null
? 0
: localStorage.getItem("mask_blur");
var _seed =
localStorage.getItem("seed") == null ? -1 : localStorage.getItem("seed");
var _enable_hr = Boolean(
localStorage.getItem("enable_hr") == (null || "false")
let _enable_hr =
localStorage.getItem("enable_hr") === null
? false
: localStorage.getItem("enable_hr")
);
var _sync_cursor_size = Boolean(
localStorage.getItem("sync_cursor_size") == (null || "false")
? false
: localStorage.getItem("sync_cursor_size")
);
: localStorage.getItem("enable_hr") === "true";
let _sync_cursor_size =
localStorage.getItem("sync_cursor_size") === null
? true
: localStorage.getItem("sync_cursor_size") === "true";
// set the values into the UI
document.getElementById("maskBlur").value = Number(_mask_blur);

View file

@ -126,7 +126,10 @@ const viewport = {
return (window.innerHeight * 1) / this.zoom;
},
viewToCanvas(x, y) {
return {x, y};
return {
x: this.cx + this.w * (x / window.innerWidth - 0.5),
y: this.cy + this.h * (y / window.innerHeight - 0.5),
};
},
canvasToView(x, y) {
return {
@ -185,7 +188,7 @@ mouse.listen.window.onwheel.on((evn) => {
});
mouse.listen.window.btn.middle.onpaintstart.on((evn) => {
worldInit = {x: viewport.cx, y: viewport.cy};
if (evn.evn.ctrlKey) worldInit = {x: viewport.cx, y: viewport.cy};
});
mouse.listen.window.btn.middle.onpaint.on((evn) => {

View file

@ -86,8 +86,8 @@
*
* @typedef MouseCoordContext
* @property {{[key: string]: MouseCoordContextDragInfo}} dragging Information about mouse button drags
* @property {{x: number, y: number}} prev Previous mouse position
* @property {{x: number, y: number}} pos Current mouse position
* @property {Point} prev Previous mouse position
* @property {Point} pos Current mouse position
*/
/* Here are keyboard-related types */

View file

@ -235,7 +235,7 @@ function createAutoComplete(name, wrapper, options = {}) {
onchange: new Observer(),
get value() {
const v = this._selectedOptions.map((opt) => opt.value);
const v = Array.from(this._selectedOptions).map((opt) => opt.value);
return options.multiple ? v : v[0];
},
set value(values) {

View file

@ -1,17 +1,58 @@
/**
* Some type definitions before the actual code
*/
/**
* Represents a simple bounding box
* Simple Point Coordinate
*
* @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
* @typedef Point
* @property {number} x - x coordinate
* @property {number} y - y coordinate
*/
/**
* 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
);
}
/**
* Gets bounding box from two points
*
* @param {Point} start Coordinate
* @param {Point} end
*/
static fromStartEnd(start, end) {
const minx = Math.min(start.x, end.x);
const miny = Math.min(start.y, end.y);
const maxx = Math.max(start.x, end.x);
const maxy = Math.max(start.y, end.y);
return new BoundingBox({
x: minx,
y: miny,
w: maxx - minx,
h: maxy - miny,
});
}
}
/**
* A simple implementation of the Observer programming pattern
* @template [T=any] Message type
@ -19,28 +60,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 +95,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 +265,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 +290,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;
@ -307,7 +361,37 @@ function downloadCanvas(options = {}) {
? cropCanvas(options.canvas).canvas
: options.canvas;
if (croppedCanvas != null) {
link.href = croppedCanvas.toDataURL("image/png");
link.click();
croppedCanvas.toBlob((blob) => {
link.href = URL.createObjectURL(blob);
link.click();
});
}
}
/**
* Makes an element in a location
* @param {string} type Element Tag
* @param {number} x X coordinate of the element
* @param {number} y Y coordinate of the element
* @param {{x: number y: offset}} offset Offset to apply to the element
* @returns
*/
const makeElement = (
type,
x,
y,
offset = {
x: -imageCollection.inputOffset.x,
y: -imageCollection.inputOffset.y,
}
) => {
const el = document.createElement(type);
el.style.position = "absolute";
el.style.left = `${x + offset.x}px`;
el.style.top = `${y + offset.y}px`;
// We can use the input element to add interactible html elements in the world
imageCollection.inputElement.appendChild(el);
return el;
};

View file

@ -76,9 +76,12 @@ async function getStyles() {
};
// Load from local storage if set
const promptDefaultValue = localStorage.getItem("prompt") || defaultPrompt;
const storedPrompt = localStorage.getItem("prompt");
const storedNeg = localStorage.getItem("neg_prompt");
const promptDefaultValue =
storedPrompt === null ? defaultPrompt : storedPrompt;
const negativePromptDefaultValue =
localStorage.getItem("neg_prompt") || defaultNegativePrompt;
storedNeg === null ? defaultNegativePrompt : storedNeg;
promptEl.value = promptEl.title = promptDefaultValue;
negativePromptEl.value = negativePromptEl.title = negativePromptDefaultValue;

File diff suppressed because it is too large Load diff

276
js/ui/tool/generic.js Normal file
View file

@ -0,0 +1,276 @@
/**
* File to add generic rendering functions and shared utilities
*/
const _tool = {
/**
* Draws a reticle used for image generation
*
* @param {BoundingBox} bb The bounding box of the reticle (world space)
* @param {string} tool Name of the tool to diplay
* @param {{w: number, h: number}} resolution Resolution of generation to display
* @param {object} style Styles to use for rendering the reticle
* @param {string} [style.sizeTextStyle = "#FFF5"] Style of the text for diplaying the bounding box size.
* @param {string} [style.genSizeTextStyle = "#FFF5"] Style of the text for diplaying generation size
* @param {string} [style.toolTextStyle = "#FFF5"] Style of the text for the tool name
* @param {number} [style.reticleWidth = 1] Width of the line of the reticle
* @param {string} [style.reticleStyle = "#FFF"] Style of the line of the reticle
*
* @returns A function that erases this reticle drawing
*/
_reticle_draw(bb, tool, resolution, style = {}) {
defaultOpt(style, {
sizeTextStyle: "#FFF5",
genSizeTextStyle: "#FFF5",
toolTextStyle: "#FFF5",
reticleWidth: 1,
reticleStyle: "#FFF",
});
const bbvp = {
...viewport.canvasToView(bb.x, bb.y),
w: viewport.zoom * bb.w,
h: viewport.zoom * bb.h,
};
uiCtx.save();
// draw targeting square reticle thingy cursor
uiCtx.lineWidth = style.reticleWidth;
uiCtx.strokeStyle = style.reticleStyle;
uiCtx.strokeRect(bbvp.x, bbvp.y, bbvp.w, bbvp.h); //origin is middle of the frame
uiCtx.font = `bold 20px Open Sans`;
// Draw Tool Name
if (bb.h > 40) {
const xshrink = Math.min(
1,
(bbvp.w - 20) / uiCtx.measureText(tool).width
);
uiCtx.font = `bold ${20 * xshrink}px Open Sans`;
uiCtx.textAlign = "left";
uiCtx.fillStyle = style.toolTextStyle;
uiCtx.fillText(tool, bbvp.x + 10, bbvp.y + 10 + 20 * xshrink, bb.w);
}
// Draw width and height
{
// Render Cursor Width
uiCtx.textAlign = "center";
uiCtx.fillStyle = style.sizeTextStyle;
uiCtx.translate(bbvp.x + bbvp.w / 2, bbvp.y + bbvp.h / 2);
const xshrink = Math.min(
1,
(bbvp.w - 30) / uiCtx.measureText(`${bb.w}px`).width
);
const yshrink = Math.min(
1,
(bbvp.h - 30) / uiCtx.measureText(`${bb.h}px`).width
);
uiCtx.font = `bold ${20 * xshrink}px Open Sans`;
uiCtx.fillText(`${bb.w}px`, 0, bbvp.h / 2 - 10 * xshrink, bb.w);
// Render Generation Width
uiCtx.fillStyle = style.genSizeTextStyle;
uiCtx.font = `bold ${10 * xshrink}px Open Sans`;
if (bb.w !== resolution.w)
uiCtx.fillText(`${resolution.w}px`, 0, bbvp.h / 2 - 30 * xshrink, bb.h);
// Render Cursor Height
uiCtx.rotate(-Math.PI / 2);
uiCtx.fillStyle = style.sizeTextStyle;
uiCtx.font = `bold ${20 * yshrink}px Open Sans`;
uiCtx.fillText(`${bb.h}px`, 0, bbvp.w / 2 - 10 * yshrink, bb.h);
// Render Generation Height
uiCtx.fillStyle = style.genSizeTextStyle;
uiCtx.font = `bold ${10 * yshrink}px Open Sans`;
if (bb.h !== resolution.h)
uiCtx.fillText(`${resolution.h}px`, 0, bbvp.w / 2 - 30 * xshrink, bb.h);
uiCtx.restore();
}
return () => {
uiCtx.save();
uiCtx.clearRect(bbvp.x - 64, bbvp.y - 64, bbvp.w + 128, bbvp.h + 128);
uiCtx.restore();
};
},
/**
* Draws a generic crosshair cursor at the specified location
*
* @param {number} x X world coordinate of the cursor
* @param {number} y Y world coordinate of the cursor
* @param {object} style Style of the lines of the cursor
* @param {string} [style.width = 3] Line width of the lines of the cursor
* @param {string} [style.style = "#FFF5"] Stroke style of the lines of the cursor
*
* @returns A function that erases this cursor drawing
*/
_cursor_draw(x, y, style = {}) {
defaultOpt(style, {
width: 3,
style: "#FFF5",
});
const vpc = viewport.canvasToView(x, y);
// Draw current cursor location
uiCtx.lineWidth = style.width;
uiCtx.strokeStyle = style.style;
uiCtx.beginPath();
uiCtx.moveTo(vpc.x, vpc.y + 10);
uiCtx.lineTo(vpc.x, vpc.y - 10);
uiCtx.moveTo(vpc.x + 10, vpc.y);
uiCtx.lineTo(vpc.x - 10, vpc.y);
uiCtx.stroke();
return () => {
uiCtx.clearRect(vpc.x - 15, vpc.y - 15, vpc.x + 30, vpc.y + 30);
};
},
/**
* Creates generic handlers for dealing with draggable selection areas
*
* @param {object} state State of the tool
* @param {boolean} state.snapToGrid Whether the cursor should snap to the grid
* @param {() => void} [state.redraw] Function to redraw the cursor
* @returns
*/
_draggable_selection(state) {
const selection = {
_inside: false,
_dirty_bb: true,
_cached_bb: null,
_selected: null,
/**
* If the cursor is cursor is currently inside the selection
*/
get inside() {
return this._inside;
},
/**
* Get intermediate selection object
*/
get selected() {
return this._selected;
},
/**
* If the selection exists
*/
get exists() {
return !!this._selected;
},
/**
* Gets the selection bounding box
*/
get bb() {
if (this._dirty_bb && this._selected) {
this._cached_bb = BoundingBox.fromStartEnd(
this._selected.start,
this._selected.now
);
this._dirty_bb = false;
}
return this._selected && this._cached_bb;
},
/**
* When the cursor enters the selection
*/
onenter: new Observer(),
/**
* When the cursor leaves the selection
*/
onleave: new Observer(),
// Utility methods
deselect() {
if (this.inside) {
this._inside = false;
this.onleave.emit({evn: null});
}
this._selected = null;
},
// Dragging handlers
/**
* Drag start event handler
*
* @param {Point} evn Drag start event
*/
dragstartcb(evn) {
const x = state.snapToGrid ? evn.ix + snap(evn.ix, 0, 64) : evn.ix;
const y = state.snapToGrid ? evn.iy + snap(evn.iy, 0, 64) : evn.iy;
this._selected = {start: {x, y}, now: {x, y}};
this._dirty_bb = true;
},
/**
* Drag event handler
*
* @param {Point} evn Drag event
*/
dragcb(evn) {
const x = state.snapToGrid ? evn.x + snap(evn.x, 0, 64) : evn.x;
const y = state.snapToGrid ? evn.y + snap(evn.y, 0, 64) : evn.y;
if (x !== this._selected.now.x || y !== this._selected.now.y) {
this._selected.now = {x, y};
this._dirty_bb = true;
}
},
/**
* Drag end event handler
*
* @param {Point} evn Drag end event
*/
dragendcb(evn) {
const x = state.snapToGrid ? evn.x + snap(evn.x, 0, 64) : evn.x;
const y = state.snapToGrid ? evn.y + snap(evn.y, 0, 64) : evn.y;
this._selected.now = {x, y};
this._dirty_bb = true;
if (
this._selected.start.x === this._selected.now.x ||
this._selected.start.y === this._selected.now.y
) {
this.deselect();
}
},
/**
* Mouse move event handler
*
* @param {Point} evn Mouse move event
*/
smousemovecb(evn) {
if (!this._selected || !this.bb.contains(evn.x, evn.y)) {
if (this.inside) {
this._inside = false;
this.onleave.emit({evn});
}
} else {
if (!this.inside) {
this._inside = true;
this.onenter.emit({evn});
}
}
},
};
return selection;
},
};

View file

@ -131,20 +131,10 @@ const selectTransformTool = () =>
);
},
handles() {
const _createHandle = (x, y, originOffset = null, size = 10) => {
const _createHandle = (x, y, originOffset = null) => {
return {
x: x - size / 2,
y: y - size / 2,
w: size,
h: size,
contains(x, y) {
return (
this.x <= x &&
x <= this.x + this.w &&
this.y <= y &&
y <= this.y + this.h
);
},
x,
y,
scaleTo: (tx, ty, keepAspectRatio = true) => {
const origin = {
x: this.original.x + this.original.w / 2,
@ -182,11 +172,13 @@ const selectTransformTool = () =>
},
};
};
const size = viewport.zoom * 10;
return [
_createHandle(this.x, this.y),
_createHandle(this.x + this.w, this.y),
_createHandle(this.x, this.y + this.h),
_createHandle(this.x + this.w, this.y + this.h),
_createHandle(this.x, this.y, size),
_createHandle(this.x + this.w, this.y, size),
_createHandle(this.x, this.y + this.h, size),
_createHandle(this.x + this.w, this.y + this.h, size),
];
},
};
@ -295,10 +287,20 @@ const selectTransformTool = () =>
state.selected.handles().forEach((handle) => {
const bbvph = {
...viewport.canvasToView(handle.x, handle.y),
w: viewport.zoom * handle.w,
h: viewport.zoom * handle.h,
w: 10,
h: 10,
};
if (handle.contains(evn.x, evn.y)) {
bbvph.x -= 5;
bbvph.y -= 5;
const inhandle =
evn.evn.clientX > bbvph.x &&
evn.evn.clientX < bbvph.x + bbvph.w &&
evn.evn.clientY > bbvph.y &&
evn.evn.clientY < bbvph.y + bbvph.h;
if (inhandle) {
cursorInHandle = true;
uiCtx.strokeRect(
bbvph.x - 1,
@ -333,10 +335,11 @@ const selectTransformTool = () =>
// Handles left mouse clicks
state.clickcb = (evn) => {
if (
state.original.x === state.selected.x &&
state.original.y === state.selected.y &&
state.original.w === state.selected.w &&
state.original.h === state.selected.h
!state.original ||
(state.original.x === state.selected.x &&
state.original.y === state.selected.y &&
state.original.w === state.selected.w &&
state.original.h === state.selected.h)
) {
state.reset();
return;
@ -381,9 +384,24 @@ const selectTransformTool = () =>
if (state.selected) {
const handles = state.selected.handles();
const activeHandle = handles.find((v) =>
v.contains(evn.ix, evn.iy)
);
const activeHandle = handles.find((v) => {
const vpc = viewport.canvasToView(v.x, v.y);
const tlc = viewport.viewToCanvas(vpc.x - 5, vpc.y - 5);
const brc = viewport.viewToCanvas(vpc.x + 5, vpc.y + 5);
const bb = {
x: tlc.x,
y: tlc.y,
w: brc.x - tlc.x,
h: brc.y - tlc.y,
};
return (
evn.ix > bb.x &&
evn.ix < bb.x + bb.w &&
evn.iy > bb.y &&
evn.iy < bb.y + bb.h
);
});
if (activeHandle) {
state.scaling = activeHandle;
return;