% lua-tikz3dtools-doc.tex % arara: lualatex: { options: ["-shell-escape"] } % arara: lualatex: { options: ["-shell-escape"] } \PassOptionsToPackage{english}{babel} \PassOptionsToPackage{colorlinks = true}{hyperref} \PassOptionsToPackage{margin = 1in}{geometry} \PassOptionsToPackage{depth = 4}{bookmark} \documentclass[12pt]{report} \usepackage{ lua-tikz3dtools ,microtype ,babel ,fontspec ,unicode-math, ,caption ,subcaption ,xcolor ,geometry ,minted ,bookmark ,hyperref } \setmathfont{IBMPlexMath-Regular} \counterwithin{figure}{section} \counterwithin{table}{section} \DeclareCaptionFormat{lua-tikz3dtools}{#1#2\par\noindent#3} \captionsetup{ justification = raggedright ,singlelinecheck = false ,format = lua-tikz3dtools } \captionsetup[subfigure]{format=hang} \setcounter{tocdepth}{3} \setmainfont{Carlito} \title{ The lua-tikz3dtools Package,\\ Version 1.0.0\\ {\small https://github.com/Pseudonym321/TikZ-Animations/tree/master1/TikZ/lua-tikz3dtools} } \author{Jasper} \date{\today} \begin{document} \maketitle \tableofcontents \chapter{Introduction} lua-tikz3dtools is a Lua-based 3D graphics package for Ti\textit{k}Z. \section{What are the current capabilities of lua-tikz3dtools?} lua-tikz3dtools can render multiple non-intersecting triangulated mesh surfaces at once. It is also possible to draw a plane which has been clipped by a rectangular prism. There are commands for drawing and labelling geometric vectors too. Curves and surfaces can be defined using matrix transformations. \section{ How does lua-tikz3dtools improve on existing packages? } There are two significant packages for 3D artwork in Ti\textit{k}Z: pgfplots and tikz-3dplot. pgfplots can z-sort the faces of a single parametric surface, but not multiple surfaces at once. Both pgfplots and tikz-3dplot are capable of changing the camera view, but the result is necessarily orthographic, and the rotations are restricted to azimuth and elevation; that is, the \(z\)-axis is always vertical. tikz-3dplot offers useful pgfmath capabilities for doing vector operations. Neither pgfplots nor tikz-3dplot enable matrix transformations. lua-tikz3dtools is built to render multiple parametric objects at once, enable arbitrary object orientations in non-orthographic projections, and enable matrix transformations. \section{Where is the package at, and where it is heading?} The vision of the package lua-tikz3dtools is to one day be able to render arbitrary numbers of intersecting polygons, by clipping them with each other; this would enable arbitrary surface intersections, without visual artefacts. A more near term goal of lua-tikz3dtools is to also implement an ODE solving tool which approximates trajectories in vector fields. I also want to add capabilities for doing programming in Lua, instead of in pgf, essentially replacing the pgfmath capabilities of tikz-3dplot with faster Lua-based capabilities. Soon there will also be proper camera culling for projective scenes; that is, the action of not drawing segments which are behind the viewing plane.\par There iss no way to store macros in Lua from the \TeX\ end yet, so sometimes there is a problem with repetition. While the geometric vectors are nice, they are not yet rendered in 3D like the parametric objects---I want to fix this as well. I will also implement a way for the user to create their own reusable Lua functions. \chapter{Geometric Vectors} \section{How are geometric vectors defined in lua-tikz3dtools?} Geometric vectors are defined by two things: a start-coordinate, and a relative displacement. There are two classifications of geometric vectors; there are zero vectors, and non-zero vectors. A zero vector is represented by a small circle. A non-zero vector is represented by a line segment with an arrow. The endpoints of a non-zerovector may or may not have a circle centered on the endpoint. If there is no circle drawn, the arrow stops directly at the endpoint. If a circle is drawn, then the arrow tip is moved back by the circle's radius. Observe the tips of the vectors in Figure \ref{fig:2-1-1}. Both tips lie on the same point, despite one arrow tip being offset by the circle's radius. \begin{figure} \centering \begin{tikzpicture} \drawvector[points = both,green]{0,0}{2,0} \drawvector[blue]{4,0}{-2,0} \end{tikzpicture} \caption{The exact location of a geometric vector's tip.} \label{fig:2-1-1} \end{figure} The syntax for drawing Figure \ref{fig:2-1-1} is shown in the following code. \begin{minted}{tex} \begin{tikzpicture} \drawvector[points = both,yellow]{0,0}{2,0} \drawvector[blue]{4,0}{-2,0} \end{tikzpicture} \end{minted} There are four values for the key \mintinline{latex}{points}: \mintinline{latex}{neither}, \mintinline{latex}{behind}, \mintinline{latex}{front} and \mintinline{latex}{both}. These tell the command \mintinline{latex}{\drawvector} whether and where to put circles over the endpoints. See Figure \ref{fig:2-1-2}, which uses the following code. \begin{minted}{tex} \begin{tikzpicture} \drawvector[points = neither]{0,0}{0,1} % a) \drawvector[points = behind]{1,0}{0,1} % b) \drawvector[points = front]{2,0}{0,1} % c) \drawvector[points = both]{3,0}{0,1} % d) \end{tikzpicture} \end{minted} \begin{figure} \centering \begin{subfigure}{0.3\textwidth} \centering \begin{tikzpicture} \drawvector[points = neither]{0,0}{0,1} \end{tikzpicture} \caption{\mintinline{latex}{points = neither}} \end{subfigure} \begin{subfigure}{0.3\textwidth} \centering \begin{tikzpicture} \drawvector[points = behind]{1,0}{0,1} \end{tikzpicture} \caption{\mintinline{latex}{points = behind}} \end{subfigure}\par \begin{subfigure}{0.3\textwidth} \centering \begin{tikzpicture} \drawvector[points = front]{2,0}{0,1} \end{tikzpicture} \caption{\mintinline{latex}{points = front}} \end{subfigure} \begin{subfigure}{0.3\textwidth} \centering \begin{tikzpicture} \drawvector[points = both]{3,0}{0,1} \end{tikzpicture} \caption{\mintinline{latex}{points = both}} \end{subfigure} \caption{The values for the key \mintinline{latex}{points}.} \label{fig:2-1-2} \end{figure} The \mintinline{latex}{\drawvector} syntax is mostly meant for diagrams which don't use \(z\)-sorting. Still, they can be used in some 3D still-diagrams; for instance, these vectors connect nicely. They will also eventually be able to be drawn flat on planes. There is also a zero-vector command available via \mintinline{latex}{\drawpoint}. See Figure \ref{fig:2-1-3}, with the following code. \begin{minted}{tex} \begin{tikzpicture} \drawpoint[red]{0,0} \end{tikzpicture} \end{minted} \begin{figure} \centering \begin{tikzpicture} \drawpoint[red]{0,0} \end{tikzpicture} \caption{Zero-vector} \label{fig:2-1-3} \end{figure} \chapter{The Math Module} When the user appends a parametric object to the segments list, they must specify several attributes for the object. In particular, when they specify the parametric equations for the object, this is done using Lua syntax. Specifically, the user writes Lua code into a tikz key, and that Lua code is later \mintinline{lua}{load()}ed by Lua. A useful feature of the lua function \mintinline{lua}{load()} is that it allows the user to input functions without their usual table prefix; this means that the user can write just \mintinline{lua}{cos()}, instead of \mintinline{lua}{math.cos()}. lua-tikz3dtools defines a growing handful of math operations, from elementary linear matrix transformations to stereographic projections. This list could always be expanded though as needs arise, so to make it robust, I will make a way for the user to make their own. See Chapter 1.\par \textbf{lua-tikz3dtools uses row-vector convention!} This means that you put the transformation on the right of the matrix multiplication unlike with column vectors. Additionally, \textbf{lua-tikz3dtools uses matrix-vector convention!} This means that vectors are formatted like matrices---meaning that the vector table is nested within a matrix table. This makes it easier for the tools to communicate, and expains why we need to index our functions at depth 2 instead of depth 1. \section{The math functions.} In addition to those provided by the math module, these ones are also added. \begin{itemize} \item \mintinline{lua}{matrix_multiply(A, B)} \item \mintinline{lua}{matrix_scale(factor, A)} \item \mintinline{lua}{reciprocate_by_homogenous(vector)} \item \mintinline{lua}{matrix_add(A, B)} \item \mintinline{lua}{matrix_subtract(A, B)} \item \mintinline{lua}{transpose(A)} \item \mintinline{lua}{inverse(matrix)} \item \mintinline{lua}{det(matrix)} \item \mintinline{lua}{yrotation(angle)} \item \mintinline{lua}{translate(x, y, z)} \item \mintinline{lua}{xscale(scale)} \item \mintinline{lua}{yscale(scale)} \item \mintinline{lua}{zscale(scale)} \item \mintinline{lua}{scale(scale)} \item \mintinline{lua}{xrotation(angle)} \item \mintinline{lua}{zrotation(angle)} \item \mintinline{lua}{euler(alpha, beta, gamma)} \item \mintinline{lua}{sphere(longitude, latitude)} \item \mintinline{lua}{dot_product(u, v)} \item \mintinline{lua}{cross_product(u, v)} \item \mintinline{lua}{norm(u)} \item \mintinline{lua}{normalize(u)} \item \mintinline{lua}{identity_matrix()} \item \mintinline{lua}{stereographic_projection(point)} \end{itemize} \chapter{Parametric Objects} \section{How is a parametric object defined?} Parametric objects are defined using a handful of different pieces of information. In particular, they use parametric functions, domain parameters and a samples parameter for each dimension in the domain. Of course, there are also parameters for color and such as well. Figure \ref{fig:3-1-1} is defined by the following code: \begin{minted}{tex} \begin{tikzpicture} \appendcurve[ u min = 0 ,u max = {tau} ,u samples = 200 ,transformation = {euler(pi/2,pi/3,pi/2)} ,x = {2*cos(u)} ,y = {2*sin(u)} ,z = {u} ,draw options = { draw =purple ,ultra thick ,line cap = round } ] \appendsurface[ u min = 0 ,u max = {tau} ,u samples = 36 ,v min = 0 ,v max = {tau} ,v samples = 36*2/3 ,transformation = {euler(pi/2,pi/3,pi/2)} ,x = {2*cos(u)+sphere(u,v)[1][1]} ,y = {2*sin(u)+sphere(u,v)[1][2]} ,z = {u+sphere(u,v)[1][3]} ,draw options = { draw = blue ,ultra thin ,line join = round ,line cap = round } ,fill options = { fill = green ,fill opacity = 0.7 } ] \foreach \u in {0,...,35} { \foreach \v in {0,...,35} { \pgfmathsetmacro{\uscale}{2*pi/(36-1)} \pgfmathsetmacro{\vscale}{2*pi/(36*2/3-1)} \pgfmathsetmacro{\u}{\u*\uscale} \pgfmathsetmacro{\v}{\v*\vscale} \appendcurve[ u min = 0 ,u max = 0.3 ,u samples = 2 ,transformation = {euler(pi/2,pi/3,pi/2)} ,x = { 2 * cos(token.get_macro("u")) + (u + 1) * sphere( token.get_macro("u") ,token.get_macro("v") )[1][1] } ,y = { 2 * sin(token.get_macro("u")) + (u + 1) * sphere( token.get_macro("u") ,token.get_macro("v") )[1][2] } ,z = { token.get_macro("u") + (u + 1) * sphere( token.get_macro("u") ,token.get_macro("v") )[1][3] } ,draw options = { -{Stealth[round]} ,line cap = round ,draw = black } ] } } \rendersegments \end{tikzpicture} \end{minted} \begin{figure} \centering \begin{tikzpicture} \appendcurve[ u min = 0 ,u max = {tau} ,u samples = 200 ,transformation = {euler(pi/2,pi/3,pi/2)} ,x = {2*cos(u)} ,y = {2*sin(u)} ,z = {u} ,draw options = { draw =purple ,ultra thick ,line cap = round } ] \appendsurface[ u min = 0 ,u max = {tau} ,u samples = 36 ,v min = 0 ,v max = {tau} ,v samples = 36*2/3 ,transformation = {euler(pi/2,pi/3,pi/2)} ,x = {2*cos(u)+sphere(u,v)[1][1]} ,y = {2*sin(u)+sphere(u,v)[1][2]} ,z = {u+sphere(u,v)[1][3]} ,draw options = { draw = blue ,ultra thin ,line join = round ,line cap = round } ,fill options = { fill = green ,fill opacity = 0.7 } ] \foreach \u in {0,...,35} { \foreach \v in {0,...,35} { \pgfmathsetmacro{\uscale}{2*pi/(36-1)} \pgfmathsetmacro{\vscale}{2*pi/(36*2/3-1)} \pgfmathsetmacro{\u}{\u*\uscale} \pgfmathsetmacro{\v}{\v*\vscale} \appendcurve[ u min = 0 ,u max = 0.3 ,u samples = 2 ,transformation = {euler(pi/2,pi/3,pi/2)} ,x = { 2 * cos(token.get_macro("u")) + (u + 1) * sphere( token.get_macro("u") ,token.get_macro("v") )[1][1] } ,y = { 2 * sin(token.get_macro("u")) + (u + 1) * sphere( token.get_macro("u") ,token.get_macro("v") )[1][2] } ,z = { token.get_macro("u") + (u + 1) * sphere( token.get_macro("u") ,token.get_macro("v") )[1][3] } ,draw options = { -{Stealth[round]} ,line cap = round ,draw = black } ] } } \rendersegments \end{tikzpicture} \caption{A triangulated mesh surface and a curve.} \label{fig:3-1-1} \end{figure} lua-tikz3dtools is also capable of handling division by zero without erroring. This is because the math is handled in Lua, where division by zero doesn't error. We clip our results to be well within the scope of the Ti\textit{z}Z canvas. The code for Figure \ref{fig:3-1-2} is as follows. \begin{minted}{tex} \begin{tikzpicture} \clip (-\textwidth/2,-5) rectangle (\textwidth/2,5) ; \pgfmathsetmacro{\angle}{0} \let\angle\angle \foreach \longitude in {1,...,36}{ \let\longitude\longitude \appendcurve[ u min = 0 ,u max = {pi} ,u samples = 4*45 ,transformation = {euler(pi/2,pi/3,pi/6)} ,draw options = { line cap = round ,draw = black } ,x = { stereographic_projection( matrix_multiply( sphere( tau * token.get_macro("longitude") / 36 ,u ) ,euler( 0 ,pi/2 ,(2*pi/24) * token.get_macro("angle") / 36 ) ) )[1][1] } ,y = { stereographic_projection( matrix_multiply( sphere( tau * token.get_macro("longitude") / 36 ,u ) ,euler( 0 ,pi/2 ,(2*pi/24) * token.get_macro("angle") / 36 ) ) )[1][2] } ,z = 0*u ] \appendcurve[ u min = 0 ,u max = {pi} ,u samples = 23 ,transformation = { matrix_multiply( euler( 0 ,pi/2 ,(2*pi/24) * token.get_macro("angle") / 36 ) ,euler(pi/2,pi/3,pi/6) ) } ,draw options = { line cap = round ,draw = black } ,x = { sphere( tau * token.get_macro("longitude") / 36 ,u )[1][1] } ,y = { sphere( tau * token.get_macro("longitude") / 36 ,u )[1][2] } ,z = { sphere( tau * token.get_macro("longitude") / 36 ,u )[1][3] } ] } \foreach \latitude in {1,...,18}{ \let\latitude\latitude \appendcurve[ u min = 0 ,u max = {2*pi} ,u samples = 4*45 ,transformation = {euler(pi/2,pi/3,pi/6)} ,draw options = { line cap = round ,draw = black } ,x = { stereographic_projection( matrix_multiply( sphere( u ,tau * token.get_macro("latitude") / 36 ) ,euler( 0 ,pi/2 ,(tau/24) * token.get_macro("angle") / 36 ) ) )[1][1] } ,y = { stereographic_projection( matrix_multiply( sphere( u ,tau * token.get_macro("latitude") / 36 ) ,euler( 0 ,pi/2 ,(tau/24) * token.get_macro("angle") / 36 ) ) )[1][2] } ,z = 0*u ] \appendcurve[ u min = 0 ,u max = {2*pi} ,u samples = 45 ,transformation = { matrix_multiply( euler( 0 ,pi/2 ,(2*pi/24) * token.get_macro("angle") / 36 ) ,euler(pi/2,pi/3,pi/6) ) } ,draw options = { line cap = round ,draw = black } ,x = { sphere( u ,tau*token.get_macro("latitude")/36 )[1][1] } ,y = { sphere( u ,tau*token.get_macro("latitude")/36 )[1][2] } ,z = { sphere( u ,tau*token.get_macro("latitude")/36 )[1][3] } ] } \rendersegments \end{tikzpicture} \end{minted} \begin{figure} \centering \begin{tikzpicture} \clip (-\textwidth/2,-5) rectangle (\textwidth/2,5) ; \pgfmathsetmacro{\angle}{0} \let\angle\angle \foreach \longitude in {1,...,36}{ \let\longitude\longitude \appendcurve[ u min = 0 ,u max = {pi} ,u samples = 4*45 ,transformation = {euler(pi/2,pi/3,pi/6)} ,draw options = { line cap = round ,draw = black } ,x = { stereographic_projection( matrix_multiply( sphere( tau * token.get_macro("longitude") / 36 ,u ) ,euler( 0 ,pi/2 ,(2*pi/24) * token.get_macro("angle") / 36 ) ) )[1][1] } ,y = { stereographic_projection( matrix_multiply( sphere( tau * token.get_macro("longitude") / 36 ,u ) ,euler( 0 ,pi/2 ,(2*pi/24) * token.get_macro("angle") / 36 ) ) )[1][2] } ,z = 0*u ] \appendcurve[ u min = 0 ,u max = {pi} ,u samples = 23 ,transformation = { matrix_multiply( euler( 0 ,pi/2 ,(2*pi/24) * token.get_macro("angle") / 36 ) ,euler(pi/2,pi/3,pi/6) ) } ,draw options = { line cap = round ,draw = black } ,x = { sphere( tau * token.get_macro("longitude") / 36 ,u )[1][1] } ,y = { sphere( tau * token.get_macro("longitude") / 36 ,u )[1][2] } ,z = { sphere( tau * token.get_macro("longitude") / 36 ,u )[1][3] } ] } \foreach \latitude in {1,...,18}{ \let\latitude\latitude \appendcurve[ u min = 0 ,u max = {2*pi} ,u samples = 4*45 ,transformation = {euler(pi/2,pi/3,pi/6)} ,draw options = { line cap = round ,draw = black } ,x = { stereographic_projection( matrix_multiply( sphere( u ,tau * token.get_macro("latitude") / 36 ) ,euler( 0 ,pi/2 ,(tau/24) * token.get_macro("angle") / 36 ) ) )[1][1] } ,y = { stereographic_projection( matrix_multiply( sphere( u ,tau * token.get_macro("latitude") / 36 ) ,euler( 0 ,pi/2 ,(tau/24) * token.get_macro("angle") / 36 ) ) )[1][2] } ,z = 0*u ] \appendcurve[ u min = 0 ,u max = {2*pi} ,u samples = 45 ,transformation = { matrix_multiply( euler( 0 ,pi/2 ,(2*pi/24) * token.get_macro("angle") / 36 ) ,euler(pi/2,pi/3,pi/6) ) } ,draw options = { line cap = round ,draw = black } ,x = { sphere( u ,tau*token.get_macro("latitude")/36 )[1][1] } ,y = { sphere( u ,tau*token.get_macro("latitude")/36 )[1][2] } ,z = { sphere( u ,tau*token.get_macro("latitude")/36 )[1][3] } ] } \rendersegments \end{tikzpicture} \caption{Automatic division by zero handling.} \label{fig:3-1-2} \end{figure} lua-tikz3dtools is meant to be compatible with Ti\textit{k}Z, and they are meant to be used together---at least for now. Maybe one day lua-tikz3dtools will have enough capabilities to do things like for loops and conditionals---\textit{at home}. The code for Figure \ref{fig:3-1-3}, which illustrates the use of a conditional, is given. \begin{minted}{tex} \begin{tikzpicture} \clip (-\textwidth/2,-5) rectangle (\textwidth/2,5) ; \foreach[count = \c from 1] \angle in {20,40,...,80} { \pgfmathsetmacro{\angle}{\angle*pi/180} \let\angle\angle \appendsurface[ u min = pi/6 ,u max = 0.75*tau ,u samples = 36 ,v min = 0 ,v max = tau ,v samples = 36 ,transformation = {euler(pi/2,pi/3,pi/6+pi)} ,draw options = { draw = black ,line cap = round ,line join = round } ,fill options = { \ifnum\c=1 fill = red \fi \ifnum\c=2 fill = yellow \fi \ifnum\c=3 fill = green \fi \ifnum\c=4 fill = blue \fi ,fill opacity = 0.6 } ,x = { (1 / cos(token.get_macro("angle"))) * cos(u) + sqrt( ( 1 / cos(token.get_macro("angle")) )^2 - cos(token.get_macro("angle")) ) * sphere(u,v)[1][1] } ,y = { (1 / cos(token.get_macro("angle"))) * sin(u) + sqrt( ( 1 / cos(token.get_macro("angle")) )^2 - cos(token.get_macro("angle")) ) * sphere(u,v)[1][2] } ,z = { sqrt( ( 1 / cos(token.get_macro("angle")) )^2 - cos(token.get_macro("angle")) ) * sphere(u,v)[1][3] } ] } \rendersegments \end{tikzpicture} \end{minted} \begin{figure} \centering \begin{tikzpicture} \clip (-\textwidth/2,-5) rectangle (\textwidth/2,5) ; \foreach[count = \c from 1] \angle in {20,40,...,80} { \pgfmathsetmacro{\angle}{\angle*pi/180} \let\angle\angle \appendsurface[ u min = pi/6 ,u max = 0.75*tau ,u samples = 36 ,v min = 0 ,v max = tau ,v samples = 36 ,transformation = {euler(pi/2,pi/3,pi/6+pi)} ,draw options = { draw = black ,line cap = round ,line join = round } ,fill options = { \ifnum\c=1 fill = red \fi \ifnum\c=2 fill = yellow \fi \ifnum\c=3 fill = green \fi \ifnum\c=4 fill = blue \fi ,fill opacity = 0.6 } ,x = { (1 / cos(token.get_macro("angle"))) * cos(u) + sqrt( ( 1 / cos(token.get_macro("angle")) )^2 - cos(token.get_macro("angle")) ) * sphere(u,v)[1][1] } ,y = { (1 / cos(token.get_macro("angle"))) * sin(u) + sqrt( ( 1 / cos(token.get_macro("angle")) )^2 - cos(token.get_macro("angle")) ) * sphere(u,v)[1][2] } ,z = { sqrt( ( 1 / cos(token.get_macro("angle")) )^2 - cos(token.get_macro("angle")) ) * sphere(u,v)[1][3] } ] } \rendersegments \end{tikzpicture} \caption{Stereographically nested tori.} \label{fig:3-1-3} \end{figure} \chapter{Clipped Subspaces} A big focus for the future of this package is triangle-triangle clipping, which generalizes to polygon clipping. This dream is twofold; firstly it will enable parametric surfaces to intersect, and secondly it will enable intersecting plane diagrams which haven't been achieved in full generality in tikz yet. \section{Clipping individual planes.} This is where the technology is at currently. We can clip a single plane by a rectangular prism, and draw them. Eventually, when the polygon clipping gets going, this will evolve into a tool for graphing intersecting planes without using approximations. The code for Figure \ref{fig:4-1-1} is given. \begin{minted}{tex} \begin{tikzpicture} \appendprism[% a = 1 ,b = 2 ,c = 3 ,d = 4 ,x min = -1 ,x max = 1 ,y min = -1 ,y max = 1 ,z min = -1 ,z max = 1 ,transformation = {euler(pi/2,pi/3,pi/3)} ] \appendplane[% a = 1 ,b = 2 ,c = 3 ,d = 1 ,x min = -1 ,x max = 1 ,y min = -1 ,y max = 1 ,z min = -1 ,z max = 1 ,transformation = {euler(pi/2,pi/3,pi/3)} ] \rendersegments \end{tikzpicture} \end{minted} \begin{figure} \centering \begin{tikzpicture} \appendprism[% a = 1 ,b = 2 ,c = 3 ,d = 4 ,x min = -1 ,x max = 1 ,y min = -1 ,y max = 1 ,z min = -1 ,z max = 1 ,transformation = {euler(pi/2,pi/3,pi/3)} ] \appendplane[% a = 1 ,b = 2 ,c = 3 ,d = 1 ,x min = -1 ,x max = 1 ,y min = -1 ,y max = 1 ,z min = -1 ,z max = 1 ,transformation = {euler(pi/2,pi/3,pi/3)} ] \rendersegments \end{tikzpicture} \caption{% A plane which has been clipped by a rectangular prism. } \label{fig:4-1-1} \end{figure} \end{document}