4

This post is origined from this excellent answer of luadraw:

Now assuming that I was neglected the [1] after parametric3d(function(t) return M(t,0,3/(t^2+0.5)) end,0,7,15):

\documentclass[border=5pt]{standalone}
\usepackage[svgnames]{xcolor}
\usepackage[3d]{luadraw}%v2.2
\begin{document}
\begin{luadraw}{name=surface}
local g = graph3d:new{window3d={-5,5,-5,5,-1,6},adjust2d=true,bbox=false,size={10,10}, viewdir={-30,50}}
g:Labelsize("footnotesize")
local curve = parametric3d(function(t) return M(t,0,3/(t^2+0.5)) end,0,7,15)-- [1] 
-- I forget `[1]` here.
-- The points on this curve are not regularly spaced.
local S = rotline(curve,{Origin,vecK},-180,180) 
-- We rotate the curve around the axis (O,k) to obtain the surface.
g:Dboxaxes3d({grid=true,gridcolor="gray",fillcolor="LightGray",xyzstep=2})
g:Dfacet(S, {usepalette={palAutumn,"z"},clip=true})
g:Dpolyline3d(curve,false,"DarkGreen,line width=0.8pt",true)
g:Ddots3d(curve,"scale=0.75,DarkGreen",true)
g:Dlabel3d("curve $M(t,0,\\frac3{t^2+0.5})$",M(5.5,0,0),{dir={vecJ,vecK},node_options="DarkGreen"})
g:Show()
\end{luadraw}
\end{document}

Then run with lualatex, I would get:

...2025/texmf-dist/tex/lualatex/luadraw/luadraw_build3d.lua:383: attempt to index a nil va
lue (local 'S')
stack traceback:
        ...2025/texmf-dist/tex/lualatex/luadraw/luadraw_build3d.lua:383: in function 'clip3d'
        ...2025/texmf-dist/tex/lualatex/luadraw/luadraw_graph3d.lua:1344: in function 'luadraw_graph3d.Dfacet'
        [\directlua]:7: in main chunk.
