# How to draw 3D surface plot from .csv with asymptote

How can one draw "regular" 3D surface plot in Asymptote from data.csv?

Following (I dare to say basic) code with the help of pgfplots does the job (suppose you have optimal data format):

\documentclass[12pt]{article}

\begin{document}

\begin{figure}
\begin{tikzpicture}
\begin{axis}[colorbar]
surf,
mesh/ordering=y varies,
mesh/cols=15,
] table [col sep=comma,] {data.csv};
\end{axis}
\end{tikzpicture}
\end{figure}

\end{document}


graph

To my basic knowledge, only mesh/cols has to be set-up for given data set. Is there any -almost- general approach also in Asymptote?

NOTES:

Why Asymptote: Pgfplots are great Latex package, fun to use with great documentation and tutorials. However, even when I compile with LuaLaTeX, I am worried about performance - I expect that I will generate several 3D plots with more than 1 000 points, with might take long. Also very big feature is .prc format, which I would like to be able to utilize.

Why generic: I am still learning LaTeX, programming (mostly in python) and Asymptote syntax seems to me hard to grasp. There is great tutorial from Mr. Staats, and "package documentation," but they dont seem to cover my situation (or am I mistaken?) and I am not sure that I am up to learn Asymptote syntax ...

Final note: If there is no general approach, which could be easily utilized, as with pgfplots code above, please inform me (than this question might become off-topic, if it is not already) or, if there is the file data.csv needed, I will upload sample data.

Regards, Tomas

EDIT: Uploaded sample data.csv here (google drive)

I have to admit I lack knowledge of terminology in 3D graph creation. I have data structured in I would call a matrix - for each value of x, there is y changing by constant value and finally value of z. This information is passed to pgfplots package in the code above by lines

mesh/ordering = y varies,
mesh/cols = 15


I think I understand the code in the example as far as this:

//Necessary module imports
import three;
import grid3;

bool renderPRC = true; //Pretty essential for me, I would like to obtain graph in PRC format

if(renderPRC) {
// PRC TRUE
settings.prc=true;
settings.embed=true;
}
else {
// RASTERIZE
settings.outformat="png";
settings.prc=false;
settings.render=3;
}

file in=input(data.csv).line().csv(); // I am not sure, if "data" should be with ".csv" or not

real[][] a=in.dimension(0,0); //Initiation of array a with data collection until the end of file

real[] x=a;
real[] y=a;
real[] z=a; // Assignment of values from array a to x, y, z arrays; depending on column index? (data.csv has 3 columns)

//if I understood correctly, next are pen settings, which I think I dont need except for any contour, that would emphasize the "z" value.


Next goes surface part, which is beyond my knowledge.

How to plot a surface from a set of data?

As I dont have enough of a education/knowledge in mathematics, understanding of that is (now) beyond me, but I see that data in that answer resemble mine by theyre structure. I have also tested triangulation approach to create 3D graph with python and matplotlib, using its triangulation (ref: https://python-graph-gallery.com/371-surface-plot/).

Is that simillar to Asymptote and can answer from user g.kov be used also in my case? I test it as soon as I will have access to Asymptote and test it further (and edit the question again if needed).

• The answer seems to be yes, here is an example. – Schrödinger's cat Nov 4 at 17:03
• @Schrödinger'scat Hello sir. I am well aware of this post, solution. But situation of that request seems to be pretty complicated. If I understand (roughly) code posted in answers, it consists of two parts of final surface, which has also part that is excluded. I dare to say that my question, although not much specific, is simplier - I have 1 set of each of x, y, z values; not rotational object or anything of that sort of complexity. Which means, I dont know which parts of code you suggested I can (or cant) exclude. Also, I am having trouble finding reference about (similar) thing in docs ... – Tomáš Kruliš Nov 4 at 17:25
• Not sure I understand what you are asking. One set of each of x, y, z values would describe a curve, not a surface. Perhaps you should upload sample data? – Charles Staats Nov 4 at 18:24
• Regardless, an answer to your question would end up divided into two parts: 1) Read the CSV file into Asymptote as a real[][]. This is not difficult (although it can be difficult to remember the boilerplate). 2) Turn your array of 3d points into a surface. This requires some extra work in Asymptote. Most of the code in the linked answer is trying to deal with this second step. – Charles Staats Nov 4 at 18:26
• @CharlesStaats Edited the question, added sample data.csv. I think I (mostly) understand the .csv loading part (thanks to your Asymptote Tutorial). Later on I have noticed that there is an answer of user g.kov to situation (I think very) similar to mine, using "triangulation." I will test aplicability of that approach as soon as I can and clarify my question further. – Tomáš Kruliš Nov 5 at 12:13

With respect to the example Asymptote: draw surface from data points in 3D, the data are very well organized. The file data.csv is just a 3 columns, with x_i,y_j,z_(i,j), first in i, then in j.

The first task is to clean the data.csv file, that is remove the first line containing characters and not data and a blank line. That is my data1.csv. Moreover do not use in as name since it is a keyword with Asymptote. Since it is three columns, real[][] a=fic.dimension(0,3) does the job. a[i] is line i+1 of the file containing x,y,z.

As in Asymptote: draw surface from data points in 3D, there is at least to solution to draw the surface.

Solution 1: surface(triple[][] f, real [] x,real [] y) We need to construct, x, y and z. ymesh/15 gives the way with loops. To simplify I work with b=transpose(a) (it avoids one loop).

Please find the code (notice that I use scaling in z so that the surface is not too flat).

import three;
import grid3;
import graph3;
import palette;

