> 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!
use the interface provided by the `GLMakie` backend to produce the
familiar graphics of calculus.
!!! note "Examples and tutorials"
`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.
Makie 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.
For `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.
The 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`:
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.
`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.
The 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.
The 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.
To 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.
To *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.
The `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.)
This 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.
```julia
x = -5:5
δ = 5eps() # for rounding purposes; our interval is [i,i+1) ≈ [i, i+1-δ]
Text 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.
For example:
```julia
xs = 1:5
pts = Point2.(xs, xs)
scatter(pts)
annotations!("Point " .* string.(xs), pts;
textsize = 50 .- 2*xs,
rotation = 2pi ./ xs)
current_figure()
```
The 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.)
Attributes for `text`, among many others, include:
* `align` Specify the text alignment through `(:pos, :pos)`, where `:pos` can be `:left`, `:center`, or `:right`.
* `rotation` to indicate how the text is to be rotated
* `textsize` the font point size for the text
* `font` to indicate the desired font
#### Line attributes
In 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.
Later, we will see how to control the placement of a legend within a figure.
#### Titles, axis labels, axis ticks
The 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.
To 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:
As 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:
The `;` 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.
To 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:
Alternatively, 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:
A 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:
A 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`:
The 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)``.
Alternatively 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.
A 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))``.
The `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:
For 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.
A *filled* contour plot is produced by `contourf`:
```julia
contourf(xs, ys, peaks)
```
A 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`:
The `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``.
The `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:
To 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!`:
The `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:
Suppose ``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.
#### Implicitly defined curves
The 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.
The contour graph can produce these graphs by setting the `levels` argument to `[0]`.
The `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:
For 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:
## Vector fields. Visualizations of ``f:R^2 \rightarrow R^2``
The 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.
The 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):
The `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."
The `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)`:
`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.
The 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:
The 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.
The 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.
This 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`.
We 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:
The 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.)
Finally, 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.