From fce9f635c0b9efecdb017c53c1f067fbba09fa1b Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Thu, 3 Oct 2024 17:16:48 +0200 Subject: [PATCH 1/5] give beeswarm plots fixed limits given input data --- src/recipe.jl | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/recipe.jl b/src/recipe.jl index c5ecb19..dbf3d87 100644 --- a/src/recipe.jl +++ b/src/recipe.jl @@ -60,7 +60,42 @@ function calculate!(buffer::AbstractVector{<: Point2}, alg::NoBeeswarm, position return end -Makie.data_limits(bs::Beeswarm) = Makie.data_limits(bs.plots[1]) +# Beeswarm plots inherently have an extent that is dependent on the placement +# algorithm and the available space. However, you cannot use the actual placement +# of scatter dots to infer limits, because adjusting the axis given these limits +# invalidates the limits again, and so on, potentially ad infinitum. +# +# Instead, it makes more sense to pick fixed limits given the input data. If +# that doesn't leave enough place for all beeswarms, probably the axis size +# has to be increased, or the marker sized decreased, anyway. +# +# The dimension that's not controlled by the beeswarm placement algorithm we +# can take directly from the input data. For the "categories" or group placement +# values, we simply determine the differences between the unique sorted values and +# increase the width at the sides by half the minimum distance. That means, we create +# equal space for all categories. If the beeswarm doesn't fit that, again, other +# parameters have to be adjusted anway. +function Makie.data_limits(bs::Beeswarm) + points = bs.converted[1][] + categories = sort(unique(p[1] for p in points)) + range_1 = if length(categories) == 1 + (only(categories) - 0.5, only(categories) + 0.5) + else + mindiff = minimum(diff(categories)) + (first(categories) - mindiff/2, last(categories) + mindiff/2) + end + range_2 = extrema(p[2] for p in points) + bb = if bs.direction[] === :y + BBox(range_1..., range_2...) + elseif bs.direction[] === :x + BBox(range_2..., range_1...) + else + error("Invalid direction $(repr(bs.direction[])), expected :x or :y") + end + return Rect3f(bb) +end + +Makie.boundingbox(s::Beeswarm, space::Symbol = :data) = Makie.apply_transform_and_model(s, Makie.data_limits(s)) function Makie.plot!(plot::Beeswarm) positions = plot.converted[1] # being PointBased, it should always receive a vector of Point2 From 7c3b832909f4a96cc0157dfac47441b268a082c0 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com> Date: Thu, 3 Oct 2024 17:33:40 +0200 Subject: [PATCH 2/5] Update src/recipe.jl Co-authored-by: Anshul Singhvi --- src/recipe.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/recipe.jl b/src/recipe.jl index dbf3d87..09b2c6e 100644 --- a/src/recipe.jl +++ b/src/recipe.jl @@ -81,7 +81,11 @@ function Makie.data_limits(bs::Beeswarm) range_1 = if length(categories) == 1 (only(categories) - 0.5, only(categories) + 0.5) else - mindiff = minimum(diff(categories)) + mindiff = if isnothing(bs.gutter[]) + minimum(diff(categories)) + else + bs.gutter[] + end (first(categories) - mindiff/2, last(categories) + mindiff/2) end range_2 = extrema(p[2] for p in points) From b907141a4c1f52e59586bade089f54a121acb467 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Thu, 3 Oct 2024 18:01:27 +0200 Subject: [PATCH 3/5] make gutters in test small enough to warn again --- test/runtests.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index 5cb9247..99e813c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -54,12 +54,12 @@ buffer = deepcopy(pixel_points) f, a, p = beeswarm(rand(1:3, 300), randn(300); color = rand(RGBAf, 300), markersize = 20, algorithm = SimpleBeeswarm()) Makie.update_state_before_display!(f) Makie.update_state_before_display!(f) - @test_warn "Gutter threshold exceeded" p.gutter = 0.5 + @test_warn "Gutter threshold exceeded" p.gutter = 0.2 # Next, we test it in direction y f, a, p = beeswarm(rand(1:3, 300), randn(300); direction = :x, color = rand(RGBAf, 300), markersize = 20, algorithm = SimpleBeeswarm()) Makie.update_state_before_display!(f) Makie.update_state_before_display!(f) - @test_warn "Gutter threshold exceeded" p.gutter = 0.5 + @test_warn "Gutter threshold exceeded" p.gutter = 0.2 # and it shouldn't warn if, when using a lower markersize, the gutter is not reached. f, a, p = beeswarm(rand(1:3, 300), randn(300); direction = :y, color = rand(RGBAf, 300), markersize = 9, algorithm = SimpleBeeswarm()) Makie.update_state_before_display!(f) From 3e4452ca6f03039056c229e18a6653165f0815fb Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Thu, 3 Oct 2024 18:04:45 +0200 Subject: [PATCH 4/5] remove unnecessary figure state updates --- docs/src/examples/examples.jl | 7 ------- test/runtests.jl | 9 ++------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/docs/src/examples/examples.jl b/docs/src/examples/examples.jl index bf63281..3b2a18c 100644 --- a/docs/src/examples/examples.jl +++ b/docs/src/examples/examples.jl @@ -22,9 +22,6 @@ using PalmerPenguins, DataFrames penguins = dropmissing(DataFrame(PalmerPenguins.load())) f = data(penguins) * mapping(:species, :bill_depth_mm, color=:sex) * visual(Beeswarm) |> draw -Makie.update_state_before_display!(f.figure) -Makie.update_state_before_display!(f.figure) -Makie.update_state_before_display!(f.figure) f # ## SwarmMakie logo @@ -43,10 +40,6 @@ f.scene.backgroundcolor[] = RGBAf(1,1,1,0) a.scene.backgroundcolor[] = RGBAf(1,1,1,0) hidedecorations!(a) hidespines!(a) -Makie.update_state_before_display!(f) -Makie.update_state_before_display!(f) -Makie.update_state_before_display!(f) -Makie.update_state_before_display!(f) f # ## Wilkinson's dot histogram diff --git a/test/runtests.jl b/test/runtests.jl index 99e813c..4fb8410 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -28,8 +28,6 @@ buffer = deepcopy(pixel_points) hidedecorations!(a) hidespines!(a) Makie.update_state_before_display!(f) - Makie.update_state_before_display!(f) - Makie.update_state_before_display!(f) img = Makie.colorbuffer(f.scene; px_per_unit = 1, pt_per_unit = 1, antialias = :none, visible = true, start_renderloop = false) # We have a matrix of all colors in the image. Now, what we do is the following: # The color white in RGBf is (1, 1, 1). For a color to be red, the blue and green components @@ -53,16 +51,13 @@ buffer = deepcopy(pixel_points) # First, we test the regular gutter with multiple categories. f, a, p = beeswarm(rand(1:3, 300), randn(300); color = rand(RGBAf, 300), markersize = 20, algorithm = SimpleBeeswarm()) Makie.update_state_before_display!(f) - Makie.update_state_before_display!(f) @test_warn "Gutter threshold exceeded" p.gutter = 0.2 # Next, we test it in direction y - f, a, p = beeswarm(rand(1:3, 300), randn(300); direction = :x, color = rand(RGBAf, 300), markersize = 20, algorithm = SimpleBeeswarm()) - Makie.update_state_before_display!(f) + f, a, p = beeswarm(rand(1:3, 300), randn(300); direction = :x, color = rand(RGBAf, 300), markersize = 20, algorithm = SimpleBeeswarm()) Makie.update_state_before_display!(f) @test_warn "Gutter threshold exceeded" p.gutter = 0.2 # and it shouldn't warn if, when using a lower markersize, the gutter is not reached. - f, a, p = beeswarm(rand(1:3, 300), randn(300); direction = :y, color = rand(RGBAf, 300), markersize = 9, algorithm = SimpleBeeswarm()) - Makie.update_state_before_display!(f) + f, a, p = beeswarm(rand(1:3, 300), randn(300); direction = :y, color = rand(RGBAf, 300), markersize = 9, algorithm = SimpleBeeswarm()) Makie.update_state_before_display!(f) @test_nowarn p.gutter = 0.5 end From 69f34cfea52784c6eb0748187886035270a3d1f0 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Thu, 3 Oct 2024 18:06:07 +0200 Subject: [PATCH 5/5] remove even more state updates and note to do so --- docs/src/algorithms.md | 1 - docs/src/introduction.md | 3 --- 2 files changed, 4 deletions(-) diff --git a/docs/src/algorithms.md b/docs/src/algorithms.md index 5c8ab53..f49ad0e 100644 --- a/docs/src/algorithms.md +++ b/docs/src/algorithms.md @@ -19,7 +19,6 @@ ax_plots = [beeswarm(fig[Tuple(idx)...], xs, ys; color = xs, algorithm = algorit jitter_plots = getproperty.(ax_plots[2, :], :plot) setproperty!.(jitter_plots, :markersize, 7) setproperty!.(jitter_plots, :alpha, 0.3) -Makie.update_state_before_display!(fig) fig ``` diff --git a/docs/src/introduction.md b/docs/src/introduction.md index 10dab2d..cf7b8b4 100644 --- a/docs/src/introduction.md +++ b/docs/src/introduction.md @@ -35,8 +35,6 @@ iris = dataset("datasets", "iris") f = data(iris) * mapping(:Species, :SepalLength; color = :Species) * visual(Beeswarm) |> draw -Makie.update_state_before_display!(f.figure) -Makie.update_state_before_display!(f.figure) f ``` @@ -44,4 +42,3 @@ f If your beeswarms are overlapping, or extending outside the axis area, try decreasing `markersize`. You can do this by setting `plot.markersize = 6` for example, and then re-displaying the figure. -Generally, the algorithm takes a few iterations of calling `Makie.update_state_before_display!(figure)` to settle in a good configuration. We are working to fix this.