From f61ed3f03e86b6a4480d4abe0d307b3d0672481d Mon Sep 17 00:00:00 2001 From: sashansamarajeewa Date: Tue, 31 Dec 2024 10:48:22 +0100 Subject: [PATCH 1/5] add inset plot tutorial --- docs/makedocs.jl | 1 + docs/src/tutorials/inset-plot-tutorial.md | 184 ++++++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 docs/src/tutorials/inset-plot-tutorial.md diff --git a/docs/makedocs.jl b/docs/makedocs.jl index d7f0b9e3cd7..f28f4345c28 100644 --- a/docs/makedocs.jl +++ b/docs/makedocs.jl @@ -155,6 +155,7 @@ pages = [ "tutorials/layout-tutorial.md", "tutorials/scenes.md", "tutorials/wrap-existing-recipe.md", + "tutorials/inset-plot-tutorial.md", ], "Explanations" => [ "Backends" => [ diff --git a/docs/src/tutorials/inset-plot-tutorial.md b/docs/src/tutorials/inset-plot-tutorial.md new file mode 100644 index 00000000000..41ab9d4cfe5 --- /dev/null +++ b/docs/src/tutorials/inset-plot-tutorial.md @@ -0,0 +1,184 @@ +# Creating an Inset Plot + +An **inset plot** (or **inset axis**) is a small plot embedded within a larger plot. It is commonly used to zoom in on a particular region of interest, show detailed views of a subset of the data or provide additional contextual information alongside the main plot. Inset plots are a valuable tool for enhancing data visualization, making them widely used in research, business and presentations. In this tutorial we will discuss how to create an inset plot in Makie. + +For example, in a plot showing stock prices over time, an inset can be used to display a magnified view of a specific time period to highlight price fluctuations more clearly. + +![](output.png) + +Let's look at how to create this plot. + +### 1. Setup and Import Packages + +Start by adding CairoMakie backend package and MakieExtra package into the environment. + +```@example inset +using Pkg +Pkg.add("CairoMakie") +Pkg.add("MakieExtra") +using CairoMakie +using MakieExtra +using Random +``` + +### 2. Prepare the Main Plot Data + +Then we will generate the data required for the main plot (stock price data over a period of time). + +```@example inset +Random.seed!(123) +time = 1:500 +stock_price = cumsum(randn(500) .+ 0.5) +``` + +### 3. Create the Main Plot + +Use `Figure()` and `Axis()` to set up the main plot area and display the stock price data. + +```@figure inset +fig = Figure(size = (800, 600)) + +ax_main = Axis(fig[1, 1], + title="Stock Price Over Time", + xlabel="Days", + ylabel="Price") + +line_main = lines!(ax_main, time, stock_price, color=:blue) +fig +``` + +### 4. Add an Inset Axis + +Now let's add an inset axis inside the main plot. We will use the same `Axis()` function, but adjust its size and position to embed it within the main plot. + +```@example inset +ax_inset = Axis(fig[1, 1], + width=Relative(0.2), + height=Relative(0.2), + halign=0.1, + valign=0.9, + title="Zoomed View") +``` + +To adjust the axis size, use `width` and `height` attributes. To adjust the axis position, use `halign` and `valign` attributes. + +### 5. Plot Data in the Inset + +We need to define the data for the inset. For instance, zoom into a specific time range to show detailed price movement. + +```@figure inset +time_inset = time[50:70] +stock_price_inset = stock_price[50:70] +line_inset = lines!(ax_inset, time_inset, stock_price_inset, color=:red) +fig +``` + +### 6. Control Drawing Order of Axes + +It is important to make sure that the inset plot is rendered above the main plot. This is done by setting the z-value in translate! function to a positive value. + +```@example inset +translate!(ax_inset.blockscene, 0, 0, 100) +``` + +### 7. Add a Legend + +Let's add a legend to clarify what each line represents. + +```@example inset +Legend(fig[1, 2], [line_main, line_inset], ["Stock Price", "Zoomed Region"]) +``` + +This adds a legend to the right of the figure, associating the blue line with the main plot and the red line with the inset plot. + +### 8. Mark the Zoomed Section + +Indicate the zoomed section of the main plot and guide the eye to the inset plot using lines. + +```@figure inset +zoom_lines!(ax_main, ax_inset) +save("output.png", fig) # hide +fig +``` + +## Complete Code Example + +Here’s the complete code snippet. + +```julia +# Setup and import packages +using Pkg +Pkg.add("CairoMakie") +Pkg.add("MakieExtra") +using CairoMakie +using MakieExtra +using Random + +# Generate dummy stock price data +Random.seed!(123) +time = 1:500 +stock_price = cumsum(randn(500) .+ 0.5) + +# Create a figure +fig = Figure(size=(800, 600)) + +# Main plot +ax_main = Axis(fig[1, 1], + title="Stock Price Over Time", + xlabel="Days", + ylabel="Price") + +line_main = lines!(ax_main, time, stock_price, color=:blue) + +# Inset axis +ax_inset = Axis(fig[1, 1], + width=Relative(0.2), + height=Relative(0.2), + halign=0.1, + valign=0.9, + title="Zoomed View") + +# Zoom into days 50 to 70 +time_inset = time[50:70] +stock_price_inset = stock_price[50:70] + +line_inset = lines!(ax_inset, time_inset, stock_price_inset, color=:red) + +# Z-Ordering for rendering order +translate!(ax_inset.blockscene, 0, 0, 100) + +# Legend +Legend(fig[1, 2], [line_main, line_inset], ["Stock Price", "Zoomed Region"]) + +# Mark the zoomed section +zoom_lines!(ax_main, ax_inset) +fig +``` + +## Explanation of Key Elements and Related Information + +### 1. Pixel Units vs. Relative Units for Inset Axis Size and Placement + +Pixel Units: Specify exact size in pixels. Useful for precise sizes in fixed size layouts. +Example: `width = 200` sets the width to 200 pixels. + +Relative Units: Define sizes as fractions of the parent container's size. Suitable for creating scalable layouts that adapt to different figure sizes. +Example: `width = Relative(0.2)` sets the width to 20% of the parent figure's width. + +`halign` and `valign` position the inset plot relative to the figure, with values ranging from 0 (left or bottom) to 1 (right or top). + +### 2. `translate!` Function and Z-Ordering + +**z-order** (depth) determines the rendering order of elements, with higher z-values appearing in front of lower ones. This is critical for ensuring that the inset plot is visible above the main plot. By explicitly setting the z-value via translate! function, you can layer elements as needed. If translate! is omitted or the z-value is too low, the inset plot may render behind the main plot, making it invisible or partially obscured. + +``` +translate!(obj, 0, 0, some_positive_z_value) +``` + +### 3. Marking the Section that the Inset Axis Shows + +It is often helpful to visually indicate which part of the main plot corresponds to the inset axis. To achieve this we could make use of the zoom_lines! function in `MakieExtra.jl` package. It uses axes' relative positions and their limits to properly draw lines and rectangles. + +``` +zoom_lines!(ax1, ax2) +``` \ No newline at end of file From 0786a3c9e6cadc4ca69e8d1f87c977121eb3a37e Mon Sep 17 00:00:00 2001 From: sashansamarajeewa Date: Sat, 11 Jan 2025 17:47:06 +0100 Subject: [PATCH 2/5] Remove MakieExtra and zoom_lines, incorporate suggested changes --- docs/src/tutorials/inset-plot-tutorial.md | 86 +++++++++++++++-------- 1 file changed, 58 insertions(+), 28 deletions(-) diff --git a/docs/src/tutorials/inset-plot-tutorial.md b/docs/src/tutorials/inset-plot-tutorial.md index 41ab9d4cfe5..52ccc88004e 100644 --- a/docs/src/tutorials/inset-plot-tutorial.md +++ b/docs/src/tutorials/inset-plot-tutorial.md @@ -8,22 +8,18 @@ For example, in a plot showing stock prices over time, an inset can be used to d Let's look at how to create this plot. -### 1. Setup and Import Packages +### 1. Load the Packages -Start by adding CairoMakie backend package and MakieExtra package into the environment. +Start by loading the CairoMakie backend package and Random package. ```@example inset -using Pkg -Pkg.add("CairoMakie") -Pkg.add("MakieExtra") using CairoMakie -using MakieExtra using Random ``` ### 2. Prepare the Main Plot Data -Then we will generate the data required for the main plot (stock price data over a period of time). +Then we will generate the data required for the main plot (stock price data over a period of 500 days). ```@example inset Random.seed!(123) @@ -64,12 +60,13 @@ To adjust the axis size, use `width` and `height` attributes. To adjust the axis ### 5. Plot Data in the Inset -We need to define the data for the inset. For instance, zoom into a specific time range to show detailed price movement. +We need to define the data for the inset. For instance, select the data between 50 to 70 days range and corresponding data for the price movement. ```@figure inset -time_inset = time[50:70] -stock_price_inset = stock_price[50:70] -line_inset = lines!(ax_inset, time_inset, stock_price_inset, color=:red) +xlims!(ax_inset, 50, 70) +min_price, max_price = minimum(stock_price[50:70]), maximum(stock_price[50:70]) +ylims!(ax_inset, min_price, max_price) +line_inset = lines!(ax_inset, time, stock_price, color=:red) fig ``` @@ -78,7 +75,7 @@ fig It is important to make sure that the inset plot is rendered above the main plot. This is done by setting the z-value in translate! function to a positive value. ```@example inset -translate!(ax_inset.blockscene, 0, 0, 100) +translate!(ax_inset.blockscene, 0, 0, 150) ``` ### 7. Add a Legend @@ -93,10 +90,17 @@ This adds a legend to the right of the figure, associating the blue line with th ### 8. Mark the Zoomed Section -Indicate the zoomed section of the main plot and guide the eye to the inset plot using lines. +Indicate the zoomed section of the main plot by drawing a border around the selected region. ```@figure inset -zoom_lines!(ax_main, ax_inset) +border_points = [ + Point(50, min_price), + Point(70, min_price), + Point(70, max_price), + Point(50, max_price), + Point(50, min_price) +] +lines!(ax_main, border_points, color=:black, linewidth=1) save("output.png", fig) # hide fig ``` @@ -106,12 +110,8 @@ fig Here’s the complete code snippet. ```julia -# Setup and import packages -using Pkg -Pkg.add("CairoMakie") -Pkg.add("MakieExtra") +# Load the packages using CairoMakie -using MakieExtra using Random # Generate dummy stock price data @@ -138,20 +138,31 @@ ax_inset = Axis(fig[1, 1], valign=0.9, title="Zoomed View") -# Zoom into days 50 to 70 -time_inset = time[50:70] -stock_price_inset = stock_price[50:70] +# Set xlims for a selected time data range +xlims!(ax_inset, 50, 70) -line_inset = lines!(ax_inset, time_inset, stock_price_inset, color=:red) +# Calculate and set ylims dynamically for the selected time data range +min_price, max_price = minimum(stock_price[50:70]), maximum(stock_price[50:70]) +ylims!(ax_inset, min_price, max_price) + +# Plot the data in the inset axis +line_inset = lines!(ax_inset, time, stock_price, color=:red) # Z-Ordering for rendering order -translate!(ax_inset.blockscene, 0, 0, 100) +translate!(ax_inset.blockscene, 0, 0, 150) # Legend Legend(fig[1, 2], [line_main, line_inset], ["Stock Price", "Zoomed Region"]) # Mark the zoomed section -zoom_lines!(ax_main, ax_inset) +border_points = [ + Point(50, min_price), # Bottom left + Point(70, min_price), # Bottom right + Point(70, max_price), # Top right + Point(50, max_price), # Top left + Point(50, min_price) # Close the rectangle +] +lines!(ax_main, border_points, color=:black, linewidth=1) fig ``` @@ -169,7 +180,13 @@ Example: `width = Relative(0.2)` sets the width to 20% of the parent figure's wi ### 2. `translate!` Function and Z-Ordering -**z-order** (depth) determines the rendering order of elements, with higher z-values appearing in front of lower ones. This is critical for ensuring that the inset plot is visible above the main plot. By explicitly setting the z-value via translate! function, you can layer elements as needed. If translate! is omitted or the z-value is too low, the inset plot may render behind the main plot, making it invisible or partially obscured. +**z-order** (depth) determines the rendering order of elements, with higher z-values appearing in front of lower ones. This is critical for ensuring that the inset axis is visible above the main plot and its elements. By explicitly setting the z-value using the translate! function, you can layer elements as needed. If the translate! function is omitted or the z-value is too low, the inset plot may render behind the main plot, making it invisible or partially obscured. + +In Makie, the z-order for various elements in an axis typically ranges from -100 to +20; 0 for user plots (by default). For an inset axis to reliably appear in front of the main axis and user plots, it must have a z-value of at least +100. If you are adding custom z-values for the main axis, ensure the inset axis has a z-value greater than the highest z-value in the main axis plus 100. + +`main_axis_translate < inset_axis_translate - 100` + +The maximum z-value allowed is 10,000. ``` translate!(obj, 0, 0, some_positive_z_value) @@ -177,8 +194,21 @@ translate!(obj, 0, 0, some_positive_z_value) ### 3. Marking the Section that the Inset Axis Shows -It is often helpful to visually indicate which part of the main plot corresponds to the inset axis. To achieve this we could make use of the zoom_lines! function in `MakieExtra.jl` package. It uses axes' relative positions and their limits to properly draw lines and rectangles. +It is often helpful to visually indicate which part of the main plot corresponds to the inset axis. To achieve this we could draw a border around the selected region. We create a list of points (`border_points`) that outline the rectangle to mark the region of interest. +These points are calculated using: + +* The x-axis range (e.g., 50 to 70) to mark the time period. +* The y-axis range (`min_price` and `max_price`) dynamically calculated from the data within this time range. + +The border points are connected sequentially, and the final point closes the rectangle by connecting back to the first point. Parameters like color and linewidth can be adjusted for visibility and style. ``` -zoom_lines!(ax1, ax2) +border_points = [ + Point(50, min_price), # Bottom left + Point(70, min_price), # Bottom right + Point(70, max_price), # Top right + Point(50, max_price), # Top left + Point(50, min_price) # Close the rectangle +] +lines!(ax_main, border_points, color=:black, linewidth=1) ``` \ No newline at end of file From 245dd3474ab04ae7a842e0acf6ee4ceef36b33b0 Mon Sep 17 00:00:00 2001 From: sashansamarajeewa Date: Mon, 13 Jan 2025 18:01:17 +0100 Subject: [PATCH 3/5] Add information and link about zoom_lines documentation --- docs/src/tutorials/inset-plot-tutorial.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/src/tutorials/inset-plot-tutorial.md b/docs/src/tutorials/inset-plot-tutorial.md index 52ccc88004e..5c88ec7c366 100644 --- a/docs/src/tutorials/inset-plot-tutorial.md +++ b/docs/src/tutorials/inset-plot-tutorial.md @@ -211,4 +211,6 @@ border_points = [ Point(50, min_price) # Close the rectangle ] lines!(ax_main, border_points, color=:black, linewidth=1) -``` \ No newline at end of file +``` + +Another approach to marking the selected region is to use the [zoom_lines](https://juliaaplavin.github.io/MakieExtraDocs.jl/notebooks/examples.html#3526c688-aea9-411b-a837-dc02ff81a7ee) function from the [MakieExtra.jl](https://juliapackages.com/p/makieextra) package. This function not only marks the region but also connects it to the inset axis with guiding lines, enhancing the visual connection between the main plot and the inset plot. \ No newline at end of file From a6807f8d6e2f7c2f1703d1f15ee4ccba34040276 Mon Sep 17 00:00:00 2001 From: sashansamarajeewa Date: Fri, 17 Jan 2025 18:35:40 +0100 Subject: [PATCH 4/5] Add a CHANGELOG entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fed9cc66f16..0b0923d45f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## [Unreleased] +- Added a tutorial on creating an inset plot [#4697](https://github.com/MakieOrg/Makie.jl/pull/4697) + ## [0.22.1] - 2025-01-17 - Allow volume textures for mesh color, to e.g. implement a performant volume slice display [#2274](https://github.com/MakieOrg/Makie.jl/pull/2274). From fa92035776198779a616dbb9a14e7faf0acdd9ed Mon Sep 17 00:00:00 2001 From: Frederic Freyer Date: Sat, 18 Jan 2025 16:55:15 +0100 Subject: [PATCH 5/5] use extrema, simplify region outline --- docs/src/tutorials/inset-plot-tutorial.md | 45 +++++++---------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/docs/src/tutorials/inset-plot-tutorial.md b/docs/src/tutorials/inset-plot-tutorial.md index 5c88ec7c366..cf53987b042 100644 --- a/docs/src/tutorials/inset-plot-tutorial.md +++ b/docs/src/tutorials/inset-plot-tutorial.md @@ -64,7 +64,7 @@ We need to define the data for the inset. For instance, select the data between ```@figure inset xlims!(ax_inset, 50, 70) -min_price, max_price = minimum(stock_price[50:70]), maximum(stock_price[50:70]) +min_price, max_price = extrema(stock_price[50:70]) ylims!(ax_inset, min_price, max_price) line_inset = lines!(ax_inset, time, stock_price, color=:red) fig @@ -93,14 +93,8 @@ This adds a legend to the right of the figure, associating the blue line with th Indicate the zoomed section of the main plot by drawing a border around the selected region. ```@figure inset -border_points = [ - Point(50, min_price), - Point(70, min_price), - Point(70, max_price), - Point(50, max_price), - Point(50, min_price) -] -lines!(ax_main, border_points, color=:black, linewidth=1) +border_rect = Rect2(50, min_price, 20, max_price - min_price) +lines!(ax_main, border_rect, color=:black, linewidth=1) save("output.png", fig) # hide fig ``` @@ -142,7 +136,7 @@ ax_inset = Axis(fig[1, 1], xlims!(ax_inset, 50, 70) # Calculate and set ylims dynamically for the selected time data range -min_price, max_price = minimum(stock_price[50:70]), maximum(stock_price[50:70]) +min_price, max_price = extrema(stock_price[50:70]) ylims!(ax_inset, min_price, max_price) # Plot the data in the inset axis @@ -154,15 +148,10 @@ translate!(ax_inset.blockscene, 0, 0, 150) # Legend Legend(fig[1, 2], [line_main, line_inset], ["Stock Price", "Zoomed Region"]) -# Mark the zoomed section -border_points = [ - Point(50, min_price), # Bottom left - Point(70, min_price), # Bottom right - Point(70, max_price), # Top right - Point(50, max_price), # Top left - Point(50, min_price) # Close the rectangle -] -lines!(ax_main, border_points, color=:black, linewidth=1) +# Mark the zoomed section (x, y, width, height) +border_rect = Rect2(50, min_price, 20, max_price - min_price) +lines!(ax_main, border_rect, color=:black, linewidth=1) + fig ``` @@ -194,23 +183,17 @@ translate!(obj, 0, 0, some_positive_z_value) ### 3. Marking the Section that the Inset Axis Shows -It is often helpful to visually indicate which part of the main plot corresponds to the inset axis. To achieve this we could draw a border around the selected region. We create a list of points (`border_points`) that outline the rectangle to mark the region of interest. -These points are calculated using: +It is often helpful to visually indicate which part of the main plot corresponds to the inset axis. To achieve this we could draw a border around the selected region. We create a rectangle to mark the region of interest. +Its limits are calculated using: * The x-axis range (e.g., 50 to 70) to mark the time period. * The y-axis range (`min_price` and `max_price`) dynamically calculated from the data within this time range. -The border points are connected sequentially, and the final point closes the rectangle by connecting back to the first point. Parameters like color and linewidth can be adjusted for visibility and style. +`lines!()` will take care of drawing the outline of the rectangle. Parameters like color and linewidth can be adjusted for visibility and style. ``` -border_points = [ - Point(50, min_price), # Bottom left - Point(70, min_price), # Bottom right - Point(70, max_price), # Top right - Point(50, max_price), # Top left - Point(50, min_price) # Close the rectangle -] -lines!(ax_main, border_points, color=:black, linewidth=1) +border_rect = Rect2(50, min_price, 20, max_price - min_price) +lines!(ax_main, border_rect, color=:black, linewidth=1) ``` -Another approach to marking the selected region is to use the [zoom_lines](https://juliaaplavin.github.io/MakieExtraDocs.jl/notebooks/examples.html#3526c688-aea9-411b-a837-dc02ff81a7ee) function from the [MakieExtra.jl](https://juliapackages.com/p/makieextra) package. This function not only marks the region but also connects it to the inset axis with guiding lines, enhancing the visual connection between the main plot and the inset plot. \ No newline at end of file +Another approach to marking the selected region is to use the [zoom_lines](https://juliaaplavin.github.io/MakieExtraDocs.jl/notebooks/examples.html#3526c688-aea9-411b-a837-dc02ff81a7ee) function from the [MakieExtra.jl](https://juliapackages.com/p/makieextra) package. This function not only marks the region but also connects it to the inset axis with guiding lines, enhancing the visual connection between the main plot and the inset plot.