asymptote: three

 
 8.29 'three'
 ============
 
 This module fully extends the notion of guides and paths in 'Asymptote'
 to three dimensions.  It introduces the new types guide3, path3, and
 surface.  Guides in three dimensions are specified with the same syntax
 as in two dimensions except that triples '(x,y,z)' are used in place of
 pairs '(x,y)' for the nodes and direction specifiers.  This
 generalization of John Hobby's spline algorithm is shape-invariant under
 three-dimensional rotation, scaling, and shifting, and reduces in the
 planar case to the two-dimensional algorithm used in 'Asymptote',
 'MetaPost', and 'MetaFont' [cf. J. C. Bowman, Proceedings in Applied
 Mathematics and Mechanics, 7:1, 2010021-2010022 (2007)].
 
    For example, a unit circle in the XY plane may be filled and drawn
 like this:
 import three;
 
 size(100);
 
 path3 g=(1,0,0)..(0,1,0)..(-1,0,0)..(0,-1,0)..cycle;
 draw(g);
 draw(O--Z,red+dashed,Arrow3);
 draw(((-1,-1,0)--(1,-1,0)--(1,1,0)--(-1,1,0)--cycle));
 dot(g,red);
 
                              [unitcircle3]
 and then distorted into a saddle:
 import three;
 
 size(100,0);
 path3 g=(1,0,0)..(0,1,1)..(-1,0,0)..(0,-1,1)..cycle;
 draw(g);
 draw(((-1,-1,0)--(1,-1,0)--(1,1,0)--(-1,1,0)--cycle));
 dot(g,red);
 
                                [saddle]
 
    Module 'three' provides constructors for converting two-dimensional
 paths to three-dimensional ones, and vice-versa:
 path3 path3(path p, triple plane(pair)=XYplane);
 path path(path3 p, pair P(triple)=xypart);
 
    A Bezier surface, the natural two-dimensional generalization of
 Bezier curves, is defined in 'three_surface.asy' as a structure
 containing an array of Bezier patches.  Surfaces may drawn with one of
 the routines
 void draw(picture pic=currentpicture, surface s, int nu=1, int nv=1,
           material surfacepen=currentpen, pen meshpen=nullpen,
           light light=currentlight, light meshlight=nolight, string name="",
           render render=defaultrender);
 void draw(picture pic=currentpicture, surface s, int nu=1, int nv=1,
           material[] surfacepen, pen meshpen,
           light light=currentlight, light meshlight=nolight, string name="",
           render render=defaultrender);
 void draw(picture pic=currentpicture, surface s, int nu=1, int nv=1,
           material[] surfacepen, pen[] meshpen=nullpens,
           light light=currentlight, light meshlight=nolight, string name="",
           render render=defaultrender);
 
    The parameters 'nu' and 'nv' specify the number of subdivisions for
 drawing optional mesh lines for each Bezier patch.  The optional 'name'
 parameter is used as a prefix for naming the surface patches in the PRC
 model tree.  Here material is a structure defined in 'three_light.asy':
 struct material {
   pen[] p; // diffusepen,ambientpen,emissivepen,specularpen
   real opacity;
   real shininess;
 ...
 }
 These material properties are used to implement 'OpenGL'-style lighting,
 based on the Phong-Blinn specular model.  Sample Bezier surfaces are
 contained in the example files 'BezierSurface.asy', 'teapot.asy', and
 'parametricsurface.asy'.  The structure 'render' contains specialized
 rendering options documented at the beginning of module 'three.asy'.
 
    The examples 'elevation.asy' and 'sphericalharmonic.asy' illustrate
 how to draw a surface with patch-dependent colors.  The examples
 'vertexshading.asy' and 'smoothelevation.asy' illustrate
 vertex-dependent colors, which is supported for both 'Asymptote''s
 native 'OpenGL' renderer and two-dimensional projections.  Since the PRC
 output format does not currently support vertex shading of Bezier
 surfaces, PRC patches are shaded with the mean of the four vertex
 colors.
 
    A surface can be constructed from a cyclic 'path3' with the
 constructor
 surface surface(path3 external, triple[] internal=new triple[],
                 pen[] colors=new pen[], bool3 planar=default);
 and then filled:
 draw(surface(unitsquare3,new triple[] {X,Y,Z,O}),red);
 draw(surface(O--X{Y}..Y{-X}--cycle,new triple[] {Z}),red);
 draw(surface(path3(polygon(5))),red,nolight);
 draw(surface(unitcircle3),red,nolight);
 draw(surface(unitcircle3,new pen[] {red,green,blue,black}),nolight);
 The first example draws a Bezier patch and the second example draws a
 Bezier triangle.  The third and fourth examples are planar surfaces.
 The last example constructs a patch with vertex-specific colors.  A
 three-dimensional planar surface in the plane 'plane' can be constructed
 from a two-dimensional cyclic path 'g' with the constructor
 surface surface(path p, triple plane(pair)=XYplane);
 and then filled:
 draw(surface((0,0)--E+2N--2E--E+N..0.2E..cycle),red);
 Planar Bezier surfaces patches are constructed using Orest Shardt's
 'bezulate' routine, which decomposes (possibly nonsimply connected)
 regions bounded (according to the 'zerowinding' fill rule) by simple
 cyclic paths (intersecting only at the endpoints) into subregions
 bounded by cyclic paths of length '4' or less.
 
    A more efficient routine also exists for drawing tessellations
 composed of many 3D triangles, with specified vertices, and optional
 normals or vertex colors:
 void draw(picture pic=currentpicture, triple[] v, int[][] vi,
           triple[] n={}, int[][] ni={}, material m=currentpen, pen[] p={},
           int[][] pi={}, light light=currentlight);
    Here, the triple array 'v' lists the distinct vertices, while the
 array 'vi' lists integer arrays of length 3 containing the indices of
 'v' corresponding to the vertices of each triangle.  Similarly, the
 arguments 'n' and 'ni' contain optional normal data and 'p' and 'pi'
 contain optional pen vertex data.  An example of this tessellation
 facility is given in 'triangles.asy'.
 
    Arbitrary thick three-dimensional curves and line caps (which the
 'OpenGL' standard does not require implementations to provide) are
 constructed with
 tube tube(path3 p, real width, render render=defaultrender);
 this returns a tube structure representing a tube of diameter 'width'
 centered approximately on 'g'.  The tube structure consists of a surface
 's' and the actual tube center, path3 'center'.  Drawing thick lines as
 tubes can be slow to render, especially with the 'Adobe Reader'
 renderer.  The setting 'thick=false' can be used to disable this feature
 and force all lines to be drawn with 'linewidth(0)' (one pixel wide,
 regardless of the resolution).  By default, mesh and contour lines in
 three-dimensions are always drawn thin, unless an explicit line width is
 given in the pen parameter or the setting 'thin' is set to 'false'.  The
 pens 'thin()' and 'thick()' defined in 'plain_pens.asy' can also be used
 to override these defaults for specific draw commands.
 
 There are four choices for viewing 3D 'Asymptote' output:
   1. Use the native 'Asymptote' adaptive 'OpenGL'-based renderer (with
      the command-line option '-V' and the default settings
      'outformat=""' and 'render=-1').  If you encounter warnings from
      your graphics card driver, try specifying '-glOptions=-indirect' on
      the command line.  On 'UNIX' systems with graphics support for
      multisampling, the sample width can be controlled with the setting
      'multisample'.  An initial screen position can be specified with
      the pair setting 'position', where negative values are interpreted
      as relative to the corresponding maximum screen dimension.  The
      default settings
      import settings;
      leftbutton=new string[] {"rotate","zoom","shift","pan"};
      middlebutton=new string[] {"menu"};
      rightbutton=new string[] {"zoom/menu","rotateX","rotateY","rotateZ"};
      wheelup=new string[] {"zoomin"};
      wheeldown=new string[] {"zoomout"};
      bind the mouse buttons as follows:
         * Left: rotate
         * Shift Left: zoom
         * Ctrl Left: shift viewport
         * Alt Left: pan
         * Middle: menu (must be unmodified; ignores Shift, Ctrl, and
           Alt)
         * Wheel Up: zoom in
         * Wheel Down: zoom out
         * Right: zoom/menu (must be unmodified)
         * Right double click: menu
         * Shift Right: rotate about the X axis
         * Ctrl Right: rotate about the Y axis
         * Alt Right: rotate about the Z axis
 
      The keyboard shortcuts are:
         * h: home
         * f: toggle fitscreen
         * x: spin about the X axis
         * y: spin about the Y axis
         * z: spin about the Z axis
         * s: stop spinning
         * m: rendering mode (solid/mesh/patch)
         * e: export
         * c: show camera parameters
         * p: play animation
         * r: reverse animation
         * : step animation
         * +: expand
         * =: expand
         * >: expand
         * -: shrink
         * _: shrink
         * <: shrink
         * q: exit
         * Ctrl-q: exit
 
   2. Render the scene to a specified rasterized format 'outformat' at
      the resolution of 'n' pixels per 'bp', as specified by the setting
      'render=n'.  A negative value of 'n' is interpreted as '|2n|' for
      EPS and PDF formats and '|n|' for other formats.  The default value
      of 'render' is -1.  By default, the scene is internally rendered at
      twice the specified resolution; this can be disabled by setting
      'antialias=1'.  High resolution rendering is done by tiling the
      image.  If your graphics card allows it, the rendering can be made
      more efficient by increasing the maximum tile size 'maxtile' to
      your screen dimensions (indicated by 'maxtile=(0,0)'.  If your
      video card generates unwanted black stripes in the output, try
      setting the horizontal and vertical components of 'maxtiles' to
      something less than your screen dimensions.  The tile size is also
      limited by the setting 'maxviewport', which restricts the maximum
      width and height of the viewport.  On 'UNIX' systems some graphics
      drivers support batch mode ('-noV') rendering in an iconified
      window; this can be enabled with the setting 'iconify=true'.  Some
      (broken) 'UNIX' graphics drivers may require the command line
      setting '-glOptions=-indirect', which requests (slower) indirect
      rendering.
 
   3. Embed the 3D PRC format in a PDF file and view the resulting PDF
      file with version '9.0' or later of 'Adobe Reader'.  In addition to
      the default 'settings.prc=true', this requires
      'settings.outformat="pdf"', which can be specified by the command
      line option '-f pdf', put in the 'Asymptote' configuration file
      (Seeconfiguration file), or specified in the script before
      'three.asy' (or 'graph3.asy') is imported.  The 'media9' LaTeX
      package is also required (Seeembed).  The example 'pdb.asy'
      illustrates how one can generate a list of predefined views (see
      '100d.views').  A stationary preview image with a resolution of 'n'
      pixels per 'bp' can be embedded with the setting 'render=n'; this
      allows the file to be viewed with other 'PDF' viewers.
      Alternatively, the file 'externalprc.tex' illustrates how the
      resulting PRC and rendered image files can be extracted and
      usage:: for an easier way to embed three-dimensional 'Asymptote'
      pictures within 'LaTeX'.  For specialized applications where only
      the raw PRC file is required, specify 'settings.outformat="prc"'.
      The open-source PRC specification is available from
      <http://livedocs.adobe.com/acrobat_sdk/9/Acrobat9_HTMLHelp/API_References/PRCReference/PRC_Format_Specification/>.
 
   4. Project the scene to a two-dimensional vector (EPS or PDF) format
      with 'render=0'.  Only limited hidden surface removal facilities
      are currently available with this approach (SeePostScript3D).
 
    Automatic picture sizing in three dimensions is accomplished with
 double deferred drawing.  The maximal desired dimensions of the scene in
 each of the three dimensions can optionally be specified with the
 routine
 void size3(picture pic=currentpicture, real x, real y=x, real z=y,
           bool keepAspect=pic.keepAspect);
 The resulting simplex linear programming problem is then solved to
 produce a 3D version of a frame (actually implemented as a 3D picture).
 The result is then fit with another application of deferred drawing to
 the viewport dimensions corresponding to the usual two-dimensional
 picture 'size' parameters.  The global pair 'viewportmargin' may be used
 to add horizontal and vertical margins to the viewport dimensions.
 Alternatively, a minimum 'viewportsize' may be specified.  A 3D picture
 'pic' can be explicitly fit to a 3D frame by calling
 frame pic.fit3(projection P=currentprojection);
 and then added to picture 'dest' about 'position' with
 void add(picture dest=currentpicture, frame src, triple position=(0,0,0));
 
    For convenience, the 'three' module defines 'O=(0,0,0)', 'X=(1,0,0)',
 'Y=(0,1,0)', and 'Z=(0,0,1)', along with a unitcircle in the XY plane:
 path3 unitcircle3=X..Y..-X..-Y..cycle;
 
    A general (approximate) circle can be drawn perpendicular to the
 direction 'normal' with the routine
 path3 circle(triple c, real r, triple normal=Z);
 
    A circular arc centered at 'c' with radius 'r' from
 'c+r*dir(theta1,phi1)' to 'c+r*dir(theta2,phi2)', drawing
 counterclockwise relative to the normal vector
 'cross(dir(theta1,phi1),dir(theta2,phi2))' if 'theta2 > theta1' or if
 'theta2 == theta1' and 'phi2 >= phi1', can be constructed with
 path3 arc(triple c, real r, real theta1, real phi1, real theta2, real phi2,
           triple normal=O);
    The normal must be explicitly specified if 'c' and the endpoints are
 colinear.  If 'r' < 0, the complementary arc of radius '|r|' is
 constructed.  For convenience, an arc centered at 'c' from triple 'v1'
 to 'v2' (assuming '|v2-c|=|v1-c|') in the direction CCW
 (counter-clockwise) or CW (clockwise) may also be constructed with
 path3 arc(triple c, triple v1, triple v2, triple normal=O,
           bool direction=CCW);
 When high accuracy is needed, the routines 'Circle' and 'Arc' defined in
 'graph3' may be used instead.  See SeeGaussianSurface for an
 example of a three-dimensional circular arc.
 
    The representation 'O--O+u--O+u+v--O+v--cycle' of the plane passing
 through point 'O' with normal 'cross(u,v)' is returned by
 path3 plane(triple u, triple v, triple O=O);
    A three-dimensional box with opposite vertices at triples 'v1' and
 'v2' may be drawn with the function
 path3[] box(triple v1, triple v2);
 For example, a unit box is predefined as
 path3[] unitbox=box(O,(1,1,1));
    'Asymptote' also provides optimized definitions for the
 three-dimensional paths 'unitsquare3' and 'unitcircle3', along with the
 surfaces 'unitdisk', 'unitplane', 'unitcube', 'unitcylinder',
 'unitcone', 'unitsolidcone', 'unitfrustum(real t1, real t2)',
 'unitsphere', and 'unithemisphere'.
 
 These projections to two dimensions are predefined:
 'oblique'
 'oblique(real angle)'
      The point '(x,y,z)' is projected to '(x-0.5z,y-0.5z)'.  If an
      optional real argument is given, the negative z axis is drawn at
      this angle in degrees.  The projection 'obliqueZ' is a synonym for
      'oblique'.
 
 'obliqueX'
 'obliqueX(real angle)'
      The point '(x,y,z)' is projected to '(y-0.5x,z-0.5x)'.  If an
      optional real argument is given, the negative x axis is drawn at
      this angle in degrees.
 
 'obliqueY'
 'obliqueY(real angle)'
      The point '(x,y,z)' is projected to '(x+0.5y,z+0.5y)'.  If an
      optional real argument is given, the positive y axis is drawn at
      this angle in degrees.
 
 'orthographic(triple camera, triple up=Z, triple target=O,
              real zoom=1, pair viewportshift=0, bool showtarget=true,
              bool center=false)'
      This projects from three to two dimensions using the view as seen
      at a point infinitely far away in the direction 'unit(camera)',
      orienting the camera so that, if possible, the vector 'up' points
      upwards.  Parallel lines are projected to parallel lines.  The
      bounding volume is expanded to include 'target' if
      'showtarget=true'.  If 'center=true', the target will be adjusted
      to the center of the bounding volume.
 
 'orthographic(real x, real y, real z, triple up=Z, triple target=O,
              real zoom=1, pair viewportshift=0, bool showtarget=true,
              bool center=false)'
      This is equivalent to
      orthographic((x,y,z),up,target,zoom,viewportshift,showtarget,center)
 
      The routine
      triple camera(real alpha, real beta);
      can be used to compute the camera position with the x axis below
      the horizontal at angle 'alpha', the y axis below the horizontal at
      angle 'beta', and the z axis up.
 
 'perspective(triple camera, triple up=Z, triple target=O,
             real zoom=1, real angle=0, pair viewportshift=0,
             bool showtarget=true, bool autoadjust=true,
             bool center=autoadjust)'
      This projects from three to two dimensions, taking account of
      perspective, as seen from the location 'camera' looking at
      'target', orienting the camera so that, if possible, the vector
      'up' points upwards.  If 'render=0', projection of
      three-dimensional cubic Bezier splines is implemented by
      approximating a two-dimensional nonuniform rational B-spline
      (NURBS) with a two-dimensional Bezier curve containing additional
      nodes and control points.  If 'autoadjust=true', the camera will
      automatically be adjusted to lie outside the bounding volume for
      all possible interactive rotations about 'target'.  If
      'center=true', the target will be adjusted to the center of the
      bounding volume.
 
 'perspective(real x, real y, real z, triple up=Z, triple target=O,
             real zoom=1, real angle=0, pair viewportshift=0,
             bool showtarget=true, bool autoadjust=true,
             bool center=autoadjust)'
      This is equivalent to
      perspective((x,y,z),up,target,zoom,angle,viewportshift,showtarget,
                  autoadjust,center)
 
 The default projection, 'currentprojection', is initially set to
 'perspective(5,4,2)'.
 
    We also define standard orthographic views used in technical drawing:
 projection LeftView=orthographic(-X,showtarget=true);
 projection RightView=orthographic(X,showtarget=true);
 projection FrontView=orthographic(-Y,showtarget=true);
 projection BackView=orthographic(Y,showtarget=true);
 projection BottomView=orthographic(-Z,showtarget=true);
 projection TopView=orthographic(Z,showtarget=true);
 The function
 void addViews(picture dest=currentpicture, picture src,
               projection[][] views=SixViewsUS,
               bool group=true, filltype filltype=NoFill);
 adds to picture 'dest' an array of views of picture 'src' using the
 layout projection[][] 'views'.  The default layout 'SixViewsUS' aligns
 the projection 'FrontView' below 'TopView' and above 'BottomView', to
 the right of 'LeftView' and left of 'RightView' and 'BackView'.  The
 predefined layouts are:
 projection[][] ThreeViewsUS={{TopView},
                              {FrontView,RightView}};
 
 projection[][] SixViewsUS={{null,TopView},
                            {LeftView,FrontView,RightView,BackView},
                            {null,BottomView}};
 
 projection[][] ThreeViewsFR={{RightView,FrontView},
                              {null,TopView}};
 
 projection[][] SixViewsFR={{null,BottomView},
                            {RightView,FrontView,LeftView,BackView},
                            {null,TopView}};
 
 projection[][] ThreeViews={{FrontView,TopView,RightView}};
 
 projection[][] SixViews={{FrontView,TopView,RightView},
                          {BackView,BottomView,LeftView}};
 
 
    A triple or path3 can be projected to a pair or path, with
 'project(triple, projection P=currentprojection)' or 'project(path3,
 projection P=currentprojection)'.
 
    It is occasionally useful to be able to invert a projection, sending
 a pair 'z' onto the plane perpendicular to 'normal' and passing through
 'point':
 triple invert(pair z, triple normal, triple point,
               projection P=currentprojection);
 A pair 'z' on the projection plane can be inverted to a triple with the
 routine
 triple invert(pair z, projection P=currentprojection);
 A pair direction 'dir' on the projection plane can be inverted to a
 triple direction relative to a point 'v' with the routine
 triple invert(pair dir, triple v, projection P=currentprojection).
 
    Three-dimensional objects may be transformed with one of the
 following built-in transform3 types (the identity transformation is
 'identity4'):
 
 'shift(triple v)'
      translates by the triple 'v';
 'xscale3(real x)'
      scales by 'x' in the x direction;
 'yscale3(real y)'
      scales by 'y' in the y direction;
 'zscale3(real z)'
      scales by 'z' in the z direction;
 'scale3(real s)'
      scales by 's' in the x, y, and z directions;
 'scale(real x, real y, real z)'
      scales by 'x' in the x direction, by 'y' in the y direction, and by
      'z' in the z direction;
 'rotate(real angle, triple v)'
      rotates by 'angle' in degrees about an axis 'v' through the origin;
 'rotate(real angle, triple u, triple v)'
      rotates by 'angle' in degrees about the axis 'u--v';
 'reflect(triple u, triple v, triple w)'
      reflects about the plane through 'u', 'v', and 'w'.
 
    When not multiplied on the left by a transform3, three-dimensional
 TeX Labels are drawn as Bezier surfaces directly on the projection
 plane:
 void label(picture pic=currentpicture, Label L, triple position,
            align align=NoAlign, pen p=currentpen,
            light light=nolight, string name="",
            render render=defaultrender, interaction interaction=
            settings.autobillboard ? Billboard : Embedded)
 The optional 'name' parameter is used as a prefix for naming the label
 patches in the PRC model tree.  The default interaction is 'Billboard',
 which means that labels are rotated interactively so that they always
 face the camera.  The interaction 'Embedded' means that the label
 interacts as a normal '3D' surface, as illustrated in the example
 'billboard.asy'.  Alternatively, a label can be transformed from the
 'XY' plane by an explicit transform3 or mapped to a specified
 two-dimensional plane with the predefined transform3 types 'XY', 'YZ',
 'ZX', 'YX', 'ZY', 'ZX'.  There are also modified versions of these
 transforms that take an optional argument 'projection
 P=currentprojection' that rotate and/or flip the label so that it is
 more readable from the initial viewpoint.
 
    A transform3 that projects in the direction 'dir' onto the plane with
 normal 'n' through point 'O' is returned by
 transform3 planeproject(triple n, triple O=O, triple dir=n);
 One can use
 triple normal(path3 p);
 to find the unit normal vector to a planar three-dimensional path 'p'.
 As illustrated in the example 'planeproject.asy', a transform3 that
 projects in the direction 'dir' onto the plane defined by a planar path
 'p' is returned by
 transform3 planeproject(path3 p, triple dir=normal(p));
 
    The functions
 surface extrude(path p, triple axis=Z);
 surface extrude(Label L, triple axis=Z);
 return the surface obtained by extruding path 'p' or Label 'L' along
 'axis'.
 
    Three-dimensional versions of the path functions 'length', 'size',
 'point', 'dir', 'accel', 'radius', 'precontrol', 'postcontrol',
 'arclength', 'arctime', 'reverse', 'subpath', 'intersect',
 'intersections', 'intersectionpoint', 'intersectionpoints', 'min',
 'max', 'cyclic', and 'straight' are also defined.
 
    The routine
 real[] intersect(path3 p, surface s, real fuzz=-1);
 returns a real array of length 3 containing the intersection times, if
 any, of a path 'p' with a surface 's'.  The routine
 real[][] intersections(path3 p, surface s, real fuzz=-1);
 returns all (unless there are infinitely many) intersection times of a
 path 'p' with a surface 's' as a sorted array of real arrays of length
 3, and
 triple[] intersectionpoints(path3 p, surface s, real fuzz=-1);
 returns the corresponding intersection points.  Here, the computations
 are performed to the absolute error specified by 'fuzz', or if 'fuzz <
 0', to machine precision.  The routine
 real orient(triple a, triple b, triple c, triple d);
 is a numerically robust computation of 'dot(cross(a-d,b-d),c-d)', which
 is the determinant
 |a.x a.y a.z 1|
 |b.x b.y b.z 1|
 |c.x c.y c.z 1|
 |d.x d.y d.z 1|
 
    The routine
 real insphere(triple a, triple b, triple c, triple d, triple e);
 returns a positive (negative) value if 'e' lies inside (outside) the
 sphere passing through points 'a,b,c,d' oriented so that
 'dot(cross(a-d,b-d),c-d)' is positive, or zero if all five points are
 cospherical.  The value returned is the determinant
 |a.x a.y a.z a.x^2+a.y^2+a.z^2 1|
 |b.x b.y b.z b.x^2+b.y^2+b.z^2 1|
 |c.x c.y c.z c.x^2+c.y^2+c.z^2 1|
 |d.x d.y d.z d.x^2+d.y^2+d.z^2 1|
 |e.x e.y e.z e.x^2+e.y^2+e.z^2 1|
 
    Here is an example showing all five guide3 connectors:
 import graph3;
 
 size(200);
 
 currentprojection=orthographic(500,-500,500);
 
 triple[] z=new triple[10];
 
 z[0]=(0,100,0); z[1]=(50,0,0); z[2]=(180,0,0);
 
 for(int n=3; n <= 9; ++n)
   z[n]=z[n-3]+(200,0,0);
 
 path3 p=z[0]..z[1]---z[2]::{Y}z[3]
 &z[3]..z[4]--z[5]::{Y}z[6]
 &z[6]::z[7]---z[8]..{Y}z[9];
 
 draw(p,grey+linewidth(4mm),currentlight);
 
 xaxis3(Label(XY()*"$x$",align=-3Y),red,above=true);
 yaxis3(Label(XY()*"$y$",align=-3X),red,above=true);
 
                                 [join3]
 
    Three-dimensional versions of bars or arrows can be drawn with one of
 the specifiers 'None', 'Blank', 'BeginBar3', 'EndBar3' (or equivalently
 'Bar3'), 'Bars3', 'BeginArrow3', 'MidArrow3', 'EndArrow3' (or
 equivalently 'Arrow3'), 'Arrows3', 'BeginArcArrow3', 'EndArcArrow3' (or
 equivalently 'ArcArrow3'), 'MidArcArrow3', and 'ArcArrows3'.
 Three-dimensional bars accept the optional arguments '(real size=0,
 triple dir=O)'.  If 'size=O', the default bar length is used; if
 'dir=O', the bar is drawn perpendicular to the path and the initial
 viewing direction.  The predefined three-dimensional arrowhead styles
 are 'DefaultHead3', 'HookHead3', 'TeXHead3'.  Versions of the
 two-dimensional arrowheads lifted to three-dimensional space and aligned
 according to the initial viewpoint (or an optionally specified 'normal'
 vector) are also defined: 'DefaultHead2(triple normal=O)',
 'HookHead2(triple normal=O)', 'TeXHead2(triple normal=O)'.  These are
 illustrated in the example 'arrows3.asy'.
 
    Module 'three' also defines the three-dimensional margins
 'NoMargin3', 'BeginMargin3', 'EndMargin3', 'Margin3', 'Margins3',
 'BeginPenMargin2', 'EndPenMargin2', 'PenMargin2', 'PenMargins2',
 'BeginPenMargin3', 'EndPenMargin3', 'PenMargin3', 'PenMargins3',
 'BeginDotMargin3', 'EndDotMargin3', 'DotMargin3', 'DotMargins3',
 'Margin3', and 'TrueMargin3'.
 
    The routine
 void pixel(picture pic=currentpicture, triple v, pen p=currentpen,
            real width=1);
 can be used to draw on picture 'pic' a pixel of width 'width' at
 position 'v' using pen 'p'.
 
    Further three-dimensional examples are provided in the files
 'near_earth.asy', 'conicurv.asy', and (in the 'animations' subdirectory)
 'cube.asy'.
 
    Limited support for projected vector graphics (effectively
 three-dimensional nonrendered 'PostScript') is available with the
 setting 'render=0'.  This currently only works for piecewise planar
 surfaces, such as those produced by the parametric 'surface' routines in
 the 'graph3' module.  Surfaces produced by the 'solids' package will
 also be properly rendered if the parameter 'nslices' is sufficiently
 large.
 
    In the module 'bsp', hidden surface removal of planar pictures is
 implemented using a binary space partition and picture clipping.  A
 planar path is first converted to a structure 'face' derived from
 'picture'.  A 'face' may be given to a two-dimensional drawing routine
 in place of any 'picture' argument.  An array of such faces may then be
 drawn, removing hidden surfaces:
 void add(picture pic=currentpicture, face[] faces,
          projection P=currentprojection);
    Labels may be projected to two dimensions, using projection 'P', onto
 the plane passing through point 'O' with normal 'cross(u,v)' by
 multiplying it on the left by the transform
 transform transform(triple u, triple v, triple O=O,
                     projection P=currentprojection);
 
    Here is an example that shows how a binary space partition may be
 used to draw a two-dimensional vector graphics projection of three
 orthogonal intersecting planes:
 size(6cm,0);
 import bsp;
 
 real u=2.5;
 real v=1;
 
 currentprojection=oblique;
 
 path3 y=plane((2u,0,0),(0,2v,0),(-u,-v,0));
 path3 l=rotate(90,Z)*rotate(90,Y)*y;
 path3 g=rotate(90,X)*rotate(90,Y)*y;
 
 face[] faces;
 filldraw(faces.push(y),project(y),yellow);
 filldraw(faces.push(l),project(l),lightgrey);
 filldraw(faces.push(g),project(g),green);
 
 add(faces);
 
 
                                [planes]