{ "hash": "445fdcf8bf8833cc50c9d4d23547cbf0", "result": { "markdown": "# JavaScript based plotting libraries\n\n\n\n:::{.callout-note}\n## Not working with quarto\nCurrently, the plots generated here are not rendering within quarto.\n\n:::\n\nThis section uses this add-on package:\n\n``` {.julia .cell-code}\nusing PlotlyLight\n```\n\n\nTo avoid a dependence on the `CalculusWithJulia` package, we load two utility packages:\n\n``` {.julia .cell-code}\nusing PlotUtils\nusing SplitApplyCombine\n```\n\n\n---\n\n\n`Julia` has different interfaces to a few JavaScript plotting libraries, notably the [vega](https://vega.github.io/vega/) and [vega-lite](https://vega.github.io/vega-lite/) through the [VegaLite.jl](https://github.com/queryverse/VegaLite.jl) package, and [plotly](https://plotly.com/javascript/) through several interfaces: `Plots.jl`, `PlotlyJS.jl`, and `PlotlyLight.jl`. These all make web-based graphics, for display through a web browser.\n\n\nThe `Plots.jl` interface is a backend for the familiar `Plots` package, making the calling syntax familiar, as is used throughout these notes. The `plotly()` command, from `Plots`, switches to this backend.\n\n\nThe `PlotlyJS.jl` interface offers direct translation from `Julia` structures to the underlying `JSON` structures needed by plotly, and has mechanisms to call back into `Julia` from `JavaScript`. This allows complicated interfaces to be produced.\n\n\nHere we discuss `PlotlyLight` which conveniently provides the translation from `Julia` structures to the `JSON` structures needed in a light-weight package, which plots quickly, without the delays due to compilation of the more complicated interfaces. Minor modifications would be needed to adjust the examples to work with `PlotlyJS` or `PlotlyBase`. The documentation for the `JavaScript` [library](https://plotly.com/javascript/) provides numerous examples which can easily be translated. The [one-page-reference](https://plotly.com/javascript/reference/) gives specific details, and is quoted from below, at times.\n\n\nThis discussion covers the basic of graphing for calculus purposes. It does not cover, for example, the faceting common in statistical usages, or the chart types common in business and statistics uses. The `plotly` library is much more extensive than what is reviewed below.\n\n\n## Julia dictionaries to JSON\n\n\n`PlotlyLight` uses the `JavaScript` interface for the `plotly` libraries. Unlike more developed interfaces, like the one for `Python`, `PlotlyLight` only manages the translation from `Julia` structures to `JavaScript` structures and the display of the results.\n\n\nThe key to translation is the mapping for `Julia`'s dictionaries to the nested `JSON` structures needed by the `JavaScript` library.\n\n\nFor example, an introductory [example](https://plotly.com/javascript/line-and-scatter/) for a scatter plot includes this `JSON` structure:\n\n``` {.julia .cell-code}\nvar trace1 = {\n x: [1, 2, 3, 4],\n y: [10, 15, 13, 17],\n mode: 'markers',\n type: 'scatter'\n};\n```\n\n\nThe `{}` create a list, the `[]` an Array (or vector, as it does with `Julia`), the `name:` are keys. The above is simply translated via:\n\n::: {.cell execution_count=5}\n``` {.julia .cell-code}\nConfig(x = [1,2,3,4],\n y = [10, 15, 13, 17],\n mode = \"markers\",\n type = \"scatter\"\n )\n```\n\n::: {.cell-output .cell-output-display execution_count=5}\n```\nConfig with 4 entries:\n :x => [1, 2, 3, 4]\n :y => [10, 15, 13, 17]\n :mode => \"markers\"\n :type => \"scatter\"\n```\n:::\n:::\n\n\nThe `Config` constructor (from the `EasyConfig` package loaded with `PlotlyLight`) is an interface for a dictionary whose keys are symbols, which are produced by the named arguments passed to `Config`. By nesting `Config` statements, nested `JavaScript` structures can be built up. As well, these can be built on the fly using `.` notation, as in:\n\n::: {.cell hold='true' execution_count=6}\n``` {.julia .cell-code}\ncfg = Config()\ncfg.key1.key2.key3 = \"value\"\ncfg\n```\n\n::: {.cell-output .cell-output-display execution_count=6}\n```\nConfig with 1 entry:\n :key1 => Config(:key2=>Config(:key3=>\"value\"))\n```\n:::\n:::\n\n\nTo produce a figure with `PlotlyLight` then is fairly straightforward: data and, optionally, a layout are created using `Config`, then passed along to the `Plot` command producing a `Plot` object which has `display` methods defined for it. This will be illustrated through the examples.\n\n\n## Scatter plot\n\n\nA basic scatter plot of points $(x,y)$ is created as follows:\n\n::: {.cell hold='true' execution_count=7}\n``` {.julia .cell-code}\nxs = 1:5\nys = rand(5)\ndata = Config(x = xs,\n y = ys,\n type=\"scatter\",\n mode=\"markers\"\n )\nPlot(data)\n```\n\n::: {.cell-output .cell-output-display execution_count=7}\n```{=html}\n
\n
\n
\n\n\n```\n:::\n:::\n\n\nThe symbols `x` and `y` (and later `z`) specify the data to `plotly`. Here the `mode` is specified to show markers.\n\n\nThe `type` key specifies the chart or trace type. The `mode` specification sets the drawing mode for the trace. Above it is \"markers\". It can be any combination of \"lines\", \"markers\", or \"text\" joined with a \"+\" if more than one is desired.\n\n\n## Line plot\n\n\nA line plot is very similar, save for a different `mode` specification:\n\n::: {.cell hold='true' execution_count=8}\n``` {.julia .cell-code}\nxs = 1:5\nys = rand(5)\ndata = Config(x = xs,\n y = ys,\n type=\"scatter\",\n mode=\"lines\"\n )\nPlot(data)\n```\n\n::: {.cell-output .cell-output-display execution_count=8}\n```{=html}\n
\n
\n
\n\n\n```\n:::\n:::\n\n\nThe difference is solely the specification of the `mode` value, for a line plot it is \"lines,\" for a scatter plot it is \"markers\" The `mode` \"lines+markers\" will plot both. The default for the \"scatter\" types is to use \"lines+markers\" for small data sets, and \"lines\" for others, so for this example, `mode` could be left off.\n\n\n### Nothing\n\n\nThe line graph plays connect-the-dots with the points specified by paired `x` and `y` values. *Typically*, when and `x` value is `NaN` that \"dot\" (or point) is skipped. However, `NaN` doesn't pass through the JSON conversion – `nothing` can be used.\n\n::: {.cell hold='true' execution_count=9}\n``` {.julia .cell-code}\ndata = Config(\n x=[0,1,nothing,3,4,5],\n\ty = [0,1,2,3,4,5],\n type=\"scatter\", mode=\"markers+lines\")\nPlot(data)\n```\n\n::: {.cell-output .cell-output-display execution_count=9}\n```{=html}\n
\n
\n
\n\n\n```\n:::\n:::\n\n\n## Multiple plots\n\n\nMore than one graph or layer can appear on a plot. The `data` argument can be a vector of `Config` values, each describing a plot. For example, here we make a scatter plot and a line plot:\n\n::: {.cell hold='true' execution_count=10}\n``` {.julia .cell-code}\ndata = [Config(x = 1:5,\n y = rand(5),\n type = \"scatter\",\n mode = \"markers\",\n name = \"scatter plot\"),\n Config(x = 1:5,\n y = rand(5),\n type = \"scatter\",\n mode = \"lines\",\n name = \"line plot\")\n ]\nPlot(data)\n```\n\n::: {.cell-output .cell-output-display execution_count=10}\n```{=html}\n
\n
\n
\n\n\n```\n:::\n:::\n\n\nThe `name` argument adjusts the name in the legend referencing the plot. This is produced by default.\n\n\n### Adding a layer\n\n\nIn `PlotlyLight`, the `Plot` object has a field `data` for storing a vector of configurations, as above. After a plot is made, this field can have values pushed onto it and the corresponding layers will be rendered when the plot is redisplayed.\n\n\nFor example, here we plot the graphs of both the $\\sin(x)$ and $\\cos(x)$ over $[0,2\\pi]$. We used the utility `PlotUtils.adapted_grid` to select the points to use for the graph.\n\n::: {.cell hold='true' execution_count=11}\n``` {.julia .cell-code}\na, b = 0, 2pi\n\nxs, ys = PlotUtils.adapted_grid(sin, (a,b))\np = Plot(Config(x=xs, y=ys, name=\"sin\"))\n\nxs, ys = PlotUtils.adapted_grid(cos, (a,b))\npush!(p.data, Config(x=xs, y=ys, name=\"cos\"))\n\np # to display the plot\n```\n\n::: {.cell-output .cell-output-display execution_count=11}\n```{=html}\n
\n
\n
\n\n\n```\n:::\n:::\n\n\nThe values for `a` and `b` are used to generate the $x$- and $y$-values. These can also be gathered from the existing plot object. Here is one way, where for each trace with an `x` key, the extrema are consulted to update a list of left and right ranges.\n\n::: {.cell hold='true' execution_count=12}\n``` {.julia .cell-code}\nxs, ys = PlotUtils.adapted_grid(x -> x^5 - x - 1, (0, 2)) # answer is (0,2)\np = Plot([Config(x=xs, y=ys, name=\"Polynomial\"),\n Config(x=xs, y=0 .* ys, name=\"x-axis\", mode=\"lines\", line=Config(width=5))]\n )\nds = filter(d -> !isnothing(get(d, :x, nothing)), p.data)\na=reduce(min, [minimum(d.x) for d ∈ ds]; init=Inf)\nb=reduce(max, [maximum(d.x) for d ∈ ds]; init=-Inf)\n(a, b)\n```\n\n::: {.cell-output .cell-output-display execution_count=12}\n```\n(0.0, 2.0)\n```\n:::\n:::\n\n\n## Interactivity\n\n\n`JavaScript` allows interaction with a plot as it is presented within a browser. (Not the `Julia` process which produced the data or the plot. For that interaction, `PlotlyJS` may be used.) The basic *default* features are:\n\n\n * The data producing a graphic are displayed on hover using flags.\n * The legend may be clicked to toggle whether the corresponding graph is displayed.\n * The viewing region can be narrowed using the mouse for selection.\n * The toolbar has several features for panning and zooming, as well as adjusting the information shown on hover.\n\n\nLater we will see that $3$-dimensional surfaces can be rotated interactively.\n\n\n## Plot attributes\n\n\nAttributes of the markers and lines may be adjusted when the data configuration is specified. A selection is shown below. Consult the reference for the extensive list.\n\n\n### Marker attributes\n\n\nA marker's attributes can be adjusted by values passed to the `marker` key. Labels for each marker can be assigned through a `text` key and adding `text` to the `mode` key. For example:\n\n::: {.cell hold='true' execution_count=13}\n``` {.julia .cell-code}\ndata = Config(x = 1:5,\n y = rand(5),\n mode=\"markers+text\",\n type=\"scatter\",\n name=\"scatter plot\",\n text = [\"marker $i\" for i in 1:5],\n textposition = \"top center\",\n marker = Config(size=12, color=:blue)\n )\nPlot(data)\n```\n\n::: {.cell-output .cell-output-display execution_count=13}\n```{=html}\n
\n
\n
\n\n\n```\n:::\n:::\n\n\nThe `text` mode specification is necessary to have text be displayed on the chart, and not just appear on hover. The `size` and `color` attributes are recycled; they can be specified using a vector for per-marker styling. Here the symbol `:blue` is used to specify a color, which could also be a name, such as `\"blue\"`.\n\n\n#### RGB Colors\n\n\nThe `ColorTypes` package is the standard `Julia` package providing an `RGB` type (among others) for specifying red-green-blue colors. To make this work with `Config` and `JSON3` requires some type-piracy (modifying `Base.string` for the `RGB` type) to get, say, `RGB(0.5, 0.5, 0.5)` to output as `\"rgb(0.5, 0.5, 0.5)\"`. (RGB values in JavaScript are integers between $0$ and $255$ or floating point values between $0$ and $1$.) A string with this content can be specified. Otherwise, something like the following can be used to avoid the type piracy:\n\n``` {.julia .cell-code}\nstruct rgb\n r\n g\n b\nend\nPlotlyLight.JSON3.StructTypes.StructType(::Type{rgb}) = PlotlyLight.JSON3.StructTypes.StringType()\nBase.string(x::rgb) = \"rgb($(x.r), $(x.g), $(x.b))\"\n```\n\n\nWith these defined, red-green-blue values can be used for colors. For example to give a range of colors, we might have:\n\n::: {.cell hold='true' execution_count=15}\n``` {.julia .cell-code}\ncols = [rgb(i,i,i) for i in range(10, 245, length=5)]\nsizes = [12, 16, 20, 24, 28]\ndata = Config(x = 1:5,\n y = rand(5),\n mode=\"markers+text\",\n type=\"scatter\",\n name=\"scatter plot\",\n text = [\"marker $i\" for i in 1:5],\n textposition = \"top center\",\n marker = Config(size=sizes, color=cols)\n )\nPlot(data)\n```\n\n::: {.cell-output .cell-output-display execution_count=15}\n```{=html}\n
\n
\n
\n\n\n```\n:::\n:::\n\n\nThe `opacity` key can be used to control the transparency, with a value between $0$ and $1$.\n\n\n#### Marker symbols\n\n\nThe `marker_symbol` key can be used to set a marker shape, with the basic values being: `circle`, `square`, `diamond`, `cross`, `x`, `triangle`, `pentagon`, `hexagram`, `star`, `diamond`, `hourglass`, `bowtie`, `asterisk`, `hash`, `y`, and `line`. Add `-open` or `-open-dot` modifies the basic shape.\n\n::: {.cell hold='true' execution_count=16}\n``` {.julia .cell-code}\nmarkers = [\"circle\", \"square\", \"diamond\", \"cross\", \"x\", \"triangle\", \"pentagon\",\n \"hexagram\", \"star\", \"diamond\", \"hourglass\", \"bowtie\", \"asterisk\",\n \"hash\", \"y\", \"line\"]\nn = length(markers)\ndata = [Config(x=1:n, y=1:n, mode=\"markers\",\n marker = Config(symbol=markers, size=10)),\n Config(x=1:n, y=2 .+ (1:n), mode=\"markers\",\n marker = Config(symbol=markers .* \"-open\", size=10)),\n Config(x=1:n, y=4 .+ (1:n), mode=\"markers\",\n marker = Config(symbol=markers .* \"-open-dot\", size=10))\n ]\nPlot(data)\n```\n\n::: {.cell-output .cell-output-display execution_count=16}\n```{=html}\n
\n
\n
\n\n\n```\n:::\n:::\n\n\n### Line attributes\n\n\nThe `line` key can be used to specify line attributes, such as `width` (pixel width), `color`, or `dash`.\n\n\nThe `width` key specifies the line width in pixels.\n\n\nThe `color` key specifies the color of the line drawn.\n\n\nThe `dash` key specifies the style for the drawn line. Values can be set by string from \"solid\", \"dot\", \"dash\", \"longdash\", \"dashdot\", or \"longdashdot\" or set by specifying a pattern in pixels, e.g. \"5px,10px,2px,2px\".\n\n\nThe `shape` attribute determine how the points are connected. The default is `linear`, but other possibilities are `hv`, `vh`, `hvh`, `vhv`, `spline` for various patterns of connectivity. The following example, from the plotly documentation, shows the differences:\n\n::: {.cell hold='true' execution_count=17}\n``` {.julia .cell-code}\nshapes = [\"linear\", \"hv\", \"vh\", \"hvh\", \"vhv\", \"spline\"]\ndata = [Config(x = 1:5, y = 5*(i-1) .+ [1,3,2,3,1], mode=\"lines+markers\", type=\"scatter\",\n name=shape,\n line=Config(shape=shape)\n ) for (i, shape) ∈ enumerate(shapes)]\nPlot(data)\n```\n\n::: {.cell-output .cell-output-display execution_count=17}\n```{=html}\n
\n
\n
\n\n\n```\n:::\n:::\n\n\n### Text\n\n\nThe text associated with each point can be drawn on the chart, when \"text\" is included in the `mode` or shown on hover.\n\n\nThe onscreen text is passed to the `text` attribute. The [`texttemplate`](https://plotly.com/javascript/reference/scatter/#scatter-texttemplate) key can be used to format the text with details in the accompanying link.\n\n\nSimilarly, the `hovertext` key specifies the text shown on hover, with [`hovertemplate`](https://plotly.com/javascript/reference/scatter/#scatter-hovertemplate) used to format the displayed text.\n\n\n### Filled regions\n\n\nThe `fill` key for a chart of mode `line` specifies how the area around a chart should be colored, or filled. The specification are declarative, with values in \"none\", \"tozeroy\", \"tozerox\", \"tonexty\", \"tonextx\", \"toself\", and \"tonext\". The value of \"none\" is the default, unless stacked traces are used.\n\n\nIn the following, to highlight the difference between $f(x) = \\cos(x)$ and $p(x) = 1 - x^2/2$ the area from $f$ to the next $y$ is declared; for $p$, the area to $0$ is declared.\n\n::: {.cell hold='true' execution_count=18}\n``` {.julia .cell-code}\nxs = range(-1, 1, 100)\ndata = [\n Config(\n\t x=xs, y=cos.(xs),\n\t fill = \"tonexty\",\n\t fillcolor = \"rgba(0,0,255,0.25)\", # to get transparency\n\t line = Config(color=:blue)\n ),\n\tConfig(\n\t\tx=xs, y=[1 - x^2/2 for x ∈ xs ],\n\t\tfill = \"tozeroy\",\n\t\tfillcolor = \"rgba(255,0,0,0.25)\", # to get transparency\n\t\tline = Config(color=:red)\n\t)\n]\nPlot(data)\n```\n\n::: {.cell-output .cell-output-display execution_count=18}\n```{=html}\n
\n
\n
\n\n\n```\n:::\n:::\n\n\nThe `toself` declaration is used below to fill in a polygon:\n\n::: {.cell hold='true' execution_count=19}\n``` {.julia .cell-code}\ndata = Config(\n\tx=[-1,1,1,-1,-1], y = [-1,1,-1,1,-1],\n\tfill=\"toself\",\n\ttype=\"scatter\")\nPlot(data)\n```\n\n::: {.cell-output .cell-output-display execution_count=19}\n```{=html}\n
\n
\n
\n\n\n```\n:::\n:::\n\n\n## Layout attributes\n\n\nThe `title` key sets the main title; the `title` key in the `xaxis` configuration sets the $x$-axis title (similarly for the $y$ axis).\n\n\nThe legend is shown when $2$ or more charts or specified, by default. This can be adjusted with the `showlegend` key, as below. The legend shows the corresponding `name` for each chart.\n\n::: {.cell hold='true' execution_count=20}\n``` {.julia .cell-code}\ndata = Config(x=1:5, y=rand(5), type=\"scatter\", mode=\"markers\", name=\"legend label\")\nlyt = Config(title = \"Main chart title\",\n xaxis = Config(title=\"x-axis label\"),\n yaxis = Config(title=\"y-axis label\"),\n showlegend=true\n )\nPlot(data, lyt)\n```\n\n::: {.cell-output .cell-output-display execution_count=20}\n```{=html}\n
\n
\n
\n\n\n```\n:::\n:::\n\n\nThe `xaxis` and `yaxis` keys have many customizations. For example: `nticks` specifies the maximum number of ticks; `range` to set the range of the axis; `type` to specify the axis type from \"linear\", \"log\", \"date\", \"category\", or \"multicategory;\" and `visible`\n\n\nThe aspect ratio of the chart can be set to be equal through the `scaleanchor` key, which specifies another axis to take a value from. For example, here is a parametric plot of a circle:\n\n::: {.cell hold='true' execution_count=21}\n``` {.julia .cell-code}\nts = range(0, 2pi, length=100)\ndata = Config(x = sin.(ts), y = cos.(ts), mode=\"lines\", type=\"scatter\")\nlyt = Config(title = \"A circle\",\n xaxis = Config(title = \"x\"),\n yaxis = Config(title = \"y\",\n scaleanchor = \"x\")\n )\nPlot(data, lyt)\n```\n\n::: {.cell-output .cell-output-display execution_count=21}\n```{=html}\n
\n
\n
\n\n\n```\n:::\n:::\n\n\n#### Annotations\n\n\nText annotations may be specified as part of the layout object. Annotations may or may not show an arrow. Here is a simple example using a vector of annotations.\n\n::: {.cell hold='true' execution_count=22}\n``` {.julia .cell-code}\ndata = Config(x = [0, 1], y = [0, 1], mode=\"markers\", type=\"scatter\")\nlayout = Config(title = \"Annotations\",\n xaxis = Config(title=\"x\",\n range = (-0.5, 1.5)),\n yaxis = Config(title=\"y\",\n range = (-0.5, 1.5)),\n annotations = [\n Config(x=0, y=0, text = \"(0,0)\"),\n Config(x=1, y=1.2, text = \"(1,1)\", showarrow=false)\n ]\n )\nPlot(data, layout)\n```\n\n::: {.cell-output .cell-output-display execution_count=22}\n```{=html}\n
\n
\n
\n\n\n```\n:::\n:::\n\n\nThe following example is more complicated use of the elements previously described. It mimics an image from [Wikipedia](https://en.wikipedia.org/wiki/List_of_trigonometric_identities) for trigonometric identities. The use of $\\LaTeX$ does not seem to be supported through the `JavaScript` interface; unicode symbols are used instead. The `xanchor` and `yanchor` keys are used to position annotations away from the default. The `textangle` key is used to rotate text, as desired.\n\n::: {.cell execution_count=23}\n``` {.julia .cell-code}\nalpha = pi/6\nbeta = pi/5\nxₘ = cos(alpha)*cos(beta)\nyₘ = sin(alpha+beta)\nr₀ = 0.1\n\ndata = [\n\tConfig(\n\t\tx = [0,xₘ, xₘ, 0, 0],\n\t\ty = [0, 0, yₘ, yₘ, 0],\n\t\ttype=\"scatter\", mode=\"line\"\n\t),\n\tConfig(\n\t\tx = [0, xₘ],\n\t\ty = [0, sin(alpha)*cos(beta)],\n\t\tfill = \"tozeroy\",\n\t\tfillcolor = \"rgba(100, 100, 100, 0.5)\"\n\t),\n\tConfig(\n\t\tx = [0, cos(alpha+beta), xₘ],\n\t\ty = [0, yₘ, sin(alpha)*cos(beta)],\n\t\tfill = \"tonexty\",\n\t\tfillcolor = \"rgba(200, 0, 100, 0.5)\",\n\t),\n\tConfig(\n\t\tx = [0, cos(alpha+beta)],\n\t\ty = [0, yₘ],\n\t\tline = Config(width=5, color=:black)\n\t)\n]\n\nlyt = Config(\n\theight=450,\n\tshowlegend=false,\n\txaxis=Config(visible=false),\n\tyaxis = Config(visible=false, scaleanchor=\"x\"),\n\tannotations = [\n\n\t\tConfig(x = r₀*cos(alpha/2), y = r₀*sin(alpha/2),\n\t\t\t text=\"α\", showarrow=false),\n\t\tConfig(x = r₀*cos(alpha+beta/2), y = r₀*sin(alpha+beta/2),\n\t\t\t text=\"β\", showarrow=false),\n\t\tConfig(x = cos(alpha+beta) + r₀*cos(pi+(alpha+beta)/2),\n\t\t\t y = yₘ + r₀*sin(pi+(alpha+beta)/2),\n\t\t\t xanchor=\"center\", yanchor=\"center\",\n\t\t\t text=\"α+β\", showarrow=false),\n\t\tConfig(x = xₘ + r₀*cos(pi/2+alpha/2),\n\t\t\t y = sin(alpha)*cos(beta) + r₀ * sin(pi/2 + alpha/2),\n\t\t\t text=\"α\", showarrow=false),\n\t\tConfig(x = 1/2 * cos(alpha+beta),\n y = 1/2 * sin(alpha+beta),\n\t\t\t text = \"1\"),\n\t\tConfig(x = xₘ/2*cos(alpha), y = xₘ/2*sin(alpha),\n\t\t\t xanchor=\"center\", yanchor=\"bottom\",\n\t\t\t text = \"cos(β)\",\n\t\t\t textangle=-rad2deg(alpha),\n\t\t\t showarrow=false),\n\t\tConfig(x = xₘ + sin(beta)/2*cos(pi/2 + alpha),\n\t\t\t y = sin(alpha)*cos(beta) + sin(beta)/2*sin(pi/2 + alpha),\n\t\t\t xanchor=\"center\", yanchor=\"top\",\n\t\t\t text = \"sin(β)\",\n\t\t\t textangle = rad2deg(pi/2-alpha),\n\t\t\t showarrow=false),\n\n\t\tConfig(x = xₘ/2,\n y = 0,\n\t\t\t xanchor=\"center\", yanchor=\"top\",\n\t\t\t text = \"cos(α)⋅cos(β)\", showarrow=false),\n\t\tConfig(x = 0,\n y = yₘ/2,\n\t\t\t xanchor=\"right\", yanchor=\"center\",\n\t\t\t text = \"sin(α+β)\",\n\t\t\t textangle=-90,\n\t\t\t showarrow=false),\n\t\tConfig(x = cos(alpha+beta)/2,\n y = yₘ,\n\t\t\t xanchor=\"center\", yanchor=\"bottom\",\n\t\t\t text = \"cos(α+β)\", showarrow=false),\n\t\tConfig(x = cos(alpha+beta) + (xₘ - cos(alpha+beta))/2,\n y = yₘ,\n\t\t\t xanchor=\"center\", yanchor=\"bottom\",\n\t\t\t text = \"sin(α)⋅sin(β)\", showarrow=false),\n\t\tConfig(x = xₘ, y=sin(alpha)*cos(beta) + (yₘ - sin(alpha)*cos(beta))/2,\n\t\t\t xanchor=\"left\", yanchor=\"center\",\n\t\t\t text = \"cos(α)⋅sin(β)\",\n\t\t\t textangle=90,\n\t\t\t showarrow=false),\n\t\tConfig(x = xₘ,\n y = sin(alpha)*cos(beta)/2,\n\t\t\t xanchor=\"left\", yanchor=\"center\",\n\t\t\t text = \"sin(α)⋅cos(β)\",\n\t\t\t textangle=90,\n\t\t\t showarrow=false)\n\t]\n)\n\nPlot(data, lyt)\n```\n\n::: {.cell-output .cell-output-display execution_count=23}\n```{=html}\n
\n
\n
\n\n\n```\n:::\n:::\n\n\n## Parameterized curves\n\n\nIn $2$-dimensions, the plotting of a parameterized curve is similar to that of plotting a function. In $3$-dimensions, an extra $z$-coordinate is included.\n\n\nTo help, we define an `unzip` function as an interface to `SplitApplyCombine`'s `invert` function:\n\n::: {.cell execution_count=24}\n``` {.julia .cell-code}\nunzip(v) = SplitApplyCombine.invert(v)\n```\n\n::: {.cell-output .cell-output-display execution_count=24}\n```\nunzip (generic function with 1 method)\n```\n:::\n:::\n\n\nEarlier, we plotted a two dimensional circle, here we plot the related helix.\n\n::: {.cell hold='true' execution_count=25}\n``` {.julia .cell-code}\nhelix(t) = [cos(t), sin(t), t]\n\nts = range(0, 4pi, length=200)\n\nxs, ys, zs = unzip(helix.(ts))\n\ndata = Config(x=xs, y=ys, z=zs,\n type = \"scatter3d\", # <<- note the 3d\n mode = \"lines\",\n line=(width=2,\n color=:red)\n )\n\nPlot(data)\n```\n\n::: {.cell-output .cell-output-display execution_count=25}\n```{=html}\n
\n
\n
\n\n\n```\n:::\n:::\n\n\nThe main difference is the chart type, as this is a $3$-dimensional plot, \"scatter3d\" is used.\n\n\n### Quiver plots\n\n\nThere is no `quiver` plot for `plotly` using JavaScript. In $2$-dimensions a text-less annotation could be employed. In $3$-dimensions, the following (from [stackoverflow.com](https://stackoverflow.com/questions/43164909/plotlypython-how-to-plot-arrows-in-3d)) is a possible workaround where a line segment is drawn and capped with a small cone. Somewhat opaquely, we use `NamedTuple` for an iterator to create the keys for the data below:\n\n::: {.cell hold='true' execution_count=26}\n``` {.julia .cell-code}\nhelix(t) = [cos(t), sin(t), t]\nhelix′(t) = [-sin(t), cos(t), 1]\nts = range(0, 4pi, length=200)\nxs, ys, zs = unzip(helix.(ts))\nhelix_trace = Config(; NamedTuple(zip((:x,:y,:z), unzip(helix.(ts))))...,\n type = \"scatter3d\", # <<- note the 3d\n mode = \"lines\",\n line=(width=2,\n color=:red)\n )\n\ntss = pi/2:pi/2:7pi/2\nrs, r′s = helix.(tss), helix′.(tss)\n\narrows = [\n\tConfig(x = [x[1], x[1]+x′[1]],\n\t\t y = [x[2], x[2]+x′[2]],\n\t\t z = [x[3], x[3]+x′[3]],\n\t\t mode=\"lines\", type=\"scatter3d\")\n\tfor (x, x′) ∈ zip(rs, r′s)\n]\n\ntips = rs .+ r′s\nlengths = 0.1 * r′s\n\ncaps = Config(;\n\t\tNamedTuple(zip([:x,:y,:z], unzip(tips)))...,\n\t\tNamedTuple(zip([:u,:v,:w], unzip(lengths)))...,\n\t\ttype=\"cone\", anchor=\"tail\")\n\ndata = vcat(helix_trace, arrows, caps)\n\nPlot(data)\n```\n\n::: {.cell-output .cell-output-display execution_count=26}\n```{=html}\n
\n
\n
\n\n\n```\n:::\n:::\n\n\nIf several arrows are to be drawn, it might be more efficient to pass multiple values in for the `x`, `y`, ... values. They expect a vector. In the above, we create $1$-element vectors.\n\n\n## Contour plots\n\n\nA contour plot is created by the \"contour\" trace type. The data is prepared as a vector of vectors, not a matrix. The following has the interior vector corresponding to slices ranging over $x$ for a fixed $y$. With this, the construction is straightforward using a comprehension:\n\n::: {.cell hold='true' execution_count=27}\n``` {.julia .cell-code}\nf(x,y) = x^2 - 2y^2\n\nxs = range(0,2,length=25)\nys = range(0,2, length=50)\nzs = [[f(x,y) for x in xs] for y in ys]\n\ndata = Config(\n\tx=xs, y=ys, z=zs,\n\ttype=\"contour\"\n)\n\nPlot(data)\n```\n\n::: {.cell-output .cell-output-display execution_count=27}\n```{=html}\n
\n
\n
\n\n\n```\n:::\n:::\n\n\nThe same `zs` data can be achieved by broadcasting and then collecting as follows:\n\n::: {.cell hold='true' execution_count=28}\n``` {.julia .cell-code}\nf(x,y) = x^2 - 2y^2\n\nxs = range(0,2,length=25)\nys = range(0,2, length=50)\nzs = collect(eachrow(f.(xs', ys)))\n\ndata = Config(\n\tx=xs, y=ys, z=zs,\n\ttype=\"contour\"\n)\n\nPlot(data)\n```\n\n::: {.cell-output .cell-output-display execution_count=28}\n```{=html}\n
\n
\n
\n\n\n```\n:::\n:::\n\n\nThe use of just `f.(xs', ys)` or `f.(xs, ys')`, as with other plotting packages, is not effective, as `JSON3` writes matrices as vectors (with linear indexing).\n\n\n## Surface plots\n\n\nThe chart type \"surface\" allows surfaces in $3$ dimensions to be plotted.\n\n\n### Surfaces defined by $z = f(x,y)$\n\n\nSurfaces defined through a scalar-valued function are drawn quite naturally, save for needing to express the height data ($z$ axis) using a vector of vectors, and not a matrix.\n\n::: {.cell hold='true' execution_count=29}\n``` {.julia .cell-code}\npeaks(x,y) = 3 * (1-x)^2 * exp(-(x^2) - (y+1)^2) -\n 10*(x/5 - x^3 - y^5) * exp(-x^2-y^2) - 1/3 * exp(-(x+1)^2 - y^2)\n\nxs = range(-3,3, length=50)\nys = range(-3,3, length=50)\nzs = [[peaks(x,y) for x in xs] for y in ys]\n\ndata = Config(x=xs, y=ys, z=zs,\n type=\"surface\")\n\nPlot(data)\n```\n\n::: {.cell-output .cell-output-display execution_count=29}\n```{=html}\n
\n
\n
\n\n\n```\n:::\n:::\n\n\n### Parametrically defined surfaces\n\n\nFor parametrically defined surfaces, the $x$ and $y$ values also correspond to matrices. Her we see a pattern to plot a torus. The [`aspectmode`](https://plotly.com/javascript/reference/layout/scene/#layout-scene-aspectmode) instructs the scene's axes to be drawn in proportion with the axes' ranges.\n\n::: {.cell hold='true' execution_count=30}\n``` {.julia .cell-code}\nr, R = 1, 5\nX(theta,phi) = [(r*cos(theta)+R)*cos(phi), (r*cos(theta)+R)*sin(phi), r*sin(theta)]\n\nus = range(0, 2pi, length=25)\nvs = range(0, pi, length=25)\n\nxs = [[X(u,v)[1] for u in us] for v in vs]\nys = [[X(u,v)[2] for u in us] for v in vs]\nzs = [[X(u,v)[3] for u in us] for v in vs]\n\ndata = Config(\n\tx = xs, y = ys, z = zs,\n\ttype=\"surface\",\n\tmode=\"scatter3d\"\n)\n\nlyt = Config(scene=Config(aspectmode=\"data\"))\n\nPlot(data, lyt)\n```\n\n::: {.cell-output .cell-output-display execution_count=30}\n```{=html}\n
\n
\n
\n\n\n```\n:::\n:::\n\n\n", "supporting": [ "plotly_plotting_files" ], "filters": [], "includes": { "include-in-header": [ "\n\n\n" ] } } }