From 374fe636b80169c78b4b5f92013681d75fa2fad6 Mon Sep 17 00:00:00 2001 From: Gerschel Date: Wed, 8 Feb 2023 18:57:32 -0800 Subject: [PATCH] Squashed commit of the following: commit b030b67ad005bfe29bcda692238a00042dcae816 Author: Gerschel Date: Wed Feb 8 16:38:56 2023 -0800 styling adjustements commit 80a2acb0230dd77489b0eb466f2efe827a053f6d Author: Gerschel Date: Wed Feb 8 10:49:47 2023 -0800 badge indicator toggles visibility by selection commit 898922e025a6422ac947fb45c1fa4f1109882f0a Merge: 745382a0 31bbfa72 Author: Gerschel <9631031+Gerschel@users.noreply.github.com> Date: Wed Feb 8 08:35:26 2023 -0800 Merge pull request #1 from w-e-w/Rounding-Method Rounding Method commit 31bbfa729a15ef35fa1f905345d3ba2b17b26ab9 Author: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Wed Feb 8 19:41:45 2023 +0900 use switch commit 85dbe511c33521d3ac62224bf0e0f3a48194ce63 Author: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Wed Feb 8 16:47:52 2023 +0900 Rounding Method commit 745382a0f4b8d16241545a3460d5206915959255 Author: Gerschel Date: Tue Feb 7 21:19:20 2023 -0800 default set to round commit 728579c618af30ec98a5af0991bd3f28bdaca399 Author: Gerschel Date: Tue Feb 7 21:17:03 2023 -0800 cleaned some commented code out; added indicator commit 5b288c24a1edd8a5c2f35214b9634316d05b8dae Author: Gerschel Date: Tue Feb 7 18:19:00 2023 -0800 needs cleaning; attempt at rounding commit d9f18ae92b929576b0b8c5f1ef8b3b38e441e381 Author: Gerschel Date: Tue Feb 7 15:46:25 2023 -0800 add rounding option in setting for aspect ratio commit af22106802c9e42205649e4c71c23fcf5b8c62f6 Author: Gerschel Date: Tue Feb 7 13:18:45 2023 -0800 added some ratios, sorted ratios by commonality commit 11e2fba73cffe8cdbf4cd0860641b94428ca0e74 Author: Gerschel Date: Tue Feb 7 10:46:53 2023 -0800 snaps to mulitples of 8 and along ratio commit fa00387e07460b10ee82671a1bfa8687e00ee60b Author: Gerschel Date: Mon Feb 6 14:54:59 2023 -0800 updated slidercomponentcontroller commit 8059bc111c3e2d1edb3314e05ab21b65120fa1dd Author: Gerschel Date: Mon Feb 6 14:29:11 2023 -0800 added step size adjustment on number field commit 641157b9f27a874a24ee7b0a854a092e9eac3eec Author: Gerschel Date: Mon Feb 6 14:12:03 2023 -0800 added return step size to default when ratio is disabled commit 5fb75ad28f2476f36100ec93922a8199adbd2a68 Author: Gerschel Date: Mon Feb 6 14:09:34 2023 -0800 added step size adjustment commit e33532883bc4709cd41c3775cbb646d1d5ab0584 Author: Gerschel Date: Mon Feb 6 11:56:15 2023 -0800 adjusted dropdown size, padding, text-align commit 81937329cee77f466c5a5b23c268d0c810128f84 Author: Gerschel Date: Mon Feb 6 11:39:57 2023 -0800 added positioning and styling commit 86eb4583782d92880a9a113a54ffbac9d92f3753 Author: Gerschel Date: Mon Feb 6 08:54:45 2023 -0800 fix typo in defaults; added preventDefault in event --- javascript/ComponentControllers.js | 2 + javascript/aspectRatioSliders.js | 164 ++++++++++++++++++++++++++--- modules/shared.py | 17 ++- modules/ui.py | 10 +- style.css | 46 ++++++++ 5 files changed, 218 insertions(+), 21 deletions(-) diff --git a/javascript/ComponentControllers.js b/javascript/ComponentControllers.js index 194589c7a..2888679b5 100644 --- a/javascript/ComponentControllers.js +++ b/javascript/ComponentControllers.js @@ -189,6 +189,8 @@ class SliderComponentController { } eventHandler() { this.element.dispatchEvent(new Event("input")); + this.childNumField.dispatchEvent(new Event("input")); + this.childRangeField.dispatchEvent(new Event("input")); } setVal(text) { this.updateVal(text); diff --git a/javascript/aspectRatioSliders.js b/javascript/aspectRatioSliders.js index f577750af..d9c4f675c 100644 --- a/javascript/aspectRatioSliders.js +++ b/javascript/aspectRatioSliders.js @@ -1,14 +1,61 @@ class AspectRatioSliderController { - constructor(widthSlider, heightSlider, ratioSource) { + constructor(widthSlider, heightSlider, ratioSource, roundingSource, roundingMethod) { + //References this.widthSlider = new SliderComponentController(widthSlider); this.heightSlider = new SliderComponentController(heightSlider); this.ratioSource = new DropdownComponentController(ratioSource); - this.widthSlider.childRangeField.addEventListener("change", () => this.resize("width")); - this.widthSlider.childNumField.addEventListener("change", () => this.resize("width")); - this.heightSlider.childRangeField.addEventListener("change", () => this.resize("height")); - this.heightSlider.childNumField.addEventListener("change", () => this.resize("height")); + this.roundingSource = new CheckboxComponentController(roundingSource); + this.roundingMethod = new RadioComponentController(roundingMethod); + this.roundingIndicatorBadge = document.createElement("div"); + // Badge implementation + this.roundingIndicatorBadge.innerText = "📐"; + this.roundingIndicatorBadge.classList.add("rounding-badge"); + this.ratioSource.element.appendChild(this.roundingIndicatorBadge); + // Check initial value of ratioSource to set badge visbility + let initialRatio = this.ratioSource.getVal(); + if (!initialRatio.includes(":")) { + this.roundingIndicatorBadge.style.display = "none"; + } + //Adjust badge icon if rounding is on + if (this.roundingSource.getVal()) { + this.roundingIndicatorBadge.classList.add("active"); + this.roundingIndicatorBadge.innerText = "⚠️"; + } + //Make badge clickable to toggle setting + this.roundingIndicatorBadge.addEventListener("click", () => { + this.roundingSource.setVal(!this.roundingSource.getVal()); + }); + //Make rounding setting toggle badge text and style if setting changes + this.roundingSource.child.addEventListener("change", () => { + if (this.roundingSource.getVal()) { + this.roundingIndicatorBadge.classList.add("active"); + this.roundingIndicatorBadge.innerText = "⚠️"; + } + else { + this.roundingIndicatorBadge.classList.remove("active"); + this.roundingIndicatorBadge.innerText = "📐"; + } + this.adjustStepSize(); + }); + //Other event listeners + this.widthSlider.childRangeField.addEventListener("change", (e) => { e.preventDefault(); this.resize("width"); }); + this.widthSlider.childNumField.addEventListener("change", (e) => { e.preventDefault(); this.resize("width"); }); + this.heightSlider.childRangeField.addEventListener("change", (e) => { e.preventDefault(); this.resize("height"); }); + this.heightSlider.childNumField.addEventListener("change", (e) => { e.preventDefault(); this.resize("height"); }); + this.ratioSource.childSelector.addEventListener("change", (e) => { + e.preventDefault(); + //Check and toggle display of badge conditionally on dropdown selection + if (!this.ratioSource.getVal().includes(":")) { + this.roundingIndicatorBadge.style.display = 'none'; + } + else { + this.roundingIndicatorBadge.style.display = 'block'; + } + this.adjustStepSize(); + }); } resize(dimension) { + //For moving slider or number field let val = this.ratioSource.getVal(); if (!val.includes(":")) { return; @@ -16,26 +63,119 @@ class AspectRatioSliderController { let [width, height] = val.split(":").map(Number); let ratio = width / height; if (dimension == 'width') { - this.heightSlider.setVal(Math.round(parseFloat(this.widthSlider.getVal()) / ratio).toString()); + let newHeight = parseInt(this.widthSlider.getVal()) / ratio; + if (this.roundingSource.getVal()) { + switch (this.roundingMethod.getVal()) { + case 'Round': + newHeight = Math.round(newHeight / 8) * 8; + break; + case 'Ceiling': + newHeight = Math.ceil(newHeight / 8) * 8; + break; + case 'Floor': + newHeight = Math.floor(newHeight / 8) * 8; + break; + } + } + this.heightSlider.setVal(newHeight.toString()); } else if (dimension == "height") { - this.widthSlider.setVal(Math.round(parseFloat(this.heightSlider.getVal()) * ratio).toString()); + let newWidth = parseInt(this.heightSlider.getVal()) * ratio; + if (this.roundingSource.getVal()) { + switch (this.roundingMethod.getVal()) { + case 'Round': + newWidth = Math.round(newWidth / 8) * 8; + break; + case 'Ceiling': + newWidth = Math.ceil(newWidth / 8) * 8; + break; + case 'Floor': + newWidth = Math.floor(newWidth / 8) * 8; + break; + } + } + this.widthSlider.setVal(newWidth.toString()); } } - static observeStartup(widthSliderId, heightSliderId, ratioSourceId) { + adjustStepSize() { + /* Sets scales/precision/rounding steps;*/ + let val = this.ratioSource.getVal(); + if (!val.includes(":")) { + //If ratio unlocked + this.widthSlider.childRangeField.step = "8"; + this.widthSlider.childRangeField.min = "64"; + this.widthSlider.childNumField.step = "8"; + this.widthSlider.childNumField.min = "64"; + this.heightSlider.childRangeField.step = "8"; + this.heightSlider.childRangeField.min = "64"; + this.heightSlider.childNumField.step = "8"; + this.heightSlider.childNumField.min = "64"; + return; + } + //Format string and calculate step sizes + let [width, height] = val.split(":").map(Number); + let decimalPlaces = (width.toString().split(".")[1] || []).length; + //keep upto 6 decimal points of precision of ratio + //euclidean gcd does not support floats, so we scale it up + decimalPlaces = decimalPlaces > 6 ? 6 : decimalPlaces; + let gcd = this.gcd(width * 10 ** decimalPlaces, height * 10 ** decimalPlaces) / 10 ** decimalPlaces; + let stepSize = 8 * height / gcd; + let stepSizeOther = 8 * width / gcd; + if (this.roundingSource.getVal()) { + //If rounding is on set/keep default stepsizes + this.widthSlider.childRangeField.step = "8"; + this.widthSlider.childRangeField.min = "64"; + this.widthSlider.childNumField.step = "8"; + this.widthSlider.childNumField.min = "64"; + this.heightSlider.childRangeField.step = "8"; + this.heightSlider.childRangeField.min = "64"; + this.heightSlider.childNumField.step = "8"; + this.heightSlider.childNumField.min = "64"; + } + else { + //if rounding is off, set step sizes so they enforce snapping + //min is changed, because it offsets snap positions + this.widthSlider.childRangeField.step = stepSizeOther.toString(); + this.widthSlider.childRangeField.min = stepSizeOther.toString(); + this.widthSlider.childNumField.step = stepSizeOther.toString(); + this.widthSlider.childNumField.min = stepSizeOther.toString(); + this.heightSlider.childRangeField.step = stepSize.toString(); + this.heightSlider.childRangeField.min = stepSize.toString(); + this.heightSlider.childNumField.step = stepSize.toString(); + this.heightSlider.childNumField.min = stepSize.toString(); + } + let currentWidth = parseInt(this.widthSlider.getVal()); + //Rounding treated kinda like pythons divmod + let stepsTaken = Math.round(currentWidth / stepSizeOther); + //this snaps it to closest rule matches (rules being html step points, and ratio) + let newWidth = stepsTaken * stepSizeOther; + this.widthSlider.setVal(newWidth.toString()); + this.heightSlider.setVal(Math.round(newWidth / (width / height)).toString()); + } + gcd(a, b) { + //euclidean gcd + if (b === 0) { + return a; + } + return this.gcd(b, a % b); + } + static observeStartup(widthSliderId, heightSliderId, ratioSourceId, roundingSourceId, roundingMethodId) { let observer = new MutationObserver(() => { let widthSlider = document.querySelector("gradio-app").shadowRoot.getElementById(widthSliderId); let heightSlider = document.querySelector("gradio-app").shadowRoot.getElementById(heightSliderId); let ratioSource = document.querySelector("gradio-app").shadowRoot.getElementById(ratioSourceId); - if (widthSlider && heightSlider && ratioSource) { + let roundingSource = document.querySelector("gradio-app").shadowRoot.getElementById(roundingSourceId); + let roundingMethod = document.querySelector("gradio-app").shadowRoot.getElementById(roundingMethodId); + if (widthSlider && heightSlider && ratioSource && roundingSource && roundingMethod) { observer.disconnect(); - new AspectRatioSliderController(widthSlider, heightSlider, ratioSource); + new AspectRatioSliderController(widthSlider, heightSlider, ratioSource, roundingSource, roundingMethod); } }); observer.observe(gradioApp(), { childList: true, subtree: true }); } } document.addEventListener("DOMContentLoaded", () => { - AspectRatioSliderController.observeStartup("txt2img_width", "txt2img_height", "txt2img_ratio"); - AspectRatioSliderController.observeStartup("img2img_width", "img2img_height", "img2img_ratio"); + //Register mutation observer for self start-up; + AspectRatioSliderController.observeStartup("txt2img_width", "txt2img_height", "txt2img_ratio", "setting_aspect_ratios_rounding", "setting_aspect_ratios_rounding_method"); + AspectRatioSliderController.observeStartup("img2img_width", "img2img_height", "img2img_ratio", "setting_aspect_ratios_rounding", "setting_aspect_ratios_rounding_method"); }); diff --git a/modules/shared.py b/modules/shared.py index ead7be360..fcd6eadfb 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -140,16 +140,21 @@ ui_reorder_categories = [ ] aspect_ratio_defaults = [ - "🔓" + "🔓", "1:1", - "1:2", - "2:1", - "2:3", "3:2", "4:3", "5:4", - "9:16", "16:9", + "9:16", + "1.85:1", + "2.35:1", + "2.39:1", + "2.40:1", + "21:9", + "1.375:1", + "1.66:1", + "1.75:1" ] cmd_opts.disable_extension_access = (cmd_opts.share or cmd_opts.listen or cmd_opts.server_name) and not cmd_opts.enable_insecure_extension_access @@ -469,6 +474,8 @@ options_templates.update(options_section(('ui', "User interface"), { "keyedit_precision_extra": OptionInfo(0.05, "Ctrl+up/down precision when editing ", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}), "quicksettings": OptionInfo("sd_model_checkpoint", "Quicksettings list"), "ui_reorder": OptionInfo(", ".join(ui_reorder_categories), "txt2img/img2img UI item order"), + "aspect_ratios_rounding": OptionInfo(True, "Round aspect ratios for more flexibility?", gr.Checkbox), + "aspect_ratios_rounding_method": OptionInfo("Ceiling", "Aspect ratios rounding method", gr.Radio,{"choices": ["Round", "Ceiling", "Floor"]}), "aspect_ratios": OptionInfo(", ".join(aspect_ratio_defaults), "txt2img/img2img aspect ratios"), "ui_extra_networks_tab_reorder": OptionInfo("", "Extra networks tab order"), "localization": OptionInfo("None", "Localization (requires restart)", gr.Dropdown, lambda: {"choices": ["None"] + list(localization.localizations.keys())}, refresh=lambda: localization.list_localizations(cmd_opts.localizations_dir)), diff --git a/modules/ui.py b/modules/ui.py index 6853485c4..873c857ad 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -483,8 +483,9 @@ def create_ui(): width = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512, elem_id="txt2img_width") height = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="txt2img_height") - res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="txt2img_res_switch_btn") - aspect_ratio_dropdown = gr.Dropdown(value="🔓", choices=aspect_ratio_list(), interactive=True, type="value", elem_id="txt2img_ratio", show_label=False, label="Aspect Ratio") + with gr.Column(elem_id="txt2img_size_toolbox", scale=0): + aspect_ratio_dropdown = gr.Dropdown(value="🔓", choices=aspect_ratio_list(), interactive=True, type="value", elem_id="txt2img_ratio", show_label=False, label="Aspect Ratio") + res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="txt2img_res_switch_btn") if opts.dimensions_and_batch_together: with gr.Column(elem_id="txt2img_column_batch"): batch_count = gr.Slider(minimum=1, step=1, label='Batch count', value=1, elem_id="txt2img_batch_count") @@ -762,8 +763,9 @@ def create_ui(): width = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512, elem_id="img2img_width") height = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="img2img_height") - res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="img2img_res_switch_btn") - aspect_ratio_dropdown = gr.Dropdown(value="🔓", choices=aspect_ratio_list(), interactive=True, type="value", elem_id="img2img_ratio", show_label=False, label="Aspect Ratio") + with gr.Column(elem_id="img2img_size_toolbox", scale=0): + aspect_ratio_dropdown = gr.Dropdown(value="🔓", choices=aspect_ratio_list(), interactive=True, type="value", elem_id="img2img_ratio", show_label=False, label="Aspect Ratio") + res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="img2img_res_switch_btn") if opts.dimensions_and_batch_together: with gr.Column(elem_id="img2img_column_batch"): batch_count = gr.Slider(minimum=1, step=1, label='Batch count', value=1, elem_id="img2img_batch_count") diff --git a/style.css b/style.css index 05572f662..5b979841c 100644 --- a/style.css +++ b/style.css @@ -747,6 +747,52 @@ footer { margin-left: 0em; } +#txt2img_size_toolbox, #img2img_size_toolbox{ + min-width: unset !important; + gap: 0; +} + +#txt2img_ratio, #img2img_ratio { + padding: 0px; + min-width: unset; + max-width: fit-content; +} +#txt2img_ratio select, #img2img_ratio select{ + -o-appearance: none; + -ms-appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-image: unset; + padding-right: unset; + min-width: 40px; + max-width: 40px; + min-height: 40px; + max-height: 40px; + line-height: 40px; + padding: 0; + text-align: center; +} +.rounding-badge { + display: inline-block; + border-radius: 0px; + background-color: #ccc; + cursor: pointer; + position: absolute; + top: -10px; + right: -10px; + width: 20px; + height: 20px; + padding: 1px; + line-height: 16px; + font-size: 14px; +} + +.rounding-badge.active { + background-color: #007bff; + border-radius: 50%; +} + .inactive{ opacity: 0.5; }