diff --git a/CHANGELOG.md b/CHANGELOG.md index 75e5af22a98..665e7409d8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [Unreleased] +- Disabled unit prefix conversions for compound units (e.g. `u"m/s"`) to avoid generating incorrect units. [#4583](https://github.com/MakieOrg/Makie.jl/pull/4583) - Fix orientation of environment light textures in RPRMakie [#4629](https://github.com/MakieOrg/Makie.jl/pull/4629). - Fix uint16 overflow for over ~65k elements in WGLMakie picking [#4604](https://github.com/MakieOrg/Makie.jl/pull/4604). - Improve performance for line plot in CairoMakie [#4601](https://github.com/MakieOrg/Makie.jl/pull/4601). diff --git a/ReferenceTests/src/tests/unitful.jl b/ReferenceTests/src/tests/unitful.jl index 30b3a02688d..e6a96ec2422 100644 --- a/ReferenceTests/src/tests/unitful.jl +++ b/ReferenceTests/src/tests/unitful.jl @@ -30,3 +30,16 @@ end st end + +@reference_test "Unit reflection" begin + # Don't swallow units past the first + f, a, p = scatter((1:10) .* u"J/s") + # Don't simplify (assume the user knows better) + scatter(f[1, 2], (1:10) .* u"K", exp.(1:10) .* u"mm/m^2") + # Only change prefixes of simple units, not compound units + scatter(f[2, 1], 10 .^ (1:6) .* u"W/m^2", (1:6) .* 1000 .* u"nm") + # Only change units/prefixes for simple units when adding more plots + scatter(f[2, 2], (0:10) .* u"W/m^2", (0:10) .* u"g") + scatter!((0:10) .* u"kW/m^2", (0:10) .* u"kg") + f +end diff --git a/src/dim-converts/unitful-integration.jl b/src/dim-converts/unitful-integration.jl index 7e84d451b9f..89ac5627520 100644 --- a/src/dim-converts/unitful-integration.jl +++ b/src/dim-converts/unitful-integration.jl @@ -20,13 +20,18 @@ base_unit(x::Period) = base_unit(Quantity(x)) unit_string(::Type{T}) where T <: Unitful.AbstractQuantity = string(Unitful.unit(T)) unit_string(unit::Type{<: Unitful.FreeUnits}) = string(unit()) -unit_string(unit::Unitful.FreeUnits) = unit_string(base_unit(unit)) +unit_string(unit::Unitful.FreeUnits) = string(unit) unit_string(unit::Unitful.Unit) = string(unit) unit_string(::Union{Number, Nothing}) = "" unit_string_long(unit) = unit_string_long(base_unit(unit)) unit_string_long(::Unitful.Unit{Sym, D}) where {Sym, D} = string(Sym) +is_compound_unit(x::Period) = is_compound_unit(Quantity(x)) +is_compound_unit(::Quantity{T, D, U}) where {T, D, U} = is_compound_unit(U) +is_compound_unit(::Unitful.FreeUnits{U}) where {U} = length(U) != 1 +is_compound_unit(::Type{<: Unitful.FreeUnits{U}}) where {U} = length(U) != 1 + function eltype_extrema(values) isempty(values) && return (eltype(values), nothing) @@ -159,7 +164,18 @@ function update_extrema!(conversion::UnitfulConversion, value_obs::Observable) imini = min(imini, mini) imaxi = max(imaxi, maxi) end - new_unit = best_unit(imini, imaxi) + # If a unit only consists off of one element, e.g. "mm" or "J", try to find + # the best prefix. Otherwise (e.g. "kg/m^3") use the unit as is and don't + # change it. + if is_compound_unit(imini) + if conversion.unit[] === automatic + new_unit = Unitful.unit(0.5 * Quantity(imini + imaxi)) + else + return + end + else + new_unit = best_unit(imini, imaxi) + end if new_unit != conversion.unit[] conversion.unit[] = new_unit end @@ -167,14 +183,28 @@ end needs_tick_update_observable(conversion::UnitfulConversion) = conversion.unit +# TODO: Convert the unit to rich text arguments instead of parsing the string +# TODO: Could also consider UnitfulLatexify? +function unit_string_to_rich(str::String) + chunks = split(str, '^') + output = Any[string(chunks[1])] + for chunk in chunks[2:end] # each chunk starts after a ^ + pieces = split(chunk, ' ') + push!(output, superscript(string(pieces[1]))) # pieces[1] is immediately after ^ + push!(output, join(pieces[2:end])) # rest is before the next ^ + end + return rich(output...) +end + function get_ticks(conversion::UnitfulConversion, ticks, scale, formatter, vmin, vmax) unit = conversion.unit[] unit isa Automatic && return [], [] unit_str = unit_string(unit) + rich_unit_str = unit_string_to_rich(unit_str) tick_vals = get_tickvalues(ticks, scale, vmin, vmax) labels = get_ticklabels(formatter, tick_vals) if conversion.units_in_label[] - labels = labels .* unit_str + labels = map(lbl -> rich(lbl, rich_unit_str), labels) end return tick_vals, labels end