2022-08-26 14:45:24 -04:00

11 lines
46 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"hash": "b88f140f4c13a197e561f5b720c34b4a",
"result": {
"markdown": "# Calculus plots with Makie\n\n\n\nThe [Makie.jl webpage](https://github.com/JuliaPlots/Makie.jl) says\n\n\n> From the Japanese word Maki-e, which is a technique to sprinkle lacquer with gold and silver powder. Data is basically the gold and silver of our age, so let's spread it out beautifully on the screen!\n\n\n\n`Makie` itself is a metapackage for a rich ecosystem. We show how to use the interface provided by the `GLMakie` backend to produce the familiar graphics of calculus.\n\n\n:::{.callout-note}\n## Examples and tutorials\n`Makie` is a sophisticated plotting package, and capable of an enormous range of plots (cf. [examples](https://makie.juliaplots.org/stable/examples/plotting_functions/).) `Makie` also has numerous [tutorials](https://makie.juliaplots.org/stable/tutorials/) to learn from. These are far more extensive that what is described herein, as this section focuses just on the graphics from calculus.\n\n:::\n\n## Figures\n\n\nMakie draws graphics onto a canvas termed a \"scene\" in the Makie documentation. A scene is an implementation detail, the basic (non-mutating) plotting commands described below return a `FigureAxisPlot` object, a compound object that combines a figure, an axes, and a plot object. The `show` method for these objects display the figure.\n\n\nFor `Makie` there are the `GLMakie`, `WGLMakie`, and `CairoMakie` backends for different types of canvases. In the following, we have used `GLMakie`. `WGLMakie` is useful for incorporating `Makie` plots into web-based technologies.\n\n\nWe begin by loading the main package and the `norm` function from the standard `LinearAlgebra` package:\n\n``` {.julia .cell-code}\nusing GLMakie\nimport LinearAlgebra: norm\n```\n\n\nThe `Makie` developers have workarounds for the delayed time to first plot, but without utilizing these the time to load the package is lengthy.\n\n\n## Points (`scatter`)\n\n\nThe task of plotting the points, say $(1,2)$, $(2,3)$, $(3,2)$ can be done different ways. Most plotting packages, and `Makie` is no exception, allow the following: form vectors of the $x$ and $y$ values then plot those with `scatter`:\n\n::: {.cell execution_count=3}\n``` {.julia .cell-code}\nxs = [1,2,3]\nys = [2,3,2]\nscatter(xs, ys)\n```\n\n::: {.cell-output .cell-output-display execution_count=4}\n![](makie_plotting_files/figure-html/cell-4-output-1.png){}\n:::\n:::\n\n\nThe `scatter` function creates and returns an object, which when displayed shows the plot.\n\n\n### `Point2`, `Point3`\n\n\nWhen learning about points on the Cartesian plane, a \"`t`\"-chart is often produced:\n\n\n```\nx | y\n-----\n1 | 2\n2 | 3\n3 | 2\n```\n\nThe `scatter` usage above used the columns. The rows are associated with the points, and these too can be used to produce the same graphic. Rather than make vectors of $x$ and $y$ (and optionally $z$) coordinates, it is more idiomatic to create a vector of \"points.\" `Makie` utilizes a `Point` type to store a 2 or 3 dimensional point. The `Point2` and `Point3` constructors will be utilized.\n\n\n`Makie` uses a GPU, when present, to accelerate the graphic rendering. GPUs employ 32-bit numbers. Julia uses an `f0` to indicate 32-bit floating points. Hence the alternate types `Point2f0` to store 2D points as 32-bit numbers and `Points3f0` to store 3D points as 32-bit numbers are seen in the documentation for Makie.\n\n\nWe can plot a vector of points in as direct manner as vectors of their coordinates:\n\n::: {.cell execution_count=4}\n``` {.julia .cell-code}\npts = [Point2(1,2), Point2(2,3), Point2(3,2)]\nscatter(pts)\n```\n\n::: {.cell-output .cell-output-display execution_count=5}\n![](makie_plotting_files/figure-html/cell-5-output-1.png){}\n:::\n:::\n\n\nA typical usage is to generate points from some vector-valued function. Say we have a parameterized function `r` taking $R$ into $R^2$ defined by:\n\n::: {.cell execution_count=5}\n``` {.julia .cell-code}\nr(t) = [sin(t), cos(t)]\n```\n\n::: {.cell-output .cell-output-display execution_count=6}\n```\nr (generic function with 1 method)\n```\n:::\n:::\n\n\nThen broadcasting values gives a vector of vectors, each identified with a point:\n\n::: {.cell execution_count=6}\n``` {.julia .cell-code}\nts = [1,2,3]\nr.(ts)\n```\n\n::: {.cell-output .cell-output-display execution_count=7}\n```\n3-element Vector{Vector{Float64}}:\n [0.8414709848078965, 0.5403023058681398]\n [0.9092974268256817, -0.4161468365471424]\n [0.1411200080598672, -0.9899924966004454]\n```\n:::\n:::\n\n\nWe can broadcast `Point2` over this to create a vector of `Point` objects:\n\n::: {.cell execution_count=7}\n``` {.julia .cell-code}\npts = Point2.(r.(ts))\n```\n\n::: {.cell-output .cell-output-display execution_count=8}\n```\n3-element Vector{Point2{Float64}}:\n [0.8414709848078965, 0.5403023058681398]\n [0.9092974268256817, -0.4161468365471424]\n [0.1411200080598672, -0.9899924966004454]\n```\n:::\n:::\n\n\nThese then can be plotted directly:\n\n::: {.cell execution_count=8}\n``` {.julia .cell-code}\nscatter(pts)\n```\n\n::: {.cell-output .cell-output-display execution_count=9}\n![](makie_plotting_files/figure-html/cell-9-output-1.png){}\n:::\n:::\n\n\nThe ploting of points in three dimesions is essentially the same, save the use of `Point3` instead of `Point2`.\n\n::: {.cell execution_count=9}\n``` {.julia .cell-code}\nr(t) = [sin(t), cos(t), t]\nts = range(0, 4pi, length=100)\npts = Point3.(r.(ts))\nscatter(pts; markersize=5)\n```\n\n::: {.cell-output .cell-output-display execution_count=10}\n![](makie_plotting_files/figure-html/cell-10-output-1.png){}\n:::\n:::\n\n\n---\n\n\nTo plot points generated in terms of vectors of coordinates, the component vectors must be created. The \"`t`\"-table shows how, simply loop over each column and add the corresponding $x$ or $y$ (or $z$) value. This utility function does exactly that, returning the vectors in a tuple.\n\n::: {.cell execution_count=10}\n``` {.julia .cell-code}\nunzip(vs) = Tuple([vs[j][i] for j in eachindex(vs)] for i in eachindex(vs[1]))\n```\n\n::: {.cell-output .cell-output-display execution_count=11}\n```\nunzip (generic function with 1 method)\n```\n:::\n:::\n\n\n:::{.callout-note}\n## Note\nIn the `CalculusWithJulia` package, `unzip` is implemented using `SplitApplyCombine.invert`.\n\n:::\n\nWe might have then:\n\n::: {.cell execution_count=11}\n``` {.julia .cell-code}\nscatter(unzip(r.(ts))...; markersize=5)\n```\n\n::: {.cell-output .cell-output-display execution_count=12}\n![](makie_plotting_files/figure-html/cell-12-output-1.png){}\n:::\n:::\n\n\nwhere splatting is used to specify the `xs`, `ys`, and `zs` to `scatter`.\n\n\n(Compare to `scatter(Point3.(r.(ts)))` or `scatter(Point3∘r).(ts))`.)\n\n\n### Attributes\n\n\nA point is drawn with a \"marker\" with a certain size and color. These attributes can be adjusted, as in the following:\n\n::: {.cell execution_count=12}\n``` {.julia .cell-code}\nscatter(xs, ys;\n marker=[:x,:cross, :circle], markersize=25,\n color=:blue)\n```\n\n::: {.cell-output .cell-output-display execution_count=13}\n![](makie_plotting_files/figure-html/cell-13-output-1.png){}\n:::\n:::\n\n\nMarker attributes include\n\n\n * `marker` a symbol, shape.\n * `marker_offset` offset coordinates\n * `markersize` size (radius pixels) of marker\n\n\nA single value will be repeated. A vector of values of a matching size will specify the attribute on a per point basis.\n\n\n## Curves\n\n\nThe curves of calculus are lines. The `lines` command of `Makie` will render a curve by connecting a series of points with straight-line segments. By taking a sufficient number of points the connect-the-dot figure can appear curved.\n\n\n### Plots of univariate functions\n\n\nThe basic plot of univariate calculus is the graph of a function $f$ over an interval $[a,b]$. This is implemented using a familiar strategy: produce a series of representative values between $a$ and $b$; produce the corresponding $f(x)$ values; plot these as points and connect the points with straight lines.\n\n\nTo create regular values between `a` and `b` typically the `range` function or the range operator (`a:h:b`) are employed. The the related `LinRange` function is also an option.\n\n\nFor example:\n\n::: {.cell execution_count=13}\n``` {.julia .cell-code}\nf(x) = sin(x)\na, b = 0, 2pi\nxs = range(a, b, length=250)\nlines(xs, f.(xs))\n```\n\n::: {.cell-output .cell-output-display execution_count=14}\n![](makie_plotting_files/figure-html/cell-14-output-1.png){}\n:::\n:::\n\n\n`Makie` also will read the interval notation of `IntervalSets` and select its own set of intermediate points:\n\n::: {.cell execution_count=14}\n``` {.julia .cell-code}\nlines(a..b, f)\n```\n\n::: {.cell-output .cell-output-display execution_count=15}\n![](makie_plotting_files/figure-html/cell-15-output-1.png){}\n:::\n:::\n\n\nAs with `scatter`, `lines` returns an object that produces a graphic when displayed.\n\n\nAs with `scatter`, `lines` can can also be drawn using a vector of points:\n\n::: {.cell execution_count=15}\n``` {.julia .cell-code}\npts = [Point2(x, f(x)) for x ∈ xs]\nlines(pts)\n```\n\n::: {.cell-output .cell-output-display execution_count=16}\n![](makie_plotting_files/figure-html/cell-16-output-1.png){}\n:::\n:::\n\n\n(Though the advantage isn't clear here, this will be useful when the points are generated in different manners.)\n\n\nWhen a `y` value is `NaN` or infinite, the connecting lines are not drawn:\n\n::: {.cell execution_count=16}\n``` {.julia .cell-code}\nxs = 1:5\nys = [1,2,NaN, 4, 5]\nlines(xs, ys)\n```\n\n::: {.cell-output .cell-output-display execution_count=17}\n![](makie_plotting_files/figure-html/cell-17-output-1.png){}\n:::\n:::\n\n\nAs with other plotting packages, this is useful to represent discontinuous functions, such as what occurs at a vertical asymptote or a step function.\n\n\n#### Adding to a figure (`lines!`, `scatter!`, ...)\n\n\nTo *add* or *modify* a scene can be done using a mutating version of a plotting primitive, such as `lines!` or `scatter!`. The names follow `Julia`'s convention of using an `!` to indicate that a function modifies an argument, in this case the underlying figure.\n\n\nHere is one way to show two plots at once:\n\n::: {.cell execution_count=17}\n``` {.julia .cell-code}\nxs = range(0, 2pi, length=100)\nlines(xs, sin.(xs))\nlines!(xs, cos.(xs))\ncurrent_figure()\n```\n\n::: {.cell-output .cell-output-display execution_count=18}\n![](makie_plotting_files/figure-html/cell-18-output-1.png){}\n:::\n:::\n\n\n:::{.callout-note}\n## Current figure\nThe `current_figure` call is needed to have the figure display, as the returned value of `lines!` is not a figure object. (Figure objects display when shown as the output of a cell.)\n\n:::\n\nWe will see soon how to modify the line attributes so that the curves can be distinguished.\n\n\nThe following shows the construction details in the graphic:\n\n::: {.cell execution_count=18}\n``` {.julia .cell-code}\nxs = range(0, 2pi, length=10)\nlines(xs, sin.(xs))\nscatter!(xs, sin.(xs);\n markersize=10)\ncurrent_figure()\n```\n\n::: {.cell-output .cell-output-display execution_count=19}\n![](makie_plotting_files/figure-html/cell-19-output-1.png){}\n:::\n:::\n\n\nAs an example, this shows how to add the tangent line to a graph. The slope of the tangent line being computed by `ForwardDiff.derivative`.\n\n::: {.cell execution_count=19}\n``` {.julia .cell-code}\nimport ForwardDiff\nf(x) = x^x\na, b= 0, 2\nc = 0.5\nxs = range(a, b, length=200)\n\ntl(x) = f(c) + ForwardDiff.derivative(f, c) * (x-c)\n\nlines(xs, f.(xs))\nlines!(xs, tl.(xs), color=:blue)\ncurrent_figure()\n```\n\n::: {.cell-output .cell-output-display execution_count=20}\n![](makie_plotting_files/figure-html/cell-20-output-1.png){}\n:::\n:::\n\n\nThis example, modified from a [discourse](https://discourse.julialang.org/t/how-to-plot-step-functions-x-correctly-in-julia/84087/5) post by user `@rafael.guerra`, shows how to plot a step function (`floor`) using `NaN`s to create line breaks. The marker colors set for `scatter!` use `:white` to match the background color.\n\n::: {.cell execution_count=20}\n``` {.julia .cell-code}\nx = -5:5\nδ = 5eps() # for rounding purposes; our interval is [i,i+1) ≈ [i, i+1-δ]\nxx = Float64[]\nfor i ∈ x[1:end-1]\n append!(xx, (i, i+1 - δ, NaN))\nend\nyy = floor.(xx)\n\nlines(xx, yy)\nscatter!(xx, yy, color=repeat([:black, :white, :white], length(xx)÷3))\n\ncurrent_figure()\n```\n\n::: {.cell-output .cell-output-display execution_count=21}\n![](makie_plotting_files/figure-html/cell-21-output-1.png){}\n:::\n:::\n\n\n### Text (`annotations`)\n\n\nText can be placed at a point, as a marker is. To place text, the desired text and a position need to be specified along with any adjustments to the default attributes.\n\n\nFor example:\n\n::: {.cell execution_count=21}\n``` {.julia .cell-code}\nxs = 1:5\npts = Point2.(xs, xs)\nscatter(pts)\nannotations!(\"Point \" .* string.(xs), pts;\n textsize = 50 .- 2*xs,\n rotation = 2pi ./ xs)\n\ncurrent_figure()\n```\n\n::: {.cell-output .cell-output-display execution_count=22}\n![](makie_plotting_files/figure-html/cell-22-output-1.png){}\n:::\n:::\n\n\nThe graphic shows that `textsize` adjusts the displayed size and `rotation` adjusts the orientation. (The graphic also shows a need to manually override the limits of the `y` axis, as the `Point 5` is chopped off; the `ylims!` function to do so will be shown later.)\n\n\nAttributes for `text`, among many others, include:\n\n\n * `align` Specify the text alignment through `(:pos, :pos)`, where `:pos` can be `:left`, `:center`, or `:right`.\n * `rotation` to indicate how the text is to be rotated\n * `textsize` the font point size for the text\n * `font` to indicate the desired font\n\n\n#### Line attributes\n\n\nIn a previous example, we added the argument `color=:blue` to the `lines!` call. This was to set an attribute for the line being drawn. Lines have other attributes that allow different ones to be distinguished, as above where colors indicate the different graphs.\n\n\nOther attributes can be seen from the help page for `lines`, and include:\n\n\n * `color` set with a symbol, as above, or a string\n * `label` a label for the line to display in a legend\n * `linestyle` available styles are set by a symbol, one of `:dash`, `:dot`, `:dashdot`, or `:dashdotdot`.\n * `linewidth` width of line\n * `transparency` the `alpha` value, a number between $0$ and $1$, smaller numbers for more transparent.\n\n\n#### Simple legends\n\n\nA simple legend displaying labels given to each curve can be produced by `axislegend`. For example:\n\n::: {.cell execution_count=22}\n``` {.julia .cell-code}\nxs = 0..pi\nlines(xs, x -> sin(x^2), label=\"sin(x^2)\")\nlines!(xs, x -> sin(x)^2, label = \"sin(x)^2\")\naxislegend()\n\ncurrent_figure()\n```\n\n::: {.cell-output .cell-output-display execution_count=23}\n![](makie_plotting_files/figure-html/cell-23-output-1.png){}\n:::\n:::\n\n\nLater, we will see how to control the placement of a legend within a figure.\n\n\n#### Titles, axis labels, axis ticks\n\n\nThe basic plots we have seen are of type `FigureAxisPlot`. The \"axis\" part controls attributes of the plot such as titles, labels, tick positions, etc. These values can be set in different manners. On construction we can pass values to a named argument `axis` using a named tuple.\n\n\nFor example:\n\n::: {.cell execution_count=23}\n``` {.julia .cell-code}\nxs = 0..2pi\nlines(xs, sin;\n axis=(title=\"Plot of sin(x)\", xlabel=\"x\", ylabel=\"sin(x)\")\n )\n```\n\n::: {.cell-output .cell-output-display execution_count=24}\n![](makie_plotting_files/figure-html/cell-24-output-1.png){}\n:::\n:::\n\n\nTo access the `axis` element of a plot **after** the plot is constructed, values can be assigned to the `axis` property of the `FigureAxisPlot` object. For example:\n\n::: {.cell execution_count=24}\n``` {.julia .cell-code}\nxs = 0..2pi\np = lines(xs, sin;\n axis=(title=\"Plot of sin(x)\", xlabel=\"x\", ylabel=\"sin(x)\")\n )\np.axis.xticks = MultiplesTicks(5, pi, \"π\") # label 5 times using `pi`\n\ncurrent_figure()\n```\n\n::: {.cell-output .cell-output-display execution_count=25}\n![](makie_plotting_files/figure-html/cell-25-output-1.png){}\n:::\n:::\n\n\nThe ticks are most easily set as a collection of values. Above, the `MultiplesTicks` function was used to label with multiples of $\\pi$.\n\n\nLater we will discuss how `Makie` allows for subsequent modification of several parts of the plot (not just the ticks) including the data.\n\n\n#### Figure resolution, $x$ and $y$ limits\n\n\nAs just mentioned, the basic plots we have seen are of type `FigureAxisPlot`. The \"figure\" part can be used to adjust the background color or the resolution. As with attributes for the axis, these too can be passed to a simple constructor:\n\n::: {.cell execution_count=25}\n``` {.julia .cell-code}\nlines(xs, sin;\n axis=(title=\"Plot of sin(x)\", xlabel=\"x\", ylabel=\"sin(x)\"),\n figure=(;resolution=(300, 300))\n )\n```\n\n::: {.cell-output .cell-output-display execution_count=26}\n![](makie_plotting_files/figure-html/cell-26-output-1.png){}\n:::\n:::\n\n\nThe `;` in the tuple passed to `figure` is one way to create a *named* tuple with a single element. Alternatively, `(resolution=(300,300), )` with a trailing comma could have been used.\n\n\nTo set the limits of the graph there are shorthand functions `xlims!`, `ylims!`, and `zlims!`. This might prove useful if vertical asymptotes are encountered, as in this example:\n\n::: {.cell execution_count=26}\n``` {.julia .cell-code}\nf(x) = 1/x\na,b = -1, 1\nxs = range(-1, 1, length=200)\nlines(xs, f.(xs))\nylims!(-10, 10)\n\ncurrent_figure()\n```\n\n::: {.cell-output .cell-output-display execution_count=27}\n![](makie_plotting_files/figure-html/cell-27-output-1.png){}\n:::\n:::\n\n\nThis still leaves the artifact due to the vertical asymptote at $0$ having different values from the left and the right.\n\n\n### Plots of parametric functions\n\n\nA space curve is a plot of a function $f:R^2 \\rightarrow R$ or $f:R^3 \\rightarrow R$.\n\n\nTo construct a curve from a set of points, we have a similar pattern in both $2$ and $3$ dimensions:\n\n::: {.cell execution_count=27}\n``` {.julia .cell-code}\nr(t) = [sin(2t), cos(3t)]\nts = range(0, 2pi, length=200)\npts = Point2.(r.(ts)) # or (Point2∘r).(ts)\nlines(pts)\n```\n\n::: {.cell-output .cell-output-display execution_count=28}\n![](makie_plotting_files/figure-html/cell-28-output-1.png){}\n:::\n:::\n\n\nOr\n\n::: {.cell execution_count=28}\n``` {.julia .cell-code}\nr(t) = [sin(2t), cos(3t), t]\nts = range(0, 2pi, length=200)\npts = Point3.(r.(ts))\nlines(pts)\n```\n\n::: {.cell-output .cell-output-display execution_count=29}\n![](makie_plotting_files/figure-html/cell-29-output-1.png){}\n:::\n:::\n\n\nAlternatively, vectors of the $x$, $y$, and $z$ components can be produced and then plotted using the pattern `lines(xs, ys)` or `lines(xs, ys, zs)`. For example, using `unzip`, as above, we might have done the prior example with:\n\n::: {.cell execution_count=29}\n``` {.julia .cell-code}\nxs, ys, zs = unzip(r.(ts))\nlines(xs, ys, zs)\n```\n\n::: {.cell-output .cell-output-display execution_count=30}\n![](makie_plotting_files/figure-html/cell-30-output-1.png){}\n:::\n:::\n\n\n#### Aspect ratio\n\n\nA simple plot of a parametrically defined circle will show an ellipse, as the aspect ratio of the $x$ and $y$ axis is not $1$. To enforce this, we can pass a value of `aspect=1` to the underlying \"Axis\" object. For example:\n\n::: {.cell execution_count=30}\n``` {.julia .cell-code}\nts = range(0, 2pi, length=100)\nxs, ys = sin.(ts), cos.(ts)\nlines(xs, ys; axis=(; aspect = 1))\n```\n\n::: {.cell-output .cell-output-display execution_count=31}\n![](makie_plotting_files/figure-html/cell-31-output-1.png){}\n:::\n:::\n\n\n#### Tangent vectors (`arrows`)\n\n\nA tangent vector along a curve can be drawn quite easily using the `arrows` function. There are different interfaces for `arrows`, but we show the one which uses a vector of positions and a vector of \"vectors\". For the latter, we utilize the `derivative` function from `ForwardDiff`:\n\n::: {.cell execution_count=31}\n``` {.julia .cell-code}\nr(t) = [sin(t), cos(t)] # vector, not tuple\nts = range(0, 4pi, length=200)\nlines(Point2.(r.(ts)))\n\nnts = 0:pi/4:2pi\nus = r.(nts)\ndus = ForwardDiff.derivative.(r, nts)\n\narrows!(Point2.(us), Point2.(dus))\n\ncurrent_figure()\n```\n\n::: {.cell-output .cell-output-display execution_count=32}\n![](makie_plotting_files/figure-html/cell-32-output-1.png){}\n:::\n:::\n\n\nIn 3 dimensions the differences are minor:\n\n::: {.cell execution_count=32}\n``` {.julia .cell-code}\nr(t) = [sin(t), cos(t), t] # vector, not tuple\nts = range(0, 4pi, length=200)\nlines(Point3.(r.(ts)))\n\nnts = 0:pi/2:(4pi-pi/2)\nus = r.(nts)\ndus = ForwardDiff.derivative.(r, nts)\n\narrows!(Point3.(us), Point3.(dus))\n\ncurrent_figure()\n```\n\n::: {.cell-output .cell-output-display execution_count=33}\n![](makie_plotting_files/figure-html/cell-33-output-1.png){}\n:::\n:::\n\n\n#### Arrow attributes\n\n\nAttributes for `arrows` include\n\n\n * `arrowsize` to adjust the size\n * `lengthscale` to scale the size\n * `arrowcolor` to set the color\n * `arrowhead` to adjust the head\n * `arrowtail` to adjust the tail\n\n\n## Surfaces\n\n\nPlots of surfaces in $3$ dimensions are useful to help understand the behavior of multivariate functions.\n\n\n#### Surfaces defined through $z=f(x,y)$\n\n\nThe \"`peaks`\" function defined below has a few prominent peaks:\n\n::: {.cell execution_count=33}\n``` {.julia .cell-code}\nfunction peaks(x, y)\n p = 3*(1-x)^2*exp(-x^2 - (y+1)^2)\n p -= 10(x/5-x^3-y^5)*exp(-x^2-y^2)\n p -= 1/3*exp(-(x+1)^2-y^2)\n p\nend\n```\n\n::: {.cell-output .cell-output-display execution_count=34}\n```\npeaks (generic function with 1 method)\n```\n:::\n:::\n\n\nHere we see how `peaks` can be visualized over the region $[-5,5]\\times[-5,5]$:\n\n::: {.cell execution_count=34}\n``` {.julia .cell-code}\nxs = ys = range(-5, 5, length=25)\nsurface(xs, ys, peaks)\n```\n\n::: {.cell-output .cell-output-display execution_count=35}\n![](makie_plotting_files/figure-html/cell-35-output-1.png){}\n:::\n:::\n\n\nThe calling pattern `surface(xs, ys, f)` implies a rectangular grid over the $x$-$y$ plane defined by `xs` and `ys` with $z$ values given by $f(x,y)$.\n\n\nAlternatively a \"matrix\" of $z$ values can be specified. For a function `f`, this is conveniently generated by the pattern `f.(xs, ys')`, the `'` being important to get a matrix of all $x$-$y$ pairs through `Julia`'s broadcasting syntax.\n\n::: {.cell execution_count=35}\n``` {.julia .cell-code}\nzs = peaks.(xs, ys')\nsurface(xs, ys, zs)\n```\n\n::: {.cell-output .cell-output-display execution_count=36}\n![](makie_plotting_files/figure-html/cell-36-output-1.png){}\n:::\n:::\n\n\nTo see how this graph is constructed, the points $(x,y,f(x,y))$ are plotted over the grid and displayed.\n\n\nHere we downsample to illustrate:\n\n::: {.cell execution_count=36}\n``` {.julia .cell-code}\nxs = ys = range(-5, 5, length=5)\npts = [Point3(x, y, peaks(x,y)) for x in xs for y in ys]\nscatter(pts, markersize=25)\n```\n\n::: {.cell-output .cell-output-display execution_count=37}\n![](makie_plotting_files/figure-html/cell-37-output-1.png){}\n:::\n:::\n\n\nThese points are then connected. The `wireframe` function illustrates just the frame:\n\n::: {.cell execution_count=37}\n``` {.julia .cell-code}\nwireframe(xs, ys, peaks.(xs, ys'); linewidth=5)\n```\n\n::: {.cell-output .cell-output-display execution_count=38}\n![](makie_plotting_files/figure-html/cell-38-output-1.png){}\n:::\n:::\n\n\nThe `surface` call triangulates the frame and fills in the shading:\n\n::: {.cell execution_count=38}\n``` {.julia .cell-code}\nsurface!(xs, ys, peaks.(xs, ys'))\ncurrent_figure()\n```\n\n::: {.cell-output .cell-output-display execution_count=39}\n![](makie_plotting_files/figure-html/cell-39-output-1.png){}\n:::\n:::\n\n\n#### Parametrically defined surfaces\n\n\nA surface may be parametrically defined through a function $r(u,v) = (x(u,v), y(u,v), z(u,v))$. For example, the surface generated by $z=f(x,y)$ is of the form with $r(u,v) = (u,v,f(u,v))$.\n\n\nThe `surface` function and the `wireframe` function can be used to display such surfaces. In previous usages, the `x` and `y` values were vectors from which a 2-dimensional grid is formed. For parametric surfaces, a grid for the `x` and `y` values must be generated. This function will do so:\n\n::: {.cell execution_count=39}\n``` {.julia .cell-code}\nfunction parametric_grid(us, vs, r)\n n,m = length(us), length(vs)\n xs, ys, zs = zeros(n,m), zeros(n,m), zeros(n,m)\n for (i, uᵢ) in pairs(us)\n for (j, vⱼ) in pairs(vs)\n x,y,z = r(uᵢ, vⱼ)\n xs[i,j] = x\n ys[i,j] = y\n zs[i,j] = z\n end\n end\n (xs, ys, zs)\nend\n```\n\n::: {.cell-output .cell-output-display execution_count=40}\n```\nparametric_grid (generic function with 1 method)\n```\n:::\n:::\n\n\nWith the data suitably massaged, we can directly plot either a `surface` or `wireframe` plot.\n\n\n---\n\nAs an aside, The above can be done more campactly with nested list comprehensions:\n\n\n```\nxs, ys, zs = [[pt[i] for pt in r.(us, vs')] for i in 1:3]\n```\n\nOr using the `unzip` function directly after broadcasting:\n\n\n```\nxs, ys, zs = unzip(r.(us, vs'))\n```\n\n---\n\n\nFor example, a sphere can be parameterized by $r(u,v) = (\\sin(u)\\cos(v), \\sin(u)\\sin(v), \\cos(u))$ and visualized through:\n\n::: {.cell execution_count=40}\n``` {.julia .cell-code}\nr(u,v) = [sin(u)*cos(v), sin(u)*sin(v), cos(u)]\nus = range(0, pi, length=25)\nvs = range(0, pi/2, length=25)\nxs, ys, zs = parametric_grid(us, vs, r)\n\nsurface(xs, ys, zs)\nwireframe!(xs, ys, zs)\ncurrent_figure()\n```\n\n::: {.cell-output .cell-output-display execution_count=41}\n![](makie_plotting_files/figure-html/cell-41-output-1.png){}\n:::\n:::\n\n\nA surface of revolution for $g(u)$ revolved about the $z$ axis can be visualized through:\n\n::: {.cell execution_count=41}\n``` {.julia .cell-code}\ng(u) = u^2 * exp(-u)\nr(u,v) = (g(u)*sin(v), g(u)*cos(v), u)\nus = range(0, 3, length=10)\nvs = range(0, 2pi, length=10)\nxs, ys, zs = parametric_grid(us, vs, r)\n\nsurface(xs, ys, zs)\nwireframe!(xs, ys, zs)\ncurrent_figure()\n```\n\n::: {.cell-output .cell-output-display execution_count=42}\n![](makie_plotting_files/figure-html/cell-42-output-1.png){}\n:::\n:::\n\n\nA torus with big radius $2$ and inner radius $1/2$ can be visualized as follows\n\n::: {.cell execution_count=42}\n``` {.julia .cell-code}\nr1, r2 = 2, 1/2\nr(u,v) = ((r1 + r2*cos(v))*cos(u), (r1 + r2*cos(v))*sin(u), r2*sin(v))\nus = vs = range(0, 2pi, length=25)\nxs, ys, zs = parametric_grid(us, vs, r)\n\nsurface(xs, ys, zs)\nwireframe!(xs, ys, zs)\ncurrent_figure()\n```\n\n::: {.cell-output .cell-output-display execution_count=43}\n![](makie_plotting_files/figure-html/cell-43-output-1.png){}\n:::\n:::\n\n\nA Möbius strip can be produced with:\n\n::: {.cell execution_count=43}\n``` {.julia .cell-code}\nws = range(-1/4, 1/4, length=8)\nthetas = range(0, 2pi, length=30)\nr(w, θ) = ((1+w*cos(θ/2))*cos(θ), (1+w*cos(θ/2))*sin(θ), w*sin(θ/2))\nxs, ys, zs = parametric_grid(ws, thetas, r)\n\nsurface(xs, ys, zs)\nwireframe!(xs, ys, zs)\ncurrent_figure()\n```\n\n::: {.cell-output .cell-output-display execution_count=44}\n![](makie_plotting_files/figure-html/cell-44-output-1.png){}\n:::\n:::\n\n\n## Contour plots (`contour`, `contourf`, `heatmap`)\n\n\nFor a function $z = f(x,y)$ an alternative to a surface plot, is a contour plot. That is, for different values of $c$ the level curves $f(x,y)=c$ are drawn.\n\n\nFor a function $f(x,y)$, the syntax for generating a contour plot follows that for `surface`.\n\n\nFor example, using the `peaks` function, previously defined, we have a contour plot over the region $[-5,5]\\times[-5,5]$ is generated through:\n\n::: {.cell execution_count=44}\n``` {.julia .cell-code}\nxs = ys = range(-5, 5, length=100)\ncontour(xs, ys, peaks)\n```\n\n::: {.cell-output .cell-output-display execution_count=45}\n![](makie_plotting_files/figure-html/cell-45-output-1.png){}\n:::\n:::\n\n\nThe default of $5$ levels can be adjusted using the `levels` keyword:\n\n::: {.cell execution_count=45}\n``` {.julia .cell-code}\ncontour(xs, ys, peaks; levels = 20)\n```\n\n::: {.cell-output .cell-output-display execution_count=46}\n![](makie_plotting_files/figure-html/cell-46-output-1.png){}\n:::\n:::\n\n\nThe `levels` argument can also specify precisely what levels are to be drawn.\n\n\nThe contour graph makes identification of peaks and valleys easy as the limits of patterns of nested contour lines.\n\n\nA *filled* contour plot is produced by `contourf`:\n\n::: {.cell execution_count=46}\n``` {.julia .cell-code}\ncontourf(xs, ys, peaks)\n```\n\n::: {.cell-output .cell-output-display execution_count=47}\n![](makie_plotting_files/figure-html/cell-47-output-1.png){}\n:::\n:::\n\n\nA related, but alternative visualization, using color to represent magnitude is a heatmap, produced by the `heatmap` function. The calling syntax is similar to `contour` and `surface`:\n\n::: {.cell execution_count=47}\n``` {.julia .cell-code}\nheatmap(xs, ys, peaks)\n```\n\n::: {.cell-output .cell-output-display execution_count=48}\n![](makie_plotting_files/figure-html/cell-48-output-1.png){}\n:::\n:::\n\n\nThis graph shows peaks and valleys through \"hotspots\" on the graph.\n\n\nThe `MakieGallery` package includes an example of a surface plot with both a wireframe and 2D contour graph added. It is replicated here using the `peaks` function scaled by $5$.\n\n\nThe function and domain to plot are described by:\n\n``` {.julia .cell-code}\nxs = ys = range(-5, 5, length=51)\nzs = peaks.(xs, ys') / 5;\n```\n\n\nThe `zs` were generated, as `wireframe` does not provide the interface for passing a function.\n\n\nThe `surface` and `wireframe` are produced as follows. Here we manually create the figure and axis object so that we can set the viewing angle through the `elevation` argument to the axis object:\n\n::: {.cell execution_count=49}\n``` {.julia .cell-code}\nfig = Figure()\nax3 = Axis3(fig[1,1];\n elevation=pi/9, azimuth=pi/16)\nsurface!(ax3, xs, ys, zs)\nwireframe!(ax3, xs, ys, zs;\n overdraw = true, transparency = true,\n color = (:black, 0.1))\ncurrent_figure()\n```\n\n::: {.cell-output .cell-output-display execution_count=50}\n![](makie_plotting_files/figure-html/cell-50-output-1.png){}\n:::\n:::\n\n\nTo add the contour, a simple call via `contour!(scene, xs, ys, zs)` will place the contour at the $z=0$ level which will make it hard to read. Rather, placing at the \"bottom\" of the figure is desirable. To identify that the minimum value, is identified (and rounded) and the argument `transformation = (:xy, zmin)` is passed to `contour!`:\n\n::: {.cell execution_count=50}\n``` {.julia .cell-code}\nezs = extrema(zs)\nzmin, zmax = floor(first(ezs)), ceil(last(ezs))\ncontour!(ax3, xs, ys, zs;\n levels = 15, linewidth = 2,\n transformation = (:xy, zmin))\nzlims!(zmin, zmax)\ncurrent_figure()\n```\n\n::: {.cell-output .cell-output-display execution_count=51}\n![](makie_plotting_files/figure-html/cell-51-output-1.png){}\n:::\n:::\n\n\nThe `transformation` plot attribute sets the \"plane\" (one of `:xy`, `:yz`, or `:xz`) at a location, in this example `zmin`.\n\n\nThe manual construction of a figure and an axis object will be further discussed later.\n\n\n### Three dimensional contour plots\n\n\nThe `contour` function can also plot $3$-dimensional contour plots. Concentric spheres, contours of $x^2 + y^2 + z^2 = c$ for $c > 0$ are presented by the following:\n\n::: {.cell execution_count=51}\n``` {.julia .cell-code}\nf(x,y,z) = x^2 + y^2 + z^2\nxs = ys = zs = range(-3, 3, length=100)\n\ncontour(xs, ys, zs, f)\n```\n\n::: {.cell-output .cell-output-display execution_count=52}\n![](makie_plotting_files/figure-html/cell-52-output-1.png){}\n:::\n:::\n\n\n### Implicitly defined curves and surfaces\n\n\nSuppose $f$ is a scalar-valued function. If `f` takes two variables for its input, then the equation $f(x,y) = 0$ implicitly defines $y$ as a function of $x$; $y$ can be visualized *locally* with a curve. If $f$ takes three variables for its input, then the equation $f(x,y,z)=0$ implicitly defines $z$ as a function of $x$ and $y$; $z$ can be visualized *locally* with a surface.\n\n\n#### Implicitly defined curves\n\n\nThe graph of an equation is the collection of all $(x,y)$ values satisfying the equation. This is more general than the graph of a function, which can be viewed as the graph of the equation $y=f(x)$. An equation in $x$-$y$ can be graphed if the set of solutions to a related equation $f(x,y)=0$ can be identified, as one can move all terms to one side of an equation and define $f$ as the rule of the side with the terms. The implicit function theorem ensures that under some conditions, *locally* near a point $(x, y)$, the value $y$ can be represented as a function of $x$. So, the graph of the equation $f(x,y)=0$ can be produced by stitching together these local function representations.\n\n\nThe contour graph can produce these graphs by setting the `levels` argument to `[0]`.\n\n::: {.cell execution_count=52}\n``` {.julia .cell-code}\nf(x,y) = x^3 + x^2 + x + 1 - x*y # solve x^3 + x^2 + x + 1 = x*y\nxs = range(-5, 5, length=100)\nys = range(-10, 10, length=100)\n\ncontour(xs, ys, f.(xs, ys'); levels=[0])\n```\n\n::: {.cell-output .cell-output-display execution_count=53}\n![](makie_plotting_files/figure-html/cell-53-output-1.png){}\n:::\n:::\n\n\nThe `implicitPlots.jl` function uses the `Contour` package along with a `Plots` recipe to plot such graphs. Here we see how to use `Makie` in a similar manner:\n\n``` {.julia .cell-code}\nimport Contour\n\nfunction implicit_plot(xs, ys, f; kwargs...)\n fig = Figure()\n ax = Axis(fig[1,1])\n implicit_plot!(ax, xs, ys, f; kwargs...)\n fig\nend\n\nfunction implicit_plot!(ax, xs, ys, f; kwargs...)\n z = [f(x, y) for x in xs, y in ys]\n cs = Contour.contour(collect(xs), collect(ys), z, 0.0)\n ls = Contour.lines(cs)\n\n isempty(ls) && error(\"empty\")\n\n for l ∈ ls\n us, vs = Contour.coordinates(l)\n lines!(ax, us, vs; kwargs...)\n end\n\nend\n```\n\n\n#### Implicitly defined surfaces, $F(x,y,z)=0$\n\n\nTo plot the equation $F(x,y,z)=0$, for $F$ a scalar-valued function, again the implicit function theorem says that, under conditions, near any solution $(x,y,z)$, $z$ can be represented as a function of $x$ and $y$, so the graph will look likes surfaces stitched together. The `Implicit3DPlotting` package takes an approach like `ImplicitPlots` to represent these surfaces. It replaces the `Contour` package computation with a $3$-dimensional alternative provided through the `Meshing` and `GeometryBasics` packages.\n\n\nThe `Implicit3DPlotting` package needs some maintenance, so we borrow the main functionality and wrap it into a function:\n\n::: {.cell execution_count=54}\n``` {.julia .cell-code}\nimport Meshing\nimport GeometryBasics\n\nfunction make_mesh(xlims, ylims, zlims, f,\n M = Meshing.MarchingCubes(); # or Meshing.MarchingTetrahedra()\n samples=(35, 35, 35),\n )\n\n lims = extrema.((xlims, ylims, zlims))\n Δ = xs -> last(xs) - first(xs)\n xs = Vec(first.(lims))\n Δxs = Vec(Δ.(lims))\n\n GeometryBasics.Mesh(f, Rect(xs, Δxs), M; samples = samples)\nend\n```\n\n::: {.cell-output .cell-output-display execution_count=54}\n```\nmake_mesh (generic function with 2 methods)\n```\n:::\n:::\n\n\nThe `make_mesh` function creates a mesh that can be visualized with the `wireframe` or `mesh` plotting functions.\n\n\nThis example, plotting an implicitly defined sphere, comes from the documentation of `Implicit3DPlotting`. The `f` in `make_mesh` is a scalar-valued function of a vector:\n\n::: {.cell execution_count=55}\n``` {.julia .cell-code}\nf(x) = sum(x.^2) - 1\nxs = ys = zs = (-5, 5)\nm = make_mesh(xs, ys, zs, f)\nwireframe(m)\n```\n\n::: {.cell-output .cell-output-display execution_count=55}\n![](makie_plotting_files/figure-html/cell-56-output-1.png){}\n:::\n:::\n\n\nHere we visualize an intersection of a sphere with another figure:\n\n::: {.cell execution_count=56}\n``` {.julia .cell-code}\nr₂(x) = sum(x.^2) - 5/4 # a sphere\nr₄(x) = sum(x.^4) - 1\nxs = ys = zs = -2:2\nm2,m4 = make_mesh(xs, ys, zs, r₂), make_mesh(xs, ys, zs, r₄)\n\nwireframe(m4, color=:yellow)\nwireframe!(m2, color=:red)\ncurrent_figure()\n```\n\n::: {.cell-output .cell-output-display execution_count=56}\n![](makie_plotting_files/figure-html/cell-57-output-1.png){}\n:::\n:::\n\n\nThis example comes from [Wikipedia](https://en.wikipedia.org/wiki/Implicit_surface) showing an implicit surface of genus $2$:\n\n::: {.cell execution_count=57}\n``` {.julia .cell-code}\nf(x,y,z) = 2y*(y^2 -3x^2)*(1-z^2) + (x^2 +y^2)^2 - (9z^2-1)*(1-z^2)\nzs = ys = xs = range(-5/2, 5/2, length=100)\nm = make_mesh(xs, ys, zs, x -> f(x...))\nwireframe(m)\n```\n\n::: {.cell-output .cell-output-display execution_count=57}\n![](makie_plotting_files/figure-html/cell-58-output-1.png){}\n:::\n:::\n\n\n(This figure does not render well through `contour(xs, ys, zs, f, levels=[0])`, as the hole is not shown.)\n\n\nFor one last example from Wikipedia, we have the Cassini oval which \"can be defined as the point set for which the *product* of the distances to $n$ given points is constant.\" That is:\n\n::: {.cell execution_count=58}\n``` {.julia .cell-code}\nfunction cassini(λ, ps = ((1,0,0), (-1, 0, 0)))\n n = length(ps)\n x -> prod(norm(x .- p) for p ∈ ps) - λ^n\nend\nxs = ys = zs = range(-2, 2, length=100)\nm = make_mesh(xs, ys, zs, cassini(1.05))\nwireframe(m)\n```\n\n::: {.cell-output .cell-output-display execution_count=58}\n![](makie_plotting_files/figure-html/cell-59-output-1.png){}\n:::\n:::\n\n\n## Vector fields. Visualizations of $f:R^2 \\rightarrow R^2$\n\n\nThe vector field $f(x,y) = \\langle y, -x \\rangle$ can be visualized as a set of vectors, $f(x,y)$, positioned at a grid. These arrows can be visualized with the `arrows` function. The `arrows` function is passed a vector of points for the anchors and a vector of points representing the vectors.\n\n\nWe can generate these on a regular grid through:\n\n::: {.cell execution_count=59}\n``` {.julia .cell-code}\nf(x, y) = [y, -x]\nxs = ys = -5:5\npts = vec(Point2.(xs, ys'))\ndus = vec(Point2.(f.(xs, ys')));\nfirst(pts), first(dus) # show an example\n```\n\n::: {.cell-output .cell-output-display execution_count=59}\n```\n([-5, -5], [-5, 5])\n```\n:::\n:::\n\n\nBroadcasting over `(xs, ys')` ensures each pair of possible values is encountered. The `vec` call reshapes an array into a vector.\n\n\nCalling `arrows` on the prepared data produces the graphic:\n\n::: {.cell execution_count=60}\n``` {.julia .cell-code}\narrows(pts, dus)\n```\n\n::: {.cell-output .cell-output-display execution_count=60}\n![](makie_plotting_files/figure-html/cell-61-output-1.png){}\n:::\n:::\n\n\nThe grid seems rotated at first glance; but is also confusing. This is due to the length of the vectors as the $(x,y)$ values get farther from the origin. Plotting the *normalized* values (each will have length $1$) can be done easily using `norm` (which is found in the standard `LinearAlgebra` library):\n\n::: {.cell execution_count=61}\n``` {.julia .cell-code}\ndvs = dus ./ norm.(dus)\narrows(pts, dvs)\n```\n\n::: {.cell-output .cell-output-display execution_count=61}\n![](makie_plotting_files/figure-html/cell-62-output-1.png){}\n:::\n:::\n\n\nThe rotational pattern becomes much clearer now.\n\n\nThe `streamplot` function also illustrates this phenomenon. This implements an \"algorithm [that] puts an arrow somewhere and extends the streamline in both directions from there. Then, it chooses a new position (from the remaining ones), repeating the the exercise until the streamline gets blocked, from which on a new starting point, the process repeats.\"\n\n\nThe `streamplot` function expects a `Point` not a pair of values, so we adjust `f` slightly and call the function using the pattern `streamplot(g, xs, ys)`:\n\n::: {.cell execution_count=62}\n``` {.julia .cell-code}\nf(x, y) = [y, -x]\ng(xs) = Point2(f(xs...))\n\nstreamplot(g, -5..5, -5..5)\n```\n\n::: {.cell-output .cell-output-display execution_count=62}\n![](makie_plotting_files/figure-html/cell-63-output-1.png){}\n:::\n:::\n\n\n(We used interval notation to set the viewing range, a range could also be used.)\n\n\n:::{.callout-note}\n## Note\nThe calling pattern of `streamplot` is different than other functions, such as `surface`, in that the function comes first.\n\n:::\n\n## Layoutables and Observables\n\n\n### Layoutables\n\n\n`Makie` makes it really easy to piece together figures from individual plots. To illustrate, we create a graphic consisting of a plot of a function, its derivative, and its second derivative. In our graphic, we also leave space for a label.\n\n\n:::{.callout-note}\n## Note\nThe Layout [Tutorial](https://makie.juliaplots.org/stable/tutorials/layout-tutorial/) has *much* more detail on this subject.\n\n:::\n\nThe basic plotting commands, like `lines`, return a `FigureAxisPlot` object. For laying out our own graphic, we manage the figure and axes manually. The commands below create a figure, then assign axes to portions of the figure:\n\n::: {.cell execution_count=63}\n``` {.julia .cell-code}\nF = Figure()\naf = F[2,1:2] = Axis(F)\nafp = F[3,1:end] = Axis(F)\nafpp = F[4,:] = Axis(F)\n```\n\n::: {.cell-output .cell-output-display execution_count=63}\n```\nAxis with 1 plots:\n ┗━ Mesh{Tuple{GeometryBasics.Mesh{3, Float32, GeometryBasics.TriangleP{3, Float32, GeometryBasics.PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}}, GeometryBasics.FaceView{GeometryBasics.TriangleP{3, Float32, GeometryBasics.PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}}, GeometryBasics.PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}, GeometryBasics.NgonFace{3, GeometryBasics.OffsetInteger{-1, UInt32}}, StructArrays.StructVector{GeometryBasics.PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}, NamedTuple{(:position, :normals), Tuple{Vector{Point{3, Float32}}, Vector{Vec{3, Float32}}}}, Int64}, Vector{GeometryBasics.NgonFace{3, GeometryBasics.OffsetInteger{-1, UInt32}}}}}}}\n```\n:::\n:::\n\n\nThe axes are named `af`, `afp` and `afpp`, as they will hold the respective graphs. The key here is the use of matrix notation to layout the graphic in a grid. The first one is row 2 and columns 1 through 2; the second row 3 and again all columns, the third is row 4 and all columns.\n\n\nIn this figure, we want the $x$-axis for each of the three graphics to be linked. This command ensures that:\n\n``` {.julia .cell-code}\nlinkxaxes!(af, afp, afpp);\n```\n\n\nBy linking axes, if one is updated, say through `xlims!`, the others will be as well.\n\n\nWe now plot our functions. The key here is the mutating form of `lines!` takes an axis object to mutate as its first argument:\n\n``` {.julia .cell-code}\nf(x) = 8x^4 - 8x^2 + 1\nfp(x) = 32x^3 - 16x\nfpp(x) = 96x^2 - 16\n\nxs = -1..1\nlines!(af, xs, f)\nlines!(afp, xs, fp)\nlines!(afp, xs, zero, color=:blue)\nlines!(afpp, xs, fpp)\nlines!(afpp, xs, zero, color=:blue);\n```\n\n\nWe can give title information to each axis:\n\n``` {.julia .cell-code}\naf.title = \"f\"\nafp.title = \"fp\"\nafpp.title = \"fpp\";\n```\n\n\nFinally, we add a label in the first row, but for illustration purposes, only use the first column.\n\n``` {.julia .cell-code}\nLabel(F[1,1], \"\"\"\nPlots of f and its first and second derivatives.\nWhen the first derivative is zero, the function\nf has relative extrema. When the second derivative\nis zero, the function f has an inflection point.\n\"\"\");\n```\n\n\nFinally we display the figure:\n\n::: {.cell execution_count=68}\n``` {.julia .cell-code}\nF\n```\n\n::: {.cell-output .cell-output-display execution_count=68}\n![](makie_plotting_files/figure-html/cell-69-output-1.png){}\n:::\n:::\n\n\n### Observables\n\n\nThe basic components of a plot in `Makie` can be updated [interactively](https://makie.juliaplots.org/stable/documentation/nodes/index.html#observables_interaction). `Makie` uses the `Observables` package which allows complicated interactions to be modeled quite naturally. In the following we give a simple example.\n\n\nIn Makie, an `Observable` is a structure that allows its value to be updated, similar to an array. When changed, observables can trigger an event. Observables can rely on other observables, so events can be cascaded.\n\n\nThis simple example shows how an observable `h` can be used to create a collection of points representing a secant line. The figure shows the value for `h=3/2`.\n\n::: {.cell execution_count=69}\n``` {.julia .cell-code}\nf(x) = sqrt(x)\nc = 1\nxs = 0..3\nh = Observable(3/2)\n\npoints = lift(h) do h\n xs = [0,c,c+h,3]\n tl = x -> f(c) + (f(c+h)-f(c))/h * (x-c)\n [Point2(x, tl(x)) for x ∈ xs]\nend\n\nlines(xs, f)\nlines!(points)\ncurrent_figure()\n```\n\n::: {.cell-output .cell-output-display execution_count=69}\n![](makie_plotting_files/figure-html/cell-70-output-1.png){}\n:::\n:::\n\n\nWe can update the value of `h` using `setindex!` notation (square brackets). For example, to see that the secant line is a good approximation to the tangent line as $h \\rightarrow 0$ we can set `h` to be `1/4` and replot:\n\n::: {.cell execution_count=70}\n``` {.julia .cell-code}\nh[] = 1/4\ncurrent_figure()\n```\n\n::: {.cell-output .cell-output-display execution_count=70}\n![](makie_plotting_files/figure-html/cell-71-output-1.png){}\n:::\n:::\n\n\nThe line `h[] = 1/4` updated `h` which then updated `points` (a points is lifted up from `h`) which updated the graphic. (In these notes, we replot to see the change, but in an interactive session, the current *displayed* figure would be updated; no replotting would be necessary.)\n\n\nFinally, this example shows how to add a slider to adjust the value of `h` with a mouse. The slider object is positioned along with a label using the grid reference, as before.\n\n::: {.cell execution_count=71}\n``` {.julia .cell-code}\nf(x) = sqrt(x)\nc = 1\nxs = 0..3\n\nF = Figure()\nax = Axis(F[1,1:2])\nh = Slider(F[2,2], range = 0.01:0.01:1.5, startvalue = 1.5)\nLabel(F[2,1], \"Adjust slider to change `h`\";\n justification = :left)\n\npoints = lift(h.value) do h\n xs = [0,c,c+h,3]\n tl = x-> f(c) + (f(c+h)-f(c))/h * (x-c)\n [Point2(x, tl(x)) for x ∈ xs]\nend\n\nlines!(ax, xs, f)\nlines!(ax, points)\ncurrent_figure()\n```\n\n::: {.cell-output .cell-output-display execution_count=71}\n![](makie_plotting_files/figure-html/cell-72-output-1.png){}\n:::\n:::\n\n\nThe slider value is \"lifted\" by its `value` component, as shown. Otherwise, the above is fairly similar to just using an observable for `h`.\n\n",
"supporting": [
"makie_plotting_files"
],
"filters": [],
"includes": {}
}
}