From 981d954f3ed5fb8be55d01943404dcee092e2c4e Mon Sep 17 00:00:00 2001
From: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com>
Date: Mon, 21 Oct 2024 16:39:43 +0200
Subject: [PATCH 01/80] Figure size and dpi how-to (#4504)
* add typst to doc deps
* Rewrite figure size implementations and add simplified how-to
* remove unnecessary mkpath
* fix links
---
docs/Project.toml | 1 +
docs/makedocs.jl | 43 ++++---
docs/src/explanations/figure.md | 110 +++++++++++++-----
docs/src/explanations/scenes.md | 2 +-
.../match-figure-size-font-sizes-and-dpi.md | 68 +++++++++++
5 files changed, 174 insertions(+), 50 deletions(-)
create mode 100644 docs/src/how-to/match-figure-size-font-sizes-and-dpi.md
diff --git a/docs/Project.toml b/docs/Project.toml
index ce3ad5a3733..54e2bd62113 100644
--- a/docs/Project.toml
+++ b/docs/Project.toml
@@ -26,4 +26,5 @@ NodeJS = "2bd173c7-0d6d-553b-b6af-13a54713934c"
Observables = "510215fc-4207-5dde-b226-833fc4488ee2"
RPRMakie = "22d9f318-5e34-4b44-b769-6e3734a732a6"
RadeonProRender = "27029320-176d-4a42-b57d-56729d2ad457"
+Typst_jll = "eb4b1da6-20f6-5c66-9826-fdb8ad410d0e"
WGLMakie = "276b4fcb-3e11-5398-bf8b-a0c2d153d008"
diff --git a/docs/makedocs.jl b/docs/makedocs.jl
index 9a600603633..5ceb92f3170 100644
--- a/docs/makedocs.jl
+++ b/docs/makedocs.jl
@@ -187,6 +187,7 @@ pages = [
"explanations/transparency.md",
],
"How-Tos" => [
+ "how-to/match-figure-size-font-sizes-and-dpi.md",
"how-to/draw-boxes-around-subfigures.md",
"how-to/save-figure-with-transparency.md",
],
@@ -197,25 +198,29 @@ pages = [
]
]
-empty!(MakieDocsHelpers.FIGURES)
-
-# filter pages here when working on docs interactively
-# pages = nested_filter(pages, r"reference/blocks/(axis|axis3|overview)")
-
-Documenter.makedocs(;
- sitename="Makie",
- format=DocumenterVitepress.MarkdownVitepress(;
- repo = "github.com/MakieOrg/Makie.jl",
- devurl = "dev",
- devbranch = "master",
- deploy_url = "https://docs.makie.org", # for local testing not setting this has broken links with Makie.jl in them
- description = "Create impressive data visualizations with Makie, the plotting ecosystem for the Julia language. Build aesthetic plots with beautiful customizable themes, control every last detail of publication quality vector graphics, assemble complex layouts and quickly prototype interactive applications to explore your data live.",
- deploy_decision,
- ),
- pages,
- expandfirst = unnest(nested_filter(pages, r"reference/(plots|blocks)/(?!overview)")),
- warnonly = get(ENV, "CI", "false") != "true",
- pagesonly = true,
+function make_docs(; pages)
+ empty!(MakieDocsHelpers.FIGURES)
+
+ Documenter.makedocs(;
+ sitename="Makie",
+ format=DocumenterVitepress.MarkdownVitepress(;
+ repo = "github.com/MakieOrg/Makie.jl",
+ devurl = "dev",
+ devbranch = "master",
+ deploy_url = "https://docs.makie.org", # for local testing not setting this has broken links with Makie.jl in them
+ description = "Create impressive data visualizations with Makie, the plotting ecosystem for the Julia language. Build aesthetic plots with beautiful customizable themes, control every last detail of publication quality vector graphics, assemble complex layouts and quickly prototype interactive applications to explore your data live.",
+ deploy_decision,
+ ),
+ pages,
+ expandfirst = unnest(nested_filter(pages, r"reference/(plots|blocks)/(?!overview)")),
+ warnonly = get(ENV, "CI", "false") != "true",
+ pagesonly = true,
+ )
+end
+
+make_docs(;
+ # filter pages here when working on docs interactively
+ pages # = nested_filter(pages, r"explanations/figure|match-figure"),
)
##
diff --git a/docs/src/explanations/figure.md b/docs/src/explanations/figure.md
index 757393dabb6..0feb5521d54 100644
--- a/docs/src/explanations/figure.md
+++ b/docs/src/explanations/figure.md
@@ -152,52 +152,102 @@ contents(f[1, 1]) == [ax]
content(f[1, 1]) == ax
```
-## Figure size and units
+## Figure size and resolution
-In Makie, figure size and attributes like line widths, font sizes, scatter marker extents, or layout column and row gaps are usually given as plain numbers, without an explicit unit attached.
-What does it mean to have a `Figure` with `size = (600, 450)`, a line with `linewidth = 10` or a column gap of `30`?
+In Makie, the **size** of a `Figure` is unitless. That is because `Figure`s can be rendered as images or vector graphics or displayed in interactive windows. The actual physical size of those outputs depends on multiple factors such as screen sizes which are often outside of Makie's control, so we don't want to promise correct output size under all circumstances for a hypothetical `Figure(size = (4cm, 5cm))`.
-The first underlying idea is that, no matter what your final output format is, these numbers are _relative_.
-You can expect a `linewidth = 10` to cover 1/60th of the width `600` of the `Figure` and a column gap of `30` to span 1/20th of the Figure.
-This holds, no matter if you later export that `Figure` as an image made out of pixels, or as a vector graphic that doesn't have pixels at all.
+The `size` of a `Figure` first and foremost tells you how much space there is for `Axis` objects and other content. For example, `fontsize` or `Axis(width = ..., height = ...)` are also unitless, but they can be understood relative to the figure size. If there is not enough space in your `Figure`, you can either increase its `size` or decrease the size of the content (for example with smaller fontsizes). However, we don't _only_ care about the `size` of the `Figure` relative to the sizes of its contents. It also has a meaning when we think about how big or small our `Figure` will look when rendered on all the different possible output devices.
-The second idea is that, given some `Figure`, we want to be able to export an image at arbitrary resolution, or a vector graphic at any size from it, as long as the relative sizes of all elements stay intact.
-So we need to _translate_ our abstract sizes to real sizes when we render.
-In Makie, this is done with two scaling factors: `px_per_unit` for images and `pt_per_unit` for vector graphics.
+Now, although Makie uses unitless numbers for figure size, it is set up by default such that these numbers can actually be thought of as CSS pixels. We have chosen this convention to simplify using Makie in web contexts which includes browser-based tools like Pluto, Jupyter notebooks or editors like VSCode. All of these use CSS to control the appearance of objects.
-A line with `linewidth = 10` will be 10 pixels wide if rendered to an image file with `px_per_unit = 1`. It will be 5 pixels wide if `px_per_unit = 0.5` and 20 pixels if `px_per_unit = 2`. A `Figure` with `size = (600, 450)` will have 600 x 450 pixels when exported with `px_per_unit = 1`, 300 x 225 with `px_per_unit = 0.5` and 1200 x 900 with `px_per_unit = 2`.
+At default settings, a `Figure` of size `(600, 450)` will be displayed at a size of 600 x 450 CSS pixels in web contexts (if Makie renders via the `text/html` or `image/svg+xml` MIME types). This is true irrespective of its **resolution**, i.e., how many pixels the output bitmap has. The image will be annotated with `width = "600px" height = "450px"` so that browsers will know the intended display size.
-It works exactly the same for vector graphics, just with a different target unit. A `pt` or point is a typographic unit that is defined as 1/72 of an inch, which comes out to about 0.353 mm. A line with `linewidth = 10` will be 10 points wide if rendered to an svg file with `pt_per_unit = 1`, it will be 5 points wide for `pt_per_unit = 0.5` and 20 points wide if `pt_per_unit = 2`. A `Figure` with `size = (600, 450)` will be 600 x 450 points in size when exported with `pt_per_unit = 1`, 300 x 225 with `pt_per_unit = 0.5` and 1200 x 900 with `pt_per_unit = 2`.
+The CSS pixel is a physical unit (1 px == 1/96 inch) but of course browsers display content on many different screens and at many different zoom levels, so you would usually not expect an element of 96px width to be exactly 1 inch wide at any given time. But even if we don't know what physical size our plots will have on our screens, we want them to fit in well next to other content and text, so we want to match the sizes conventionally used on today's systems. For example, a common fontsize is `12 pt`, which is equivalent to `16 px` (1 px == 3/4 pt).
-### Defaults of `px_per_unit` and `pt_per_unit`
+This also applies to pdf outputs. When preparing plots for publications, we usually want to match font sizes of their plots to the base document, for example 12pt. But today we don't usually print pdfs on paper at their intended physical dimensions. Often, they are read on mobile devices where they are zoomed in and out, so any given text will rarely be at 12pt physically.
-What are the default values of `px_per_unit` and `pt_per_unit` in each Makie backend, and why are they set that way?
+While vector graphics are always rendered sharply at a given zoom level, for bitmaps, the actual number of pixels decides at what zoom level or viewing distance they look sharp or blurry. This "sharpness" factor is often specified in `dpi` or dots per inch. Again, the "inch" here should not be expected to always match an actual physical inch (like in the printing days) because of the way we zoom in and out on digital screens. But if we conventionally use CSS pixels to describe sizes, we can also use `dpi` and we'll know what sharpness to expect on typical devices and typical zoom levels.
-Let us start with `pt_per_unit` because this value is only relevant for one backend, which is CairoMakie.
-The default value in CairoMakie is `pt_per_unit = 0.75`. So if you `save("output.svg", figure)` a `Figure` with `size = (600, 450)`, this comes out as a vector graphic that is 450 x 337.5 pt large.
+To sum up, we have two factors that affect the rendered output of a Makie `Figure`. Its **size**, which determines the space available for content and the display size when interpreted in units like CSS pixels, and the **resolution** or sharpness in terms of pixel density or `dpi`. For vector graphics we only care about the size factor (unless we're embedding rasterized bitmaps in them).
-Why 0.75 and not simply 1? This has to do with web standards and device-independent pixels. Websites mix vector graphics and images, so they need some way to relate the sizes of both types to each other. In principle, a pixel in an image doesn't have a real-world width. But you don't want the images on your site to shrink relative to the other content when device pixels are small, or grow when device pixels are large. So web browsers don't directly map image pixels to device pixels. Instead, they use a concept called device-independent pixels. If you place an image with 600 x 450 pixels in a website, this image is interpreted by default to be 600 x 450 device-independent pixels wide. One device-independent pixel is defined to be 0.75 pt wide, that's where the factor 0.75 comes in. So an image with 600 x 450 device-independent pixels is the same apparent size as a vector graphic with size 450 x 337.5 pt. On high-resolution screens, browsers then simply render one device-independent pixel with multiple device pixels (for example 2x2 on an Apple Retina display) so that content stays at readable sizes and doesn't look tiny.
+### The `px_per_unit` factor
-For Makie, we decided that we want our abstract units to match device-independent pixels when used in web contexts, because that's very convenient and easy to predict for the end user. If you have a Jupyter or Pluto notebook, it's nice if a `Figure` comes out at the same apparent size, no matter if you're currently in CairoMakie's svg mode, or in the bitmap mode of any backend. Therefore, we annotate images with the original `Figure` size in device-independent pixels, so they are of the same apparent size, no matter what the `px_per_unit` value and therefore the effective pixel size is. And we give svg files the default scaling factor of 0.75 so that svgs always match images in apparent size.
+If we display a `Figure(size = (600, 450))` in a web context, by Makie's convention the image will be annotated with `width = "600px" height = "450px"`. But how many pixels does the actual bitmap have, i.e., how sharp is the image?
-Now let us look at the default values for `px_per_unit`. In CairoMakie, the default is `px_per_unit = 2`. This means, a `Figure` with `size = (600, 450)` will be rendered as a 1200 x 900 pixel image. The reason it isn't `px_per_unit = 1` is that CairoMakie plots are often embedded in notebooks or websites, or looked at in image viewers or IDEs like VSCode. On websites, you don't know in advance what the pixel density of a reader's display is going to be. And in image viewers and IDEs, people like to zoom in to look at details. To cover these use cases by default, we decided `px_per_unit = 2` is a good compromise between sharp resolution and appropriate file size. Again, the _apparent_ size of output images in notebooks and websites (wherever the `MIME"text/html"` type is used) depends only on the `size`, because the output images are embedded with ` Increasing `size` gives you more space for your content and a larger bitmap. When scaled to the same size in an output context (a pdf document for example), a figure with larger `size` will appear to have smaller content.
+
+```@raw html
+
+```
+
+> Increasing `px_per_unit` leaves the space for your content the same but gives a larger bitmap due to higher resolution. When scaled to the same size in an output context (a pdf document for example), a figure with larger `px_per_unit` will appear to have the same content, but sharper.
+
+```@raw html
+
+```
+
+There is also a `pt_per_unit` factor with which you can scale the output for vector graphics up or down. But if you keep with the convention that Makie's unitless numbers are actually CSS pixels, you can leave the default `pt_per_unit` at 0.75 and get size-matched bitmaps and vector graphics automatically.
diff --git a/docs/src/explanations/scenes.md b/docs/src/explanations/scenes.md
index d637d957efe..9a829196324 100644
--- a/docs/src/explanations/scenes.md
+++ b/docs/src/explanations/scenes.md
@@ -17,7 +17,7 @@ A Scene's subscenes (also called children) can be accessed through `scene.childr
Any `Scene` with an axis also has a `camera` associated with it; this can be accessed through `camera(scene)`, and its controls through `cameracontrols(scene)`. More documentation about these is in the [Cameras](@ref) section.
-`Scene`s have a configurable size. You can set the size in device-independent pixels by doing `Scene(size = (500, 500))`. (More about sizes, resolutions and units in [Figure size and units](@ref))
+`Scene`s have a configurable size. You can set the size in device-independent pixels by doing `Scene(size = (500, 500))`. (More about sizes, resolutions and units in [Figure size and resolution](@ref) or [How to match Figure size, font sizes and dpi](@ref))
Any keyword argument given to the `Scene` will be propagated to its plots; therefore, you can set the palette or the colormap in the Scene itself.
diff --git a/docs/src/how-to/match-figure-size-font-sizes-and-dpi.md b/docs/src/how-to/match-figure-size-font-sizes-and-dpi.md
new file mode 100644
index 00000000000..7f9132c5848
--- /dev/null
+++ b/docs/src/how-to/match-figure-size-font-sizes-and-dpi.md
@@ -0,0 +1,68 @@
+# How to match Figure size, font sizes and dpi
+
+We want to create three plots for inclusion in a document. These are the requirements:
+
+- Figure 1: png @ 4x3 inches and 100 dpi
+- Figure 2: png @ 9x7 cm and 300 dpi
+- Figure 3: svg @ 4x3 inches
+
+The fontsize of all three should match the document's 12pt setting.
+
+We assume the convention that Makie's unitless figure size is actually equivalent to CSS pixels.
+For a deeper explanation why, check the section [Figure size and resolution](@ref).
+
+We're using Typst here but the technique applies similarly for all authoring tools that allow you to set the dimensions of included images.
+
+```@example
+using CairoMakie
+CairoMakie.activate!() # hide
+using Typst_jll
+
+# these are relative to 1 CSS px
+inch = 96
+pt = 4/3
+cm = inch / 2.54
+
+f1 = Figure(size = (4inch, 3inch), fontsize = 12pt)
+f2 = Figure(size = (9cm, 7cm), fontsize = 12pt)
+f3 = Figure(size = (4inch, 3inch), fontsize = 12pt)
+
+titles = [
+ "Figure 1: png @ 4x3 inches and 100 dpi",
+ "Figure 2: png @ 9x7 cm and 300 dpi",
+ "Figure 3: svg @ 4x3 inches",
+]
+
+data = cumsum(randn(100))
+
+for (f, title) in zip([f1, f2, f3], titles)
+ ax = Axis(f[1, 1]; title, xlabel = "time (s)", ylabel = "value (€)")
+ lines!(ax, data)
+end
+
+save("figure1.png", f1, px_per_unit = 100/inch)
+save("figure2.png", f2, px_per_unit = 300/inch)
+save("figure3.svg", f3)
+
+typst_code = """
+ #set page(fill: rgb("#f5f2eb"))
+ #set text(font: "TeX Gyre Heros Makie", size: 12pt, fill: luma(50%))
+
+ This is some text at 12pt which the figures below should match.
+
+ #image("figure1.png", width: 4in, height: 3in)
+ #image("figure2.png", width: 9cm, height: 7cm)
+ #image("figure3.svg") // vector graphics have physical dimensions
+"""
+
+open(io -> println(io, typst_code), "document.typ", "w")
+
+cp(Makie.assetpath("fonts", "TeXGyreHerosMakie-Regular.otf"), "./texgyre.otf")
+
+run(`$(Typst_jll.typst()) compile --font-path . document.typ output.svg`)
+
+nothing # hide
+```
+
+![](output.svg)
+
From 814d8d5b19e742b9b2bc68ef41aa9724edcb19ca Mon Sep 17 00:00:00 2001
From: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com>
Date: Mon, 21 Oct 2024 19:40:52 +0200
Subject: [PATCH 02/80] Use polys for axis3 (#4463)
* use poly for axis3 panels so that svgs aren't rasterized
* add rasterization tests for Axis3 and PolarAxis
* disable stroke
* remove depth shift
* Update CHANGELOG.md
---------
Co-authored-by: Simon
---
CHANGELOG.md | 1 +
CairoMakie/test/svg_tests.jl | 2 ++
src/makielayout/blocks/axis3d.jl | 43 +++++++++++++++++++++-----------
3 files changed, 31 insertions(+), 15 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8cf94ec24d8..9061fd1100a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@
- Changed image, heatmap and surface picking indices to correctly index the relevant matrix arguments. [#4459](https://github.com/MakieOrg/Makie.jl/pull/4459)
- Improved performance of `record` by avoiding unnecessary copying in common cases [#4475](https://github.com/MakieOrg/Makie.jl/pull/4475).
- Fix usage of `AggMean()` and other aggregations operating on 3d data for `datashader` [#4346](https://github.com/MakieOrg/Makie.jl/pull/4346).
+- Use polys for axis3 [#4463](https://github.com/MakieOrg/Makie.jl/pull/4463).
- Changed default for `circular_rotation` in Camera3D to false, so that the camera doesn't change rotation direction anymore [4492](https://github.com/MakieOrg/Makie.jl/pull/4492)
- Fixed `pick(scene, rect2)` in WGLMakie [#4488](https://github.com/MakieOrg/Makie.jl/pull/4488)
diff --git a/CairoMakie/test/svg_tests.jl b/CairoMakie/test/svg_tests.jl
index 5efb72c4baf..da30bba0948 100644
--- a/CairoMakie/test/svg_tests.jl
+++ b/CairoMakie/test/svg_tests.jl
@@ -13,6 +13,8 @@ end
@testset "SVG rasterization" begin
@test svg_isnt_rasterized(Scene())
@test svg_isnt_rasterized(begin f = Figure(); Axis(f[1, 1]); f end)
+ @test svg_isnt_rasterized(begin f = Figure(); Axis3(f[1, 1]); f end)
+ @test svg_isnt_rasterized(begin f = Figure(); PolarAxis(f[1, 1]); f end)
@test svg_isnt_rasterized(scatter(1:3))
@test svg_isnt_rasterized(lines(1:3))
@test svg_isnt_rasterized(heatmap(rand(5, 5)))
diff --git a/src/makielayout/blocks/axis3d.jl b/src/makielayout/blocks/axis3d.jl
index ab3121fb487..a6b97462925 100644
--- a/src/makielayout/blocks/axis3d.jl
+++ b/src/makielayout/blocks/axis3d.jl
@@ -664,29 +664,42 @@ function add_panel!(scene, ax, dim1, dim2, dim3, limits, min3)
string((:x, :y, :z)[dim2]) * string(sym))
attr(sym) = getproperty(ax, dimsym(sym))
- vertices = lift(limits, min3) do lims, mi3
-
+ rect = lift(limits) do lims
mi = minimum(lims)
ma = maximum(lims)
+ Polygon([
+ Point2(mi[dim1], mi[dim2]),
+ Point2(ma[dim1], mi[dim2]),
+ Point2(ma[dim1], ma[dim2]),
+ Point2(mi[dim1], ma[dim2])
+ ])
+ end
- v3 = if mi3
- mi[dim3] + 0.005 * (mi[dim3] - ma[dim3])
- else
- ma[dim3] + 0.005 * (ma[dim3] - mi[dim3])
- end
+ plane_offset = lift(limits, min3) do lims, mi3
+ mi = minimum(lims)
+ ma = maximum(lims)
- p1 = dim3point(dim1, dim2, dim3, mi[dim1], mi[dim2], v3)
- p2 = dim3point(dim1, dim2, dim3, mi[dim1], ma[dim2], v3)
- p3 = dim3point(dim1, dim2, dim3, ma[dim1], ma[dim2], v3)
- p4 = dim3point(dim1, dim2, dim3, ma[dim1], mi[dim2], v3)
- [p1, p2, p3, p4]
+ mi3 ? mi[dim3] : ma[dim3]
end
- faces = [1 2 3; 3 4 1]
+ plane = Symbol((:x, :y, :z)[dim1], (:x, :y, :z)[dim2])
- panel = mesh!(scene, vertices, faces, shading = NoShading, inspectable = false,
+ panel = poly!(scene, rect, inspectable = false,
xautolimits = false, yautolimits = false, zautolimits = false,
- color = attr(:panelcolor), visible = attr(:panelvisible))
+ color = attr(:panelcolor), visible = attr(:panelvisible),
+ strokecolor = :transparent, strokewidth = 0,
+ transformation = (plane, 0),
+ )
+
+ on(plane_offset) do offset
+ translate!(
+ panel,
+ dim3 == 1 ? offset : zero(offset),
+ dim3 == 2 ? offset : zero(offset),
+ dim3 == 3 ? offset : zero(offset),
+ )
+ end
+
return panel
end
From 6b267799d179909ab59632e0b913b79e9f419086 Mon Sep 17 00:00:00 2001
From: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com>
Date: Mon, 21 Oct 2024 22:54:39 +0200
Subject: [PATCH 03/80] Benchmark simplification (#4493)
* use poly for axis3 panels so that svgs aren't rasterized
* add rasterization tests for Axis3 and PolarAxis
* disable stroke
* remove depth shift
* don't use BenchmarkTools, just run 100 trials and store all results
* interleave project runs
* bump run number to 20
* change timing names
* store results as artifact
* collect gc times separately
* only include previous 5 columns in comment to match old template
* use median of timings to avoid outliers
* store each json artifact separately
* remove superfluous arrays
---------
Co-authored-by: Simon
---
.github/workflows/compilation-benchmark.yaml | 7 ++++-
metrics/ttfp/benchmark-ttfp.jl | 32 ++++++++++++++++----
metrics/ttfp/run-benchmark.jl | 16 +++++++---
3 files changed, 44 insertions(+), 11 deletions(-)
diff --git a/.github/workflows/compilation-benchmark.yaml b/.github/workflows/compilation-benchmark.yaml
index 6df32a3184f..17cb146fed4 100644
--- a/.github/workflows/compilation-benchmark.yaml
+++ b/.github/workflows/compilation-benchmark.yaml
@@ -37,4 +37,9 @@ jobs:
GITHUB_TOKEN: ${{ secrets.BENCHMARK_KEY }}
PR_NUMBER: ${{ github.event.number }}
run: >
- DISPLAY=:0 xvfb-run -s '-screen 0 1024x768x24' julia --project=./metrics/ttfp/ ./metrics/ttfp/run-benchmark.jl ${{ matrix.package }} 7 ${{ github.event.pull_request.base.ref }}
+ DISPLAY=:0 xvfb-run -s '-screen 0 1024x768x24' julia --project=./metrics/ttfp/ ./metrics/ttfp/run-benchmark.jl ${{ matrix.package }} 20 ${{ github.event.pull_request.base.ref }}
+ - name: Upload measurements as artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ matrix.package }}
+ path: ./json
diff --git a/metrics/ttfp/benchmark-ttfp.jl b/metrics/ttfp/benchmark-ttfp.jl
index 274b1c3e38d..d44f016091d 100644
--- a/metrics/ttfp/benchmark-ttfp.jl
+++ b/metrics/ttfp/benchmark-ttfp.jl
@@ -20,9 +20,9 @@ set_theme!(size=(800, 600))
create_time = @ctime fig = scatter(1:4; color=1:4, colormap=:turbo, markersize=20, visible=true)
display_time = @ctime colorbuffer(fig; px_per_unit=1)
-using BenchmarkTools
-using BenchmarkTools.JSON
+using JSON
using Pkg
+using Statistics: median
project_name = basename(dirname(Pkg.project().path))
@@ -31,13 +31,33 @@ old = isfile(result) ? JSON.parse(read(result, String)) : [[], [], [], [], []]
@show [t_using, create_time, display_time]
push!.(old[1:3], [t_using, create_time, display_time])
-b1 = @benchmark fig = scatter(1:4; color=1:4, colormap=:turbo, markersize=20, visible=true)
-b2 = @benchmark colorbuffer(fig; px_per_unit=1)
+macro simple_median_time(expr)
+ time_expr = quote
+ local elapsedtime = time_ns()
+ $expr
+ elapsedtime = time_ns() - elapsedtime
+ Float64(elapsedtime)
+ end
+
+ quote
+ times = Float64[]
+ for i in 1:101
+ t = Core.eval(Main, $(QuoteNode(time_expr)))
+ if i > 1
+ push!(times, t)
+ end
+ end
+ median(times)
+ end
+end
+@time "creating figure" figure_time = @simple_median_time fig = scatter(1:4; color=1:4, colormap=:turbo, markersize=20, visible=true)
+fig = scatter(1:4; color=1:4, colormap=:turbo, markersize=20, visible=true)
+@time "colorbuffer" colorbuffer_time = @simple_median_time colorbuffer(fig; px_per_unit=1)
using Statistics
-push!(old[4], mean(b1.times))
-push!(old[5], mean(b2.times))
+push!(old[4], figure_time)
+push!(old[5], colorbuffer_time)
open(io-> JSON.print(io, old), result, "w")
diff --git a/metrics/ttfp/run-benchmark.jl b/metrics/ttfp/run-benchmark.jl
index 2d7d4602eca..84374eae260 100644
--- a/metrics/ttfp/run-benchmark.jl
+++ b/metrics/ttfp/run-benchmark.jl
@@ -182,9 +182,10 @@ using Random
function run_benchmarks(projects; n=n_samples)
benchmark_file = joinpath(@__DIR__, "benchmark-ttfp.jl")
- for project in shuffle!(repeat(projects; outer=n))
+ # go A, B, A, B, A, etc.
+ for project in repeat(projects, n)
+ println(basename(project))
run(`$(Base.julia_cmd()) --startup-file=no --project=$(project) $benchmark_file $Package`)
- project_name = basename(project)
end
return
end
@@ -221,13 +222,13 @@ end
pkgs = NamedTuple[(; path="./MakieCore"), (; path="."), (; path="./$Package")]
# cd("dev/Makie")
Pkg.develop(pkgs)
-Pkg.add([(; name="BenchmarkTools")])
+Pkg.add([(; name="JSON")])
@time Pkg.precompile()
project2 = make_project_folder(base_branch)
Pkg.activate(project2)
-pkgs = [(; rev=base_branch, name="MakieCore"), (; rev=base_branch, name="Makie"), (; rev=base_branch, name="$Package"), (;name="BenchmarkTools")]
+pkgs = [(; rev=base_branch, name="MakieCore"), (; rev=base_branch, name="Makie"), (; rev=base_branch, name="$Package"), (;name="JSON")]
Package == "WGLMakie" && push!(pkgs, (; name="Electron"))
Pkg.add(pkgs)
@time Pkg.precompile()
@@ -249,3 +250,10 @@ else
@info("Not commenting, no PR found")
println(update_comment(COMMENT_TEMPLATE, Package, benchmark_rows))
end
+
+mkdir("json")
+for p in [project1, project2]
+ name = basename(p)
+ file = "$name-benchmark.json"
+ mv(file, joinpath("json", file))
+end
From 49b29b61f8f5e5d3d5a8880e3e455238987997ce Mon Sep 17 00:00:00 2001
From: Eddie Groshev
Date: Mon, 21 Oct 2024 17:16:03 -0600
Subject: [PATCH 04/80] Allow `width` to be set per box in `boxplot` (#4447)
* allow width per category in BoxPlot
* fix boxplot when range is zero
* update CHANGELOG
* add test for variable width
---------
Co-authored-by: Simon
Co-authored-by: Frederic Freyer
---
CHANGELOG.md | 1 +
ReferenceTests/src/tests/examples2d.jl | 13 ++++++-------
src/stats/boxplot.jl | 20 +++++++++++---------
3 files changed, 18 insertions(+), 16 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9061fd1100a..35dc4c886a6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
## [Unreleased]
+- Allow `width` to be set per box in `boxplot` [#4447](https://github.com/MakieOrg/Makie.jl/pull/4447).
- For `Textbox`es in which a fixed width is specified, the text is now scrolled
if the width is exceeded [#4293](https://github.com/MakieOrg/Makie.jl/pull/4293)
- Changed image, heatmap and surface picking indices to correctly index the relevant matrix arguments. [#4459](https://github.com/MakieOrg/Makie.jl/pull/4459)
diff --git a/ReferenceTests/src/tests/examples2d.jl b/ReferenceTests/src/tests/examples2d.jl
index 4fa7d400b79..94414126528 100644
--- a/ReferenceTests/src/tests/examples2d.jl
+++ b/ReferenceTests/src/tests/examples2d.jl
@@ -1613,8 +1613,8 @@ end
boxplot(fig[1, 1], categories, values)
dodge = RNG.rand(1:2, 900)
- boxplot(fig[1, 2], categories, values, dodge = dodge, show_notch = true,
- color = map(d->d==1 ? :blue : :red, dodge),
+ boxplot(fig[1, 2], categories, values, dodge = dodge, show_notch = true,
+ color = map(d->d==1 ? :blue : :red, dodge),
outliercolor = RNG.rand([:red, :green, :blue, :black, :orange], 900)
)
@@ -1631,16 +1631,15 @@ end
weights = 1.0 ./ (1.0 .+ abs.(values))
boxplot!(ax_vert, categories, values, orientation=:vertical, weights = weights,
- gap = 0.5,
- show_notch = true, notchwidth = 0.75,
+ gap = 0.5,
+ show_notch = true, notchwidth = 0.75,
markersize = 5, strokewidth = 2.0, strokecolor = :black,
medianlinewidth = 5, mediancolor = :orange,
whiskerwidth = 1.0, whiskerlinewidth = 3, whiskercolor = :green,
outlierstrokewidth = 1.0, outlierstrokecolor = :red,
- width = 1.5,
-
+ width = 1.5,
)
- boxplot!(ax_horiz, categories, values; orientation=:horizontal)
+ boxplot!(ax_horiz, categories, values; orientation=:horizontal, width = categories ./ 3)
fig
end
diff --git a/src/stats/boxplot.jl b/src/stats/boxplot.jl
index 10cc2f9c6aa..e761b96301e 100644
--- a/src/stats/boxplot.jl
+++ b/src/stats/boxplot.jl
@@ -85,11 +85,10 @@ function Makie.plot!(plot::BoxPlot)
plot[:color],
args...,
) do x, y, color, weights, width, range, show_outliers, whiskerwidth, show_notch, orientation, gap, dodge, n_dodge, dodge_gap
- x̂, boxwidth = compute_x_and_width(x, width, gap, dodge, n_dodge, dodge_gap)
+ x̂, widths = compute_x_and_width(x, width, gap, dodge, n_dodge, dodge_gap)
if !(whiskerwidth === :match || whiskerwidth >= 0)
error("whiskerwidth must be :match or a positive number. Found: $whiskerwidth")
end
- ww = whiskerwidth === :match ? boxwidth : whiskerwidth * boxwidth
outlier_points = Point2f[]
centers = Float32[]
medians = Float32[]
@@ -99,8 +98,10 @@ function Makie.plot!(plot::BoxPlot)
notchmax = Float32[]
t_segments = Point2f[]
outlier_indices = Int[]
- T = color isa AbstractVector ? eltype(color) : typeof(color)
- boxcolor = T[]
+ CT = color isa AbstractVector ? eltype(color) : typeof(color)
+ boxcolor = CT[]
+ WT = widths isa AbstractVector ? eltype(widths) : typeof(widths)
+ boxwidth = WT[]
for (i, (center, idxs)) in enumerate(StructArrays.finduniquesorted(x̂))
values = view(y, idxs)
@@ -117,7 +118,7 @@ function Makie.plot!(plot::BoxPlot)
end
# outliers
- if Float64(range) != 0.0 # if the range is 0.0, the whiskers will extend to the data
+ if !iszero(range) # if the range is 0, the whiskers will extend to the data
limit = range * (q4 - q2)
inside = Float64[]
for (value, idx) in zip(values,idxs)
@@ -134,18 +135,19 @@ function Makie.plot!(plot::BoxPlot)
# change q1 and q5 to show outliers
# using maximum and minimum values inside the limits
q1, q5 = extrema_nan(inside)
- # register boxcolor
- push!(boxcolor, getuniquevalue(color, idxs))
end
# whiskers
- HW = 0.5 * _cycle(ww, i) # Whisker width
- lw, rw = center - HW, center + HW
+ bw = getuniquevalue(widths, idxs) # Box width
+ ww = whiskerwidth === :match ? bw : whiskerwidth * bw # Whisker width
+ lw, rw = center - ww/2, center + ww/2
push!(t_segments, (center, q2), (center, q1), (lw, q1), (rw, q1)) # lower T
push!(t_segments, (center, q4), (center, q5), (rw, q5), (lw, q5)) # upper T
# box
+ push!(boxcolor, getuniquevalue(color, idxs))
push!(centers, center)
+ push!(boxwidth, bw)
push!(boxmin, q2)
push!(medians, q3)
push!(boxmax, q4)
From e3c7521d3b5bc51bb1234b9f0eae2645d5b6463f Mon Sep 17 00:00:00 2001
From: Anshul Singhvi
Date: Mon, 21 Oct 2024 17:09:31 -0700
Subject: [PATCH 05/80] Improve CairoMakie's 2D mesh performance (#4132)
* Remove splats in draw_mesh2d inner loop
This also restores parity in the loop body with `draw_mesh3d`.
* Squash point type instability in project_position
This shaves ~20% runtime from mesh2d
* Fix types in function signature
* Add changelog entry
---------
Co-authored-by: Simon
Co-authored-by: Frederic Freyer
---
CHANGELOG.md | 1 +
CairoMakie/src/primitives.jl | 10 +++++-----
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 35dc4c886a6..2045a7854b5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
## [Unreleased]
+- Improved CairoMakie's 2D mesh drawing performance by ~30% [#4132](https://github.com/MakieOrg/Makie.jl/pull/4132).
- Allow `width` to be set per box in `boxplot` [#4447](https://github.com/MakieOrg/Makie.jl/pull/4447).
- For `Textbox`es in which a fixed width is specified, the text is now scrolled
if the width is exceeded [#4293](https://github.com/MakieOrg/Makie.jl/pull/4293)
diff --git a/CairoMakie/src/primitives.jl b/CairoMakie/src/primitives.jl
index a87386c5ce9..795359670ca 100644
--- a/CairoMakie/src/primitives.jl
+++ b/CairoMakie/src/primitives.jl
@@ -908,11 +908,11 @@ function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Maki
return nothing
end
-function draw_mesh2D(scene, screen, @nospecialize(plot), @nospecialize(mesh))
+function draw_mesh2D(scene, screen, @nospecialize(plot::Makie.Mesh), @nospecialize(mesh::GeometryBasics.Mesh))
space = to_value(get(plot, :space, :data))::Symbol
transform_func = Makie.transform_func(plot)
model = plot.model[]::Mat4d
- vs = project_position(scene, transform_func, space, decompose(Point, mesh), model)
+ vs = project_position(scene, transform_func, space, GeometryBasics.metafree(GeometryBasics.coordinates(mesh)), model)::Vector{Point2f}
fs = decompose(GLTriangleFace, mesh)::Vector{GLTriangleFace}
uv = decompose_uv(mesh)::Union{Nothing, Vector{Vec2f}}
# Note: This assume the function is only called from mesh plots
@@ -943,9 +943,9 @@ function draw_mesh2D(screen, per_face_cols, vs::Vector{<: Point2}, fs::Vector{GL
Cairo.mesh_pattern_begin_patch(pattern)
- Cairo.mesh_pattern_move_to(pattern, t1...)
- Cairo.mesh_pattern_line_to(pattern, t2...)
- Cairo.mesh_pattern_line_to(pattern, t3...)
+ Cairo.mesh_pattern_move_to(pattern, t1[1], t1[2])
+ Cairo.mesh_pattern_line_to(pattern, t2[1], t2[2])
+ Cairo.mesh_pattern_line_to(pattern, t3[1], t3[2])
mesh_pattern_set_corner_color(pattern, 0, c1)
mesh_pattern_set_corner_color(pattern, 1, c2)
From 2c739420e2ea330b217fe3156d135f6ef6cdfb82 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Tue, 22 Oct 2024 10:17:55 +0200
Subject: [PATCH 06/80] CompatHelper: bump compat for ColorTypes to 0.12, (keep
existing compat) (#4502)
Co-authored-by: CompatHelper Julia
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index c4026e2a4a0..92538749fb0 100644
--- a/Project.toml
+++ b/Project.toml
@@ -69,7 +69,7 @@ Base64 = "1.0, 1.6"
CRC32c = "1.0, 1.6"
ColorBrewer = "0.4"
ColorSchemes = "3.5"
-ColorTypes = "0.8, 0.9, 0.10, 0.11"
+ColorTypes = "0.8, 0.9, 0.10, 0.11, 0.12"
Colors = "0.9, 0.10, 0.11, 0.12"
Contour = "0.5, 0.6"
DelaunayTriangulation = "1.0"
From 948096846edd26e54f2e7e3e8530ecdd32788dac Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Tue, 22 Oct 2024 11:44:29 +0200
Subject: [PATCH 07/80] CompatHelper: bump compat for ColorTypes to 0.12 for
package MakieCore, (keep existing compat) (#4507)
Co-authored-by: CompatHelper Julia
---
MakieCore/Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MakieCore/Project.toml b/MakieCore/Project.toml
index 5dde5dfb08c..f6ec96acc73 100644
--- a/MakieCore/Project.toml
+++ b/MakieCore/Project.toml
@@ -10,7 +10,7 @@ IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953"
Observables = "510215fc-4207-5dde-b226-833fc4488ee2"
[compat]
-ColorTypes = "0.8, 0.9, 0.10, 0.11"
+ColorTypes = "0.8, 0.9, 0.10, 0.11, 0.12"
GeometryBasics = "0.4.11"
IntervalSets = "0.6, 0.7"
Observables = "0.5.1"
From 6dfb420445065c56f5af7944c87ecdfc22eff507 Mon Sep 17 00:00:00 2001
From: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com>
Date: Tue, 22 Oct 2024 11:51:48 +0200
Subject: [PATCH 08/80] Move favicon and logo to where DocumenterVitepress
expects them (#4505)
* Move favicon and logo to where DocumenterVitepress expects them
* move logo.svg
---
docs/src/{assets => public}/favicon.ico | Bin
docs/src/public/logo.png | Bin 0 -> 20893 bytes
docs/src/{assets => public}/logo.svg | 0
3 files changed, 0 insertions(+), 0 deletions(-)
rename docs/src/{assets => public}/favicon.ico (100%)
create mode 100644 docs/src/public/logo.png
rename docs/src/{assets => public}/logo.svg (100%)
diff --git a/docs/src/assets/favicon.ico b/docs/src/public/favicon.ico
similarity index 100%
rename from docs/src/assets/favicon.ico
rename to docs/src/public/favicon.ico
diff --git a/docs/src/public/logo.png b/docs/src/public/logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..aa8ce3e73d42fe732de854a4a5066d9b7b6cbdd9
GIT binary patch
literal 20893
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
L&Gf2tu+jeqO
Date: Wed, 23 Oct 2024 12:04:46 +0200
Subject: [PATCH 09/80] CompatHelper: bump compat for Colors to 0.13, (keep
existing compat) (#4509)
Co-authored-by: CompatHelper Julia
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 92538749fb0..ac3a5b2fa53 100644
--- a/Project.toml
+++ b/Project.toml
@@ -70,7 +70,7 @@ CRC32c = "1.0, 1.6"
ColorBrewer = "0.4"
ColorSchemes = "3.5"
ColorTypes = "0.8, 0.9, 0.10, 0.11, 0.12"
-Colors = "0.9, 0.10, 0.11, 0.12"
+Colors = "0.9, 0.10, 0.11, 0.12, 0.13"
Contour = "0.5, 0.6"
DelaunayTriangulation = "1.0"
Distributions = "0.17, 0.18, 0.19, 0.20, 0.21, 0.22, 0.23, 0.24, 0.25"
From a1c513b73f6238f6fcbfcc3d7c9d5ba41493e2a7 Mon Sep 17 00:00:00 2001
From: Julius Krumbiegel
Date: Wed, 23 Oct 2024 12:17:34 +0200
Subject: [PATCH 10/80] Fix capitalization in section header
---
docs/src/explanations/scenes.md | 2 +-
docs/src/how-to/match-figure-size-font-sizes-and-dpi.md | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/src/explanations/scenes.md b/docs/src/explanations/scenes.md
index 9a829196324..fb72cf2e75f 100644
--- a/docs/src/explanations/scenes.md
+++ b/docs/src/explanations/scenes.md
@@ -17,7 +17,7 @@ A Scene's subscenes (also called children) can be accessed through `scene.childr
Any `Scene` with an axis also has a `camera` associated with it; this can be accessed through `camera(scene)`, and its controls through `cameracontrols(scene)`. More documentation about these is in the [Cameras](@ref) section.
-`Scene`s have a configurable size. You can set the size in device-independent pixels by doing `Scene(size = (500, 500))`. (More about sizes, resolutions and units in [Figure size and resolution](@ref) or [How to match Figure size, font sizes and dpi](@ref))
+`Scene`s have a configurable size. You can set the size in device-independent pixels by doing `Scene(size = (500, 500))`. (More about sizes, resolutions and units in [Figure size and resolution](@ref) or [How to match figure size, font sizes and dpi](@ref))
Any keyword argument given to the `Scene` will be propagated to its plots; therefore, you can set the palette or the colormap in the Scene itself.
diff --git a/docs/src/how-to/match-figure-size-font-sizes-and-dpi.md b/docs/src/how-to/match-figure-size-font-sizes-and-dpi.md
index 7f9132c5848..2375a9e3f52 100644
--- a/docs/src/how-to/match-figure-size-font-sizes-and-dpi.md
+++ b/docs/src/how-to/match-figure-size-font-sizes-and-dpi.md
@@ -1,4 +1,4 @@
-# How to match Figure size, font sizes and dpi
+# How to match figure size, font sizes and dpi
We want to create three plots for inclusion in a document. These are the requirements:
From 06c986d6ddf601698634b4d164cfaeaa386bb760 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Thu, 24 Oct 2024 12:15:17 +0200
Subject: [PATCH 11/80] CompatHelper: bump compat for ColorTypes to 0.12 for
package GLMakie, (keep existing compat) (#4518)
Co-authored-by: CompatHelper Julia
---
GLMakie/Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/GLMakie/Project.toml b/GLMakie/Project.toml
index cbd5fe66c22..93b3fe0470d 100644
--- a/GLMakie/Project.toml
+++ b/GLMakie/Project.toml
@@ -22,7 +22,7 @@ ShaderAbstractions = "65257c39-d410-5151-9873-9b3e5be5013e"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
[compat]
-ColorTypes = "0.9, 0.10, 0.11"
+ColorTypes = "0.9, 0.10, 0.11, 0.12"
Colors = "0.11, 0.12"
FileIO = "1.6"
FixedPointNumbers = "0.7, 0.8"
From 445d7aea5035734299f265f22a3e794e68c415c5 Mon Sep 17 00:00:00 2001
From: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com>
Date: Thu, 24 Oct 2024 12:19:34 +0200
Subject: [PATCH 12/80] Test each backend for relocatability (#4288)
* Test each backend for relocatability
* different bash syntax
* `@info` to see hangs
* add Electron for WGLMakie
* don't swallow stdout and stderr when executing app
* enable color so output is easier to read
* fix WGLMakie
* Update relocatability.jl
* revert using checkout commit and dev instead
---------
Co-authored-by: Simon
---
.github/workflows/relocatability.yml | 20 ++++++++++++----
relocatability.jl | 35 +++++++++++++++++++++-------
2 files changed, 41 insertions(+), 14 deletions(-)
diff --git a/.github/workflows/relocatability.yml b/.github/workflows/relocatability.yml
index d3e62f5c33f..7f333b57670 100644
--- a/.github/workflows/relocatability.yml
+++ b/.github/workflows/relocatability.yml
@@ -17,8 +17,8 @@ concurrency:
cancel-in-progress: true
jobs:
- glmakie:
- name: GLMakie relocatability
+ makie-relocatability:
+ name: Relocatability ${{ matrix.backend }}
env:
MODERNGL_DEBUGGING: "true" # turn on errors when running OpenGL tests
runs-on: ${{ matrix.os }}
@@ -31,6 +31,10 @@ jobs:
- ubuntu-20.04
arch:
- x64
+ backend:
+ - GLMakie
+ - WGLMakie
+ - CairoMakie
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -39,7 +43,13 @@ jobs:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: julia-actions/cache@v2
- - 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 dependencies for GPU backends
+ if: matrix.backend != 'CairoMakie'
+ 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: Relocatability test
- run: >
- DISPLAY=:0 xvfb-run -s '-screen 0 1024x768x24' julia ./relocatability.jl
+ run: |
+ if [ "${{ matrix.backend }}" != "CairoMakie" ]; then
+ DISPLAY=:0 xvfb-run -s '-screen 0 1024x768x24' julia --color=yes ./relocatability.jl ${{ matrix.backend }}
+ else
+ julia --color=yes ./relocatability.jl ${{ matrix.backend }}
+ fi
\ No newline at end of file
diff --git a/relocatability.jl b/relocatability.jl
index b036ef4903f..6977bdd8eff 100644
--- a/relocatability.jl
+++ b/relocatability.jl
@@ -1,11 +1,22 @@
-
+const BACKEND = ARGS[1]
+@assert BACKEND in ["CairoMakie", "GLMakie", "WGLMakie"]
module_src = """
module MakieApp
-using GLMakie
+using $BACKEND
+
+if "$BACKEND" == "WGLMakie"
+ using Electron
+ function _display(fig)
+ disp = WGLMakie.Bonito.use_electron_display()
+ display(disp, WGLMakie.Bonito.App(fig))
+ end
+else
+ _display(fig) = display(fig)
+end
function julia_main()::Cint
- screen = display(scatter(1:4))
+ screen = _display(scatter(1:4))
# wait(screen) commented out to test if this blocks anything, but didn't change anything
return 0 # if things finished successfully
end
@@ -21,14 +32,15 @@ Pkg.generate("MakieApp")
Pkg.activate("MakieApp")
makie_dir = @__DIR__
-commit = cd(makie_dir) do
- chomp(read(`git rev-parse --verify HEAD`, String))
-end
# Add packages from branch, to make it easier to move the code later (e.g. when running this locally)
# Since, package dir is much easier to move then the active project (on windows at least).
-paths = ["MakieCore", "Makie", "GLMakie"]
-Pkg.add(map(x -> (; name=x, rev=commit), paths))
+paths = ["MakieCore", "", BACKEND]
+Pkg.develop(map(x -> (; path=joinpath(makie_dir, x)), paths))
+
+if BACKEND == "WGLMakie"
+ pkg"add Electron@5.1"
+end
open("MakieApp/src/MakieApp.jl", "w") do io
print(io, module_src)
@@ -44,14 +56,19 @@ exe = joinpath(pwd(), "executable", "bin", "MakieApp")
# `run` allows to see potential informative printouts, `success` swallows those
p = run(`$(exe)`)
@test p.exitcode == 0
+
julia_pkg_dir = joinpath(Base.DEPOT_PATH[1], "packages")
@test isdir(julia_pkg_dir)
mvd_julia_pkg_dir = julia_pkg_dir * ".old"
-mv(julia_pkg_dir, mvd_julia_pkg_dir, force = true)
+new_makie_dir = makie_dir * ".old"
+mv(julia_pkg_dir, mvd_julia_pkg_dir; force=true)
+mv(makie_dir, new_makie_dir; force=true)
# Move package dir so that we can test relocatability (hardcoded paths to package dir being invalid now)
try
+ @info "Running executable in relocated mode..."
p2 = run(`$(exe)`)
@test p2.exitcode == 0
finally
mv(mvd_julia_pkg_dir, julia_pkg_dir)
+ mv(new_makie_dir, makie_dir)
end
From de0818680bde161022695abad6fad4542582044b Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 25 Oct 2024 12:01:40 +0200
Subject: [PATCH 13/80] CompatHelper: bump compat for Colors to 0.13 for
package GLMakie, (keep existing compat) (#4525)
Co-authored-by: CompatHelper Julia
---
GLMakie/Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/GLMakie/Project.toml b/GLMakie/Project.toml
index 93b3fe0470d..9ade73d46f1 100644
--- a/GLMakie/Project.toml
+++ b/GLMakie/Project.toml
@@ -23,7 +23,7 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
[compat]
ColorTypes = "0.9, 0.10, 0.11, 0.12"
-Colors = "0.11, 0.12"
+Colors = "0.11, 0.12, 0.13"
FileIO = "1.6"
FixedPointNumbers = "0.7, 0.8"
FreeTypeAbstraction = "0.10"
From 9d758c96fd2f684e132b2ef7880a6cdc8bb9c13e Mon Sep 17 00:00:00 2001
From: Frederic Freyer
Date: Fri, 25 Oct 2024 12:01:57 +0200
Subject: [PATCH 14/80] Try temporary fix for WGLMakie CI (#4523)
restrict LoggingExtras version
---
WGLMakie/Project.toml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/WGLMakie/Project.toml b/WGLMakie/Project.toml
index 5d469a901af..886599cc21e 100644
--- a/WGLMakie/Project.toml
+++ b/WGLMakie/Project.toml
@@ -11,6 +11,7 @@ FreeTypeAbstraction = "663a7486-cb36-511b-a19d-713bb74d65c9"
GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326"
Hyperscript = "47d2ed2b-36de-50cf-bf87-49c2cf4b8b91"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
+LoggingExtras = "e6f89c97-d47a-5376-807f-9c37f3926c36"
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
Observables = "510215fc-4207-5dde-b226-833fc4488ee2"
PNGFiles = "f57f5aa1-a3ce-4bc8-8ab9-96f992907883"
@@ -27,6 +28,7 @@ FreeTypeAbstraction = "0.10"
GeometryBasics = "0.4.11"
Hyperscript = "0.0.3, 0.0.4, 0.0.5"
LinearAlgebra = "1.0, 1.6"
+LoggingExtras = "<1.1.0"
Makie = "=0.21.14"
Observables = "0.5.1"
PNGFiles = "0.3, 0.4"
From 1f67094e0f90b40f506076cc3276f5e3d6d7b463 Mon Sep 17 00:00:00 2001
From: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com>
Date: Fri, 25 Oct 2024 12:04:19 +0200
Subject: [PATCH 15/80] Allow creation of `Legend` with entries that have no
legend elements (#4526)
---
CHANGELOG.md | 1 +
src/makielayout/blocks/legend.jl | 3 ---
test/makielayout.jl | 5 +++++
3 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2045a7854b5..41db9c09615 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
## [Unreleased]
+- Allowed creation of `Legend` with entries that have no legend elements [#4526](https://github.com/MakieOrg/Makie.jl/pull/4526).
- Improved CairoMakie's 2D mesh drawing performance by ~30% [#4132](https://github.com/MakieOrg/Makie.jl/pull/4132).
- Allow `width` to be set per box in `boxplot` [#4447](https://github.com/MakieOrg/Makie.jl/pull/4447).
- For `Textbox`es in which a fixed width is specified, the text is now scrolled
diff --git a/src/makielayout/blocks/legend.jl b/src/makielayout/blocks/legend.jl
index bf1a07226f3..8344667b28e 100644
--- a/src/makielayout/blocks/legend.jl
+++ b/src/makielayout/blocks/legend.jl
@@ -406,9 +406,6 @@ function LegendEntry(label, content, legend; kwargs...)
else
elems = legendelements(content, legend)
end
- if isempty(elems)
- error("`legendelements` returned an empty list for content element of type $(typeof(content)). That could mean that neither this object nor any possible child objects had a method for `legendelements` defined that returned a non-empty result.")
- end
LegendEntry(elems, attrs)
end
diff --git a/test/makielayout.jl b/test/makielayout.jl
index d62db7bd3e5..d0da12133e5 100644
--- a/test/makielayout.jl
+++ b/test/makielayout.jl
@@ -476,6 +476,11 @@ end
@test_nowarn axislegend()
end
+@testset "Legend with empty element" begin
+ f = Figure()
+ @test_nowarn Legend(f[1, 1], [[]], ["No legend elements"])
+end
+
@testset "ReversibleScale" begin
@test ReversibleScale(identity).inverse === identity
@test ReversibleScale(log).inverse === exp
From a12c284742215ded6d6691aa75c775caf1c379e7 Mon Sep 17 00:00:00 2001
From: Simon
Date: Fri, 25 Oct 2024 16:05:18 +0200
Subject: [PATCH 16/80] fix pick_sorted for WGLMakie and hover over
heatmap(Resampler(data)) (#4521)
* fix pick_sorted for WGLMakie and hover over heatmap(Resampler(data))
* test pick_closest & fix range handling
* test pick_sorted
---------
Co-authored-by: ffreyer
---
GLMakie/src/picking.jl | 2 +-
.../src/tests/generic_components.jl | 558 +++++++++++-------
WGLMakie/src/picking.jl | 8 +-
WGLMakie/src/wglmakie.bundled.js | 2 +-
WGLMakie/src/wglmakie.js | 2 +-
src/interaction/inspector.jl | 4 +-
src/interaction/interactive_api.jl | 2 +-
7 files changed, 355 insertions(+), 223 deletions(-)
diff --git a/GLMakie/src/picking.jl b/GLMakie/src/picking.jl
index 47270f9df8e..cffc12dd73b 100644
--- a/GLMakie/src/picking.jl
+++ b/GLMakie/src/picking.jl
@@ -84,7 +84,7 @@ function Makie.pick_closest(scene::Scene, screen::Screen, xy, range)
sids = zeros(SelectionID{UInt32}, dx, dy)
glReadPixels(x0, y0, dx, dy, buff.format, buff.pixeltype, sids)
- min_dist = floatmax(Float32)
+ min_dist = ppu * ppu * range * range
id = SelectionID{Int}(0, 0)
x, y = xy .* ppu .+ 1 .- Vec2f(x0, y0)
for i in 1:dx, j in 1:dy
diff --git a/ReferenceTests/src/tests/generic_components.jl b/ReferenceTests/src/tests/generic_components.jl
index 1e55b31af39..d7aa43a6f29 100644
--- a/ReferenceTests/src/tests/generic_components.jl
+++ b/ReferenceTests/src/tests/generic_components.jl
@@ -26,6 +26,9 @@
s2 = surface!(scene, 210..180, 80..110, rand(2, 2))
hm2 = heatmap!(scene, [210, 180], [140, 170], [1 2; 3 4])
+ # for ranged picks
+ m2 = mesh!(scene, Rect2f(190, 330, 10, 10))
+
scene # for easy reviewing of the plot
# render one frame to generate picking texture
@@ -48,239 +51,368 @@
end
# raw picking tests
+ @testset "pick(scene, point)" begin
+ @testset "scatter" begin
+ @test pick(scene, Point2f(20, 20)) == (sc1, 1)
+ @test pick(scene, Point2f(29, 59)) == (sc1, 3)
+ @test pick(scene, Point2f(57, 58)) == (nothing, 0) # maybe fragile
+ @test pick(scene, Point2f(57, 13)) == (sc2, 1) # maybe fragile
+ @test pick(scene, Point2f(20, 80)) == (nothing, 0)
+ @test pick(scene, Point2f(50, 80)) == (sc2, 4)
+ end
- @testset "scatter" begin
- @test pick(scene, Point2f(20, 20)) == (sc1, 1)
- @test pick(scene, Point2f(29, 59)) == (sc1, 3)
- @test pick(scene, Point2f(57, 58)) == (nothing, 0) # maybe fragile
- @test pick(scene, Point2f(57, 13)) == (sc2, 1) # maybe fragile
- @test pick(scene, Point2f(20, 80)) == (nothing, 0)
- @test pick(scene, Point2f(50, 80)) == (sc2, 4)
- end
+ @testset "meshscatter" begin
+ @test pick(scene, (20, 110)) == (ms, 1)
+ @test pick(scene, (44, 117)) == (ms, 3)
+ @test pick(scene, (57, 117)) == (nothing, 0)
+ end
- @testset "meshscatter" begin
- @test pick(scene, (20, 110)) == (ms, 1)
- @test pick(scene, (44, 117)) == (ms, 3)
- @test pick(scene, (57, 117)) == (nothing, 0)
- end
+ @testset "lines" begin
+ # Bit less precise since joints aren't strictly one segment or the other
+ @test pick(scene, 22, 140) == (l1, 2)
+ @test pick(scene, 48, 140) == (l1, 2)
+ @test pick(scene, 50, 142) == (l1, 3)
+ @test pick(scene, 50, 168) == (l1, 3)
+ @test pick(scene, 48, 170) == (l1, 4)
+ @test pick(scene, 22, 170) == (l1, 4)
+ @test pick(scene, 20, 168) == (l1, 5)
+ @test pick(scene, 20, 142) == (l1, 5)
- @testset "lines" begin
- # Bit less precise since joints aren't strictly one segment or the other
- @test pick(scene, 22, 140) == (l1, 2)
- @test pick(scene, 48, 140) == (l1, 2)
- @test pick(scene, 50, 142) == (l1, 3)
- @test pick(scene, 50, 168) == (l1, 3)
- @test pick(scene, 48, 170) == (l1, 4)
- @test pick(scene, 22, 170) == (l1, 4)
- @test pick(scene, 20, 168) == (l1, 5)
- @test pick(scene, 20, 142) == (l1, 5)
-
- # more precise checks around borders (these maybe off by a pixel due to AA)
- @test pick(scene, 20, 200) == (l2, 2)
- @test pick(scene, 30, 209) == (l2, 2)
- @test pick(scene, 30, 211) == (nothing, 0)
- @test pick(scene, 59, 200) == (l2, 2)
- @test pick(scene, 61, 200) == (nothing, 0)
- @test pick(scene, 57, 206) == (l2, 2)
- @test pick(scene, 57, 208) == (nothing, 0)
- @test pick(scene, 40, 230) == (l2, 5) # nan handling
- end
+ # more precise checks around borders (these maybe off by a pixel due to AA)
+ @test pick(scene, 20, 200) == (l2, 2)
+ @test pick(scene, 30, 209) == (l2, 2)
+ @test pick(scene, 30, 211) == (nothing, 0)
+ @test pick(scene, 59, 200) == (l2, 2)
+ @test pick(scene, 61, 200) == (nothing, 0)
+ @test pick(scene, 57, 206) == (l2, 2)
+ @test pick(scene, 57, 208) == (nothing, 0)
+ @test pick(scene, 40, 230) == (l2, 5) # nan handling
+ end
- @testset "linesegments" begin
- @test pick(scene, 8, 260) == (nothing, 0) # off by a pixel due to AA
- @test pick(scene, 10, 260) == (ls, 2)
- @test pick(scene, 30, 269) == (ls, 2)
- @test pick(scene, 30, 271) == (nothing, 0)
- @test pick(scene, 59, 260) == (ls, 2)
- @test pick(scene, 61, 260) == (nothing, 0)
-
- @test pick(scene, 8, 290) == (nothing, 0) # off by a pixel due to AA
- @test pick(scene, 10, 290) == (ls, 6)
- @test pick(scene, 30, 280) == (ls, 6)
- @test pick(scene, 30, 278) == (nothing, 0) # off by a pixel due to AA
- @test pick(scene, 59, 290) == (ls, 6)
- @test pick(scene, 61, 290) == (nothing, 0)
- end
+ @testset "linesegments" begin
+ @test pick(scene, 8, 260) == (nothing, 0) # off by a pixel due to AA
+ @test pick(scene, 10, 260) == (ls, 2)
+ @test pick(scene, 30, 269) == (ls, 2)
+ @test pick(scene, 30, 271) == (nothing, 0)
+ @test pick(scene, 59, 260) == (ls, 2)
+ @test pick(scene, 61, 260) == (nothing, 0)
- @testset "text" begin
- @test pick(scene, 15, 320) == (t, 1)
- @test pick(scene, 13, 320) == (nothing, 0)
- # edge checks, further outside due to AA
- @test pick(scene, 20, 306) == (nothing, 0)
- @test pick(scene, 20, 320) == (t, 1)
- @test pick(scene, 20, 333) == (nothing, 0)
- # space is counted
- @test pick(scene, 43, 320) == (t, 3)
- @test pick(scene, 48, 324) == (t, 3)
- @test pick(scene, 49, 326) == (nothing, 0)
- # characters at nan position are counted
- @test pick(scene, 20, 350) == (t, 6)
- end
+ @test pick(scene, 8, 290) == (nothing, 0) # off by a pixel due to AA
+ @test pick(scene, 10, 290) == (ls, 6)
+ @test pick(scene, 30, 280) == (ls, 6)
+ @test pick(scene, 30, 278) == (nothing, 0) # off by a pixel due to AA
+ @test pick(scene, 59, 290) == (ls, 6)
+ @test pick(scene, 61, 290) == (nothing, 0)
+ end
- @testset "image" begin
- # outside border
- for p in vcat(
- [(x, y) for x in (79, 141) for y in (21, 49)],
- [(x, y) for x in (81, 139) for y in (19, 51)]
- )
- @test pick(scene, p) == (nothing, 0)
+ @testset "text" begin
+ @test pick(scene, 15, 320) == (t, 1)
+ @test pick(scene, 13, 320) == (nothing, 0)
+ # edge checks, further outside due to AA
+ @test pick(scene, 20, 306) == (nothing, 0)
+ @test pick(scene, 20, 320) == (t, 1)
+ @test pick(scene, 20, 333) == (nothing, 0)
+ # space is counted
+ @test pick(scene, 43, 320) == (t, 3)
+ @test pick(scene, 48, 324) == (t, 3)
+ @test pick(scene, 49, 326) == (nothing, 0)
+ # characters at nan position are counted
+ @test pick(scene, 20, 350) == (t, 6)
end
-
- # cell centered checks
- @test pick(scene, 90, 30) == (i, 1)
- @test pick(scene, 110, 30) == (i, 2)
- @test pick(scene, 130, 30) == (i, 3)
- @test pick(scene, 90, 40) == (i, 4)
- @test pick(scene, 110, 40) == (i, 5)
- @test pick(scene, 130, 40) == (i, 6)
-
- # precise check (around cell intersection)
- @test pick(scene, 100-1, 35-1) == (i, 1)
- @test pick(scene, 100+1, 35-1) == (i, 2)
- @test pick(scene, 100-1, 35+1) == (i, 4)
- @test pick(scene, 100+1, 35+1) == (i, 5)
-
- @test pick(scene, 120-1, 35-1) == (i, 2)
- @test pick(scene, 120+1, 35-1) == (i, 3)
- @test pick(scene, 120-1, 35+1) == (i, 5)
- @test pick(scene, 120+1, 35+1) == (i, 6)
-
- # reversed axis check
- @test pick(scene, 200, 30) == (i2, 1)
- @test pick(scene, 190, 30) == (i2, 2)
- @test pick(scene, 200, 40) == (i2, 3)
- @test pick(scene, 190, 40) == (i2, 4)
- end
- @testset "surface" begin
- # outside border
- for p in vcat(
- [(x, y) for x in (79, 141) for y in (81, 109)],
- [(x, y) for x in (81, 139) for y in (79, 111)]
- )
- @test pick(scene, p) == (nothing, 0)
+ @testset "image" begin
+ # outside border
+ for p in vcat(
+ [(x, y) for x in (79, 141) for y in (21, 49)],
+ [(x, y) for x in (81, 139) for y in (19, 51)]
+ )
+ @test pick(scene, p) == (nothing, 0)
+ end
+
+ # cell centered checks
+ @test pick(scene, 90, 30) == (i, 1)
+ @test pick(scene, 110, 30) == (i, 2)
+ @test pick(scene, 130, 30) == (i, 3)
+ @test pick(scene, 90, 40) == (i, 4)
+ @test pick(scene, 110, 40) == (i, 5)
+ @test pick(scene, 130, 40) == (i, 6)
+
+ # precise check (around cell intersection)
+ @test pick(scene, 100-1, 35-1) == (i, 1)
+ @test pick(scene, 100+1, 35-1) == (i, 2)
+ @test pick(scene, 100-1, 35+1) == (i, 4)
+ @test pick(scene, 100+1, 35+1) == (i, 5)
+
+ @test pick(scene, 120-1, 35-1) == (i, 2)
+ @test pick(scene, 120+1, 35-1) == (i, 3)
+ @test pick(scene, 120-1, 35+1) == (i, 5)
+ @test pick(scene, 120+1, 35+1) == (i, 6)
+
+ # reversed axis check
+ @test pick(scene, 200, 30) == (i2, 1)
+ @test pick(scene, 190, 30) == (i2, 2)
+ @test pick(scene, 200, 40) == (i2, 3)
+ @test pick(scene, 190, 40) == (i2, 4)
+ end
+
+ @testset "surface" begin
+ # outside border
+ for p in vcat(
+ [(x, y) for x in (79, 141) for y in (81, 109)],
+ [(x, y) for x in (81, 139) for y in (79, 111)]
+ )
+ @test pick(scene, p) == (nothing, 0)
+ end
+
+ # cell centered checks
+ @test pick(scene, 90, 90) == (s, 1)
+ @test pick(scene, 110, 90) == (s, 2)
+ @test pick(scene, 130, 90) == (s, 3)
+ @test pick(scene, 90, 100) == (s, 4)
+ @test pick(scene, 110, 100) == (s, 5)
+ @test pick(scene, 130, 100) == (s, 6)
+
+ # precise check (around cell intersection)
+ @test pick(scene, 95-1, 95-1) == (s, 1)
+ @test pick(scene, 95+1, 95-1) == (s, 2)
+ @test pick(scene, 95-1, 95+1) == (s, 4)
+ @test pick(scene, 95+1, 95+1) == (s, 5)
+
+ @test pick(scene, 125-1, 95-1) == (s, 2)
+ @test pick(scene, 125+1, 95-1) == (s, 3)
+ @test pick(scene, 125-1, 95+1) == (s, 5)
+ @test pick(scene, 125+1, 95+1) == (s, 6)
+
+ # reversed axis check
+ @test pick(scene, 200, 90) == (s2, 1)
+ @test pick(scene, 190, 90) == (s2, 2)
+ @test pick(scene, 200, 100) == (s2, 3)
+ @test pick(scene, 190, 100) == (s2, 4)
+ end
+
+ @testset "heatmap" begin
+ # outside border
+ for p in vcat(
+ [(x, y) for x in (64, 156) for y in (126, 184)],
+ [(x, y) for x in (66, 154) for y in (124, 186)]
+ )
+ @test pick(scene, p) == (nothing, 0)
+ end
+
+ # cell centered checks
+ @test pick(scene, 80, 140) == (hm, 1)
+ @test pick(scene, 110, 140) == (hm, 2)
+ @test pick(scene, 140, 140) == (hm, 3)
+ @test pick(scene, 80, 170) == (hm, 4)
+ @test pick(scene, 110, 170) == (hm, 5)
+ @test pick(scene, 140, 170) == (hm, 6)
+
+ # precise check (around cell intersection)
+ @test pick(scene, 94, 154) == (hm, 1)
+ @test pick(scene, 96, 154) == (hm, 2)
+ @test pick(scene, 94, 156) == (hm, 4)
+ @test pick(scene, 96, 156) == (hm, 5)
+
+ @test pick(scene, 124, 154) == (hm, 2)
+ @test pick(scene, 126, 154) == (hm, 3)
+ @test pick(scene, 124, 156) == (hm, 5)
+ @test pick(scene, 126, 156) == (hm, 6)
+
+ # reversed axis check
+ @test pick(scene, 210, 140) == (hm2, 1)
+ @test pick(scene, 180, 140) == (hm2, 2)
+ @test pick(scene, 210, 170) == (hm2, 3)
+ @test pick(scene, 180, 170) == (hm2, 4)
+ end
+
+ @testset "mesh" begin
+ @test pick(scene, 80, 200)[1] == m
+ @test pick(scene, 79, 200) == (nothing, 0)
+ @test pick(scene, 80, 199) == (nothing, 0)
+ @test pick(scene, 81, 201) == (m, 3)
+ @test pick(scene, 81, 225) == (m, 3)
+ @test pick(scene, 105, 201) == (m, 3)
+ @test pick(scene, 85, 229) == (m, 4)
+ @test pick(scene, 109, 205) == (m, 4)
+ @test pick(scene, 109, 229) == (m, 4)
+ @test pick(scene, 109, 229)[1] == m
+ @test pick(scene, 111, 230) == (nothing, 0)
+ @test pick(scene, 110, 231) == (nothing, 0)
+ end
+
+ @testset "voxel" begin
+ # outside border
+ for p in vcat(
+ [(x, y) for x in (64, 156) for y in (246, 304)],
+ [(x, y) for x in (66, 154) for y in (244, 306)]
+ )
+ @test pick(scene, p) == (nothing, 0)
+ end
+
+ # cell centered checks
+ @test pick(scene, 80, 260) == (vx, 1)
+ @test pick(scene, 110, 260) == (vx, 2)
+ @test pick(scene, 140, 260) == (vx, 3)
+ @test pick(scene, 80, 290) == (vx, 4)
+ @test pick(scene, 110, 290) == (vx, 5)
+ @test pick(scene, 140, 290) == (vx, 6)
+
+ # precise check (around cell intersection)
+ @test pick(scene, 94, 274) == (vx, 1)
+ @test pick(scene, 96, 274) == (vx, 2)
+ @test pick(scene, 94, 276) == (vx, 4)
+ @test pick(scene, 96, 276) == (vx, 5)
+
+ @test pick(scene, 124, 274) == (vx, 2)
+ @test pick(scene, 126, 274) == (vx, 3)
+ @test pick(scene, 124, 276) == (vx, 5)
+ @test pick(scene, 126, 276) == (vx, 6)
end
- # cell centered checks
- @test pick(scene, 90, 90) == (s, 1)
- @test pick(scene, 110, 90) == (s, 2)
- @test pick(scene, 130, 90) == (s, 3)
- @test pick(scene, 90, 100) == (s, 4)
- @test pick(scene, 110, 100) == (s, 5)
- @test pick(scene, 130, 100) == (s, 6)
-
- # precise check (around cell intersection)
- @test pick(scene, 95-1, 95-1) == (s, 1)
- @test pick(scene, 95+1, 95-1) == (s, 2)
- @test pick(scene, 95-1, 95+1) == (s, 4)
- @test pick(scene, 95+1, 95+1) == (s, 5)
-
- @test pick(scene, 125-1, 95-1) == (s, 2)
- @test pick(scene, 125+1, 95-1) == (s, 3)
- @test pick(scene, 125-1, 95+1) == (s, 5)
- @test pick(scene, 125+1, 95+1) == (s, 6)
-
- # reversed axis check
- @test pick(scene, 200, 90) == (s2, 1)
- @test pick(scene, 190, 90) == (s2, 2)
- @test pick(scene, 200, 100) == (s2, 3)
- @test pick(scene, 190, 100) == (s2, 4)
+ @testset "volume" begin
+ # volume doesn't produce indices because we can't resolve the depth of
+ # the pick
+ @test pick(scene, 80, 320)[1] == vol
+ @test pick(scene, 79, 320) == (nothing, 0)
+ @test pick(scene, 80, 319) == (nothing, 0)
+ @test pick(scene, 81, 321) == (vol, 0)
+ @test pick(scene, 81, 349) == (vol, 0)
+ @test pick(scene, 109, 321) == (vol, 0)
+ @test pick(scene, 109, 349) == (vol, 0)
+ @test pick(scene, 109, 349)[1] == vol
+ @test pick(scene, 111, 350) == (nothing, 0)
+ @test pick(scene, 110, 351) == (nothing, 0)
+ end
end
- @testset "heatmap" begin
- # outside border
- for p in vcat(
- [(x, y) for x in (64, 156) for y in (126, 184)],
- [(x, y) for x in (66, 154) for y in (124, 186)]
- )
- @test pick(scene, p) == (nothing, 0)
+
+ @testset "ranged pick/pick_sorted" begin
+ @testset "scatter" begin
+ @test pick(scene, Point2f(40, 60), 10) == (sc2, 2)
+ end
+ @testset "meshscatter" begin
+ @test pick(scene, (35, 117), 10) == (ms, 3)
+ end
+ @testset "lines" begin
+ @test pick(scene, 10, 160, 10) == (l1, 5)
+ @test pick(scene, 40, 218, 10) == (l2, 5)
+ end
+ @testset "linesegments" begin
+ @test pick(scene, 5, 280, 10) == (ls, 6)
+ end
+ @testset "text" begin
+ @test pick(scene, 32, 320, 10) == (t, 1)
+ @test pick(scene, 35, 320, 10) == (t, 3)
+ end
+ @testset "image" begin
+ @test pick(scene, 98, 15, 10) == (i, 1)
+ @test pick(scene, 102, 15, 10) == (i, 2)
+ @test pick(scene, 200, 15, 10) == (i2, 1)
+ @test pick(scene, 190, 15, 10) == (i2, 2)
+ end
+ @testset "surface" begin
+ @test pick(scene, 93, 75, 10) == (s, 1)
+ @test pick(scene, 97, 75, 10) == (s, 2)
+ @test pick(scene, 200, 75, 10) == (s2, 1)
+ @test pick(scene, 190, 75, 10) == (s2, 2)
+ end
+ @testset "heatmap" begin
+ @test pick(scene, 93, 120, 10) == (hm, 1)
+ @test pick(scene, 97, 120, 10) == (hm, 2)
+ @test pick(scene, 200, 120, 10) == (hm2, 1)
+ @test pick(scene, 190, 120, 10) == (hm2, 2)
+ end
+ @testset "mesh" begin
+ @test pick(scene, 115, 230, 10) == (m, 4)
+ end
+ @testset "voxel" begin
+ @test pick(scene, 93, 240, 10) == (vx, 1)
+ @test pick(scene, 97, 240, 10) == (vx, 2)
+ end
+ @testset "volume" begin
+ @test pick(scene, 75, 320, 10) == (vol, 0)
+ end
+ @testset "range" begin
+ # mesh!(scene, Rect2f(200, 330, 10, 10))
+ # verify borders
+ @test pick(scene, 189, 331) == (nothing, 0)
+ @test pick(scene, 191, 329) == (nothing, 0)
+ @test pick(scene, 191, 331) == (m2, 4)
+ @test pick(scene, 199, 339) == (m2, 4)
+ @test pick(scene, 201, 339) == (nothing, 0)
+ @test pick(scene, 199, 341) == (nothing, 0)
+
+ @testset "horizontal" begin
+ @test pick(scene, 170, 335, 19) == (nothing, 0)
+ @test pick(scene, 170, 335, 21) == (m2, 3)
+ @test pick(scene, 220, 335, 19) == (nothing, 0)
+ @test pick(scene, 220, 335, 21) == (m2, 4)
+ end
+
+ @testset "vertical" begin
+ @test pick(scene, 205, 310, 19) == (nothing, 0)
+ @test pick(scene, 205, 310, 21) == (m2, 4)
+ @test pick(scene, 205, 360, 19) == (nothing, 0)
+ @test pick(scene, 205, 360, 22) == (m2, 4) # off by one?
+ end
+ @testset "diagonals" begin
+ # 190, 330
+ @test pick(scene, 180, 320, 14) == (nothing, 0)
+ @test pick(scene, 180, 320, 15) == (m2, 4)
+ @test pick(scene, 180, 350, 14) == (nothing, 0)
+ @test pick(scene, 180, 350, 15) == (m2, 3)
+ @test pick(scene, 210, 320, 14) == (nothing, 0)
+ @test pick(scene, 210, 320, 15) == (m2, 4)
+ @test pick(scene, 210, 350, 14) == (nothing, 0)
+ @test pick(scene, 210, 350, 16) == (m2, 4) # off by one?
+ end
end
-
- # cell centered checks
- @test pick(scene, 80, 140) == (hm, 1)
- @test pick(scene, 110, 140) == (hm, 2)
- @test pick(scene, 140, 140) == (hm, 3)
- @test pick(scene, 80, 170) == (hm, 4)
- @test pick(scene, 110, 170) == (hm, 5)
- @test pick(scene, 140, 170) == (hm, 6)
-
- # precise check (around cell intersection)
- @test pick(scene, 94, 154) == (hm, 1)
- @test pick(scene, 96, 154) == (hm, 2)
- @test pick(scene, 94, 156) == (hm, 4)
- @test pick(scene, 96, 156) == (hm, 5)
-
- @test pick(scene, 124, 154) == (hm, 2)
- @test pick(scene, 126, 154) == (hm, 3)
- @test pick(scene, 124, 156) == (hm, 5)
- @test pick(scene, 126, 156) == (hm, 6)
-
- # reversed axis check
- @test pick(scene, 210, 140) == (hm2, 1)
- @test pick(scene, 180, 140) == (hm2, 2)
- @test pick(scene, 210, 170) == (hm2, 3)
- @test pick(scene, 180, 170) == (hm2, 4)
end
- @testset "mesh" begin
- @test pick(scene, 80, 200)[1] == m
- @test pick(scene, 79, 200) == (nothing, 0)
- @test pick(scene, 80, 199) == (nothing, 0)
- @test pick(scene, 81, 201) == (m, 3)
- @test pick(scene, 81, 225) == (m, 3)
- @test pick(scene, 105, 201) == (m, 3)
- @test pick(scene, 85, 229) == (m, 4)
- @test pick(scene, 109, 205) == (m, 4)
- @test pick(scene, 109, 229) == (m, 4)
- @test pick(scene, 109, 229)[1] == m
- @test pick(scene, 111, 230) == (nothing, 0)
- @test pick(scene, 110, 231) == (nothing, 0)
+ # pick_sorted
+ @testset "pick_sorted" begin
+ list = Makie.pick_sorted(scene, Vec2(100, 100), 50)
+ @test length(list) == 14
+ @test list[1] == (s, 5)
+ @test list[2] == (s, 2)
+ @test list[3] == (s, 4)
+ @test list[4] == (s, 1)
+ @test list[5] == (s, 6)
+ @test list[6] == (hm, 2)
+ @test list[7] == (s, 3)
+ @test list[8] == (hm, 1)
+ @test list[9] == (hm, 3)
+ @test list[10] == (ms, 3)
+ @test list[11] == (sc2, 4)
+ @test list[12] == (l1, 3)
+ @test list[13] == (l1, 2)
+ @test list[14] == (sc2, 2)
end
- @testset "voxel" begin
- # outside border
- for p in vcat(
- [(x, y) for x in (64, 246) for y in (126, 184)],
- [(x, y) for x in (66, 244) for y in (124, 186)]
- )
- @test pick(scene, p) == (nothing, 0)
+ #=
+ For Verfication
+ Note that the text only marks the index in the picking list. The position
+ that is closest (that pick_sorted used) is somewhere else in the marked
+ element. Check scene2 to see the pickable regions if unsure
+
+ list = Makie.pick_sorted(scene, Vec2(100, 100), 50)
+ ps = Point2f[]
+ for (p, idx) in list
+ if p isa Union{Surface, Heatmap}
+ data = Point2f.(p.converted[1][], collect(p.converted[2][])')
+ push!(ps, data[idx])
+ else
+ push!(ps, p.converted[1][][idx])
end
-
- # cell centered checks
- @test pick(scene, 80, 260) == (vx, 1)
- @test pick(scene, 110, 260) == (vx, 2)
- @test pick(scene, 140, 260) == (vx, 3)
- @test pick(scene, 80, 290) == (vx, 4)
- @test pick(scene, 110, 290) == (vx, 5)
- @test pick(scene, 140, 290) == (vx, 6)
-
- # precise check (around cell intersection)
- @test pick(scene, 94, 274) == (vx, 1)
- @test pick(scene, 96, 274) == (vx, 2)
- @test pick(scene, 94, 276) == (vx, 4)
- @test pick(scene, 96, 276) == (vx, 5)
-
- @test pick(scene, 124, 274) == (vx, 2)
- @test pick(scene, 126, 274) == (vx, 3)
- @test pick(scene, 124, 276) == (vx, 5)
- @test pick(scene, 126, 276) == (vx, 6)
- end
-
- @testset "volume" begin
- # volume doesn't produce indices because we can't resolve the depth of
- # the pick
- @test pick(scene, 80, 320)[1] == vol
- @test pick(scene, 79, 320) == (nothing, 0)
- @test pick(scene, 80, 319) == (nothing, 0)
- @test pick(scene, 81, 321) == (vol, 0)
- @test pick(scene, 81, 349) == (vol, 0)
- @test pick(scene, 109, 321) == (vol, 0)
- @test pick(scene, 109, 349) == (vol, 0)
- @test pick(scene, 109, 349)[1] == vol
- @test pick(scene, 111, 350) == (nothing, 0)
- @test pick(scene, 110, 351) == (nothing, 0)
end
+ scatter!(scene, Vec2f(100, 100), color = :white, strokecolor = :black, strokewidth = 2, overdraw = true)
+ text!(
+ scene, ps, text = ["$i" for i in 1:14],
+ strokecolor = :white, strokewidth = 2,
+ align = (:center, :center), overdraw = true)
+ =#
+ # pick(scene, Rect)
# grab all indices and generate a plot for them (w/ fixed px_per_unit)
full_screen = last.(pick(scene, scene.viewport[]))
diff --git a/WGLMakie/src/picking.jl b/WGLMakie/src/picking.jl
index 74c06e227f6..93bf7a56d9d 100644
--- a/WGLMakie/src/picking.jl
+++ b/WGLMakie/src/picking.jl
@@ -45,12 +45,11 @@ function Makie.pick_closest(scene::Scene, screen::Screen, xy, range::Integer)
lookup = plot_lookup(scene)
!haskey(lookup, selection[1]) && return (nothing, 0)
plt = lookup[selection[1]]
- return (plt, selection[2] + !(plt isa Volume))
+ return (plt, Int(selection[2]) + !(plt isa Volume))
end
# Skips some allocations
function Makie.pick_sorted(scene::Scene, screen::Screen, xy, range)
-
xy_vec = Cint[round.(Cint, xy)...]
range = round(Int, range)
session = get_screen_session(screen)
@@ -61,9 +60,10 @@ function Makie.pick_sorted(scene::Scene, screen::Screen, xy, range)
""")
isnothing(selection) && return Tuple{Plot,Int}[]
lookup = plot_lookup(scene)
- return map(filter((id, idx) -> haskey(lookup, id), selection)) do (id, idx)
+ filter!(((id, idx),) -> haskey(lookup, id), selection)
+ return map(selection) do (id, idx)
plt = lookup[id]
- return (plt, index + !(plt isa Volume))
+ return (plt, Int(idx) + !(plt isa Volume))
end
end
diff --git a/WGLMakie/src/wglmakie.bundled.js b/WGLMakie/src/wglmakie.bundled.js
index 34a6cd90b0b..a2e349a9981 100644
--- a/WGLMakie/src/wglmakie.bundled.js
+++ b/WGLMakie/src/wglmakie.bundled.js
@@ -23280,7 +23280,7 @@ function pick_closest(scene, xy, range) {
const dy = y1 - y0;
const [plot_data, _] = pick_native(scene, x0, y0, dx, dy, false);
const plot_matrix = plot_data.data;
- let min_dist = 1e30;
+ let min_dist = px_per_unit * px_per_unit * range * range;
let selection = [
null,
0
diff --git a/WGLMakie/src/wglmakie.js b/WGLMakie/src/wglmakie.js
index 215c8c3b1f2..0cf289a459f 100644
--- a/WGLMakie/src/wglmakie.js
+++ b/WGLMakie/src/wglmakie.js
@@ -607,7 +607,7 @@ export function pick_closest(scene, xy, range) {
const dy = y1 - y0;
const [plot_data, _] = pick_native(scene, x0, y0, dx, dy, false);
const plot_matrix = plot_data.data;
- let min_dist = 1e30;
+ let min_dist = px_per_unit * px_per_unit * range * range;
let selection = [null, 0];
const x = xy[0] * px_per_unit + 1 - x0;
const y = xy[1] * px_per_unit + 1 - y0;
diff --git a/src/interaction/inspector.jl b/src/interaction/inspector.jl
index b138d858e19..ba8a6901728 100644
--- a/src/interaction/inspector.jl
+++ b/src/interaction/inspector.jl
@@ -655,7 +655,7 @@ function show_imagelike(inspector, plot, name, idx, edge_based)
return true
end
- if plot.interpolate[]
+ if plot.interpolate[] || isnothing(idx)
i, j, z = _interpolated_getindex(xrange, yrange, zrange, pos)
x, y = pos
else
@@ -712,7 +712,7 @@ function show_imagelike(inspector, plot, name, idx, edge_based)
notify(p[1])
end
else
- bbox = _pixelated_image_bbox(xrange, yrange, zrange, i, j, edge_based)
+ bbox = _pixelated_image_bbox(xrange, yrange, zrange, round(Int, i), round(Int, j), edge_based)
if inspector.selection != plot || (length(inspector.temp_plots) != 1) ||
!(inspector.temp_plots[1] isa Wireframe)
clear_temporary_plots!(inspector, plot)
diff --git a/src/interaction/interactive_api.jl b/src/interaction/interactive_api.jl
index 64753f08321..b3fdff252e4 100644
--- a/src/interaction/interactive_api.jl
+++ b/src/interaction/interactive_api.jl
@@ -108,7 +108,7 @@ function pick_closest(scene::SceneLike, screen, xy, range)
x, y = xy .+ 1 .- Vec2f(x0, y0)
for i in 1:dx, j in 1:dy
d = (x-i)^2 + (y-j)^2
- if (d < min_dist) && (picks[i, j][1] != nothing)
+ if (d < min_dist) && (picks[i, j][1] !== nothing)
min_dist = d
selected = (i, j)
end
From f0b3a86a6e599862c53fb0a087b6e4579075bc78 Mon Sep 17 00:00:00 2001
From: Frederic Freyer
Date: Fri, 25 Oct 2024 18:45:18 +0200
Subject: [PATCH 17/80] Fix surface update & add test (#4529)
* fix surface update & add test
* update changelog
* fix step syntax
---------
Co-authored-by: Simon
---
CHANGELOG.md | 1 +
GLMakie/src/drawing_primitives.jl | 1 +
ReferenceTests/src/tests/updating.jl | 26 +++++++++++++++++++++++++-
WGLMakie/src/imagelike.jl | 2 +-
4 files changed, 28 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 41db9c09615..085af96762e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@
- Use polys for axis3 [#4463](https://github.com/MakieOrg/Makie.jl/pull/4463).
- Changed default for `circular_rotation` in Camera3D to false, so that the camera doesn't change rotation direction anymore [4492](https://github.com/MakieOrg/Makie.jl/pull/4492)
- Fixed `pick(scene, rect2)` in WGLMakie [#4488](https://github.com/MakieOrg/Makie.jl/pull/4488)
+- Fixed resizing of `surface` data not working correctly. (I.e. drawing out-of-bounds data or only drawing part of the data.) [#4529](https://github.com/MakieOrg/Makie.jl/pull/4529)
## [0.21.14] - 2024-10-11
diff --git a/GLMakie/src/drawing_primitives.jl b/GLMakie/src/drawing_primitives.jl
index f364b2e3468..60301cbe3a3 100644
--- a/GLMakie/src/drawing_primitives.jl
+++ b/GLMakie/src/drawing_primitives.jl
@@ -814,6 +814,7 @@ function draw_atomic(screen::Screen, scene::Scene, plot::Surface)
gl_attributes[:image] = Texture(img; minfilter=interp)
@assert to_value(plot[3]) isa AbstractMatrix
+ gl_attributes[:instances] = map(z -> (size(z,1)-1) * (size(z,2)-1), plot[3])
types = map(v -> typeof(to_value(v)), plot[1:2])
if all(T -> T <: Union{AbstractMatrix, AbstractVector}, types)
diff --git a/ReferenceTests/src/tests/updating.jl b/ReferenceTests/src/tests/updating.jl
index bf177009373..b80a3b2ef12 100644
--- a/ReferenceTests/src/tests/updating.jl
+++ b/ReferenceTests/src/tests/updating.jl
@@ -174,7 +174,6 @@ end
Makie.step!(st)
end
-
@reference_test "event ticks in record" begin
# Checks whether record calculates and triggers event.tick by drawing a
# Point at y = 1 for each frame where it does. The animation is irrelevant
@@ -191,4 +190,29 @@ end
f.scene.events.tick[] = Makie.Tick(Makie.UnknownTickState, 0, 0.0, 0.0)
end
f
+end
+
+@reference_test "updating surface size" begin
+ X = Observable(-5:5)
+ Y = Observable(-5:5)
+ Z = Observable([0.01 * x*x * y*y for x in X.val, y in Y.val])
+
+ f = Figure(size = (800, 400))
+ surface(f[1, 1], X, Y, Z)
+ surface(f[1, 2], map(collect, X), map(collect, Y), Z)
+ surface(f[1, 3],
+ map((X, Y) -> [x for x in X, y in Y], X, Y),
+ map((X, Y) -> [y for x in X, y in Y], X, Y), Z)
+ st = Stepper(f)
+ Makie.step!(st)
+
+ X.val = -5:0
+ Z.val = Z.val[1:6, :]
+ notify(Z)
+ Makie.step!(st)
+
+ X.val = -5:5
+ Z.val = [0.01 * x*x * y*y for x in X.val, y in Y.val]
+ notify(Z)
+ Makie.step!(st)
end
\ No newline at end of file
diff --git a/WGLMakie/src/imagelike.jl b/WGLMakie/src/imagelike.jl
index 0406f3ddb8f..b6c2651ed3c 100644
--- a/WGLMakie/src/imagelike.jl
+++ b/WGLMakie/src/imagelike.jl
@@ -18,7 +18,7 @@ function create_shader(mscene::Scene, plot::Surface)
positions = Buffer(ps)
rect = lift(z -> Tesselation(Rect2(0f0, 0f0, 1f0, 1f0), size(z)), plot, pz)
fs = lift(r -> decompose(QuadFace{Int}, r), plot, rect)
- fs = map((ps, fs) -> filter(f -> !any(i -> isnan(ps[i]), f), fs), plot, ps, fs)
+ fs = map((ps, fs) -> filter(f -> !any(i -> (i > length(ps)) || isnan(ps[i]), f), fs), plot, ps, fs)
faces = Buffer(fs)
# This adjusts uvs (compared to decompose_uv) so texture sampling starts at
# the center of a texture pixel rather than the edge, fixing
From 19c0283ba3c03ff15d3ae19fa6ce48e1867e35b5 Mon Sep 17 00:00:00 2001
From: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com>
Date: Sat, 26 Oct 2024 09:58:35 +0200
Subject: [PATCH 18/80] Prepare 0.21.15 (#4528)
* bump versions
* edit changelog
---------
Co-authored-by: Frederic Freyer
---
CHANGELOG.md | 9 ++++++---
CairoMakie/Project.toml | 4 ++--
GLMakie/Project.toml | 4 ++--
Project.toml | 2 +-
RPRMakie/Project.toml | 4 ++--
WGLMakie/Project.toml | 4 ++--
6 files changed, 15 insertions(+), 12 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 085af96762e..dd7bb35e2c9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,8 @@
## [Unreleased]
+## [0.21.15] - 2024-10-25
+
- Allowed creation of `Legend` with entries that have no legend elements [#4526](https://github.com/MakieOrg/Makie.jl/pull/4526).
- Improved CairoMakie's 2D mesh drawing performance by ~30% [#4132](https://github.com/MakieOrg/Makie.jl/pull/4132).
- Allow `width` to be set per box in `boxplot` [#4447](https://github.com/MakieOrg/Makie.jl/pull/4447).
@@ -9,8 +11,8 @@
if the width is exceeded [#4293](https://github.com/MakieOrg/Makie.jl/pull/4293)
- Changed image, heatmap and surface picking indices to correctly index the relevant matrix arguments. [#4459](https://github.com/MakieOrg/Makie.jl/pull/4459)
- Improved performance of `record` by avoiding unnecessary copying in common cases [#4475](https://github.com/MakieOrg/Makie.jl/pull/4475).
-- Fix usage of `AggMean()` and other aggregations operating on 3d data for `datashader` [#4346](https://github.com/MakieOrg/Makie.jl/pull/4346).
-- Use polys for axis3 [#4463](https://github.com/MakieOrg/Makie.jl/pull/4463).
+- Fixed usage of `AggMean()` and other aggregations operating on 3d data for `datashader` [#4346](https://github.com/MakieOrg/Makie.jl/pull/4346).
+- Fixed forced rasterization when rendering figures with `Axis3` to svg [#4463](https://github.com/MakieOrg/Makie.jl/pull/4463).
- Changed default for `circular_rotation` in Camera3D to false, so that the camera doesn't change rotation direction anymore [4492](https://github.com/MakieOrg/Makie.jl/pull/4492)
- Fixed `pick(scene, rect2)` in WGLMakie [#4488](https://github.com/MakieOrg/Makie.jl/pull/4488)
- Fixed resizing of `surface` data not working correctly. (I.e. drawing out-of-bounds data or only drawing part of the data.) [#4529](https://github.com/MakieOrg/Makie.jl/pull/4529)
@@ -640,7 +642,8 @@ All other changes are collected [in this PR](https://github.com/MakieOrg/Makie.j
- Fixed rendering of `heatmap`s with one or more reversed ranges in CairoMakie, as in `heatmap(1:10, 10:-1:1, rand(10, 10))` [#1100](https://github.com/MakieOrg/Makie.jl/pull/1100).
- Fixed volume slice recipe and added docs for it [#1123](https://github.com/MakieOrg/Makie.jl/pull/1123).
-[Unreleased]: https://github.com/MakieOrg/Makie.jl/compare/v0.21.14...HEAD
+[Unreleased]: https://github.com/MakieOrg/Makie.jl/compare/v0.21.15...HEAD
+[0.21.15]: https://github.com/MakieOrg/Makie.jl/compare/v0.21.14...v0.21.15
[0.21.14]: https://github.com/MakieOrg/Makie.jl/compare/v0.21.13...v0.21.14
[0.21.13]: https://github.com/MakieOrg/Makie.jl/compare/v0.21.12...v0.21.13
[0.21.12]: https://github.com/MakieOrg/Makie.jl/compare/v0.21.11...v0.21.12
diff --git a/CairoMakie/Project.toml b/CairoMakie/Project.toml
index e3dbf0c22ce..2f368c39073 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.12.14"
+version = "0.12.15"
[deps]
CRC32c = "8bf52ea8-c179-5cab-976a-9e18b702a9bc"
@@ -24,7 +24,7 @@ FileIO = "1.1"
FreeType = "3, 4.0"
GeometryBasics = "0.4.11"
LinearAlgebra = "1.0, 1.6"
-Makie = "=0.21.14"
+Makie = "=0.21.15"
PrecompileTools = "1.0"
julia = "1.3"
diff --git a/GLMakie/Project.toml b/GLMakie/Project.toml
index 9ade73d46f1..2a6fdb5c075 100644
--- a/GLMakie/Project.toml
+++ b/GLMakie/Project.toml
@@ -1,6 +1,6 @@
name = "GLMakie"
uuid = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
-version = "0.10.14"
+version = "0.10.15"
[deps]
ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
@@ -30,7 +30,7 @@ FreeTypeAbstraction = "0.10"
GLFW = "3.4.3"
GeometryBasics = "0.4.11"
LinearAlgebra = "1.0, 1.6"
-Makie = "=0.21.14"
+Makie = "=0.21.15"
Markdown = "1.0, 1.6"
MeshIO = "0.4"
ModernGL = "1"
diff --git a/Project.toml b/Project.toml
index ac3a5b2fa53..1f23f1941e9 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Makie"
uuid = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
authors = ["Simon Danisch", "Julius Krumbiegel"]
-version = "0.21.14"
+version = "0.21.15"
[deps]
Animations = "27a7e980-b3e6-11e9-2bcd-0b925532e340"
diff --git a/RPRMakie/Project.toml b/RPRMakie/Project.toml
index 550b0291ea8..7f3b985c80e 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.7.14"
+version = "0.7.15"
[deps]
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
@@ -17,7 +17,7 @@ Colors = "0.9, 0.10, 0.11, 0.12"
FileIO = "1.6"
GeometryBasics = "0.4.11"
LinearAlgebra = "1.0, 1.6"
-Makie = "=0.21.14"
+Makie = "=0.21.15"
Printf = "1.0, 1.6"
RadeonProRender = "0.3.0"
julia = "1.3"
diff --git a/WGLMakie/Project.toml b/WGLMakie/Project.toml
index 886599cc21e..bfcb818ef32 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.10.14"
+version = "0.10.15"
[deps]
Bonito = "824d6782-a2ef-11e9-3a09-e5662e0c26f8"
@@ -29,7 +29,7 @@ GeometryBasics = "0.4.11"
Hyperscript = "0.0.3, 0.0.4, 0.0.5"
LinearAlgebra = "1.0, 1.6"
LoggingExtras = "<1.1.0"
-Makie = "=0.21.14"
+Makie = "=0.21.15"
Observables = "0.5.1"
PNGFiles = "0.3, 0.4"
PrecompileTools = "1.0"
From ccf0572ab0b75ae088a8e7f55a8c718080125c3f Mon Sep 17 00:00:00 2001
From: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com>
Date: Sat, 26 Oct 2024 18:15:47 +0200
Subject: [PATCH 19/80] Make benchmark plots and post a gist with them (#4515)
---
.github/workflows/compilation-benchmark.yaml | 62 +++++-
metrics/ttfp/Project.toml | 9 +-
metrics/ttfp/run-benchmark.jl | 216 +++----------------
3 files changed, 90 insertions(+), 197 deletions(-)
diff --git a/.github/workflows/compilation-benchmark.yaml b/.github/workflows/compilation-benchmark.yaml
index 17cb146fed4..f12dfe01828 100644
--- a/.github/workflows/compilation-benchmark.yaml
+++ b/.github/workflows/compilation-benchmark.yaml
@@ -10,6 +10,7 @@ on:
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
+
jobs:
benchmark:
name: ${{ matrix.package }}
@@ -38,8 +39,65 @@ jobs:
PR_NUMBER: ${{ github.event.number }}
run: >
DISPLAY=:0 xvfb-run -s '-screen 0 1024x768x24' julia --project=./metrics/ttfp/ ./metrics/ttfp/run-benchmark.jl ${{ matrix.package }} 20 ${{ github.event.pull_request.base.ref }}
- - name: Upload measurements as artifact
+ - name: Upload plots as artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.package }}
- path: ./json
+ path: ./benchmark_results
+ post-gist:
+ name: Post Benchmark Gist
+ needs: benchmark # Wait for all benchmark jobs to complete
+ runs-on: ubuntu-20.04
+ permissions:
+ statuses: write # Permission to post workflow status
+ steps:
+ - name: Download artifacts
+ uses: actions/download-artifact@v4
+ with:
+ path: ./images
+ merge-multiple: true
+
+ - name: Create Gist with images
+ env:
+ GH_TOKEN: ${{ secrets.BENCHMARK_KEY }}
+ run: |
+ # Create a gist with the three images
+ gist_url=$(gh gist create ./images/CairoMakie.svg ./images/GLMakie.svg ./images/WGLMakie.svg | grep -Eo 'https://gist.github.com[/a-zA-Z0-9]+')
+ echo "Gist created: $gist_url"
+
+ # Save the gist URL for later steps
+ echo "GIST_URL=$gist_url" >> $GITHUB_ENV
+ echo "GIST_URL_USERCONTENT=$(echo $gist_url | sed 's|github|githubusercontent|')" >> $GITHUB_ENV
+
+ - name: Post workflow status with gist link
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ gist_url=$GIST_URL
+ gh api \
+ --method POST \
+ -H "Accept: application/vnd.github+json" \
+ -H "X-GitHub-Api-Version: 2022-11-28" \
+ /repos/${{ github.repository }}/statuses/${{ github.event.pull_request.head.sha }} \
+ -f "state=success" \
+ -f "context=Benchmark Results" \
+ -f "description=Plots are available under Details" \
+ -f "target_url=$gist_url"
+ - name: Post comment
+ uses: thollander/actions-comment-pull-request@v3
+ with:
+ github-token: ${{ secrets.BENCHMARK_KEY }}
+ comment-tag: benchmark # this allows to update the same post with new data
+ message: |
+ # Benchmark Results
+
+ SHA: [${{ github.event.pull_request.head.sha }}](https://github.com/${{ github.repository }}/commit/${{ github.event.pull_request.head.sha }})
+
+ > [!WARNING]
+ > These results are subject to substantial noise because GitHub's CI runs on shared machines that are not ideally suited for benchmarking.
+
+ ![GLMakie](${{ env.GIST_URL_USERCONTENT }}/raw/GLMakie.svg)
+ ![CairoMakie](${{ env.GIST_URL_USERCONTENT }}/raw/CairoMakie.svg)
+ ![WGLMakie](${{ env.GIST_URL_USERCONTENT }}/raw/WGLMakie.svg)
+
+
diff --git a/metrics/ttfp/Project.toml b/metrics/ttfp/Project.toml
index 22fc2f05a19..ef310db892d 100644
--- a/metrics/ttfp/Project.toml
+++ b/metrics/ttfp/Project.toml
@@ -1,7 +1,6 @@
[deps]
-BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
-GitHub = "bc5e4493-9b4d-5f90-b8aa-2b2bcaad7a26"
-HypothesisTests = "09f84164-cd44-5f33-b23f-e6b0d136a0d5"
+AlgebraOfGraphics = "cbdf2221-f076-402e-a563-3d30da359d67"
+CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
+DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
+JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
-Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
-Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
diff --git a/metrics/ttfp/run-benchmark.jl b/metrics/ttfp/run-benchmark.jl
index 84374eae260..7384031413b 100644
--- a/metrics/ttfp/run-benchmark.jl
+++ b/metrics/ttfp/run-benchmark.jl
@@ -9,8 +9,8 @@ Pkg.activate(@__DIR__)
Pkg.instantiate()
pkg"registry up"
Pkg.update()
-using Statistics, GitHub, Printf, BenchmarkTools, Markdown, HypothesisTests
-using BenchmarkTools.JSON
+
+using JSON, AlgebraOfGraphics, CairoMakie, DataFrames
Package = ARGS[1]
n_samples = length(ARGS) > 1 ? parse(Int, ARGS[2]) : 7
base_branch = length(ARGS) > 2 ? ARGS[3] : "master"
@@ -21,164 +21,6 @@ base_branch = length(ARGS) > 2 ? ARGS[3] : "master"
@info("Benchmarking $(Package) against $(base_branch) with $(n_samples)")
-COMMENT_TEMPLATE = """
-## Compile Times benchmark
-
-Note, that these numbers may fluctuate on the CI servers, so take them with a grain of salt.
-All benchmark results are based on the mean time and negative percent mean faster than the base branch.
-Note, that GLMakie + WGLMakie run on an emulated GPU, so the runtime benchmark is much slower.
-Results are from running:
-
-```julia
-using_time = @ctime using Backend
-# Compile time
-create_time = @ctime fig = scatter(1:4; color=1:4, colormap=:turbo, markersize=20, visible=true)
-display_time = @ctime Makie.colorbuffer(display(fig))
-# Runtime
-create_time = @benchmark fig = scatter(1:4; color=1:4, colormap=:turbo, markersize=20, visible=true)
-display_time = @benchmark Makie.colorbuffer(fig)
-```
-
-| | using | create | display | create | display |
-|--------------:|:----------|:---------|:---------|:---------|:---------|
-| GLMakie | -- | -- | -- | -- | -- |
-| $base_branch | -- | -- | -- | -- | -- |
-| evaluation | -- | -- | -- | -- | -- |
-| CairoMakie | -- | -- | -- | -- | -- |
-| $base_branch | -- | -- | -- | -- | -- |
-| evaluation | -- | -- | -- | -- | -- |
-| WGLMakie | -- | -- | -- | -- | -- |
-| $base_branch | -- | -- | -- | -- | -- |
-| evaluation | -- | -- | -- | -- | -- |
-"""
-
-function github_context()
- owner = "MakieOrg"
- return (
- owner = owner,
- repo = GitHub.Repo("$(owner)/Makie.jl"),
- auth = GitHub.authenticate(ENV["GITHUB_TOKEN"]),
- scratch_repo = GitHub.Repo("MakieOrg/scratch")
- )
-end
-
-function best_unit(m)
- if m < 1e3
- return 1, "ns"
- elseif m < 1e6
- return 1e3, "μs"
- elseif m < 1e9
- return 1e6, "ms"
- else
- return 1e9, "s"
- end
-end
-
-function cohen_d(x, y)
- nx = length(x); ny = length(y)
- ddof = nx + ny - 2
- poolsd = sqrt(((nx - 1) * var(x) + (ny - 1) * var(y)) / ddof)
- d = (mean(x) - mean(y)) / poolsd
-end
-
-function analyze(pr, master)
- f, unit = best_unit(pr[1])
- pr, master = Float64.(pr) ./ f, Float64.(master) ./ f
- tt = UnequalVarianceTTest(pr, master)
- d = cohen_d(pr, master)
- std_p = (std(pr) + std(master)) / 2
- m_pr = mean(pr)
- m_m = mean(master)
- mean_diff = m_pr - m_m
- speedup = m_m / m_pr
- p = pvalue(tt)
- mean_diff_str = string(round(mean_diff; digits=2), unit)
- percent_change = (speedup - 1) * 100
- result = if p < 0.05
- if abs(d) > 0.2
- indicator = abs(percent_change) < 5 ? ["faster ✓", "slower X"] : ["**faster**✅", "**slower**❌"]
- indicator[d < 0 ? 1 : 2]
- else
- "*invariant*"
- end
- else
- if abs(percent_change) < 5
- "*invariant*"
- else
- "*noisy*🤷♀️"
- end
- end
-
- return @sprintf("%.2fx %s, %s (%.2fd, %.2fp, %.2fstd)", speedup, result, mean_diff_str, d, p,
- std_p)
-end
-
-function summarize_stats(timings)
- f, unit = best_unit(timings[1])
- m = mean(timings) / f
- mini = minimum(timings) / f
- maxi = maximum(timings) / f
- s = std(timings) / f
- @sprintf("%.2f%s (%.2f, %.2f) %.2f+-", m, unit, mini, maxi, s)
-end
-
-function get_row_values(results_pr, results_m)
- master_row = []
- pr_row = []
- evaluation_row = []
- n = length(results_pr)
- for i in 1:n
- push!(pr_row, summarize_stats(results_pr[i]))
- push!(master_row, summarize_stats(results_m[i]))
- push!(evaluation_row, analyze(results_pr[i], results_m[i]))
- end
- return pr_row, master_row, evaluation_row
-end
-
-function update_comment(old_comment, package_name, (pr_bench, master_bench, evaluation))
- md = Markdown.parse(old_comment)
- rows = md.content[end].rows
- idx = findfirst(rows) do row
- cell = first(row)
- isempty(cell) && return false
- return first(cell) == package_name
- end
- if isnothing(idx)
- @warn("Could not find $package_name in $(md). Not updating benchmarks")
- return old_comment
- end
- for (i, value) in enumerate(pr_bench)
- rows[idx][i + 1] = [value]
- end
- for (i, value) in enumerate(master_bench)
- rows[idx + 1][i + 1] = [value]
- end
- for (i, value) in enumerate(evaluation)
- rows[idx + 2][i + 1] = [value]
- end
- open("benchmark.md", "w") do io
- return show(io, md)
- end
- return sprint(show, md)
-end
-
-function make_or_edit_comment(ctx, pr, package_name, benchmarks)
- prev_comments, _ = GitHub.comments(ctx.repo, pr; auth=ctx.auth)
- idx = findfirst(c-> c.user.login == "MakieBot", prev_comments)
- if isnothing(idx)
- comment = update_comment(COMMENT_TEMPLATE, package_name, benchmarks)
- println(comment)
- GitHub.create_comment(ctx.repo, pr; auth=ctx.auth, params=Dict("body"=>comment))
- else
- old_comment = prev_comments[idx].body
- comment = update_comment(old_comment, package_name, benchmarks)
- println(comment)
- GitHub.edit_comment(ctx.repo, prev_comments[idx], :pr; auth=ctx.auth, params=Dict("body" => comment))
- end
-end
-
-
-using Random
function run_benchmarks(projects; n=n_samples)
benchmark_file = joinpath(@__DIR__, "benchmark-ttfp.jl")
@@ -200,18 +42,6 @@ function make_project_folder(name)
return project
end
-function load_results(name)
- result = "$name-benchmark.json"
- return JSON.parse(read(result, String))
-end
-
-ctx = try
- github_context()
-catch e
- @warn "Not authorized" exception=e
- # bad credentials because PR isn't from a contributor
- exit()
-end
ENV["JULIA_PKG_PRECOMPILE_AUTO"] = 0
project1 = make_project_folder("current-pr")
@@ -237,23 +67,29 @@ projects = [project1, project2]
run_benchmarks(projects)
-results_pr = load_results(basename(project1))
-results_m = load_results(basename(project2))
-benchmark_rows = get_row_values(results_pr, results_m)
-
-pr_to_comment = get(ENV, "PR_NUMBER", nothing)
-
-if !isnothing(pr_to_comment)
- pr = GitHub.pull_request(ctx.repo, pr_to_comment)
- make_or_edit_comment(ctx, pr, Package, benchmark_rows)
-else
- @info("Not commenting, no PR found")
- println(update_comment(COMMENT_TEMPLATE, Package, benchmark_rows))
+json_files = map([project1, project2]) do p
+ "$(basename(p))-benchmark.json"
end
-mkdir("json")
-for p in [project1, project2]
- name = basename(p)
- file = "$name-benchmark.json"
- mv(file, joinpath("json", file))
-end
+colnames = ["using", "first create", "first display", "create", "display"]
+
+df = reduce(vcat, map(json_files) do filename
+ name = replace(filename, r"-benchmark.*" => "")
+ arrs = map(x -> map(identity, x), JSON.parsefile(filename))
+ df = DataFrame(colnames .=> arrs)
+ df.name .= name
+ df
+ end)
+
+plt = AlgebraOfGraphics.data(df) *
+ mapping(:name, colnames .=> (x -> x / 1e9) .=> "time (s)", color = :name, layout = dims(1) => renamer(colnames)) *
+ visual(RainClouds, orientation = :horizontal, markersize = 5, show_median = false, plot_boxplots = false) |>
+ draw(
+ scales(Color = (; legend = false)),
+ facet = (; linkxaxes = false),
+ axis = (; xticklabelrotation = pi/4, width = 200, height = 150),
+ figure = (; title = "$Package Benchmarks")
+ )
+
+mkpath("benchmark_results")
+save(joinpath("benchmark_results", "$Package.svg"), plt)
From e5ace3b80659472b6b9ca9111d157c07a771ed87 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Mon, 28 Oct 2024 00:35:57 +0100
Subject: [PATCH 20/80] CompatHelper: bump compat for Colors to 0.13 for
package WGLMakie, (keep existing compat) (#4533)
Co-authored-by: CompatHelper Julia
---
WGLMakie/Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/WGLMakie/Project.toml b/WGLMakie/Project.toml
index bfcb818ef32..3ac4cf07a6b 100644
--- a/WGLMakie/Project.toml
+++ b/WGLMakie/Project.toml
@@ -22,7 +22,7 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
[compat]
Bonito = "3.2.1"
-Colors = "0.11, 0.12"
+Colors = "0.11, 0.12, 0.13"
FileIO = "1.1"
FreeTypeAbstraction = "0.10"
GeometryBasics = "0.4.11"
From 1e055431e6557f013dffb498cd1c36a95b8f0ba0 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Mon, 28 Oct 2024 13:25:04 +0100
Subject: [PATCH 21/80] CompatHelper: bump compat for Colors to 0.13 for
package CairoMakie, (keep existing compat) (#4536)
Co-authored-by: CompatHelper Julia
---
CairoMakie/Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CairoMakie/Project.toml b/CairoMakie/Project.toml
index 2f368c39073..b0e5b8afd77 100644
--- a/CairoMakie/Project.toml
+++ b/CairoMakie/Project.toml
@@ -19,7 +19,7 @@ PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
CRC32c = "1.0, 1.6"
Cairo = "1.0.4"
Cairo_jll = "1.18.0"
-Colors = "0.10, 0.11, 0.12"
+Colors = "0.10, 0.11, 0.12, 0.13"
FileIO = "1.1"
FreeType = "3, 4.0"
GeometryBasics = "0.4.11"
From 042dc468a1054b184d034f55b86bda774e3b9348 Mon Sep 17 00:00:00 2001
From: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com>
Date: Tue, 29 Oct 2024 11:05:32 +0100
Subject: [PATCH 22/80] Fix the docs build folder artifact (#4541)
---
.github/workflows/Docs.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/Docs.yml b/.github/workflows/Docs.yml
index 6323a40ab13..5b404b016ac 100644
--- a/.github/workflows/Docs.yml
+++ b/.github/workflows/Docs.yml
@@ -50,4 +50,4 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: Docs build
- path: ./docs/__site
+ path: ./docs/build
From 13ac70cb00e408b3549da1715aaabcddbbebbe38 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Tue, 29 Oct 2024 13:07:22 +0100
Subject: [PATCH 23/80] CompatHelper: bump compat for Colors to 0.13 for
package RPRMakie, (keep existing compat) (#4539)
Co-authored-by: CompatHelper Julia
---
RPRMakie/Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/RPRMakie/Project.toml b/RPRMakie/Project.toml
index 7f3b985c80e..79976728995 100644
--- a/RPRMakie/Project.toml
+++ b/RPRMakie/Project.toml
@@ -13,7 +13,7 @@ Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
RadeonProRender = "27029320-176d-4a42-b57d-56729d2ad457"
[compat]
-Colors = "0.9, 0.10, 0.11, 0.12"
+Colors = "0.9, 0.10, 0.11, 0.12, 0.13"
FileIO = "1.6"
GeometryBasics = "0.4.11"
LinearAlgebra = "1.0, 1.6"
From fd41dd4f5ee2477a264163cedd1e72fcd30ff2f2 Mon Sep 17 00:00:00 2001
From: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com>
Date: Tue, 29 Oct 2024 15:02:04 +0100
Subject: [PATCH 24/80] Subscript/superscript combinations (#4489)
* Add right and left subsup rich text
* make text a little smaller again
* tweak positioning
* add to rich text test
* revert to old sizing
* add changelog
* add example to docs page
---
CHANGELOG.md | 2 +
.../src/tests/figures_and_makielayout.jl | 8 +-
docs/src/reference/plots/text.md | 6 +-
src/basic_recipes/text.jl | 140 ++++++++++++++----
4 files changed, 126 insertions(+), 30 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dd7bb35e2c9..b2b23539527 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,8 @@
## [Unreleased]
+- Added `subsup` and `left_subsup` functions that offer stacked sub- and superscripts for `rich` text which means this style can be used with arbitrary fonts and is not limited to fonts supported by MathTeXEngine.jl [#4489](https://github.com/MakieOrg/Makie.jl/pull/4489).
+
## [0.21.15] - 2024-10-25
- Allowed creation of `Legend` with entries that have no legend elements [#4526](https://github.com/MakieOrg/Makie.jl/pull/4526).
diff --git a/ReferenceTests/src/tests/figures_and_makielayout.jl b/ReferenceTests/src/tests/figures_and_makielayout.jl
index 42bbad2a222..6660fd7d2db 100644
--- a/ReferenceTests/src/tests/figures_and_makielayout.jl
+++ b/ReferenceTests/src/tests/figures_and_makielayout.jl
@@ -392,8 +392,12 @@ end
xlabel = rich("X", subscript("label", fontsize = 25)),
ylabel = rich("Y", superscript("label")),
)
- Label(f[1, 2], rich("Hi", rich("Hi", offset = (0.2, 0.2), color = :blue)), tellheight = false)
- Label(f[1, 3], rich("X", superscript("super"), subscript("sub")), tellheight = false)
+ gl = GridLayout(f[1, 2], tellheight = false)
+ Label(gl[1, 1], rich("Hi", rich("Hi", offset = (0.2, 0.2), color = :blue)))
+ Label(gl[2, 1], rich("X", superscript("super"), subscript("sub")))
+ Label(gl[3, 1], rich(left_subsup("92", "238"), "U"))
+ Label(gl[4, 1], rich("SO", subsup("4", "2−")))
+ Label(gl[5, 1], rich("x", subsup("f", "g")))
f
end
diff --git a/docs/src/reference/plots/text.md b/docs/src/reference/plots/text.md
index 1ee30b5a999..ec7e8145e20 100644
--- a/docs/src/reference/plots/text.md
+++ b/docs/src/reference/plots/text.md
@@ -195,9 +195,9 @@ f
## Rich text
With rich text, you can conveniently plot text whose parts have different colors or fonts, and you can position sections as subscripts and superscripts.
-You can create such rich text objects using the functions `rich`, `superscript` and `subscript`, all of which create `RichText` objects.
+You can create such rich text objects using the functions `rich`, `superscript`, `subscript`, `subsup` and `left_subsup`, all of which create `RichText` objects.
-Each of these functions takes a variable number of arguments, each of which can be a `String` or `RichText`.
+Each of these functions takes a variable number of arguments (except `subsup` and `left_subsup` which take exactly two arguments), each of which can be a `String` or `RichText`.
Each can also take keyword arguments such as `color` or `font`, to set these attributes for the given part.
The top-level settings for font, color, etc. are taken from the `text` attributes as usual.
@@ -221,6 +221,8 @@ end
Label(f[2, 1], rich(rainbow_chars...), font = :bold)
+Label(f[3, 1], rich("Chemists use notations like ", left_subsup("92", "238"), "U or PO", subsup("4", "3−")))
+
f
```
diff --git a/src/basic_recipes/text.jl b/src/basic_recipes/text.jl
index 7a6b77cc3dd..2c38861e438 100644
--- a/src/basic_recipes/text.jl
+++ b/src/basic_recipes/text.jl
@@ -307,11 +307,40 @@ function Base.show(io::IO, ::MIME"text/plain", r::RichText)
print(io, "RichText: \"$(String(r))\"")
end
+"""
+ rich(args...; kwargs...)
+
+Create a `RichText` object containing all elements in `args`.
+"""
rich(args...; kwargs...) = RichText(:span, args...; kwargs...)
+"""
+ subscript(args...; kwargs...)
+
+Create a `RichText` object representing a superscript containing all elements in `args`.
+"""
subscript(args...; kwargs...) = RichText(:sub, args...; kwargs...)
+"""
+ superscript(args...; kwargs...)
+
+Create a `RichText` object representing a superscript containing all elements in `args`.
+"""
superscript(args...; kwargs...) = RichText(:sup, args...; kwargs...)
+"""
+ subsup(subscript, superscript; kwargs...)
+
+Create a `RichText` object representing a right subscript/superscript combination,
+where both scripts are left-aligned against the preceding text.
+"""
+subsup(args...; kwargs...) = RichText(:subsup, args...; kwargs...)
+"""
+ left_subsup(subscript, superscript; kwargs...)
+
+Create a `RichText` object representing a left subscript/superscript combination,
+where both scripts are right-aligned against the following text.
+"""
+left_subsup(args...; kwargs...) = RichText(:leftsubsup, args...; kwargs...)
-export rich, subscript, superscript
+export rich, subscript, superscript, subsup, left_subsup
function _get_glyphcollection_and_linesegments(rt::RichText, index, ts, f, fset, al, rot, jus, lh, col, scol, swi, www, offs)
gc = layout_text(rt, ts, f, fset, al, rot, jus, lh, col)
@@ -381,11 +410,11 @@ function layout_text(rt::RichText, ts, f, fset, al, rot, jus, lh, col)
_f = to_font(fset, f)
- stack = [GlyphState(0, 0, Vec2f(ts), _f, to_color(col))]
-
lines = [GlyphInfo[]]
- process_rt_node!(stack, lines, rt, fset)
+ gs = GlyphState(0, 0, Vec2f(ts), _f, to_color(col))
+
+ process_rt_node!(lines, gs, rt, fset)
apply_lineheight!(lines, lh)
apply_alignment_and_justification!(lines, jus, al)
@@ -463,23 +492,64 @@ function float_justification(ju, al)::Float32
end
end
-function process_rt_node!(stack, lines, rt::RichText, fonts)
- _type(x) = nothing
- _type(r::RichText) = r.type
+function process_rt_node!(lines, gs::GlyphState, rt::RichText, fonts)
+ T = Val(rt.type)
+
+ if T === Val(:subsup) || T === Val(:leftsubsup)
+ if length(rt.children) != 2
+ throw(ArgumentError("Found subsup rich text with $(length(rt.children)) which has to have exactly 2 children instead. The children were: $(rt.children)"))
+ end
+ sub, sup = rt.children
+ sub_lines = Vector{GlyphInfo}[[]]
+ new_gs_sub = new_glyphstate(gs, rt, Val(:subsup_sub), fonts)
+ new_gs_sub_post = process_rt_node!(sub_lines, new_gs_sub, sub, fonts)
+ sup_lines = Vector{GlyphInfo}[[]]
+ new_gs_sup = new_glyphstate(gs, rt, Val(:subsup_sup), fonts)
+ new_gs_sup_post = process_rt_node!(sup_lines, new_gs_sup, sup, fonts)
+ if length(sub_lines) != 1
+ error("It is not allowed to include linebreaks in a subsup rich text element, the invalid element was: $(repr(sub))")
+ end
+ if length(sup_lines) != 1
+ error("It is not allowed to include linebreaks in a subsup rich text element, the invalid element was: $(repr(sup))")
+ end
+ sub_line = only(sub_lines)
+ sup_line = only(sup_lines)
+ if T === Val(:leftsubsup)
+ right_align!(sub_line, sup_line)
+ end
+ append!(lines[end], sub_line)
+ append!(lines[end], sup_line)
+ x = max(new_gs_sub_post.x, new_gs_sup_post.x)
+ else
+ new_gs = new_glyphstate(gs, rt, T, fonts)
+ for (i, c) in enumerate(rt.children)
+ new_gs = process_rt_node!(lines, new_gs, c, fonts)
+ end
+ x = new_gs.x
+ end
+
+ return GlyphState(x, gs.baseline, gs.size, gs.font, gs.color)
+end
- push!(stack, new_glyphstate(stack[end], rt, Val(rt.type), fonts))
- for (i, c) in enumerate(rt.children)
- process_rt_node!(stack, lines, c, fonts)
+function right_align!(line1::Vector{GlyphInfo}, line2::Vector{GlyphInfo})
+ isempty(line1) || isempty(line2) && return
+ xmax1, xmax2 = map((line1, line2)) do line
+ maximum(line; init = 0f0) do ginfo
+ GlyphInfo
+ ginfo.origin[1] + ginfo.size[1] * (ginfo.extent.ink_bounding_box.origin[1] + ginfo.extent.ink_bounding_box.widths[1])
+ end
+ end
+ line_to_shift = xmax1 > xmax2 ? line2 : line1
+ for j in eachindex(line_to_shift)
+ l = line_to_shift[j]
+ o = l.origin
+ l = GlyphInfo(l; origin = o .+ Point2f(abs(xmax2 - xmax1), 0))
+ line_to_shift[j] = l
end
- gs = pop!(stack)
- gs_top = stack[end]
- # x needs to continue even if going a level up
- stack[end] = GlyphState(gs.x, gs_top.baseline, gs_top.size, gs_top.font, gs_top.color)
return
end
-function process_rt_node!(stack, lines, s::String, _)
- gs = stack[end]
+function process_rt_node!(lines, gs::GlyphState, s::String, _)
y = gs.baseline
x = gs.x
for char in s
@@ -505,12 +575,7 @@ function process_rt_node!(stack, lines, s::String, _)
x = x + gext.hadvance * gs.size[1]
end
end
- stack[end] = GlyphState(x, y, gs.size, gs.font, gs.color)
- return
-end
-
-function new_glyphstate(gs::GlyphState, rt::RichText, val::Val, fonts)
- gs
+ return GlyphState(x, y, gs.size, gs.font, gs.color)
end
_get_color(attributes, default)::RGBAf = haskey(attributes, :color) ? to_color(attributes[:color]) : default
@@ -518,7 +583,7 @@ _get_font(attributes, default::NativeFont, fonts)::NativeFont = haskey(attribute
_get_fontsize(attributes, default)::Vec2f = haskey(attributes, :fontsize) ? Vec2f(to_fontsize(attributes[:fontsize])) : default
_get_offset(attributes, default)::Vec2f = haskey(attributes, :offset) ? Vec2f(attributes[:offset]) : default
-function new_glyphstate(gs::GlyphState, rt::RichText, val::Val{:sup}, fonts)
+function new_glyphstate(gs::GlyphState, rt::RichText, ::Val{:sup}, fonts)
att = rt.attributes
fontsize = _get_fontsize(att, gs.size * 0.66)
offset = _get_offset(att, Vec2f(0)) .* fontsize
@@ -531,7 +596,7 @@ function new_glyphstate(gs::GlyphState, rt::RichText, val::Val{:sup}, fonts)
)
end
-function new_glyphstate(gs::GlyphState, rt::RichText, val::Val{:span}, fonts)
+function new_glyphstate(gs::GlyphState, rt::RichText, ::Val{:span}, fonts)
att = rt.attributes
fontsize = _get_fontsize(att, gs.size)
offset = _get_offset(att, Vec2f(0)) .* fontsize
@@ -544,13 +609,36 @@ function new_glyphstate(gs::GlyphState, rt::RichText, val::Val{:span}, fonts)
)
end
-function new_glyphstate(gs::GlyphState, rt::RichText, val::Val{:sub}, fonts)
+function new_glyphstate(gs::GlyphState, rt::RichText, ::Val{:sub}, fonts)
att = rt.attributes
fontsize = _get_fontsize(att, gs.size * 0.66)
offset = _get_offset(att, Vec2f(0)) .* fontsize
GlyphState(
gs.x + offset[1],
- gs.baseline - 0.15 * gs.size[2] + offset[2],
+ gs.baseline - 0.25 * gs.size[2] + offset[2],
+ fontsize,
+ _get_font(att, gs.font, fonts),
+ _get_color(att, gs.color),
+ )
+end
+
+function new_glyphstate(gs::GlyphState, rt::RichText, ::Val{:subsup_sub}, fonts)
+ att = rt.attributes
+ fontsize = _get_fontsize(att, gs.size * 0.66)
+ GlyphState(
+ gs.x,
+ gs.baseline - 0.25 * gs.size[2],
+ fontsize,
+ _get_font(att, gs.font, fonts),
+ _get_color(att, gs.color),
+ )
+end
+function new_glyphstate(gs::GlyphState, rt::RichText, ::Val{:subsup_sup}, fonts)
+ att = rt.attributes
+ fontsize = _get_fontsize(att, gs.size * 0.66)
+ GlyphState(
+ gs.x,
+ gs.baseline + 0.4 * gs.size[2],
fontsize,
_get_font(att, gs.font, fonts),
_get_color(att, gs.color),
From 4f045d7a804a4325d567951397e711be36c9d4fe Mon Sep 17 00:00:00 2001
From: Simon
Date: Tue, 29 Oct 2024 20:06:03 +0100
Subject: [PATCH 25/80] Fix CI slowdown by relying on new bonito version with
new HTTP.jl (#4542)
properly fix CI slowdown by relying on new bonito version with new http version
---
WGLMakie/Project.toml | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/WGLMakie/Project.toml b/WGLMakie/Project.toml
index 3ac4cf07a6b..bf3a3d33a64 100644
--- a/WGLMakie/Project.toml
+++ b/WGLMakie/Project.toml
@@ -11,7 +11,6 @@ FreeTypeAbstraction = "663a7486-cb36-511b-a19d-713bb74d65c9"
GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326"
Hyperscript = "47d2ed2b-36de-50cf-bf87-49c2cf4b8b91"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
-LoggingExtras = "e6f89c97-d47a-5376-807f-9c37f3926c36"
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
Observables = "510215fc-4207-5dde-b226-833fc4488ee2"
PNGFiles = "f57f5aa1-a3ce-4bc8-8ab9-96f992907883"
@@ -21,14 +20,13 @@ ShaderAbstractions = "65257c39-d410-5151-9873-9b3e5be5013e"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
[compat]
-Bonito = "3.2.1"
+Bonito = "3.2.4"
Colors = "0.11, 0.12, 0.13"
FileIO = "1.1"
FreeTypeAbstraction = "0.10"
GeometryBasics = "0.4.11"
Hyperscript = "0.0.3, 0.0.4, 0.0.5"
LinearAlgebra = "1.0, 1.6"
-LoggingExtras = "<1.1.0"
Makie = "=0.21.15"
Observables = "0.5.1"
PNGFiles = "0.3, 0.4"
From 22863fd53f9ffd122ec624735e537ec479dcf2d8 Mon Sep 17 00:00:00 2001
From: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com>
Date: Wed, 30 Oct 2024 11:17:39 +0100
Subject: [PATCH 26/80] Fix docstrings for 1.11 (#4548)
---
MakieCore/src/recipes.jl | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/MakieCore/src/recipes.jl b/MakieCore/src/recipes.jl
index 4b1d9aaff97..fd44f41fdbe 100644
--- a/MakieCore/src/recipes.jl
+++ b/MakieCore/src/recipes.jl
@@ -392,6 +392,14 @@ end
function types_for_plot_arguments end
+function extract_docstring(str)
+ if VERSION >= v"1.11" && str isa Base.Docs.DocStr
+ return only(str.text::Core.SimpleVector)
+ else
+ return str
+ end
+end
+
function create_recipe_expr(Tsym, args, attrblock)
funcname_sym = to_func_name(Tsym)
funcname!_sym = Symbol("$(funcname_sym)!")
@@ -421,7 +429,7 @@ function create_recipe_expr(Tsym, args, attrblock)
Core.@__doc__ $(esc(docs_placeholder)) = nothing
binding = Docs.Binding(@__MODULE__, $(QuoteNode(docs_placeholder)))
user_docstring = if haskey(Docs.meta(@__MODULE__), binding)
- _docstring = @doc($docs_placeholder)
+ _docstring = extract_docstring(@doc($docs_placeholder))
delete!(Docs.meta(@__MODULE__), binding)
_docstring
else
@@ -462,7 +470,7 @@ function create_recipe_expr(Tsym, args, attrblock)
end
$(arg_type_func)
- docstring_modified = make_recipe_docstring($PlotType, $(QuoteNode(Tsym)), $(QuoteNode(funcname_sym)),user_docstring)
+ docstring_modified = make_recipe_docstring($PlotType, $(QuoteNode(Tsym)), $(QuoteNode(funcname_sym)), user_docstring)
@doc docstring_modified $funcname_sym
@doc "`$($(string(Tsym)))` is the plot type associated with plotting function `$($(string(funcname_sym)))`. Check the docstring for `$($(string(funcname_sym)))` for further information." $Tsym
@doc "`$($(string(funcname!_sym)))` is the mutating variant of plotting function `$($(string(funcname_sym)))`. Check the docstring for `$($(string(funcname_sym)))` for further information." $funcname!_sym
From 70e5d2ca235f8cae79ec87743c36d372b5cd90dc Mon Sep 17 00:00:00 2001
From: Simon
Date: Thu, 31 Oct 2024 12:40:14 +0100
Subject: [PATCH 27/80] implement S.Colorbar(plotspec) (#4520)
* implement S.Colorbar(plotspec)
* improve name
* implement lookup_default, to better get colorbar defaults from spec argument
* fix tests
* Update CHANGELOG.md
---
CHANGELOG.md | 1 +
MakieCore/src/recipes.jl | 188 ++++++++++++++--------------
ReferenceTests/src/tests/specapi.jl | 40 ++++++
src/specapi.jl | 108 ++++++++++++++--
4 files changed, 235 insertions(+), 102 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b2b23539527..7e70668c2f1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
## [Unreleased]
- Added `subsup` and `left_subsup` functions that offer stacked sub- and superscripts for `rich` text which means this style can be used with arbitrary fonts and is not limited to fonts supported by MathTeXEngine.jl [#4489](https://github.com/MakieOrg/Makie.jl/pull/4489).
+- Implement S.Colorbar(plotspec) [#4520](https://github.com/MakieOrg/Makie.jl/pull/4520).
## [0.21.15] - 2024-10-25
diff --git a/MakieCore/src/recipes.jl b/MakieCore/src/recipes.jl
index fd44f41fdbe..6e8da443ba6 100644
--- a/MakieCore/src/recipes.jl
+++ b/MakieCore/src/recipes.jl
@@ -72,7 +72,7 @@ theme(x::AbstractPlot, key; default=nothing) = deepcopy(get(x.attributes, key, d
Attributes(x::AbstractPlot) = x.attributes
-default_theme(scene, T) = Attributes()
+function default_theme end
"""
# Plot Recipes in `Makie`
@@ -209,17 +209,58 @@ attribute_names(_) = nothing
Base.@kwdef struct AttributeMetadata
docstring::Union{Nothing,String}
+ default_value::Any
default_expr::String # stringified expression, just needed for docs purposes
end
update_metadata(am1::AttributeMetadata, am2::AttributeMetadata) = AttributeMetadata(
am2.docstring === nothing ? am1.docstring : am2.docstring,
+ am2.default_value,
am2.default_expr # TODO: should it be possible to overwrite only a docstring by not giving a default expr?
)
struct DocumentedAttributes
d::Dict{Symbol,AttributeMetadata}
- closure::Function
+end
+
+struct Inherit
+ key::Symbol
+ fallback::Any
+end
+
+function lookup_default(meta::AttributeMetadata, theme)
+ default = meta.default_value
+ if default isa Inherit
+ if haskey(theme, default.key)
+ to_value(theme[default.key]) # only use value of theme entry
+ else
+ if isnothing(default.fallback)
+ error("Inherited key $(default.key) not found in theme with no fallback given.")
+ else
+ return default.fallback
+ end
+ end
+ else
+ return default
+ end
+end
+
+function get_default_expr(default)
+ if default isa Expr && default.head === :macrocall && default.args[1] === Symbol("@inherit")
+ if length(default.args) ∉ (3, 4)
+ error("@inherit works with 1 or 2 arguments, expression was $default")
+ end
+ if !(default.args[3] isa Symbol)
+ error("Argument 1 of @inherit must be a Symbol, got $(default.args[3])")
+ end
+ key = default.args[3]
+ _default = get(default.args, 4, :(nothing))
+ # first check scene theme
+ # then default value
+ return :($(MakieCore.Inherit)($(QuoteNode(key)), $(esc(_default))))
+ else
+ return esc(default)
+ end
end
macro DocumentedAttributes(expr::Expr)
@@ -256,40 +297,20 @@ macro DocumentedAttributes(expr::Expr)
if !(sym isa Symbol)
error("$sym should be a symbol")
end
-
- push!(metadata_exprs, quote
- am = AttributeMetadata(; docstring = $docs, default_expr = $(_default_expr_string(default)))
- if haskey(d, $(QuoteNode(sym)))
- d[$(QuoteNode(sym))] = update_metadata(d[$(QuoteNode(sym))], am)
+ qsym = QuoteNode(sym)
+ metadata = quote
+ am = AttributeMetadata(;
+ docstring = $docs,
+ default_value = $(get_default_expr(default)),
+ default_expr = $(default_expr_string(default))
+ )
+ if haskey(d, $(qsym))
+ d[$(qsym)] = update_metadata(d[$(qsym)], am)
else
- d[$(QuoteNode(sym))] = am
- end
- end)
-
- if default isa Expr && default.head === :macrocall && default.args[1] === Symbol("@inherit")
- if length(default.args) ∉ (3, 4)
- error("@inherit works with 1 or 2 arguments, expression was $d")
+ d[$(qsym)] = am
end
- if !(default.args[3] isa Symbol)
- error("Argument 1 of @inherit must be a Symbol, got $(default.args[3])")
- end
- key = default.args[3]
- _default = get(default.args, 4, :(error("Inherited key $($(QuoteNode(key))) not found in theme with no fallback given.")))
- # first check scene theme
- # then default value
- d = :(
- dict[$(QuoteNode(sym))] = if haskey(thm, $(QuoteNode(key)))
- to_value(thm[$(QuoteNode(key))]) # only use value of theme entry
- else
- $(esc(_default))
- end
- )
- push!(closure_exprs, d)
- else
- push!(closure_exprs, :(
- dict[$(QuoteNode(sym))] = $(esc(default))
- ))
end
+ push!(metadata_exprs, metadata)
elseif is_mixin_line
# this intermediate variable is needed to evaluate each mixin only once
# and is inserted at the start of the final code block
@@ -302,14 +323,6 @@ macro DocumentedAttributes(expr::Expr)
end
end)
- # the actual runtime values of the mixed in defaults
- # are computed using the closure stored in the DocumentedAttributes
- closure_exp = quote
- # `scene` and `dict` here are defined below where this exp is interpolated into
- merge!(dict, $gsym.closure(scene))
- end
- push!(closure_exprs, closure_exp)
-
# docstrings and default expressions of the mixed in
# DocumentedAttributes are inserted
metadata_exp = quote
@@ -330,13 +343,7 @@ macro DocumentedAttributes(expr::Expr)
$(mixin_exprs...)
d = Dict{Symbol,AttributeMetadata}()
$(metadata_exprs...)
- closure = function (scene)
- thm = theme(scene)
- dict = Dict{Symbol,Any}()
- $(closure_exprs...)
- return dict
- end
- DocumentedAttributes(d, closure)
+ DocumentedAttributes(d)
end
end
@@ -392,6 +399,43 @@ end
function types_for_plot_arguments end
+documented_attributes(_) = nothing
+
+function attribute_names(T::Type{<:Plot})
+ attr = documented_attributes(T)
+ isnothing(attr) && return nothing
+ return keys(attr.d)
+end
+
+function lookup_default(::Type{T}, scene, attribute::Symbol) where {T<:Plot}
+ thm = theme(scene)
+ metas = documented_attributes(T).d
+ psym = plotsym(T)
+ if haskey(thm, psym)
+ overwrite = thm[psym]
+ if haskey(overwrite, attribute)
+ return to_value(overwrite[attribute])
+ end
+ end
+ if haskey(metas, attribute)
+ return lookup_default(metas[attribute], thm)
+ else
+ return nothing
+ end
+end
+
+function default_theme(scene, T::Type{<: Plot})
+ metas = documented_attributes(T)
+ attr = Attributes()
+ isnothing(metas) && return attr
+ thm = theme(scene)
+ _attr = attr.attributes
+ for (k, meta) in metas.d
+ _attr[k] = lookup_default(meta, thm)
+ end
+ return attr
+end
+
function extract_docstring(str)
if VERSION >= v"1.11" && str isa Base.Docs.DocStr
return only(str.text::Core.SimpleVector)
@@ -461,13 +505,6 @@ function create_recipe_expr(Tsym, args, attrblock)
_create_plot!($funcname, kwdict, args...)
end
- function $(MakieCore).attribute_names(T::Type{<:$(PlotType)})
- keys(documented_attributes(T).d)
- end
-
- function $(MakieCore).default_theme(scene, T::Type{<:$(PlotType)})
- Attributes(documented_attributes(T).closure(scene))
- end
$(arg_type_func)
docstring_modified = make_recipe_docstring($PlotType, $(QuoteNode(Tsym)), $(QuoteNode(funcname_sym)), user_docstring)
@@ -490,6 +527,8 @@ function create_recipe_expr(Tsym, args, attrblock)
return q
end
+
+
function make_recipe_docstring(P::Type{<:Plot}, Tsym, funcname_sym, docstring)
io = IOBuffer()
@@ -528,8 +567,8 @@ function rmlines(x::Expr)
end
end
-_default_expr_string(x) = string(rmlines(x))
-_default_expr_string(x::String) = repr(x)
+default_expr_string(x) = string(rmlines(x))
+default_expr_string(x::String) = repr(x)
function extract_attribute_metadata(arg)
has_docs = arg isa Expr && arg.head === :macrocall && arg.args[1] isa GlobalRef
@@ -561,41 +600,6 @@ function extract_attribute_metadata(arg)
(docs = docs, symbol = attr_symbol, type = type, default = default)
end
-function make_default_theme_expr(attrs, scenesym::Symbol)
-
- exprs = map(attrs) do a
-
- d = a.default
- if d isa Expr && d.head === :macrocall && d.args[1] == Symbol("@inherit")
- if length(d.args) != 4
- error("@inherit works with exactly 2 arguments, expression was $d")
- end
- if !(d.args[3] isa QuoteNode)
- error("Argument 1 of @inherit must be a :symbol, got $(d.args[3])")
- end
- key, default = d.args[3:4]
- # first check scene theme
- # then default value
- d = quote
- if haskey(thm, $key)
- to_value(thm[$key]) # only use value of theme entry
- else
- $default
- end
- end
- end
-
- :(attr[$(QuoteNode(a.symbol))] = $d)
- end
-
- quote
- thm = theme($scenesym)
- attr = Attributes()
- $(exprs...)
- attr
- end
-end
-
function expand_mixins(attrblock::Expr)
Expr(:block, mapreduce(expand_mixin, vcat, attrblock.args)...)
end
diff --git a/ReferenceTests/src/tests/specapi.jl b/ReferenceTests/src/tests/specapi.jl
index f12a39585ee..ab5847915d9 100644
--- a/ReferenceTests/src/tests/specapi.jl
+++ b/ReferenceTests/src/tests/specapi.jl
@@ -116,3 +116,43 @@ end
sync_step!(st)
st
end
+
+function to_plot(plots)
+ axes = map(permutedims(plots)) do plot
+ ax = S.Axis(;
+ plots=[plot], xticksvisible=false,
+ yticksvisible=false, yticklabelsvisible=false,
+ xticklabelsvisible=false)
+ return S.GridLayout([ax S.Colorbar(plot)])
+ end
+ return S.GridLayout(axes)
+end
+
+@reference_test "Colorbar from Plots" begin
+ data = vcat((1:4)', (4:-1:1)')
+ plots = [S.Heatmap(data),
+ S.Image(data),
+ S.Lines(1:4; linewidth=4, color=1:4),
+ S.Scatter(1:4; markersize=20, color=1:4)]
+ obs = Observable(to_plot(plots))
+ fig = plot(obs; figure=(; size=(700, 150)))
+ img1 = copy(colorbuffer(fig))
+ plots = [S.Heatmap(data; colormap=:inferno),
+ S.Image(data; colormap=:inferno),
+ S.Lines(1:4; linewidth=4, color=1:4, colormap=:inferno),
+ S.Scatter(1:4; markersize=20, color=1:4, colormap=:inferno)]
+ obs[] = to_plot(plots)
+ img2 = copy(colorbuffer(fig))
+
+ plots = [S.Heatmap(data; colorrange=(2, 3)),
+ S.Image(data; colorrange=(2, 3)),
+ S.Lines(1:4; linewidth=4, color=1:4, colorrange=(2, 3)),
+ S.Scatter(1:4; markersize=20, color=1:4, colorrange=(2, 3))]
+ obs[] = to_plot(plots)
+ img3 = copy(colorbuffer(fig))
+
+ imgs = hcat(rotr90.((img3, img2, img1))...)
+ s = Scene(; size=size(imgs))
+ image!(s, imgs; space=:pixel)
+ s
+end
diff --git a/src/specapi.jl b/src/specapi.jl
index 6120d0ad08e..2f5e1a6e08d 100644
--- a/src/specapi.jl
+++ b/src/specapi.jl
@@ -190,6 +190,7 @@ function Base.getproperty(p::BlockSpec, k::Symbol)
end
Base.propertynames(p::BlockSpec) = Tuple(keys(p.kwargs))
+
function BlockSpec(typ::Symbol, args...; plots::Vector{PlotSpec}=PlotSpec[], kw...)
attr = Dict{Symbol,Any}(kw)
if typ == :Legend
@@ -201,8 +202,16 @@ function BlockSpec(typ::Symbol, args...; plots::Vector{PlotSpec}=PlotSpec[], kw.
attr[:entrygroups] = entrygroups
return BlockSpec(typ, attr, plots)
else
+ if typ == :Colorbar && !isempty(args)
+ if length(args) == 1 && args[1] isa PlotSpec
+ attr[:plotspec] = args[1]
+ args = ()
+ else
+ error("Only one argument `arg::PlotSpec` is supported for S.Colorbar. Found: $(args)")
+ end
+ end
if !isempty(args)
- error("BlockSpecs, with an exception for Legend, don't support positional arguments yet.")
+ error("BlockSpecs, with an exception for Legend and Colorbar, don't support positional arguments yet.")
end
return BlockSpec(typ, attr, plots)
end
@@ -395,7 +404,6 @@ function Base.getproperty(::_SpecApi, field::Symbol)
end
end
-
function update_plot!(obs_to_notify, plot::AbstractPlot, oldspec::PlotSpec, spec::PlotSpec)
# Update args in plot `input_args` list
for i in eachindex(spec.args)
@@ -423,6 +431,22 @@ function update_plot!(obs_to_notify, plot::AbstractPlot, oldspec::PlotSpec, spec
push!(obs_to_notify, old_attr)
end
end
+
+ reset_to_default = setdiff(keys(oldspec.kwargs), keys(spec.kwargs))
+ filter!(x -> x != :cycle, reset_to_default) # dont reset cycle
+ if !isempty(reset_to_default)
+ for k in reset_to_default
+ old_attr = plot[k]
+ new_value = MakieCore.lookup_default(typeof(plot), parent_scene(plot), k)
+ # In case of e.g. dim_conversions
+ isnothing(new_value) && continue
+ # only update if different
+ if is_different(old_attr[], new_value)
+ old_attr.val = new_value
+ push!(obs_to_notify, old_attr)
+ end
+ end
+ end
# Cycling needs to be handled separately sadly,
# since they're implicitely mutating attributes, e.g. if I re-use a plot
# that has been on cycling position 2, and now I re-use it for the first plot in the list
@@ -437,7 +461,6 @@ function update_plot!(obs_to_notify, plot::AbstractPlot, oldspec::PlotSpec, spec
end
end
end
-
if !isempty(uncycled)
# remove all attributes that don't need cycling
for (attr_vec, _) in cycle.cycle
@@ -450,6 +473,7 @@ function update_plot!(obs_to_notify, plot::AbstractPlot, oldspec::PlotSpec, spec
return
end
+
"""
plotlist!(
[
@@ -645,10 +669,64 @@ function add_observer!(block::BlockSpec, obs::AbstractVector{<:ObserverFunction}
return
end
+function get_numeric_colors(plot::PlotSpec)
+ if plot.type in [:Heatmap, :Image, :Surface]
+ z = plot.args[end]
+ if z isa AbstractMatrix{<:Real}
+ return z
+ end
+ else
+ if haskey(plot.kwargs, :color) && plot.kwargs[:color] isa AbstractArray{<:Real}
+ return plot.kwargs[:color]
+ end
+ end
+ return nothing
+end
+
+# TODO it's really hard to get from PlotSpec -> Plot object in the
+# Colorbar constructor (to_layoutable),
+# since the plot may not be created yet and may change when calling
+# update_layoutable!. So for now, we manually extract the Colorbar arguments from the spec
+# Which is a bit brittle and won't work for Recipes which overload the Colorbar api (extract_colormap)
+# We hope to improve the situation after the observable refactor, which may bring us a bit closer to
+# Being able to use the Plot object itself instead of a spec.
+function extract_colorbar_kw(legend::BlockSpec, scene::Scene)
+ if haskey(legend.kwargs, :plotspec)
+ kw = copy(legend.kwargs)
+ spec = pop!(kw, :plotspec)
+ pt = plottype(spec)
+ for k in [:colorrange, :colormap, :lowclip, :highclip]
+ get!(kw, k) do
+ haskey(spec.kwargs, k) && return spec.kwargs[k]
+ if k === :colorrange
+ color = get_numeric_colors(spec)
+ if !isnothing(color)
+ return nan_extrema(color)
+ end
+ else
+ MakieCore.lookup_default(pt, scene, k)
+ end
+ end
+ end
+ return kw
+ else
+ return legend.kwargs
+ end
+end
+
function to_layoutable(parent, position::GridLayoutPosition, spec::BlockSpec)
BType = getfield(Makie, spec.type)
- # TODO forward kw
- block = BType(get_top_parent(parent); spec.kwargs...)
+ fig = get_top_parent(parent)
+
+ block = if spec.type === :Colorbar
+ # We use the root scene to extract any theming
+ # This means, we dont support a separate theme per scene
+ # Which I think has been bitrotting anyways.
+ kw = extract_colorbar_kw(spec, root(get_scene(fig)))
+ BType(fig; kw...)
+ else
+ BType(fig; spec.kwargs...)
+ end
parent[position...] = block
for func in spec.then_funcs
observers = func(block)
@@ -675,8 +753,17 @@ end
function update_layoutable!(block::T, plot_obs, old_spec::BlockSpec, spec::BlockSpec) where T <: Block
unhide!(block)
- old_attr = keys(old_spec.kwargs)
- new_attr = keys(spec.kwargs)
+ if spec.type === :Colorbar
+ # To get plot defaults for Colorbar(specapi), we need a theme / scene
+ # So we have to look up the kwargs here instead of the BlockSpec constructor.
+ old_kw = extract_colorbar_kw(old_spec, root(block.blockscene))
+ new_kw = extract_colorbar_kw(spec, root(block.blockscene))
+ else
+ old_kw = old_spec.kwargs
+ new_kw = spec.kwargs
+ end
+ old_attr = keys(old_kw)
+ new_attr = keys(new_kw)
# attributes that have been set previously and need to get unset now
reset_to_defaults = setdiff(old_attr, new_attr)
if !isempty(reset_to_defaults)
@@ -688,7 +775,7 @@ function update_layoutable!(block::T, plot_obs, old_spec::BlockSpec, spec::Block
# Attributes needing an update
to_update = setdiff(new_attr, reset_to_defaults)
for key in to_update
- val = spec.kwargs[key]
+ val = new_kw[key]
prev_val = to_value(getproperty(block, key))
if is_different(val, prev_val)
setproperty!(block, key, val)
@@ -816,7 +903,6 @@ get_layout!(fig::Figure) = fig.layout
get_layout!(gp::Union{GridSubposition,GridPosition}) = GridLayoutBase.get_layout_at!(gp; createmissing=true)
-
delete_layoutable!(block::Block) = delete!(block)
function delete_layoutable!(grid::GridLayout)
gc = grid.layoutobservables.gridcontent[]
@@ -836,7 +922,9 @@ function update_gridlayout!(target_layout::GridLayout, layout_spec::GridLayoutSp
update_gridlayout!(target_layout, 1, nothing, layout_spec, unused_layoutables, new_layoutables)
foreach(unused_layoutables) do (p, (block, obs))
# disconnect! all unused layoutables, so they dont show up anymore
- disconnect!(block)
+ if block isa Block
+ disconnect!(block)
+ end
return
end
layouts_to_update = Set{GridLayout}([target_layout])
From 379bb69bc7d1acd77fbfbfd1ebab2ded40eacd6f Mon Sep 17 00:00:00 2001
From: Anshul Singhvi
Date: Thu, 31 Oct 2024 09:18:53 -0700
Subject: [PATCH 28/80] Fix legend for plotlist with multiple plots (#4546)
* Fix `legend` for plotlist with multiple plots
* Add a test
* Update CHANGELOG.md
---------
Co-authored-by: SimonDanisch
---
CHANGELOG.md | 1 +
src/makielayout/blocks/legend.jl | 8 +++++++-
test/specapi.jl | 17 +++++++++++++++++
3 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7e70668c2f1..05384f15cd0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
## [Unreleased]
- Added `subsup` and `left_subsup` functions that offer stacked sub- and superscripts for `rich` text which means this style can be used with arbitrary fonts and is not limited to fonts supported by MathTeXEngine.jl [#4489](https://github.com/MakieOrg/Makie.jl/pull/4489).
+- Expand PlotList plots to expose their child plots to the legend interface, allowing `axislegend`show plots within PlotSpecs as individual entries. [#4546](https://github.com/MakieOrg/Makie.jl/pull/4546)
- Implement S.Colorbar(plotspec) [#4520](https://github.com/MakieOrg/Makie.jl/pull/4520).
## [0.21.15] - 2024-10-25
diff --git a/src/makielayout/blocks/legend.jl b/src/makielayout/blocks/legend.jl
index 8344667b28e..90a3168dc39 100644
--- a/src/makielayout/blocks/legend.jl
+++ b/src/makielayout/blocks/legend.jl
@@ -661,7 +661,8 @@ end
function get_labeled_plots(ax; merge::Bool, unique::Bool)
lplots = filter(get_plots(ax)) do plot
- haskey(plot.attributes, :label)
+ haskey(plot.attributes, :label) ||
+ plot isa PlotList && any(x -> haskey(x.attributes, :label), plot.plots)
end
labels = map(lplots) do l
l.label[]
@@ -713,6 +714,11 @@ function get_labeled_plots(ax; merge::Bool, unique::Bool)
end
get_plots(p::AbstractPlot) = [p]
+# NOTE: this is important, since we know that `get_plots` is only ever called on the toplevel,
+# we can assume that any plotlist on the toplevel should be decomposed into individual plots.
+# However, if the user passes a label argument with a legend override, what do we do?
+get_plots(p::PlotList) = haskey(p.attributes, :label) && p.attributes[:label] isa Pair ? [p] : p.plots
+
get_plots(ax::Union{Axis, Axis3}) = get_plots(ax.scene)
get_plots(lscene::LScene) = get_plots(lscene.scene)
function get_plots(scene::Scene)
diff --git a/test/specapi.jl b/test/specapi.jl
index d948768d821..cea645640aa 100644
--- a/test/specapi.jl
+++ b/test/specapi.jl
@@ -170,3 +170,20 @@ end
@test isempty(f.content)
@test isempty(f.layout.content)
end
+
+@testset "Legend construction" begin
+ f, ax, pl = plotlist([S.Scatter(1:4, 1:4; marker = :circle, label="A"), S.Scatter(1:6, 1:6; marker = :rect, label="B")])
+ leg = axislegend(ax)
+ # Test that the legend has two scatter plots
+ @test count(x -> x isa Makie.Scatter, leg.scene.plots) == 2
+
+ # Test that the scatter plots have the correct markers
+ # This is too internal and fragile, so we won't actually test this
+ # @test leg.scene.plots[2].marker[] == :circle
+ # @test leg.scene.plots[3].marker[] == :rect
+
+ # Test that the legend has the correct labels.
+ # Again, I consider this too fragile to work with!
+ # @test contents(contents(leg.grid)[1])[2].text[] == "A"
+ # @test contents(contents(leg.grid)[2])[4].text[] == "B"
+end
From 8b914d05ebd42bdfc8cd10f9a00397091aac2de6 Mon Sep 17 00:00:00 2001
From: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com>
Date: Thu, 31 Oct 2024 18:42:28 +0100
Subject: [PATCH 29/80] Median ratio bootstrap plot for benchmarks (#4553)
* add violin plots of bootstrapped median ratios
* 1000 samples should be enough
* remove manually set ticks
* add colored background to visualize good vs bad change in 5% bands
---
metrics/ttfp/Project.toml | 1 +
metrics/ttfp/run-benchmark.jl | 55 +++++++++++++++++++++++++++++------
2 files changed, 47 insertions(+), 9 deletions(-)
diff --git a/metrics/ttfp/Project.toml b/metrics/ttfp/Project.toml
index ef310db892d..a77f1a9977f 100644
--- a/metrics/ttfp/Project.toml
+++ b/metrics/ttfp/Project.toml
@@ -1,5 +1,6 @@
[deps]
AlgebraOfGraphics = "cbdf2221-f076-402e-a563-3d30da359d67"
+Bootstrap = "e28b5b4c-05e8-5b66-bc03-6f0c0a0a06e0"
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
diff --git a/metrics/ttfp/run-benchmark.jl b/metrics/ttfp/run-benchmark.jl
index 7384031413b..e1f916ece07 100644
--- a/metrics/ttfp/run-benchmark.jl
+++ b/metrics/ttfp/run-benchmark.jl
@@ -10,7 +10,8 @@ Pkg.instantiate()
pkg"registry up"
Pkg.update()
-using JSON, AlgebraOfGraphics, CairoMakie, DataFrames
+using JSON, AlgebraOfGraphics, CairoMakie, DataFrames, Bootstrap
+using Statistics: median
Package = ARGS[1]
n_samples = length(ARGS) > 1 ? parse(Int, ARGS[2]) : 7
base_branch = length(ARGS) > 2 ? ARGS[3] : "master"
@@ -19,7 +20,7 @@ base_branch = length(ARGS) > 2 ? ARGS[3] : "master"
# n_samples = 2
# base_branch = "breaking-release"
-@info("Benchmarking $(Package) against $(base_branch) with $(n_samples)")
+@info("Benchmarking $(Package) against $(base_branch) with $(n_samples) samples")
function run_benchmarks(projects; n=n_samples)
@@ -64,24 +65,26 @@ Pkg.add(pkgs)
@time Pkg.precompile()
projects = [project1, project2]
+projnames = map(basename, [project1, project2])
run_benchmarks(projects)
-json_files = map([project1, project2]) do p
- "$(basename(p))-benchmark.json"
+json_files = map(projnames) do pname
+ "$(pname)-benchmark.json"
end
colnames = ["using", "first create", "first display", "create", "display"]
-df = reduce(vcat, map(json_files) do filename
- name = replace(filename, r"-benchmark.*" => "")
+df = reduce(vcat, map(json_files, projnames) do filename, pname
arrs = map(x -> map(identity, x), JSON.parsefile(filename))
df = DataFrame(colnames .=> arrs)
- df.name .= name
+ df.name .= pname
df
end)
-plt = AlgebraOfGraphics.data(df) *
+##
+
+fgrid = AlgebraOfGraphics.data(df) *
mapping(:name, colnames .=> (x -> x / 1e9) .=> "time (s)", color = :name, layout = dims(1) => renamer(colnames)) *
visual(RainClouds, orientation = :horizontal, markersize = 5, show_median = false, plot_boxplots = false) |>
draw(
@@ -91,5 +94,39 @@ plt = AlgebraOfGraphics.data(df) *
figure = (; title = "$Package Benchmarks")
)
+df_current_pr = df[df.name .== projnames[1], :]
+df_base_branch = df[df.name .== projnames[2], :]
+
+medians_df = map(names(df_current_pr, Not(:name))) do colname
+ col_base = df_base_branch[!, colname]
+ col_pr = df_current_pr[!, colname]
+ medians_base = bootstrap(median, col_base, Bootstrap.BasicSampling(1000))
+ medians_pr = bootstrap(median, col_pr, Bootstrap.BasicSampling(1000))
+ ratios = Bootstrap.straps(medians_pr)[1] ./ Bootstrap.straps(medians_base)[1]
+ colname => ratios
+end |> DataFrame
+
+specmedians = AlgebraOfGraphics.data(stack(medians_df)) *
+ mapping(:variable => presorted => "", :value => "Ratios of medians\n$(projnames[1]) / $(projnames[2])") * visual(Violin, show_median = true)
+
+background_bands = AlgebraOfGraphics.pregrouped([0.75:0.05:1.20], [0.8:0.05:1.25]) *
+ AlgebraOfGraphics.visual(HSpan, color = range(-1, 1, length = 10), colormap = [:green, :white, :tomato], alpha = 0.5)
+
+zeroline = AlgebraOfGraphics.pregrouped([1]) * AlgebraOfGraphics.visual(HLines, color = :gray60)
+
+spec = background_bands + zeroline + specmedians
+
+AlgebraOfGraphics.draw!(fgrid.figure[2, 3], spec, axis = (;
+ yaxisposition = :right,
+ xticklabelrotation = pi/4,
+ title = "Bootstrapped median ratios",
+ yautolimitmargin = (0, 0),
+ yticks = WilkinsonTicks(7, k_min = 5),
+))
+
+resize_to_layout!(fgrid.figure)
+
+##
+
mkpath("benchmark_results")
-save(joinpath("benchmark_results", "$Package.svg"), plt)
+save(joinpath("benchmark_results", "$Package.svg"), fgrid)
From f5528a663789349d1ea56c75cd7e7153f8638566 Mon Sep 17 00:00:00 2001
From: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com>
Date: Thu, 31 Oct 2024 20:40:30 +0100
Subject: [PATCH 30/80] Use ABBA pattern for benchmarking (#4555)
---
metrics/ttfp/run-benchmark.jl | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/metrics/ttfp/run-benchmark.jl b/metrics/ttfp/run-benchmark.jl
index e1f916ece07..c36efce30d1 100644
--- a/metrics/ttfp/run-benchmark.jl
+++ b/metrics/ttfp/run-benchmark.jl
@@ -25,8 +25,16 @@ base_branch = length(ARGS) > 2 ? ARGS[3] : "master"
function run_benchmarks(projects; n=n_samples)
benchmark_file = joinpath(@__DIR__, "benchmark-ttfp.jl")
- # go A, B, A, B, A, etc.
- for project in repeat(projects, n)
+ # go A, A, B, B, A, A, B, B, etc. because if A or B have some effect on their
+ # subsequent run, then we distribute those more evenly. If we used A, B, A, B then
+ # B would always influence A and A always B which might bias the results (something
+ # that can carry over separate processes like thermal throttling or so)
+
+ A, B = projects
+ As = Iterators.partition(fill(A, n), 2)
+ Bs = Iterators.partition(fill(B, n), 2)
+
+ for project in Iterators.flatten(Iterators.flatten(zip(As, Bs)))
println(basename(project))
run(`$(Base.julia_cmd()) --startup-file=no --project=$(project) $benchmark_file $Package`)
end
From 9d5501073def1806af0439a2d18f30b6643310fe Mon Sep 17 00:00:00 2001
From: t-bltg
Date: Fri, 1 Nov 2024 13:07:59 +0100
Subject: [PATCH 31/80] correct spelling (#4522)
* correct spelling
* check if single quote causes timeouts
Seems like the only change in the WGLMakie source to me that could possibly have an effect on anything, if interpolation of that string into javascript has some escaping bugs.
---------
Co-authored-by: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com>
---
CHANGELOG.md | 8 ++++----
CairoMakie/src/infrastructure.jl | 2 +-
CairoMakie/src/primitives.jl | 4 ++--
CairoMakie/test/svg_tests.jl | 2 +-
GLMakie/src/GLAbstraction/AbstractGPUArray.jl | 2 +-
GLMakie/src/GLAbstraction/GLAbstraction.jl | 2 +-
GLMakie/src/GLAbstraction/GLRender.jl | 2 +-
GLMakie/src/GLAbstraction/GLRenderObject.jl | 2 +-
GLMakie/src/GLAbstraction/GLTexture.jl | 4 ++--
GLMakie/src/GLAbstraction/GLTypes.jl | 2 +-
GLMakie/src/GLAbstraction/GLUtils.jl | 6 +++---
GLMakie/src/glshaders/lines.jl | 8 ++++----
GLMakie/src/screen.jl | 4 ++--
GLMakie/test/glmakie_refimages.jl | 2 +-
GLMakie/test/unit_tests.jl | 2 +-
MakieCore/src/conversion.jl | 2 +-
RPRMakie/src/meshes.jl | 2 +-
ReferenceTests/src/tests/categorical.jl | 4 ++--
ReferenceTests/src/tests/primitives.jl | 2 +-
ReferenceUpdater/src/local_server.jl | 2 +-
ReferenceUpdater/src/reference_images.html | 2 +-
WGLMakie/src/display.jl | 8 ++++----
WGLMakie/src/lines.jl | 16 +++++++--------
WGLMakie/src/serialization.jl | 2 +-
WGLMakie/src/voxel.jl | 2 +-
WGLMakie/test/runtests.jl | 2 +-
docs/src/explanations/backends/glmakie.md | 4 ++--
docs/src/explanations/backends/rprmakie.md | 2 +-
docs/src/explanations/backends/wglmakie.md | 2 +-
docs/src/reference/blocks/colorbar.md | 2 +-
docs/src/reference/blocks/intervalslider.md | 2 +-
docs/src/reference/blocks/polaraxis.md | 2 +-
docs/src/reference/blocks/slider.md | 2 +-
docs/src/reference/plots/datashader.md | 2 +-
docs/src/reference/plots/heatmap.md | 2 +-
docs/src/reference/plots/rainclouds.md | 2 +-
docs/src/tutorials/scenes.md | 2 +-
src/Makie.jl | 2 +-
src/basic_recipes/barplot.jl | 2 +-
src/basic_recipes/datashader.jl | 12 +++++------
src/basic_recipes/raincloud.jl | 2 +-
src/basic_recipes/text.jl | 2 +-
src/basic_recipes/timeseries.jl | 2 +-
src/bezier.jl | 2 +-
src/configuration.yaml | 2 +-
src/conversions.jl | 10 +++++-----
src/dim-converts/categorical-integration.jl | 2 +-
src/dim-converts/dates-integration.jl | 2 +-
src/dim-converts/dim-converts.jl | 2 +-
src/display.jl | 6 +++---
src/documentation/documentation.jl | 4 ++--
src/ffmpeg-util.jl | 2 +-
src/float32-scaling.jl | 6 +++---
src/interaction/inspector.jl | 4 ++--
src/jl_rasterizer/bmp.jl | 4 ++--
src/makielayout/blocks/colorbar.jl | 20 +++++++++----------
src/makielayout/blocks/polaraxis.jl | 2 +-
src/specapi.jl | 4 ++--
src/stats/violin.jl | 2 +-
src/theming.jl | 6 +++---
src/types.jl | 2 +-
src/utilities/utilities.jl | 4 ++--
test/Plane.jl | 2 +-
test/PolarAxis.jl | 2 +-
test/convert_arguments.jl | 2 +-
test/events.jl | 2 +-
test/float32convert.jl | 2 +-
test/ray_casting.jl | 2 +-
test/scenes.jl | 4 ++--
69 files changed, 122 insertions(+), 122 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 05384f15cd0..a3e4de0309d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -169,7 +169,7 @@
- `boundingbox` overwrites must now include a secondary space argument to work `boundingbox(plot, space::Symbol = :data)` [#3723](https://github.com/MakieOrg/Makie.jl/pull/3723)
- `boundingbox` now always consider `transform_func` and `model`
- `data_limits(::Scatter)` and `boundingbox(::Scatter)` now consider marker transformations [#3716](https://github.com/MakieOrg/Makie.jl/pull/3716)
-- **Breaking** Improved Float64 compatability of Axis [#3681](https://github.com/MakieOrg/Makie.jl/pull/3681)
+- **Breaking** Improved Float64 compatibility of Axis [#3681](https://github.com/MakieOrg/Makie.jl/pull/3681)
- This added an extra conversion step which only takes effect when Float32 precision becomes relevant. In those cases code using `project()` functions will be wrong as the transformation is not applied. Use `project(plot_or_scene, ...)` or apply the conversion yourself beforehand with `Makie.f32_convert(plot_or_scene, transformed_point)` and use `patched_model = Makie.patch_model(plot_or_scene, model)`.
- `Makie.to_world(point, matrix, resolution)` has been deprecated in favor of `Makie.to_world(scene_or_plot, point)` to include float32 conversions.
- **Breaking** Reworked line shaders in GLMakie and WGLMakie [#3558](https://github.com/MakieOrg/Makie.jl/pull/3558)
@@ -230,7 +230,7 @@
## [0.20.7] - 2024-02-04
- Equalized alignment point of mirrored ticks to that of normal ticks [#3598](https://github.com/MakieOrg/Makie.jl/pull/3598).
-- Fixed stack overflow error on conversion of gridlike data with missings [#3597](https://github.com/MakieOrg/Makie.jl/pull/3597).
+- Fixed stack overflow error on conversion of gridlike data with `missing`s [#3597](https://github.com/MakieOrg/Makie.jl/pull/3597).
- Fixed mutation of CairoMakie src dir when displaying png files [#3588](https://github.com/MakieOrg/Makie.jl/pull/3588).
- Added better error messages for plotting into `FigureAxisPlot` and `AxisPlot` as Plots.jl users are likely to do [#3596](https://github.com/MakieOrg/Makie.jl/pull/3596).
- Added compat bounds for IntervalArithmetic.jl due to bug with DelaunayTriangulation.jl [#3595](https://github.com/MakieOrg/Makie.jl/pull/3595).
@@ -246,7 +246,7 @@
- Use plot plot instead of scene transform functions in CairoMakie, fixing missplaced h/vspan. [#3552](https://github.com/MakieOrg/Makie.jl/pull/3552)
- Fix error printing on shader error [#3530](https://github.com/MakieOrg/Makie.jl/pull/3530).
- Update pagefind to 1.0.4 for better headline search [#3534](https://github.com/MakieOrg/Makie.jl/pull/3534).
-- Remove unecessary deps, e.g. Setfield [3546](https://github.com/MakieOrg/Makie.jl/pull/3546).
+- Remove unnecessary deps, e.g. Setfield [3546](https://github.com/MakieOrg/Makie.jl/pull/3546).
- Don't clear args, rely on delete deregister_callbacks [#3543](https://github.com/MakieOrg/Makie.jl/pull/3543).
- Add interpolate keyword for Surface [#3541](https://github.com/MakieOrg/Makie.jl/pull/3541).
- Fix a DataInspector bug if inspector_label is used with RGB images [#3468](https://github.com/MakieOrg/Makie.jl/pull/3468).
@@ -591,7 +591,7 @@ role as `datalimits` in `violin` [#2137](https://github.com/MakieOrg/Makie.jl/pu
- **Breaking** Cleaned up `Scene` type [#1192](https://github.com/MakieOrg/Makie.jl/pull/1192), [#1393](https://github.com/MakieOrg/Makie.jl/pull/1393). The `Scene()` constructor doesn't create any axes or limits anymore. All keywords like `raw`, `show_axis` have been removed. A scene now always works like it did when using the deprecated `raw=true`. All the high level functionality like showing an axis and adding a 3d camera has been moved to `LScene`. See the new `Scene` tutorial for more info: https://docs.makie.org/dev/tutorials/scenes/.
- **Breaking** Lights got moved to `Scene`, see the [lighting docs](https://docs.makie.org/stable/documentation/lighting) and [RPRMakie examples](https://docs.makie.org/stable/documentation/backends/rprmakie/).
- Added ECDF plot [#1310](https://github.com/MakieOrg/Makie.jl/pull/1310).
-- Added Order Independent Transparency to GLMakie [#1418](https://github.com/MakieOrg/Makie.jl/pull/1418), [#1506](https://github.com/MakieOrg/Makie.jl/pull/1506). This type of transparency is now used with `transpareny = true`. The old transparency handling is available with `transparency = false`.
+- Added Order Independent Transparency to GLMakie [#1418](https://github.com/MakieOrg/Makie.jl/pull/1418), [#1506](https://github.com/MakieOrg/Makie.jl/pull/1506). This type of transparency is now used with `transparency = true`. The old transparency handling is available with `transparency = false`.
- Fixed blurry text in GLMakie and WGLMakie [#1494](https://github.com/MakieOrg/Makie.jl/pull/1494).
- Introduced a new experimental backend for ray tracing: [RPRMakie](https://docs.makie.org/stable/documentation/backends/rprmakie/).
- Added the `Cycled` type, which can be used to select the i-th value from the current cycler for a specific attribute [#1248](https://github.com/MakieOrg/Makie.jl/pull/1248).
diff --git a/CairoMakie/src/infrastructure.jl b/CairoMakie/src/infrastructure.jl
index 0f70934682b..4dc5d4deba9 100644
--- a/CairoMakie/src/infrastructure.jl
+++ b/CairoMakie/src/infrastructure.jl
@@ -154,7 +154,7 @@ end
function draw_plot_as_image(scene::Scene, screen::Screen{RT}, primitive::Plot, scale::Number = 1) where RT
# you can provide `p.rasterize = scale::Int` or `p.rasterize = true`, both of which are numbers
- # Extract scene width in device indepentent units
+ # Extract scene width in device independent units
w, h = size(scene)
# Create a new Screen which renders directly to an image surface,
# specifically for the plot's parent scene.
diff --git a/CairoMakie/src/primitives.jl b/CairoMakie/src/primitives.jl
index 795359670ca..4cf37ea8b7c 100644
--- a/CairoMakie/src/primitives.jl
+++ b/CairoMakie/src/primitives.jl
@@ -928,7 +928,7 @@ end
function draw_mesh2D(screen, per_face_cols, vs::Vector{<: Point2}, fs::Vector{GLTriangleFace})
ctx = screen.context
- # Priorize colors of the mesh if present
+ # Prioritize colors of the mesh if present
# This is a hack, which needs cleaning up in the Mesh plot type!
for (f, (c1, c2, c3)) in zip(fs, per_face_cols)
@@ -984,7 +984,7 @@ function draw_mesh3D(
meshuvs = map(uv -> uv_transform * to_ndim(Vec3f, uv, 1), meshuvs)
end
- # Priorize colors of the mesh if present
+ # Prioritize 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)
diff --git a/CairoMakie/test/svg_tests.jl b/CairoMakie/test/svg_tests.jl
index da30bba0948..8d7f4239437 100644
--- a/CairoMakie/test/svg_tests.jl
+++ b/CairoMakie/test/svg_tests.jl
@@ -61,7 +61,7 @@ end
@test svg_isnt_rasterized(poly(MultiPolyWrapper([poly1, poly1]); color=[:red, :blue]))
end
-@testset "reproducable svg ids" begin
+@testset "reproducible svg ids" begin
# https://github.com/MakieOrg/Makie.jl/issues/2406
f, ax, sc = scatter(1:10)
save("test1.svg", f)
diff --git a/GLMakie/src/GLAbstraction/AbstractGPUArray.jl b/GLMakie/src/GLAbstraction/AbstractGPUArray.jl
index 2241931b7f4..edfba611746 100644
--- a/GLMakie/src/GLAbstraction/AbstractGPUArray.jl
+++ b/GLMakie/src/GLAbstraction/AbstractGPUArray.jl
@@ -192,7 +192,7 @@ max_dim(t) = error("max_dim not implemented for: $(typeof(t)). This happen
function (::Type{GPUArrayType})(data::Observable; kw...) where GPUArrayType <: GPUArray
gpu_mem = GPUArrayType(data[]; kw...)
- # TODO merge these and handle update tracking during contruction
+ # TODO merge these and handle update tracking during construction
obs2 = on(new_data -> update!(gpu_mem, new_data), data)
if GPUArrayType <: TextureBuffer
push!(gpu_mem.buffer.observers, obs2)
diff --git a/GLMakie/src/GLAbstraction/GLAbstraction.jl b/GLMakie/src/GLAbstraction/GLAbstraction.jl
index e8c36de91c7..67c6843ed10 100644
--- a/GLMakie/src/GLAbstraction/GLAbstraction.jl
+++ b/GLMakie/src/GLAbstraction/GLAbstraction.jl
@@ -53,7 +53,7 @@ export update! # updates a gpu array with a Julia array
export gpu_data # gets the data of a gpu array as a Julia Array
export RenderObject # An object which holds all GPU handles and datastructes to ready for rendering by calling render(obj)
-export prerender! # adds a function to a RenderObject, which gets executed befor setting the OpenGL render state
+export prerender! # adds a function to a RenderObject, which gets executed before setting the OpenGL render state
export postrender! # adds a function to a RenderObject, which gets executed after setting the OpenGL render states
export extract_renderable
export set_arg!
diff --git a/GLMakie/src/GLAbstraction/GLRender.jl b/GLMakie/src/GLAbstraction/GLRender.jl
index 146eeb6dd44..357ce23781b 100644
--- a/GLMakie/src/GLAbstraction/GLRender.jl
+++ b/GLMakie/src/GLAbstraction/GLRender.jl
@@ -160,7 +160,7 @@ function renderinstanced(vao::GLVertexArray{GLBuffer{T}}, amount::Integer, primi
end
"""
-Renders `amount` instances of an not indexed geoemtry geometry
+Renders `amount` instances of an not indexed geometry geometry
"""
function renderinstanced(vao::GLVertexArray, amount::Integer, primitive=GL_TRIANGLES)
glDrawElementsInstanced(primitive, length(vao), GL_UNSIGNED_INT, C_NULL, amount)
diff --git a/GLMakie/src/GLAbstraction/GLRenderObject.jl b/GLMakie/src/GLAbstraction/GLRenderObject.jl
index fe644f5c586..c47b2479849 100644
--- a/GLMakie/src/GLAbstraction/GLRenderObject.jl
+++ b/GLMakie/src/GLAbstraction/GLRenderObject.jl
@@ -30,7 +30,7 @@ function (sp::StandardPrerender)()
glDepthFunc(GL_LEQUAL)
end
- # Disable cullface for now, untill all rendering code is corrected!
+ # Disable cullface for now, until all rendering code is corrected!
glDisable(GL_CULL_FACE)
# glCullFace(GL_BACK)
diff --git a/GLMakie/src/GLAbstraction/GLTexture.jl b/GLMakie/src/GLAbstraction/GLTexture.jl
index 39d3cf3bb60..e0334e490f7 100644
--- a/GLMakie/src/GLAbstraction/GLTexture.jl
+++ b/GLMakie/src/GLAbstraction/GLTexture.jl
@@ -430,7 +430,7 @@ default_colorformat_sym(::Type{T}) where {T <: Colorant} = default_colorformat_s
@generated function default_colorformat(::Type{T}) where T
sym = default_colorformat_sym(T)
if !isdefined(ModernGL, sym)
- error("$T doesn't have a propper mapping to an OpenGL format")
+ error("$T doesn't have a proper mapping to an OpenGL format")
end
:($sym)
end
@@ -462,7 +462,7 @@ end
@generated function default_internalcolorformat(::Type{T}) where T
sym = default_internalcolorformat_sym(T)
if !isdefined(ModernGL, sym)
- error("$T doesn't have a propper mapping to an OpenGL format")
+ error("$T doesn't have a proper mapping to an OpenGL format")
end
:($sym)
end
diff --git a/GLMakie/src/GLAbstraction/GLTypes.jl b/GLMakie/src/GLAbstraction/GLTypes.jl
index b9ce99f0167..b580152fcb9 100644
--- a/GLMakie/src/GLAbstraction/GLTypes.jl
+++ b/GLMakie/src/GLAbstraction/GLTypes.jl
@@ -401,7 +401,7 @@ function RenderObject(
program = gl_convert(to_value(program), data) # "compile" lazyshader
vertexarray = GLVertexArray(Dict(buffers), program)
- # remove all uniforms not occuring in shader
+ # remove all uniforms not occurring in shader
# ssao, instances transparency are special for rendering passes. TODO do this more cleanly
special = Set([:ssao, :transparency, :instances, :fxaa, :num_clip_planes])
for k in setdiff(keys(data), keys(program.nametype))
diff --git a/GLMakie/src/GLAbstraction/GLUtils.jl b/GLMakie/src/GLAbstraction/GLUtils.jl
index 4176bdc97b9..5fe35eda5c7 100644
--- a/GLMakie/src/GLAbstraction/GLUtils.jl
+++ b/GLMakie/src/GLAbstraction/GLUtils.jl
@@ -28,7 +28,7 @@ gen_defaults! dict begin
a = 55
b = a * 2 # variables, like a, will get made visible in local scope
c::JuliaType = X # `c` needs to be of type JuliaType. `c` will be made available with it's original type and then converted to JuliaType when inserted into `dict`
- d = x => GLType # OpenGL convert target. Get's only applied if `x` is convertible to GLType. Will only be converted when passed to RenderObject
+ d = x => GLType # OpenGL convert target. Gets only applied if `x` is convertible to GLType. Will only be converted when passed to RenderObject
d = x => \"doc string\"
d = x => (GLType, \"doc string and gl target\")
end
@@ -39,12 +39,12 @@ macro gen_defaults!(dict, args)
a = 55
b = a * 2 # variables, like a, will get made visible in local scope
c::JuliaType = X # c needs to be of type JuliaType. c will be made available with it's original type and then converted to JuliaType when inserted into data
- d = x => GLType # OpenGL convert target. Get's only applied if x is convertible to GLType. Will only be converted when passed to RenderObject
+ d = x => GLType # OpenGL convert target. Gets only applied if x is convertible to GLType. Will only be converted when passed to RenderObject
end")
tuple_list = args.args
dictsym = gensym()
return_expression = Expr(:block)
- push!(return_expression.args, :($dictsym = $dict)) # dict could also be an expression, so we need to asign it to a variable at the beginning
+ push!(return_expression.args, :($dictsym = $dict)) # dict could also be an expression, so we need to assign it to a variable at the beginning
push!(return_expression.args, :(gl_convert_targets = get!($dictsym, :gl_convert_targets, Dict{Symbol, Any}()))) # exceptions for glconvert.
push!(return_expression.args, :(doc_strings = get!($dictsym, :doc_string, Dict{Symbol, Any}()))) # exceptions for glconvert.
# @gen_defaults can be used multiple times, so we need to reuse gl_convert_targets if already in here
diff --git a/GLMakie/src/glshaders/lines.jl b/GLMakie/src/glshaders/lines.jl
index c4fa81d8cf8..9f29d47d8b7 100644
--- a/GLMakie/src/glshaders/lines.jl
+++ b/GLMakie/src/glshaders/lines.jl
@@ -1,5 +1,5 @@
function sumlengths(points, resolution)
- # normalize w component if availabke
+ # normalize w component if available
f(p::VecTypes{4}) = p[Vec(1, 2)] / p[4]
f(p::VecTypes) = p[Vec(1, 2)]
@@ -45,10 +45,10 @@ function generate_indices(positions)
# if A != F (no loop): 0 A B C D E F 0
# where 0 is NaN
# It marks vertices as invalid (0) if they are NaN, valid (1) if they
- # are part of a continous line section, or as ghost edges (2) used to
+ # are part of a continuous line section, or as ghost edges (2) used to
# cleanly close a loop. The shader detects successive vertices with
# 1-2-0 and 0-2-1 validity to avoid drawing ghost segments (E-A from
- # 0-E-A-B and F-B from E-F-B-0 which would dublicate E-F and A-B)
+ # 0-E-A-B and F-B from E-F-B-0 which would duplicate E-F and A-B)
last_start_pos = eltype(ps)(NaN)
last_start_idx = -1
@@ -60,7 +60,7 @@ function generate_indices(positions)
if not_nan
if last_start_idx == -1
# place nan before section of line vertices
- # (or dublicate ps[1])
+ # (or duplicate ps[1])
push!(indices, i-1)
last_start_idx = length(indices) + 1
last_start_pos = p
diff --git a/GLMakie/src/screen.jl b/GLMakie/src/screen.jl
index 6188b400bd3..31bd584a089 100644
--- a/GLMakie/src/screen.jl
+++ b/GLMakie/src/screen.jl
@@ -571,7 +571,7 @@ function destroy!(rob::RenderObject)
# but we do share the texture atlas, so we check v !== tex, since we can't just free shared resources
# TODO, refcounting, or leaving freeing to GC...
- # GC is a bit tricky with active contexts, so immediate free is prefered.
+ # GC is a bit tricky with active contexts, so immediate free is preferred.
# I guess as long as we make it hard for users to share buffers directly, this should be fine!
GLAbstraction.free(v)
end
@@ -978,7 +978,7 @@ function renderloop(screen)
end
if screen.close_after_renderloop
try
- @debug("Closing screen after quiting renderloop!")
+ @debug("Closing screen after quitting renderloop!")
close(screen)
catch e
@warn "error closing screen" exception=(e, Base.catch_backtrace())
diff --git a/GLMakie/test/glmakie_refimages.jl b/GLMakie/test/glmakie_refimages.jl
index d4e911474db..afc81dd8c17 100644
--- a/GLMakie/test/glmakie_refimages.jl
+++ b/GLMakie/test/glmakie_refimages.jl
@@ -10,7 +10,7 @@ using ReferenceTests.RNG
# Directly access texture parameters:
x = Sampler(fill(to_color(:yellow), 100, 100), minfilter=:nearest)
scene = image(x)
- # indexing will go straight to the GPU, while only transfering the changes
+ # indexing will go straight to the GPU, while only transferring the changes
st = Stepper(scene)
x[1:10, 1:50] .= to_color(:red)
Makie.step!(st)
diff --git a/GLMakie/test/unit_tests.jl b/GLMakie/test/unit_tests.jl
index 6e08a85aba3..d62843af195 100644
--- a/GLMakie/test/unit_tests.jl
+++ b/GLMakie/test/unit_tests.jl
@@ -133,7 +133,7 @@ end
end
end
-@testset "emtpy!(fig)" begin
+@testset "empty!(fig)" begin
GLMakie.closeall()
fig = Figure()
ax = Axis(fig[1,1])
diff --git a/MakieCore/src/conversion.jl b/MakieCore/src/conversion.jl
index 16c450ffc17..09d91fd9f9b 100644
--- a/MakieCore/src/conversion.jl
+++ b/MakieCore/src/conversion.jl
@@ -139,7 +139,7 @@ to be overloaded for DimConversions, e.g. for CategoricalConversion:
`has_typed_convert(plot_or_trait)` and `should_dim_convert(get_element_type(args))`
are true. The former is defined as true by `@convert_target`, i.e. when
`convert_arguments_typed` is defined for the given plot type or conversion trait.
-The latter marks specific types as convertable.
+The latter marks specific types as convertible.
If a recipe wants to use dim conversions, it should overload this function:
```julia
diff --git a/RPRMakie/src/meshes.jl b/RPRMakie/src/meshes.jl
index d635d58a8f7..b77138d10f2 100644
--- a/RPRMakie/src/meshes.jl
+++ b/RPRMakie/src/meshes.jl
@@ -167,7 +167,7 @@ function to_rpr_object(context, matsys, scene, plot::Makie.Surface)
positions = lift(grid, x, y, z, Makie.transform_func_obs(plot))
r = Tesselation(Rect2f((0, 0), (1, 1)), size(z[]))
# decomposing a rectangle into uv and triangles is what we need to map the z coordinates on
- # since the xyz data assumes the coordinates to have the same neighouring relations
+ # since the xyz data assumes the coordinates to have the same neighbouring relations
# like a grid
faces = decompose(GLTriangleFace, r)
uv = decompose_uv(r)
diff --git a/ReferenceTests/src/tests/categorical.jl b/ReferenceTests/src/tests/categorical.jl
index bd2bd4865cb..4740ac2a4a4 100644
--- a/ReferenceTests/src/tests/categorical.jl
+++ b/ReferenceTests/src/tests/categorical.jl
@@ -11,7 +11,7 @@ using Makie: Categorical
end
@reference_test "different types without sorting function" begin
- # If we set the ticks explicitely, with sortby defaulting to nothing,
+ # If we set the ticks explicitly, with sortby defaulting to nothing,
# we can combine all objects:
f = Figure()
ax = Axis(f[1, 1];
@@ -42,7 +42,7 @@ end
f
end
-@reference_test "new categories, inbetween old values" begin
+@reference_test "new categories, in between old values" begin
obs = Observable(Categorical(["a", "c", "e", "g"]))
f, ax, p = scatter(1:4, obs, markersize=20, color=1:4, colormap=:viridis)
obs[] = Categorical(["b", "d", "f", "h"])
diff --git a/ReferenceTests/src/tests/primitives.jl b/ReferenceTests/src/tests/primitives.jl
index f13448dd51d..919e74e903b 100644
--- a/ReferenceTests/src/tests/primitives.jl
+++ b/ReferenceTests/src/tests/primitives.jl
@@ -573,7 +573,7 @@ end
campixel!(scene)
# marker is in front, so it should not be smaller than the background rectangle
plot_row!(scene, 0, false)
- # marker is in the background, so one shouldnt see a single pixel of the marker
+ # marker is in the background, so one shouldn't see a single pixel of the marker
plot_row!(scene, 300, true)
center = Point2f(size(scene) ./ 2)
diff --git a/ReferenceUpdater/src/local_server.jl b/ReferenceUpdater/src/local_server.jl
index 4496911129d..668af3fd6d9 100644
--- a/ReferenceUpdater/src/local_server.jl
+++ b/ReferenceUpdater/src/local_server.jl
@@ -276,7 +276,7 @@ function group_files(path, input_filename, output_filename)
end
end
- # generate new structed file
+ # generate new structured file
open(joinpath(path, output_filename), "w") do file
for (filename, valid) in data
println(file,
diff --git a/ReferenceUpdater/src/reference_images.html b/ReferenceUpdater/src/reference_images.html
index 29f4f69e309..61af9f3e80f 100644
--- a/ReferenceUpdater/src/reference_images.html
+++ b/ReferenceUpdater/src/reference_images.html
@@ -57,7 +57,7 @@ Images with references
This is the normal case where the selected CI run produced an image and the reference image exists.
Each row shows one image per backend from the same reference image test, which can be compared with its reference image.
Rows are sorted based on the maximum row score (bigger = more different).
- Red cells fail CI (assuming the thresholds are up to date), yellow cells may but likely don't have signficant visual difference and gray cells are visually equivalent.
+ Red cells fail CI (assuming the thresholds are up to date), yellow cells may but likely don't have significant visual difference and gray cells are visually equivalent.