Minor improvements

This commit is contained in:
BRADSEC
2025-12-07 20:46:52 +10:00
parent b764f915c8
commit 480f15bb15
7 changed files with 89 additions and 72 deletions
+1
View File
@@ -122,3 +122,4 @@ Desktop.ini
# Claude Code files # Claude Code files
.claude/ .claude/
CLAUDE.md
+12 -10
View File
@@ -1,4 +1,4 @@
# Resolution Selector for ComfyUI # Resolution Selector Plus for ComfyUI
Custom node for [ComfyUI](https://github.com/comfyanonymous/ComfyUI) providing easy resolution and empty latent selection for image generation workflows. Custom node for [ComfyUI](https://github.com/comfyanonymous/ComfyUI) providing easy resolution and empty latent selection for image generation workflows.
@@ -21,52 +21,54 @@ cd pathTo/ComfyUI
cd custom_nodes cd custom_nodes
# Clone the repo into custom_nodes # Clone the repo into custom_nodes
git clone https://github.com/bradsec/ComfyUI_ResolutionSelector.git git clone https://github.com/bradsec/ComfyUI_ResolutionSelectorPlus.git
# Restart ComfyUI # Restart ComfyUI
``` ```
## Usage ## Usage
### Resolution Selector ### Resolution Selector Plus
**Add Node:** `Add Node > utils > Resolution Selector` **Add Node:** `Add Node > utils > Resolution Selector Plus`
**Inputs:** **Inputs:**
- `model` (dropdown) - Select your image generation model or "All" for all resolutions - `model` (dropdown) - Select your image generation model or "All" for all resolutions
- `resolution` (dropdown) - Choose from model-optimized presets with aspect ratios (e.g., "1920x1080 (16:9 Landscape)") - `resolution` (dropdown) - Choose from model-optimized presets with aspect ratios (e.g., "1920x1080 (16:9 Landscape)")
- `resolution_multiplier` (dropdown) - Multiply preset resolution (1x, 2x, 3x, 4x) - `resolution_multiplier` (dropdown) - Multiply preset resolution (1x, 2x, 3x, 4x)
- `batch_size` (number) - Number of latent samples to generate (1-64, default: 1)
- `custom_width` (optional) - Override with custom width (0-4096, step 8) - `custom_width` (optional) - Override with custom width (0-4096, step 8)
- `custom_height` (optional) - Override with custom height (0-4096, step 8) - `custom_height` (optional) - Override with custom height (0-4096, step 8)
- `custom_multiplier` (optional) - Independent multiplier for custom dimensions (1x, 2x, 3x, 4x) - `custom_multiplier` (optional) - Independent multiplier for custom dimensions (1x, 2x, 3x, 4x)
- `custom_batch` (optional) - Number of latent samples for custom resolution (1-64, default: 1)
**Outputs:** **Outputs:**
- `width` (INT) - Preset resolution width in pixels - `width` (INT) - Preset resolution width in pixels
- `height` (INT) - Preset resolution height in pixels - `height` (INT) - Preset resolution height in pixels
- `latent` (LATENT) - Empty latent tensor for preset resolution - `latent` (LATENT) - Empty latent tensor for preset resolution (batch_size samples)
- `custom_width` (INT) - Custom width (0 if not set) - `custom_width` (INT) - Custom width (0 if not set)
- `custom_height` (INT) - Custom height (0 if not set) - `custom_height` (INT) - Custom height (0 if not set)
- `custom_latent` (LATENT) - Empty latent tensor for custom resolution - `custom_latent` (LATENT) - Empty latent tensor for custom resolution (custom_batch samples)
**Example Workflows:** **Example Workflows:**
**Basic preset with multiplier:** **Basic preset with multiplier:**
``` ```
Resolution Selector (model: SDXL, resolution: "1024x1024 (1:1 Square)", multiplier: 2x) Resolution Selector Plus (model: SDXL, resolution: "1024x1024 (1:1 Square)", multiplier: 2x)
→ width: 2048, height: 2048 → width: 2048, height: 2048
→ latent → KSampler → latent → KSampler
``` ```
**Custom dimensions with independent multiplier:** **Custom dimensions with independent multiplier:**
``` ```
Resolution Selector (custom_width: 1024, custom_height: 768, custom_multiplier: 2x) Resolution Selector Plus (custom_width: 1024, custom_height: 768, custom_multiplier: 2x)
→ custom_width: 2048, custom_height: 1536 → custom_width: 2048, custom_height: 1536
→ custom_latent → KSampler → custom_latent → KSampler
``` ```
**Using "All" model option:** **Using "All" model option:**
``` ```
Resolution Selector (model: All, resolution: "1920x1080 (16:9 Landscape)") Resolution Selector Plus (model: All, resolution: "1920x1080 (16:9 Landscape)")
→ Shows all 34 unique resolutions from all models → Shows all 74 unique resolutions from all models
``` ```
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 84 KiB

+2 -2
View File
@@ -1,11 +1,11 @@
[project] [project]
name = "comfyui_resolutionselectorplus" name = "comfyui_resolutionselectorplus"
description = "Custom node for ComfyUI providing easy resolution and empty latent selection for image generation workflows." description = "Custom node for ComfyUI providing easy resolution and empty latent selection for image generation workflows."
version = "2.0.4" version = "2.0.5"
license = {file = "LICENSE"} license = {file = "LICENSE"}
[project.urls] [project.urls]
Repository = "https://github.com/bradsec/ComfyUI_ResolutionSelector" Repository = "https://github.com/bradsec/ComfyUI_ResolutionSelectorPlus"
# Used by Comfy Registry https://comfyregistry.org # Used by Comfy Registry https://comfyregistry.org
[tool.comfy] [tool.comfy]
+44 -34
View File
@@ -8,35 +8,36 @@ except ImportError:
# Model-specific resolution presets with constraints # Model-specific resolution presets with constraints
# Includes: model-optimized sizes + photo print (4x6, 5x7, 8x10) + digital/social + canvas art ratios
MODEL_RESOLUTIONS = { MODEL_RESOLUTIONS = {
"Flux": { "Flux": {
"square": [(512, 512), (768, 768), (1024, 1024), (1280, 1280), (1536, 1536), (1920, 1920), (2048, 2048)], "square": [(512, 512), (768, 768), (1024, 1024), (1088, 1088), (1280, 1280), (1536, 1536), (1920, 1920), (2048, 2048)],
"portrait": [(768, 1344), (832, 1216), (896, 1152), (928, 1664), (1024, 1536), (1024, 1792), (1080, 1920), (1152, 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": [(1344, 768), (1216, 832), (1152, 896), (1536, 1024), (1664, 928), (1792, 1024), (1920, 1080), (2048, 1152)], "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)],
"constraints": {"divisible_by": 16, "min": 256, "max": 2048} "constraints": {"divisible_by": 16, "min": 256, "max": 2048}
}, },
"Qwen Image": { "Qwen Image": {
"square": [(1024, 1024), (1280, 1280), (1328, 1328), (1536, 1536), (1920, 1920), (2048, 2048)], "square": [(1024, 1024), (1080, 1080), (1280, 1280), (1328, 1328), (1536, 1536), (1920, 1920), (2048, 2048)],
"portrait": [(928, 1664), (1024, 1536), (1080, 1920), (1140, 1472), (1152, 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": [(1472, 1140), (1536, 1024), (1664, 928), (1920, 1080), (2048, 1152)], "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)],
"constraints": {"divisible_by": 8, "min": 256, "max": 2048} "constraints": {"divisible_by": 8, "min": 256, "max": 2048}
}, },
"Z-Image": { "Z-Image": {
"square": [(512, 512), (768, 768), (1024, 1024), (1280, 1280), (1536, 1536), (1920, 1920), (2048, 2048)], "square": [(512, 512), (768, 768), (1024, 1024), (1080, 1080), (1280, 1280), (1536, 1536), (1920, 1920), (2048, 2048)],
"portrait": [(720, 1280), (768, 1024), (1080, 1920), (1152, 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), (1920, 1080), (2048, 1152)], "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)],
"constraints": {"divisible_by": 8, "min": 256, "max": 2048} "constraints": {"divisible_by": 8, "min": 256, "max": 2048}
}, },
"SD 1.5": { "SD 1.5": {
"square": [(512, 512), (768, 768), (1024, 1024), (1280, 1280), (1536, 1536)], "square": [(512, 512), (768, 768), (1024, 1024), (1080, 1080), (1280, 1280), (1536, 1536)],
"portrait": [(512, 768), (512, 682), (512, 1024), (768, 1024), (768, 1344), (1080, 1920)], "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), (1344, 768), (1536, 512), (1920, 1080)], "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)],
"constraints": {"divisible_by": 8, "min": 256, "max": 2048} "constraints": {"divisible_by": 8, "min": 256, "max": 2048}
}, },
"SDXL": { "SDXL": {
"square": [(1024, 1024), (1280, 1280), (1536, 1536), (1920, 1920), (2048, 2048)], "square": [(1024, 1024), (1080, 1080), (1280, 1280), (1536, 1536), (1920, 1920), (2048, 2048)],
"portrait": [(640, 1536), (768, 1344), (832, 1216), (896, 1152), (1024, 1536), (1080, 1920), (1152, 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), (1344, 768), (1536, 640), (1536, 1024), (1920, 1080), (2048, 1152)], "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)],
"constraints": {"divisible_by": 8, "min": 256, "max": 2048} "constraints": {"divisible_by": 8, "min": 256, "max": 2048}
} }
} }
@@ -60,7 +61,7 @@ def gcd(a, b):
def calculate_aspect_ratio(width, height): def calculate_aspect_ratio(width, height):
""" """
Calculate simplified aspect ratio from dimensions, rounding to common ratios. Calculate simplified aspect ratio from dimensions, using nearest common ratio.
Args: Args:
width (int): Width in pixels width (int): Width in pixels
@@ -91,19 +92,12 @@ def calculate_aspect_ratio(width, height):
(0.56, "9:16"), # 0.5625 (0.56, "9:16"), # 0.5625
(0.5, "1:2"), # 0.5 (0.5, "1:2"), # 0.5
(0.42, "5:12"), # 0.4166... (0.42, "5:12"), # 0.4166...
(0.33, "1:3"), # 0.333... (panoramic)
] ]
# Find closest common ratio (within 5% tolerance) # Find closest common ratio by absolute difference
tolerance = 0.05 closest_ratio = min(common_ratios, key=lambda r: abs(actual_ratio - r[0]))
for ratio_value, ratio_str in common_ratios: return closest_ratio[1]
if abs(actual_ratio - ratio_value) / ratio_value < tolerance:
return ratio_str
# If no close match, calculate GCD-based ratio
divisor = gcd(width, height)
ratio_width = width // divisor
ratio_height = height // divisor
return f"{ratio_width}:{ratio_height}"
def format_resolution(width, height): def format_resolution(width, height):
@@ -283,6 +277,13 @@ class ResolutionSelector:
"resolution_multiplier": (["1x", "2x", "3x", "4x"], { "resolution_multiplier": (["1x", "2x", "3x", "4x"], {
"default": "1x" "default": "1x"
}), }),
"batch_size": ("INT", {
"default": 1,
"min": 1,
"max": 64,
"step": 1,
"display": "number"
}),
}, },
"optional": { "optional": {
"custom_width": ("INT", { "custom_width": ("INT", {
@@ -302,6 +303,13 @@ class ResolutionSelector:
"custom_multiplier": (["1x", "2x", "3x", "4x"], { "custom_multiplier": (["1x", "2x", "3x", "4x"], {
"default": "1x" "default": "1x"
}), }),
"custom_batch": ("INT", {
"default": 1,
"min": 1,
"max": 64,
"step": 1,
"display": "number"
}),
} }
} }
@@ -310,7 +318,7 @@ class ResolutionSelector:
FUNCTION = "select_resolution" FUNCTION = "select_resolution"
CATEGORY = "utils" CATEGORY = "utils"
def select_resolution(self, model, resolution, resolution_multiplier="1x", custom_width=0, custom_height=0, custom_multiplier="1x"): def select_resolution(self, model, resolution, resolution_multiplier="1x", batch_size=1, custom_width=0, custom_height=0, custom_multiplier="1x", custom_batch=1):
""" """
Select and validate resolution, generate outputs. Select and validate resolution, generate outputs.
@@ -318,9 +326,11 @@ class ResolutionSelector:
model (str): Selected model name model (str): Selected model name
resolution (str): Selected preset resolution string resolution (str): Selected preset resolution string
resolution_multiplier (str): Multiplier for resolution (1x-4x) resolution_multiplier (str): Multiplier for resolution (1x-4x)
batch_size (int): Number of latent samples for preset resolution (default: 1)
custom_width (int, optional): Custom width override custom_width (int, optional): Custom width override
custom_height (int, optional): Custom height override custom_height (int, optional): Custom height override
custom_multiplier (str, optional): Multiplier for custom dimensions (1x-4x) custom_multiplier (str, optional): Multiplier for custom dimensions (1x-4x)
custom_batch (int, optional): Number of latent samples for custom resolution (default: 1)
Returns: Returns:
tuple: (width: int, height: int, latent: dict, custom_width: int, custom_height: int, custom_latent: dict) tuple: (width: int, height: int, latent: dict, custom_width: int, custom_height: int, custom_latent: dict)
@@ -336,8 +346,8 @@ class ResolutionSelector:
width *= multiplier width *= multiplier
height *= multiplier height *= multiplier
# Generate latent for preset resolution # Generate latent for preset resolution with batch size
latent = self._generate_empty_latent(width, height) latent = self._generate_empty_latent(width, height, batch_size)
# Determine final custom dimensions # Determine final custom dimensions
if custom_width > 0 and custom_height > 0: if custom_width > 0 and custom_height > 0:
@@ -349,13 +359,13 @@ class ResolutionSelector:
if model != "All": if model != "All":
self._validate_dimensions(model, final_custom_width, final_custom_height) self._validate_dimensions(model, final_custom_width, final_custom_height)
# Generate custom latent # Generate custom latent with custom batch size
custom_latent = self._generate_empty_latent(final_custom_width, final_custom_height) custom_latent = self._generate_empty_latent(final_custom_width, final_custom_height, custom_batch)
return (width, height, latent, final_custom_width, final_custom_height, custom_latent) return (width, height, latent, final_custom_width, final_custom_height, custom_latent)
else: else:
# No custom dimensions, return zeros and minimal empty custom latent # No custom dimensions, return zeros and minimal empty custom latent
custom_latent = self._generate_empty_latent(1, 1) custom_latent = self._generate_empty_latent(1, 1, 1)
return (width, height, latent, 0, 0, custom_latent) return (width, height, latent, 0, 0, custom_latent)
@@ -433,5 +443,5 @@ NODE_CLASS_MAPPINGS = {
} }
NODE_DISPLAY_NAME_MAPPINGS = { NODE_DISPLAY_NAME_MAPPINGS = {
"ResolutionSelector": "Resolution Selector", "ResolutionSelector": "Resolution Selector Plus",
} }
+2 -1
View File
@@ -85,7 +85,8 @@ def test_model_resolutions():
flux_res = get_resolution_list("Flux") flux_res = get_resolution_list("Flux")
print(f" Flux has {len(flux_res)} resolutions") print(f" Flux has {len(flux_res)} resolutions")
assert len(flux_res) > 0, "Flux should have resolutions" assert len(flux_res) > 0, "Flux should have resolutions"
assert any("1920x1080" in r for r in flux_res), "Flux should have 1920x1080" # Flux uses 16-pixel divisibility, so 1920x1080 becomes 1920x1088
assert any("1920x1088" in r for r in flux_res), "Flux should have 1920x1088 (Full HD adapted to 16px divisibility)"
# Test All model # Test All model
all_res = get_resolution_list("All") all_res = get_resolution_list("All")
+28 -25
View File
@@ -1,30 +1,32 @@
import { app } from "/scripts/app.js"; 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 = { const MODEL_RESOLUTIONS = {
"Flux": { "Flux": {
"square": [[512, 512], [768, 768], [1024, 1024], [1280, 1280], [1536, 1536], [1920, 1920], [2048, 2048]], "square": [[512, 512], [768, 768], [1024, 1024], [1088, 1088], [1280, 1280], [1536, 1536], [1920, 1920], [2048, 2048]],
"portrait": [[768, 1344], [832, 1216], [896, 1152], [928, 1664], [1024, 1536], [1024, 1792], [1080, 1920], [1152, 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": [[1344, 768], [1216, 832], [1152, 896], [1536, 1024], [1664, 928], [1792, 1024], [1920, 1080], [2048, 1152]], "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": { "Qwen Image": {
"square": [[1024, 1024], [1280, 1280], [1328, 1328], [1536, 1536], [1920, 1920], [2048, 2048]], "square": [[1024, 1024], [1080, 1080], [1280, 1280], [1328, 1328], [1536, 1536], [1920, 1920], [2048, 2048]],
"portrait": [[928, 1664], [1024, 1536], [1080, 1920], [1140, 1472], [1152, 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": [[1472, 1140], [1536, 1024], [1664, 928], [1920, 1080], [2048, 1152]], "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": { "Z-Image": {
"square": [[512, 512], [768, 768], [1024, 1024], [1280, 1280], [1536, 1536], [1920, 1920], [2048, 2048]], "square": [[512, 512], [768, 768], [1024, 1024], [1080, 1080], [1280, 1280], [1536, 1536], [1920, 1920], [2048, 2048]],
"portrait": [[720, 1280], [768, 1024], [1080, 1920], [1152, 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], [1920, 1080], [2048, 1152]], "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": { "SD 1.5": {
"square": [[512, 512], [768, 768], [1024, 1024], [1280, 1280], [1536, 1536]], "square": [[512, 512], [768, 768], [1024, 1024], [1080, 1080], [1280, 1280], [1536, 1536]],
"portrait": [[512, 768], [512, 682], [512, 1024], [768, 1024], [768, 1344], [1080, 1920]], "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], [1344, 768], [1536, 512], [1920, 1080]], "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": { "SDXL": {
"square": [[1024, 1024], [1280, 1280], [1536, 1536], [1920, 1920], [2048, 2048]], "square": [[1024, 1024], [1080, 1080], [1280, 1280], [1536, 1536], [1920, 1920], [2048, 2048]],
"portrait": [[640, 1536], [768, 1344], [832, 1216], [896, 1152], [1024, 1536], [1080, 1920], [1152, 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], [1344, 768], [1536, 640], [1536, 1024], [1920, 1080], [2048, 1152]], "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]],
} }
}; };
@@ -60,21 +62,22 @@ function calculateAspectRatio(width, height) {
[0.56, "9:16"], // 0.5625 [0.56, "9:16"], // 0.5625
[0.5, "1:2"], // 0.5 [0.5, "1:2"], // 0.5
[0.42, "5:12"], // 0.4166... [0.42, "5:12"], // 0.4166...
[0.33, "1:3"], // 0.333... (panoramic)
]; ];
// Find closest common ratio (within 5% tolerance) // Find closest common ratio by absolute difference
const tolerance = 0.05; let closestRatio = commonRatios[0];
for (const [ratioValue, ratioStr] of commonRatios) { let minDiff = Math.abs(actualRatio - closestRatio[0]);
if (Math.abs(actualRatio - ratioValue) / ratioValue < tolerance) {
return ratioStr; for (const ratio of commonRatios) {
const diff = Math.abs(actualRatio - ratio[0]);
if (diff < minDiff) {
minDiff = diff;
closestRatio = ratio;
} }
} }
// If no close match, calculate GCD-based ratio return closestRatio[1];
const divisor = gcd(width, height);
const ratioWidth = width / divisor;
const ratioHeight = height / divisor;
return `${ratioWidth}:${ratioHeight}`;
} }
function formatResolution(width, height) { function formatResolution(width, height) {