From 84a6d8a56039b720e09c4fdff0a887aadc466cd2 Mon Sep 17 00:00:00 2001 From: icweaver Date: Sun, 9 Mar 2025 13:45:02 -0700 Subject: [PATCH 01/23] basic impl started --- Project.toml | 4 ++++ ext/DynamicQuantitiesMakieExt.jl | 37 ++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 ext/DynamicQuantitiesMakieExt.jl diff --git a/Project.toml b/Project.toml index b9ee6693..f2849b33 100644 --- a/Project.toml +++ b/Project.toml @@ -10,12 +10,14 @@ Tricks = "410a4b4d-49e4-4fbc-ab6d-cb71b17b3775" [weakdeps] LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" ScientificTypes = "321657f4-b219-11e9-178b-2701a2544e81" Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" [extensions] DynamicQuantitiesLinearAlgebraExt = "LinearAlgebra" +DynamicQuantitiesMakieExt = "Makie" DynamicQuantitiesMeasurementsExt = "Measurements" DynamicQuantitiesScientificTypesExt = "ScientificTypes" DynamicQuantitiesUnitfulExt = "Unitful" @@ -23,6 +25,7 @@ DynamicQuantitiesUnitfulExt = "Unitful" [compat] DispatchDoctor = "0.4" LinearAlgebra = "1" +Makie = "0.22.2" Measurements = "2" ScientificTypes = "3" TestItems = "0.1, 1" @@ -32,6 +35,7 @@ julia = "1.10" [extras] LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" ScientificTypes = "321657f4-b219-11e9-178b-2701a2544e81" Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" diff --git a/ext/DynamicQuantitiesMakieExt.jl b/ext/DynamicQuantitiesMakieExt.jl new file mode 100644 index 00000000..613f2f53 --- /dev/null +++ b/ext/DynamicQuantitiesMakieExt.jl @@ -0,0 +1,37 @@ +module DynamicQuantitiesMakieExt + +using DynamicQuantities: UnionAbstractQuantity, ustrip, dimension + +import Makie as M + +struct DQConversion <: M.AbstractDimConversion + quantities::M.Observable{Any} + DQConversion() = new(M.automatic) +end + +M.expand_dimensions(::M.PointBased, y::AbstractVector{<:UnionAbstractQuantity}) = (keys(y), y) + +M.needs_tick_update_observable(conversion::DQConversion) = nothing + +M.create_dim_conversion(::Type{<:UnionAbstractQuantity}) = DQConversion() + +M.MakieCore.should_dim_convert(::Type{<:UnionAbstractQuantity}) = true + +M.convert_dim_value(::DQConversion, quantities) = [ustrip(q) for q in quantities] + +function M.convert_dim_observable(conversion::DQConversion, values_obs::M.Observable, deregister) + result = M.Observable(Float64[]) + f = M.on(values_obs; update=true) do values + result[] = M.convert_dim_value(conversion, values) + conversion.quantities[] = values + end + push!(deregister, f) + return result +end + +function M.get_ticks(conversion::DQConversion, ticks, scale, formatter, vmin, vmax) + tick_vals, labels = M.get_ticks(ticks, scale, formatter, vmin, vmax) + return tick_vals, string.(labels, string(dimension(conversion.quantities[]))) +end + +end From 2eb116f23bed93ef5acc40550d860b163ddf2b31 Mon Sep 17 00:00:00 2001 From: icweaver Date: Sat, 29 Mar 2025 09:55:48 -0700 Subject: [PATCH 02/23] cleanup + started adding tests --- ext/DynamicQuantitiesMakieExt.jl | 89 +++++++++++++++++++++++++------- 1 file changed, 71 insertions(+), 18 deletions(-) diff --git a/ext/DynamicQuantitiesMakieExt.jl b/ext/DynamicQuantitiesMakieExt.jl index 613f2f53..7d262d86 100644 --- a/ext/DynamicQuantitiesMakieExt.jl +++ b/ext/DynamicQuantitiesMakieExt.jl @@ -1,37 +1,90 @@ module DynamicQuantitiesMakieExt -using DynamicQuantities: UnionAbstractQuantity, ustrip, dimension +using DynamicQuantities: UnionAbstractQuantity, AbstractDimensions, ustrip, dimension +using TestItems: @testitem import Makie as M -struct DQConversion <: M.AbstractDimConversion - quantities::M.Observable{Any} - DQConversion() = new(M.automatic) +M.expand_dimensions(::M.PointBased, y::AbstractVector{<:UnionAbstractQuantity}) = (keys(y), y) +M.create_dim_conversion(::Type{<:UnionAbstractQuantity}) = DQConversion() +M.MakieCore.should_dim_convert(::Type{<:UnionAbstractQuantity}) = true + +unit_string(unit::D) where D <: AbstractDimensions = string(dimension(unit)) + +function unit_convert(::M.Automatic, x) + x end -M.expand_dimensions(::M.PointBased, y::AbstractVector{<:UnionAbstractQuantity}) = (keys(y), y) +function unit_convert(unit::D, x::AbstractArray) where D <: AbstractDimensions + unit_convert.(Ref(unit), x) +end -M.needs_tick_update_observable(conversion::DQConversion) = nothing +function unit_convert(unit::D, value) where D <: AbstractDimensions + conv = value / unit + return Float64(ustrip(conv)) +end -M.create_dim_conversion(::Type{<:UnionAbstractQuantity}) = DQConversion() +struct DQConversion <: M.AbstractDimConversion + unit::M.Observable{Any} + automatic_units::Bool + units_in_label::M.Observable{Bool} +end -M.MakieCore.should_dim_convert(::Type{<:UnionAbstractQuantity}) = true +function DQConversion(unit=M.automatic; units_in_label=true) + return DQConversion(dimension(unit), unit isa M.Automatic, units_in_label) +end + +M.needs_tick_update_observable(conversion::DQConversion) = conversion.unit -M.convert_dim_value(::DQConversion, quantities) = [ustrip(q) for q in quantities] +function M.get_ticks(conversion::DQConversion, ticks, scale, formatter, vmin, vmax) + unit = conversion.unit[] + unit isa M.Automatic && return [], [] + unit_str = unit_string(unit) + tick_vals, labels = M.get_ticks(ticks, scale, formatter, vmin, vmax) + return tick_vals, labels .* unit_str #string.(labels, string(dimension(conversion.quantities[]))) +end -function M.convert_dim_observable(conversion::DQConversion, values_obs::M.Observable, deregister) - result = M.Observable(Float64[]) - f = M.on(values_obs; update=true) do values - result[] = M.convert_dim_value(conversion, values) - conversion.quantities[] = values +function M.convert_dim_observable(conversion::DQConversion, value_obs::M.Observable, deregister) + result = map(conversion.unit, value_obs; ignore_equal_values=true) do unit, values + if !isempty(values) + # try if conversion works, to through error if not! + # Is there a function for this to check in DynamicQuantities? + unit_convert(unit, values[1]) + end + conversion.unit[] = dimension(values) + return unit_convert(conversion.unit[], values) end - push!(deregister, f) + append!(deregister, result.inputs) return result end -function M.get_ticks(conversion::DQConversion, ticks, scale, formatter, vmin, vmax) - tick_vals, labels = M.get_ticks(ticks, scale, formatter, vmin, vmax) - return tick_vals, string.(labels, string(dimension(conversion.quantities[]))) +function M.convert_dim_value(conversion::DQConversion, value::UnionAbstractQuantity) + return unit_convert(conversion.unit[], value) +end + +@testitem "conversion" begin + using DynamicQuantities, Makie, Dates + const DQConversion = Base.get_extension(DynamicQuantities, :DynamicQuantitiesMakieExt).DQConversion + + f, ax, pl = scatter(u"m" .* (1:10)) + @test pl isa Scatter{Tuple{Vector{Point2{Float64}}}} + + @recipe(DQPlot, x) do scene + return Attributes() + end + + function Makie.plot!(plot::DQPlot) + return scatter!(plot, plot.x, map(x -> x .* u"s", plot.x)) + end + + f, ax, pl = dqplot(1:5) + + pl_conversion = Makie.get_conversions(pl) + ax_conversion = Makie.get_conversions(ax) + + @test pl_conversion[2] isa DQConversion + @test ax_conversion[2] isa DQConversion + @test pl.plots[1][1][] == Point{2,Float32}.(1:5, 1:5) end end From bbf9effbed120d3c975973c3aae56fe7b9be9f48 Mon Sep 17 00:00:00 2001 From: icweaver Date: Sat, 29 Mar 2025 11:29:12 -0700 Subject: [PATCH 03/23] cleanup --- ext/DynamicQuantitiesMakieExt.jl | 34 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/ext/DynamicQuantitiesMakieExt.jl b/ext/DynamicQuantitiesMakieExt.jl index 7d262d86..a7488990 100644 --- a/ext/DynamicQuantitiesMakieExt.jl +++ b/ext/DynamicQuantitiesMakieExt.jl @@ -1,6 +1,6 @@ module DynamicQuantitiesMakieExt -using DynamicQuantities: UnionAbstractQuantity, AbstractDimensions, ustrip, dimension +using DynamicQuantities: UnionAbstractQuantity, ustrip, dimension using TestItems: @testitem import Makie as M @@ -9,57 +9,57 @@ M.expand_dimensions(::M.PointBased, y::AbstractVector{<:UnionAbstractQuantity}) M.create_dim_conversion(::Type{<:UnionAbstractQuantity}) = DQConversion() M.MakieCore.should_dim_convert(::Type{<:UnionAbstractQuantity}) = true -unit_string(unit::D) where D <: AbstractDimensions = string(dimension(unit)) +unit_string(quantitiy::UnionAbstractQuantity) = string(dimension(quantitiy)) function unit_convert(::M.Automatic, x) x end -function unit_convert(unit::D, x::AbstractArray) where D <: AbstractDimensions - unit_convert.(Ref(unit), x) +function unit_convert(quantitiy::UnionAbstractQuantity, x::AbstractArray) + unit_convert.(Ref(quantitiy), x) end -function unit_convert(unit::D, value) where D <: AbstractDimensions - conv = value / unit +function unit_convert(quantitiy::UnionAbstractQuantity, value) + conv = value / quantitiy return Float64(ustrip(conv)) end struct DQConversion <: M.AbstractDimConversion - unit::M.Observable{Any} + quantity::M.Observable{Any} automatic_units::Bool units_in_label::M.Observable{Bool} end -function DQConversion(unit=M.automatic; units_in_label=true) - return DQConversion(dimension(unit), unit isa M.Automatic, units_in_label) +function DQConversion(quantitiy=M.automatic; units_in_label=true) + return DQConversion(quantitiy, quantitiy isa M.Automatic, units_in_label) end -M.needs_tick_update_observable(conversion::DQConversion) = conversion.unit +M.needs_tick_update_observable(conversion::DQConversion) = conversion.quantity function M.get_ticks(conversion::DQConversion, ticks, scale, formatter, vmin, vmax) - unit = conversion.unit[] - unit isa M.Automatic && return [], [] - unit_str = unit_string(unit) + quantity = conversion.quantity[] + quantity isa M.Automatic && return [], [] + unit_str = unit_string(quantity) tick_vals, labels = M.get_ticks(ticks, scale, formatter, vmin, vmax) return tick_vals, labels .* unit_str #string.(labels, string(dimension(conversion.quantities[]))) end function M.convert_dim_observable(conversion::DQConversion, value_obs::M.Observable, deregister) - result = map(conversion.unit, value_obs; ignore_equal_values=true) do unit, values + conversion.quantity[] = value_obs[][1] + result = map(conversion.quantity, value_obs; ignore_equal_values=true) do unit, values if !isempty(values) # try if conversion works, to through error if not! # Is there a function for this to check in DynamicQuantities? unit_convert(unit, values[1]) end - conversion.unit[] = dimension(values) - return unit_convert(conversion.unit[], values) + return unit_convert(conversion.quantity[], values) end append!(deregister, result.inputs) return result end function M.convert_dim_value(conversion::DQConversion, value::UnionAbstractQuantity) - return unit_convert(conversion.unit[], value) + return unit_convert(conversion.quantity[], value) end @testitem "conversion" begin From 5b7920f03ccff225b7c779323e6f47b8ca321fe7 Mon Sep 17 00:00:00 2001 From: icweaver Date: Sat, 29 Mar 2025 12:26:42 -0700 Subject: [PATCH 04/23] fixed conversion bug --- ext/DynamicQuantitiesMakieExt.jl | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ext/DynamicQuantitiesMakieExt.jl b/ext/DynamicQuantitiesMakieExt.jl index a7488990..ce42a121 100644 --- a/ext/DynamicQuantitiesMakieExt.jl +++ b/ext/DynamicQuantitiesMakieExt.jl @@ -9,18 +9,18 @@ M.expand_dimensions(::M.PointBased, y::AbstractVector{<:UnionAbstractQuantity}) M.create_dim_conversion(::Type{<:UnionAbstractQuantity}) = DQConversion() M.MakieCore.should_dim_convert(::Type{<:UnionAbstractQuantity}) = true -unit_string(quantitiy::UnionAbstractQuantity) = string(dimension(quantitiy)) +unit_string(quantity::UnionAbstractQuantity) = string(dimension(quantity)) function unit_convert(::M.Automatic, x) x end -function unit_convert(quantitiy::UnionAbstractQuantity, x::AbstractArray) - unit_convert.(Ref(quantitiy), x) +function unit_convert(quantity::UnionAbstractQuantity, x::AbstractArray) + unit_convert.(Ref(quantity), x) end -function unit_convert(quantitiy::UnionAbstractQuantity, value) - conv = value / quantitiy +function unit_convert(quantity::UnionAbstractQuantity, value) + conv = value / dimension(quantity) return Float64(ustrip(conv)) end @@ -30,8 +30,8 @@ struct DQConversion <: M.AbstractDimConversion units_in_label::M.Observable{Bool} end -function DQConversion(quantitiy=M.automatic; units_in_label=true) - return DQConversion(quantitiy, quantitiy isa M.Automatic, units_in_label) +function DQConversion(quantity=M.automatic; units_in_label=true) + return DQConversion(quantity, quantity isa M.Automatic, units_in_label) end M.needs_tick_update_observable(conversion::DQConversion) = conversion.quantity @@ -41,7 +41,7 @@ function M.get_ticks(conversion::DQConversion, ticks, scale, formatter, vmin, vm quantity isa M.Automatic && return [], [] unit_str = unit_string(quantity) tick_vals, labels = M.get_ticks(ticks, scale, formatter, vmin, vmax) - return tick_vals, labels .* unit_str #string.(labels, string(dimension(conversion.quantities[]))) + return tick_vals, labels .* unit_str end function M.convert_dim_observable(conversion::DQConversion, value_obs::M.Observable, deregister) @@ -52,14 +52,14 @@ function M.convert_dim_observable(conversion::DQConversion, value_obs::M.Observa # Is there a function for this to check in DynamicQuantities? unit_convert(unit, values[1]) end - return unit_convert(conversion.quantity[], values) + return M.convert_dim_value(conversion, values) end append!(deregister, result.inputs) return result end -function M.convert_dim_value(conversion::DQConversion, value::UnionAbstractQuantity) - return unit_convert(conversion.quantity[], value) +function M.convert_dim_value(conversion::DQConversion, values) + return unit_convert(conversion.quantity[], values) end @testitem "conversion" begin From 3362969070d3d32435c613080eae84940111c51d Mon Sep 17 00:00:00 2001 From: icweaver Date: Sat, 29 Mar 2025 13:04:08 -0700 Subject: [PATCH 05/23] ax.dim1/2_conversion working! --- ext/DynamicQuantitiesMakieExt.jl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ext/DynamicQuantitiesMakieExt.jl b/ext/DynamicQuantitiesMakieExt.jl index ce42a121..6025cd2c 100644 --- a/ext/DynamicQuantitiesMakieExt.jl +++ b/ext/DynamicQuantitiesMakieExt.jl @@ -20,7 +20,7 @@ function unit_convert(quantity::UnionAbstractQuantity, x::AbstractArray) end function unit_convert(quantity::UnionAbstractQuantity, value) - conv = value / dimension(quantity) + conv = value / quantity return Float64(ustrip(conv)) end @@ -45,7 +45,10 @@ function M.get_ticks(conversion::DQConversion, ticks, scale, formatter, vmin, vm end function M.convert_dim_observable(conversion::DQConversion, value_obs::M.Observable, deregister) - conversion.quantity[] = value_obs[][1] + # TODO: replace with update_extrema + if conversion.automatic_units + conversion.quantity[] = oneunit(value_obs[][1]) + end result = map(conversion.quantity, value_obs; ignore_equal_values=true) do unit, values if !isempty(values) # try if conversion works, to through error if not! From e439270722ed64aea475a07d3f4673d2df6aebd7 Mon Sep 17 00:00:00 2001 From: icweaver Date: Sat, 29 Mar 2025 13:19:57 -0700 Subject: [PATCH 06/23] started docs and tests --- docs/src/examples.md | 30 ++++++++++++++++++++++++++++++ ext/DynamicQuantitiesMakieExt.jl | 1 + test/Project.toml | 2 ++ 3 files changed, 33 insertions(+) diff --git a/docs/src/examples.md b/docs/src/examples.md index 47a056ee..7db91abe 100644 --- a/docs/src/examples.md +++ b/docs/src/examples.md @@ -504,3 +504,33 @@ function my_func(x::UnionAbstractQuantity{T,D}) where {T,D} return x / ustrip(x) end ``` + + +### Plotting + +!!! warn "Experimental" + +We can also use `DynamicQuantities.jl` for plotting with units in [`Makie.jl`](https://docs.makie.org/v0.22/). + +```julia +using CairoMakie, DynamicQuantities + +# Temporary until this is upstreamed to Makie.jl +const DQConversion = Base.get_extension(DynamicQuantities, :DynamicQuantitiesMakieExt).DQConversion + +scatter((6:10)u"m") + +# Note use of `us""` instead of `u""` +scatter((6:10)u"m"; axis=(; dim2_conversion=DQConversion(us"cm"))) +``` + +```julia +fig = Figure() + +ax = Axis(fig[1, 1]; dim2_conversion=DQConversion(us"m")) + +scatter!(ax, (6:10)u"m") +scatter!(ax, (6:10)u"km") + +fig +``` diff --git a/ext/DynamicQuantitiesMakieExt.jl b/ext/DynamicQuantitiesMakieExt.jl index 6025cd2c..dd3f4482 100644 --- a/ext/DynamicQuantitiesMakieExt.jl +++ b/ext/DynamicQuantitiesMakieExt.jl @@ -24,6 +24,7 @@ function unit_convert(quantity::UnionAbstractQuantity, value) return Float64(ustrip(conv)) end +# TODO: Maybe only allow symbolic units to avoid bugs? struct DQConversion <: M.AbstractDimConversion quantity::M.Observable{Any} automatic_units::Bool diff --git a/test/Project.toml b/test/Project.toml index cf8f60df..649a4e98 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,7 +1,9 @@ [deps] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" DispatchDoctor = "8d63f2c5-f18a-4cf2-ba9d-b3f60fc568c8" +DynamicQuantities = "06fc5a27-2a28-4c7c-a15d-362465fb6821" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" Ratios = "c84ed2f1-dad5-54f0-aa8e-dbefe2724439" SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" From e1637d3ace3712bf403b980bc471face41654806 Mon Sep 17 00:00:00 2001 From: icweaver Date: Sat, 29 Mar 2025 14:40:11 -0700 Subject: [PATCH 07/23] switched to 2-arg ustrip --- ext/DynamicQuantitiesMakieExt.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/DynamicQuantitiesMakieExt.jl b/ext/DynamicQuantitiesMakieExt.jl index dd3f4482..ac9a8162 100644 --- a/ext/DynamicQuantitiesMakieExt.jl +++ b/ext/DynamicQuantitiesMakieExt.jl @@ -20,8 +20,8 @@ function unit_convert(quantity::UnionAbstractQuantity, x::AbstractArray) end function unit_convert(quantity::UnionAbstractQuantity, value) - conv = value / quantity - return Float64(ustrip(conv)) + conv = ustrip(quantity, value) + return Float64(conv) end # TODO: Maybe only allow symbolic units to avoid bugs? From 8dcc31eba6f2192729fc9ccb6b8ffd48d0e845fb Mon Sep 17 00:00:00 2001 From: icweaver Date: Sun, 30 Mar 2025 11:41:00 -0700 Subject: [PATCH 08/23] more tests --- ext/DynamicQuantitiesMakieExt.jl | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/ext/DynamicQuantitiesMakieExt.jl b/ext/DynamicQuantitiesMakieExt.jl index ac9a8162..264b1b4d 100644 --- a/ext/DynamicQuantitiesMakieExt.jl +++ b/ext/DynamicQuantitiesMakieExt.jl @@ -66,12 +66,16 @@ function M.convert_dim_value(conversion::DQConversion, values) return unit_convert(conversion.quantity[], values) end -@testitem "conversion" begin +@testitem "1 arg expansion" begin using DynamicQuantities, Makie, Dates - const DQConversion = Base.get_extension(DynamicQuantities, :DynamicQuantitiesMakieExt).DQConversion f, ax, pl = scatter(u"m" .* (1:10)) @test pl isa Scatter{Tuple{Vector{Point2{Float64}}}} +end + +@testitem "recipe" begin + using DynamicQuantities, Makie, Dates + const DQConversion = Base.get_extension(DynamicQuantities, :DynamicQuantitiesMakieExt).DQConversion @recipe(DQPlot, x) do scene return Attributes() @@ -91,4 +95,25 @@ end @test pl.plots[1][1][] == Point{2,Float32}.(1:5, 1:5) end +@testitem "unit switching" begin + using DynamicQuantities, Makie + f, ax, pl = scatter((1:10)u"m") + @test_throws DynamicQuantities.DimensionError scatter!(ax, (1:10)u"kg") + @test_throws MethodError scatter!(ax, (1:10)) +end + +@testitem "observables cleanup" begin + using DynamicQuantities, Makie + + function test_cleanup(arg) + obs = Observable(arg) + f, ax, pl = scatter(obs) + @test length(obs.listeners) == 1 + delete!(ax, pl) + @test length(obs.listeners) == 0 + end + + test_cleanup([0.01u"km", 0.02u"km", 0.03u"km", 0.04u"km"]) +end + end From 331d8c623e44d7d145de395392e3b06b45653f04 Mon Sep 17 00:00:00 2001 From: icweaver Date: Sun, 30 Mar 2025 13:08:11 -0700 Subject: [PATCH 09/23] docs up --- docs/Project.toml | 1 + docs/src/examples.md | 69 ++++++++++++++++++++++++++++++++------------ 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/docs/Project.toml b/docs/Project.toml index dfa65cd1..e3e6bc6b 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,2 +1,3 @@ [deps] +CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" diff --git a/docs/src/examples.md b/docs/src/examples.md index 7db91abe..9b607b94 100644 --- a/docs/src/examples.md +++ b/docs/src/examples.md @@ -40,13 +40,13 @@ Also, using `DynamicQuantities.Constants`, we were able to obtain the (dimension Let's solve a simple projectile motion problem. First load the `DynamicQuantities` module: -```julia +```@example projectile using DynamicQuantities ``` Set up initial conditions as quantities: -```julia +```@example projectile # Can explicitly import units: using DynamicQuantities: km, m, s, min @@ -54,39 +54,44 @@ y0 = 10km v0 = 250m/s θ = deg2rad(60) g = 9.81m/s^2 +nothing # hide ``` Next, we use trig functions to calculate x and y components of initial velocity. `vx0` is the x component and `vy0` is the y component: -```julia +```@example projectile vx0 = v0 * cos(θ) vy0 = v0 * sin(θ) +nothing # hide ``` Next, let's create a time vector from 0 seconds to 1.3 minutes. Note that these are the same dimension (time), so it's fine to treat them as dimensionally equivalent! -```julia +```@example projectile t = range(0s, 1.3min, length=100) +nothing # hide ``` Next, use kinematic equations to calculate x and y as a function of time. `x(t)` is the x position at time t, and `y(t)` is the y position: -```julia +```@example projectile x(t) = vx0*t y(t) = vy0*t - 0.5*g*t^2 + y0 +nothing # hide ``` These are functions, so let's evaluate them: -```julia +```@example projectile x_si = x.(t) y_si = y.(t) +nothing # hide ``` These are regular vectors of quantities @@ -106,6 +111,8 @@ Now, we plot: plot(x_km, y_km, label="Trajectory", xlabel="x [km]", ylabel="y [km]") ``` +See [Plotting](@ref) for more plotting support with units. + ## 3. Using dimensional angles Say that we wish to track angles as a unit, rather than assume @@ -508,29 +515,55 @@ end ### Plotting -!!! warn "Experimental" +We can use [`Makie.jl`](https://docs.makie.org/v0.22/) to create plots with units. Below are a few usage examples. See [Makie.jl > Dimension conversion](https://docs.makie.org/stable/explanations/dim-converts#Current-conversions-in-Makie) for more. -We can also use `DynamicQuantities.jl` for plotting with units in [`Makie.jl`](https://docs.makie.org/v0.22/). +!!! warning "Experimental" + Unit support is still a new feature, so please report an issue if you notice any unintended behavior. -```julia -using CairoMakie, DynamicQuantities +Continuing from [2. Projectile motion](@ref), we can also plot `x_si` and `y_si` directly without needing to manually strip their units beforehand: + +```@example projectile +using CairoMakie + +lines(x_si, y_si; axis=(xlabel="x", ylabel="y")) +``` + +To convert units, we pass a `DQConversion` object to `axis` with our desired unit: + +```@example projectile # Temporary until this is upstreamed to Makie.jl const DQConversion = Base.get_extension(DynamicQuantities, :DynamicQuantitiesMakieExt).DQConversion -scatter((6:10)u"m") - -# Note use of `us""` instead of `u""` -scatter((6:10)u"m"; axis=(; dim2_conversion=DQConversion(us"cm"))) +lines(x_si, y_si; + axis = ( + xlabel = "x", + ylabel = "y", + dim1_conversion = DQConversion(us"km"), + dim2_conversion = DQConversion(us"km"), + ) +) ``` -```julia +!!! warning + Make sure to use [Symbolic Dimensions](@ref) for this conversion to work properly. + +Finally, the desired units for a figure can also be set ahead of time. All plot objects within it will automatically convert to the given units: + +```@example projectile fig = Figure() -ax = Axis(fig[1, 1]; dim2_conversion=DQConversion(us"m")) +ax = Axis(fig[1, 1]; + xlabel = "time", + ylabel = "displacement", + dim1_conversion=DQConversion(us"s"), + dim2_conversion=DQConversion(us"km"), +) + +lines!(ax, t, x_si; label="x") +lines!(ax, t, y_si; label="y") -scatter!(ax, (6:10)u"m") -scatter!(ax, (6:10)u"km") +axislegend() fig ``` From 64923479988d6431b2a640262c30ecd75294ae79 Mon Sep 17 00:00:00 2001 From: icweaver Date: Sun, 30 Mar 2025 13:50:29 -0700 Subject: [PATCH 10/23] added docstring for DQConversion --- ext/DynamicQuantitiesMakieExt.jl | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ext/DynamicQuantitiesMakieExt.jl b/ext/DynamicQuantitiesMakieExt.jl index 264b1b4d..8d22f25e 100644 --- a/ext/DynamicQuantitiesMakieExt.jl +++ b/ext/DynamicQuantitiesMakieExt.jl @@ -25,6 +25,26 @@ function unit_convert(quantity::UnionAbstractQuantity, value) end # TODO: Maybe only allow symbolic units to avoid bugs? +""" + DQConversion(unit=automatic; units_in_label=false) +Allows to plot arrays of DynamicQuantity objects into an axis. +# Arguments +- `unit=automatic`: sets the unit as conversion target. If left at automatic, the best unit will be chosen for all plots + values plotted to the axis (e.g. years for long periods, or km for long distances, or nanoseconds for short times). +- `units_in_label=true`: controls, whether plots are shown in the label_prefix of the axis labels, or in the tick labels +# Examples +```julia +using DynamicQuantities, CairoMakie +# DQConversion will get chosen automatically: +scatter(1:4, [1u"ns", 2u"ns", 3u"ns", 4u"ns"]) +``` +Fix unit to always use Meter & display unit in the xlabel: +```julia +# Temporary until this is upstreamed to Makie.jl +const DQConversion = Base.get_extension(DynamicQuantities, :DynamicQuantitiesMakieExt).DQConversion +dqc = DQConversion(us"m"; units_in_label=false) +scatter(1:4, [0.01u"km", 0.02u"km", 0.03u"km", 0.04u"km"]; axis=(dim2_conversion=dqc, xlabel="x (km)")) +``` +""" struct DQConversion <: M.AbstractDimConversion quantity::M.Observable{Any} automatic_units::Bool From b307dd8ffb228b041067198482b36df5325e90a5 Mon Sep 17 00:00:00 2001 From: icweaver Date: Sun, 30 Mar 2025 13:55:14 -0700 Subject: [PATCH 11/23] enabled units_in_label option --- ext/DynamicQuantitiesMakieExt.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ext/DynamicQuantitiesMakieExt.jl b/ext/DynamicQuantitiesMakieExt.jl index 8d22f25e..c38af215 100644 --- a/ext/DynamicQuantitiesMakieExt.jl +++ b/ext/DynamicQuantitiesMakieExt.jl @@ -62,7 +62,10 @@ function M.get_ticks(conversion::DQConversion, ticks, scale, formatter, vmin, vm quantity isa M.Automatic && return [], [] unit_str = unit_string(quantity) tick_vals, labels = M.get_ticks(ticks, scale, formatter, vmin, vmax) - return tick_vals, labels .* unit_str + if conversion.units_in_label[] + labels = labels .* unit_str + end + return tick_vals, labels end function M.convert_dim_observable(conversion::DQConversion, value_obs::M.Observable, deregister) From 43f95ab8584122495e28e5fa27f23e6ecac4d58d Mon Sep 17 00:00:00 2001 From: icweaver Date: Sun, 30 Mar 2025 14:43:00 -0700 Subject: [PATCH 12/23] reftest --- ext/DynamicQuantitiesMakieExt.jl | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/ext/DynamicQuantitiesMakieExt.jl b/ext/DynamicQuantitiesMakieExt.jl index c38af215..b84bcb6c 100644 --- a/ext/DynamicQuantitiesMakieExt.jl +++ b/ext/DynamicQuantitiesMakieExt.jl @@ -139,4 +139,23 @@ end test_cleanup([0.01u"km", 0.02u"km", 0.03u"km", 0.04u"km"]) end +# TODO: Move upstream to Makie.jl +@testitem "reftest" begin + using DynamicQuantities, Makie + const DQConversion = Base.get_extension(DynamicQuantities, :DynamicQuantitiesMakieExt).DQConversion + + fig = Figure() + + ax1 = Axis(fig[1, 1]; dim2_conversion=DQConversion(us"J/s")) + ax2 = Axis(fig[1, 2]; dim2_conversion=DQConversion(us"mm/m^2")) + ax3 = Axis(fig[2, 1]; dim1_conversion=DQConversion(us"W/m^2"), dim2_conversion=DQConversion(us"μm")) + ax4 = Axis(fig[2, 2]; dim1_conversion=DQConversion(us"W/m^2")) + + scatter!(ax1, (1:10) .* u"J/s") + scatter!(ax2, (1:10) .* u"K", exp.(1:10) .* u"mm/m^2") + scatter!(ax3, 10 .^ (1:6) .* u"W/m^2", (1:6) .* 1000 .* u"nm") + scatter!(ax4, (0:10) .* u"W/m^2", (0:10) .* u"g") + scatter!(ax4, (0:10) .* u"kW/m^2", (0:10) .* u"kg") +end + end From 90c5d46cf891c6484956dabc37f67649260de25a Mon Sep 17 00:00:00 2001 From: icweaver Date: Sun, 30 Mar 2025 14:43:02 -0700 Subject: [PATCH 13/23] up --- docs/Project.toml | 1 + docs/make.jl | 62 +++++++++++++++++++++++------------------------ 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/docs/Project.toml b/docs/Project.toml index e3e6bc6b..d09d2cd7 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,3 +1,4 @@ [deps] CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +DynamicQuantities = "06fc5a27-2a28-4c7c-a15d-362465fb6821" diff --git a/docs/make.jl b/docs/make.jl index 96120d1f..fa5845b2 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -6,27 +6,27 @@ using Documenter DocMeta.setdocmeta!(DynamicQuantities, :DocTestSetup, :(using DynamicQuantities); recursive=true) doctest(DynamicQuantities) -readme = open(dirname(@__FILE__) * "/../README.md") do io - read(io, String) -end - -# We replace every instance of with ![](IMAGE). -readme = replace(readme, r"]+>.*" => s"![](\1)") - -# Then, we remove any line with " s"") - -# Finally, we read in file docs/src/index_base.md: -index_base = open(dirname(@__FILE__) * "/src/index_base.md") do io - read(io, String) -end - -# And then we create "/src/index.md": -open(dirname(@__FILE__) * "/src/index.md", "w") do io - write(io, readme) - write(io, "\n") - write(io, index_base) -end +#readme = open(dirname(@__FILE__) * "/../README.md") do io +# read(io, String) +#end +# +## We replace every instance of with ![](IMAGE). +#readme = replace(readme, r"]+>.*" => s"![](\1)") +# +## Then, we remove any line with " s"") +# +## Finally, we read in file docs/src/index_base.md: +#index_base = open(dirname(@__FILE__) * "/src/index_base.md") do io +# read(io, String) +#end +# +## And then we create "/src/index.md": +#open(dirname(@__FILE__) * "/src/index.md", "w") do io +# write(io, readme) +# write(io, "\n") +# write(io, index_base) +#end makedocs(; modules=[DynamicQuantities, DynamicQuantities.Units], @@ -51,15 +51,15 @@ makedocs(; warnonly = [:missing_docs] ) -deploydocs(; - repo="github.com/SymbolicML/DynamicQuantities.jl", - devbranch="main" -) +#deploydocs(; +# repo="github.com/SymbolicML/DynamicQuantities.jl", +# devbranch="main" +#) # Mirror to DAMTP: -ENV["DOCUMENTER_KEY"] = ENV["DOCUMENTER_KEY_CAM"] -ENV["GITHUB_REPOSITORY"] = "ai-damtp-cam-ac-uk/dynamicquantities.git" -deploydocs(; - repo="github.com/ai-damtp-cam-ac-uk/dynamicquantities.git", - devbranch="main" -) +#ENV["DOCUMENTER_KEY"] = ENV["DOCUMENTER_KEY_CAM"] +#ENV["GITHUB_REPOSITORY"] = "ai-damtp-cam-ac-uk/dynamicquantities.git" +#deploydocs(; +# repo="github.com/ai-damtp-cam-ac-uk/dynamicquantities.git", +# devbranch="main" +#) From 7d83d1bb200408b3502d6b2da1f84836de5c5e4a Mon Sep 17 00:00:00 2001 From: icweaver Date: Sun, 30 Mar 2025 15:47:32 -0700 Subject: [PATCH 14/23] workaround for broadcasting error with s --- ext/DynamicQuantitiesMakieExt.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ext/DynamicQuantitiesMakieExt.jl b/ext/DynamicQuantitiesMakieExt.jl index b84bcb6c..ec3b3bf3 100644 --- a/ext/DynamicQuantitiesMakieExt.jl +++ b/ext/DynamicQuantitiesMakieExt.jl @@ -16,7 +16,10 @@ function unit_convert(::M.Automatic, x) end function unit_convert(quantity::UnionAbstractQuantity, x::AbstractArray) - unit_convert.(Ref(quantity), x) + # Note: unit_convert.(Ref(quantity), x) currently causes broadcasting error for `QuantityArray`s + map(x) do xi + unit_convert(quantity, xi) + end end function unit_convert(quantity::UnionAbstractQuantity, value) @@ -71,6 +74,7 @@ end function M.convert_dim_observable(conversion::DQConversion, value_obs::M.Observable, deregister) # TODO: replace with update_extrema if conversion.automatic_units + @info :wuuh value_obs[] conversion.quantity[] = oneunit(value_obs[][1]) end result = map(conversion.quantity, value_obs; ignore_equal_values=true) do unit, values From 66f8899842eb32e3f781a738a59b794daddc16e5 Mon Sep 17 00:00:00 2001 From: icweaver Date: Sun, 30 Mar 2025 16:54:03 -0700 Subject: [PATCH 15/23] added reference to AoG --- docs/src/examples.md | 3 +++ ext/DynamicQuantitiesMakieExt.jl | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/src/examples.md b/docs/src/examples.md index 9b607b94..1d45f247 100644 --- a/docs/src/examples.md +++ b/docs/src/examples.md @@ -567,3 +567,6 @@ axislegend() fig ``` + +!!! tip + For nice unit support with Makie.jl and tabular data, see [AlgebraOfGraphics.jl > Units](https://aog.makie.org/dev/examples/scales/units#units). diff --git a/ext/DynamicQuantitiesMakieExt.jl b/ext/DynamicQuantitiesMakieExt.jl index ec3b3bf3..012aded6 100644 --- a/ext/DynamicQuantitiesMakieExt.jl +++ b/ext/DynamicQuantitiesMakieExt.jl @@ -74,7 +74,6 @@ end function M.convert_dim_observable(conversion::DQConversion, value_obs::M.Observable, deregister) # TODO: replace with update_extrema if conversion.automatic_units - @info :wuuh value_obs[] conversion.quantity[] = oneunit(value_obs[][1]) end result = map(conversion.quantity, value_obs; ignore_equal_values=true) do unit, values From 8336dc519fe2b57fda87370ee9b600a461addb5c Mon Sep 17 00:00:00 2001 From: icweaver Date: Sun, 30 Mar 2025 17:18:49 -0700 Subject: [PATCH 16/23] oop, put make.jl back --- docs/make.jl | 64 ++++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index fa5845b2..09c848cd 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -6,27 +6,27 @@ using Documenter DocMeta.setdocmeta!(DynamicQuantities, :DocTestSetup, :(using DynamicQuantities); recursive=true) doctest(DynamicQuantities) -#readme = open(dirname(@__FILE__) * "/../README.md") do io -# read(io, String) -#end -# -## We replace every instance of with ![](IMAGE). -#readme = replace(readme, r"]+>.*" => s"![](\1)") -# -## Then, we remove any line with " s"") -# -## Finally, we read in file docs/src/index_base.md: -#index_base = open(dirname(@__FILE__) * "/src/index_base.md") do io -# read(io, String) -#end -# -## And then we create "/src/index.md": -#open(dirname(@__FILE__) * "/src/index.md", "w") do io -# write(io, readme) -# write(io, "\n") -# write(io, index_base) -#end +readme = open(dirname(@__FILE__) * "/../README.md") do io + read(io, String) +end + +# We replace every instance of with ![](IMAGE). +readme = replace(readme, r"]+>.*" => s"![](\1)") + +# Then, we remove any line with " s"") + +# Finally, we read in file docs/src/index_base.md: +index_base = open(dirname(@__FILE__) * "/src/index_base.md") do io + read(io, String) +end + +# And then we create "/src/index.md": +open(dirname(@__FILE__) * "/src/index.md", "w") do io + write(io, readme) + write(io, "\n") + write(io, index_base) +end makedocs(; modules=[DynamicQuantities, DynamicQuantities.Units], @@ -51,15 +51,15 @@ makedocs(; warnonly = [:missing_docs] ) -#deploydocs(; -# repo="github.com/SymbolicML/DynamicQuantities.jl", -# devbranch="main" -#) +deploydocs(; + repo="github.com/SymbolicML/DynamicQuantities.jl", + devbranch="main" +) -# Mirror to DAMTP: -#ENV["DOCUMENTER_KEY"] = ENV["DOCUMENTER_KEY_CAM"] -#ENV["GITHUB_REPOSITORY"] = "ai-damtp-cam-ac-uk/dynamicquantities.git" -#deploydocs(; -# repo="github.com/ai-damtp-cam-ac-uk/dynamicquantities.git", -# devbranch="main" -#) + Mirror to DAMTP: +ENV["DOCUMENTER_KEY"] = ENV["DOCUMENTER_KEY_CAM"] +ENV["GITHUB_REPOSITORY"] = "ai-damtp-cam-ac-uk/dynamicquantities.git" +deploydocs(; + repo="github.com/ai-damtp-cam-ac-uk/dynamicquantities.git", + devbranch="main" +) From 1c5127e9d264bab0eb056ab85297aa28ac00189b Mon Sep 17 00:00:00 2001 From: icweaver Date: Sun, 30 Mar 2025 17:19:42 -0700 Subject: [PATCH 17/23] fr this time --- docs/make.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/make.jl b/docs/make.jl index 09c848cd..96120d1f 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -56,7 +56,7 @@ deploydocs(; devbranch="main" ) - Mirror to DAMTP: +# Mirror to DAMTP: ENV["DOCUMENTER_KEY"] = ENV["DOCUMENTER_KEY_CAM"] ENV["GITHUB_REPOSITORY"] = "ai-damtp-cam-ac-uk/dynamicquantities.git" deploydocs(; From 2893c01ac2e34cb4a2e6735e7c6fae45918f2bc5 Mon Sep 17 00:00:00 2001 From: icweaver Date: Sun, 30 Mar 2025 17:22:06 -0700 Subject: [PATCH 18/23] whitespace --- docs/src/examples.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/src/examples.md b/docs/src/examples.md index 1d45f247..6bb22819 100644 --- a/docs/src/examples.md +++ b/docs/src/examples.md @@ -512,7 +512,6 @@ function my_func(x::UnionAbstractQuantity{T,D}) where {T,D} end ``` - ### Plotting We can use [`Makie.jl`](https://docs.makie.org/v0.22/) to create plots with units. Below are a few usage examples. See [Makie.jl > Dimension conversion](https://docs.makie.org/stable/explanations/dim-converts#Current-conversions-in-Makie) for more. @@ -520,7 +519,6 @@ We can use [`Makie.jl`](https://docs.makie.org/v0.22/) to create plots with unit !!! warning "Experimental" Unit support is still a new feature, so please report an issue if you notice any unintended behavior. - Continuing from [2. Projectile motion](@ref), we can also plot `x_si` and `y_si` directly without needing to manually strip their units beforehand: ```@example projectile From d412ed6a11a5badfd2a33f86decec0be0d8f2b63 Mon Sep 17 00:00:00 2001 From: Ian Weaver Date: Mon, 12 May 2025 19:00:57 -0700 Subject: [PATCH 19/23] Update ext/DynamicQuantitiesMakieExt.jl Co-authored-by: Miles Cranmer --- ext/DynamicQuantitiesMakieExt.jl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ext/DynamicQuantitiesMakieExt.jl b/ext/DynamicQuantitiesMakieExt.jl index 012aded6..0619c5c7 100644 --- a/ext/DynamicQuantitiesMakieExt.jl +++ b/ext/DynamicQuantitiesMakieExt.jl @@ -17,9 +17,7 @@ end function unit_convert(quantity::UnionAbstractQuantity, x::AbstractArray) # Note: unit_convert.(Ref(quantity), x) currently causes broadcasting error for `QuantityArray`s - map(x) do xi - unit_convert(quantity, xi) - end + map(Base.Fix1(unit_convert, quantity), x) end function unit_convert(quantity::UnionAbstractQuantity, value) From 1277f6f3f0880041ece2699a65d4f51228dae844 Mon Sep 17 00:00:00 2001 From: icweaver Date: Mon, 12 May 2025 19:04:11 -0700 Subject: [PATCH 20/23] relax Makie compat, lower bound set to unitful axis PR --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 8a17114e..df029d09 100644 --- a/Project.toml +++ b/Project.toml @@ -26,7 +26,7 @@ DynamicQuantitiesUnitfulExt = "Unitful" [compat] DispatchDoctor = "0.4" LinearAlgebra = "1" -Makie = "0.22.2" +Makie = ">= 0.21.0" Measurements = "2" PrecompileTools = "1" ScientificTypes = "3" From 1afe36454dbb83465e90807b97cb36fc459ee122 Mon Sep 17 00:00:00 2001 From: Ian Weaver Date: Mon, 12 May 2025 19:13:15 -0700 Subject: [PATCH 21/23] Update test/Project.toml Co-authored-by: Miles Cranmer --- test/Project.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/test/Project.toml b/test/Project.toml index 649a4e98..d7f27a97 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,7 +1,6 @@ [deps] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" DispatchDoctor = "8d63f2c5-f18a-4cf2-ba9d-b3f60fc568c8" -DynamicQuantities = "06fc5a27-2a28-4c7c-a15d-362465fb6821" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" From 2d09a663cddda280ce139cc969c85e333c248e8b Mon Sep 17 00:00:00 2001 From: icweaver Date: Mon, 12 May 2025 19:23:35 -0700 Subject: [PATCH 22/23] space out DQConversion docstring a bit --- ext/DynamicQuantitiesMakieExt.jl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ext/DynamicQuantitiesMakieExt.jl b/ext/DynamicQuantitiesMakieExt.jl index 0619c5c7..fb3eef5d 100644 --- a/ext/DynamicQuantitiesMakieExt.jl +++ b/ext/DynamicQuantitiesMakieExt.jl @@ -28,21 +28,30 @@ end # TODO: Maybe only allow symbolic units to avoid bugs? """ DQConversion(unit=automatic; units_in_label=false) + Allows to plot arrays of DynamicQuantity objects into an axis. + # Arguments - `unit=automatic`: sets the unit as conversion target. If left at automatic, the best unit will be chosen for all plots + values plotted to the axis (e.g. years for long periods, or km for long distances, or nanoseconds for short times). - `units_in_label=true`: controls, whether plots are shown in the label_prefix of the axis labels, or in the tick labels + # Examples + ```julia using DynamicQuantities, CairoMakie + # DQConversion will get chosen automatically: scatter(1:4, [1u"ns", 2u"ns", 3u"ns", 4u"ns"]) ``` + Fix unit to always use Meter & display unit in the xlabel: + ```julia # Temporary until this is upstreamed to Makie.jl const DQConversion = Base.get_extension(DynamicQuantities, :DynamicQuantitiesMakieExt).DQConversion + dqc = DQConversion(us"m"; units_in_label=false) + scatter(1:4, [0.01u"km", 0.02u"km", 0.03u"km", 0.04u"km"]; axis=(dim2_conversion=dqc, xlabel="x (km)")) ``` """ From ef2d0d85c909aea049b030ff61599ff370d28e12 Mon Sep 17 00:00:00 2001 From: icweaver Date: Tue, 13 May 2025 13:11:07 -0700 Subject: [PATCH 23/23] enforce SymbolicDimensions for DQConversion --- docs/src/examples.md | 4 ++-- ext/DynamicQuantitiesMakieExt.jl | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/src/examples.md b/docs/src/examples.md index 6bb22819..24bd198c 100644 --- a/docs/src/examples.md +++ b/docs/src/examples.md @@ -543,8 +543,8 @@ lines(x_si, y_si; ) ``` -!!! warning - Make sure to use [Symbolic Dimensions](@ref) for this conversion to work properly. +!!! note + A [Symbolic Dimensions](@ref) object is passed to `DQConversion` for this conversion to work properly. Passing a regular `Quantity`, e.g., (u"km") will throw an error. Finally, the desired units for a figure can also be set ahead of time. All plot objects within it will automatically convert to the given units: diff --git a/ext/DynamicQuantitiesMakieExt.jl b/ext/DynamicQuantitiesMakieExt.jl index fb3eef5d..0d1ab993 100644 --- a/ext/DynamicQuantitiesMakieExt.jl +++ b/ext/DynamicQuantitiesMakieExt.jl @@ -1,6 +1,6 @@ module DynamicQuantitiesMakieExt -using DynamicQuantities: UnionAbstractQuantity, ustrip, dimension +using DynamicQuantities: UnionAbstractQuantity, SymbolicDimensions, ustrip, dimension using TestItems: @testitem import Makie as M @@ -25,7 +25,6 @@ function unit_convert(quantity::UnionAbstractQuantity, value) return Float64(conv) end -# TODO: Maybe only allow symbolic units to avoid bugs? """ DQConversion(unit=automatic; units_in_label=false) @@ -40,7 +39,7 @@ Allows to plot arrays of DynamicQuantity objects into an axis. ```julia using DynamicQuantities, CairoMakie -# DQConversion will get chosen automatically: +# DQConversion will get chosen automatically, scatter(1:4, [1u"ns", 2u"ns", 3u"ns", 4u"ns"]) ``` @@ -56,7 +55,7 @@ scatter(1:4, [0.01u"km", 0.02u"km", 0.03u"km", 0.04u"km"]; axis=(dim2_conversion ``` """ struct DQConversion <: M.AbstractDimConversion - quantity::M.Observable{Any} + quantity::M.Observable{UnionAbstractQuantity{T1, SymbolicDimensions{T2}} where {T1 <: Real, T2 <: Real}} automatic_units::Bool units_in_label::M.Observable{Bool} end