# frozen_string_literal: true module Sass module Value class Number # The {Unit} module. module Unit CONVERSIONS = { # Length 'in' => { 'in' => Rational(1), 'cm' => Rational(1, 2.54), 'pc' => Rational(1, 6), 'mm' => Rational(1, 25.4), 'q' => Rational(1, 101.6), 'pt' => Rational(1, 72), 'px' => Rational(1, 96) }, 'cm' => { 'in' => Rational(2.54), 'cm' => Rational(1), 'pc' => Rational(2.54, 6), 'mm' => Rational(1, 10), 'q' => Rational(1, 40), 'pt' => Rational(2.54, 72), 'px' => Rational(2.54, 96) }, 'pc' => { 'in' => Rational(6), 'cm' => Rational(6, 2.54), 'pc' => Rational(1), 'mm' => Rational(6, 25.4), 'q' => Rational(6, 101.6), 'pt' => Rational(1, 12), 'px' => Rational(1, 16) }, 'mm' => { 'in' => Rational(25.4), 'cm' => Rational(10), 'pc' => Rational(25.4, 6), 'mm' => Rational(1), 'q' => Rational(1, 4), 'pt' => Rational(25.4, 72), 'px' => Rational(25.4, 96) }, 'q' => { 'in' => Rational(101.6), 'cm' => Rational(40), 'pc' => Rational(101.6, 6), 'mm' => Rational(4), 'q' => Rational(1), 'pt' => Rational(101.6, 72), 'px' => Rational(101.6, 96) }, 'pt' => { 'in' => Rational(72), 'cm' => Rational(72, 2.54), 'pc' => Rational(12), 'mm' => Rational(72, 25.4), 'q' => Rational(72, 101.6), 'pt' => Rational(1), 'px' => Rational(3, 4) }, 'px' => { 'in' => Rational(96), 'cm' => Rational(96, 2.54), 'pc' => Rational(16), 'mm' => Rational(96, 25.4), 'q' => Rational(96, 101.6), 'pt' => Rational(4, 3), 'px' => Rational(1) }, # Rotation 'deg' => { 'deg' => Rational(1), 'grad' => Rational(9, 10), 'rad' => Rational(180, Math::PI), 'turn' => Rational(360) }, 'grad' => { 'deg' => Rational(10, 9), 'grad' => Rational(1), 'rad' => Rational(200, Math::PI), 'turn' => Rational(400) }, 'rad' => { 'deg' => Rational(Math::PI, 180), 'grad' => Rational(Math::PI, 200), 'rad' => Rational(1), 'turn' => Rational(Math::PI * 2) }, 'turn' => { 'deg' => Rational(1, 360), 'grad' => Rational(1, 400), 'rad' => Rational(1, Math::PI * 2), 'turn' => Rational(1) }, # Time 's' => { 's' => Rational(1), 'ms' => Rational(1, 1000) }, 'ms' => { 's' => Rational(1000), 'ms' => Rational(1) }, # Frequency 'Hz' => { 'Hz' => Rational(1), 'kHz' => Rational(1000) }, 'kHz' => { 'Hz' => Rational(1, 1000), 'kHz' => Rational(1) }, # Pixel density 'dpi' => { 'dpi' => Rational(1), 'dpcm' => Rational(2.54), 'dppx' => Rational(96) }, 'dpcm' => { 'dpi' => Rational(1, 2.54), 'dpcm' => Rational(1), 'dppx' => Rational(96, 2.54) }, 'dppx' => { 'dpi' => Rational(1, 96), 'dpcm' => Rational(2.54, 96), 'dppx' => Rational(1) } }.freeze UNITS_BY_TYPE = { time: %w[s ms], frequency: %w[Hz kHz], 'pixel density': %w[dpi dpcm dppx] }.freeze TYPES_BY_UNIT = UNITS_BY_TYPE.each_with_object({}) do |(key, values), hash| values.each do |value| hash[value] = key end end module_function def conversion_factor(unit1, unit2) return 1 if unit1 == unit2 CONVERSIONS.dig(unit1, unit2) end def canonicalize_units(units) return units if units.empty? if units.length == 1 type = TYPES_BY_UNIT[units.first] return type.nil? ? units : [UNITS_BY_TYPE[type].first] end units.map do |unit| type = TYPES_BY_UNIT[unit] type.nil? ? units : [UNITS_BY_TYPE[type].first] end.sort end def canonical_multiplier(units) units.reduce(1) do |multiplier, unit| multiplier * canonical_multiplier_for_unit(unit) end end def canonical_multiplier_for_unit(unit) inner_map = CONVERSIONS[unit] inner_map.nil? ? 1 : 1 / inner_map.values.first end end private_constant :Unit end end end