// since the value of z=f(x,y) are small
// there is scaling in z
unitsize(.5cm,.5cm,2cm);
currentprojection=orthographic(18,17,3);

bool renderPRC = true; //Pretty essential for me, I would like to obtain graph in PRC format

if(renderPRC) {
// PRC TRUE
settings.prc=true;
settings.embed=true;
}
else {
// RASTERIZE
settings.outformat="png";
settings.prc=false;
settings.render=3;
}

// data1.csv is a refactorized to eliminate the first line containing string
// and also a blank line
file fic =input("data1.csv").line().csv();

// it is 3 columns
real[][] a=fic.dimension(0,3);
// a[i] is 3 array of x,y,z value

int xmesh=15;
int ymesh=a.length#15;
real [][] b=transpose(a);

// solution 1 : construct the vectors real []x, real [] y and the matrix real[][] z
// which correspond to the grid and the values on the grid
real [] xpt,ypt;
ypt=b[0:ymesh];
for(int i=0;i<xmesh;++i)
{
xpt.push(b[i*ymesh]);
}
real[][] zpt=new real[xmesh][ymesh];
for(int i=0;i<xmesh;++i)
{
for (int j=0;j<ymesh;++j)
{
write(i,j);
zpt[i][j]=b[i*ymesh+j];
}
}

surface s=surface(zpt,xpt,ypt,Spline);
s.colors(palette(s.map(zpart),Rainbow()));
draw(s);
xaxis3(Label("$x$",.1),Bounds,InTicks);
yaxis3(Label("$y$",0.5),Bounds,InTicks);
zaxis3(Label("$z$",0.5),Bounds,InTicks(beginlabel=false));


Solution 2: create a function

As in Asymptote: draw surface from data points in 3D, it is possible to use a function f depending on two variables and to draw the desired parametric surface. In my opinnion it is more difficult to understand and it is crucial to give the right parameters. Please find the code

//Necessary module imports
import three;
import grid3;
import graph3;
import palette;

// since the value of z=f(x,y) are small
// there is scaling in z
unitsize(.5cm,.5cm,2cm);
currentprojection=orthographic(18,17,3);

bool renderPRC = true; //Pretty essential for me, I would like to obtain graph in PRC format

if(renderPRC) {
// PRC TRUE
settings.prc=true;
settings.embed=true;
}
else {
// RASTERIZE
settings.outformat="png";
settings.prc=false;
settings.render=3;
}

// data1.csv is a refactorized to eliminate the first line containing string
// and also a blank line
file fic =input("data1.csv").line().csv();

// it is 3 columns
real[][] a=fic.dimension(0,3);
// a[i] is 3 array of x,y,z value

int xmesh=15;
int ymesh=a.length#15;
real [][] b=transpose(a);

// Solution 2 : create a function.
// Be careful the arguments for the surface creation are strongly related
// to the shape of the data
triple f (pair t) {
return (b[round(t.x)*ymesh],b[round(t.y)],b[round(t.x)*ymesh+round(t.y)]);
}

surface ns=surface(f,(0,0),(xmesh-1,ymesh-1),xmesh-1,ymesh-1,Spline);
ns.colors(palette(ns.map(zpart),Rainbow()));
draw(ns);

xaxis3(Label("$x$",.1),Bounds,InTicks);
yaxis3(Label("$y$",0.5),Bounds,InTicks);
zaxis3(Label("$z$",0.5),Bounds,InTicks(beginlabel=false));


and the same result • Thank you for your help sir, both solutions are working perfectly. According to your suggestion I will stick with solution 1 further on. You have put notes, explanations (for me as complete novice more as hints) in the code and in text at the beginning. I would have a few more questions for clarification, but first, is there any reference, that I could study in order to be able to "ask the right way" or find out about how is solution 1 created ? – Tomáš Kruliš Nov 6 at 21:22
• @TomášKruliš no problem if you have more questions. How is solution 1 is created ? First understand surface(real [][] f, real [] x, real [] y) : it describes the surface (x[i],y[j],f[i][j]), a discrete surface on a rectangular box. Then observe your data.csv to be able to construct x,y,f. Here mesh=15 is the key. x has size 15 while y has size length of the file/15 ( I hope that data are well x,y,z). Then you are able to make a link between x[i], y[j] and f[i][j]... – O.G. Nov 6 at 21:55
• Ok, understood! So the ´#´ symbol in line ´int ymesh=a.length#15;´ acts as a division operator in mesh size determination? I am also trying to work out this line: ´xpt.push(b[iymesh]);´ sadly I dont much understand stack-programming ... I generally know how LIFO works, but I did not find anywhere corresponding "pop" (or are the data taken out of the stack during ´write(i,j); zpt[i][j]=b[iymesh+j];´ ? I also dont know what ´Spline´ means. Still applies, if the explanation is not simple (or is lengthy) I will be perfectly happy if you redirect me to any reference I should read. – Tomáš Kruliš Nov 7 at 20:24
• # : yes integer division since '/' gives a float (avoid a (int) cast).xpt.push : see Documentation of Asymptote, it adds the element to the array. In fact it is simpler to replace by real [] xpt=new real[ymesh] for the xpt variable so that the size is fixed and in the loop xpt[i]=b[i*ymesh]. I will modify later the answer. Spline : search Bézier spline surface. Allows smooth surface (if possible) instead of planar patch like triangular mesh or quads mesh. As in dimension 1, allows smooth curve. – O.G. Nov 8 at 7:50
• Just a minor note: commands import three and import graph3 are optional, since the module grid3.asy automatically loads graph3.asy, which loads the module three.asy. – g.kov Nov 11 at 0:04