# Cropping 3D Graphs in Asymptote

I would like to crop the 3D graph below at the plane z=1. I have tried using limits((-1,-1,0),(1,1,1),Crop);, but this just removed my frame.

\documentclass{article}
\usepackage{asymptote}

\begin{document}

\begin{asy}[width=0.5\linewidth]
import graph3;
size(200,200,IgnoreAspect);

currentprojection=perspective(4,2,3);

real f(pair z) {return sqrt(4*(z.x)^2+(z.y)^2);}

draw(surface(f,(-1,-1),(1,1),nx=30,Spline),green+opacity(0.4),render(merge=true));

limits((-1,-1,0),(1,1,1),Crop);

xaxis3("$x$",Bounds,InTicks);
yaxis3("$y$",Bounds,InTicks(beginlabel=false));
zaxis3("$z$",Bounds,InTicks);
\end{asy}

\end{document}


Note: I'm sure I could the graph I want by scaling a cone, but I would like to be able to crop the graph of f(x,y) in other circumstances too. I am aware of this question How to cut a surface at the intersection with another surface in Asymptote?, but its answer is insufficient and I would guess that an easier way exists.

• First comment: The limits command is apparently for two-dimensional graphs only. When you use it with triples rather than pairs, Asymptote throws an error and terminates early; this is why the axes get removed. – Charles Staats Jan 7 '14 at 3:35

As it turns out, there is a better way to do this, albeit a fairly memory-intensive one. First, save the following code in a file called crop3D.asy. This code was inspired by the example splitpatch.asy.

import three;

/**********************************************/
/* Code for splitting surfaces: */

struct possibleInt {
int value;
bool holds;
}

int operator cast(possibleInt i) { return i.value; }

surface[] divide(surface s, int region(triple), int numregions,
bool keepregion(int) = null) {

int defaultdepth = 17;

if (keepregion == null) keepregion = new bool(int region) {
return (0 <= region && region < numregions);
};

surface[] toreturn = new surface[numregions];
for (int i = 0; i < numregions; ++i)
toreturn[i] = new surface;

possibleInt region(patch P) {
triple[][] controlpoints = P.P;
possibleInt theRegion;
theRegion.value = region(controlpoints[0][0]);
theRegion.holds = true;
for (triple[] ta : controlpoints) {
for (triple t : ta) {
if (region(t) != theRegion.value) {
theRegion.holds = false;
break;
}
}
if (!theRegion.holds) break;
}
return theRegion;
}

void addPatch(patch P, int region) {
if (keepregion(region)) toreturn[region].push(P);
}

void divide(patch P, int depth) {
if (depth == 0) {
return;
}

possibleInt region = region(P);
if (region.holds) {
return;
}

// Choose the splitting function based on the parity of the recursion depth.
triple[][][] Split(triple[][] P) {
if (depth % 2 == 0) return hsplit(P);
else return vsplit(P);
}

patch[] Split(patch P) {
triple[][][] patches = Split(P.P);
return sequence(new patch(int i) {return patch(patches[i]);}, patches.length);
}

patch[] patches = Split(P);
for (patch PP : patches)
divide(PP, depth-1);
}

for (patch P : s.s)
divide(P, defaultdepth);

}

/**************************************************/
/* Code for cropping surfaces */

// Return 0 iff the point lies in box(a,b).
int region(triple pt, triple a=O, triple b=(1,1,1)) {
real x=pt.x, y=pt.y, z=pt.z;
int toreturn=0;
real xmin=a.x, xmax=b.x, ymin = a.y, ymax=b.y, zmin=a.z, zmax=b.z;
if (xmin > xmax) { xmin = b.x; xmax = a.x; }
if (ymin > ymax) { ymin = b.y; ymax = a.y; }
if (zmin > zmax) { zmin = b.z; zmax = a.z; }
if (x < xmin) --toreturn;
else if (x > xmax) ++toreturn;
toreturn *= 2;
if (y < ymin) --toreturn;
else if (y > ymax) ++toreturn;
toreturn *= 2;
if (z < zmin) --toreturn;
else if (z > zmax) ++toreturn;
}

bool keepregion(int region) { return (region == 0); }

// Crop the surface to box(a,b).
surface crop(surface s, triple a, triple b) {
int region(triple pt) {
return region(pt, a, b);
}
return divide(s, region=region, numregions=1, keepregion=keepregion)[0];
}


Then save the following in, say, foo.asy in the same directory:

settings.outformat="png";
settings.render=16;
import crop3D;
import graph3;

size(390pt/2, IgnoreAspect);    //390pt is the default text width for the article class

currentprojection=perspective(4,2,3);

real f(pair z) {return sqrt(4*(z.x)^2+(z.y)^2);}

surface s = surface(f,(-1,-1),(1,1),nx=30,Spline);

s = crop(s, (-1,-1,-1),(1,1,1));

draw(s, green+opacity(0.4), render(merge=true));

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


Then compile it by typing asy foo at the command line. You should end up with a file foo.png that looks like this:

• You should just add this as an update to your other answer. – Svend Tveskæg Feb 9 '14 at 11:54
• @Svend Tveskæg: I considered this, and decided to make this a different answer because I believe it is, fundamentally, a different answer. But I can see an argument the other way. Is there a meta thread addressing this sort of thing? – Charles Staats Feb 9 '14 at 14:27
• Fair enough. :) I'm not sure if there is a meta thread for this; I haven't searched for one. – Svend Tveskæg Feb 9 '14 at 14:28

So far as I know, the closest thing Asymptote offers as a solution to your general problem is an optional parameter (a function that outputs a bool) that allows you to discard undesired patches:

\documentclass[margin=10pt]{standalone}
\usepackage{asymptote}

\begin{document}

\begin{asy}
import graph3;
size(390pt/2, IgnoreAspect);        //390pt is the default text width for the article class

currentprojection=perspective(4,2,3);

real f(pair z) {return sqrt(4*(z.x)^2+(z.y)^2);}
bool allow(pair z) {return f(z) <= 1;}

surface conegraph = surface(f,(-1,-1),(1,1),nx=100,Spline,allow);
draw(conegraph,green+opacity(0.4),render(merge=true));

xaxis3("$x$",Bounds,InTicks);
yaxis3("$y$",Bounds,InTicks(beginlabel=false));
zaxis3("$z$",Bounds,InTicks);
\end{asy}

\end{document}


Unfortunately, this solution is not very satisfactory, since it tends to produce jagged edges:

Thus, my general recommendation would be to reparametrize; this holds doubly for this particular function, since the Spline option only really works for differentiable functions. (If you look closely, the "point" on the graph is a bit too rounded.) For an example of this, see the question

Drawing a surface over a nonrectangular domain in asymptote