From 2688773458ce670cf61aac78ef1ff791bfd25b70 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com> Date: Sat, 15 Jun 2024 21:01:09 +0200 Subject: [PATCH 1/6] Fix scaling factor for svgs (#3964) * fix scaling factor for svgs * add changelog --- CHANGELOG.md | 1 + CairoMakie/Project.toml | 2 ++ CairoMakie/src/screen.jl | 10 +++++++--- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cf3986ce6c..790ac9498c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [Unreleased] - CairoMakie: Add argument `pdf_version` to restrict the PDF version when saving a figure as a PDF [#3845](https://github.com/MakieOrg/Makie.jl/pull/3845). +- CairoMakie: Fix incorrect scaling factor for SVGs with Cairo_jll 1.18 [#3964](https://github.com/MakieOrg/Makie.jl/pull/3964). ## [0.21.2] - 2024-05-22 diff --git a/CairoMakie/Project.toml b/CairoMakie/Project.toml index 3e4495fd8b5..aca69ddd2d3 100644 --- a/CairoMakie/Project.toml +++ b/CairoMakie/Project.toml @@ -6,6 +6,7 @@ version = "0.12.2" [deps] CRC32c = "8bf52ea8-c179-5cab-976a-9e18b702a9bc" Cairo = "159f3aea-2a34-519c-b102-8c37f9878175" +Cairo_jll = "83423d85-b0ee-5818-9007-b63ccbeb887a" Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" FreeType = "b38be410-82b0-50bf-ab77-7b57e271db43" @@ -17,6 +18,7 @@ PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a" [compat] CRC32c = "1.0, 1.6" Cairo = "1.0.4" +Cairo_jll = "1.18.0" Colors = "0.10, 0.11, 0.12" FileIO = "1.1" FreeType = "3, 4.0" diff --git a/CairoMakie/src/screen.jl b/CairoMakie/src/screen.jl index af5b7042a26..d9d23959c6d 100644 --- a/CairoMakie/src/screen.jl +++ b/CairoMakie/src/screen.jl @@ -112,13 +112,17 @@ struct ScreenConfig end end +css_px_per_unit(pt_per_unit) = pt_per_unit / 0.75 + function device_scaling_factor(rendertype, sc::ScreenConfig) - isv = is_vector_backend(convert(RenderType, rendertype)) - return isv ? sc.pt_per_unit : sc.px_per_unit + rt = convert(RenderType, rendertype) + isv = is_vector_backend(rt) + # from version 1.18 on, Cairo saves SVGs without the pt unit specified, so they are actually in CSS px now + return rt === SVG ? css_px_per_unit(sc.pt_per_unit) : isv ? sc.pt_per_unit : sc.px_per_unit end function device_scaling_factor(surface::Cairo.CairoSurface, sc::ScreenConfig) - return is_vector_backend(surface) ? sc.pt_per_unit : sc.px_per_unit + return device_scaling_factor(get_render_type(surface), sc) end const LAST_INLINE = Ref{Union{Makie.Automatic,Bool}}(Makie.automatic) From ebfe6fd8309b151d66df14f3388904d14e662bcc Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com> Date: Sat, 15 Jun 2024 23:39:48 +0200 Subject: [PATCH 2/6] Overhaul Getting started (#3965) --- docs/makedocs.jl | 3 +- docs/src/index.md | 4 +- docs/src/tutorials/basic-tutorial.md | 457 -------------------------- docs/src/tutorials/getting-started.md | 211 ++++++++++++ 4 files changed, 215 insertions(+), 460 deletions(-) delete mode 100644 docs/src/tutorials/basic-tutorial.md create mode 100644 docs/src/tutorials/getting-started.md diff --git a/docs/makedocs.jl b/docs/makedocs.jl index f6f67f3230e..03cf85c6043 100644 --- a/docs/makedocs.jl +++ b/docs/makedocs.jl @@ -144,7 +144,7 @@ pages = [ ] ], "Tutorials" => [ - "tutorials/basic-tutorial.md", + "tutorials/getting-started.md", "tutorials/aspect-tutorial.md", "tutorials/layout-tutorial.md", "tutorials/scenes.md", @@ -230,6 +230,7 @@ generate_redirects([ "/explanations/observables.html" => "/explanations/nodes/index.html", "/reference/plots/overview.html" => "/reference/plots/index.html", "/reference/blocks/overview.html" => "/reference/blocks/index.html", + "/tutorials/getting-started.html" => "/tutorials/basic-tutorial.html", ], dry_run = false) deploy(params; target = "build") diff --git a/docs/src/index.md b/docs/src/index.md index 1b56bd28d3b..a7b815051e2 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -13,7 +13,7 @@ hero: actions: - theme: brand text: Getting started - link: /tutorials/basic-tutorial + link: /tutorials/getting-started - theme: alt text: View on Github link: https://github.com/MakieOrg/Makie.jl @@ -107,7 +107,7 @@ There's no need to install `Makie.jl` separately, it is re-exported by each back ## First Steps -If you are new to Makie, have a look at [Getting started with Makie](@ref). +If you are new to Makie, have a look at [Getting started](@ref). For inspiration, visit [Beautiful Makie](https://beautiful.makie.org/) for a collection of interesting plots. diff --git a/docs/src/tutorials/basic-tutorial.md b/docs/src/tutorials/basic-tutorial.md deleted file mode 100644 index 64a8c6d2513..00000000000 --- a/docs/src/tutorials/basic-tutorial.md +++ /dev/null @@ -1,457 +0,0 @@ -# Getting started with Makie - -## Preface - -Here is a quick tutorial to get you started with Makie! - -Makie is the name of the whole plotting ecosystem and `Makie.jl` is the main package that describes how plots work. -To actually render and save plots, we need a backend that knows how to translate plots into images or vector graphics. - -There are three main backends which you can use to render plots (for more information, have a look at [What is a backend](@ref)): - -- `CairoMakie.jl` if you want to render vector graphics or high quality 2D images and don't need interactivity or true 3D rendering. -- `GLMakie.jl` if you need interactive windows and true 3D rendering but no vector output. -- Or `WGLMakie.jl` which is similar to `GLMakie` but works in web browsers, not native windows. - -This tutorial uses CairoMakie, but the code can be executed with any backend. -Note that CairoMakie can _create_ images but it cannot _display_ them. - -To see the output of plotting commands when using CairoMakie, we recommend you either use an IDE which supports png or svg output, such as VSCode, Atom/Juno, Jupyter, Pluto, etc., or try using a viewer package such as [ElectronDisplay.jl](https://github.com/queryverse/ElectronDisplay.jl), or alternatively save your plots to files directly. -The Julia REPL by itself does not have the ability to show the plots. - -Ok, now that this is out of the way, let's get started! - -## Importing - -First, we import CairoMakie. This makes all the exported symbols from `Makie.jl` available as well. - -```@example basic -using CairoMakie -CairoMakie.activate!() # hide - -nothing # hide -``` - -## Important objects - -The objects most important for our first steps with Makie are the `Figure`, the `Axis` and plots. -In a normal Makie plot you will usually find a `Figure` which contains an `Axis` which contains one or more plot objects like `Lines` or `Scatter`. - -In the next steps, we will take a look at how we can create these objects. - -## An empty figure - -The basic container object in Makie is the [`Figure`](@ref). -It is a canvas onto which we can add objects like `Axis`, `Colorbar`, `Legend` and others. - -Let's create a `Figure` and give it a background color other than the default white so we can see it. -Returning a `Figure` from an expression will `display` it if your coding environment can show images. - -```@figure basic -f = Figure(backgroundcolor = :tomato) -``` - -Another common thing to do is to give a figure a different size. -The default is 800x600, let's try halving the height: - -```@figure basic -f = Figure(backgroundcolor = :tomato, size = (800, 300)) -``` - -## Adding an Axis - -The most common object you can add to a figure which you need for most plotting is the [Axis](@ref). -The usual syntax for adding such an object to a figure is to specify a position in the `Figure`'s layout as the first argument. -We'll learn more about layouts later, but for now the position `f[1, 1]` will just fill the whole figure. - -```@figure basic -f = Figure() -ax = Axis(f[1, 1]) -f -``` - -The default axis has no title or labels, you can pass those as keyword arguments. -For a whole list of available attributes, check the docstring for [`Axis`](@ref) (you can also do that by running `?Axis` in the REPL). -Be warned, it's very long! - -```@figure basic -f = Figure() -ax = Axis(f[1, 1], - title = "A Makie Axis", - xlabel = "The x label", - ylabel = "The y label" -) -f -``` - -## Adding a plot to an Axis - -Now we're ready to actually plot something into an `Axis`! - -Makie has many different plotting functions, the first we will learn about is [`lines!`](@ref). -Let's try plotting a sine function into an `Axis`, by passing it as the first argument: - -```@figure basic -f = Figure() -ax = Axis(f[1, 1]) -x = range(0, 10, length=100) -y = sin.(x) -lines!(ax, x, y) -f -``` - -There we have our first line plot. - -## Scatter plot - -Another common function is [`scatter!`](@ref). -It works very similar to [`lines!`](@ref) but shows separate markers for each input point. - -```@figure basic -f = Figure() -ax = Axis(f[1, 1]) -x = range(0, 10, length=100) -y = sin.(x) -scatter!(ax, x, y) -f -``` - -## Creating Figure, Axis and plot in one call - -So far we have seen how to plot into an existing `Axis` with `lines!` and `scatter!`. - -However, it would be nice if we didn't have to explicitly create `Figure` and `Axis` for every plot that we're making. - -That's why every plotting function comes in a pair, one version that plots into an existing `Axis` and one that creates its own `Axis` implicitly for convenience. -For example, `lines!` mutates an existing `Axis`, `lines` creates an implicit one, `scatter!` mutates, `scatter` does not, and so on. - -Let's see how to make a line plot without creating `Figure` and `Axis` ourselves first. - -```@figure basic -x = range(0, 10, length=100) -y = sin.(x) -lines(x, y) -``` - -The return type of `lines(x, y)` is `FigureAxisPlot`. -The `lines` function first creates a `Figure`, then puts an `Axis` into it and finally adds a plot of type `Lines` to that axis. - -Because these three objects are created at once, the function returns all three, just bundled up into one `FigureAxisPlot` object. -That's just so we can overload the `display` behavior for that type to match `Figure`. -Normally, multiple return values are returned as `Tuple`s in Julia but it's uncommon to overload `display` for `Tuple` types. - -If you need the objects, for example to add more things to the figure later and edit axis and plot attributes, you could destructure the return value: - -```@figure basic -figure, axis, lineplot = lines(x, y) -figure -``` - -As you can see, the output of returning the extracted figure is the same. - -## Passing Figure and Axis styles - -You might wonder how to specify a different resolution for this scatter plot, or set an axis title and labels. -Because a normal plotting function like `lines` or `scatter` creates these objects before it creates the plot, you can pass special keyword arguments to it called `axis` and `figure`. -You can pass any kind of object with symbol-value pairs and these will be used as keyword arguments for `Figure` and `Axis`, respectively. - -```@figure basic -x = range(0, 10, length=100) -y = sin.(x) -scatter(x, y; - figure = (; size = (400, 400)), - axis = (; title = "Scatter plot", xlabel = "x label") -) -``` - -The `;` in `(; size = (400, 400))` is nothing special, it just clarifies that we want a one-element `NamedTuple` and not a variable called `size`. -It's good habit to include it but it's not needed for `NamedTuple`s with more than one entry. - -## Argument conversions - -So far we have called `lines` and `scatter` with `x` and `y` arguments, where `x` was a range object and `y` vector of numbers. -Most plotting functions have different options how you can call them. -The input arguments are converted internally to one or more target representations that can be handled by the rendering backends. - -Here are a few different examples of what you can use with `lines`: - -An interval and a function: - -```@figure basic -lines(0..10, sin) -``` - -A collection of numbers and a function: - -```@figure basic -lines(0:1:10, cos) -``` - -A collection of `Point`s from `GeometryBasics.jl` (which supplies most geometric primitives in Makie): - -```@figure basic -lines([Point(0, 0), Point(5, 10), Point(10, 5)]) -``` - -The input arguments you can use with `lines` and `scatter` are mostly the same because they have the same conversion trait `PointBased`. -Other plotting functions have different conversion traits, [heatmap](@ref) for example expects two-dimensional grid data. -The respective trait is called `CellGrid`. - -## Layering multiple plots - -As we've seen above, every plotting function has a version with and one without `!` at the end. -For example, there's `scatter` and `scatter!`, `lines` and `lines!`, etc. - -To plot two things into the same axis, you can use the mutating plotting functions like `lines!` and `scatter!`. -For example, here's how you could plot two lines on top of each other: - -```@figure basic -x = range(0, 10, length=100) - -f, ax, l1 = lines(x, sin) -l2 = lines!(ax, x, cos) -f -``` - -The second `lines!` call plots into the axis created by the first `lines` call. -It's colored differently because the `Axis` keeps track of what has been plotted into it and cycles colors for similar plotting functions. - -You can also leave out the axis argument for convenience, then the axis being used is the `current_axis()`, which is usually just the axis that was created last. - -```@figure basic -x = range(0, 10, length=100) - -f, ax, l1 = lines(x, sin) -lines!(x, cos) -f -``` - -Note that you cannot pass `figure` and `axis` keywords to mutating plotting functions like `lines!` or `scatter!`. -That's because they don't create an `Figure` and `Axis`, and we chose not to allow modification of the existing objects in plotting calls so it's clearer what is going on. - -## Attributes - -Every plotting function has attributes which you can set through keyword arguments. -The lines in the previous example have colors from Makie's default palette, but we can easily specify our own. - -There are multiple ways you can specify colors, but common ones are: - -- By name, like `:red` or `"red"` -- By hex string, like `"#ffccbk"` -- With color types like the Makie-exported `RGBf(0.5, 0, 0.6)` or `RGBAf(0.3, 0.8, 0.2, 0.8)` -- As a tuple where the first part is a color and the second an alpha value to make it transparent, like `(:red, 0.5)` - -You can read more about colors at [juliagraphics.github.io/Colors.jl](https://juliagraphics.github.io/Colors.jl). - -Here's a plot with one named color and one where we use `RGBf`: - -```@figure basic -x = range(0, 10, length=100) - -f, ax, l1 = lines(x, sin, color = :tomato) -l2 = lines!(ax, x, cos, color = RGBf(0.2, 0.7, 0.9)) -f -``` - -Other plotting functions have different attributes. -The function `scatter`, for example, does not only have the `color` attribute, but also a `markersize` attribute. - -You can read about all possible attributes by running `?scatter` in the REPL, and examples are shown on the page [scatter](@ref). - -```@figure basic -x = range(0, 10, length=100) - -f, ax, sc1 = scatter(x, sin, color = :red, markersize = 5) -sc2 = scatter!(ax, x, cos, color = :blue, markersize = 10) -f -``` - -You can also manipulate most plot attributes afterwards with the syntax `plot.attribute = new_value`. - -```@figure basic -sc1.marker = :utriangle -sc1.markersize = 20 - -sc2.color = :transparent -sc2.markersize = 20 -sc2.strokewidth = 1 -sc2.strokecolor = :purple - -f -``` - -## Array attributes - -A lot of attributes can be set to either a single value or an array with as many elements as there are data points. -For example, it is usually much more performant to draw many points with one scatter object, than to create many scatter objects with one point each. - -Here, we vary markersize and color: - -```@figure basic -x = range(0, 10, length=100) - -scatter(x, sin, - markersize = range(5, 15, length=100), - color = range(0, 1, length=100), - colormap = :thermal -) -``` - -Note that the color array does not actually contain colors, rather the numerical values are mapped to the plot's `colormap`. -There are many different colormaps to choose from, take a look on the [Colors](@ref) page. - -The values are mapped to colors via the `colorrange` attribute, which by default goes from the minimum to the maximum color value. -But we can also limit or expand the range manually. -For example, we can constrain the previous scatter plot's color range to (0.33, 0.66), which will clip the colors at the bottom and the top. - -```@figure basic -x = range(0, 10, length=100) - -scatter(x, sin, - markersize = range(5, 15, length=100), - color = range(0, 1, length=100), - colormap = :thermal, - colorrange = (0.33, 0.66) -) -``` - -Of course you can also use an array of colors directly, in which case the `colorrange` is ignored: - -```@figure basic - -using CairoMakie - -x = range(0, 10, length=100) - -colors = repeat([:crimson, :dodgerblue, :slateblue1, :sienna1, :orchid1], 20) - -scatter(x, sin, color = colors, markersize = 20) -``` - -## Simple legend - -If you add label attributes to your plots, you can call the `axislegend` function to add a `Legend` with all labeled plots to the current `Axis`, or optionally to one you pass as the first argument. - -```@figure basic - -using CairoMakie - -x = range(0, 10, length=100) - -lines(x, sin, color = :red, label = "sin") -lines!(x, cos, color = :blue, label = "cos") -axislegend() -current_figure() -``` - -## Subplots - -Makie uses a powerful layout system under the hood, which allows you to create very complex figures with many subplots. -So far, we have only used the default position [1, 1], where the Axis is created in a standard plotting call. - -We can make subplots by giving the location of the subplot in our layout grid as the first argument to our plotting function. -The basic syntax for specifying the location in a figure is `fig[row, col]`. - -```@figure basic - -using CairoMakie - -x = LinRange(0, 10, 100) -y = sin.(x) - -fig = Figure() -lines(fig[1, 1], x, y, color = :red) -lines(fig[1, 2], x, y, color = :blue) -lines(fig[2, 1:2], x, y, color = :green) - -fig -``` - -Each `lines` call creates a new axis in the position given as the first argument, that's why we use `lines` and not `lines!` here. - -We can also create a couple of axes manually at first and then plot into them later. -For example, we can create a figure with three axes. - -```@figure basic - -using CairoMakie - -fig = Figure() -ax1 = Axis(fig[1, 1]) -ax2 = Axis(fig[1, 2]) -ax3 = Axis(fig[2, 1:2]) -fig -``` - -And then we can continue to plot into these empty axes. - -```@figure basic - -lines!(ax1, 0..10, sin) -lines!(ax2, 0..10, cos) -lines!(ax3, 0..10, sqrt) -fig -``` - -## Legend and Colorbar - -We have seen two `Blocks` so far, the [Axis](@ref) and the [Legend](@ref) which was created by the function `axislegend`. -All `Block`s can be placed into the layout of a figure at arbitrary positions, which makes it easy to assemble complex figures. - -In the same way as with the [Axis](@ref) before, you can also create a [Legend](@ref) manually and then place it freely, wherever you want, in the figure. -There are multiple ways to create [Legend](@ref)s, for one of them you pass one vector of plot objects and one vector of label strings. - -You can see here that we can deconstruct the return value from the two `lines` calls into one newly created axis and one plot object each. -We can then feed the plot objects to the legend constructor. -We place the legend in the second column and across both rows, which centers it nicely next to the two axes. - -```@figure basic - -using CairoMakie - -fig = Figure() -ax1, l1 = lines(fig[1, 1], 0..10, sin, color = :red) -ax2, l2 = lines(fig[2, 1], 0..10, cos, color = :blue) -Legend(fig[1:2, 2], [l1, l2], ["sin", "cos"]) -fig -``` - -The [Colorbar](@ref) works in a very similar way. -We just need to pass a position in the figure to it, and one plot object. -In this example, we use a `heatmap`. - -You can see here that we split the return value of `heatmap` into three parts: the newly created figure, the axis and the heatmap plot object. -This is useful as we can then continue with the figure `fig` and the heatmap `hm` which we need for the colorbar. - -```@figure basic - -using CairoMakie - -fig, ax, hm = heatmap(randn(20, 20)) -Colorbar(fig[1, 2], hm) -fig -``` - -The previous short syntax is basically equivalent to this longer, manual version. -You can switch between those workflows however you please. - -```@figure basic - -using CairoMakie - -fig = Figure() -ax = Axis(fig[1, 1]) -hm = heatmap!(ax, randn(20, 20)) -Colorbar(fig[1, 2], hm) -fig -``` - -## Next steps - -We've only looked at a small subset of Makie's functionality here. - -If you want to learn about making complex figures with nested sublayouts, have a look at the tutorial [Creating complex layouts](@ref). - -If you're interested in creating interactive visualizations that use Makie's special `Observables` workflow, this is explained in more detail in the [Observables](@ref) section. - -If you want to create animated movies, you can find more information in the [Animations](@ref) section. diff --git a/docs/src/tutorials/getting-started.md b/docs/src/tutorials/getting-started.md new file mode 100644 index 00000000000..960d6299fe8 --- /dev/null +++ b/docs/src/tutorials/getting-started.md @@ -0,0 +1,211 @@ +# Getting started + +Welcome to Makie, the data visualization ecosystem for the Julia language! + +This tutorial will show you how to get set up and create plots like this: + +![](./first_figure.svg) + +## Requirements + +You only need to have a reasonably recent Julia installed on your system. +If you don't have that, yet, follow the directions at [julialang.org/downloads/](https://julialang.org/downloads/). Makie is available for Windows, Mac and Linux. + +## Installation + +We will be using the [CairoMakie](@ref) package in this tutorial. + +Makie offers multiple [backend packages](@ref "What is a backend") that each have different strengths. +CairoMakie is good at static 2D graphics and it should run on most computers as it uses only the CPU and does not need a GPU. + +First, create an empty folder somewhere on your system and call it `makie_tutorial`. +We are going to use that folder to install CairoMakie and to save plots. + +Installing CairoMakie works the same as with any other typical Julia package. +First, start a Julia REPL somewhere, for example in your terminal or IDE. + +Next, switch the active working directory to the `makie_tutorial` folder. +Execute this command in the REPL, but be sure to replace the path with the location where you created the `makie_tutorial` folder, for example: + +```julia +cd("path/to/the/folder/makie_tutorial") +``` + +We want to start from a clean slate, so we install CairoMakie into a new environment which we are going to create in our new, empty folder. +This way, we can be sure that there can be no problems because of other, already installed Julia packages. + +First, make the `Pkg` package manager library available + +```julia +using Pkg +``` + +Next, activate the current directory, denoted by `"."` (this means our `makie_tutorial` folder) as the active Pkg environment: + +```julia +Pkg.activate(".") +``` + +Now, we can install CairoMakie and all its dependencies by running: + +```julia +Pkg.add("CairoMakie") +``` + +This command will probably take a while to finish. +After it has completed, you should find a `Project.toml` and a `Manifest.toml` file in the `makie_tutorial` folder. +Those files "are" your new environment, the actual packages you have just installed are stored somewhere else, in a central location. + +If everything has worked, you should be able to load CairoMakie now: + +```@example tutorial +using CairoMakie +``` + +Congratulations, now we can start plotting! + +## Plotting + +Run these two lines to make the "data" for our first plot available in your Julia session. +It represents some imaginary measurements made over the span of two seconds. + +```@example tutorial +seconds = 0:0.1:2 +measurements = [8.2, 8.4, 6.3, 9.5, 9.1, 10.5, 8.6, 8.2, 10.5, 8.5, 7.2, + 8.8, 9.7, 10.8, 12.5, 11.6, 12.1, 12.1, 15.1, 14.7, 13.1] +nothing # hide +``` + +Let's have a first look at this data as a line plot. +Line plots are created with the [lines](@ref) function in Makie. + +```@figure tutorial +lines(seconds, measurements) +``` + +!!! info + Returning `lines(seconds, measurements)` in the REPL should show you the plot in some form. + Which form it is depends on the context in which you have your Julia REPL running. + + If you are in an IDE like VSCode with the Julia extension installed, the plot pane might have opened. + If no other display is found, your OS's image viewing application or a browser should show the image. + +Let's try another plot function, to show each data point as a separate marker. +The right function for that is [scatter](@ref). + +```@figure tutorial +scatter(seconds, measurements) +``` + +Our goal is to show the measurement data together with a line representing an exponential fit. +Let us pretend that the function we have "fit" is `f(x) = exp(x) + 7`. +We can plot it as a line like this: + +```@figure tutorial +lines(seconds, exp.(seconds) .+ 7) +``` + +Now, we'd like to have the scatter and lines plots layered on top of each other. + +You can plot into an existing axis with plotting functions that end with a `!`: + +```@figure tutorial +scatter(seconds, measurements) +lines!(seconds, exp.(seconds) .+ 7) +current_figure() +``` + +## Figure and Axis + +So far, we have used two important objects in Makie only implicitly, the [Figure](@ref Figures) and the [Axis](@ref). + +The `Figure` is the outermost container object. And an `Axis` is one type of axis object that can contain plots. An `Axis` can be placed in a `Figure` and then be plotted into. +Let's try the previous plot with this system: + +```@figure tutorial +f = Figure() +ax = Axis(f[1, 1]) +scatter!(ax, seconds, measurements) +lines!(ax, seconds, exp.(seconds) .+ 7) +f +``` + +Both `scatter!` and `lines!` now explicitly plot into an `Axis` which we put into a `Figure`. +`Axis(f[1, 1])` means that we put the `Axis` at the `Figure`'s layout at position row 1, column 1. + +We can now give our `Axis` a title, as well as x and y axis labels: + +```@figure tutorial +f = Figure() +ax = Axis(f[1, 1], + title = "Experimental data and exponential fit", + xlabel = "Time (seconds)", + ylabel = "Value", +) +scatter!(ax, seconds, measurements) +lines!(ax, seconds, exp.(seconds) .+ 7) +f +``` + +## Plot styling + +Plotting functions take many different style attributes as keyword arguments. +Let's change the color of both plots to a red called `:tomato`, and the line style to `:dash`: + +```@figure tutorial +f = Figure() +ax = Axis(f[1, 1], + title = "Experimental data and exponential fit", + xlabel = "Time (seconds)", + ylabel = "Value", +) +scatter!(ax, seconds, measurements, color = :tomato) +lines!(ax, seconds, exp.(seconds) .+ 7, color = :tomato, linestyle = :dash) +f +``` + +## Legend + +The last element we're missing is the legend. +One way to create a legend is by labelling plots with the `label` keyword and using the [`axislegend`](@ref) function: + +```@figure tutorial +f = Figure() +ax = Axis(f[1, 1], + title = "Experimental data and exponential fit", + xlabel = "Time (seconds)", + ylabel = "Value", +) +scatter!( + ax, + seconds, + measurements, + color = :tomato, + label = "Measurements" +) +lines!( + ax, + seconds, + exp.(seconds) .+ 7, + color = :tomato, + linestyle = :dash, + label = "f(x) = exp(x) + 7", +) +axislegend(position = :rb) +f +``` + +## Saving a Figure + +Once we are satisfied with our plot, we can save it to a file using the [`save`](@ref) function. +The most common formats are `png` for images and `svg` or `pdf` for vector graphics: + +```@example tutorial +save("first_figure.png", f) +save("first_figure.svg", f) +save("first_figure.pdf", f) +nothing # hide +``` + +You should now find the three files in your `makie_tutorial` folder. + From cae34cce5340f64297db245a42721a46d5334809 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com> Date: Sun, 16 Jun 2024 12:30:11 +0200 Subject: [PATCH 3/6] Simplify Getting started further (#3966) * simplify Getting started further * typo --- docs/src/tutorials/getting-started.md | 33 +++++++++++++-------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/docs/src/tutorials/getting-started.md b/docs/src/tutorials/getting-started.md index 960d6299fe8..a5a14b1f4db 100644 --- a/docs/src/tutorials/getting-started.md +++ b/docs/src/tutorials/getting-started.md @@ -8,39 +8,37 @@ This tutorial will show you how to get set up and create plots like this: ## Requirements -You only need to have a reasonably recent Julia installed on your system. -If you don't have that, yet, follow the directions at [julialang.org/downloads/](https://julialang.org/downloads/). Makie is available for Windows, Mac and Linux. +You only need an internet connection and a reasonably recent Julia installation. +If you don't have Julia installed, yet, follow the directions at [julialang.org/downloads/](https://julialang.org/downloads/). + +Makie is available for Windows, Mac and Linux. ## Installation We will be using the [CairoMakie](@ref) package in this tutorial. -Makie offers multiple [backend packages](@ref "What is a backend") that each have different strengths. -CairoMakie is good at static 2D graphics and it should run on most computers as it uses only the CPU and does not need a GPU. +!!! info + Makie offers multiple [backend packages](@ref "What is a backend") that each have different strengths. + CairoMakie is good at static 2D graphics and it should run on most computers as it uses only the CPU and does not need a GPU. -First, create an empty folder somewhere on your system and call it `makie_tutorial`. +First, create a new folder somewhere on your system and call it `makie_tutorial`. We are going to use that folder to install CairoMakie and to save plots. -Installing CairoMakie works the same as with any other typical Julia package. -First, start a Julia REPL somewhere, for example in your terminal or IDE. +Now, start Julia, for example by executing the command `julia` in a terminal. -Next, switch the active working directory to the `makie_tutorial` folder. -Execute this command in the REPL, but be sure to replace the path with the location where you created the `makie_tutorial` folder, for example: +In the Julia REPL (the **R**ead-**E**val-**P**rint-**L**oop which is what Julia's command line interface is called), change the active working directory to the `makie_tutorial` folder by executing this command, but be sure to replace the path with the location where you created the `makie_tutorial` folder: ```julia cd("path/to/the/folder/makie_tutorial") ``` -We want to start from a clean slate, so we install CairoMakie into a new environment which we are going to create in our new, empty folder. -This way, we can be sure that there can be no problems because of other, already installed Julia packages. - -First, make the `Pkg` package manager library available +Now, make the `Pkg` package manager library available ```julia using Pkg ``` -Next, activate the current directory, denoted by `"."` (this means our `makie_tutorial` folder) as the active Pkg environment: +Next, activate the current directory, also called `"."` (this means our `makie_tutorial` folder), as a Pkg environment: ```julia Pkg.activate(".") @@ -52,9 +50,10 @@ Now, we can install CairoMakie and all its dependencies by running: Pkg.add("CairoMakie") ``` -This command will probably take a while to finish. -After it has completed, you should find a `Project.toml` and a `Manifest.toml` file in the `makie_tutorial` folder. -Those files "are" your new environment, the actual packages you have just installed are stored somewhere else, in a central location. +This command will probably take a while to finish. You will need an internet connection so all the necessary files can be downloaded. + +After this process has completed, you should find a `Project.toml` and a `Manifest.toml` file in the `makie_tutorial` folder. +Those files describe the new environment, the downloaded packages are stored somewhere else, in a central, shared location. If everything has worked, you should be able to load CairoMakie now: From 1346cb9aa422be8f6a590f31906a16589c47e3cd Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com> Date: Mon, 17 Jun 2024 10:28:29 +0200 Subject: [PATCH 4/6] Fix broken SVGs by not using `Cairo.pattern_set_extend(p, Cairo.EXTEND_PAD)` (#3967) * Fix broken SVGs by not using `Cairo.pattern_set_extend(p, Cairo.EXTEND_PAD)` * add changelog --- CHANGELOG.md | 1 + CairoMakie/src/infrastructure.jl | 9 ++++++--- CairoMakie/src/primitives.jl | 9 ++++++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 790ac9498c0..6433b0d463a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## [Unreleased] +- CairoMakie: Fix broken SVGs when using non-interpolated image primitives, for example Colorbars, with recent Cairo versions [#3967](https://github.com/MakieOrg/Makie.jl/pull/3967). - CairoMakie: Add argument `pdf_version` to restrict the PDF version when saving a figure as a PDF [#3845](https://github.com/MakieOrg/Makie.jl/pull/3845). - CairoMakie: Fix incorrect scaling factor for SVGs with Cairo_jll 1.18 [#3964](https://github.com/MakieOrg/Makie.jl/pull/3964). diff --git a/CairoMakie/src/infrastructure.jl b/CairoMakie/src/infrastructure.jl index 46e0e3bcc2d..1b54a8fd8e5 100644 --- a/CairoMakie/src/infrastructure.jl +++ b/CairoMakie/src/infrastructure.jl @@ -143,7 +143,7 @@ end # instead of the whole Scene # - Recognize when a screen is an image surface, and set scale to render the plot # at the scale of the device pixel -function draw_plot_as_image(scene::Scene, screen::Screen, primitive::Plot, scale::Number = 1) +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 @@ -163,8 +163,11 @@ function draw_plot_as_image(scene::Scene, screen::Screen, primitive::Plot, scale # Cairo.scale(screen.context, w / scr.surface.width, h / scr.surface.height) Cairo.set_source_surface(screen.context, scr.surface, 0, 0) p = Cairo.get_source(scr.context) - # this is needed to avoid blurry edges - Cairo.pattern_set_extend(p, Cairo.EXTEND_PAD) + if RT !== SVG + # this is needed to avoid blurry edges in png renderings, however since Cairo 1.18 this + # setting seems to create broken SVGs + Cairo.pattern_set_extend(p, Cairo.EXTEND_PAD) + end # Set filter doesn't work!? Cairo.pattern_set_filter(p, Cairo.FILTER_BILINEAR) Cairo.fill(screen.context) diff --git a/CairoMakie/src/primitives.jl b/CairoMakie/src/primitives.jl index b594c79b11d..486afd9eea3 100644 --- a/CairoMakie/src/primitives.jl +++ b/CairoMakie/src/primitives.jl @@ -782,7 +782,7 @@ premultiplied_rgba(a::AbstractArray{<:Color}) = RGBA.(a) premultiplied_rgba(r::RGBA) = RGBA(r.r * r.alpha, r.g * r.alpha, r.b * r.alpha, r.alpha) premultiplied_rgba(c::Colorant) = premultiplied_rgba(RGBA(c)) -function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Union{Heatmap, Image})) +function draw_atomic(scene::Scene, screen::Screen{RT}, @nospecialize(primitive::Union{Heatmap, Image})) where RT ctx = screen.context image = primitive[3][] xs, ys = primitive[1][], primitive[2][] @@ -858,8 +858,11 @@ function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Unio Cairo.scale(ctx, w / s.width, h / s.height) Cairo.set_source_surface(ctx, s, 0, 0) p = Cairo.get_source(ctx) - # this is needed to avoid blurry edges - Cairo.pattern_set_extend(p, Cairo.EXTEND_PAD) + if RT !== SVG + # this is needed to avoid blurry edges in png renderings, however since Cairo 1.18 this + # setting seems to create broken SVGs + Cairo.pattern_set_extend(p, Cairo.EXTEND_PAD) + end filt = interpolate ? Cairo.FILTER_BILINEAR : Cairo.FILTER_NEAREST Cairo.pattern_set_filter(p, filt) Cairo.fill(ctx) From 36b8b21610b08bb935178920dc7ed50a42f16f84 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com> Date: Mon, 17 Jun 2024 10:28:58 +0200 Subject: [PATCH 5/6] Fix stack overflow in `offset_bezierpath` (#3969) * Fix stack overflow in `offset_bezierpath` * add changelog --- CHANGELOG.md | 2 ++ src/utilities/texture_atlas.jl | 4 ++-- test/boundingboxes.jl | 13 +++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6433b0d463a..d0ce95d49eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## [Unreleased] + +- Fix stack overflows when using `markerspace = :data` with `scatter` [#3960](https://github.com/MakieOrg/Makie.jl/issues/3960). - CairoMakie: Fix broken SVGs when using non-interpolated image primitives, for example Colorbars, with recent Cairo versions [#3967](https://github.com/MakieOrg/Makie.jl/pull/3967). - CairoMakie: Add argument `pdf_version` to restrict the PDF version when saving a figure as a PDF [#3845](https://github.com/MakieOrg/Makie.jl/pull/3845). - CairoMakie: Fix incorrect scaling factor for SVGs with Cairo_jll 1.18 [#3964](https://github.com/MakieOrg/Makie.jl/pull/3964). diff --git a/src/utilities/texture_atlas.jl b/src/utilities/texture_atlas.jl index 4a671d1e6a3..ef497fc7cdf 100644 --- a/src/utilities/texture_atlas.jl +++ b/src/utilities/texture_atlas.jl @@ -458,7 +458,7 @@ function primitive_uv_offset_width(atlas::TextureAtlas, marker::Observable, font return lift((m, f)-> primitive_uv_offset_width(atlas, m, f), marker, font; ignore_equal_values=true) end -_bcast(x::Vec) = (x,) +_bcast(x::Vec) = Ref(x) _bcast(x) = x # Calculates the scaling factor from unpadded size -> padded size @@ -534,7 +534,7 @@ function offset_bezierpath(atlas::TextureAtlas, bp::BezierPath, markersize::Vec2 end function offset_bezierpath(atlas::TextureAtlas, bp, scale, offset) - return offset_bezierpath.(Ref(atlas), bp, _bcast(scale), _bcast(offset)) + return offset_bezierpath.(Ref(atlas), bp, Vec2d.(_bcast(scale)), Vec2d.(_bcast(offset))) end function offset_marker(atlas::TextureAtlas, marker::Union{T, AbstractVector{T}}, font, markersize, markeroffset) where T <: BezierPath diff --git a/test/boundingboxes.jl b/test/boundingboxes.jl index 95c874bf5fd..66897c8444f 100644 --- a/test/boundingboxes.jl +++ b/test/boundingboxes.jl @@ -145,3 +145,16 @@ end @test data_limits(p) ≈ bb1 @test boundingbox(p) ≈ bb3 end + +@testset "issue 3960" begin + fig = Figure() + ax = Axis(fig[1, 1]) + triangle = BezierPath([ + MoveTo(Point(0, 0)), + LineTo(Point(1, 0)), + LineTo(Point(0, 1)), + ClosePath() + ]) + sc = scatter!(ax, Point(0, 0), marker=triangle, markerspace=:data) + data_limits(sc) # doesn't stackoverflow +end From 12aa2fd54315420c89d811e47cb1749603891814 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com> Date: Mon, 17 Jun 2024 12:32:54 +0200 Subject: [PATCH 6/6] Prepare v0.21.3 (#3970) prepare v0.21.3 --- CHANGELOG.md | 7 +++++-- CairoMakie/Project.toml | 4 ++-- GLMakie/Project.toml | 4 ++-- MakieCore/Project.toml | 2 +- Project.toml | 4 ++-- RPRMakie/Project.toml | 4 ++-- WGLMakie/Project.toml | 4 ++-- 7 files changed, 16 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0ce95d49eb..95ecb0c71c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## [Unreleased] +## [0.21.3] - 2024-06-17 + - Fix stack overflows when using `markerspace = :data` with `scatter` [#3960](https://github.com/MakieOrg/Makie.jl/issues/3960). - CairoMakie: Fix broken SVGs when using non-interpolated image primitives, for example Colorbars, with recent Cairo versions [#3967](https://github.com/MakieOrg/Makie.jl/pull/3967). - CairoMakie: Add argument `pdf_version` to restrict the PDF version when saving a figure as a PDF [#3845](https://github.com/MakieOrg/Makie.jl/pull/3845). @@ -510,8 +512,9 @@ 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.2...HEAD -[0.21.2]: https://github.com/MakieOrg/Makie.jl/compare/v0.21.0...v0.21.2 +[Unreleased]: https://github.com/MakieOrg/Makie.jl/compare/v0.21.3...HEAD +[0.21.3]: https://github.com/MakieOrg/Makie.jl/compare/v0.21.2...v0.21.3 +[0.21.2]: https://github.com/MakieOrg/Makie.jl/compare/v0.21.1...v0.21.2 [0.21.1]: https://github.com/MakieOrg/Makie.jl/compare/v0.21.0...v0.21.1 [0.21.0]: https://github.com/MakieOrg/Makie.jl/compare/v0.20.10...v0.21.0 [0.20.10]: https://github.com/MakieOrg/Makie.jl/compare/v0.20.9...v0.20.10 diff --git a/CairoMakie/Project.toml b/CairoMakie/Project.toml index aca69ddd2d3..f9846929958 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.2" +version = "0.12.3" [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.2" +Makie = "=0.21.3" PrecompileTools = "1.0" julia = "1.3" diff --git a/GLMakie/Project.toml b/GLMakie/Project.toml index 74730985804..8311284ca46 100644 --- a/GLMakie/Project.toml +++ b/GLMakie/Project.toml @@ -1,6 +1,6 @@ name = "GLMakie" uuid = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" -version = "0.10.2" +version = "0.10.3" [deps] ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" @@ -30,7 +30,7 @@ FreeTypeAbstraction = "0.10" GLFW = "3.3" GeometryBasics = "0.4.11" LinearAlgebra = "1.0, 1.6" -Makie = "=0.21.2" +Makie = "=0.21.3" Markdown = "1.0, 1.6" MeshIO = "0.4" ModernGL = "1" diff --git a/MakieCore/Project.toml b/MakieCore/Project.toml index fd59d61d75e..695cd2b97e9 100644 --- a/MakieCore/Project.toml +++ b/MakieCore/Project.toml @@ -1,7 +1,7 @@ name = "MakieCore" uuid = "20f20a25-4f0e-4fdf-b5d1-57303727442b" authors = ["Simon Danisch"] -version = "0.8.2" +version = "0.8.3" [deps] ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" diff --git a/Project.toml b/Project.toml index 21352e6f8f9..82051ce5107 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.2" +version = "0.21.3" [deps] Animations = "27a7e980-b3e6-11e9-2bcd-0b925532e340" @@ -90,7 +90,7 @@ KernelDensity = "0.5, 0.6" LaTeXStrings = "1.2" LinearAlgebra = "1.0, 1.6" MacroTools = "0.5" -MakieCore = "=0.8.2" +MakieCore = "=0.8.3" Markdown = "1.0, 1.6" MathTeXEngine = "0.5, 0.6" Observables = "0.5.5" diff --git a/RPRMakie/Project.toml b/RPRMakie/Project.toml index 1948224246d..6153f8abf3a 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.2" +version = "0.7.3" [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.2" +Makie = "=0.21.3" Printf = "1.0, 1.6" RadeonProRender = "0.3.0" julia = "1.3" diff --git a/WGLMakie/Project.toml b/WGLMakie/Project.toml index c37efb0a074..83e2626802d 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.2" +version = "0.10.3" [deps] Bonito = "824d6782-a2ef-11e9-3a09-e5662e0c26f8" @@ -27,7 +27,7 @@ FreeTypeAbstraction = "0.10" GeometryBasics = "0.4.11" Hyperscript = "0.0.3, 0.0.4, 0.0.5" LinearAlgebra = "1.0, 1.6" -Makie = "=0.21.2" +Makie = "=0.21.3" Observables = "0.5.1" PNGFiles = "0.3, 0.4" PrecompileTools = "1.0"