/** * AspectCalc.js - アスペクト比計算ライブラリ * 縦解像度と横解像度から3種類のアスペクト比を計算します * * MIT License * * Copyright (c) 2025 SenY * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ class AspectCalc { /** * 最大公約数を計算する関数 * @param {number} a - 数値1 * @param {number} b - 数値2 * @returns {number} 最大公約数 */ static gcd(a, b) { while (b !== 0) { let temp = b; b = a % b; a = temp; } return a; } /** * 2つの数値の最大公約数で割って、最も簡単な整数比を求める * @param {number} width - 幅 * @param {number} height - 高さ * @returns {Object} {x: number, y: number} の形式 */ static getIntegerRatio(width, height) { const divisor = this.gcd(width, height); return { x: width / divisor, y: height / divisor }; } /** * 縦横共に20以下の整数比で表せる最も近似のアスペクト比を求める * @param {number} width - 幅 * @param {number} height - 高さ * @returns {Object} {x: number, y: number} の形式 */ static getApproximateRatio(width, height) { const targetRatio = width / height; let bestRatio = { x: 1, y: 1 }; let minError = Math.abs(targetRatio - 1); // 1から20までの全ての組み合わせを試す for (let x = 1; x <= 20; x++) { for (let y = 1; y <= 20; y++) { const ratio = x / y; const error = Math.abs(targetRatio - ratio); if (error < minError) { minError = error; bestRatio = { x, y }; } } } return bestRatio; } /** * 一般的なディスプレイアスペクト比の中で最も近似のものを求める * @param {number} width - 幅 * @param {number} height - 高さ * @returns {Object} {x: number, y: number} の形式 */ static getStandardRatio(width, height) { const targetRatio = width / height; // 一般的なアスペクト比のリスト(正規化された値) const standardRatios = [ { x: 1, y: 1 }, // 1:1 { x: 16, y: 9 }, // 16:9 { x: 4, y: 3 }, // 4:3 { x: 8, y: 5 }, // 8:5 { x: 3, y: 2 }, // 3:2 { x: 5, y: 4 }, // 5:4 { x: 9, y: 16 }, // 9:16 (16:9の逆) { x: 3, y: 4 }, // 3:4 (4:3の逆) { x: 5, y: 8 }, // 5:8 (8:5の逆) { x: 2, y: 3 }, // 2:3 (3:2の逆) { x: 4, y: 5 } // 4:5 (5:4の逆) ]; let bestRatio = standardRatios[0]; let minError = Math.abs(targetRatio - (bestRatio.x / bestRatio.y)); for (const ratio of standardRatios) { const error = Math.abs(targetRatio - (ratio.x / ratio.y)); if (error < minError) { minError = error; bestRatio = ratio; } } return bestRatio; } /** * メイン関数:3種類のアスペクト比を計算して返す * @param {number} width - 幅 * @param {number} height - 高さ * @returns {Object} 3種類のアスペクト比を含むオブジェクト */ static calculate(width, height) { if (width <= 0 || height <= 0) { throw new Error('幅と高さは正の数である必要があります'); } return { integer: this.getIntegerRatio(width, height), approximate: this.getApproximateRatio(width, height), standard: this.getStandardRatio(width, height) }; } } // モジュールエクスポート対応 if (typeof module !== 'undefined' && module.exports) { module.exports = AspectCalc; } // グローバル変数としても利用可能にする if (typeof window !== 'undefined') { window.AspectCalc = AspectCalc; }