diff --git a/.github/workflows/Docs.yml b/.github/workflows/Docs.yml index 713a076214e..5362c068677 100644 --- a/.github/workflows/Docs.yml +++ b/.github/workflows/Docs.yml @@ -1,16 +1,14 @@ name: Docs build and deploy on: - push: + pull_request: branches: - - main - master - - breaking-release - tags: '*' - pull_request: + - sd/beta-20 + push: + tags: + - '*' branches: - - main - master - - breaking-release concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} diff --git a/.github/workflows/cairomakie.yaml b/.github/workflows/cairomakie.yaml index 8f314850197..4f1c4d90892 100644 --- a/.github/workflows/cairomakie.yaml +++ b/.github/workflows/cairomakie.yaml @@ -6,15 +6,10 @@ on: - '*.md' branches: - master - - breaking-release + - sd/beta-20 push: - paths-ignore: - - 'docs/**' - - '*.md' - branches: - - master - - breaking-release - tags: '*' + tags: + - '*' concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f8e0b4ee222..70e6bbf0fdd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,15 +6,12 @@ on: - '*.md' branches: - master - - breaking-release + - sd/beta-20 push: - paths-ignore: - - 'docs/**' - - '*.md' + tags: + - '*' branches: - master - - breaking-release - tags: '*' concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} diff --git a/.github/workflows/compilation-benchmark.yaml b/.github/workflows/compilation-benchmark.yaml index fabccef141b..a3255a6030b 100644 --- a/.github/workflows/compilation-benchmark.yaml +++ b/.github/workflows/compilation-benchmark.yaml @@ -6,7 +6,7 @@ on: - '*.md' branches: - master - - breaking-release + - sd/beta-20 jobs: benchmark: name: ${{ matrix.package }} diff --git a/.github/workflows/glmakie.yaml b/.github/workflows/glmakie.yaml index 10e80561807..6723f735052 100644 --- a/.github/workflows/glmakie.yaml +++ b/.github/workflows/glmakie.yaml @@ -6,15 +6,12 @@ on: - '*.md' branches: - master - - breaking-release + - sd/beta-20 push: - paths-ignore: - - 'docs/**' - - '*.md' + tags: + - '*' branches: - master - - breaking-release - tags: '*' concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} @@ -44,7 +41,7 @@ jobs: version: ${{ matrix.version }} arch: ${{ matrix.arch }} - uses: julia-actions/cache@v1 - - run: sudo apt-get update && sudo apt-get install -y xorg-dev mesa-utils xvfb libgl1 freeglut3-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxext-dev + - run: sudo apt-get update && sudo apt-get install -y xorg-dev mesa-utils xvfb libgl1 freeglut3-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxext-dev xsettingsd x11-xserver-utils - name: Install Julia dependencies shell: julia --project=monorepo {0} run: | diff --git a/.github/workflows/rprmakie.yaml b/.github/workflows/rprmakie.yaml index d6e301ec30b..6b5e7a89e7c 100644 --- a/.github/workflows/rprmakie.yaml +++ b/.github/workflows/rprmakie.yaml @@ -6,15 +6,12 @@ on: - '*.md' branches: - master - - breaking-release + - sd/beta-20 push: - paths-ignore: - - 'docs/**' - - '*.md' + tags: + - '*' branches: - master - - breaking-release - tags: '*' concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} diff --git a/.github/workflows/wglmakie.yaml b/.github/workflows/wglmakie.yaml index 242e8b2056c..2942af72af5 100644 --- a/.github/workflows/wglmakie.yaml +++ b/.github/workflows/wglmakie.yaml @@ -6,16 +6,12 @@ on: - '*.md' branches: - master - - breaking-release + - sd/beta-20 push: - paths-ignore: - - 'docs/**' - - '*.md' + tags: + - '*' branches: - master - - breaking-release - tags: '*' - concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true diff --git a/CairoMakie/Project.toml b/CairoMakie/Project.toml index 40a15c43faa..7c6bf59841e 100644 --- a/CairoMakie/Project.toml +++ b/CairoMakie/Project.toml @@ -1,7 +1,7 @@ name = "CairoMakie" uuid = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" author = ["Simon Danisch "] -version = "0.10.8" +version = "0.10.9" [deps] Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" @@ -23,7 +23,7 @@ FFTW = "1" FileIO = "1.1" FreeType = "3, 4.0" GeometryBasics = "0.4.1" -Makie = "=0.19.8" +Makie = "=0.19.9" PrecompileTools = "1.0" julia = "1.3" diff --git a/CairoMakie/src/primitives.jl b/CairoMakie/src/primitives.jl index 212b6ba1f62..f636b129b8c 100644 --- a/CairoMakie/src/primitives.jl +++ b/CairoMakie/src/primitives.jl @@ -3,8 +3,7 @@ ################################################################################ function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Union{Lines, LineSegments})) - fields = @get_attribute(primitive, (color, linewidth, linestyle)) - linestyle = Makie.convert_attribute(linestyle, Makie.key"linestyle"()) + @get_attribute(primitive, (color, linewidth, linestyle)) ctx = screen.context model = primitive[:model][] positions = primitive[1][] @@ -245,7 +244,7 @@ function draw_multi(primitive::Lines, ctx, positions, colors::AbstractArray, lin this_linewidth != prev_linewidth && error("Encountered two different linewidth values $prev_linewidth and $this_linewidth in `lines` at index $(i-1). Different linewidths in one line are only permitted in CairoMakie when separated by a NaN point.") Cairo.line_to(ctx, this_position...) prev_continued = true - + if i == lastindex(positions) # this is the last element so stroke this Cairo.set_line_width(ctx, this_linewidth) @@ -293,8 +292,7 @@ end ################################################################################ function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Scatter)) - fields = @get_attribute(primitive, (markersize, strokecolor, strokewidth, marker, marker_offset, rotations)) - @get_attribute(primitive, (transform_marker,)) + @get_attribute(primitive, (markersize, strokecolor, strokewidth, marker, marker_offset, rotations, transform_marker)) ctx = screen.context model = primitive.model[] @@ -306,22 +304,15 @@ function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Scat colors = to_color(primitive.calculated_colors[]) - markerspace = to_value(get(primitive, :markerspace, :pixel)) - space = to_value(get(primitive, :space, :data)) - + markerspace = primitive.markerspace[] + space = primitive.space[] transfunc = Makie.transform_func(primitive) - marker_conv = _marker_convert(marker) - - draw_atomic_scatter(scene, ctx, transfunc, colors, markersize, strokecolor, strokewidth, marker_conv, marker_offset, rotations, model, positions, size_model, font, markerspace, space) + return draw_atomic_scatter(scene, ctx, transfunc, colors, markersize, strokecolor, strokewidth, marker, + marker_offset, rotations, model, positions, size_model, font, markerspace, + space) end -# an array of markers is converted to string by itself, which is inconvenient for the iteration logic -_marker_convert(markers::AbstractArray) = map(m -> convert_attribute(m, key"marker"(), key"scatter"()), markers) -_marker_convert(marker) = convert_attribute(marker, key"marker"(), key"scatter"()) -# image arrays need to be converted as a whole -_marker_convert(marker::AbstractMatrix{<:Colorant}) = [ convert_attribute(marker, key"marker"(), key"scatter"()) ] - function draw_atomic_scatter(scene, ctx, transfunc, colors, markersize, strokecolor, strokewidth, marker, marker_offset, rotations, model, positions, size_model, font, markerspace, space) broadcast_foreach(positions, colors, markersize, strokecolor, strokewidth, marker, marker_offset, remove_billboard(rotations)) do point, col, @@ -336,15 +327,14 @@ function draw_atomic_scatter(scene, ctx, transfunc, colors, markersize, strokeco Cairo.set_source_rgba(ctx, rgbatuple(col)...) Cairo.save(ctx) - marker_converted = Makie.to_spritemarker(m) # Setting a markersize of 0.0 somehow seems to break Cairos global state? # At least it stops drawing any marker afterwards # TODO, maybe there's something wrong somewhere else? if !(norm(scale) ≈ 0.0) - if marker_converted isa Char - draw_marker(ctx, marker_converted, best_font(m, font), pos, scale, strokecolor, strokewidth, offset, rotation) + if m isa Char + draw_marker(ctx, m, best_font(m, font), pos, scale, strokecolor, strokewidth, offset, rotation) else - draw_marker(ctx, marker_converted, pos, scale, strokecolor, strokewidth, offset, rotation) + draw_marker(ctx, m, pos, scale, strokecolor, strokewidth, offset, rotation) end end Cairo.restore(ctx) @@ -691,7 +681,7 @@ function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Unio t = Makie.transform_func(primitive) identity_transform = (t === identity || t isa Tuple && all(x-> x === identity, t)) && (abs(model[1, 2]) < 1e-15) regular_grid = xs isa AbstractRange && ys isa AbstractRange - xy_aligned = let + xy_aligned = let # Only allow scaling and translation pv = scene.camera.projectionview[] M = Mat4f( @@ -720,7 +710,7 @@ function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Unio xymax = project_position(scene, space, Point2f(last.(imsize)), model) w, h = xymax .- xy - can_use_fast_path = !(is_vector && !interpolate) && regular_grid && identity_transform && + can_use_fast_path = !(is_vector && !interpolate) && regular_grid && identity_transform && (interpolate || xy_aligned) use_fast_path = can_use_fast_path && !disable_fast_path @@ -862,23 +852,19 @@ nan2zero(x) = !isnan(x) * x function draw_mesh3D(scene, screen, attributes, mesh; pos = Vec4f(0), scale = 1f0) - # Priorize colors of the mesh if present - @get_attribute(attributes, (color,)) + @get_attribute(attributes, (shading, diffuse, specular, shininess, faceculling)) matcap = to_value(get(attributes, :matcap, nothing)) - meshpoints = decompose(Point3f, mesh)::Vector{Point3f} meshfaces = decompose(GLTriangleFace, mesh)::Vector{GLTriangleFace} meshnormals = decompose_normals(mesh)::Vector{Vec3f} meshuvs = texturecoordinates(mesh)::Union{Nothing, Vector{Vec2f}} + # Priorize colors of the mesh if present color = hasproperty(mesh, :color) ? mesh.color : to_value(attributes.calculated_colors) per_face_col = per_face_colors(color, matcap, meshfaces, meshnormals, meshuvs) - @get_attribute(attributes, (shading, diffuse, - specular, shininess, faceculling)) - model = attributes.model[]::Mat4f space = to_value(get(attributes, :space, :data))::Symbol func = Makie.transform_func(attributes) @@ -1052,8 +1038,6 @@ end function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Makie.MeshScatter)) @get_attribute(primitive, (model, marker, markersize, rotations)) - - m = convert_attribute(marker, key"marker"(), key"meshscatter"()) pos = primitive[1][] # For correct z-ordering we need to be in view/camera or screen space model = copy(model) @@ -1093,7 +1077,7 @@ function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Maki scale = markersize isa Vector ? markersize[i] : markersize draw_mesh3D( - scene, screen, submesh, m, pos = p, + scene, screen, submesh, marker, pos = p, scale = scale isa Real ? Vec3f(scale) : to_ndim(Vec3f, scale, 1f0) ) end diff --git a/CairoMakie/test/Project.toml b/CairoMakie/test/Project.toml new file mode 100644 index 00000000000..da4b952feee --- /dev/null +++ b/CairoMakie/test/Project.toml @@ -0,0 +1,4 @@ +[deps] +Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" +ReferenceTests = "d37af2e0-5618-4e00-9939-d430db56ee94" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/CairoMakie/test/runtests.jl b/CairoMakie/test/runtests.jl index d7ae59760ff..84330b132e7 100644 --- a/CairoMakie/test/runtests.jl +++ b/CairoMakie/test/runtests.jl @@ -1,10 +1,8 @@ using Test using CairoMakie -using Pkg using Makie.FileIO +using ReferenceTests -path = normpath(joinpath(dirname(pathof(Makie)), "..", "ReferenceTests")) -Pkg.develop(PackageSpec(path = path)) # Before changing Pkg environment, try the test in #864 @testset "Runs without error" begin fig = Figure() diff --git a/GLMakie/Project.toml b/GLMakie/Project.toml index 06f998ca642..8e4f9bf07eb 100644 --- a/GLMakie/Project.toml +++ b/GLMakie/Project.toml @@ -1,6 +1,6 @@ name = "GLMakie" uuid = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" -version = "0.8.8" +version = "0.8.9" [deps] ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" @@ -29,7 +29,7 @@ FixedPointNumbers = "0.7, 0.8" FreeTypeAbstraction = "0.10" GLFW = "3" GeometryBasics = "0.4.1" -Makie = "=0.19.8" +Makie = "=0.19.9" MeshIO = "0.4" ModernGL = "1" Observables = "0.5.1" diff --git a/GLMakie/test/Project.toml b/GLMakie/test/Project.toml index b87115ddd72..e8cafac0840 100644 --- a/GLMakie/test/Project.toml +++ b/GLMakie/test/Project.toml @@ -1,8 +1,10 @@ [deps] FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" +GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326" ImageMagick = "6218d12a-5da1-5696-b52f-db25d2ecc6d1" Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +ReferenceTests = "d37af2e0-5618-4e00-9939-d430db56ee94" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/GLMakie/test/runtests.jl b/GLMakie/test/runtests.jl index 8df6f3b28cf..b235938df7d 100644 --- a/GLMakie/test/runtests.jl +++ b/GLMakie/test/runtests.jl @@ -1,20 +1,16 @@ +using Makie using GLMakie, Test using FileIO using GeometryBasics using GeometryBasics: origin -using Makie -using ImageMagick -using Pkg using Random +using ReferenceTests if !GLMakie.ModernGL.enable_opengl_debugging # can't error, since we can't enable debugging for users @warn("TESTING WITHOUT OPENGL DEBUGGING") end -reference_tests_dir = normpath(joinpath(dirname(pathof(Makie)), "..", "ReferenceTests")) -Pkg.develop(PackageSpec(path = reference_tests_dir)) -using ReferenceTests GLMakie.activate!(framerate=1.0) diff --git a/MakieCore/Project.toml b/MakieCore/Project.toml index 1db14f9ab24..2e60870acac 100644 --- a/MakieCore/Project.toml +++ b/MakieCore/Project.toml @@ -1,7 +1,7 @@ authors = ["Simon Danisch"] name = "MakieCore" uuid = "20f20a25-4f0e-4fdf-b5d1-57303727442b" -version = "0.6.5" +version = "0.6.6" [deps] Observables = "510215fc-4207-5dde-b226-833fc4488ee2" diff --git a/MakieCore/src/attributes.jl b/MakieCore/src/attributes.jl index 30f8d2cf5ad..b552e0d7ecd 100644 --- a/MakieCore/src/attributes.jl +++ b/MakieCore/src/attributes.jl @@ -235,7 +235,12 @@ function get_attribute(dict, key, default=nothing) if haskey(dict, key) value = to_value(dict[key]) value isa Automatic && return default - return convert_attribute(to_value(dict[key]), Key{key}()) + plot_k = plotkey(dict) + if isnothing(plot_k) + return convert_attribute(value, Key{key}()) + else + return convert_attribute(value, Key{key}(), Key{plot_k}()) + end else return default end diff --git a/MakieCore/src/basic_plots.jl b/MakieCore/src/basic_plots.jl index f186f92cb8c..99381416a82 100644 --- a/MakieCore/src/basic_plots.jl +++ b/MakieCore/src/basic_plots.jl @@ -261,7 +261,7 @@ Creates a connected line plot for each element in `(x, y, z)`, `(x, y)` or `posi - `color=theme(scene, :linecolor)` sets the color of the line. If no color is set, multiple calls to `line!` will cycle through the axis color palette. Otherwise, one can set one color per line point by passing a `Vector{<:Colorant}`, or one colorant for the whole line. If color is a vector of numbers, the colormap args are used to map the numbers to colors. - `cycle::Vector{Symbol} = [:color]` sets which attributes to cycle when creating multiple plots. -- `linestyle::Union{Nothing, Symbol, Vector} = nothing` sets the pattern of the line (e.g. `:solid`, `:dot`, `:dashdot`) +- `linestyle::Union{Nothing, Symbol, Linestyle} = nothing` sets the pattern of the line e.g. `:solid`, `:dot`, `:dashdot`. For custom patterns look at `Linestyle(Number[...])`. - `linewidth::Union{Real, Vector} = 1.5` sets the width of the line in pixel units. $(Base.Docs.doc(colormap_attributes!)) diff --git a/MakieCore/src/recipes.jl b/MakieCore/src/recipes.jl index 741ab37a169..e0cc89b5cb9 100644 --- a/MakieCore/src/recipes.jl +++ b/MakieCore/src/recipes.jl @@ -21,6 +21,7 @@ func2type(f::Function) = Combined{f} plotkey(::Type{<: AbstractPlot{Typ}}) where Typ = Symbol(lowercase(func2string(Typ))) plotkey(::T) where T <: AbstractPlot = plotkey(T) plotkey(::Nothing) = :scatter +plotkey(any) = nothing """ default_plot_signatures(funcname, funcname!, PlotType) @@ -55,8 +56,8 @@ end # Since we can use Combined like a scene in some circumstances, we define this alias theme(x::SceneLike, args...) = theme(x.parent, args...) theme(x::AbstractScene) = x.theme -theme(x::AbstractScene, key) = deepcopy(x.theme[key]) -theme(x::AbstractPlot, key) = deepcopy(x.attributes[key]) +theme(x::AbstractScene, key; default=nothing) = deepcopy(get(x.theme, key, default)) +theme(x::AbstractPlot, key; default=nothing) = deepcopy(get(x.attributes, key, default)) Attributes(x::AbstractPlot) = x.attributes diff --git a/NEWS.md b/NEWS.md index 75076899c13..53847d03dcd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,10 +4,16 @@ - [Breaking] Changed the default order Polar arguments to (theta, r). [#3154](https://github.com/MakieOrg/Makie.jl/pull/3154) - General improvements to `PolarAxis`: full rlimtis & thetalimits, more controls and visual tweaks. See pr for more details.[#3154](https://github.com/MakieOrg/Makie.jl/pull/3154) + +## v0.19.9 + +- Allow arbitrary reversible scale functions through `ReversibleScale`. +- Deprecated `linestyle=vector_of_gaps` in favor of `linestyle=Linestyle(vector_of_gaps)` [3135](https://github.com/MakieOrg/Makie.jl/pull/3135), [3193](https://github.com/MakieOrg/Makie.jl/pull/3193). - Fixed some errors around dynamic changes of `ax.xscale` or `ax.yscale` [#3084](https://github.com/MakieOrg/Makie.jl/pull/3084) - Improved Barplot Label Alignment [#3160](https://github.com/MakieOrg/Makie.jl/issues/3160). - Fixed regression in determining axis limits [#3179](https://github.com/MakieOrg/Makie.jl/pull/3179) - Added a theme `theme_latexfonts` that uses the latex font family as default fonts [#3147](https://github.com/MakieOrg/Makie.jl/pull/3147). +- Upgrades `StableHashTraits` from 0.3 to 1.0 ## v0.19.8 diff --git a/Project.toml b/Project.toml index 7b4959c42d3..725e8e1d2c8 100644 --- a/Project.toml +++ b/Project.toml @@ -1,11 +1,12 @@ name = "Makie" uuid = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" authors = ["Simon Danisch", "Julius Krumbiegel"] -version = "0.19.8" +version = "0.19.9" [deps] Animations = "27a7e980-b3e6-11e9-2bcd-0b925532e340" Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" +CRC32c = "8bf52ea8-c179-5cab-976a-9e18b702a9bc" ColorBrewer = "a2cac450-b92f-5266-8821-25eda20663c8" ColorSchemes = "35d6a980-a343-548e-a6ea-1d62b119f2f4" ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" @@ -81,7 +82,7 @@ Isoband = "0.1" KernelDensity = "0.5, 0.6" LaTeXStrings = "1.2" MacroTools = "0.5" -MakieCore = "=0.6.5" +MakieCore = "=0.6.6" Match = "1.1" MathTeXEngine = "0.5" Observables = "0.5.3" @@ -95,7 +96,7 @@ Setfield = "1" ShaderAbstractions = "0.3" Showoff = "0.3, 1.0.2" SignedDistanceFields = "0.4" -StableHashTraits = "0.3" +StableHashTraits = "1" StatsBase = "0.31, 0.32, 0.33, 0.34" StatsFuns = "0.9, 1.0" StructArrays = "0.3, 0.4, 0.5, 0.6" diff --git a/RPRMakie/Project.toml b/RPRMakie/Project.toml index af1b0811261..c651e55ab83 100644 --- a/RPRMakie/Project.toml +++ b/RPRMakie/Project.toml @@ -1,7 +1,7 @@ name = "RPRMakie" uuid = "22d9f318-5e34-4b44-b769-6e3734a732a6" authors = ["Simon Danisch"] -version = "0.5.8" +version = "0.5.9" [deps] Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" @@ -17,7 +17,7 @@ julia = "1.3" Colors = "0.9, 0.10, 0.11, 0.12" FileIO = "1.6" GeometryBasics = "0.4.1" -Makie = "=0.19.8" +Makie = "=0.19.9" RadeonProRender = "0.3.0" [extras] diff --git a/ReferenceTests/src/tests/primitives.jl b/ReferenceTests/src/tests/primitives.jl index 150aab8a35d..dbc411ac2d0 100644 --- a/ReferenceTests/src/tests/primitives.jl +++ b/ReferenceTests/src/tests/primitives.jl @@ -5,7 +5,7 @@ points = Point2f[(1, 1), (1, 2), (2, 3), (2, 1)] linestyles = [ :solid, :dash, :dot, :dashdot, :dashdotdot, - [1, 2, 3], [1, 2, 4, 5] + Linestyle([1, 2, 3]), Linestyle([1, 2, 4, 5]) ] for linewidth in 1:10 for (i, linestyle) in enumerate(linestyles) @@ -270,13 +270,13 @@ end # Same as above markers = [ - :rect, :circle, :cross, :x, :utriangle, :rtriangle, :dtriangle, :ltriangle, :pentagon, + :rect, :circle, :cross, :x, :utriangle, :rtriangle, :dtriangle, :ltriangle, :pentagon, :hexagon, :octagon, :star4, :star5, :star6, :star8, :vline, :hline, 'x', 'X' ] for (i, marker) in enumerate(markers) scatter!( - Point2f.(1:5, i), marker = marker, + Point2f.(1:5, i), marker = marker, markersize = range(10, 30, length = 5), color = :orange, strokewidth = 2, strokecolor = :black ) @@ -451,9 +451,9 @@ end lab1 = L"\int f(x) dx" lab2 = lab1 # lab2 = L"\frac{a}{b} - \sqrt{b}" # this will not work until #2667 is fixed - + barplot(fig[1,1], [1, 2], [0.5, 0.2], bar_labels = [lab1, lab2], flip_labels_at = 0.3, direction=:x) barplot(fig[1,2], [1, 2], [0.5, 0.2], bar_labels = [lab1, lab2], flip_labels_at = 0.3) fig -end \ No newline at end of file +end diff --git a/WGLMakie/Project.toml b/WGLMakie/Project.toml index e4c1ea1c489..675889dd1ac 100644 --- a/WGLMakie/Project.toml +++ b/WGLMakie/Project.toml @@ -1,7 +1,7 @@ name = "WGLMakie" uuid = "276b4fcb-3e11-5398-bf8b-a0c2d153d008" authors = ["SimonDanisch "] -version = "0.8.12" +version = "0.8.13" [deps] Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" @@ -26,7 +26,7 @@ FreeTypeAbstraction = "0.10" GeometryBasics = "0.4.1" Hyperscript = "0.0.3, 0.0.4" JSServe = "2.2" -Makie = "=0.19.8" +Makie = "=0.19.9" Observables = "0.5.1" RelocatableFolders = "0.1, 0.2, 0.3, 1.0" ShaderAbstractions = "0.3" diff --git a/WGLMakie/test/Project.toml b/WGLMakie/test/Project.toml index 0edb0064c73..066e2ecdc44 100644 --- a/WGLMakie/test/Project.toml +++ b/WGLMakie/test/Project.toml @@ -4,7 +4,5 @@ FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" JSServe = "824d6782-a2ef-11e9-3a09-e5662e0c26f9" Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +ReferenceTests = "d37af2e0-5618-4e00-9939-d430db56ee94" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[compat] -Electron = "4.1.1" diff --git a/WGLMakie/test/runtests.jl b/WGLMakie/test/runtests.jl index 20b317aecaa..3d13c3cb842 100644 --- a/WGLMakie/test/runtests.jl +++ b/WGLMakie/test/runtests.jl @@ -1,11 +1,7 @@ using FileIO using WGLMakie, Makie, Test -using Pkg using WGLMakie.JSServe import Electron - -path = normpath(joinpath(dirname(pathof(Makie)), "..", "ReferenceTests")) -Pkg.develop(PackageSpec(path = path)) using ReferenceTests @testset "mimes" begin diff --git a/docs/reference/plots/contour.md b/docs/reference/plots/contour.md index c1aa8231645..4c9d8fd5bdd 100644 --- a/docs/reference/plots/contour.md +++ b/docs/reference/plots/contour.md @@ -57,8 +57,8 @@ x = y = range(-6, 6; length=100) z = himmelblau.(x, y') levels = 10.0.^range(0.3, 3.5; length=10) -colormap = Makie.sampler(:hsv, 100; scaling=Makie.Scaling(x -> x^(1 / 10), nothing)) -f, ax, ct = contour(x, y, z; labels=true, levels, colormap) +colorscale = ReversibleScale(x -> x^(1 / 10), x -> x^10) +f, ax, ct = contour(x, y, z; labels=true, levels, colormap=:hsv, colorscale) f ``` \end{examplefigure} diff --git a/docs/reference/plots/heatmap.md b/docs/reference/plots/heatmap.md index fb6e8325774..a7302c53b7b 100644 --- a/docs/reference/plots/heatmap.md +++ b/docs/reference/plots/heatmap.md @@ -116,3 +116,25 @@ Colorbar(fig[:, end+1], colorrange = joint_limits) # equivalent fig ``` \end{examplefigure} + + +### Using a custom colorscale + +One can define a custom (color)scale using the `ReversibleScale` type. When the transformation is simple enough (`log`, `sqrt`, ...), the inverse transform is automatically deduced. + +\begin{examplefigure}{} +```julia +using CairoMakie +CairoMakie.activate!() # hide + +x = 10.0.^(1:0.1:4) +y = 1.0:0.1:5.0 +z = broadcast((x, y) -> x - 10, x, y') + +scale = ReversibleScale(x -> asinh(x / 2) / log(10), x -> 2sinh(log(10) * x)) +fig, ax, hm = heatmap(x, y, z; colorscale = scale, axis = (; xscale = scale)) +Colorbar(fig[1, 2], hm) + +fig +``` +\end{examplefigure} diff --git a/docs/reference/plots/lines.md b/docs/reference/plots/lines.md index bae56aa59c3..28145387529 100644 --- a/docs/reference/plots/lines.md +++ b/docs/reference/plots/lines.md @@ -45,7 +45,7 @@ for (i, lw) in enumerate([1, 2, 3]) lines!(xs, ys .- i/6 .- 2, linestyle = :dot, linewidth = lw) lines!(xs, ys .- i/6 .- 3, linestyle = :dashdot, linewidth = lw) lines!(xs, ys .- i/6 .- 4, linestyle = :dashdotdot, linewidth = lw) - lines!(xs, ys .- i/6 .- 5, linestyle = [0.5, 1.0, 1.5, 2.5], linewidth = lw) + lines!(xs, ys .- i/6 .- 5, linestyle = Linestyle([0.5, 1.0, 1.5, 2.5]), linewidth = lw) end f diff --git a/src/Makie.jl b/src/Makie.jl index f72041008aa..126fd91db16 100644 --- a/src/Makie.jl +++ b/src/Makie.jl @@ -23,6 +23,7 @@ using ColorBrewer using ColorTypes using Colors using ColorSchemes +using CRC32c using Packing using SignedDistanceFields using Markdown @@ -287,6 +288,7 @@ export save, colorbuffer export cgrad, available_gradients, showgradients export Pattern +export ReversibleScale export assetpath # default icon for Makie diff --git a/src/basic_recipes/bracket.jl b/src/basic_recipes/bracket.jl index bb2426f7c77..f32d1dfe2fd 100644 --- a/src/basic_recipes/bracket.jl +++ b/src/basic_recipes/bracket.jl @@ -37,7 +37,7 @@ function Makie.convert_arguments(::Type{<:Bracket}, x1::AbstractVector{<:Real}, points = broadcast(x1, y1, x2, y2) do x1, y1, x2, y2 (Point2f(x1, y1), Point2f(x2, y2)) end - (points,) + return (points,) end function Makie.plot!(pl::Bracket) @@ -117,7 +117,7 @@ function Makie.plot!(pl::Bracket) end function point_iterator(pl::Bracket) - point_iterator(pl.plots[1]) + return Point2f[tupl[i] for tupl in pl[1][] for i in 1:2] end bracket_bezierpath(style::Symbol, args...) = bracket_bezierpath(Val(style), args...) diff --git a/src/basic_recipes/text.jl b/src/basic_recipes/text.jl index aee85ac7d5e..d713e45c7b8 100644 --- a/src/basic_recipes/text.jl +++ b/src/basic_recipes/text.jl @@ -161,8 +161,13 @@ function plot!(plot::Text{<:Tuple{<:AbstractArray{<:Tuple{<:Any, <:Point}}}}) strs = first.(str_pos) poss = to_ndim.(Ref(Point3f), last.(str_pos), 0) - strings.val != strs && (strings[] = strs) - positions.val != poss && (positions[] = poss) + strings_unequal = strings.val != strs + pos_unequal = positions.val != poss + strings_unequal && (strings.val = strs) + pos_unequal && (positions.val = poss) + # Check for equality very imortant, otherwise we get an infinite loop + strings_unequal && notify(strings) + pos_unequal && notify(positions) return end diff --git a/src/bezier.jl b/src/bezier.jl index ca6cf185cac..81f18f506cc 100644 --- a/src/bezier.jl +++ b/src/bezier.jl @@ -42,13 +42,6 @@ struct BezierPath commands::Vector{PathCommand} end -StableHashTraits.transform(path::BezierPath) = path.commands -StableHashTraits.transform(c::EllipticalArc) = [c.c[1], c.c[2], c.r1, c.r2, c.angle, c.a1, c.a2] -StableHashTraits.transform(c::CurveTo) = [c.c1[1], c.c1[2], c.c2[1], c.c2[2], c.p[1], c.p[2]] -StableHashTraits.transform(c::LineTo) = [c.p[1], c.p[2]] -StableHashTraits.transform(c::MoveTo) = [c.p[1], c.p[2]] -StableHashTraits.transform(c::ClosePath) = 0 - # so that the same bezierpath with a different instance of a vector hashes the same # and we don't create the same texture atlas entry twice Base.:(==)(b1::BezierPath, b2::BezierPath) = b1.commands == b2.commands diff --git a/src/colorsampler.jl b/src/colorsampler.jl index 71cc0d275a0..47a16029f98 100644 --- a/src/colorsampler.jl +++ b/src/colorsampler.jl @@ -179,7 +179,7 @@ end struct ColorMap{N,T<:AbstractArray{<:Number,N},T2<:AbstractArray{<:Number,N}} color::Observable{T} colormap::Observable{Vector{RGBAf}} - scale::Observable{Function} + scale::Observable{Union{ReversibleScale, Function}} mapping::Observable{Union{Nothing, Vector{Float64}}} colorrange::Observable{Vec{2,Float64}} @@ -198,7 +198,7 @@ function assemble_colors(::T, @nospecialize(color), @nospecialize(plot)) where { color_tight = convert(Observable{T}, color) colormap = Observable(RGBAf[]; ignore_equal_values=true) categorical = Observable(false) - colorscale = convert(Observable{Function}, plot.colorscale) + colorscale = convert(Observable{Union{ReversibleScale, Function}}, plot.colorscale) mapping = Observable{Union{Nothing, Vector{Float64}}}(nothing) function update_colors(cmap, a) diff --git a/src/layouting/transformation.jl b/src/layouting/transformation.jl index 6c0d72c12e7..4b2f0516466 100644 --- a/src/layouting/transformation.jl +++ b/src/layouting/transformation.jl @@ -339,7 +339,6 @@ function apply_transform(f, itr::ClosedInterval) return apply_transform(f, mini) .. apply_transform(f, maxi) end - function apply_transform(f, r::Rect) mi = minimum(r) ma = maximum(r) @@ -360,66 +359,42 @@ apply_transform(f::typeof(identity), r::Rect) = r apply_transform(f::NTuple{2, typeof(identity)}, r::Rect) = r apply_transform(f::NTuple{3, typeof(identity)}, r::Rect) = r +const pseudolog10 = ReversibleScale( + x -> sign(x) * log10(abs(x) + 1), + x -> sign(x) * (exp10(abs(x)) - 1); + limits=(0f0, 3f0) +) -pseudolog10(x) = sign(x) * log10(abs(x) + 1) -inv_pseudolog10(x) = sign(x) * (exp10(abs(x)) - 1) - -struct Symlog10 - low::Float64 - high::Float64 - function Symlog10(low, high) - if !(low < 0 && high > 0) - error("Low bound needs to be smaller than 0 and high bound larger than 0. You gave $low, $high.") - end - new(Float64(low), Float64(high)) - end -end - -Symlog10(x) = Symlog10(-x, x) - -function (s::Symlog10)(x) - if x > 0 - x <= s.high ? x / s.high * log10(s.high) : log10(x) +Symlog10(hi) = Symlog10(-hi, hi) +function Symlog10(lo, hi) + forward(x) = if x > 0 + x <= hi ? x / hi * log10(hi) : log10(x) elseif x < 0 - x >= s.low ? x / abs(s.low) * log10(abs(s.low)) : sign(x) * log10(abs(x)) + x >= lo ? x / abs(lo) * log10(abs(lo)) : -log10(abs(x)) else x end -end - -function inv_symlog10(x, low, high) - if x > 0 - l = log10(high) - x <= l ? x / l * high : exp10(x) + inverse(x) = if x > 0 + l = log10(hi) + x <= l ? x / l * hi : exp10(x) elseif x < 0 - l = sign(x) * log10(abs(low)) - x >= l ? x / l * abs(low) : sign(x) * exp10(abs(x)) + l = -log10(abs(lo)) + x >= l ? x / l * abs(lo) : -exp10(abs(x)) else x end + ReversibleScale(forward, inverse; limits=(0f0, 3f0)) end -const REVERSIBLE_SCALES = Union{ - # typeof(identity), # no, this is a noop - typeof(log10), - typeof(log), - typeof(log2), - typeof(sqrt), - typeof(pseudolog10), - typeof(logit), - Symlog10, -} - inverse_transform(::typeof(identity)) = identity inverse_transform(::typeof(log10)) = exp10 -inverse_transform(::typeof(log)) = exp inverse_transform(::typeof(log2)) = exp2 +inverse_transform(::typeof(log)) = exp inverse_transform(::typeof(sqrt)) = x -> x ^ 2 -inverse_transform(::typeof(pseudolog10)) = inv_pseudolog10 inverse_transform(F::Tuple) = map(inverse_transform, F) inverse_transform(::typeof(logit)) = logistic -inverse_transform(s::Symlog10) = x -> inv_symlog10(x, s.low, s.high) -inverse_transform(s) = nothing +inverse_transform(s::ReversibleScale) = s.inverse +inverse_transform(::Any) = nothing function is_identity_transform(t) return t === identity || t isa Tuple && all(x-> x === identity, t) diff --git a/src/makielayout/blocks/axis.jl b/src/makielayout/blocks/axis.jl index ee6edd121a6..44ecafcf582 100644 --- a/src/makielayout/blocks/axis.jl +++ b/src/makielayout/blocks/axis.jl @@ -495,7 +495,7 @@ function initialize_block!(ax::Axis; palette = nothing) register_events!(ax, scene) # these are the user defined limits - on(blockscene, ax.limits) do mlims + on(blockscene, ax.limits) do _ reset_limits!(ax) end @@ -1352,22 +1352,19 @@ defaultlimits(limits::Tuple{Real, Nothing}, scale) = (limits[1], defaultlimits(s defaultlimits(limits::Tuple{Nothing, Real}, scale) = (defaultlimits(scale)[1], limits[2]) defaultlimits(limits::Tuple{Nothing, Nothing}, scale) = defaultlimits(scale) - -defaultlimits(::typeof(log10)) = (1.0, 1000.0) -defaultlimits(::typeof(log2)) = (1.0, 8.0) -defaultlimits(::typeof(log)) = (1.0, exp(3.0)) +defaultlimits(scale::ReversibleScale) = inverse_transform(scale).(scale.limits) +defaultlimits(scale::LogFunctions) = let inv_scale = inverse_transform(scale) + (inv_scale(0.0), inv_scale(3.0)) +end defaultlimits(::typeof(identity)) = (0.0, 10.0) defaultlimits(::typeof(sqrt)) = (0.0, 100.0) defaultlimits(::typeof(Makie.logit)) = (0.01, 0.99) -defaultlimits(::typeof(Makie.pseudolog10)) = (0.0, 100.0) -defaultlimits(::Makie.Symlog10) = (0.0, 100.0) +defined_interval(scale::ReversibleScale) = scale.interval defined_interval(::typeof(identity)) = OpenInterval(-Inf, Inf) -defined_interval(::Union{typeof(log2), typeof(log10), typeof(log)}) = OpenInterval(0.0, Inf) +defined_interval(::LogFunctions) = OpenInterval(0.0, Inf) defined_interval(::typeof(sqrt)) = Interval{:closed,:open}(0, Inf) defined_interval(::typeof(Makie.logit)) = OpenInterval(0.0, 1.0) -defined_interval(::typeof(Makie.pseudolog10)) = OpenInterval(-Inf, Inf) -defined_interval(::Makie.Symlog10) = OpenInterval(-Inf, Inf) function update_state_before_display!(ax::Axis) reset_limits!(ax) diff --git a/src/makielayout/blocks/colorbar.jl b/src/makielayout/blocks/colorbar.jl index b2e07f9ca89..266d6efb049 100644 --- a/src/makielayout/blocks/colorbar.jl +++ b/src/makielayout/blocks/colorbar.jl @@ -90,9 +90,15 @@ function Colorbar(fig_or_scene, voronoi::Voronoiplot; kwargs...) ) end -colorbar_range(start, stop, length, _) = LinRange(start, stop, length) # noop -function colorbar_range(start, stop, length, scale::REVERSIBLE_SCALES) - inverse_transform(scale).(range(start, stop; length)) +function colorbar_range(start, stop, length, colorscale) + colorscale === identity && return LinRange(start, stop, length) + + inverse = inverse_transform(colorscale) + isnothing(inverse) && throw(ArgumentError( + "Cannot determine inverse transform: you can use `ReversibleScale($(colorscale), inverse($(colorscale)))` instead." + )) + + inverse.(range(start, stop; length)) end function initialize_block!(cb::Colorbar) @@ -168,7 +174,7 @@ function initialize_block!(cb::Colorbar) map_is_categorical = lift(x -> x isa PlotUtils.CategoricalColorGradient, blockscene, cgradient) steps = lift(blockscene, cgradient, cb.nsteps, cb.scale) do cgradient, n, scale - s = if cgradient isa PlotUtils.CategoricalColorGradient + if cgradient isa PlotUtils.CategoricalColorGradient cgradient.values else collect(colorbar_range(0, 1, n, scale)) @@ -385,17 +391,13 @@ end Sets the space allocated for the ticklabels of the `Colorbar` to the minimum that is needed and returns that value. """ -function tight_ticklabel_spacing!(cb::Colorbar) - space = tight_ticklabel_spacing!(cb.axis) - return space -end +tight_ticklabel_spacing!(cb::Colorbar) = tight_ticklabel_spacing!(cb.axis) function scaled_steps(steps, scale, lims) - # first scale to limits so we can actually apply the scale to the values - # (log(0) doesn't work etc.) - s_limits = steps .* (lims[2] - lims[1]) .+ lims[1] # scale with scaling function - s_limits_scaled = scale.(s_limits) + steps_scaled = scale.(steps) + # normalize to lims range + steps_lim_scaled = @. steps_scaled * (scale(lims[2]) - scale(lims[1])) + scale(lims[1]) # then rescale to 0 to 1 - s_scaled = (s_limits_scaled .- s_limits_scaled[1]) ./ (s_limits_scaled[end] - s_limits_scaled[1]) + @. (steps_lim_scaled - steps_lim_scaled[begin]) / (steps_lim_scaled[end] - steps_lim_scaled[begin]) end diff --git a/src/makielayout/lineaxis.jl b/src/makielayout/lineaxis.jl index b4692915c02..2e0a41122b8 100644 --- a/src/makielayout/lineaxis.jl +++ b/src/makielayout/lineaxis.jl @@ -234,9 +234,6 @@ function update_minor_ticks(minortickpositions, limits::NTuple{2, Float32}, pos_ px_o = extents[1] px_width = extents[2] - extents[1] - lim_o = limits[1] - lim_w = limits[2] - limits[1] - tickvalues_scaled = scale.(minortickvalues) tick_fractions = (tickvalues_scaled .- scale(limits[1])) ./ (scale(limits[2]) - scale(limits[1])) @@ -494,6 +491,7 @@ function LineAxis(parent::Scene, attrs::Attributes) # before other stuff is triggered by them, which accesses the # ticklabel boundingbox (which needs to be updated already) # so we move the new listener from text! to the front + pushfirst!(ticklabel_annotation_obs.listeners, pop!(ticklabel_annotation_obs.listeners)) # trigger calculation of ticklabel width once, now that it's not nothing anymore @@ -556,12 +554,12 @@ end get_tickvalues(::Automatic, ::typeof(identity), vmin, vmax) = get_tickvalues(WilkinsonTicks(5, k_min = 3), vmin, vmax) # fall back to identity if not overloaded scale function is used with automatic -get_tickvalues(::Automatic, F, vmin, vmax) = get_tickvalues(automatic, identity, vmin, vmax) +get_tickvalues(::Automatic, _, vmin, vmax) = get_tickvalues(automatic, identity, vmin, vmax) # fall back to non-scale aware behavior if no special version is overloaded -get_tickvalues(ticks, scale, vmin, vmax) = get_tickvalues(ticks, vmin, vmax) +get_tickvalues(ticks, _, vmin, vmax) = get_tickvalues(ticks, vmin, vmax) -function get_ticks(ticks_and_labels::Tuple{Any, Any}, any_scale, ::Automatic, vmin, vmax) +function get_ticks(ticks_and_labels::Tuple{Any, Any}, _, ::Automatic, vmin, vmax) n1 = length(ticks_and_labels[1]) n2 = length(ticks_and_labels[2]) if n1 != n2 @@ -570,7 +568,7 @@ function get_ticks(ticks_and_labels::Tuple{Any, Any}, any_scale, ::Automatic, vm ticks_and_labels end -function get_ticks(tickfunction::Function, any_scale, formatter, vmin, vmax) +function get_ticks(tickfunction::Function, _, formatter, vmin, vmax) result = tickfunction(vmin, vmax) if result isa Tuple{Any, Any} tickvalues, ticklabels = result @@ -585,14 +583,13 @@ _logbase(::typeof(log10)) = "10" _logbase(::typeof(log2)) = "2" _logbase(::typeof(log)) = "e" - -function get_ticks(::Automatic, scale::Union{typeof(log10), typeof(log2), typeof(log)}, - any_formatter, vmin, vmax) - get_ticks(LogTicks(WilkinsonTicks(5, k_min = 3)), scale, any_formatter, vmin, vmax) +function get_ticks(::Automatic, scale::LogFunctions, any_formatter, vmin, vmax) + ticks = LogTicks(WilkinsonTicks(5, k_min = 3)) + get_ticks(ticks, scale, any_formatter, vmin, vmax) end # log ticks just use the normal pipeline but with log'd limits, then transform the labels -function get_ticks(l::LogTicks, scale::Union{typeof(log10), typeof(log2), typeof(log)}, ::Automatic, vmin, vmax) +function get_ticks(l::LogTicks, scale::LogFunctions, ::Automatic, vmin, vmax) ticks_scaled = get_tickvalues(l.linear_ticks, identity, scale(vmin), scale(vmax)) ticks = Makie.inverse_transform(scale).(ticks_scaled) @@ -605,7 +602,7 @@ function get_ticks(l::LogTicks, scale::Union{typeof(log10), typeof(log2), typeof ) labels = rich.(_logbase(scale), superscript.(labels_scaled, offset = Vec2f(0.1f0, 0f0))) - (ticks, labels) + ticks, labels end # function get_ticks(::Automatic, scale::typeof(Makie.logit), any_formatter, vmin, vmax) @@ -684,7 +681,6 @@ Gets tick labels by formatting each value in `values` according to a `Formatting """ get_ticklabels(formatstring::AbstractString, values) = [Formatting.format(formatstring, v) for v in values] - function get_ticks(m::MultiplesTicks, any_scale, ::Automatic, vmin, vmax) dvmin = vmin / m.multiple dvmax = vmax / m.multiple @@ -759,8 +755,7 @@ function get_minor_tickvalues(i::IntervalsBetween, scale, tickvalues, vmin, vmax end # for log scales, we need to step in log steps at the edges -function get_minor_tickvalues(i::IntervalsBetween, scale::Union{typeof(log),typeof(log2),typeof(log10)}, - tickvalues, vmin, vmax) +function get_minor_tickvalues(i::IntervalsBetween, scale::LogFunctions, tickvalues, vmin, vmax) vals = Float64[] length(tickvalues) < 2 && return vals n = i.n diff --git a/src/theming.jl b/src/theming.jl index db0c2afa8a5..79ed248882f 100644 --- a/src/theming.jl +++ b/src/theming.jl @@ -197,7 +197,7 @@ function with_theme(f, theme = Theme(); kwargs...) end end -theme(::Nothing, key::Symbol) = theme(key) +theme(::Nothing, key::Symbol; default=nothing) = theme(key; default) function theme(key::Symbol; default=nothing) if haskey(CURRENT_DEFAULT_THEME, key) val = to_value(CURRENT_DEFAULT_THEME[key]) diff --git a/src/types.jl b/src/types.jl index c28e74df6a8..fd8fb4078c6 100644 --- a/src/types.jl +++ b/src/types.jl @@ -200,6 +200,9 @@ end Struct to hold all relevant matrices and additional parameters, to let backends apply camera based transformations. + +## Fields +$(TYPEDFIELDS) """ struct Camera """ @@ -378,3 +381,51 @@ end # The color type we ideally use for most color attributes const RGBColors = Union{RGBAf, Vector{RGBAf}, Vector{Float32}} + +const LogFunctions = Union{typeof(log10), typeof(log2), typeof(log)} + +""" + ReversibleScale + +Custom scale struct, taking a forward and inverse arbitrary scale function. + +## Fields +$(TYPEDFIELDS) +""" +struct ReversibleScale{F <: Function, I <: Function, T <: AbstractInterval} + """ + forward transformation (e.g. `log10`) + """ + forward::F + """ + inverse transformation (e.g. `exp10` for `log10` such that inverse ∘ forward ≡ identity) + """ + inverse::I + """ + default limits (optional) + """ + limits::NTuple{2,Float32} + """ + valid limits interval (optional) + """ + interval::T + function ReversibleScale(forward, inverse = Automatic(); limits = (0f0, 10f0), interval = (-Inf32, Inf32)) + inverse isa Automatic && (inverse = inverse_transform(forward)) + isnothing(inverse) && throw(ArgumentError( + "Cannot determine inverse transform: you can use `ReversibleScale($(forward), inverse($(forward)))` instead." + )) + interval isa AbstractInterval || (interval = OpenInterval(Float32.(interval)...)) + + lft, rgt = limits = Tuple(Float32.(limits)) + + Id = inverse ∘ forward + lft ≈ Id(lft) || throw(ArgumentError("Invalid inverse transform: $lft !≈ $(Id(lft))")) + rgt ≈ Id(rgt) || throw(ArgumentError("Invalid inverse transform: $rgt !≈ $(Id(rgt))")) + + new{typeof(forward),typeof(inverse),typeof(interval)}(forward, inverse, limits, interval) + end +end + +function (s::ReversibleScale)(args...) # functor + s.forward(args...) +end diff --git a/src/utilities/texture_atlas.jl b/src/utilities/texture_atlas.jl index 1db50d598db..00b6727330c 100644 --- a/src/utilities/texture_atlas.jl +++ b/src/utilities/texture_atlas.jl @@ -1,3 +1,4 @@ +const SERIALIZATION_FORMAT_VERSION = "v4" struct TextureAtlas rectangle_packer::RectanglePacker{Int32} @@ -70,8 +71,6 @@ function Base.show(io::IO, atlas::TextureAtlas) println(io, " font_render_callback: ", length(atlas.font_render_callback)) end -const SERIALIZATION_FORMAT_VERSION = "v3" - # basically a singleton for the textureatlas function get_cache_path(resolution::Int, pix_per_glyph::Int) path = abspath( @@ -292,14 +291,16 @@ function glyph_uv_width!(atlas::TextureAtlas, b::BezierPath) return atlas.uv_rectangles[glyph_index!(atlas, b)] end +crc(x, seed=UInt32(0)) = crc32c(collect(x), seed) function insert_glyph!(atlas::TextureAtlas, glyph, font::NativeFont) glyphindex = FreeTypeAbstraction.glyph_index(font, glyph) - hash = StableHashTraits.stable_hash((glyphindex, FreeTypeAbstraction.fontname(font))) + hash = StableHashTraits.stable_hash((glyphindex, FreeTypeAbstraction.fontname(font)); + alg=crc) return insert_glyph!(atlas, hash, (glyphindex, font)) end function insert_glyph!(atlas::TextureAtlas, path::BezierPath) - return insert_glyph!(atlas, StableHashTraits.stable_hash(path), path) + return insert_glyph!(atlas, StableHashTraits.stable_hash(path; alg=crc), path) end diff --git a/src/utilities/utilities.jl b/src/utilities/utilities.jl index 564b1a53418..5f8e01a5628 100644 --- a/src/utilities/utilities.jl +++ b/src/utilities/utilities.jl @@ -169,20 +169,20 @@ end attr_broadcast_length(x::NativeFont) = 1 attr_broadcast_length(x::VecTypes) = 1 # these are our rules, and for what we do, Vecs are usually scalars -attr_broadcast_length(x::AbstractArray) = length(x) +attr_broadcast_length(x::AbstractVector) = length(x) attr_broadcast_length(x::AbstractPattern) = 1 attr_broadcast_length(x) = 1 attr_broadcast_length(x::ScalarOrVector) = x.sv isa Vector ? length(x.sv) : 1 attr_broadcast_getindex(x::NativeFont, i) = x attr_broadcast_getindex(x::VecTypes, i) = x # these are our rules, and for what we do, Vecs are usually scalars -attr_broadcast_getindex(x::AbstractArray, i) = x[i] +attr_broadcast_getindex(x::AbstractVector, i) = x[i] attr_broadcast_getindex(x::AbstractPattern, i) = x attr_broadcast_getindex(x, i) = x attr_broadcast_getindex(x::Ref, i) = x[] # unwrap Refs just like in normal broadcasting, for protecting iterables attr_broadcast_getindex(x::ScalarOrVector, i) = x.sv isa Vector ? x.sv[i] : x.sv -is_vector_attribute(x::AbstractArray) = true +is_vector_attribute(x::AbstractVector) = true is_vector_attribute(x::NativeFont) = false is_vector_attribute(x::Quaternion) = false is_vector_attribute(x::VecTypes) = false diff --git a/test/makielayout.jl b/test/makielayout.jl index ae4586b8d69..54ebaec3f7c 100644 --- a/test/makielayout.jl +++ b/test/makielayout.jl @@ -377,6 +377,31 @@ end f end +@testset "ReversibleScale" begin + @test ReversibleScale(identity).inverse === identity + @test ReversibleScale(log).inverse === exp + @test_throws ArgumentError ReversibleScale(x -> log10(x)) # missing inverse scale + @test_throws ArgumentError ReversibleScale(sqrt, exp10) # incorrect inverse scale +end + +@testset "Invalid inverse transform" begin + f = Figure() + @test_throws ArgumentError Colorbar(f[1, 1], limits = (1, 100), scale = x -> log10(x)) +end + +@testset "Colorscales" begin + x = 10.0.^(1:0.1:4) + y = 1.0:0.1:5.0 + z = broadcast((x, y) -> x, x, y') + + scale = Makie.Symlog10(2) + fig, ax, hm = heatmap(x, y, z; colorscale = scale, axis = (; xscale = scale)) + Colorbar(fig[1, 2], hm) + + scale = Makie.pseudolog10 + fig, ax, hm = heatmap(x, y, z; colorscale = scale, axis = (; xscale = scale)) + Colorbar(fig[1, 2], hm) +end @testset "Axis scale" begin # This just shouldn't error @@ -402,4 +427,4 @@ end @test false rethrow(e) end -end \ No newline at end of file +end diff --git a/test/scenes.jl b/test/scenes.jl index e16cc6049f3..b28a6dd51b8 100644 --- a/test/scenes.jl +++ b/test/scenes.jl @@ -4,4 +4,6 @@ @testset "getproperty(scene, :$field)" for field in fieldnames(Scene) @test getproperty(scene, field) !== missing # well, just don't error end + @test theme(nothing, :nonexistant, default=1) == 1 + @test theme(scene, :nonexistant, default=1) == 1 end