\directlua{t0 = os.clock()} \documentclass{article} %\usepackage{fontspec} %\setmainfont{Libertinus Serif} %\usepackage{mathtools,unicode-math} %\setmainfont{Libertinus Math} \usepackage[a4paper,margin=2cm]{geometry} \usepackage{tikz} %\tikzset{every picture/.style=thick} \tikzset{ thin/.style={line width=0.8pt}, thick/.style={line width=1pt}, } \usepackage[dvipsnames]{xcolor} \usepackage{multicol} \usepackage[normalem]{ulem} \usepackage{tcolorbox} \tcbuselibrary{listings} \tcbuselibrary{skins,breakable} \usepackage{fancyvrb} \hbadness=10000 \hfuzz=3pt \usepackage{luahyperbolic} \usepackage{imakeidx} \makeindex[title=Index of functions used in this documentation] \makeindex[name=fields, title=Index of constants and parameters] \usepackage[colorlinks]{hyperref} \lstdefinelanguage{Lua}{ morekeywords={and,break,do,else,elseif,end,false,for,function,if,in, local,nil,not,or,repeat,return,then,true,until,while}, sensitive=true, morecomment=[l]{--}, morestring=[b]", morestring=[b]' } \tcbset{ myluacolors/.style={ listing options={ language=Lua, tabsize=2, basicstyle=\ttfamily\small, keywordstyle=\color{blue!60!black}\bfseries, commentstyle=\color{gray}\itshape, stringstyle=\color{red!50!black}, emph={hyper}, emphstyle={\color{green!40!black}\bfseries}, showstringspaces=false, breaklines=true, postbreak=\mbox{\textcolor{red}{$\hookrightarrow$}\space} } } } \newtcblisting{codeandresult}{% myluacolors, colback=white, colframe=gray!20, fonttitle=\bfseries, listing side text, lefthand width=0.64\linewidth, righthand width=0.36\linewidth } \newtcbinputlisting{codefromfile}[2][]{% myluacolors, % shared coloring options colback=white, colframe=black, fonttitle=\bfseries, listing file=#2, % path to external file title=#1, breakable } \title{The \textbf{\textsf{luahyperbolic}} latex package} \author{Damien Mégy} \date{Version : \the\year/\the\month/\the\day} \begin{document} \maketitle % - - - - - TITLE PICTURE \begin{luacode*} t1 = os.clock() texio.write_nl(string.format("[TIMING] %.2f s: beginning title picture", t1)) \end{luacode*} \begin{center} \begin{luacode*} hyper.tikzBegin("scale=5") hyper.fillTilingFromAngles(math.pi/2, math.pi/4, math.pi/5, 24,"teal") hyper.tikzEnd() \end{luacode*} \par LaTeX code for above tiling : \verb+\hyperbolicTiling{2}{4}{5}[scale=5,color=teal,depth=24]+ \\ The latest version of this documentation is available at: \href{https://github.com/dmegy/luahyperbolic}{github.com/dmegy/luahyperbolic}. \end{center} \begin{luacode*} t2 = os.clock() texio.write_nl(string.format("[TIMING] %.2f s: finished title picture in %.2f seconds", os.clock() - t0, t2-t1)) \end{luacode*} \vspace{1cm} \begin{multicols}{2} \setcounter{tocdepth}{2} \tableofcontents \end{multicols} \newpage \section{Introduction} \subsection{What you can do with this package} \begin{enumerate} \item Draw hyperbolic geometry pictures, high precision (pdf output). \item Use an easy syntax. At least, easier than with \LaTeX macros and bloody pgf. \item Export generated pictures to external tikz files if you want. \item Read the source code and learn hyperbolic geometry. \item Compile **without** installing python packages or lua packages etc. \item Compile **without** "shell-escape" mode. \item Compile everything in a single pass. No (Leeloo Dallas) multipass. Want to illustrate Euclid's fifth postulate ? Do it right there in your TeX file without having to include external files : \begin{codeandresult} \begin{luacode*} hyper.tikzBegin("scale=2") local P = complex(0.5,-0.2) local A = complex.exp_i(math.pi/20) for k=1,10 do hyper.drawLine(P, A^k, "teal") end hyper.labelPoint(P, "$P$", "left=.2cm") local style = "very thick, dashed, red" hyper.drawLine(complex.J,-complex.I,style) hyper.tikzEnd() \end{luacode*} \end{codeandresult} \index[fields]{complex!I}\index[fields]{complex!J} \index{complex!exp\_i}\index{tikzBegin}\index{tikzEnd} \item Draw Tilings in one line, without any Lua code : \begin{codeandresult} \hyperbolicTiling{3}{4}{7}[scale=1.4] \hyperbolicTiling{4}{4}{inf}[scale=1.4, color=violet, backgroundcolor=pink!20, depth=6] \end{codeandresult} \end{enumerate} \subsection{What this package cannot do for you} \begin{enumerate} \item Really intensive computations, millions of geodesics etc. Source code is not optimized to keep maximum readability. All computations involve automorphisms. See other remarks below. \item Coffee \end{enumerate} \subsection{Efficiency} For heavy computations, please use a \sout{Python} C library. Nevertheless, this library is still reasonably efficient and usable: \begin{itemize} \item The first tiling illustration of this documentation (hyperbolic 2-4-5 tiling with 2361 geodesics) compiled in \directlua{tex.print(math.round(100*(t2-t1))/100)} seconds on a 2020 intel macbook air laptop. \item This entire documentation including all pictures compiles in approx. 20 seconds on the same machine, the majority of the time spent on the tilings. The package is suitable for producing lecture notes in hyperbolic geometry, which was the reason for all of this. For an entire book with dozens of pictures, you may need to externalize some of the pictures. \item Tests show that the bottleneck is TikZ, not Lua. In a future version, some future maintainer may add other fontends to the library, including drawing directly to the pdf with pdf primitives wich are several orders of magnitude faster than tikz. \end{itemize} \subsection{How to load the package} \subsubsection*{Local install} Simply put the \verb+luahyperbolic.sty+ file in your working directory and add the following line to your tex preamble : \begin{verbatim} \usepackage{luahyperbolic} \end{verbatim} The single file \texttt{luahyperbolic.sty} contains everything needed, including the necessary library for complex numbers. It can be sent to colleagues by email, etc. It will declare two lua globals : \texttt{complex} and \texttt{hyper}, and define a \texttt{hyperbolicTiling} command for convenience. If the sty file is not in the same directory as your tex file, you can write \verb+\usepackage{../luahyperbolic}+. %\subsubsection*{CTAN} \subsection{Dependencies} The package luahyperbolic requires the following packages: \begin{itemize} \item tikz \item luacode \end{itemize} It loads them if they are not loaded already. The latex files must be compiled with the \textbf{lualatex} engine. \subsection{Exporting the pictures} \subsubsection{When finishing a picture} \index{tikzExport} Simply add a filename to the closing \texttt{hyper.tikzEnd()} instruction, as below: \begin{center} \texttt{hyper.tikzEnd("myfile.tikz")} \end{center} This will save the current picture (including \verb+\begin{tikzpicture}[options]+ and \verb+\end{tikzpicture}+). \subsubsection{In the middle of the TikZpicture} It is also possible to export the current picture at any point with \verb+hyper.tikzExport(optional-filename)+. If no filename is provided, the package will name it \verb+hyper_picture_N+ with \texttt{N} the picture counter. Luahyperbolic keeps count of how many pictures have been produced in the document. This allows exporting images in loops, or drawing several steps of the same picture. \subsection{(Advanced users) Build from source} The \texttt{luahyperbolic.sty} is pre-generated from a template sty file and four lua modules : \begin{enumerate} \item \texttt{complex.lua} \item \texttt{luahyperbolic-core.lua} \item \texttt{luahyperbolic-tikz.lua} \item \texttt{luahyperbolic-tilings.lua} \end{enumerate} These files are in the \texttt{lua/} subdirectory of the package repo. To build the \texttt{luahyperbolic.sty} from lua source files, execute \texttt{lua build.lua} in the root directory of the package. This will buid and replace the existing sty file. \section{Drawing and labeling elements} % ------------------ Figure 1 : Points ------------------ \subsection{Draw and label points} \subsubsection{Draw and label points} \index{drawPoint}\index{drawPoints}\index[fields]{DRAW\_POINT\_RADIUS} \index{labelPoint}\index{labelPoints} \begin{codeandresult} \begin{luacode*} local A = complex(0.3,0.2) local B = 2*A local I, J = complex.I, complex.J local C, D, E = I*A, I*B, J*B hyper.tikzBegin() hyper.drawPoint(0,"white, draw=red") hyper.drawPoints(A, B, C, "teal") hyper.drawPoints(D, E) hyper.labelPoint(A,"$A$", "below right, blue") hyper.labelPoints(B,"$B$", C, "$C$") hyper.labelPoint(D,"$D$","left=.4cm, red") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsection{Draw/label/mark segments/lines/rays} \subsubsection{Segments lines and rays (through two points)} \index{drawLine}\index{drawSegment}\index{drawRay} \begin{codeandresult} \begin{luacode*} local A = -complex.I/2 local B = (1+complex.I)/2 local C = complex(-0.7,0.4) local D = complex(-0.7,-0.3) hyper.tikzBegin() hyper.drawLine(A, B, "red") hyper.drawSegment(B,C,"dashed, ForestGreen") hyper.drawRay(C,D,"double,double distance=1pt") hyper.drawLine(D,-D, "green!50!black") hyper.drawLine(A, 1, "gray, line width=2pt") hyper.drawLine(1, complex.I, "teal") hyper.drawPoints(A, B, C, D) hyper.labelPoints(A, "$A$", B, "$B$", C, "$C$", D, "$D$") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Triangles and polygons} \index{drawTriangle}\index{drawPolygon}\index{drawPolygonFromTable} \begin{codeandresult} \begin{luacode*} local A = complex(.9,.2) local B = complex(.5,.7) local C = complex(0,.9) hyper.tikzBegin() hyper.drawTriangle(A, B, C) hyper.drawTriangle(-A, -B, -C, "red") hyper.drawPolygon(-.9, -.7, complex(-.7,.4), complex(-.9,.3), "blue") local points={} for k=1,5 do table.insert(points, complex.polar(.4,k*2*math.pi/5)) end hyper.drawPolygonFromTable(points,"teal") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Triangles with prescribed angles} \index{triangleWithAngles} \begin{codeandresult} \begin{luacode*} local function myTriangle(a, b, c, color) hyper.tikzBegin("scale=1.4") local A, B, C = hyper.triangleWithAngles(a, b, c) hyper.drawTriangle(A, B, C, color) hyper.tikzEnd() end local PI = math.pi myTriangle(PI/2, PI/6, PI/10, "red") myTriangle(2*PI/5, PI/4, PI/5, "red!50!blue") tex.print("\\par") myTriangle(2*PI/3, PI/8, PI/20, "blue") myTriangle(PI/3, PI/4, 0, "blue!50!green") tex.print("\\par") myTriangle(PI/3, 0, 0, "ForestGreen") myTriangle(0, 0, 0, "green!50!red") \end{luacode*} \end{codeandresult} \subsubsection{Label and mark segments} \index{markSegment}\index{labelSegment} \begin{codeandresult} \begin{luacode*} local A = -complex.I/2 local B = (1+complex.I)/2 local C = complex(-0.7,0.4) local D = complex(-0.7,-0.3) hyper.tikzBegin() hyper.drawPolygon(A, B, C, D, "teal") hyper.markSegment(A,B,"|") local marking = "\\textcolor{red}{||}" hyper.markSegment(B,C,marking) hyper.labelSegment(C,D,"$3$", "fill=white, draw=blue") hyper.labelSegment(D, A,"$\\alpha$", "below") hyper.labelPoint(A, "$A$", "below") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Regular polygons} \index{drawRegularPolygon} \begin{codeandresult} \begin{luacode*} local O1 = complex(.5,.2) local A1 = complex(.7,.3) local O2 = complex(-0.1,0) local A2 = complex(-.4,.3) local O3 = complex(0,.7) local A3 = complex(0,.9) hyper.tikzBegin() hyper.drawRegularPolygon(O1, A1, 3) hyper.drawRegularPolygon(O2, A2, 5, "blue") hyper.drawRegularPolygon(O3, A3, 8, "green!50!black") hyper.drawPoints(O1,O2,O3) hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Polygonal lines} \index{drawPolyline}\index{drawPolylineFromTable} \begin{codeandresult} \begin{luacode*} hyper.tikzBegin() -- drawPolyline : hyper.drawPolyline(0, 1, complex.I, -1, "red") -- drawPolylineFromTable : local points={} local N=30 for k=1, N do table.insert(points, complex.polar(k/N, k*math.pi/3)) end hyper.drawPolylineFromTable(points,"teal") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Lines and rays (point and tangent vector)} \index{drawLineFromVector}\index{drawRayFromVector} \index{drawVector} \begin{codeandresult} \begin{luacode*} local A = complex(-0.2,-0.5) local B = complex(-0.5,0.5) local C = complex(0,0.2) local v = complex(1,0) hyper.tikzBegin() myVec = ">=latex, red" myLine = "dashed, gray" hyper.drawLineFromVector(A, v, myLine) hyper.drawRayFromVector(B, v, myLine) hyper.drawRayFromVector(C, v, myLine) hyper.drawVector(A,v,myVec) hyper.drawVector(B,v,myVec) hyper.drawVector(C,v,myVec) hyper.drawPoints(A, B,C) hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Perpendicular bisector} \index{drawPerpendicularBisector} \begin{codeandresult} \begin{luacode*} local A = complex(-0.2,-0.5) local B = complex(-0.7,0.3) hyper.tikzBegin() hyper.drawPerpendicularBisector(A,B,"red") hyper.drawSegment(A, B, "teal") hyper.drawPoints(A,B) hyper.labelPoints(A,"$A$", B, "$B$", "above right") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Angle bisector} \index{drawAngleBisector} \begin{codeandresult} \begin{luacode*} local A = complex(-0.2,-0.6) local O = complex(0.3,0.3) local B = complex(-0.7,0.3) hyper.tikzBegin() hyper.drawAngleBisector(A,O,B,"red") hyper.drawPolyline(A,O,B, "teal") hyper.drawPoints(A,O,B) hyper.labelPoints(A, "$A$", O, "$O$", B, "$B$") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsection{Circles, semicircles, arcs} \subsubsection{Center and radius} \index{drawCircle} \begin{codeandresult} \begin{luacode*} local points = { complex(0,0), complex(-0.5,-0.4), complex(0.8,0.1), complex(0,0.9) } hyper.tikzBegin() for _,point in ipairs(points) do hyper.drawCircle(point,1,"teal") hyper.drawPoint(point) end -- NOTE : all circles have same radius=1 ! hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Circle through point} \index{drawCircleThrough} \begin{codeandresult} \begin{luacode*} local O = complex(0.3,0.2) local P = complex(0.7,-0.4) local Q = complex(-0.1,0.4) hyper.tikzBegin() hyper.drawCircleThrough(O, P, "dotted, red") -- or draw by hand : hyper.drawCircle(O, hyper.distance(O,Q), "blue") hyper.drawPoints(O, P, Q) hyper.labelPoints(O, "$O$", P, "$P$", Q, "$Q$") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Semicircles} \index{drawSemicircle} \begin{codeandresult} \begin{luacode*} local O = complex(0,0) local A = complex(-0.3,0) local P = complex(0.5,0.2) local B = complex(0.7,-0.4) hyper.tikzBegin() hyper.drawSemicircle(O, A, "blue") hyper.drawSemicircle(P, B, "red") hyper.labelPoints(O, "$O$", A, "$A$", P, "$P$", B, "$B$") hyper.drawLine(P,B, "gray, dashed") hyper.drawPoints(O, A, P, B) hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Arc (center and endpoints)} \index{drawArc} \begin{codeandresult} \begin{luacode*} local O = complex(0,0) local A = complex(0.5,0) local B = complex.polar(0.5,math.pi/3) hyper.tikzBegin() hyper.drawArc(O, A, B, "very thick, red") hyper.drawArc(O, B, A, "dashed") hyper.drawPoints(O,A,B) hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Angles} \index{drawAngle} \begin{codeandresult} \begin{luacode*} local A = complex(-.5,-.3) local B = complex(0.5,0) local C = complex(0.2,.7) hyper.tikzBegin() hyper.drawLines(A, B, B, C, C, A, "teal") hyper.drawAngle(B, A, C, "red, very thick") hyper.drawAngle(A, C, B, "thick, double, double distance=2pt, blue") hyper.drawAngle(A, B, C, "very thick, ->, green!50!black") hyper.drawPoints(A, B, C) hyper.labelPoints(A, "$A$", C, "$C$") hyper.labelPoint(N, "$B$", "above right") -- poor man's labelAngle: hyper.labelPoint(A,"$\\alpha$", "above=.6cm, right=.7cm") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Tangent (line) at point} \index{drawTangentAt} \begin{codeandresult} \begin{luacode*} local O = complex(0.3,0.2) local P = complex(-0.1,0) local phi = hyper.rotation(O,math.pi/4) local Q =phi(P) local R = phi(Q) hyper.tikzBegin() hyper.drawCircleThrough(O, P, "teal") hyper.drawTangentAt(O,P,"red") hyper.drawTangentAt(O,Q,"red") hyper.drawTangentAt(O,R,"red") hyper.drawPoints(O, P, Q, R) hyper.tikzEnd() \end{luacode*} \end{codeandresult} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{Horocycles and hypercycles} \subsubsection{Horocycles} \index{drawHorocycle} \begin{codeandresult} \begin{luacode*} local theta=math.pi/3 local idealPoint = complex.exp_i(theta) local N = 10 hyper.tikzBegin() for k=1,N-1 do local point = idealPoint * (2*k/N-1) hyper.drawHorocycle(idealPoint,point,"red") -- and an orthogonal geodesic : local endPoint = complex.exp_i(theta+2*k*math.pi/N) hyper.drawLine(idealPoint,endPoint,"gray") end hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Hypercycles} \index{drawHypercycleThrough} \begin{codeandresult} \begin{luacode*} hyper.tikzBegin() local A = 1 local B = complex.J hyper.drawLine(A,B,"very thick, teal") local N = 10 for k=1,N-1 do local P = (2*k/N-1)*(-B^2) hyper.drawHypercycleThrough(P, A, B, "red") end hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsection{Printing custom strings to TikZ} In case you want to insert custom TikZ instructions into the TikZ picture, the library provides a \texttt{tikzPintf()} function, which is essentially \texttt{tex.print} composed with \texttt{string.format()}, but also saves the printed string to the tikz buffer. \index{tikzPrintf}\index[fields]{complex!ONE} \begin{codeandresult} \begin{luacode*} local A = complex.ONE local B = complex.I local C = (-A-B)/2 hyper.tikzBegin() hyper.tikzPrintf( "\\draw[%s] (%f,%f) -- (%f,%f) ;", "red, thick, dashed", A.re, A.im, B.re, B.im ) hyper.tikzPrintf( "\\draw[%s] (%f,%f) rectangle (%f,%f) ;", "blue, thick", C.re, C.im, 0.8,0.7 ) hyper.tikzEnd() \end{luacode*} \end{codeandresult} The package doesn't expose euclidean drawing functions. Users should write TikZ directly in the picture as above with \texttt{tikzPrintf}, or use some other library, for example the amazing library \texttt{tkz-euclide}. \subsection{Modifying default styles} The drawing default styles are defined in lua constants that the user can modify. Keys and default values include: \begin{lstlisting}[language=Lua] hyper.LABEL_STYLE = "above left" hyper.DRAW_POINT_RADIUS = 0.02 hyper.DRAW_POINT_STYLE = "white, draw=black" hyper.BOUNDARY_CIRCLE_STYLE = "very thick, black" \end{lstlisting} For a complete list, see the source file \verb+luahyperbolic-tikz.lua+ \section{Defining points} \subsection{Random points with uniform density} \index{randomPoint} \begin{codeandresult} \begin{luacode*} local rMin, rMax = .3, .8 hyper.tikzBegin() for k=1,100 do local M = hyper.randomPoint(rMin,rMax) hyper.drawPoint(M) end hyper.tikzPrintf("\\draw[dashed] (0,0) circle (%s) ;", rMin) hyper.tikzPrintf("\\draw[dashed] (0,0) circle (%s) ;", rMax) hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsection{Endpoints, midpoint, interpolate} \subsubsection{Endpoints} \index{endpoints} \begin{codeandresult} \begin{luacode*} local A = complex(.4,.5) local B = complex(-.5,0) local AA, BB = hyper.endpoints(A,B) hyper.tikzBegin() hyper.drawSegment(A,B, "very thick, teal") hyper.drawSegments(AA,A, B, BB, "dashed, red") hyper.labelPoints(A, "$A$", B, "$B$") hyper.labelPoint(AA, "$A'$", "red, below") hyper.labelPoint(BB, "$B'$", "red, below right") hyper.drawPoints(A, B) hyper.drawPoints(AA,BB,"white, draw=red") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Midpoint} \index{midpoint} \begin{codeandresult} \begin{luacode*} local A = complex(0.4,0.6) local B = complex(-0.8,-0.3) local M = hyper.midpoint(A,B) hyper.tikzBegin() hyper.drawSegment(A,B, "very thick, teal") hyper.drawPoints(A, B, M) hyper.labelPoints(A, "$A$", B, "$B$") hyper.labelPoint(M, "$M$", "below right, red") local marking = "\\textcolor{red}{||}" hyper.markSegment(A,M,marking) hyper.markSegment(M,B,marking) hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Interpolate and barycenter} \index{interpolate} \begin{codeandresult} \begin{luacode*} local A = complex(0.3,0.8) local B = complex(-0.6,-0.3) local P1 = hyper.interpolate(A, B, 1/3) local P2 = hyper.interpolate(A, B, 2/3) hyper.tikzBegin() hyper.drawSegment(A,B, "thick, teal") hyper.labelPoints(A, "$A$", B, "$B$") hyper.markSegments(A, P1, P1, P2, P2, B, "||") hyper.drawPoints(A, B, P1, P2) hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsection{Intersections} \subsubsection{Line-Line (LL) intersection} \index{interLL} \begin{codeandresult} \begin{luacode*} local A = complex(0.4,0.3) local B = complex(-0.4,-0.7) local C = complex(0,0.7) local D = complex(0.8,-0.4) local P = hyper.interLL(A, B, C, D) hyper.tikzBegin() hyper.drawLines(A, B, C, D, "thick, teal") hyper.drawPoints(A, B, C, D,"thick, teal") hyper.drawPoint(P,"white, draw=red") hyper.labelPoint(A, "$A$", "above") hyper.labelPoint(B, "$B$") hyper.labelPoint(C, "$C$", "left") hyper.labelPoint(D, "$D$", "above") hyper.labelPoint(P, "$P$", "left=.1cm, red") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Line-circle (LC) intersection} \index{interLC} \begin{codeandresult} \begin{luacode*} local A = complex(0.5,0.4) local B = complex(-0.3,-0.7) local O = complex(-0.4,0.2) local radius = 1.5 local P, Q = hyper.interLC(A, B, O, radius) hyper.tikzBegin() hyper.drawLine(A, B, "teal") hyper.drawCircle(O,radius, "teal") hyper.drawSegments(O, P, O, Q, "red, dashed") hyper.drawPoints(A, B, O, "teal") hyper.drawPoints(P, Q, "white, draw=red") hyper.labelPoints(A, "$A$", B, "$B$", O, "$O$") hyper.labelPoints(P, "$P$", Q, "$Q$", "red, below right") local mark = "\\textcolor{red}{||}" hyper.markSegments(O, P, O, Q, mark) hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Circle-circle (CC) intersection} \index{interCC} \begin{codeandresult} \begin{luacode*} local A = complex(-0.5,-.3) local B = complex(0.3,0) local r = hyper.distance(A, B) local P, Q = hyper.interCC(A, r, B, r) hyper.tikzBegin() hyper.drawCircle(A, r, "teal") hyper.drawCircle(B, r, "teal") hyper.drawPolygon(A, B, P, "dashed, red") hyper.drawPoints(P, Q, "white, draw=red") hyper.drawPoints(A, B, "teal") hyper.labelPoints(A, "$A$", B, "$B$", "below, teal") hyper.labelPoints(P, "$P$", "above left, red") hyper.labelPoint(Q, "$Q$", "red, below") local mark = "\\textcolor{red}{||}" hyper.markSegments(A, B, B, P, P, A, mark) hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsection{Reflections and projections} \subsubsection{Reflection across a geodesic line} \index{reflection}\index{drawRightAngle} \begin{codeandresult} \begin{luacode*} local A = complex(0.8,0.2) local B = complex(-0.3,0.5) local M = complex(0.3,-0.6) local Mp = (hyper.reflection(A,B))(M) hyper.tikzBegin() hyper.drawLine(A, B, "thick, teal") hyper.drawPoints(A, B, M) hyper.drawPoint(Mp,"red") hyper.drawSegment(M,Mp,"dashed,red") hyper.labelPoint(Mp,"$M'$", "left, red") local P = hyper.interLL(A, B, M, Mp) local mark="\\textcolor{red}{||}" hyper.labelPoints(A, "$A$", B, "$B$", M, "$M$", "below") hyper.markSegments(M, P, P, Mp, mark) hyper.drawRightAngle(M, P, A, "red") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Projection onto a geodesic line} \index{projection} \begin{codeandresult} \begin{luacode*} local A = complex(0.8,0.2) local B = complex(-0.3,0.5) local M = complex(0.3,-0.6) local P = (hyper.projection(A,B))(M) hyper.tikzBegin() hyper.drawLine(A, B, "thick, teal") hyper.drawPoints(A, B, M) hyper.drawPoint(P,"white, draw=red") hyper.drawSegment(M,P,"dashed,red") hyper.labelPoint(P,"$P$", "above, red") hyper.labelPoints(A, "$A$", B, "$B$", M, "$M$", "below") hyper.drawRightAngle(M,P,A,"red") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsection{Exponential map} \subsubsection{Image of a vector by exponentiel map at a point} \index{expMap} \begin{codeandresult} \begin{luacode*} local P = complex(-.2,-0.3) local vect = complex.exp_i(math.pi/4) hyper.tikzBegin() hyper.drawRayFromVector(P, vect, "gray") for k=1,5 do local A = hyper.expMap(P,k*vect) hyper.drawPoint(A, "white, draw=red") end hyper.drawPoint(P) hyper.labelPoint(P,"$P$") hyper.drawVector(P,vect, "red") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsection{Automorphisms (direct isometries)} \subsubsection{(Elliptic) Rotation by $60^\circ$} \index{rotation} \begin{codeandresult} \begin{luacode*} local O = complex(0.3,0.3) local r = hyper.rotation(O,math.pi/3) local points = {0, -.8, complex.J/2, -complex.I/2, complex(-.5,-.3), complex(.7,.3)} hyper.tikzBegin() for _,P in ipairs(points) do local rP = r(P) hyper.drawCircleThrough(O,P, "gray!10") hyper.drawArc(O,P,rP,"->, red, thick") hyper.drawPolyline(P,O,rP, "red!10") hyper.drawPoint(P) end hyper.drawPoint(O,"black") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Symmetry around a point (rotation by $180^\circ$)} \index{symmetry} \begin{codeandresult} \begin{luacode*} local O = complex(0.2,0.2) local s = hyper.symmetry(O) local points={complex(-.8,.1), complex(.6,0), complex(0,-.5)} hyper.tikzBegin() for k, P in ipairs(points) do hyper.drawCircleThrough(O, P, "gray!10") local sP = s(P) hyper.drawSegment(P,sP, "red, dashed") hyper.drawPoints(P,sP) hyper.labelPoint(P, "$P_{".. k .."}$") hyper.labelPoint(sP, "$\\sigma(P_{".. k .."})$") end hyper.drawPoint(O) hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Hyperbolic automorphims} \index{automorphism} \begin{codeandresult} \begin{luacode*} local A = complex(0.4,0.3) local phi = hyper.automorphism(A,0) local N=6 hyper.tikzBegin() style1 = "dotted, red!50" style2 = "thick, red" for k=1,N do local P = complex.polar(k/N, k*math.pi/N) local Q = phi(P) hyper.drawLine(0,P,style1) hyper.drawLine(-A,Q,style2) if k < N then hyper.drawCircleThrough(0,P, style1) hyper.drawCircleThrough(-A,Q,style2) end end hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsection{Orbits under automorphisms} \subsubsection{Orbit under elliptic automorphism (rotation)} \index{drawPointOrbit} \begin{codeandresult} \begin{luacode*} local nbIterations = 10 local O = complex(0.1,0.2) local points = {0, -.8, complex.J/2, -complex.I/2, complex(-.5,-.3), complex(.7,.3)} local rot = hyper.rotation(O,math.pi/30) -- angle in radians hyper.tikzBegin() hyper.drawPoint(O,"red") for _,P in ipairs(points) do hyper.drawCircleThrough(O,P,"draw opacity=0.2") hyper.drawPointOrbit(P, rot, nbIterations) end hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Orbit under hyperbolic automorphism} \begin{codeandresult} \begin{luacode*} local points = {0, -.8, complex.J/2, -complex.I/2, complex(-.5,-.3), complex(.7,.3)} local nbIterations = 10 local A = complex(0.05,0.05) local phi = hyper.automorphism(A,0) local X, Y = hyper.endpoints(A,0) hyper.tikzBegin() hyper.drawLine(A,0,"red!50") for _,P in ipairs(points) do hyper.drawHypercycleThrough(P,X,Y,"gray") hyper.drawPointOrbit(P, phi, nbIterations) end hyper.drawPoint(A,"red") hyper.labelPoint(A,"$A$", "above left, red") hyper.drawPointOrbit(A, phi, nbIterations, "red") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Orbit under parabolic automorphism} \index{parabolic} \begin{codeandresult} \begin{luacode*} local nbIterations = 10 local limit = complex.exp_i(math.pi/3) -- on circle local phi = hyper.parabolic(limit,math.pi/30) local points={ complex(0,0), complex(0,.7), complex(.4,.4), complex(.3,.7), complex(-.8,.4), complex(-.4,0), complex(.4,-.45)} hyper.tikzBegin() for _,P in ipairs(points) do hyper.drawPointOrbit(P, phi, nbIterations) hyper.drawHorocycle(limit, P, "red, draw opacity=.3") end hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsection{Triangle geometry (CAUTION)} \subsubsection{Centroid (intersection of medians)} \index{triangleCentroid} \begin{codeandresult} \begin{luacode*} hyper.tikzBegin() local A = complex(0.1,0.8) local B = complex(-0.7,-0.3) local C = complex(0.6,-0.2) local AA = hyper.midpoint(B,C) local BB = hyper.midpoint(C,A) local CC = hyper.midpoint(A,B) local G = hyper.triangleCentroid(A,B,C) hyper.drawTriangle(A,B,C) hyper.drawSegments(A, AA, B, BB, C, CC, "teal") hyper.drawPoints(AA,BB,CC) hyper.drawPoint(G,"white, draw=teal") -- CAUTION : local wrong = hyper.interpolate(A,AA,2/3) hyper.drawPoint(wrong, "red") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Incenter and incircle} \index{triangleIncenter}\index{drawIncircle} \begin{codeandresult} \begin{luacode*} hyper.tikzBegin() local A = complex(0.1,0.8) local B = complex(-0.7,-0.3) local C = complex(0.6,-0.2) local I = hyper.triangleIncenter(A,B,C) hyper.drawTriangle(A,B,C,"teal") hyper.drawPoint(I,"white, draw=red") hyper.labelPoint(I,"$I$", "below left") hyper.drawIncircle(A,B,C,"red") -- show intersection : local mystyle = "dashed, red!50" hyper.drawAngleBisector(A,B, C, mystyle) hyper.drawAngleBisector(B,C,A, mystyle) hyper.drawAngleBisector(C,A,B, mystyle) hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Circumcenter and circumcircle (CAUTION : does not always exist)} \index{triangleCircumcenter}\index{drawCircumcircle} \begin{codeandresult} \begin{luacode*} local A = complex(0.1,0.8) local B = complex(-0.6,-0.5) local C = complex(0.6,-0.2) local O = hyper.triangleCircumcenter(A,B,C) local BC = hyper.midpoint(B,C) local CA = hyper.midpoint(C,A) local AB = hyper.midpoint(A,B) hyper.tikzBegin() local mystyle = "dashed, red!50" hyper.drawTriangle(A,B,C,"teal") hyper.drawPerpendicularBisector(A, B, mystyle) hyper.drawPerpendicularBisector(B, C, mystyle) hyper.drawPerpendicularBisector(C, A, mystyle) hyper.drawCircumcircle(A,B,C,"red") hyper.labelPoint(O,"$O$", "below left") hyper.drawRightAngle(O, CA, C,"red",1/8) hyper.drawRightAngle(O,BC,B,"red",1/8) hyper.drawPoints(O,A,B,C,AB,BC,CA) hyper.labelPoints(A, "$A$", B, "$B$", C, "$C$","above") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Orthocenter (CAUTION : does not always exist)} \index{triangleOrthocenter}\index{drawPerpendicularThrough} \begin{codeandresult} \begin{luacode*} local A = complex(0.1,0.8) local B = complex(-0.7,-0.4) local C = complex(0.6,-0.2) local H = hyper.triangleOrthocenter(A,B,C) local AA = hyper.projection(B,C)(A) local BB = hyper.projection(C,A)(B) local CC = hyper.projection(A,B)(C) hyper.tikzBegin() local mystyle = "dashed, red!50" local style2 = "very thick, red" hyper.drawTriangle(A,B,C,"teal") hyper.drawPerpendicularThrough(A,B,C,mystyle) hyper.drawPerpendicularThrough(B,C,A,mystyle) hyper.drawPerpendicularThrough(C,A,B,mystyle) hyper.drawRightAngle(A,AA,B,style2,1/15) hyper.drawRightAngle(B,BB,C,style2,1/8) hyper.drawRightAngle(C,CC,A,style2,1/12) hyper.labelPoints(A, "$A$", B, "$B$", C, "$C$") hyper.drawPoints(AA,BB,CC,H,"above") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \newpage \section{Tilings} \subsection{Draw Tilings} \subsubsection{Triangle tiling (almost) by hand} \index{propagateGeodesics}\index{drawLinesFromTable} \begin{codeandresult} \begin{luacode*} local PI = math.pi local A, B, C = hyper.triangleWithAngles(PI/2, PI/4, PI/5) local geodesics = { {hyper.endpoints(A,B)}, {hyper.endpoints(B,C)}, {hyper.endpoints(C,A)} } geodesics = hyper.propagateGeodesics(geodesics, 12) -- depth = 12 hyper.tikzBegin() hyper.drawLinesFromTable(geodesics, "green!50!black, draw opacity=.5") hyper.drawTriangle(A,B,C,"red, very thick") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Triangle tiling with less code} \index{drawTilingFromTriangle} \begin{codeandresult} \begin{luacode*} local PI = math.pi local DEPTH = 12 local A, B, C = hyper.triangleWithAngles(PI/2,PI/3,0) hyper.tikzBegin() hyper.drawTilingFromTriangle(A, B, C, DEPTH, "teal, draw opacity=.5") hyper.drawTriangle(A,B,C,"red, very thick") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Triangle tiling with even less code} \index{drawTilingFromAngles} \begin{codeandresult} \begin{luacode*} local PI = math.pi hyper.tikzBegin() hyper.drawTilingFromAngles(PI/3,PI/5,PI/7,12) hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsection{Fill tilings} \subsubsection{The 3--4--5 triangle tiling} \index{fillTilingFromTriangle} \begin{codeandresult} \begin{luacode*} local PI = math.pi local DEPTH = 10 local A, B, C = hyper.triangleWithAngles(PI/3, PI/4, PI/5) hyper.tikzBegin() hyper.fillTilingFromTriangle(A, B, C, DEPTH) -- default color is black hyper.drawPolygon(A, B, C, "thick, red") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Same 3--4--5 tiling centered on point $B$} \begin{codeandresult} \begin{luacode*} local PI = math.pi local DEPTH = 10 local A, B, C = hyper.triangleWithAngles(PI/3, PI/4, PI/5) local phi = hyper.automorphism(B) A, B, C = phi(A), phi(B), phi(C) -- put B in the center hyper.tikzBegin() hyper.fillTilingFromTriangle(A, B, C, DEPTH, "fill opacity=.2, teal!80!black, draw=black") hyper.drawPolygon(A, B, C, "thick, red") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsubsection{Same 3--4--5 tiling centered on point $C$} \begin{codeandresult} \begin{luacode*} local PI = math.pi local DEPTH = 10 local A, B, C = hyper.triangleWithAngles(PI/3, PI/4, PI/5) local phi = hyper.automorphism(C) A, B, C = phi(A), phi(B), phi(C) -- put C in the center hyper.tikzBegin() hyper.tikzPrintf("\\fill[orange!30] (0,0) circle (1);") hyper.fillTilingFromTriangle(A, B, C, DEPTH, "teal") hyper.drawPolygon(A, B, C, "thick, red") hyper.tikzEnd() \end{luacode*} \end{codeandresult} \subsection{Tilings without any Lua code} The package provides a TeX macro for users who want to completely avoid Lua. \bigskip \noindent \hyperbolicTiling{3}{4}{5}[scale=4]\hfill \hyperbolicTiling{3}{3}{4}[scale=4,depth=12] \\ \hyperbolicTiling{2}{3}{7}[scale=2.5, color=teal, depth=16]\hfill \hyperbolicTiling{2}{5}{9}[scale=2.5, depth=8, color=gray]\hfill \hyperbolicTiling{4}{4}{4}[scale=2.5, depth=6, color=purple,backgroundcolor=pink!30] \\ \hyperbolicTiling{2}{5}{inf}[scale=2, depth=6, color=brown]\hfill \hyperbolicTiling{2}{inf}{inf}[scale=2, depth=6, color=olive]\hfill \hyperbolicTiling{3}{inf}{inf}[scale=2, depth=6, color=orange]\hfill \hyperbolicTiling{inf}{inf}{inf}[scale=2, depth=5, color=violet] The syntax is \verb+\hyperbolicTiling{p}{q}{r}[options]+ with $p, q, r$ positive integers or \texttt{inf} satisfying $\frac1p + \frac1q+\frac1r < 1$. The associated hyperbolic triangle has angles $\frac{\pi}{p}$, $\frac{\pi}{q}$, and $\frac{\pi}{r}$. The above tilings were obtained with the following commands: \begin{verbatim} \hyperbolicTiling{3}{4}{5}[scale=4] \hyperbolicTiling{3}{3}{4}[scale=4,depth=12] \hyperbolicTiling{2}{3}{7}[scale=2.5, color=teal, depth=16] \hyperbolicTiling{2}{5}{9}[scale=2.5, color=gray] \hyperbolicTiling{4}{4}{4}[scale=2.5, depth=6, color=purple,backgroundcolor=pink!30] \hyperbolicTiling{2}{5}{inf}[scale=2, depth=6, color=brown] \hyperbolicTiling{2}{inf}{inf}[scale=2, depth=6, color=olive] \hyperbolicTiling{3}{inf}{inf}[scale=2, depth=6, color=orange] \hyperbolicTiling{inf}{inf}{inf}[scale=2, depth=5, color=violet] \end{verbatim} Default values are : scale=3, depth=8, color=black, backgroundcolor=white. \newpage \section{Overview of the lua libraries} The Lua files are in the \verb+lua/+ subdirectory of the repo. To build the \verb+luahyperbolic.sty+ from the lua source files, run \verb+lua build.lua+ on the \verb+lua/+ directory. (Lua must be installed.) The Lua source files are not needed to compile a latex document using luahyperbolic, only the sty file is necessary. \begin{luacode*} -- Print all entries of a given type in a module function print_module_type(module, wanted_type, sep) sep = sep or ", " local names = {} for k, v in pairs(module) do if type(v) == wanted_type then table.insert(names, k) end end table.sort(names) for i, name in ipairs(names) do tex.sprint(-2, name) tex.sprint(sep) end end \end{luacode*} \subsection{complex.lua} \subsubsection*{Functions} \begin{multicols}{6} \noindent\directlua{print_module_type(complex, "function", "\\\\")} \end{multicols} \subsubsection*{Constants (numbers and complex numbers)} \directlua{print_module_type(complex, "number", ", ")} \directlua{print_module_type(complex, "table", ", ")} \subsection{luahyperbolic} The luahyperbolic library consists of three submodules : luahyperbolic-core, luahyperbolic-tikz and luahyperpolic-tilings. These three submodules are flattened in a single \texttt{hyper} global module. (The submodules are still available in three tables hyper.core, hyper.tikz and hyper.tilings if necessary.) Functions beginning with underscore are available in the module but should not be needed for normal use. Don't use them, they could become private in the future and not be available to users anymore. \subsubsection*{Functions} \begin{multicols}{3} \noindent\directlua{print_module_type(hyper, "function", "\\\\")} \end{multicols} \subsubsection*{Constants} \textbf{Numbers : } \directlua{print_module_type(hyper, "number", ", ")} \textbf{Strings : } \directlua{print_module_type(hyper, "string", ", ")} \textbf{Tables : } \directlua{print_module_type(hyper, "table", ", ")} \section{Known bugs} \begin{enumerate} \item Cannot use the TeX macro \verb+\hyperbolicTiling+ inside a \verb+begin{center}+ environment. Boxes and \verb+\centering+ don't work well either. \item This documentation sometimes errors during compilation with error \texttt{pgf : dimension too large} but a second compilation always succeeds. \end{enumerate} \section{Index} \printindex \end{document}