Files
bradsec-ComfyUI_ResolutionS…/js/resolution_selector.js
T

240 lines
9.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { app } from "/scripts/app.js";
// Model-specific resolution presets with constraints
// Includes: model-optimized sizes + photo print (4x6, 5x7, 8x10) + digital/social + canvas art ratios
const MODEL_RESOLUTIONS = {
"Flux": {
"square": [[512, 512], [768, 768], [1024, 1024], [1088, 1088], [1280, 1280], [1536, 1536], [1920, 1920], [2048, 2048]],
"portrait": [[688, 2048], [768, 1344], [832, 1216], [896, 1152], [928, 1664], [1024, 1536], [1024, 1792], [1024, 2048], [1088, 1920], [1152, 2048], [1200, 1792], [1360, 2048], [1456, 2048], [1536, 2048], [1616, 2048], [1632, 2048], [1712, 2048]],
"landscape": [[1280, 720], [1344, 768], [1216, 832], [1152, 896], [1536, 1024], [1664, 928], [1792, 1024], [1792, 1200], [1920, 1088], [2048, 688], [2048, 1024], [2048, 1152], [2048, 1360], [2048, 1456], [2048, 1536], [2048, 1616], [2048, 1632], [2048, 1712]],
},
"Qwen Image": {
"square": [[1024, 1024], [1080, 1080], [1280, 1280], [1328, 1328], [1536, 1536], [1920, 1920], [2048, 2048]],
"portrait": [[680, 2048], [928, 1664], [1024, 1536], [1024, 2048], [1080, 1920], [1140, 1472], [1152, 2048], [1200, 1800], [1368, 2048], [1464, 2048], [1536, 2048], [1608, 2048], [1640, 2048], [1704, 2048]],
"landscape": [[1280, 720], [1472, 1140], [1536, 1024], [1664, 928], [1800, 1200], [1920, 1080], [2048, 680], [2048, 1024], [2048, 1152], [2048, 1368], [2048, 1464], [2048, 1536], [2048, 1608], [2048, 1640], [2048, 1704]],
},
"Z-Image": {
"square": [[512, 512], [768, 768], [1024, 1024], [1080, 1080], [1280, 1280], [1536, 1536], [1920, 1920], [2048, 2048]],
"portrait": [[680, 2048], [720, 1280], [768, 1024], [1024, 2048], [1080, 1920], [1152, 2048], [1200, 1800], [1368, 2048], [1464, 2048], [1536, 2048], [1608, 2048], [1640, 2048], [1704, 2048]],
"landscape": [[1024, 768], [1280, 720], [1800, 1200], [1920, 1080], [2048, 680], [2048, 1024], [2048, 1152], [2048, 1368], [2048, 1464], [2048, 1536], [2048, 1608], [2048, 1640], [2048, 1704]],
},
"SD 1.5": {
"square": [[512, 512], [768, 768], [1024, 1024], [1080, 1080], [1280, 1280], [1536, 1536]],
"portrait": [[512, 768], [512, 682], [512, 1024], [680, 2048], [768, 1024], [768, 1344], [1024, 2048], [1080, 1920], [1200, 1800], [1368, 2048], [1464, 2048], [1536, 2048], [1608, 2048], [1640, 2048], [1704, 2048]],
"landscape": [[768, 512], [1024, 512], [1024, 768], [1280, 720], [1344, 768], [1536, 512], [1800, 1200], [1920, 1080], [2048, 680], [2048, 1024], [2048, 1368], [2048, 1464], [2048, 1536], [2048, 1608], [2048, 1640], [2048, 1704]],
},
"SDXL": {
"square": [[1024, 1024], [1080, 1080], [1280, 1280], [1536, 1536], [1920, 1920], [2048, 2048]],
"portrait": [[640, 1536], [680, 2048], [768, 1344], [832, 1216], [896, 1152], [1024, 1536], [1024, 2048], [1080, 1920], [1152, 2048], [1200, 1800], [1368, 2048], [1464, 2048], [1536, 2048], [1608, 2048], [1640, 2048], [1704, 2048]],
"landscape": [[1152, 896], [1216, 832], [1280, 720], [1344, 768], [1536, 640], [1536, 1024], [1800, 1200], [1920, 1080], [2048, 680], [2048, 1024], [2048, 1152], [2048, 1368], [2048, 1464], [2048, 1536], [2048, 1608], [2048, 1640], [2048, 1704]],
}
};
function gcd(a, b) {
while (b !== 0) {
const temp = b;
b = a % b;
a = temp;
}
return a;
}
function calculateAspectRatio(width, height) {
// Calculate actual ratio as decimal
const actualRatio = width / height;
// Common aspect ratios [ratio_value, "width:height" string]
const commonRatios = [
[1.0, "1:1"], // Square
[1.25, "5:4"], // 1.25
[1.33, "4:3"], // 1.333...
[1.5, "3:2"], // 1.5
[1.6, "16:10"], // 1.6
[1.78, "16:9"], // 1.777...
[2.0, "2:1"], // 2.0
[2.35, "21:9"], // 2.333... (ultrawide)
[2.4, "12:5"], // 2.4
[3.0, "3:1"], // 3.0
// Portrait ratios
[0.75, "3:4"], // 0.75
[0.67, "2:3"], // 0.666...
[0.625, "5:8"], // 0.625
[0.56, "9:16"], // 0.5625
[0.5, "1:2"], // 0.5
[0.42, "5:12"], // 0.4166...
[0.33, "1:3"], // 0.333... (panoramic)
];
// Find closest common ratio by absolute difference
let closestRatio = commonRatios[0];
let minDiff = Math.abs(actualRatio - closestRatio[0]);
for (const ratio of commonRatios) {
const diff = Math.abs(actualRatio - ratio[0]);
if (diff < minDiff) {
minDiff = diff;
closestRatio = ratio;
}
}
return closestRatio[1];
}
function formatResolution(width, height) {
const aspectRatio = calculateAspectRatio(width, height);
let orientation;
if (width === height) {
orientation = "Square";
} else if (width < height) {
orientation = "Portrait";
} else {
orientation = "Landscape";
}
// Format with fixed width for better alignment
const resolutionStr = `${width}x${height}`;
const paddedResolution = resolutionStr.padEnd(13, ' '); // Pad to 13 chars for alignment
return `${paddedResolution}(${aspectRatio} ${orientation})`;
}
function getAllResolutions() {
const uniqueResolutions = new Map();
// Collect all unique width×height pairs
for (const modelName in MODEL_RESOLUTIONS) {
const modelData = MODEL_RESOLUTIONS[modelName];
for (const category of ["square", "portrait", "landscape"]) {
if (modelData[category]) {
for (const [w, h] of modelData[category]) {
const key = `${w}x${h}`;
if (!uniqueResolutions.has(key)) {
uniqueResolutions.set(key, { width: w, height: h, pixels: w * h });
}
}
}
}
}
// Sort by total pixels, then by width
const sorted = Array.from(uniqueResolutions.values())
.sort((a, b) => {
if (a.pixels !== b.pixels) return a.pixels - b.pixels;
return a.width - b.width;
});
return sorted.map(({ width, height }) => formatResolution(width, height));
}
function getResolutionsForModel(model) {
if (model === "All") {
return getAllResolutions();
}
const modelData = MODEL_RESOLUTIONS[model];
if (!modelData) return [];
const resolutions = [];
for (const category of ["square", "portrait", "landscape"]) {
if (modelData[category]) {
for (const [w, h] of modelData[category]) {
resolutions.push(formatResolution(w, h));
}
}
}
return resolutions;
}
function getDefaultResolution(model) {
// Model-specific native/optimal resolutions
const defaultResolutions = {
"Flux": [1024, 1024],
"Qwen Image": [1328, 1328],
"Z-Image": [1024, 1024],
"SD 1.5": [512, 512],
"SDXL": [1024, 1024],
"All": [1024, 1024]
};
if (defaultResolutions[model]) {
const [w, h] = defaultResolutions[model];
return formatResolution(w, h);
}
// Fallback
return formatResolution(1024, 1024);
}
app.registerExtension({
name: "ResolutionSelectorPlus.DynamicDropdown",
async nodeCreated(node) {
if (node.comfyClass !== "ResolutionSelectorPlus") return;
// Set wider default node width to prevent text cutoff
node.setSize([400, node.size[1]]);
const modelWidget = node.widgets.find(w => w.name === "model");
const resolutionWidget = node.widgets.find(w => w.name === "resolution");
if (!modelWidget || !resolutionWidget) {
console.error("ResolutionSelector: Required widgets not found");
return;
}
const origCallback = modelWidget.callback;
const updateResolutions = (modelValue) => {
const resolutions = getResolutionsForModel(modelValue);
if (resolutions.length === 0) {
console.warn(`ResolutionSelector: No resolutions found for model ${modelValue}`);
return;
}
resolutionWidget.options.values = resolutions;
// Set to model-specific default resolution
const defaultRes = getDefaultResolution(modelValue);
if (resolutions.includes(defaultRes)) {
resolutionWidget.value = defaultRes;
} else if (!resolutions.includes(resolutionWidget.value)) {
// Fallback to first resolution if default not found
resolutionWidget.value = resolutions[0];
}
node.setDirtyCanvas(true, true);
};
modelWidget.callback = function(value) {
if (origCallback) {
origCallback.apply(this, arguments);
}
updateResolutions(value);
};
setTimeout(() => {
updateResolutions(modelWidget.value);
}, 10);
},
async loadedGraphNode(node) {
if (node.comfyClass !== "ResolutionSelectorPlus") return;
const modelWidget = node.widgets.find(w => w.name === "model");
if (modelWidget) {
const resolutions = getResolutionsForModel(modelWidget.value);
const resolutionWidget = node.widgets.find(w => w.name === "resolution");
if (resolutionWidget && resolutions.length > 0) {
resolutionWidget.options.values = resolutions;
if (!resolutions.includes(resolutionWidget.value)) {
resolutionWidget.value = resolutions[0];
}
}
}
}
});