Merge branch 'transform-vp' into rotate

Signed-off-by: Victor Seiji Hariki <victorseijih@gmail.com>
This commit is contained in:
Victor Seiji Hariki 2023-01-10 01:08:48 -03:00
commit 8d88ca6632
9 changed files with 210 additions and 121 deletions

View file

@ -25,6 +25,7 @@
.layer-render-target .collection { .layer-render-target .collection {
position: absolute; position: absolute;
transform-origin: 0px 0px;
} }
.layer-render-target .collection > .collection-input-overlay { .layer-render-target .collection > .collection-input-overlay {

View file

@ -8,7 +8,7 @@
<link href="css/icons.css?v=caa702e" rel="stylesheet" /> <link href="css/icons.css?v=caa702e" rel="stylesheet" />
<link href="css/index.css?v=5b8d4d6" rel="stylesheet" /> <link href="css/index.css?v=5b8d4d6" rel="stylesheet" />
<link href="css/layers.css?v=a94b43d" rel="stylesheet" /> <link href="css/layers.css?v=92c0352" rel="stylesheet" />
<link href="css/ui/generic.css?v=4b9afe2" rel="stylesheet" /> <link href="css/ui/generic.css?v=4b9afe2" rel="stylesheet" />
@ -49,6 +49,7 @@
</div> </div>
</div> </div>
</label> </label>
<!-- Prompts section --> <!-- Prompts section -->
<button type="button" class="collapsible">Prompts</button> <button type="button" class="collapsible">Prompts</button>
<div class="content prompt"> <div class="content prompt">
@ -332,15 +333,15 @@
<div class="ui separator"></div> <div class="ui separator"></div>
<iframe <iframe
id="page-overlay" id="page-overlay"
src="pages/configuration.html?v=3d710ce"></iframe> src="pages/configuration.html?v=7fca00b"></iframe>
</div> </div>
</div> </div>
<!-- Basics --> <!-- Basics -->
<script src="js/global.js?v=3a1cde6" type="text/javascript"></script> <script src="js/global.js?v=5ee46bd" type="text/javascript"></script>
<!-- Base Libs --> <!-- Base Libs -->
<script src="js/lib/util.js?v=c7cdf59" type="text/javascript"></script> <script src="js/lib/util.js?v=25f0dbb" type="text/javascript"></script>
<script src="js/lib/events.js?v=2ab7933" type="text/javascript"></script> <script src="js/lib/events.js?v=2ab7933" type="text/javascript"></script>
<script src="js/lib/input.js?v=09298ac" type="text/javascript"></script> <script src="js/lib/input.js?v=09298ac" type="text/javascript"></script>
<script src="js/lib/layers.js?v=a1f8aea" type="text/javascript"></script> <script src="js/lib/layers.js?v=a1f8aea" type="text/javascript"></script>
@ -350,7 +351,7 @@
<script src="js/lib/ui.js?v=76ede2b" type="text/javascript"></script> <script src="js/lib/ui.js?v=76ede2b" type="text/javascript"></script>
<script <script
src="js/initalize/layers.populate.js?v=c81f0a5" src="js/initalize/layers.populate.js?v=938a1b2"
type="text/javascript"></script> type="text/javascript"></script>
<!-- Configuration --> <!-- Configuration -->
@ -358,7 +359,7 @@
<!-- Content --> <!-- Content -->
<script src="js/prompt.js?v=7a1c68c" type="text/javascript"></script> <script src="js/prompt.js?v=7a1c68c" type="text/javascript"></script>
<script src="js/index.js?v=c6539e5" type="text/javascript"></script> <script src="js/index.js?v=b446b93" type="text/javascript"></script>
<script <script
src="js/ui/floating/history.js?v=fc92d14" src="js/ui/floating/history.js?v=fc92d14"
@ -369,15 +370,15 @@
<!-- Load Tools --> <!-- Load Tools -->
<script <script
src="js/ui/tool/generic.js?v=11c9556" src="js/ui/tool/generic.js?v=cd4ae12"
type="text/javascript"></script> type="text/javascript"></script>
<script src="js/ui/tool/dream.js?v=18e3b66" type="text/javascript"></script> <script src="js/ui/tool/dream.js?v=76bc565" type="text/javascript"></script>
<script <script
src="js/ui/tool/maskbrush.js?v=1e8a893" src="js/ui/tool/maskbrush.js?v=1e8a893"
type="text/javascript"></script> type="text/javascript"></script>
<script <script
src="js/ui/tool/colorbrush.js?v=8acb4f6" src="js/ui/tool/colorbrush.js?v=3f8c01a"
type="text/javascript"></script> type="text/javascript"></script>
<script <script
src="js/ui/tool/select.js?v=7db1d0c" src="js/ui/tool/select.js?v=7db1d0c"

View file

@ -143,6 +143,7 @@ var stableDiffusionData = {
var host = ""; var host = "";
var url = "/sdapi/v1/"; var url = "/sdapi/v1/";
const basePixelCount = 64; //64 px - ALWAYS 64 PX const basePixelCount = 64; //64 px - ALWAYS 64 PX
var focused = true;
function startup() { function startup() {
testHostConfiguration(); testHostConfiguration();
@ -168,6 +169,7 @@ function startup() {
changeHiResSquare(); changeHiResSquare();
changeRestoreFaces(); changeRestoreFaces();
changeSyncCursorSize(); changeSyncCursorSize();
checkFocus();
} }
function setFixedHost(h, changePromptMessage) { function setFixedHost(h, changePromptMessage) {
@ -335,7 +337,23 @@ async function testHostConnection() {
let checkInProgress = false; let checkInProgress = false;
const checkConnection = async (notify = false) => { const checkConnection = async (
notify = false,
simpleProgressStatus = false
) => {
const apiIssueResult = () => {
setConnectionStatus("apiissue");
const message = `The host is online, but the API seems to be disabled.\nHave you run the webui with the flag '--api', or is the flag '--gradio-debug' currently active?`;
console.error(message);
if (notify) alert(message);
};
const offlineResult = () => {
setConnectionStatus("offline");
const message = `The connection with the host returned an error: ${response.status} - ${response.statusText}`;
console.error(message);
if (notify) alert(message);
};
if (checkInProgress) if (checkInProgress)
throw new CheckInProgressError( throw new CheckInProgressError(
"Check is currently in progress, please try again" "Check is currently in progress, please try again"
@ -344,6 +362,24 @@ async function testHostConnection() {
var url = document.getElementById("host").value + "/startup-events"; var url = document.getElementById("host").value + "/startup-events";
// Attempt normal request // Attempt normal request
try { try {
if (simpleProgressStatus) {
const response = await fetch(
document.getElementById("host").value + "/sdapi/v1/progress" // seems to be the "lightest" endpoint?
);
switch (response.status) {
case 200: {
setConnectionStatus("online");
break;
}
case 404: {
apiIssueResult();
break;
}
default: {
offlineResult();
}
}
} else {
// Check if API is available // Check if API is available
const response = await fetch( const response = await fetch(
document.getElementById("host").value + "/sdapi/v1/options" document.getElementById("host").value + "/sdapi/v1/options"
@ -377,17 +413,12 @@ async function testHostConnection() {
break; break;
} }
case 404: { case 404: {
setConnectionStatus("apiissue"); apiIssueResult();
const message = `The host is online, but the API seems to be disabled.\nHave you run the webui with the flag '--api', or is the flag '--gradio-debug' currently active?`;
console.error(message);
if (notify) alert(message);
break; break;
} }
default: { default: {
setConnectionStatus("offline"); offlineResult();
const message = `The connection with the host returned an error: ${response.status} - ${response.statusText}`; }
console.error(message);
if (notify) alert(message);
} }
} }
} catch (e) { } catch (e) {
@ -413,7 +444,9 @@ async function testHostConnection() {
return status; return status;
}; };
if (focused || firstTimeOnline) {
await checkConnection(!urlParams.has("noprompt")); await checkConnection(!urlParams.has("noprompt"));
}
// On click, attempt to refresh // On click, attempt to refresh
connectionIndicator.onclick = async () => { connectionIndicator.onclick = async () => {
@ -425,15 +458,23 @@ async function testHostConnection() {
} }
}; };
// Checks every 5 seconds if offline, 30 seconds if online // Checks every 5 seconds if offline, 60 seconds if online
const checkAgain = () => { const checkAgain = () => {
checkFocus();
if (focused || firstTimeOnline) {
setTimeout( setTimeout(
async () => { async () => {
await checkConnection(); let simple = !firstTimeOnline;
await checkConnection(false, simple);
checkAgain(); checkAgain();
}, },
connectionStatus ? 30000 : 5000 connectionStatus ? 60000 : 5000
); );
} else {
setTimeout(() => {
checkAgain();
}, 60000);
}
}; };
checkAgain(); checkAgain();
@ -1242,3 +1283,24 @@ function resetToDefaults() {
localStorage.clear(); localStorage.clear();
} }
} }
document.addEventListener("visibilitychange", () => {
checkFocus();
});
window.addEventListener("blur", () => {
checkFocus();
});
window.addEventListener("focus", () => {
checkFocus();
});
function checkFocus() {
let hasFocus = document.hasFocus();
if (document.hidden || !hasFocus) {
focused = false;
} else {
focused = true;
}
}

View file

@ -160,61 +160,72 @@ debugLayer.hide(); // Hidden by default
* The global viewport object (may be modularized in the future). All * The global viewport object (may be modularized in the future). All
* coordinates given are of the center of the viewport * coordinates given are of the center of the viewport
* *
* cx and cy are the viewport's world coordinates, scaled to zoom level. * cx and cy are the viewport's world coordinates.
* _x and _y are actual coordinates in the DOM space
* *
* The transform() function does some transforms and writes them to the * The transform() function does some transforms and writes them to the
* provided element. * provided element.
*/ */
const viewport = { class Viewport {
get cx() { cx = 0;
return this._x * this.zoom; cy = 0;
},
set cx(v) { zoom = 1;
return (this._x = v / this.zoom);
}, /**
_x: 0, * Gets viewport width in canvas coordinates
get cy() { */
return this._y * this.zoom;
},
set cy(v) {
return (this._y = v / this.zoom);
},
_y: 0,
zoom: 1,
rotation: 0,
get w() { get w() {
return (window.innerWidth * 1) / this.zoom; return window.innerWidth * this.zoom;
}, }
/**
* Gets viewport height in canvas coordinates
*/
get h() { get h() {
return (window.innerHeight * 1) / this.zoom; return window.innerHeight * this.zoom;
}, }
constructor(x, y) {
this.x = x;
this.y = y;
}
get v2c() {
const m = new DOMMatrix();
m.translateSelf(-this.w / 2, -this.h / 2);
m.translateSelf(this.cx, this.cy);
m.scaleSelf(this.zoom);
return m;
}
get c2v() {
return this.v2c.invertSelf();
}
viewToCanvas(x, y) { viewToCanvas(x, y) {
return { if (x.x !== undefined) return this.v2c.transformPoint(x);
x: this.cx + this.w * (x / window.innerWidth - 0.5), return this.v2c.transformPoint({x, y});
y: this.cy + this.h * (y / window.innerHeight - 0.5), }
};
},
canvasToView(x, y) { canvasToView(x, y) {
return { if (x.x !== undefined) return this.c2v.transformPoint(x);
x: window.innerWidth * ((x - this.cx) / this.w) + window.innerWidth / 2, return this.c2v.transformPoint({x, y});
y: window.innerHeight * ((y - this.cy) / this.h) + window.innerHeight / 2, }
};
},
/** /**
* Apply transformation * Apply transformation
* *
* @param {HTMLElement} el Element to apply CSS transform to * @param {HTMLElement} el Element to apply CSS transform to
*/ */
transform(el) { transform(el) {
el.style.transformOrigin = `${this.cx}px ${this.cy}px`; el.style.transformOrigin = "0px 0px";
el.style.transform = `scale(${this.zoom}) translate(${-( el.style.transform = this.c2v;
this._x - }
this.w / 2 }
)}px, ${-(this._y - this.h / 2)}px)`;
}, const viewport = new Viewport(0, 0);
};
viewport.cx = imageCollection.size.w / 2; viewport.cx = imageCollection.size.w / 2;
viewport.cy = imageCollection.size.h / 2; viewport.cy = imageCollection.size.h / 2;
@ -296,7 +307,7 @@ mouse.listen.camera.onwheel.on((evn) => {
const pcy = viewport.cy; const pcy = viewport.cy;
// Apply zoom // Apply zoom
viewport.zoom *= 1 - evn.delta * 0.0002; viewport.zoom *= 1 + evn.delta * 0.0002;
// Apply normal zoom (center of viewport) // Apply normal zoom (center of viewport)
viewport.cx = pcx; viewport.cx = pcx;
@ -305,11 +316,11 @@ mouse.listen.camera.onwheel.on((evn) => {
viewport.transform(imageCollection.element); viewport.transform(imageCollection.element);
// Calculate new viewport center and move // Calculate new viewport center and move
const newCursorPosition = viewport.viewToCanvas(evn.x, evn.y); //const newCursorPosition = viewport.viewToCanvas(evn.x, evn.y);
viewport.cx = pcx - (newCursorPosition.x - cursorPosition.x); //viewport.cx = pcx - (newCursorPosition.x - cursorPosition.x);
viewport.cy = pcy - (newCursorPosition.y - cursorPosition.y); //viewport.cy = pcy - (newCursorPosition.y - cursorPosition.y);
viewport.transform(imageCollection.element); //viewport.transform(imageCollection.element);
toolbar.currentTool.redraw(); toolbar.currentTool.redraw();
}); });
@ -320,8 +331,8 @@ const cameraPaintStart = (evn) => {
const cameraPaint = (evn) => { const cameraPaint = (evn) => {
if (worldInit) { if (worldInit) {
viewport.cx = worldInit.x + (evn.ix - evn.x) / viewport.zoom; viewport.cx = worldInit.x + (evn.ix - evn.x) * viewport.zoom;
viewport.cy = worldInit.y + (evn.iy - evn.y) / viewport.zoom; viewport.cy = worldInit.y + (evn.iy - evn.y) * viewport.zoom;
// Limits // Limits
viewport.cx = Math.max( viewport.cx = Math.max(

View file

@ -32,6 +32,16 @@ class BoundingBox {
w = 0; w = 0;
h = 0; h = 0;
/** @type {Point} */
get tl() {
return {x: this.x, y: this.y};
}
/** @type {Point} */
get br() {
return {x: this.x + this.w, y: this.y + this.h};
}
constructor({x, y, w, h} = {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.x = x;
this.y = y; this.y = y;

View file

@ -176,7 +176,7 @@ const colorBrushTool = () =>
uiCtx.arc( uiCtx.arc(
vcp.x, vcp.x,
vcp.y, vcp.y,
(state.eyedropper ? 50 : state.brushSize / 2) * viewport.zoom, (state.eyedropper ? 50 : state.brushSize / 2) / viewport.zoom,
0, 0,
2 * Math.PI, 2 * Math.PI,
true true
@ -197,7 +197,7 @@ const colorBrushTool = () =>
uiCtx.arc( uiCtx.arc(
vcp.x, vcp.x,
vcp.y, vcp.y,
(state.brushSize / 2) * viewport.zoom, state.brushSize / (2 * viewport.zoom),
0, 0,
2 * Math.PI, 2 * Math.PI,
true true

View file

@ -177,6 +177,7 @@ const _dream = async (endpoint, request) => {
* @returns {Promise<HTMLImageElement | null>} * @returns {Promise<HTMLImageElement | null>}
*/ */
const _generate = async (endpoint, request, bb, options = {}) => { const _generate = async (endpoint, request, bb, options = {}) => {
var alertCount = 0;
defaultOpt(options, { defaultOpt(options, {
drawEvery: 0.2 / request.n_iter, drawEvery: 0.2 / request.n_iter,
keepUnmask: null, keepUnmask: null,
@ -532,9 +533,14 @@ const _generate = async (endpoint, request, bb, options = {}) => {
seeds.push(...dreamData.seeds); seeds.push(...dreamData.seeds);
imageindextxt.textContent = `${at}/${images.length - 1}`; imageindextxt.textContent = `${at}/${images.length - 1}`;
} catch (e) { } catch (e) {
if (alertCount < 2) {
alert( alert(
`Error generating images. Please try again or see console for more details` `Error generating images. Please try again or see console for more details`
); );
} else {
eagerGenerateCount = 0;
}
alertCount++;
console.warn(`[dream] Error generating images:`); console.warn(`[dream] Error generating images:`);
console.warn(e); console.warn(e);
} finally { } finally {
@ -1912,11 +1918,10 @@ const img2imgTool = () =>
return; return;
} }
const bbvp = { const bbvp = BoundingBox.fromStartEnd(
...viewport.canvasToView(bb.x, bb.y), viewport.canvasToView(bb.tl),
w: viewport.zoom * bb.w, viewport.canvasToView(bb.br)
h: viewport.zoom * bb.h, );
};
// For displaying border mask // For displaying border mask
const bbCanvas = document.createElement("canvas"); const bbCanvas = document.createElement("canvas");

View file

@ -27,11 +27,10 @@ const _tool = {
reticleStyle: global.hasActiveInput ? "#BBF" : "#FFF", reticleStyle: global.hasActiveInput ? "#BBF" : "#FFF",
}); });
const bbvp = { const bbvp = BoundingBox.fromStartEnd(
...viewport.canvasToView(bb.x, bb.y), viewport.canvasToView(bb.tl),
w: viewport.zoom * bb.w, viewport.canvasToView(bb.br)
h: viewport.zoom * bb.h, );
};
uiCtx.save(); uiCtx.save();

View file

@ -8,7 +8,7 @@
<link href="../css/icons.css?v=caa702e" rel="stylesheet" /> <link href="../css/icons.css?v=caa702e" rel="stylesheet" />
<link href="../css/index.css?v=5b8d4d6" rel="stylesheet" /> <link href="../css/index.css?v=5b8d4d6" rel="stylesheet" />
<link href="../css/layers.css?v=a94b43d" rel="stylesheet" /> <link href="../css/layers.css?v=92c0352" rel="stylesheet" />
<link href="../css/ui/generic.css?v=4b9afe2" rel="stylesheet" /> <link href="../css/ui/generic.css?v=4b9afe2" rel="stylesheet" />