diff --git a/css/layers.css b/css/layers.css index 434e77e..32c2420 100644 --- a/css/layers.css +++ b/css/layers.css @@ -28,12 +28,18 @@ transform-origin: 0px 0px; } -.layer-render-target .collection > .collection-input-overlay { +.layer-render-target .collection > .collection-div { position: absolute; top: 0; left: 0; +} + +.layer-render-target .collection > .collection-underlay { + z-index: -10; +} +.layer-render-target .collection > .collection-overlay { z-index: 10; } diff --git a/css/ui/layers.css b/css/ui/layers.css index 8adc44c..49f5fe1 100644 --- a/css/ui/layers.css +++ b/css/ui/layers.css @@ -144,7 +144,7 @@ padding: 0; border: 0; - background-color: transparent; + background-color: #4667; cursor: pointer; @@ -154,6 +154,10 @@ border: 2px solid #293d3d30; } +.expand-button:hover { + background-color: #293d3d77; +} + .expand-button::after { content: ""; @@ -200,7 +204,3 @@ border-bottom-right-radius: 10px; border-bottom-left-radius: 10px; } - -.expand-button:hover { - background-color: #293d3d77; -} diff --git a/index.html b/index.html index ee7286f..7308e4d 100644 --- a/index.html +++ b/index.html @@ -8,12 +8,12 @@ - + - + @@ -340,7 +340,7 @@
+ src="pages/configuration.html?v=e4d660f"> @@ -348,26 +348,26 @@ - + - + - + - + - + diff --git a/js/index.js b/js/index.js index f8797c0..88eca30 100644 --- a/js/index.js +++ b/js/index.js @@ -821,6 +821,19 @@ function isCanvasBlank(x, y, w, h, canvas) { .data.some((channel) => channel !== 0); } +function recalculateBg() { + bgLayer.canvas.style.backgroundPosition = `${-snap( + imageCollection.origin.x, + 0, + config.gridSize * 2 + )}px ${-snap(imageCollection.origin.y, 0, config.gridSize * 2)}px`; + imageCollection.bgElement.style.backgroundPosition = `${-snap( + -imageCollection.divOffset.x, + 0, + config.gridSize * 2 + )}px ${-snap(-imageCollection.divOffset.y, 0, config.gridSize * 2)}px`; +} + function drawBackground() { { // Existing Canvas BG @@ -842,10 +855,33 @@ function drawBackground() { canvas.toBlob((blob) => { const url = window.URL.createObjectURL(blob); - console.debug(url); bgLayer.canvas.style.backgroundImage = `url(${url})`; }); } + + { + // External Canvas BG + const canvas = document.createElement("canvas"); + canvas.width = config.gridSize * 2; + canvas.height = config.gridSize * 2; + + const ctx = canvas.getContext("2d"); + ctx.fillStyle = theme.grid.extDark; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.fillStyle = theme.grid.extLight; + ctx.fillRect(0, 0, config.gridSize, config.gridSize); + ctx.fillRect( + config.gridSize, + config.gridSize, + config.gridSize, + config.gridSize + ); + + canvas.toBlob((blob) => { + const url = window.URL.createObjectURL(blob); + imageCollection.bgElement.style.backgroundImage = `url(${url})`; + }); + } return; // Checkerboard diff --git a/js/initalize/layers.populate.js b/js/initalize/layers.populate.js index be63ef6..8244e2e 100644 --- a/js/initalize/layers.populate.js +++ b/js/initalize/layers.populate.js @@ -65,101 +65,6 @@ uiCanvas.width = uiCanvas.clientWidth; uiCanvas.height = uiCanvas.clientHeight; const uiCtx = uiCanvas.getContext("2d", {desynchronized: true}); -/** - * Here we setup canvas dynamic scaling - */ -(() => { - let expandSize = localStorage.getItem("openoutpaint/expand-size") || 1024; - expandSize = parseInt(expandSize, 10); - - const askSize = (e) => { - if (e.ctrlKey) return expandSize; - const by = prompt("How much do you want to expand by?", expandSize); - - if (!by) return null; - else { - const len = parseInt(by, 10); - localStorage.setItem("openoutpaint/expand-size", len); - expandSize = len; - return len; - } - }; - - const leftButton = makeElement("button", -64, 0); - leftButton.classList.add("expand-button", "left"); - leftButton.style.width = "64px"; - leftButton.style.height = `${imageCollection.size.h}px`; - leftButton.addEventListener("click", (e) => { - let size = null; - if ((size = askSize(e))) { - imageCollection.expand(size, 0, 0, 0); - bgLayer.canvas.style.backgroundPosition = `${-snap( - imageCollection.origin.x, - 0, - config.gridSize * 2 - )}px ${-snap(imageCollection.origin.y, 0, config.gridSize * 2)}px`; - const newLeft = -imageCollection.inputOffset.x - imageCollection.origin.x; - leftButton.style.left = newLeft - 64 + "px"; - topButton.style.left = newLeft + "px"; - bottomButton.style.left = newLeft + "px"; - topButton.style.width = imageCollection.size.w + "px"; - bottomButton.style.width = imageCollection.size.w + "px"; - } - }); - - const rightButton = makeElement("button", imageCollection.size.w, 0); - rightButton.classList.add("expand-button", "right"); - rightButton.style.width = "64px"; - rightButton.style.height = `${imageCollection.size.h}px`; - rightButton.addEventListener("click", (e) => { - let size = null; - if ((size = askSize(e))) { - imageCollection.expand(0, 0, size, 0); - rightButton.style.left = - parseInt(rightButton.style.left, 10) + size + "px"; - topButton.style.width = imageCollection.size.w + "px"; - bottomButton.style.width = imageCollection.size.w + "px"; - } - }); - - const topButton = makeElement("button", 0, -64); - topButton.classList.add("expand-button", "top"); - topButton.style.height = "64px"; - topButton.style.width = `${imageCollection.size.w}px`; - topButton.addEventListener("click", (e) => { - let size = null; - if ((size = askSize(e))) { - imageCollection.expand(0, size, 0, 0); - bgLayer.canvas.style.backgroundPosition = `${-snap( - imageCollection.origin.x, - 0, - config.gridSize * 2 - )}px ${-snap(imageCollection.origin.y, 0, config.gridSize * 2)}px`; - const newTop = -imageCollection.inputOffset.y - imageCollection.origin.y; - topButton.style.top = newTop - 64 + "px"; - leftButton.style.top = newTop + "px"; - rightButton.style.top = newTop + "px"; - leftButton.style.height = imageCollection.size.h + "px"; - rightButton.style.height = imageCollection.size.h + "px"; - } - }); - - const bottomButton = makeElement("button", 0, imageCollection.size.h); - bottomButton.classList.add("expand-button", "bottom"); - bottomButton.style.height = "64px"; - bottomButton.style.width = `${imageCollection.size.w}px`; - bottomButton.addEventListener("click", (e) => { - let size = null; - if ((size = askSize(e))) { - imageCollection.expand(0, 0, 0, size); - bottomButton.style.top = - parseInt(bottomButton.style.top, 10) + size + "px"; - leftButton.style.height = imageCollection.size.h + "px"; - rightButton.style.height = imageCollection.size.h + "px"; - } - }); -})(); - debugLayer.hide(); // Hidden by default // Where CSS and javascript magic happens to make the canvas viewport work diff --git a/js/lib/layers.d.js b/js/lib/layers.d.js index b847fd7..43b35d4 100644 --- a/js/lib/layers.d.js +++ b/js/lib/layers.d.js @@ -35,7 +35,7 @@ * @property {string} name The display name of the collection * @property {HTMLDivElement} element The base element of the collection * @property {HTMLDivElement} inputElement The element used for input handling for the collection - * @property {Point} inputOffset The offset for calculating layer coordinates from input element input information + * @property {Point} divOffset The offset for calculating layer coordinates from input element input information * @property {Point} origin The location of the origin ((0, 0) point) of the collection (If canvas goes from -64, -32 to 128, 512, it's (64, 32)) * @property {BoundingBox} bb The current bounding box of the collection, in layer coordinates * @property {{[key: string]: Layer}} layers An object for quick access to named layers of the collection diff --git a/js/lib/layers.js b/js/lib/layers.js index 3c513cd..96ea9c0 100644 --- a/js/lib/layers.js +++ b/js/lib/layers.js @@ -180,7 +180,7 @@ const layers = { * @param {object} options Extra options for the collection * @param {string} [options.name=key] The display name of the collection * @param {{key: string, options: object}} [options.initLayer] The configuration for the initial layer to be created - * @param {number} [options.inputSizeMultiplier=9] Size of the input area element, in pixels + * @param {number} [options.divSizeMultiplier=9] Size of the input area element, in pixels * @param {HTMLElement} [options.targetElement] Element the collection will be inserted into * @param {Size} [options.resolution=size] The resolution of the collection (canvas size). Not sure it works. * @returns {LayerCollection} The newly created layer collection @@ -197,7 +197,7 @@ const layers = { }, // Input multiplier (Size of the input element div) - inputSizeMultiplier: 9, + divSizeMultiplier: 9, // Target targetElement: document.getElementById("layer-render"), @@ -206,7 +206,7 @@ const layers = { resolution: size, }); - if (options.inputSizeMultiplier % 2 === 0) options.inputSizeMultiplier++; + if (options.divSizeMultiplier % 2 === 0) options.divSizeMultiplier++; // Path used for logging purposes const _logpath = "layers.collections." + key; @@ -221,10 +221,24 @@ const layers = { element.style.height = `${size.h}px`; element.classList.add("collection"); + // Background element (element for background drawing) + const bgel = document.createElement("div"); + bgel.id = `collection-bg-${id}`; + bgel.classList.add( + "collection-div", + "collection-underlay", + "collection-bg" + ); + element.appendChild(bgel); + // Input element (overlay element for input handling) const inputel = document.createElement("div"); inputel.id = `collection-input-${id}`; - inputel.classList.add("collection-input-overlay"); + inputel.classList.add( + "collection-div", + "collection-overlay", + "collection-input-overlay" + ); element.appendChild(inputel); options.targetElement.appendChild(element); @@ -242,10 +256,13 @@ const layers = { key, name: options.name, element, + + bgElement: bgel, inputElement: inputel, - _inputOffset: null, - get inputOffset() { - return this._inputOffset; + + _divOffset: null, + get divOffset() { + return this._divOffset; }, _origin: {x: 0, y: 0}, @@ -264,38 +281,56 @@ const layers = { _resizeInputDiv() { // Set offset - const oldOffset = {...this._inputOffset}; - this._inputOffset = { + const oldOffset = {...this._divOffset}; + this._divOffset = { x: - -Math.floor(options.inputSizeMultiplier / 2) * size.w - + -Math.floor(options.divSizeMultiplier / 2) * size.w - this._origin.x, y: - -Math.floor(options.inputSizeMultiplier / 2) * size.h - + -Math.floor(options.divSizeMultiplier / 2) * size.h - this._origin.y, }; + // Resize the background element + this.bgElement.style.left = `${this.divOffset.x}px`; + this.bgElement.style.top = `${this.divOffset.y}px`; + this.bgElement.style.width = `${ + size.w * options.divSizeMultiplier + }px`; + this.bgElement.style.height = `${ + size.h * options.divSizeMultiplier + }px`; + + // Move elements inside to new offset + for (const child of this.bgElement.children) { + if (child.style.position === "absolute") { + child.style.left = `${ + parseInt(child.style.left, 10) + oldOffset.x - this._divOffset.x + }px`; + child.style.top = `${ + parseInt(child.style.top, 10) + oldOffset.y - this._divOffset.y + }px`; + } + } + // Resize the input element - this.inputElement.style.left = `${this.inputOffset.x}px`; - this.inputElement.style.top = `${this.inputOffset.y}px`; + this.inputElement.style.left = `${this.divOffset.x}px`; + this.inputElement.style.top = `${this.divOffset.y}px`; this.inputElement.style.width = `${ - size.w * options.inputSizeMultiplier + size.w * options.divSizeMultiplier }px`; this.inputElement.style.height = `${ - size.h * options.inputSizeMultiplier + size.h * options.divSizeMultiplier }px`; // Move elements inside to new offset for (const child of this.inputElement.children) { if (child.style.position === "absolute") { child.style.left = `${ - parseInt(child.style.left, 10) + - oldOffset.x - - this._inputOffset.x + parseInt(child.style.left, 10) + oldOffset.x - this._divOffset.x }px`; child.style.top = `${ - parseInt(child.style.top, 10) + - oldOffset.y - - this._inputOffset.y + parseInt(child.style.top, 10) + oldOffset.y - this._divOffset.y }px`; } } @@ -327,6 +362,31 @@ const layers = { } }, + /** + * Auto-expands the collection and its full layers to make sure it fits the box we pass + * + * @param {?BoundingBox} box + */ + auto_expand_to_fit(box) { + var expand_by = [0, 0, 0, 0]; + if (box.x < this.bb.x) { + expand_by[0] = this.bb.x - box.x; + } + if (box.y < this.bb.y) { + expand_by[1] = this.bb.y - box.y; + } + if (box.x + box.w > this.bb.x + this.bb.w) { + expand_by[2] = box.x + box.w - (this.bb.x + this.bb.w); + } + if (box.y + box.h > this.bb.y + this.bb.h) { + expand_by[3] = box.y + box.h - (this.bb.y + this.bb.h); + } + + if (JSON.stringify(expand_by) !== JSON.stringify([0, 0, 0, 0])) { + this.expand(...expand_by); + } + }, + size, resolution: options.resolution, @@ -660,7 +720,7 @@ const layers = { }, }, _logpath, - ["_inputOffset"] + ["_divOffset"] ); collection._resizeInputDiv(); diff --git a/js/lib/util.js b/js/lib/util.js index 2c59631..df3199e 100644 --- a/js/lib/util.js +++ b/js/lib/util.js @@ -460,8 +460,8 @@ const makeElement = ( x, y, offset = { - x: -imageCollection.inputOffset.x, - y: -imageCollection.inputOffset.y, + x: -imageCollection.divOffset.x, + y: -imageCollection.divOffset.y, } ) => { const el = document.createElement(type); diff --git a/js/theme.js b/js/theme.js index 73acb1f..bb5de22 100644 --- a/js/theme.js +++ b/js/theme.js @@ -2,5 +2,7 @@ const theme = { grid: { dark: "#333", light: "#555", + extDark: "#555A5A", + extLight: "#666A6A", }, }; diff --git a/js/ui/tool/dream.js b/js/ui/tool/dream.js index bce7e3b..c06c1ef 100644 --- a/js/ui/tool/dream.js +++ b/js/ui/tool/dream.js @@ -1515,6 +1515,8 @@ const dreamTool = () => }; if (global.connection === "online") { + imageCollection.auto_expand_to_fit(bb); + recalculateBg(); dream_generate_callback(bb, resolution, state); } else { const stop = march(bb, { diff --git a/pages/configuration.html b/pages/configuration.html index a7fcd42..956fdf5 100644 --- a/pages/configuration.html +++ b/pages/configuration.html @@ -8,12 +8,12 @@ - + - +