12

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.

enter image description here

\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.

2
  • 2
    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. Jan 7, 2014 at 3:35
  • 1
    To clarify: there is a 3D liimits command but it does not currently accept a Crop argument like the 2D one. Sep 19, 2021 at 15:57

2 Answers 2

12

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) {
      addPatch(P, region(P.point(1/2,1/2)));
      return;
    }

    possibleInt region = region(P);
    if (region.holds) {
      addPatch(P, region);
      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);

  return toreturn;
}

/**************************************************/
/* 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;
  return 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:

3
  • You should just add this as an update to your other answer. Feb 9, 2014 at 11:54
  • 1
    @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? Feb 9, 2014 at 14:27
  • Fair enough. :) I'm not sure if there is a meta thread for this; I haven't searched for one. Feb 9, 2014 at 14:28
5

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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .