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
    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
10

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:

  • 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
4

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.