\luacode@dbg@exec ...code@maybe@printdbg {#1} #1 }

l.18 \end{luadraw}

Now, I have two options to debug:

  • to read the error information attempt to index a nil value (local 'S') which is not really readable friendly
  • dive into the manual, to consult what parametric3d returns

As nidarfp mentioned at here:

parametric3d() returns a list (table) of lists of 3d points, because a curve can be made up of several pieces (several connected components), so [1] means that we take the first component of the curve (and as the function is continuous, there is no other)

Or conduct some debug to show some intermediate variables, such as in my case, I want to know what is local S actually look like.

In tikz case, we can use \node {<somevalue I want to know>}; to show what it is. But in the world of luadraw, I know little.

Is that any easy method in this specific case to know what local S, which is actually "a list (table) of lists of 3d points"? with which, it could be more easier for me to debug and know I only need to use the first component of the curve.

Edited:

As nidarfp show isListOfPt3d and isListOfListOfPt3d to detect what local S actually is. But what I was after is a more general "debug" scheme. Take tikz as an example:

\documentclass[tikz,border=5pt]{standalone}
\begin{document}
\def\radius{1}
\def\height{1.05}%to tune the eccentricity
\def\leftheight{2}
\def\rightheight{4}
\def\midheight{\fpeval{(\leftheight+\rightheight)/2}}
\def\hh{\fpeval{abs(\leftheight-\rightheight)/2}}
\begin{tikzpicture}[line join=round]
    \draw[dashed] (\radius,0) arc (0:180:{\radius} and {1/3});
    \draw (-\radius,0) arc (180:360:{\radius} and {1/3});
    \draw (-\radius,0) -- (-\radius,\leftheight) coordinate (A);
    \draw (\radius,0) -- (\radius,\rightheight) coordinate (B);
    \def\phii{\fpeval{acos(\hh/\height)}}
    \def\xa{0}
    \def\ya{\fpeval{-\height*sin(\phii)}}
    \def\xb{\radius}
    \def\yb{\fpeval{\height*cos(\phii)}}
    \draw[cm={\xa,\ya,\xb,\yb,(0,\midheight)}] circle [radius=1cm];
    \filldraw[red] (A) circle[radius=.5pt] node[left,text=black] {$A$} (B) circle[radius=.5pt] node[right,text=black] {$B$};
    \node[align=left,rectangle] at (\radius+4,\rightheight/2) {%
        $\phi$ \texttt{=\phii}\\
        $(x_1,y_1)$ \texttt{=(\xa,\ya)}\\
        $(x_2,y_2)$ \texttt{=(\xb,\yb)}
    };
\end{tikzpicture}
\end{document}

res

In the above coding process, I could use:

\node[align=left,rectangle] at (\radius+4,\rightheight/2) {%
        $\phi$ \texttt{=\phii}\\
        $(x_1,y_1)$ \texttt{=(\xa,\ya)}\\
        $(x_2,y_2)$ \texttt{=(\xb,\yb)}
    };

to show the parameters' value, which helps me to check whether my code is correct.

but is that possible I could show that local S=surface(...), which is something like:

S = { {Z(-4,0), Z(0,2), Z(1,3)}, {Z(0,0), Z(4,-2), Z(1,-1)}}

in the PDF, or in the teriminal or .log file, to help debug for the datas structure(?) with luadraw? Define a function to test elements' type and content is not generally a efficient debug method...

Or I can take another example with Python, if we saw that print(element) gives:

element = [ [1,2,3] , [2,3,4,5] ] 

It's easily to know that is "list in list", and we can use element[0] or element[1][2] to index what we want. Is that possible for lualatex to provide something like this to help debug and programming?

I hope with the parallel examples, I make it clearly....

2
  • 1
    With Lua you can also use print(S) (in luadraw code) to see S in the terminal, but lua will print table, to see the elements you must use print(table.unpack(S)). Commented Oct 21, 2025 at 16:36
  • @nidarfp Impressive comment, I add my answer I think I was after. Thank! You are an actual master in lua! Commented Oct 21, 2025 at 16:48

2 Answers 2

4

Thanks to nidarfp's comment, I explore something what I want:

By adding

print(curve)
print(table.unpack(curve))
print(table.unpack(curve[1]))

after local curve = parametric3d(function(t) return M(t,0,3/(t^2+0.5)) end, 0,7, 15)

The complete code

\documentclass[border=5pt]{standalone}
\usepackage[svgnames]{xcolor}
\usepackage[3d]{luadraw}%v2.2
\begin{document}
\begin{luadraw}{name=surface}
local g = graph3d:new{window3d={-5,5,-5,5,-1,6},adjust2d=true,bbox=false,size={10,10}, viewdir={-30,50}}
g:Labelsize("footnotesize")
local curve = parametric3d(function(t) return M(t,0,3/(t^2+0.5)) end, 0,7, 15)--[1] -- I forget `[1]` here.
print(curve)
print(table.unpack(curve))
print(table.unpack(curve[1]))
-- The points on this curve are not regularly spaced.
local S = rotline(curve,{Origin,vecK},-180,180) -- We rotate the curve around the axis (O,k) to obtain the surface.
g:Dboxaxes3d({grid=true,gridcolor="gray",fillcolor="LightGray",xyzstep=2})
g:Dfacet(S, {usepalette={palAutumn,"z"},clip=true})
g:Dpolyline3d(curve,false,"DarkGreen,line width=0.8pt",true)
g:Ddots3d(curve,"scale=0.75,DarkGreen",true)
g:Dlabel3d("curve $M(t,0,\\frac3{t^2+0.5})$", M(5.5,0,0),{dir={vecJ,vecK},node_options="DarkGreen"})
g:Show()
\end{luadraw}
\end{document}

In the terminal it shows that:

table: 000000000B472640
table: 000000000B472280
M(0,0,6.0)      M(0.015625,0,5.9970717423133)
M(0.03125,0,5.9883040935673)    M(0.046875,0,5.9737481769567) 
M(0.0625,0,5.953488372093)      M(0.078125,0,5.9276410998553)   
M(0.09375,0,5.8963531669866)   M(0.125,0,5.8181818181818)      
M(0.25,0,5.3333333333333)       M(0.5,0,4.0)  
M(0.75,0,2.8235294117647)        M(1.0,0,2.0)    
M(1.125,0,1.6991150442478)      M(1.25,0,1.4545454545455)      
M(1.375,0,1.2549019607843)      M(1.5,0,1.0909090909091)        
M(1.625,0,0.95522388059701)    M(1.75,0,0.84210526315789)      
M(1.875,0,0.74708171206226)     M(2.0,0,0.66666666666667)      
M(2.25,0,0.53932584269663)      M(2.5,0,0.44444444444444)       
M(3.0,0,0.31578947368421)      M(3.5,0,0.23529411764706)       
M(4.0,0,0.18181818181818)       M(4.5,0,0.14457831325301)      
M(5.0,0,0.11764705882353)       M(5.5,0,0.097560975609756)     
M(6.0,0,0.082191780821918)     M(6.5,0,0.070175438596491)      
M(7.0,0,0.060606060606061)

That is equals to says that:

curve = { 
           {
             M(0,0,6.0) , 
             M(0.015625,0,5.9970717423133) ,
             ... ,
             M(7.0,0,0.060606060606061) 
           } 
        }

That is so-called "parametric3d() returns a list(table) of lists of 3d points" for. With this, it could be more efficient to debug for me😊😊!

Edited

The Lua-command table.print(...) is more effieient:

\documentclass[border=5pt]{standalone}
\usepackage[svgnames]{xcolor}
\usepackage[3d]{luadraw}%v2.2
\begin{document}
\begin{luadraw}{name=surface}
local g = graph3d:new{window3d={-5,5,-5,5,-1,6},adjust2d=true,bbox=false,size={10,10}, viewdir={-30,50}}
g:Labelsize("footnotesize")
local curve = parametric3d(function(t) return M(t,0,3/(t^2+0.5)) end, 0,7, 15)--[1] -- I forget `[1]` here.
table.print(curve)
-- The points on this curve are not regularly spaced.
local S = rotline(curve,{Origin,vecK},-180,180) -- We rotate the curve around the axis (O,k) to obtain the surface.
g:Dboxaxes3d({grid=true,gridcolor="gray",fillcolor="LightGray",xyzstep=2})
g:Dfacet(S, {usepalette={palAutumn,"z"},clip=true})
g:Dpolyline3d(curve,false,"DarkGreen,line width=0.8pt",true)
g:Ddots3d(curve,"scale=0.75,DarkGreen",true)
g:Dlabel3d("curve $M(t,0,\\frac3{t^2+0.5})$", M(5.5,0,0),{dir={vecJ,vecK},node_options="DarkGreen"})
g:Show()
\end{luadraw}
\end{document}

With table.print(curve), in the terminal, we can get that:

t={
 {
  ["x"]=0,
  ["y"]=0,
  ["z"]=6.0,
 },
 {
  ["x"]=0.015625,
  ["y"]=0,
  ["z"]=5.9970717423133,
 },
 {
  ["x"]=0.03125,
  ["y"]=0,
  ["z"]=5.9883040935673,
 },
 {
  ["x"]=0.046875,
  ["y"]=0,
  ["z"]=5.9737481769567,
 },
 {
  ["x"]=0.0625,
  ["y"]=0,
  ["z"]=5.953488372093,
 },
 {
  ["x"]=0.078125,
  ["y"]=0,
  ["z"]=5.9276410998553,
 },
 {
  ["x"]=0.09375,
  ["y"]=0,
  ["z"]=5.8963531669866,
 },
 {
  ["x"]=0.125,
  ["y"]=0,
  ["z"]=5.8181818181818,
 },
 {
  ["x"]=0.25,
  ["y"]=0,
  ["z"]=5.3333333333333,
 },
 {
  ["x"]=0.5,
  ["y"]=0,
  ["z"]=4.0,
 },
 {
  ["x"]=0.75,
  ["y"]=0,
  ["z"]=2.8235294117647,
 },
 {
  ["x"]=1.0,
  ["y"]=0,
  ["z"]=2.0,
 },
 {
  ["x"]=1.125,
  ["y"]=0,
  ["z"]=1.6991150442478,
 },
 {
  ["x"]=1.25,
  ["y"]=0,
  ["z"]=1.4545454545455,
 },
 {
  ["x"]=1.375,
  ["y"]=0,
  ["z"]=1.2549019607843,
 },
 {
  ["x"]=1.5,
  ["y"]=0,
  ["z"]=1.0909090909091,
 },
 {
  ["x"]=1.625,
  ["y"]=0,
  ["z"]=0.95522388059701,
 },
 {
  ["x"]=1.75,
  ["y"]=0,
  ["z"]=0.84210526315789,
 },
 {
  ["x"]=1.875,
  ["y"]=0,
  ["z"]=0.74708171206226,
 },
 {
  ["x"]=2.0,
  ["y"]=0,
  ["z"]=0.66666666666667,
 },
 {
  ["x"]=2.25,
  ["y"]=0,
  ["z"]=0.53932584269663,
 },
 {
  ["x"]=2.5,
  ["y"]=0,
  ["z"]=0.44444444444444,
 },
 {
  ["x"]=3.0,
  ["y"]=0,
  ["z"]=0.31578947368421,
 },
 {
  ["x"]=3.5,
  ["y"]=0,
  ["z"]=0.23529411764706,
 },
 {
  ["x"]=4.0,
  ["y"]=0,
  ["z"]=0.18181818181818,
 },
 {
  ["x"]=4.5,
  ["y"]=0,
  ["z"]=0.14457831325301,
 },
 {
  ["x"]=5.0,
  ["y"]=0,
  ["z"]=0.11764705882353,
 },
 {
  ["x"]=5.5,
  ["y"]=0,
  ["z"]=0.097560975609756,
 },
 {
  ["x"]=6.0,
  ["y"]=0,
  ["z"]=0.082191780821918,
 },
 {
  ["x"]=6.5,
  ["y"]=0,
  ["z"]=0.070175438596491,
 },
 {
  ["x"]=7.0,
  ["y"]=0,
  ["z"]=0.060606060606061,
 },
}

which shows the structure of list-in-list more explictly.

Edited Again with whatis in luadraw-v2.3

Thanks to the development, and I was Glad to know this new feature, it makes debug more easy:

result

\documentclass[border=5pt]{standalone}
\usepackage[svgnames]{xcolor}
\usepackage[3d]{luadraw}%v2.2
\begin{document}
\begin{luadraw}{name=surface}
local g = graph3d:new{window3d={-5,5,-5,5,-1,6},adjust2d=true,bbox=false,size={10,10}, viewdir={-30,50}}
g:Labelsize("footnotesize")
local curve = parametric3d(function(t) return M(t,0,3/(t^2+0.5)) end, 0,7, 15)-- [1] -- Noted that I forget `[1]` here.
whatis(curve,"what is curve?")
-- The points on this curve are not regularly spaced.
local S = rotline(curve,{Origin,vecK},-180,180) -- We rotate the curve around the axis (O,k) to obtain the surface.
g:Dboxaxes3d({grid=true,gridcolor="gray",fillcolor="LightGray",xyzstep=2})
g:Dfacet(S, {usepalette={palAutumn,"z"},clip=true})
g:Dpolyline3d(curve,false,"DarkGreen,line width=0.8pt",true)
g:Ddots3d(curve,"scale=0.75,DarkGreen",true)
g:Dlabel3d("curve $M(t,0,\\frac3{t^2+0.5})$", M(5.5,0,0),{dir={vecJ,vecK},node_options="DarkGreen"})
g:Show()
\end{luadraw}
\end{document}

By adding whatis(curve), in the terminal, the type of curve is easy to know as below:

what is curve? is a list of lists of 3d points
value = { { M(0,0,6.0), M(0.015625,0,5.9970717423133), M(0.03125,0,5.9883040935673), M(0.046875,0,5.9737481769567), M(0.0625,0,5.953488372093), M(0.078125,0,5.9276410998553), M(0.09375,0,5.8963531669866), M(0.125,0,5.8181818181818), M(0.25,0,5.3333333333333), M(0.5,0,4.0), M(0.75,0,2.8235294117647), M(1.0,0,2.0), M(1.125,0,1.6991150442478), M(1.25,0,1.4545454545455), M(1.375,0,1.2549019607843), M(1.5,0,1.0909090909091), M(1.625,0,0.95522388059701), M(1.75,0,0.84210526315789), M(1.875,0,0.74708171206226), M(2.0,0,0.66666666666667), M(2.25,0,0.53932584269663), M(2.5,0,0.44444444444444), M(3.0,0,0.31578947368421), M(3.5,0,0.23529411764706), M(4.0,0,0.18181818181818), M(4.5,0,0.14457831325301), M(5.0,0,0.11764705882353), M(5.5,0,0.097560975609756), M(6.0,0,0.082191780821918), M(6.5,0,0.070175438596491), M(7.0,0,0.060606060606061) } }
3

There is a function that tests if we have a 3d point, so from there we can write a function that tests if we have a list of 3d points, and a function that tests if we have a list of lists of 3d points:

\documentclass[border=5pt]{standalone}% compile with lualatex only
\usepackage[svgnames]{xcolor}
\usepackage[3d]{luadraw}%https://github.com/pfradin/luadraw
%https://tex.stackexchange.com/questions/752634/a-logistical-way-to-recursively-subdivide-triangles-into-6-sub-triangles-such

\begin{luacode*}
function isListOfPt3d(S) -- tests if S is a list of 3d points
    return (S ~= nil) and (type(S)=="table") and (#S>0) and isPoint3d(S[1])
end

function isListOfListOfPt3d(S) -- tests if S is a list of lists 3d points
    return (S ~= nil) and (type(S)=="table") and (#S>0) and isListOfPt3d(S[1])
end
\end{luacode*}

\begin{document}
\begin{luadraw}{name=surface}
local g = graph3d:new{window3d={-5,5,-5,5,-1,6},adjust2d=true,bbox=false,size={10,10}, viewdir={-30,50}}
g:Labelsize("footnotesize")
local curve = parametric3d(function(t) return M(t,0,3/(t^2+0.5)) end, 0,7, 15) -- The points on this curve are not regularly spaced.

if isListOfListOfPt3d(curve) then curve = curve[1] end
-- rotline function needs a list of 3d points and not a list of lists
local S = rotline(curve,{Origin,vecK},-180,180) -- We rotate the curve around the axis (O,k) to obtain the surface.
g:Dboxaxes3d({grid=true,gridcolor="gray",fillcolor="LightGray",xyzstep=2})
g:Dfacet(S, {usepalette={palAutumn,"z"},clip=true})
g:Dpolyline3d(curve,false,"DarkGreen,line width=0.8pt",true)
g:Ddots3d(curve,"scale=0.75,DarkGreen",true)
g:Dlabel3d("curve $M(t,0,\\frac3{t^2+0.5})$", M(5.5,0,0),{dir={vecJ,vecK},node_options="DarkGreen"})
g:Show()
\end{luadraw}
\end{document}

Maybe such functions should be integrated into luadraw...

3
  • I can get the code's meaning in luacode* with lua syntax, but what I want, is a bit different. "Test whether one element is table" and "show what exactly the elements is" is not the same. I would edit the post and show a parallel case with tikz. Commented Oct 21, 2025 at 16:14
  • 1
    If I understand correctly, you want a function similar to Lua's type function, but which takes into account the different types created by Luadraw (complex numbers, 3D points, lists of ). I think this is possible Commented Oct 21, 2025 at 16:25
  • Ummm, there is no need to know luadraw's type. I only want the basic "type" that lua internally provided. If I know that rotline's first parameter only gobble "list of 3D points", but parametric3d here gives "list of list of 3D points", if I can detect this difference, I would easily to know that I need [1], and the debug process would be more efficient, compared with deeply consulting the manual. Commented Oct 21, 2025 at 16:29

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.