now selection dream compatible with mouse functions
Signed-off-by: Victor Seiji Hariki <victorseijih@gmail.com>
This commit is contained in:
parent
ec05084d71
commit
9a81946eef
5 changed files with 497 additions and 222 deletions
|
@ -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>
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
/**
|
||||
* Some type definitions before the actual code
|
||||
*/
|
||||
|
||||
/**
|
||||
* Simple Point Coordinate
|
||||
*
|
||||
* @typedef Point
|
||||
* @property {number} x - x coordinate
|
||||
* @property {number} y - y coordinate
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a simple bouding box
|
||||
*/
|
||||
|
@ -22,6 +31,26 @@ class BoundingBox {
|
|||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -515,6 +515,16 @@ const _generate = async (endpoint, request, bb, options = {}) => {
|
|||
1,
|
||||
true
|
||||
);
|
||||
const onmorehandler = mouse.listen.world.btn.middle.onclick.on(
|
||||
(evn, state) => {
|
||||
if (!state.dream_processed && bb.contains(evn.x, evn.y)) {
|
||||
makeMore();
|
||||
state.dream_processed = true;
|
||||
}
|
||||
},
|
||||
1,
|
||||
true
|
||||
);
|
||||
const onwheelhandler = mouse.listen.world.onwheel.on(
|
||||
(evn, state) => {
|
||||
if (evn.evn.ctrlKey) return;
|
||||
|
@ -542,9 +552,10 @@ const _generate = async (endpoint, request, bb, options = {}) => {
|
|||
|
||||
// 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);
|
||||
mouse.listen.world.btn.middle.onclick.clear(onmorehandler);
|
||||
mouse.listen.world.onwheel.clear(onwheelhandler);
|
||||
};
|
||||
|
||||
redraw();
|
||||
|
@ -926,87 +937,6 @@ const dream_img2img_callback = (bb, resolution, state) => {
|
|||
/**
|
||||
* Dream and img2img tools
|
||||
*/
|
||||
const _reticle_draw = (bb, state, 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();
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic wheel handler
|
||||
|
@ -1045,15 +975,24 @@ const dreamTool = () =>
|
|||
mouse.listen.world.onmousemove.on(state.mousemovecb);
|
||||
mouse.listen.world.onwheel.on(state.wheelcb);
|
||||
|
||||
mouse.listen.world.onmousemove.on(state.mousemovecb);
|
||||
mouse.listen.world.onwheel.on(state.wheelcb);
|
||||
mouse.listen.world.btn.left.onclick.on(state.dreamcb);
|
||||
mouse.listen.world.btn.right.onclick.on(state.erasecb);
|
||||
|
||||
// Select Region listeners
|
||||
mouse.listen.world.btn.left.ondragstart.on(state.dragstartcb);
|
||||
mouse.listen.world.btn.left.ondrag.on(state.dragcb);
|
||||
mouse.listen.world.btn.left.ondragend.on(state.dragendcb);
|
||||
|
||||
mouse.listen.world.btn.left.onclick.on(state.dreamcb);
|
||||
mouse.listen.world.btn.right.onclick.on(state.erasecb);
|
||||
mouse.listen.world.onmousemove.on(state.smousemovecb, 2, true);
|
||||
mouse.listen.world.onwheel.on(state.swheelcb, 2, true);
|
||||
mouse.listen.world.btn.left.onclick.on(state.sdreamcb, 2, true);
|
||||
mouse.listen.world.btn.right.onclick.on(state.serasecb, 2, true);
|
||||
mouse.listen.world.btn.middle.onclick.on(state.smiddlecb, 2, true);
|
||||
|
||||
// Clear Selection
|
||||
state.selected = null;
|
||||
state.selection.deselect();
|
||||
|
||||
// Display Mask
|
||||
setMask(state.invertMask ? "hold" : "clear");
|
||||
|
@ -1068,15 +1007,22 @@ const dreamTool = () =>
|
|||
mouse.listen.world.onmousemove.clear(state.mousemovecb);
|
||||
mouse.listen.world.onwheel.clear(state.wheelcb);
|
||||
|
||||
mouse.listen.world.btn.left.onclick.clear(state.dreamcb);
|
||||
mouse.listen.world.btn.right.onclick.clear(state.erasecb);
|
||||
|
||||
// Clear Select Region listeners
|
||||
mouse.listen.world.btn.left.ondragstart.clear(state.dragstartcb);
|
||||
mouse.listen.world.btn.left.ondrag.clear(state.dragcb);
|
||||
mouse.listen.world.btn.left.ondragend.clear(state.dragendcb);
|
||||
|
||||
mouse.listen.world.btn.left.onclick.clear(state.dreamcb);
|
||||
mouse.listen.world.btn.right.onclick.clear(state.erasecb);
|
||||
mouse.listen.world.onmousemove.clear(state.smousemovecb);
|
||||
mouse.listen.world.onwheel.clear(state.swheelcb);
|
||||
mouse.listen.world.btn.left.onclick.clear(state.sdreamcb);
|
||||
mouse.listen.world.btn.right.onclick.clear(state.serasecb);
|
||||
mouse.listen.world.btn.middle.onclick.clear(state.smiddlecb);
|
||||
|
||||
// Clear Selection
|
||||
state.selected = null;
|
||||
state.selection.deselect();
|
||||
|
||||
// Hide Mask
|
||||
setMask("none");
|
||||
|
@ -1104,32 +1050,60 @@ const dreamTool = () =>
|
|||
...mouse.coords.world.pos,
|
||||
};
|
||||
|
||||
state.dragstartcb = (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;
|
||||
state.selected = {start: {x, y}, now: {x, y}};
|
||||
/**
|
||||
* Selection handlers
|
||||
*/
|
||||
const selection = _tool._draggable_selection(state);
|
||||
state.dragstartcb = (evn) => selection.dragstartcb(evn);
|
||||
state.dragcb = (evn) => selection.dragcb(evn);
|
||||
state.dragendcb = (evn) => selection.dragendcb(evn);
|
||||
state.smousemovecb = (evn, estate) => {
|
||||
selection.smousemovecb(evn);
|
||||
if (selection.inside) {
|
||||
imageCollection.inputElement.style.cursor = "pointer";
|
||||
|
||||
estate.dream_processed = true;
|
||||
} else {
|
||||
imageCollection.inputElement.style.cursor = "auto";
|
||||
}
|
||||
};
|
||||
state.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;
|
||||
|
||||
state.selected.now = {x, y};
|
||||
};
|
||||
state.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;
|
||||
|
||||
state.selected.now = {x, y};
|
||||
|
||||
if (
|
||||
state.selected.start.x === state.selected.now.x ||
|
||||
state.selected.start.y === state.selected.now.y
|
||||
) {
|
||||
state.selected = null;
|
||||
state.redraw();
|
||||
state.swheelcb = (evn, estate) => {
|
||||
if (selection.inside) {
|
||||
state.wheelcb(evn, {});
|
||||
estate.dream_processed = true;
|
||||
}
|
||||
};
|
||||
|
||||
state.sdreamcb = (evn, estate) => {
|
||||
if (selection.exists && !selection.inside) {
|
||||
selection.deselect();
|
||||
state.redraw();
|
||||
estate.selection_processed = true;
|
||||
}
|
||||
if (selection.inside) {
|
||||
state.dreamcb(evn, {});
|
||||
estate.dream_processed = true;
|
||||
}
|
||||
};
|
||||
|
||||
state.serasecb = (evn, estate) => {
|
||||
if (selection.inside) {
|
||||
selection.deselect();
|
||||
state.redraw();
|
||||
estate.dream_processed = true;
|
||||
}
|
||||
};
|
||||
state.smiddlecb = (evn, estate) => {
|
||||
if (selection.inside) {
|
||||
estate.dream_processed = true;
|
||||
}
|
||||
};
|
||||
|
||||
state.selection = selection;
|
||||
|
||||
/**
|
||||
* Dream Handlers
|
||||
*/
|
||||
state.mousemovecb = (evn) => {
|
||||
state.lastMouseMove = evn;
|
||||
|
||||
|
@ -1143,36 +1117,10 @@ const dreamTool = () =>
|
|||
y += snap(evn.y, 0, 64);
|
||||
}
|
||||
|
||||
const vpc = viewport.canvasToView(x, y);
|
||||
state.erasePrevReticle = _tool._cursor_draw(x, y);
|
||||
|
||||
// Draw current cursor location
|
||||
uiCtx.lineWidth = 3;
|
||||
uiCtx.strokeStyle = "#FFF5";
|
||||
|
||||
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();
|
||||
state.eraseCursor = () => {
|
||||
uiCtx.clearRect(vpc.x - 15, vpc.y - 15, vpc.x + 30, vpc.y + 30);
|
||||
};
|
||||
|
||||
if (state.selected) {
|
||||
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);
|
||||
const maxx = Math.max(state.selected.now.x, state.selected.start.x);
|
||||
const maxy = Math.max(state.selected.now.y, state.selected.start.y);
|
||||
|
||||
bb.x = minx;
|
||||
bb.y = miny;
|
||||
bb.w = maxx - minx;
|
||||
bb.h = maxy - miny;
|
||||
|
||||
state.selected.bb = bb;
|
||||
if (state.selection.exists) {
|
||||
const bb = state.selection.bb;
|
||||
|
||||
const style =
|
||||
state.cursorSize > stableDiffusionData.width
|
||||
|
@ -1181,9 +1129,8 @@ const dreamTool = () =>
|
|||
? "#BFB5"
|
||||
: "#FFF5";
|
||||
|
||||
state.erasePrevReticle = _reticle_draw(
|
||||
state.erasePrevReticle = _tool._reticle_draw(
|
||||
bb,
|
||||
state,
|
||||
"Dream",
|
||||
{
|
||||
w: Math.round(
|
||||
|
@ -1194,6 +1141,7 @@ const dreamTool = () =>
|
|||
),
|
||||
},
|
||||
{
|
||||
reticleStyle: state.selection.inside ? "#F55" : "#FFF",
|
||||
sizeTextStyle: style,
|
||||
}
|
||||
);
|
||||
|
@ -1206,7 +1154,7 @@ const dreamTool = () =>
|
|||
: state.cursorSize < stableDiffusionData.width
|
||||
? "#BFB5"
|
||||
: "#FFF5";
|
||||
state.erasePrevReticle = _reticle_draw(
|
||||
state.erasePrevReticle = _tool._reticle_draw(
|
||||
getBoundingBox(
|
||||
evn.x,
|
||||
evn.y,
|
||||
|
@ -1214,7 +1162,6 @@ const dreamTool = () =>
|
|||
state.cursorSize,
|
||||
state.snapToGrid && basePixelCount
|
||||
),
|
||||
state,
|
||||
"Dream",
|
||||
{
|
||||
w: stableDiffusionData.width,
|
||||
|
@ -1235,9 +1182,9 @@ const dreamTool = () =>
|
|||
_dream_onwheel(evn, state);
|
||||
};
|
||||
state.dreamcb = (evn, estate) => {
|
||||
if (estate.dream_processed) return;
|
||||
if (estate.dream_processed || estate.selection_processed) return;
|
||||
const bb =
|
||||
(state.selected && state.selected.bb) ||
|
||||
state.selection.bb ||
|
||||
getBoundingBox(
|
||||
evn.x,
|
||||
evn.y,
|
||||
|
@ -1245,16 +1192,16 @@ const dreamTool = () =>
|
|||
state.cursorSize,
|
||||
state.snapToGrid && basePixelCount
|
||||
);
|
||||
const resolution = (state.selected && state.selected.bb) || {
|
||||
const resolution = state.selection.bb || {
|
||||
w: stableDiffusionData.width,
|
||||
h: stableDiffusionData.height,
|
||||
};
|
||||
dream_generate_callback(bb, resolution, state);
|
||||
state.selected = null;
|
||||
state.selection.deselect();
|
||||
};
|
||||
state.erasecb = (evn, estate) => {
|
||||
if (state.selected) {
|
||||
state.selected = null;
|
||||
if (state.selection.exists) {
|
||||
state.selection.deselect();
|
||||
state.redraw();
|
||||
return;
|
||||
}
|
||||
|
@ -1361,15 +1308,22 @@ const img2imgTool = () =>
|
|||
mouse.listen.world.onmousemove.on(state.mousemovecb);
|
||||
mouse.listen.world.onwheel.on(state.wheelcb);
|
||||
|
||||
mouse.listen.world.btn.left.onclick.on(state.dreamcb);
|
||||
mouse.listen.world.btn.right.onclick.on(state.erasecb);
|
||||
|
||||
// Select Region listeners
|
||||
mouse.listen.world.btn.left.ondragstart.on(state.dragstartcb);
|
||||
mouse.listen.world.btn.left.ondrag.on(state.dragcb);
|
||||
mouse.listen.world.btn.left.ondragend.on(state.dragendcb);
|
||||
|
||||
mouse.listen.world.btn.left.onclick.on(state.dreamcb);
|
||||
mouse.listen.world.btn.right.onclick.on(state.erasecb);
|
||||
mouse.listen.world.onmousemove.on(state.smousemovecb, 2, true);
|
||||
mouse.listen.world.onwheel.on(state.swheelcb, 2, true);
|
||||
mouse.listen.world.btn.left.onclick.on(state.sdreamcb, 2, true);
|
||||
mouse.listen.world.btn.right.onclick.on(state.serasecb, 2, true);
|
||||
mouse.listen.world.btn.middle.onclick.on(state.smiddlecb, 2, true);
|
||||
|
||||
// Clear Selection
|
||||
state.selected = null;
|
||||
state.selection.deselect();
|
||||
|
||||
// Display Mask
|
||||
setMask(state.invertMask ? "hold" : "clear");
|
||||
|
@ -1384,15 +1338,22 @@ const img2imgTool = () =>
|
|||
mouse.listen.world.onmousemove.clear(state.mousemovecb);
|
||||
mouse.listen.world.onwheel.clear(state.wheelcb);
|
||||
|
||||
mouse.listen.world.btn.left.onclick.clear(state.dreamcb);
|
||||
mouse.listen.world.btn.right.onclick.clear(state.erasecb);
|
||||
|
||||
// Clear Select Region listeners
|
||||
mouse.listen.world.btn.left.ondragstart.clear(state.dragstartcb);
|
||||
mouse.listen.world.btn.left.ondrag.clear(state.dragcb);
|
||||
mouse.listen.world.btn.left.ondragend.clear(state.dragendcb);
|
||||
|
||||
mouse.listen.world.btn.left.onclick.clear(state.dreamcb);
|
||||
mouse.listen.world.btn.right.onclick.clear(state.erasecb);
|
||||
mouse.listen.world.onmousemove.clear(state.smousemovecb);
|
||||
mouse.listen.world.onwheel.clear(state.swheelcb);
|
||||
mouse.listen.world.btn.left.onclick.clear(state.sdreamcb);
|
||||
mouse.listen.world.btn.right.onclick.clear(state.serasecb);
|
||||
mouse.listen.world.btn.middle.onclick.clear(state.smiddlecb);
|
||||
|
||||
// Clear Selection
|
||||
state.selected = null;
|
||||
state.selection.deselect();
|
||||
|
||||
// Hide mask
|
||||
setMask("none");
|
||||
|
@ -1424,33 +1385,59 @@ const img2imgTool = () =>
|
|||
...mouse.coords.world.pos,
|
||||
};
|
||||
|
||||
state.dragstartcb = (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;
|
||||
state.selected = {start: {x, y}, now: {x, y}};
|
||||
/**
|
||||
* Selection handlers
|
||||
*/
|
||||
const selection = _tool._draggable_selection(state);
|
||||
state.dragstartcb = (evn) => selection.dragstartcb(evn);
|
||||
state.dragcb = (evn) => selection.dragcb(evn);
|
||||
state.dragendcb = (evn) => selection.dragendcb(evn);
|
||||
state.smousemovecb = (evn, estate) => {
|
||||
selection.smousemovecb(evn);
|
||||
if (selection.inside) {
|
||||
imageCollection.inputElement.style.cursor = "pointer";
|
||||
|
||||
estate.dream_processed = true;
|
||||
} else {
|
||||
imageCollection.inputElement.style.cursor = "auto";
|
||||
}
|
||||
};
|
||||
state.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;
|
||||
|
||||
state.selected.now = {x, y};
|
||||
};
|
||||
|
||||
state.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;
|
||||
|
||||
state.selected.now = {x, y};
|
||||
|
||||
if (
|
||||
state.selected.start.x === state.selected.now.x ||
|
||||
state.selected.start.y === state.selected.now.y
|
||||
) {
|
||||
state.selected = null;
|
||||
state.redraw();
|
||||
state.swheelcb = (evn, estate) => {
|
||||
if (selection.inside) {
|
||||
state.wheelcb(evn, {});
|
||||
estate.dream_processed = true;
|
||||
}
|
||||
};
|
||||
|
||||
state.sdreamcb = (evn, estate) => {
|
||||
if (selection.exists && !selection.inside) {
|
||||
selection.deselect();
|
||||
estate.selection_processed = true;
|
||||
}
|
||||
if (selection.inside) {
|
||||
state.dreamcb(evn, {});
|
||||
estate.dream_processed = true;
|
||||
}
|
||||
};
|
||||
|
||||
state.serasecb = (evn, estate) => {
|
||||
if (selection.inside) {
|
||||
state.erasecb(evn, {});
|
||||
estate.dream_processed = true;
|
||||
}
|
||||
};
|
||||
|
||||
state.smiddlecb = (evn, estate) => {
|
||||
if (selection.inside) {
|
||||
estate.dream_processed = true;
|
||||
}
|
||||
};
|
||||
|
||||
state.selection = selection;
|
||||
|
||||
/**
|
||||
* Dream handlers
|
||||
*/
|
||||
state.mousemovecb = (evn) => {
|
||||
state.lastMouseMove = evn;
|
||||
|
||||
|
@ -1464,46 +1451,25 @@ const img2imgTool = () =>
|
|||
y += snap(evn.y, 0, 64);
|
||||
}
|
||||
|
||||
const vpc = viewport.canvasToView(x, y);
|
||||
|
||||
// Draw current cursor location
|
||||
uiCtx.lineWidth = 3;
|
||||
uiCtx.strokeStyle = "#FFF5";
|
||||
|
||||
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();
|
||||
state.eraseCursor = () => {
|
||||
uiCtx.clearRect(vpc.x - 15, vpc.y - 15, vpc.x + 30, vpc.y + 30);
|
||||
};
|
||||
state.erasePrevReticle = _tool._cursor_draw(x, y);
|
||||
|
||||
// Resolution
|
||||
let bb = null;
|
||||
let request = null;
|
||||
|
||||
if (state.selected) {
|
||||
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);
|
||||
const maxx = Math.max(state.selected.now.x, state.selected.start.x);
|
||||
const maxy = Math.max(state.selected.now.y, state.selected.start.y);
|
||||
|
||||
bb.x = minx;
|
||||
bb.y = miny;
|
||||
bb.w = maxx - minx;
|
||||
bb.h = maxy - miny;
|
||||
|
||||
state.selected.bb = bb;
|
||||
if (state.selection.exists) {
|
||||
bb = state.selection.bb;
|
||||
|
||||
request = {width: bb.w, height: bb.h};
|
||||
|
||||
state.erasePrevReticle = _reticle_draw(
|
||||
const style =
|
||||
state.cursorSize > stableDiffusionData.width
|
||||
? "#FBB5"
|
||||
: state.cursorSize < stableDiffusionData.width
|
||||
? "#BFB5"
|
||||
: "#FFF5";
|
||||
state.erasePrevReticle = _tool._reticle_draw(
|
||||
bb,
|
||||
state,
|
||||
"Img2Img",
|
||||
{
|
||||
w: Math.round(
|
||||
|
@ -1514,6 +1480,7 @@ const img2imgTool = () =>
|
|||
),
|
||||
},
|
||||
{
|
||||
reticleStyle: state.selection.inside ? "#F55" : "#FFF",
|
||||
sizeTextStyle: style,
|
||||
}
|
||||
);
|
||||
|
@ -1537,9 +1504,8 @@ const img2imgTool = () =>
|
|||
: state.cursorSize < stableDiffusionData.width
|
||||
? "#BFB5"
|
||||
: "#FFF5";
|
||||
state.erasePrevReticle = _reticle_draw(
|
||||
state.erasePrevReticle = _tool._reticle_draw(
|
||||
bb,
|
||||
state,
|
||||
"Img2Img",
|
||||
{w: request.width, h: request.height},
|
||||
{
|
||||
|
@ -1549,9 +1515,11 @@ const img2imgTool = () =>
|
|||
}
|
||||
|
||||
if (
|
||||
state.selected &&
|
||||
(state.selected.now.x === state.selected.start.x ||
|
||||
state.selected.now.y === state.selected.start.y)
|
||||
state.selection.exists &&
|
||||
(state.selection.selected.now.x ===
|
||||
state.selection.selected.start.x ||
|
||||
state.selection.selected.now.y ===
|
||||
state.selection.selected.start.y)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
@ -1651,9 +1619,9 @@ const img2imgTool = () =>
|
|||
_dream_onwheel(evn, state);
|
||||
};
|
||||
state.dreamcb = (evn, estate) => {
|
||||
if (estate.dream_processed) return;
|
||||
if (estate.dream_processed || estate.selection_processed) return;
|
||||
const bb =
|
||||
(state.selected && state.selected.bb) ||
|
||||
state.selection.bb ||
|
||||
getBoundingBox(
|
||||
evn.x,
|
||||
evn.y,
|
||||
|
@ -1661,18 +1629,18 @@ const img2imgTool = () =>
|
|||
state.cursorSize,
|
||||
state.snapToGrid && basePixelCount
|
||||
);
|
||||
const resolution = (state.selected && state.selected.bb) || {
|
||||
const resolution = state.selection.bb || {
|
||||
w: stableDiffusionData.width,
|
||||
h: stableDiffusionData.height,
|
||||
};
|
||||
dream_img2img_callback(bb, resolution, state);
|
||||
state.selected = null;
|
||||
state.selection.deselect();
|
||||
state.redraw();
|
||||
};
|
||||
state.erasecb = (evn, estate) => {
|
||||
if (estate.dream_processed) return;
|
||||
if (state.selected) {
|
||||
state.selected = null;
|
||||
if (state.selection.exists) {
|
||||
state.selection.deselect();
|
||||
state.redraw();
|
||||
return;
|
||||
}
|
||||
|
|
276
js/ui/tool/generic.js
Normal file
276
js/ui/tool/generic.js
Normal 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;
|
||||
},
|
||||
};
|
Loading…
Reference in a new issue