% featpost3Dplus2D.mp
% L. Nobre G., [email protected], http://matagalatlante.org
% C. Barbarosie
% J. Schwaiger
% B. Jackowski
% P. J. Sebasti�o
% P. J�rgensen
% S. Pakin
%
% Copyright (C) 2014

% This set of macros adds a lot of features to
% the MetaPost language and eases the production of
% physics diagrams and animations.

% This is free software; you can redistribute it and/or
% modify it under the terms of the GNU General Public License
% as published by the Free Software Foundation; either version 3
% of the License, or (at your option) any later version.

% This set of macros is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.

   message "FeatPost-0.8.8";

   warningcheck := 0;
   background := 0.987white;
   defaultscale := 0.75;
   defaultfont := "cmss17";   % This is used by cartaxes

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Global Variables %%%%%%%%%%%

   boolean      ParallelProj, SphericalDistortion, FCD[], ShadowOn;
   boolean      OverRidePolyhedricColor, MalcomX;
   numeric      Nobjects, RefDist[], HoriZon, RopeColorSeq[], PhotoMarks;
   numeric      Spread, PrintStep, PageHeight, PageWidth, ActuC, Shifts;
   numeric      NL, npl[], NF, npf[], FC[], MaxFearLimit, TableColors;
   numeric      TDAtiplen, TDAhalftipbase, TDAhalfthick, RopeColors, NCL;
   pair         OriginProjPagePos, ShiftV, PhotoPair[];
   path         VGAborder, CLPath[];
   color        f, viewcentr, V[], L[]p[], F[]p[], TableC[];
   color        HigColor, SubColor, LightSource, PhotoPoint[];
   string       ostr[];
   pen          BackPen, ForePen;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Default Values %%%%%%%%%%%%%%%

           f := (3,5,4);   % This f is the point of view in 3D

           viewcentr := black; % This is the aim of the view

           Spread := 140;   % Magnification

           Shifts := 105.00mm;

           ShiftV := Shifts*(1,1); % Central coordinates on paper

           OriginProjPagePos := (Shifts,148.45mm); % This should be the
                                                   % page center.

           ParallelProj := false; % Kind of perspective %
                                                        % Can't have both true
           SphericalDistortion := false; % Kind of lens %

           VGAborder := (182.05,210.00)--       % This definition assumes
                        (412.05,210.00)--       % ShiftV = 105.00mm(1,1)
                        (412.05,382.05)--       % Use: gs -r200 and you
                        (182.05,382.05)--cycle; % get few extra pixels

           PrintStep := 5;   % Coarseness, in resolvec

           PageHeight := 9in;
           PageWidth := 6in;   % And this is used by produce_auto_scale

           MaxFearLimit := 17;       % Valid Maximum Distance from Origin

           HigColor := 0.85white;          % These two colors are used in
           SubColor := 0.35white;          %        fillfacewithlight
           LightSource := 10*(4,-3,4);     % This also
           OverRidePolyhedricColor:=false; % And also this
           ShadowOn := false; % Some objects may block the light and
           HoriZon := 0;      % cast a shadow on a horizontal plane at this Z

           TableC0 := 0.85white;           % grey        %% G N U P L O T
           TableC1 := red;                 % red         %%
           TableC2 := ( 0.2, 0.2, 1.0 );   % blue        %%    colors
           TableC3 := ( 1.0, 0.7, 0.0 );   % orange      %%
           TableC4 := 0.85green;           % pale green  %%
           TableC5 := 0.90*(red+blue);     % magenta     %%
           TableC6 := 0.85*(green+blue);   % cyan        %%
           TableC7 := 0.85*(red+green);    % yellow      %%

           TableColors := 7;
           ActuC := 5;

           RopeColorSeq0 := 3;             %
           RopeColorSeq1 := 3;             %
           RopeColorSeq2 := 1;             %
           RopeColorSeq3 := 3;             % ropepattern
           RopeColorSeq4 := 7;             %
           RopeColorSeq5 := 5;             %
                                           %
           RopeColors := 5;                %

           Nobjects := 0;                  % getready and doitnow

           TDAtiplen := 0.05;              % tdarrow and tdcircarrow
           TDAhalftipbase := 0.02;         % Three-Dimensional (Circular)
           TDAhalfthick := 0.01;           % Arrow

           NCL := 0;                       % closedline
           ForePen := pencircle scaled 15pt;
           BackPen := pencircle scaled 9pt;

           MalcomX := false;

           %%% The variables PhotoMarks, PhotoPair[], PhotoPoint[]
           %%% and CLPath[] have NO default values.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Part I:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Very basic:

% Colors have three or four coordinates. Get one.

   def X(expr A) =
     if color A: redpart A elseif MalcomX: blackpart A else: cyanpart A fi
   enddef;

   def Y(expr A) =
     if color A: greenpart A else: magentapart A fi
   enddef;

   def Z(expr A) =
     if color A: bluepart A else: yellowpart A fi
   enddef;

   def W(expr A) =
     blackpart A
   enddef;

% The length of a vector.

   def conorm(expr A) =
     ( X(A) ++ Y(A) ++ Z(A) )
   enddef;

   def cmyknorm(expr A) = %% This is not good when MalcomX is true
     ( X(A) ++ Y(A) ++ Z(A) ++ W(A) )
   enddef;

   def makecmyk( expr A, B ) =
     ( ( X(A), Y(A), Z(A), B ) )
   enddef;

   def maketrio( expr A  ) =
     ( ( X(A), Y(A), Z(A) ) )
   enddef;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Vector Calculus:

% Calculate the unit vector of a vector (or a point)

   def N(expr A) =
       begingroup
           save M, exitcolor;
           numeric M;
           color exitcolor;
           M = conorm( A );
           if M > 0:
               exitcolor = ( X(A)/M, Y(A)/M, Z(A)/M );
           else:
               exitcolor := black;
           fi;
           ( exitcolor )
       endgroup
   enddef;

   def cdotprod(expr A, B) =
       ( X(A)*X(B) + Y(A)*Y(B) + Z(A)*Z(B) )
   enddef;

   def ccrossprod(expr A, B) =
       ( Y(A)*Z(B) - Z(A)*Y(B),
         Z(A)*X(B) - X(A)*Z(B),
         X(A)*Y(B) - Y(A)*X(B) )
   enddef;

% The dotproduct of two normalized vectors is the cosine of the angle
% they form.

   def ndotprod(expr A, B) =
       begingroup
           save a, b;
           color a, b;
           a = N(A);
           b = N(B);
           ( ( X(a)*X(b) + Y(a)*Y(b) + Z(a)*Z(b) ) )
       endgroup
   enddef;

% The normalized crossproduct of two vectors.

   def ncrossprod(expr A, B) =
       N( ccrossprod( A, B ) )
   enddef;

% Haahaa! Trigonometry.

   def getangle(expr A, B) =
       begingroup
           save coss, sine;
           numeric coss, sine;
           coss := cdotprod( A, B );
           sine := conorm( ccrossprod( A, B ) );
           ( angle( coss, sine ) )
       endgroup
   enddef;

% Something I need for spatialhalfsfear.

   def getcossine( expr Center, Radius ) =
     begingroup
       save a, b;
       numeric a, b;
       a = conorm( f - Center );
       b = Radius/a;
       if abs(b) >= 1:
         show "The point of view f is too close (getcossine).";
         b := 2;                                                % DANGER!
       fi;
       ( b )
     endgroup
   enddef;

% The following routine is used by circularsheet and may be used to
% rotate vectors elliptically. Also used by tdcircarrow.

   vardef planarrotation( expr VecX, VecY, TheAngle ) =
     ( VecX*cosd( TheAngle ) + VecY*sind( TheAngle ) )
   enddef;

% The following routine could be used by kindofcube and may be used to
% rotate polyhedra (must cycle through all Vs before calling makeface).

   def eulerrotation( expr AngA, AngB, AngC, Vec ) =
     begingroup
       save auxx, auxy, veca, vecb, vecc;
       color auxx, auxy, veca, vecb, vecc;
       veca = ( cosd(AngA)*cosd(AngB),
         sind(AngA)*cosd(AngB),
         sind(AngB) );
       auxx = ( cosd(AngA+90), sind(AngA+90), 0 );
       auxy = ccrossprod( veca, auxx );
       vecb = cosd(AngC)*auxx + sind(AngC)*auxy;
       vecc = cosd(AngC+90)*auxx + sind(AngC+90)*auxy;
       ( X(Vec)*veca + Y(Vec)*vecb + Z(Vec)*vecc )
     endgroup
   enddef;

% Rotate a vector around another. Supposes all vectors share the same origin.

   def rotvecaroundanother( expr Angle, RotVec, FixVec ) =
     begingroup
       save uf, cf, xr, yr;
       color uf, cf, xr, yr;
       uf = N( FixVec );
       yr = ccrossprod( uf, RotVec );
       cf = uf*cdotprod( uf, RotVec );
       xr = RotVec - cf;
       ( cf + planarrotation( xr, yr, Angle ) )
     endgroup
   enddef;

% inplanarvolume is used by kindofcube.

   def inplanarvolume( expr PointPerpA, PointPerpB, Point ) =
     begingroup
       save va, vb, vc;
       color va, vb, vc;
       va = Point - PointPerpA;
       vb = Point - PointPerpB;
       vc = PointPerpB - PointPerpA;
       ( cdotprod(va,vc)*cdotprod(vb,vc) <= 0 )
     endgroup
   enddef;

% Maybe you would like to calculate the angular arguments of kindofcube...

   def getanglepair( expr InVec ) =
     begingroup
       save alphaone, alphatwo;
       numeric alphaone, alphatwo;
       alphaone = angle( ( X(InVec), Y(InVec) ) );
       alphatwo = angle( ( X(InVec) ++ Y(InVec), Z(InVec) ) );
       ( (alphaone,alphatwo) )
     endgroup
   enddef;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Auxiliary:

% Projection Size. Meant for objects with size one.
% Used by signalvertex.

   def ps(expr A, Thicken_Factor) =
       Thicken_Factor/conorm(A-f)/3
   enddef;

% Rigorous Projection of a Point. Draws a circle with
% a diameter inversely proportional to the distance of
% that Point from the point of view.

   def signalvertex(expr A, TF, Col) =
     draw rp(A) withcolor Col withpen pencircle scaled (Spread*ps(A,TF))
   enddef;

   def signalshadowvertex(expr A, TF, Col) =
     begingroup
       save auxc, auxn;
       color auxc;
       numeric auxn;
       auxc := cb(A);
       auxn := TF*conorm(f-auxc)/conorm(LightSource-A);
       signalvertex( auxc, auxn, Col )
     endgroup
   enddef;

% Get the vector that projects onto the resolution

   def resolvec(expr A, B) =
       begingroup
           save sizel, returnvec;
           numeric sizel;
           color returnvec;
           sizel = abs( rp(A) - rp(B) );
           if sizel > 0:
               returnvec = PrintStep*(B-A)/sizel;
           else:
               returnvec = 0.3*(B-A);
           fi;
           ( returnvec )
       endgroup
   enddef;

% Movies need a constant frame

   def produce_vga_border =
     begingroup
       draw VGAborder withcolor background withpen pencircle scaled 0;
       clip currentpicture to VGAborder
     endgroup
   enddef;

% The following macro fits a figure in the page.
% Probably it is obsolete since MetaPost 1.000
% Should be the last command in a figure.

   def produce_auto_scale =
       begingroup
           picture storeall, scaleall;
           numeric pwidth, pheight;
           storeall = currentpicture shifted -(center currentpicture);
           currentpicture := nullpicture;
           pwidth = xpart ((lrcorner storeall)-(llcorner storeall));
           pheight = ypart ((urcorner storeall)-(lrcorner storeall));
           if PageHeight/PageWidth < pheight/pwidth:
               scaleall = storeall scaled (PageHeight/pheight);
           else:
               scaleall = storeall scaled (PageWidth/pwidth);
           fi;
           draw scaleall shifted OriginProjPagePos
       endgroup
   enddef;

% The following two procedures are useful for getready.

   vardef cstr( expr Cl ) =
     "(" &
       decimal(X(Cl)) &
       "," &
       decimal(Y(Cl)) &
       "," &
       decimal(Z(Cl)) &
       ")"
   enddef;

   vardef bstr( expr bv ) =
     save bstring; string bstring;
     if bv: bstring = "true"; else: bstring = "false"; fi;
     bstring
   enddef;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Fundamental:

% Rigorous Projection. This the kernel of all these lines of code.
% It won't work if R belongs the plane that contains f and that is
% ortogonal to vector f-viewcentr, unless SphericalDistortion is true.
% f-viewcentr must not be (anti-)parallel to zz.

   def rp(expr R) =
       begingroup

           save projpoi;
           save v, u;
           save verti, horiz, eta, squarf, radio, ang, lenpl;
           pair projpoi;
           color v, u;
           numeric verti, horiz, eta, squarf, radio, ang, lenpl;

           v = N( (-Y(f-viewcentr), X(f-viewcentr), 0) );
           u = ncrossprod( f-viewcentr, v );

           horiz = cdotprod( R-viewcentr, v );
           verti = cdotprod( R-viewcentr, u );

           if SphericalDistortion:
               if ( horiz <> 0 ) or ( verti <> 0 ):
                   lenpl = ( horiz ++ verti )*20; %%%%%%%%%%%%%%% DANGER
                   ang = getangle( f-R, f-viewcentr );
                   horiz := ang*horiz/lenpl;
                   verti := ang*verti/lenpl;
                   projpoi = (horiz,verti);
               else:
                   projpoi = origin;
               fi;
           else:
               if ParallelProj:
                   eta = 1;
               else:
                   squarf = cdotprod( f-viewcentr, f-viewcentr );
                   radio = cdotprod( R-viewcentr, f-viewcentr );
                   eta = 1 - radio/squarf;
                   if abs((horiz,verti)) > MaxFearLimit*eta:
                       eta := abs((horiz,verti))/MaxFearLimit;
                   fi;
               fi;
               projpoi = (horiz,verti)/eta;
           fi;

           ( projpoi*Spread + ShiftV )

       endgroup
   enddef;

% Much improved rigorous pseudo-projection algorithm that follows
% an idea from Cristian Barbarosie.
% This makes shadows caused by a light source point.

   def cb(expr R) =
     begingroup
       save    ve, ho;
       numeric ve, ho;
       LightSource-ho*red-ve*green-HoriZon*blue=whatever*(LightSource-R);
       ( ho*red + ve*green + HoriZon*blue )
     endgroup
   enddef;

% And this just projects points rigorously on some generic plane using
% LightSource as the point of convergence (focus).

   def projectpoint(expr ViewCentr, R) =
       begingroup
           save    verti, horiz;
           save    v, u, lray;
           numeric verti, horiz;
           color   v, u, lray;
           lray = LightSource-ViewCentr;
           v = N( (-Y(lray), X(lray), 0) );
           u = ncrossprod( lray, v );
           lray - horiz*v - verti*u = whatever*( LightSource - R );
           ( horiz*v + verti*u + ViewCentr )
       endgroup
   enddef;

% And this is the way to calculate the intersection of some line with some
% plan.

   def lineintersectplan( expr LinePoi, LineDir, PlanPoi, PlanDir ) =
     begingroup
       save incognitus;
       color incognitus;
       cdotprod( incognitus-PlanPoi, PlanDir ) = 0;
       whatever*LineDir + LinePoi = incognitus;
       ( incognitus )
     endgroup
   enddef;

% Vanishing point.

   def vp( expr D ) =
     begingroup
       ( rp( lineintersectplan( f, D, viewcentr, f-viewcentr) ) )
     endgroup
   enddef;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Basic Functions:

% Get the 2D path of a straight line in beetween 3D points A and B.
% This would add rigor to rigorousdisc, if one would introduce the
% the concept of three-dimensional path. That is not possible now.
% Also this is only interesting when using SphericalDistortion:=true

   def pathofstraightline( expr A, B ) =
       begingroup
           save  k, i, mark, stepVec, returnp, pos;
           numeric k, i;
           color mark, stepVec;
           path returnp;
           pair pos[];
           stepVec = resolvec(A,B);
           pos0 = rp( A );
           k = 1;
           forever:
               mark := A+(k*stepVec);
               exitif cdotprod(B-mark,stepVec) <= 0;
               pos[k] = rp( mark );
               k := incr(k);
           endfor;
           pos[k] = rp(B);
           returnp = pos0 for i=1 upto k: ..pos[i] endfor;
           ( returnp )
       endgroup
   enddef;

   def drawsegment( expr A, B ) =
       begingroup
           if SphericalDistortion:
             draw pathofstraightline( A, B );
           else:
             draw rp(A)--rp(B);
           fi
       endgroup
   enddef;

% Cartesian axes with prescribed lengths.

   def cartaxes(expr axex, axey, axez) =
       begingroup
           save orig, axxc, ayyc, azzc;
           color orig, axxc, ayyc, azzc;
           orig = (0,0,0);
           axxc = (axex,0,0);
           ayyc = (0,axey,0);
           azzc = (0,0,axez);
           drawarrow rp(orig)..rp(axxc);
           drawarrow rp(orig)..rp(ayyc);
           drawarrow rp(orig)..rp(azzc);
           label.bot( "x" ,rp(axxc));       %%%%%%%%%%%%%%%%%%%%%%%%%
           label.bot( "y" ,rp(ayyc));       %%   Some  Labels...   %%
           label.lft( "z" ,rp(azzc));       %%%%%%%%%%%%%%%%%%%%%%%%%
       endgroup
   enddef;

% Orthogonal axes with prescribed lengths and labels.

   def orthaxes(expr axex, strx, axey, stry, axez, strz ) =
       begingroup
           save axxc, ayyc, azzc;
           color axxc, ayyc, azzc;
           axxc = (axex,0,0);
           ayyc = (0,axey,0);
           azzc = (0,0,axez);
           drawarrow rp(black)..rp(axxc);
           drawarrow rp(black)..rp(ayyc);
           drawarrow rp(black)..rp(azzc);
           label.bot( strx ,rp(axxc));
           label.bot( stry ,rp(ayyc));
           label.lft( strz ,rp(azzc));
       endgroup
   enddef;

% This is it. Draw an arch beetween two straight lines with a
% common point (Or) in three-dimensional-euclidian-space and
% place a label near the middle of the arch. Points A and B
% define the lines. The arch is at a distance W from Or. The
% label is S and the position is RelPos (rt,urt,top,ulft,lft,
% llft,bot,lrt). But arches must be smaller than 180 degrees.

   def angline(expr A, B, Or, W, S)(suffix RelPos) =
       begingroup
           save G, Dna, Dnb, al;
           numeric G;
           color Dna, Dnb;
           path al;
           G = conorm( W*( N(A-Or) - N(B-Or) ) )/2.5; %%%%%%% BIG DANGER!
           Dna = ncrossprod(ncrossprod(A-Or,B-Or),A-Or);
           Dnb = ncrossprod(ncrossprod(B-Or,A-Or),B-Or);
           al = rp(W*N(A-Or)+Or)..
                                 controls rp(W*N(A-Or)+Or+G*Dna)
                                      and rp(W*N(B-Or)+Or+G*Dnb)..
                 rp(W*N(B-Or)+Or);
           draw al;
           label.RelPos( S, point 0.5*length al of al )
       endgroup
   enddef;

% As i don't know how to declare variables of type suffix,
% i provide a way to avoid the problem. This time RelPos may
% be 0,1,2,3,4,6,7 or anything else.

   def anglinen(expr A, B, Or, W, S, RelPos) =
       begingroup
           save G, Dna, Dnb, al, middlarc;
           numeric G;
           color Dna, Dnb;
           path al;
           pair middlarc;
           G = conorm( W*( N(A-Or) - N(B-Or) ) )/3;
           Dna = ncrossprod(ncrossprod(A-Or,B-Or),A-Or);
           Dnb = ncrossprod(ncrossprod(B-Or,A-Or),B-Or);
           al = rp(W*N(A-Or)+Or)..
                                 controls rp(W*N(A-Or)+Or+G*Dna)
                                      and rp(W*N(B-Or)+Or+G*Dnb)..
                 rp(W*N(B-Or)+Or);
           draw al;
           middlarc = point 0.5*length al of al;
           if RelPos = 0:
               label.rt( S, middlarc );
           elseif RelPos =1:
               label.urt( S, middlarc );
           elseif RelPos =2:
               label.top( S, middlarc );
           elseif RelPos =3:
               label.ulft( S, middlarc );
           elseif RelPos =4:
               label.lft( S, middlarc );
           elseif RelPos =5:
               label.llft( S, middlarc );
           elseif RelPos =6:
               label.bot( S, middlarc );
           elseif RelPos =7:
               label.lrt( S, middlarc );
           else:
               label( S, middlarc );
           fi
       endgroup
   enddef;

% As a bigger avoidance, replace the arch by a paralellogram.

   def squareangline(expr A, B, Or, W) =
     begingroup
       save sal;
       path sal;
       sal = rp(Or)--rp(W*N(A-Or)+Or)--
             rp(W*(N(B-Or)+N(A-Or))+Or)--rp(W*N(B-Or)+Or)--cycle;
       draw sal
     endgroup
   enddef;

% Just as we are here we can draw circles. (color,color,numeric)

   def rigorouscircle( expr CenterPos, AngulMom, Radius ) =
     begingroup
       save ind, G, Dn, Dna, Dnb, al, vec;
       numeric ind, G;
       color vec[], Dn, Dna, Dnb;
       path al;
       vec1 = ncrossprod( CenterPos-f, AngulMom);
       for ind=2 step 2 until 8:
         vec[ind+1] = ncrossprod( vec[ind-1], AngulMom );
         vec[ind] = N( vec[ind-1] + vec[ind+1] );
       endfor;
       G = conorm( Radius*( vec1 - vec2 ) )/3;
       al = rp(Radius*vec1+CenterPos)
       for ind=2 upto 8:
         hide(
           Dn:=ncrossprod(vec[ind-1],vec[ind]);
           Dna:=ncrossprod(Dn,vec[ind-1]);
           Dnb:=ncrossprod(-Dn,vec[ind])
           )
         ..controls rp(Radius*vec[ind-1]+CenterPos+G*Dna)
         and rp(Radius*vec[ind]  +CenterPos+G*Dnb)
         ..rp(Radius*vec[ind]  +CenterPos)
       endfor
       ...cycle;
       ( al )
     endgroup
   enddef;

% 3D straight arrow.

   def tdarrow(expr FromPos, ToTip ) =
     begingroup
       save basevec, longvec, a, b, c, d, e, g, h, len, p;
       color basevec, longvec, a, b, c, d, e, g, h;
       numeric len;
       path p;
       len = conorm( ToTip - FromPos );
       longvec := N( ToTip - FromPos );
       basevec := ncrossprod( FromPos-f, longvec );
       if len <= TDAtiplen:
         b = basevec*TDAhalftipbase*len/TDAtiplen;
         c = FromPos+b;
         e = FromPos-b;
         p = rp(ToTip)--rp(c)--rp(e)--cycle;
       else:
         d = ToTip-longvec*TDAtiplen;
         a = FromPos+basevec*TDAhalfthick;
         h = FromPos-basevec*TDAhalfthick;
         b = d+basevec*TDAhalfthick;
         g = d-basevec*TDAhalfthick;
         c = d+basevec*TDAhalftipbase;
         e = d-basevec*TDAhalftipbase;
         p = rp(a)--rp(b)--rp(c)--rp(ToTip)--rp(e)--rp(g)--rp(h)--cycle;
       fi;
       unfill p;
       draw p
     endgroup
   enddef;

% 3D circular arrow.

   def tdcircarrow(expr CenterPos, AngulMom, Ray, StartAngle, Amplituda ) =
     begingroup
       save veca, vecb, vecc, vecd, a, b, c, d, p, stepa, numa, anga, angb;
       save signus, ca, da, i;
       color veca, vecb, vecc, vecd;
       pair a, b, c, d, ca, da, aa;
       numeric stepa, numa, anga, angb, signus, i;
       path p;
       signus = Amplituda/abs(Amplituda);
       stepa = 6signus; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% DANGER.
       vecd = ncrossprod( CenterPos-f, AngulMom );
       vecc = ncrossprod( vecd, AngulMom );
       anga = signus*180*(TDAhalfthick/TDAhalftipbase)*TDAtiplen/(3.14159*Ray);
       angb = signus*180*TDAtiplen/(3.14159*Ray);
       numa = StartAngle+Amplituda;
       a = rp(Ray*planarrotation(vecc,vecd,StartAngle+anga));
       b = rp(Ray*planarrotation(vecc,vecd,numa+angb));
       c = rp((Ray+TDAhalftipbase)*planarrotation(vecc,vecd,numa));
       d = rp((Ray-TDAhalftipbase)*planarrotation(vecc,vecd,numa));
       ca = rp((Ray+TDAhalfthick)*planarrotation(vecc,vecd,numa));
       da = rp((Ray-TDAhalfthick)*planarrotation(vecc,vecd,numa));
       aa = rp((Ray-TDAhalfthick)*planarrotation(vecc,vecd,StartAngle));
       p = for i=StartAngle step stepa until numa:
             rp((Ray+TDAhalfthick)*planarrotation(vecc,vecd,i))..
           endfor ca--c--b--d--da..
           for i=numa-stepa step -stepa until StartAngle:
             rp((Ray-TDAhalfthick)*planarrotation(vecc,vecd,i))..
           endfor aa--a--cycle;
       unfill p;
       draw p
     endgroup
   enddef;

% Draw lines with a better expression of three-dimensionality.

   def emptyline(expr JoinP,ThickenFactor,OutCol,InCol,theN,EmptyFrac,sN)(text LinFunc) =
     begingroup
       save i, j, k;
       numeric i, j, k;
       k = ThickenFactor*EmptyFrac;
       if ShadowOn:
         for i = 0 upto theN:
           signalshadowvertex( LinFunc(i/theN), ThickenFactor, black );
         endfor;
       fi;
       for j = 0 upto sN-1:
         signalvertex( LinFunc(j/theN), ThickenFactor, OutCol );
       endfor;
       if JoinP:
         for j = -sN upto 0:
           signalvertex( LinFunc(j/theN), k, InCol );
         endfor;
       fi;
       for i = sN upto theN:
         signalvertex( LinFunc( i/theN ), ThickenFactor, OutCol );
         for j = sN downto 0:
           signalvertex( LinFunc( (i-j)/theN ), k, InCol );
         endfor;
       endfor
     endgroup
   enddef;

% Draw space-paths of possibly closed lines making use of "getready"

   def closedline( expr ThisIsClosed, theN, ForeFrac, BackFrac )( text LinFunc ) =
     begingroup
       save i, comm;
       numeric i;
       string comm;
       NCL := incr( NCL );
       if ThisIsClosed:
         CLPath[NCL] :=
           for i=1 upto theN:
             rp(LinFunc(i/theN))..
           endfor
           cycle;
         for i=1 upto theN:
           comm:="draw subpath ("
             & decimal(i-ForeFrac)
             & ","
             & decimal(i+ForeFrac)
             & ") of CLPath"
             & decimal(NCL)
             & " withpen ForePen; undraw subpath ("
             & decimal(i-BackFrac)
             & ","
             & decimal(i+BackFrac)
             & ") of CLPath"
             & decimal(NCL)
             & " withpen BackPen;";
           getready( comm, LinFunc(i/theN) );
         endfor;
       else:
         CLPath[NCL] := rp(LinFunc(0))
           for i=1 upto theN: ..rp(LinFunc(i/theN)) endfor;
         comm:="draw subpath (0,"
           & decimal(ForeFrac)
           & ") of CLPath"
           & decimal(NCL)
           & " withpen ForePen; undraw subpath (0,"
           & decimal(BackFrac)
           & ") of CLPath"
           & decimal(NCL)
           & " withpen BackPen;";
         getready( comm, LinFunc(1/theN) );
         for i=2 upto theN-1:
           comm:="draw subpath ("
             & decimal(i-ForeFrac)
             & ","
             & decimal(i+ForeFrac)
             & ") of CLPath"
             & decimal(NCL)
             & " withpen ForePen; undraw subpath ("
             & decimal(i-BackFrac)
             & ","
             & decimal(i+BackFrac)
             & ") of CLPath"
             & decimal(NCL)
             & " withpen BackPen;";
           getready( comm, LinFunc(i/theN) );
         endfor;
         comm:="draw subpath ("
           & decimal(theN-ForeFrac)
           & ","
           & decimal(theN)
           & ") of CLPath"
           & decimal(NCL)
           & " withpen ForePen; undraw subpath ("
           & decimal(theN-BackFrac)
           & ","
           & decimal(theN)
           & ") of CLPath"
           & decimal(NCL)
           & " withpen BackPen;";
         getready( comm, LinFunc(1) );
       fi
     endgroup
   enddef;

% The next allows you to draw any solid that has no vertices and that has
% two, exactly two, cyclic edges. In fact, it doesn't need to be a solid.
% In order to complete the drawing of this solid you have to choose one of
% the edges to be drawn immediatly afterwards.

   def twocyclestogether( expr CycleA, CycleB ) =
       begingroup
           save TheLengthOfA, TheLengthOfB, TheMargin, Leng, i;
           save SubPathA, SubPathB, PolygonPath, FinalPath;
           numeric TheLengthOfA, TheLengthOfB, TheMargin, Leng, i;
           path SubPathA, SubPathB, PolygonPath, FinalPath;
           TheMargin = 0.02;
           TheLengthOfA = ( length CycleA ) - TheMargin;
           TheLengthOfB = ( length CycleB ) - TheMargin;
           SubPathA = subpath ( 0, TheLengthOfA ) of CycleA;
           SubPathB = subpath ( 0, TheLengthOfB ) of CycleB;
           PolygonPath = makepath makepen ( SubPathA--SubPathB--cycle );
           Leng = (length PolygonPath) - 1;
           FinalPath = point 0 of PolygonPath
                       for i = 1 upto Leng:
                           --point i of PolygonPath
                       endfor
                       --cycle;
           ( FinalPath )
       endgroup
   enddef;

% Ellipse on the air.

   def ellipticpath(expr CenterPos, OneAxe, OtherAxe ) =
     begingroup
       save ind;
       numeric ind;
       ( for ind=0 upto 35:
           rp( CenterPos+planarrotation(OneAxe,OtherAxe,ind*10) )...
         endfor cycle )
     endgroup
   enddef;

% Shadow of an ellipse on the air.

   def ellipticshadowpath(expr CenterPos, OneAxe, OtherAxe ) =
     begingroup
       save ind;
       numeric ind;
       ( for ind=1 upto 36:
           rp( cb( CenterPos+planarrotation(OneAxe,OtherAxe,ind*10) ) )...
         endfor cycle )
     endgroup
   enddef;

% It should be possible to attach some text to some plan.
% Unfortunately, this only works correctly when ParallelProj := true;

   def labelinspace(expr KeepRatio,RefPoi,BaseVec,UpVec)(text SomeString) =
     begingroup
       save labelpic, plak, lrc, ulc, llc, centerc, aratio, newbase;
       picture labelpic;
       pair lrc, ulc, llc;
       transform plak;
       color centerc, newbase;
       numeric aratio;
       labelpic = thelabel( SomeString, origin );
       lrc = lrcorner labelpic;
       ulc = ulcorner labelpic;
       llc = llcorner labelpic;
       aratio = (xpart  lrc - xpart llc)/(ypart ulc - ypart llc);
       if KeepRatio:
         newbase = conorm(UpVec)*aratio*N(BaseVec);
       else:
         newbase = BaseVec;
       fi;
       rp(RefPoi+newbase) = lrc transformed plak;
       rp(RefPoi+UpVec) = ulc transformed plak;
       centerc = RefPoi+0.5(newbase+UpVec);
       rp(RefPoi) = llc transformed plak;
       label( labelpic transformed plak, rp(centerc) )
     endgroup
   enddef;

% It should be possible to attach some path to some surface.

   def closedpathinspace( expr SomeTDPath, NDivide )( text ConverterFunc ) =
     begingroup
       save i, outpath, st;
       numeric i, st;
       path outpath;
       st = 1/NDivide;
       outpath = for i=st step st until (length SomeTDPath):
         ConverterFunc( point i of SomeTDPath ) --
       endfor cycle;
       ( outpath )
     endgroup
   enddef;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Standard Objects:

% And more precisely. The next routines spatialhalfcircle and
% rigorousfear require circles drawn in a systematic and precise way.

   def goodcirclepath(expr CenterPos, AngulMom, Radius ) =
       begingroup
           save cirath, vecx, vecy, ind, goodangulmom, decision, view;
           numeric ind, decision;
           color vecx, vecy, goodangulmom, view;
           path cirath;
           view = f-CenterPos;
           decision = cdotprod( view, AngulMom );
           if decision < 0:
               goodangulmom = -AngulMom;
           else:
               goodangulmom = AngulMom;
           fi;
           vecx = Radius*ncrossprod( view, goodangulmom );
           decision := getangle( view, goodangulmom );
           if decision > 0.5:                  %%%%%%%%%%%%%%% DANGER %%%
               vecy = Radius*ncrossprod( goodangulmom, vecx );
               cirath = for ind=10 step 10 until 360:
                            rp( CenterPos + planarrotation(vecx,vecy,ind) )...
                        endfor cycle;
           else:
             cirath = head_on_circle( CenterPos, Radius );
           fi;
           ( cirath )
       endgroup
   enddef;

% And its shadow.

   def circleshadowpath(expr CenterPos, AngulMom, Radius ) =
     begingroup
       save cirath, vecx, vecy, view, decision;
       numeric decision;
       color vecx, vecy, view;
       path cirath;
       view = LightSource-CenterPos;
       vecx = ncrossprod( view, AngulMom );
       decision := getangle( view, AngulMom );
       if decision > 0.5:                  %%%%%%%%%%%%%%% DANGER %%%
         vecy = ncrossprod( AngulMom, vecx );
         cirath = ellipticshadowpath(CenterPos,vecx*Radius,vecy*Radius);
       else:
         vecx := N( (-Y(view), X(view), 0) );
         vecy = ncrossprod( view, vecx );
         cirath = ellipticshadowpath(CenterPos,vecx*Radius,vecy*Radius);
       fi;
       ( cirath )
     endgroup
   enddef;

% When there are numerical problems with the previous routine
% use the following alternative:

   def head_on_circle(expr Pos, Radius ) =
       begingroup
           save vecx, vecy, ind, view;
           numeric ind;
           color vecx, vecy, view;
           view = f-Pos;
           vecx = Radius*N( (-Y(view), X(view), 0) );
           vecy = Radius*ncrossprod( view, vecx );
           ( for ind=10 step 10 until 360:
               rp( Pos + planarrotation(vecx,vecy,ind) )...
           endfor cycle )
       endgroup
   enddef;

% The nearest or the furthest part of a circle returned as a path.
% This function has been set to work for rigorousdisc (next).
% Very tough settings they were.

   def spatialhalfcircle(expr Center, AngulMom, Radius, ItsTheNearest ) =
     begingroup
       save va, vb, vc, cc, vd, ux, uy, pa, pb;
       save nr, cn, valx, valy, valr, choiceang;
       save auxil, auxih, fcirc, returnp;
       save choice;
       color va, vb, vc, cc, vd, ux, uy, pa, pb;
       numeric nr, cn, valx, valy, valr, choiceang;
       path auxil, auxih, fcirc, returnp;
       boolean choice;
       va := Center - f;
       vb := N( AngulMom );
       vc := vb*( cdotprod( va, vb ) );
       cc := f + vc;
       vd := cc - Center;  % vd := va + vc;
       nr := conorm( vd );
       if Radius >= nr:
         returnp := rp( cc ); % this single point will show up in spheroid
       else:
         valr := Radius*Radius;
         valx := valr/nr;
         valy := sqrt( valr - valx*valx );
         ux := N( vd );
         choiceang := getangle( vc, va );                   %%%%%%%%%%%%%
         choice := ( choiceang < 89 ) or ( choiceang > 91 );%%  DANGER  %
         if choice:                                         %%%%%%%%%%%%%
           uy := ncrossprod( vc, va );
         else:
           uy := ncrossprod( AngulMom, va );
         fi;
         pa := valx*ux + valy*uy + Center;
         pb := pa - 2*valy*uy;
         if choice:
           auxil := rp(1.1[Center,pb])--rp(0.9[Center,pb]);
           auxih := rp(1.1[Center,pa])--rp(0.9[Center,pa]);
           fcirc := goodcirclepath( Center, AngulMom, Radius );
           if ItsTheNearest:
             returnp := (fcirc cutafter auxih) cutbefore auxil;
           else:
             returnp := (fcirc cutbefore auxih)..(fcirc cutafter auxil);
           fi;
         else:
           if ItsTheNearest:
             if cdotprod( va, AngulMom ) > 0:
               returnp := rp(pb)--rp(pa);
             else:
               returnp := rp(pa)--rp(pb);
             fi;
           else:
             if cdotprod( va, AngulMom ) < 0:
               returnp := rp(pb)--rp(pa);
             else:
               returnp := rp(pa)--rp(pb);
             fi;
           fi;
         fi;
       fi;
       ( returnp )
     endgroup
   enddef;

% Cylinders or tubes ( numeric, boolean, color, numeric, color ).
% Great stuff. The "disc" in the name comes from the fact that
% when SphericalDistortion := true; the sides of cylinders are
% not drawn correctly (they are straight). And when it is a tube
% you should force the background to be white.

   def rigorousdisc(expr InRay, FullFill, BaseCenter, Radius, LenVec) =
     begingroup
       save va, vb, vc, cc, vd, base, holepic;
       save vA, cC, nr, vala, valb, hashole, istube;
       save auxil, auxih, halfl, halfh, thehole;
       save auxili, auxihi, rect, theshadow;

       color va, vb, vc, cc, vd, base;
       picture holepic;
       color vA, cC;
       numeric nr, vala, valb;
       boolean hashole, istube;
       path auxil, auxih, halfl, halfh, thehole;
       path auxili, auxihi, rect, theshadow;

       va := BaseCenter - f;
       vb := N( LenVec );
       vc := vb*( cdotprod( va, vb ) );
       cc := f + vc;
       vd := cc - BaseCenter;
       nr := conorm( vd );
       base := BaseCenter + LenVec;
       vA := base - f;
       vala := conorm( va );
       valb := conorm( vA );
       if ShadowOn:
         auxil := circleshadowpath( BaseCenter, LenVec, Radius );
         auxih := circleshadowpath( base, LenVec, Radius );
         fill twocyclestogether( auxil, auxih );
       fi;
       auxil := goodcirclepath( base, LenVec, Radius );
       auxih := goodcirclepath( BaseCenter, LenVec, Radius );
       istube := false;
       hashole := false;
       if InRay > 0:
         istube := true;
         auxili := goodcirclepath( base, LenVec, InRay );
         auxihi := goodcirclepath( BaseCenter, LenVec, InRay );
         hashole := (-1,-1) <> ( auxili intersectiontimes auxihi );
         if hashole:
           draw auxili;
           draw auxihi;
           holepic := currentpicture;
           clip holepic to auxili;
           clip holepic to auxihi;
         fi;
       fi;
                        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
       if Radius >= nr: % THE CASE Radius > nr > InRay IS NOT SUPPORTED %
         if vala <= valb :
           thehole := auxil;
           auxil := auxih;
           auxih := thehole;
         fi;
         if istube:
           if vala <= valb :
             thehole := auxili;
             auxili := auxihi;
             auxihi := thehole;
           fi;
           holepic := currentpicture;
           clip holepic to auxihi;
         fi;
         unfill auxil;
         draw auxil;
         if istube:
           draw holepic;
           draw auxihi;
         fi;
       else:
         cC := base + vd;
         if ( cdotprod( f - cc, f - cC ) <= 0 ) or ( not FullFill ):
           halfl := spatialhalfcircle(BaseCenter,LenVec,Radius,true);
           halfh := spatialhalfcircle(base,LenVec,Radius,true);
           if FullFill:
             rect := halfl--halfh--cycle;
           else:
             rect := halfl--(reverse halfh)--cycle;
           fi;
           unfill rect;
           draw rect;
         elseif vala > valb:
           halfl := spatialhalfcircle(BaseCenter,LenVec,Radius,true);
           halfh := spatialhalfcircle(base,LenVec,Radius,false);
           rect := halfl--halfh--cycle;
           unfill rect;
           draw rect;
           if istube:
             if hashole:
               draw holepic;
             fi;
             draw auxili;
           fi;
           draw auxil;
         else:
           halfl := spatialhalfcircle(BaseCenter,LenVec,Radius,false);
           halfh := spatialhalfcircle(base,LenVec,Radius,true);
           rect := halfl--halfh--cycle;
           unfill rect;
           draw rect;
           if istube:
             if hashole:
               draw holepic;
             fi;
             draw auxihi;
           fi;
           draw auxih;
         fi;
       fi
     endgroup
   enddef;

% And maybe a full cone border. The vertex may go anywhere.
% Choose the full cone border (UsualForm=true) or just the nearest
% part of the base edge (UsualForm=false).
% This is used by tropicalglobe as a generic spatialhalfcircle to
% draw only the in fact visible part of circular lines. Please, don't
% put the vertex too close to the base plan when UsualForm=false.

   def rigorouscone(expr UsualForm,CenterPos,AngulMom,Radius,VertexPos) =
     begingroup
       save basepath, thesubpath, fullpath, finalpath, auxpath, bigcirc;
       save themargin, newlen, i, auxt, startt, endt, thelengthofc;
       save pa, pb, pc, pd, pe;
       path basepath, thesubpath, fullpath, finalpath, auxpath;
       path bigcirc;
       numeric themargin, newlen, i, auxt, startt, endt, thelengthofc;
       pair pa, pb, pc, pd, pe;
       themargin = 0.02; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% DANGER
       basepath = goodcirclepath( CenterPos, AngulMom, Radius );
       thelengthofc = ( length basepath ) - themargin;
       thesubpath = subpath ( 0, thelengthofc ) of basepath;
       fullpath = makepath makepen ( rp(VertexPos)--thesubpath--cycle );
       pa = 0.995[rp(CenterPos),rp(VertexPos)];
       pb = 1.005[rp(CenterPos),rp(VertexPos)];
       auxpath = pa--pb;
       pc = auxpath intersectiontimes fullpath;
       if pc <> (-1,-1):
         auxt = ypart pc;
         newlen = length fullpath;
         if UsualForm:
           finalpath = point auxt of fullpath
             --point auxt+1 of fullpath
             for i = auxt+2 upto auxt+newlen-1:
               ...point i of fullpath
             endfor
             --cycle;
         else:
           bigcirc = goodcirclepath( CenterPos, AngulMom, 1.005*Radius );
           pd = bigcirc intersectiontimes fullpath;
           pe = ( reverse bigcirc ) intersectiontimes fullpath;
           startt = floor( xpart pd );
           endt = ceiling( ( length bigcirc ) - ( xpart pe ) );
           finalpath = subpath (startt,endt) of basepath;
         fi;
       else:
         finalpath = rp(VertexPos);
       fi;
       ( finalpath )
     endgroup
   enddef;

   def verygoodcone(expr BackDash,CenterPos,AngulMom,Radius,VertexPos) =
     begingroup
       save thepath, lenpath, bonevec, sidevec, viewaxe, cipath;
       save thelengthofc, thesubpath, themargin, basepath;
       color bonevec, sidevec, viewaxe;
       path thepath, cipath, basepath, thesubpath;
       numeric lenpath, thelengthofc, themargin;
       themargin = 0.02;
       bonevec = VertexPos - CenterPos;
       if cdotprod( bonevec, AngulMom ) < 0:
         sidevec = -N(AngulMom);
       else:
         sidevec = N(AngulMom);
       fi;
       viewaxe = f-CenterPos;
       if ShadowOn:
         basepath = circleshadowpath( CenterPos, AngulMom, Radius );
         thelengthofc = ( length basepath ) - themargin;
         thesubpath = subpath ( 0, thelengthofc ) of basepath;
         fill makepath makepen ( rp(cb(VertexPos))--thesubpath--cycle );
       fi;
       thepath = rigorouscone(true,CenterPos,AngulMom,Radius,VertexPos);
       lenpath = length thepath;
       if lenpath<>0:
         unfill thepath;
         draw thepath;
         if cdotprod( sidevec, viewaxe ) < 0:
           draw goodcirclepath( CenterPos, AngulMom, Radius );
         else:
           if BackDash:
             draw
             goodcirclepath( CenterPos, AngulMom, Radius ) dashed evenly;
           fi;
         fi;
       else:
         cipath = goodcirclepath( CenterPos, AngulMom, Radius );
         unfill cipath;
         draw cipath;
         if cdotprod( sidevec, viewaxe ) > 0:
           draw rp( VertexPos );
         fi;
       fi
     endgroup
   enddef;

% Its a sphere, don't fear, but remember that the rigorous projection
% of a sphere is an ellipse.

   def rigorousfearpath(expr Center, Radius ) =
       begingroup
           save auxil, ux, uy, newcen, nr, valx, valy, valr;
           color ux, uy, newcen;
           numeric nr, valx, valy, valr;
           path auxil;
           nr := conorm( Center - f );
           valr := Radius**2;
           valx := valr/nr;
           valy := sqrt( valr - valx**2 );
           newcen := valx*( f - Center )/nr;
           auxil := head_on_circle( Center+newcen, valy );
           ( auxil )
       endgroup
   enddef;

   def rigorousfearshadowpath(expr Center, Radius ) =
       begingroup
           save ux, uy, newcen;
           save nr, valx, valy, valr, lenr;
           save auxil, auxih, fcirc, returnp;
           save dcenter;
           color ux, uy, newcen;
           numeric nr, valx, valy, valr, lenr;
           path auxil, auxih, fcirc, returnp;
           pair dcenter;
           nr := conorm( Center - LightSource );
           valr := Radius**2;
           valx := valr/nr;
           valy := sqrt( valr - valx**2 );
           newcen := valx*( LightSource - Center )/nr;
           auxil := circleshadowpath( Center+newcen, newcen, valy );
           ( auxil )
       endgroup
   enddef;

% It's a globe (without land).

   def tropicalglobe( expr NumLats, TheCenter, Radius, AngulMom ) =
     begingroup
       save viewaxe, sinalfa, sinbeta, globaxe, aux, limicos, lc;
       save stepang, actang, newradius, foc, newcenter, cpath, i;
       save outerpath, conditiona, conditionb;
       color viewaxe, globaxe, foc, newcenter;
       numeric sinalfa, sinbeta, aux, limicos, stepang, actang;
       numeric newradius, lc, i;
       path cpath, outerpath;
       boolean conditiona, conditionb;
       if ShadowOn:
         fill rigorousfearshadowpath( TheCenter, Radius );
       fi;
       viewaxe = f-TheCenter;
       sinalfa = Radius/conorm( viewaxe );
       aux = cdotprod( viewaxe, AngulMom );
       if aux < 0:
         globaxe = -N(AngulMom);
       else:
         globaxe = N(AngulMom);
       fi;
       sinbeta = cdotprod( globaxe, N(viewaxe) );
       aux := sqrt((1-sinalfa**2)*(1-sinbeta**2));
       limicos = aux - sinalfa*sinbeta;
       stepang = 180/NumLats;
       globaxe := globaxe*Radius;
       outerpath = rigorousfearpath(TheCenter,Radius);
       unfill outerpath;
       draw outerpath;
       for actang = 0.5*stepang step stepang until 179:
         if cosd(actang) < limicos-0.005: %%%%%%%%%%%%%%%%%%%%%%%% DANGER
           newradius := Radius*sind(actang);
           newcenter := TheCenter - globaxe*cosd(actang);
           conditiona:=(actang<94) and (actang>86); % DANGER % DANGER VV
           conditionb:=abs(cdotprod(globaxe/Radius,N(f-newcenter)))<0.08;
           if conditiona or conditionb:
             draw spatialhalfcircle(newcenter,globaxe,newradius,true);
           else:
             foc := TheCenter - globaxe/cosd(actang);
             lena := -Radius*cosd(actang);
             lenb := cdotprod(viewaxe,globaxe/Radius);
             if (actang <= 86) or ((lenb<lena) and (actang>=94)):
               cpath :=
               rigorouscone(false,newcenter,globaxe,newradius,foc);
               draw cpath;
             else:
               cpath :=
               rigorouscone(true,newcenter,globaxe,newradius,foc);
               lc := length cpath;
               if lc <> 0:
                 draw subpath (1,lc-1) of cpath;
               else:
                 draw rigorouscircle( newcenter,globaxe,newradius );
               fi;
             fi;
           fi;
         fi;
       endfor
     endgroup
   enddef;

% An elliptical frustum:

   def whatisthis(expr CenterPos,OneAxe,OtherAxe,CentersDist,TheFactor) =
     begingroup
       save patha, pathb, pathc, centersvec, noption;
       path patha, pathb, pathc;
       color centersvec;
       numeric noption;
       centersvec = CentersDist*ncrossprod( OneAxe, OtherAxe );
       if ShadowOn:
         patha = ellipticshadowpath( CenterPos,
           OneAxe,
           OtherAxe );
         pathb = ellipticshadowpath( CenterPos+centersvec,
           TheFactor*OneAxe,
           TheFactor*OtherAxe );
         pathc = twocyclestogether( patha, pathb );
         fill pathc;
       fi;
       patha := ellipticpath( CenterPos,
         OneAxe,
         OtherAxe );
       pathb := ellipticpath( CenterPos+centersvec,
         TheFactor*OneAxe,
         TheFactor*OtherAxe );
       pathc := twocyclestogether( patha, pathb );
       unfill pathc;
       draw pathc;
       noption = cdotprod( centersvec, f-CenterPos );
       if  noption > (CentersDist**2):
         draw pathb;
       elseif noption < 0:
         draw patha;
       fi
     endgroup
   enddef;

% Probably the last algorithm I'm going to write for featpost...
% Wrong. The last is ultraimprovertex.
% Wrong again. The last is necplusimprovertex.
% And again wrong. The last is tdcircarrow.
% Well, what can I say, really the last is ellipsoid.
% Wait! It is torushadow!
% Bullshit. Only death can stop me. Now it's revolparab.
% Continuing with torus accessories...
% And I forgot to mention intersectprolatespheroid!!!
% The first of Red October, 2014, added vp.

   def spheroidshadow( expr CentrPoi, NorthPoleVec, Ray ) =
     begingroup
       save a, k, fx, fy, tmpa, tmpb, tmpc, ep, vax, wax, xax, yax, zax, cm, cp;
       save bdh, bdm, bdp, bdv, sm, sp, i, cac;
       numeric a, k, fx, fy, tmpa, tmpb, tmpc, cm, cp, sm, sp, i;
       path ep;
       color vax, wax, xax, yax, zax, cac;
       pair bdh, bdm, bdp, bdv;
       vax = LightSource-CentrPoi;
       if cdotprod(NorthPoleVec,vax)<0:
         xax = -N(NorthPoleVec);
       else:
         xax = N(NorthPoleVec);
       fi;
       a = conorm(NorthPoleVec);
       k = a/Ray;
       if getangle(xax,vax) > 0.5:               %%%%%%%%%%%%%%% DANGER %%%
         zax = ncrossprod(xax,vax);
       else:
         zax = N( ( 0, Z(vax), -Y(vax) ) );
       fi;
       yax = ncrossprod(zax,xax);
       fx = cdotprod(vax,xax);
       fy = cdotprod(vax,yax);
       tmpa = Ray*fx/k;
       tmpb = fy*(fy ++ (fx/k) +-+ Ray);
       tmpc = ((fx/k)**2)+(fy**2);
       cm = (tmpa-tmpb)/tmpc;
       cp = (tmpa+tmpb)/tmpc;
       sm = 1 +-+ cm;
       if fx<a:
         sp = 1 +-+ cp;
       else:
         sp = -( 1 +-+ cp );
       fi;
       bdm = (k*cm,sm)*Ray;
       bdp = (k*cp,sp)*Ray;
       bdh = 0.5[bdp,bdm];
       tmpc := Ray*( 1 +-+ ((xpart bdh)/a) );
       tmpb := tmpc +-+ (ypart bdh);
       bdv = bdm-bdp;
       wax = 0.5*( xax*( xpart (bdv) ) + yax*( ypart (bdv) ) );
       cac = CentrPoi+ xax*( xpart (bdh) ) + yax*( ypart (bdh) );
       fill ellipticshadowpath( cac, wax, zax*tmpb );
     endgroup
   enddef;

   def spheroid( expr CentrPoi, NorthPoleVec, Ray ) =
     begingroup
       save a, k, fx, fy, tmpa, tmpb, tmpc, ep;
       save vax, wax, xax, yax, zax, cm, cp;
       save bdh, bdm, bdp, bdv, sm, sp, i, cac;
       numeric a, k, fx, fy, tmpa, tmpb, tmpc, cm, cp, sm, sp, i;
       path ep;
       color vax, wax, xax, yax, zax, cac;
       pair bdh, bdm, bdp, bdv;
       if ShadowOn:
         spheroidshadow( CentrPoi, NorthPoleVec, Ray );
       fi;
       vax = f-CentrPoi;
       if cdotprod(NorthPoleVec,vax)<0:
         xax = -N(NorthPoleVec);
       else:
         xax = N(NorthPoleVec);
       fi;
       a = conorm(NorthPoleVec);
       k = a/Ray;
       if getangle(xax,vax) > 0.5:               %%%%%%%%%%%%%%% DANGER %%%
         zax = ncrossprod(xax,vax);
       else:
         zax = N( (-Y(vax), X(vax), 0) );
       fi;
       yax = ncrossprod(zax,xax);
       fx = cdotprod(vax,xax);
       fy = cdotprod(vax,yax);
       tmpa = Ray*fx/k;
       tmpb = fy*(fy ++ (fx/k) +-+ Ray);
       tmpc = ((fx/k)**2)+(fy**2);
       cm = (tmpa-tmpb)/tmpc;
       cp = (tmpa+tmpb)/tmpc;
       sm = 1 +-+ cm;
       if fx<a:
         sp = 1 +-+ cp;
       else:
         sp = -( 1 +-+ cp );
       fi;
       bdm = (k*cm,sm)*Ray;
       bdp = (k*cp,sp)*Ray;
       bdh = 0.5[bdp,bdm];
       tmpc := Ray*( 1 +-+ ((xpart bdh)/a) );
       tmpb := tmpc +-+ (ypart bdh);
       bdv = bdm-bdp;
       wax = 0.5*( xax*( xpart (bdv) ) + yax*( ypart (bdv) ) );
       cac = CentrPoi+ xax*( xpart (bdh) ) + yax*( ypart (bdh) );
       ep = ellipticpath( cac, wax, zax*tmpb );
       unfill ep;
       draw ep;
       draw spatialhalfcircle( CentrPoi, NorthPoleVec, Ray, true );
     endgroup
   enddef;

% Another brute-force algorythm.
% It's advisable to use three orthogonal axes.

   def ellipsoid( expr Centr, AxOne, AxTwo, AxThr ) =
     begingroup
       save count, i, j, axx, axy, cyc, cy, di, leng;
       numeric count, i, j, leng;
       color axx, axy, di[];
       path cy, cyc[];
       di1 = AxOne;
       di2 = AxTwo;
       di3 = AxThr;
       count = 0;
       for i=1 upto 3:
         if i=1:
           axx := di2;
           axy := di3;
         elseif i=2:
           axx := di1;
           axy := di3;
         else:
           axx := di1;
           axy := di2;
         fi;
         for j=5 step 10 until 175:
           cyc[incr(count)] = ellipticpath( Centr, di[i],
               Centr + planarrotation( axx, axy, j ) );
         endfor;
       endfor;
       cy = cyc1;
       for i=2 upto count-1:
         cy := twocyclestogether( cy, cyc[i] );
       endfor;
       leng = (length cy) - 1;
       ( point 0 of cy for i = 1 upto leng: ..point i of cy endfor ..cycle )
     endgroup
   enddef;

   def ellipsoidshadow( expr Centr, AxOne, AxTwo, AxThr ) =
     begingroup
       save count, i, j, axx, axy, cyc, cy, di, leng;
       numeric count, i, j, leng;
       color axx, axy, di[];
       path cy, cyc[];
       di1 = AxOne;
       di2 = AxTwo;
       di3 = AxThr;
       count = 0;
       for i=1 upto 3:
         if i=1:
           axx := di2;
           axy := di3;
         elseif i=2:
           axx := di1;
           axy := di3;
         else:
           axx := di1;
           axy := di2;
         fi;
         for j=5 step 10 until 175:
           cyc[incr(count)] = ellipticshadowpath( Centr, di[i],
               Centr + planarrotation( axx, axy, j ) );
         endfor;
       endfor;
       cy = cyc1;
       for i=2 upto count-1:
         cy := twocyclestogether( cy, cyc[i] );
       endfor;
       leng = (length cy) - 1;
       ( point 0 of cy for i = 1 upto leng: ..point i of cy endfor ..cycle )
     endgroup
   enddef;

   def revolparab( expr BaseCenter, ParabTip, BaseRay ) =
     begingroup
       save bcpt, conetip, fakex, fakey, fakez, tipview, ellicenter, coneview;
       save tanefe, majorvec, minorvec, cutvec, auxpoi, crux, auxx, baseview;
       save xzero, yzero, xdelta, fakea, xpos, ypos, xneg, yneg, l, apertur;
       save auxy, maxy, ellmaxang, ymin, xefe, auxray, auxcos, auxsin, ste, a;
       save auxpath, tippath;
       save conda, condb, condc;
       color bcpt, conetip, fakex, fakey, fakez, tipview, ellicenter, coneview;
       color tanefe, majorvec, minorvec, cutvec, auxpoi, crux, auxx, baseview;
       numeric xzero, yzero, xdelta, fakea, xpos, ypos, xneg, yneg, l, apertur;
       numeric auxy, maxy, ellmaxang, ymin, xefe, auxray, auxcos, auxsin, ste;
       numeric a;
       path auxpath, tippath;
       boolean conda, condb, condc;
       bcpt = BaseCenter-ParabTip;
       conetip = BaseCenter-2*bcpt;
       maxy = conorm(bcpt);
       apertur = angle(2*maxy,BaseRay);
       fakey = N(bcpt);
       coneview = f-conetip;
       tipview = f-ParabTip;
       baseview = f-BaseCenter;
       a = getangle(coneview,bcpt);
       conda = a<=apertur;
       condb = a>=180-apertur;
       fakez = ncrossprod( tipview, bcpt );
       fakex = ccrossprod( fakey, fakez );
       xzero = cdotprod( fakex, tipview );
       yzero = cdotprod( fakey, tipview );
       fakea = maxy/(BaseRay**2);
       condc = yzero>=fakea*(xzero**2);
       if (conda and condc) or condb:
         auxpath = rigorouscircle( BaseCenter, bcpt, BaseRay );
         unfill auxpath;
         draw auxpath;
       else:
         xdelta = sqrt(xzero**2 - yzero/fakea);
         xpos = xzero + xdelta;
         xneg = xzero - xdelta;
         ypos = fakea*(xpos**2);
         yneg = fakea*(xneg**2);
         auxy = 0.5[ypos,yneg];
         ellicenter = ParabTip+fakex*xzero+fakey*auxy;
         if yneg<ypos:
           ymin=yneg;
           xefe=xneg;
         else:
           ymin=ypos;
           xefe=xpos;
         fi;
         tanefe = ParabTip +fakex*xefe+fakey*ymin;
         minorvec = xdelta*fakez;
         majorvec = tanefe-ellicenter;
         if (yneg<maxy) and (ypos<maxy):
           draw ellipticpath( ellicenter, majorvec, minorvec );
         else:
           auxpoi = lineintersectplan(conetip,conetip-f,BaseCenter,bcpt);
           auxray = conorm( BaseCenter-auxpoi );
           auxcos = BaseRay/auxray;
           auxsin = 1 +-+ auxcos;
           if cdotprod(fakex,auxpoi-BaseCenter)>0:
             auxx = fakex;
           else:
             auxx = -fakex;
           fi;
           crux = BaseCenter+BaseRay*(auxx*auxcos +fakez*auxsin);
           cutvec = crux-ellicenter;
           auxcos := cdotprod(cutvec,N(majorvec))/conorm(majorvec);
           auxsin := cdotprod(cutvec,N(minorvec))/conorm(minorvec);
           ellmaxang = angle(auxcos,auxsin);
           ste = ellmaxang/18; %%%%%%%%%%%%%%%%%%%%%%%%%%%% DANGER %%%%%%%%%%%%
           tippath =
             rp(ellicenter+planarrotation(majorvec,minorvec,-ellmaxang))
             for l=ste-ellmaxang step ste until ellmaxang:
             ..rp(ellicenter+planarrotation(majorvec,minorvec,l))
           endfor --rp(ellicenter+planarrotation(majorvec,minorvec,ellmaxang));
           if cdotprod( baseview, bcpt )<0:
             auxpath = rigorouscone(true,BaseCenter,bcpt,BaseRay,conetip);
             numeric len;
             len = length auxpath;
             auxpath := subpath (1,len-1) of auxpath;
             auxpath := tippath--(reverse auxpath)--cycle;
             unfill auxpath;
             draw auxpath;
           else:
             tippath := tippath--cycle;
             unfill tippath;
             draw tippath;
             auxpath = rigorouscircle( BaseCenter, bcpt, BaseRay );
             unfill auxpath;
             draw auxpath;
           fi;
         fi;
       fi
     endgroup
   enddef;

% You can't see through this hole. f must not be on the hole axis.
% Not yet documented because "buildcycle" doesn't work properly.

   def fakehole( expr CenterPos, LenVec, Radius ) =
     begingroup
       save patha, pathb, pathc, noption, hashole, auxv, poption, vv;
       save ta, tb, taf, tbf, margint, stopair, pa, pb, testpath, isin;
       path patha, pathb, pathc, pa, pb, testpath;
       numeric noption, ta, tb, margint;
       boolean hashole, poption, isin;
       color auxv, vv;
       pair stopair;
       vv = f-CenterPos;
       patha := rigorouscircle( CenterPos, LenVec, Radius );
       pathb := rigorouscircle( CenterPos+LenVec, LenVec, Radius );
%       patha := goodcirclepath( CenterPos, LenVec, Radius );
%       pathb := goodcirclepath( CenterPos+LenVec, LenVec, Radius );
       auxv := ncrossprod( LenVec, ccrossprod( vv, LenVec ) );
       poption := abs( cdotprod( vv, auxv ) ) <= 1.05*Radius;% DANGER!
       if poption:
         draw patha;
         draw pathb;
       else:
         noption = cdotprod( LenVec, vv );
         if  noption > (conorm(LenVec)**2):
           pa = patha;
           pb = pathb;
         elseif noption < 0:
           pa = pathb;
           pb = patha;
         fi;
         draw pb;
         stopair = pa intersectiontimes pb;
         hashole = (-1,-1) <> stopair;
         if hashole:
           testpath = rp(CenterPos+0.5*LenVec)--(point 0 of pa);
           isin = (-1,-1) <> testpath intersectiontimes pb;
           if not isin:
             ta = xpart stopair;
             tb = ypart stopair;
             stopair := (reverse pa) intersectiontimes (reverse pb);
             taf = length pa - xpart stopair;
             tbf = length pb - ypart stopair;
             margint = 0.01;                                     % DANGER!
             draw (subpath (0,ta-margint) of pa)--
             (subpath (tb+margint,tbf-margint) of pb)--
             (subpath (taf+margint,length pa - margint) of pa)--
             cycle;
           else:
             pathc := buildcycle( pa, pb ); % I don't get it!
             % Why doesn't buildcycle work all the time??? See fakehole.mp
             % When point 0 of pa is inside pb, builcycle doesn't work!!
             draw pathc;
           fi;
         fi;
       fi
     endgroup
   enddef;

% It is time for a kind of cube. Don't use SphericalDistortion here.

   def kindofcube(expr WithDash, IsVertex, RefP, AngA, AngB, AngC, LenA, LenB, LenC ) =
     begingroup
       save star, pos, patw, patb, refv, near, centre, farv;
       save newa, newb, newc, veca, vecb, vecc, auxx, auxy, i;
       color star, pos[], refv, near, newa, newb, newc;
       color veca, vecb, vecc, auxx, auxy, centre, farv;
       path patw, patb;
       numeric i;
       veca = ( cosd(AngA)*cosd(AngB),
         sind(AngA)*cosd(AngB),
         sind(AngB) );
       auxx = ( cosd(AngA+90), sind(AngA+90), 0 );
       auxy = ccrossprod( veca, auxx );
       vecb = cosd(AngC)*auxx + sind(AngC)*auxy;
       vecc = cosd(AngC+90)*auxx + sind(AngC+90)*auxy;
       veca := LenA*veca;
       vecb := LenB*vecb;
       vecc := LenC*vecc;
       if IsVertex:
         star = RefP;
         centre = RefP + 0.5*( veca + vecb + vecc);
       else:
         star = RefP - 0.5*( veca + vecb + vecc);
         centre = RefP;
       fi;
       pos1 = star + veca;
       pos2 = pos1 + vecb;
       pos3 = pos2 + vecc;
       pos4 = pos3 - vecb;
       pos5 = pos4 - veca;
       pos6 = pos5 + vecb;
       pos7 = pos6 - vecc;
       if ShadowOn:
         patw = rp(cb(star))--rp(cb(pos1))--rp(cb(pos2))
           --rp(cb(pos3))--rp(cb(pos4))
           --rp(cb(pos5))--rp(cb(pos6))--rp(cb(pos7))--cycle;
         patb = makepath makepen patw;
         fill patb;
       fi;
       patw := rp(star)--rp(pos1)--rp(pos2)--rp(pos3)--rp(pos4)
         --rp(pos5)--rp(pos6)--rp(pos7)--cycle;
       patb := makepath makepen patw;
       unfill patb;
       draw patb;
       i = 0;
       if inplanarvolume( star, star+veca, f ): i := incr( i ); fi;
       if inplanarvolume( star, star+vecb, f ): i := incr( i ); fi;
       if inplanarvolume( star, star+vecc, f ): i := incr( i ); fi;
       if (i=2) and WithDash:
         message "Unable to dash kindofcube " & cstr( RefP ) & ".";
       elseif i = 3:
         message "f is inside kindofcube " & cstr( RefP ) & ".";
       else:
         refv = f - centre;
         if cdotprod( refv, veca ) > 0:
           newa = -veca;
         else:
           newa = veca;
         fi;
         if cdotprod( refv, vecb ) > 0:
           newb = -vecb;
         else:
           newb = vecb;
         fi;
         if cdotprod( refv, vecc ) > 0:
           newc = -vecc;
         else:
           newc = vecc;
         fi;
         near = centre - 0.5*( newa + newb + newc );
         draw rp(near)--rp(near+newa);
         draw rp(near)--rp(near+newb);
         draw rp(near)--rp(near+newc);
         if WithDash:
           if i=1:
             message "Unable to dash kindofcube " & cstr( RefP ) & ".";
           else:
             farv = centre + 0.5*( newa + newb + newc );
             draw rp(farv)--rp(farv-newa) dashed evenly;
             draw rp(farv)--rp(farv-newb) dashed evenly;
             draw rp(farv)--rp(farv-newc) dashed evenly;
           fi;
         fi;
       fi
     endgroup
   enddef;

% It's a bit late now but the stage must be set.

   def setthestage( expr NumberOfSideSquares, SideSize ) =
     begingroup
       save i, j, squaresize, squarepath, ca, cb, cc, cd;
       numeric i, j, squaresize;
       path squarepath;
       color ca, cb, cc, cd;
       squaresize = SideSize/(2*NumberOfSideSquares-1);
       for i=-0.5*SideSize step 2*squaresize until 0.5*SideSize:
         for j=-0.5*SideSize step 2*squaresize until 0.5*SideSize:
           ca := (i,j,HoriZon);
           cb := (i,j+squaresize,HoriZon);
           cc := (i+squaresize,j+squaresize,HoriZon);
           cd := (i+squaresize,j,HoriZon);
           squarepath := rp(ca)--rp(cb)--rp(cc)--rp(cd)--cycle;
           unfill squarepath;
           draw squarepath;
         endfor;
       endfor
     endgroup
   enddef;

   def setthearena( expr NumberOfDiameterCircles, ArenaDiameter ) =
     begingroup
       save i, j, circlesize, polar, currpos, phi, cpath;
       numeric i, j, circlesize, polar, phi;
       color currpos;
       path cpath;
       circlesize = ArenaDiameter/NumberOfDiameterCircles;
       for i=0.5*ArenaDiameter step -circlesize until 0.4*circlesize:
         polar := floor(6.28318*i/circlesize);
         for j=1 upto polar:
           phi := 360*j/polar;
           currpos := i*(cosd(phi),sind(phi),0)+(0,0,HoriZon);
           cpath := rigorouscircle( currpos, blue, 0.3*circlesize);
           unfill cpath;
           draw cpath;
         endfor;
       endfor
     endgroup
   enddef;

% And a transparent dome. The angular momentum vector is supposed
% to point from the concavity of the dome and into outer space.
% The pen can only be changed with a previous drawoptions().

   def spatialhalfsfear(expr Center, AngulMom, Radius ) =
     begingroup
       save spath, cpath, fpath, rpath, cutp;
       path spath, cpath, fpath, rpath, cutp;
       save ap, bp, cp, dp, cuti, cute, vp;
       pair ap, bp, cp, dp, cuti, cute, vp;
       save auxcos, actcos, actsin, auxsin;
       numeric auxcos, actcos, actsin, auxsin;
       picture partoffear;
       save A, B;
       color A, B;
       spath = rigorousfearpath( Center, Radius );
       auxcos = getcossine( Center, Radius );
       actcos = cdotprod( N( f - Center ), N( AngulMom ) );
       actsin = sqrt(1-actcos**2);
       auxsin = sqrt(1-auxcos**2);
       if actsin <= auxcos:
         if actcos >= 0:
           cpath = goodcirclepath( Center, AngulMom, Radius );
           draw cpath;
         else:
           draw spath;
         fi;
       else:
         fpath = spatialhalfcircle( Center, AngulMom, Radius, true );
         rpath = spatialhalfcircle( Center, AngulMom, Radius, false );
         cuti = point 0 of rpath;
         cute = point ( length rpath ) of rpath;
         ap = 1.1[cuti,cute];
         bp = 1.1[cute,cuti];
         partoffear = nullpicture;
         addto partoffear doublepath spath;
         A = ncrossprod( f-Center, ncrossprod( f-Center, AngulMom ) );
         B = Center + 1.1*Radius*( auxcos*N( f-Center ) + auxsin*A );
         vp = rp(B) - rp(Center);
         cp = ap + vp;
         dp = bp + vp;
         cutp = ap--cp--dp--bp--cycle;
         clip partoffear to cutp;
         draw fpath;
         draw partoffear;
         if actcos >= 0:
           draw rpath;
         fi;
       fi
     endgroup
   enddef;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Toroidal Stuff

% Take a donut.

   def smoothtorus( expr Tcenter, Tmoment, Bray, Sray ) =
     begingroup
       save nearaxe, sideaxe, viewline, circlecenter, circlemoment;
       save ang, ind, i, anglim, angstep, cuspcond, holepic, coofrac;
       save cpath, apath, opath, ipath, wp, ep, refpair, distance, lr;
       color nearaxe, sideaxe, viewline, circlecenter, circlemoment;
       numeric ang, ind, i, anglim, angstep, distance, coofrac, lr;
       path cpath, apath, opath, ipath, wp, ep;
       pair outerp[], innerp[], refpair;
       boolean cuspcond;
       picture holepic;
       save tmoment;
       color tmoment;
       angstep= 4; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% DANGER!
       if ShadowOn:
         torushadow( Tcenter, Tmoment, Bray, Sray );
       fi;
       viewline = f-Tcenter;
       if cdotprod( viewline, Tmoment ) < 0:
         tmoment = -Tmoment;
       else:
         tmoment = Tmoment;
       fi;
       refpair = unitvector( rp(Tcenter+tmoment)-rp(Tcenter) );
       sideaxe = Bray*ncrossprod( tmoment, viewline );
       nearaxe = Bray*ncrossprod( sideaxe, tmoment );
       coofrac = cdotprod( viewline, N( tmoment ) )/Sray;
       if (coofrac <= 1.04) and (coofrac >= 1.01): %%%%%%%%%%% DANGER!
         ind = 360/angstep;
         anglim = 0.5*angstep;
         for i=1 upto ind:
           ang := i*angstep-anglim-180.0;
           circlecenter:= nearaxe*cosd(ang)+sideaxe*sind(ang)+Tcenter;
           circlemoment:=-nearaxe*sind(ang)+sideaxe*cosd(ang);
           cpath:=spatialhalfcircle(circlecenter,circlemoment,Sray,true);
           if i >= 0.5*ind+1:
             outerp[i]=point 0 of cpath;
           else:
             outerp[i]=point (length cpath) of cpath;
           fi;
         endfor;
         opath = for i=1 upto ind: outerp[i].. endfor cycle;
         unfill opath;
         draw opath;
       elseif coofrac < 1.01:
         distance = conorm( viewline );
         lr = Bray + Sray*( 1 +-+ coofrac );
         anglim = angle( ( lr, distance +-+ lr ) );
         ind = 2*floor(anglim/angstep);
         angstep := 2*anglim/(ind+1);
         for i=0 upto 0.5*ind-1:
           ang := i*angstep-anglim;
           circlecenter:= nearaxe*cosd(ang)+sideaxe*sind(ang)+Tcenter;
           circlemoment:=-nearaxe*sind(ang)+sideaxe*cosd(ang);
           cpath:=spatialhalfcircle(circlecenter,circlemoment,Sray,true);
           innerp[i]=point 0 of cpath;
           outerp[i]=point (length cpath) of cpath;
         endfor;
         for i=0.5*ind upto ind-2:
           ang := (i+2)*angstep-anglim;
           circlecenter:= nearaxe*cosd(ang)+sideaxe*sind(ang)+Tcenter;
           circlemoment:=-nearaxe*sind(ang)+sideaxe*cosd(ang);
           cpath:=spatialhalfcircle(circlecenter,circlemoment,Sray,true);
           outerp[i]=point 0 of cpath;
           innerp[i]=point (length cpath) of cpath;
         endfor;
         if coofrac > 0.94:
           apath = innerp0
             for i=1 upto ind-2:
               ..innerp[i]
             endfor
             --cycle;
         else:
           apath = innerp0 for i=2 upto ind-2: ..innerp[i] endfor
           ..outerp[ind-2] for i=ind-3 downto 0: ..outerp[i] endfor
           ..cycle;
         fi;
         unfill apath;
         draw apath;
       else:
         ind = 360/angstep;
         anglim = 0.5*angstep;
         for i=1 upto ind:
           ang := i*angstep-anglim-180.0;
           circlecenter:= nearaxe*cosd(ang)+sideaxe*sind(ang)+Tcenter;
           circlemoment:=-nearaxe*sind(ang)+sideaxe*cosd(ang);
           cpath:=spatialhalfcircle(circlecenter,circlemoment,Sray,true);
           if i >= 0.5*ind+1:
             outerp[i]=point 0 of cpath;
             innerp[i]=point (length cpath) of cpath;
           else:
             innerp[i]=point 0 of cpath;
             outerp[i]=point (length cpath) of cpath;
           fi;
         endfor;
         opath = for i=1 upto ind: outerp[i].. endfor cycle;
         ipath = for i=1 upto ind: innerp[i].. endfor cycle;
         holepic = currentpicture;
         clip holepic to ipath;
         unfill opath;
         draw holepic;
         draw opath;
         draw ipath;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Perhaps there is an analytic way of getting the angle of the cusp point?
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
         i := ceiling(1+0.5*ind);
         cuspcond = false;
         forever:
           i := incr( i );
           exitif i > ind-1;
           cuspcond :=
             refpair dotprod innerp[i+1] < refpair dotprod innerp[i];
           exitif cuspcond;
         endfor;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
         if cuspcond:
           show "Torus shows cusp points.";
           ep = outerp[ind-i+1]--innerp[ind-i+1];
           wp = innerp[i]--outerp[i];
           unfill buildcycle(reverse opath,ep,ipath,wp);
           draw opath;
           draw subpath (i-1,ind-i) of ipath;
         fi;
       fi;
     endgroup
   enddef;

% The shadow of a torus

   def torushadow( expr Tcenter, Tmoment, Bray, Sray ) =
     begingroup
       save theplace, viewline, tmoment, refpair, sideaxe, nearaxe, i;
       color theplace, viewline, tmoment, refpair, sideaxe, nearaxe;
       numeric i;
       viewline = f-Tcenter;
       if cdotprod( viewline, Tmoment ) < 0:
         tmoment = -Tmoment;
       else:
         tmoment = Tmoment;
       fi;
       sideaxe = Bray*ncrossprod( tmoment, viewline );
       nearaxe = Bray*ncrossprod( sideaxe, tmoment );
       for i=1 upto 60:  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% DANGER!
         theplace := Tcenter + planarrotation( sideaxe, nearaxe, 6i ); %%
         fill rigorousfearshadowpath( theplace, Sray );
       endfor;
     endgroup
   enddef;

% Take a "quarter" of a donut
% (no longer under construction but contains a bug).

   def quartertorus( expr Tcenter, Tstart, Tfinis, Sray ) =
     begingroup
       save sideaxe, viewline, circlecenter, circlemoment, amax;
       save i, angstep, tmoment, cpath, opath, ipath, fpath, finc;
       save vstart, vfinis, ostart, ofinis, cstart, cfinis, finis;
       color sideaxe, viewline, circlecenter, circlemoment;
       color tmoment, vstart, vfinis, finis, finc;
       numeric i, angstep, amax;
       path cpath, opath, ipath, cstart, cfinis, fpath;
       pair outerp[], innerp[];
       boolean ostart, ofinis;
       angstep = 4; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% DANGER!
       viewline = f-Tcenter;
       tmoment = ncrossprod( Tstart, Tfinis );
       vstart = ncrossprod( Tstart, tmoment );
       vfinis = ncrossprod( tmoment, Tfinis );
       finis = -vstart*conorm( Tstart );
       amax = getangle( Tstart, Tfinis );
       finc = N( Tfinis )*conorm( Tstart );
       if cdotprod( viewline, tmoment ) < 0:
         tmoment := -tmoment;
       fi;
       sideaxe = ncrossprod( tmoment, viewline );
       for i=0 step angstep until amax:
         circlecenter:= Tstart*cosd(i)+finis*sind(i);
         circlemoment:= ccrossprod(circlecenter,tmoment);
         cpath:=spatialhalfcircle(circlecenter+Tcenter,circlemoment,Sray,true);
         if cdotprod( sideaxe, circlecenter ) < 0:
           outerp[i/angstep]=point 0 of cpath;
           innerp[i/angstep]=point (length cpath) of cpath;
         else:
           innerp[i/angstep]=point 0 of cpath;
           outerp[i/angstep]=point (length cpath) of cpath;
         fi;
       endfor;
       cstart = spatialhalfcircle(Tstart+Tcenter,vstart,Sray,true);
       if cdotprod( sideaxe, Tstart ) > 0:
         cstart := reverse cstart;
       fi;
       cfinis = spatialhalfcircle(finc+Tcenter,vfinis,Sray,true);
       if cdotprod( sideaxe, finc ) < 0:
         cfinis := reverse cfinis;
       fi;
       opath = outerp0 for i=angstep step angstep until amax:
         ..outerp[i/angstep] endfor;
       ipath = innerp0 for i=angstep step angstep until amax:
         ..innerp[i/angstep] endfor;
       fpath = ipath---cfinis---reverse opath---cstart---cycle;
       unfill fpath;
       draw fpath;
       ostart = cdotprod( viewline-Tstart, vstart ) > 0;
       if ostart:
         cpath := rigorouscircle( Tcenter+Tstart, vstart, Sray );
         unfill cpath;
         draw cpath;
       fi;
       ofinis = cdotprod( viewline-finc, vfinis ) > 0;
       if ofinis:
         cpath := rigorouscircle( Tcenter+finc, vfinis, Sray );
         unfill cpath;
         draw cpath;
       fi;
       %draw opath withcolor blue;
       %draw ipath withcolor red;
     endgroup
   enddef;

   def pointinsidetorus( expr Point, Tcenter, Tmoment, Bray, Sray ) =
     begingroup
       save outputboolean, height, dist, dray;
       boolean outputboolean;
       numeric height, dist, dray;
       height = cdotprod(N(Tmoment),Point-Tcenter);
       if abs(height)>=Sray:
         outputboolean = false;
       else:
         dist = conorm(Point-Tcenter-height*N(Tmoment));
         dray = Sray +-+ abs(height);
         if (dist<Bray+dray) and (dist>Bray-dray):
           outputboolean = true;
         else:
           outputboolean = false;
         fi;
       fi;
       ( outputboolean )
     endgroup
   enddef;

   def pointrelativetotorus( expr Point, Tcenter, Tmoment, Bray, Sray ) =
     begingroup
       save height, dist;
       numeric height;
       color dist;
       height = cdotprod(N(Tmoment),Point-Tcenter);
       dist = N(Point-Tcenter-height*N(Tmoment));
       ( conorm(Point-Bray*dist)-Sray )
     endgroup
   enddef;

   def intersectorus( expr Tcenter, Tmoment, Bray, Sray, LinePoi, LineDir ) =
     begingroup
       save trypoi, factry, linedi;
       color trypoi, linedi;
       numeric factry, j, auxd;
       trypoi = LinePoi;
       factry = 0.25;
       linedi = N(LineDir);
       for j= 1 upto 50:
         auxd := pointrelativetotorus( trypoi, Tcenter, Tmoment, Bray, Sray );
         trypoi := trypoi+factry*linedi*auxd;
       endfor;
       ( trypoi )
     endgroup
   enddef;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Non-standard objects:

   def positivecharge( expr InFactPositive, Center, BallRay ) =
     begingroup
       save auxc, axehorf, axeside, viewline, spath, pa, pb, pc, pd;
       color auxc, axehorf, axeside, viewline, pa, pb, pc, pd;
       path spath;
       viewline = f - Center;
       axehorf = N( ( X(viewline), Y(viewline), 0 ) );
       axeside = ccrossprod( axehorf, blue );
       if ShadowOn:
         fill rigorousfearshadowpath( Center, BallRay );
       fi;
       spath = rigorousfearpath( Center, BallRay );
       unfill spath;
       draw spath;
       auxc = Center + sqrt(3)*axehorf;
       pa = auxc + axeside;
       pb = auxc - axeside;
       angline( pa, pb, Center, BallRay, "", top );
       if InFactPositive:
         pc = auxc + blue;
         pd = auxc - blue;
         angline( pc, pd, Center, BallRay, "", top );
       fi
     endgroup
   enddef;

   def simplecar(expr RefP, AngCol, LenCol, FronWheelCol, RearWheelCol ) =
     begingroup
       save veca, vecb, vecc, anga, angb, angc, lena, lenb, lenc;
       save auxn, viewline, auxm, fl, fr, rl, rr, auxx, auxy;
       save fmar, fthi, fray, rmar, rthi, rray, inrefp;;
       color veca, auxx, auxy, vecb, vecc, viewline;
       color fl, fr, rl, rr, inrefp;
       numeric anga, angb, angc, lena, lenb, lenc, auxm, auxn;
       numeric fmar, fthi, fray, rmar, rthi, rray;
       anga = X( AngCol );
       angb = Y( AngCol );
       angc = Z( AngCol );
       lena = X( LenCol );
       lenb = Y( LenCol );
       lenc = Z( LenCol );
       fmar = X( FronWheelCol );
       fthi = Y( FronWheelCol );
       fray = Z( FronWheelCol );
       rmar = X( RearWheelCol );
       rthi = Y( RearWheelCol );
       rray = Z( RearWheelCol );
       veca = ( cosd(anga)*cosd(angb),
         sind(anga)*cosd(angb),
         sind(angb) );
       auxx = ( cosd(anga+90), sind(anga+90), 0 );
       auxy = ccrossprod( veca, auxx );
       vecb = cosd(angc)*auxx + sind(angc)*auxy;
       vecc = cosd(angc+90)*auxx + sind(angc+90)*auxy;
       viewline = f - RefP;
       auxm = cdotprod( viewline, veca );
       auxn = cdotprod( viewline, vecb );
       inrefp = RefP - 0.5*lenc*vecc;
       fl = inrefp + (0.5*lena-fmar-fray)*veca + 0.5*lenb*vecb;
       fr = inrefp + (0.5*lena-fmar-fray)*veca - 0.5*lenb*vecb;
       rl = inrefp - (0.5*lena-rmar-rray)*veca + 0.5*lenb*vecb;
       rr = inrefp - (0.5*lena-rmar-rray)*veca - 0.5*lenb*vecb;
       if auxn > 0.5*lenb:
         if auxm > 0:
           rigorousdisc( 0, true, rr, rray, -rthi*vecb );
           rigorousdisc( 0, true, fr, fray, -fthi*vecb );
           kindofcube(false,false,RefP,anga,angb,angc,lena,lenb,lenc);
           rigorousdisc( 0, true, rl, rray, rthi*vecb );
           rigorousdisc( 0, true, fl, fray, fthi*vecb );
         else:
           rigorousdisc( 0, true, fr, fray, -fthi*vecb );
           rigorousdisc( 0, true, rr, rray, -rthi*vecb );
           kindofcube(false,false,RefP,anga,angb,angc,lena,lenb,lenc);
           rigorousdisc( 0, true, fl, fray, fthi*vecb );
           rigorousdisc( 0, true, rl, rray, rthi*vecb );
         fi;
       elseif auxn < -0.5*lenb:
         if auxm > 0:
           rigorousdisc( 0, true, rl, rray, rthi*vecb );
           rigorousdisc( 0, true, fl, fray, fthi*vecb );
           kindofcube(false,false,RefP,anga,angb,angc,lena,lenb,lenc);
           rigorousdisc( 0, true, rr, rray, -rthi*vecb );
           rigorousdisc( 0, true, fr, fray, -fthi*vecb );
         else:
           rigorousdisc( 0, true, fl, fray, fthi*vecb );
           rigorousdisc( 0, true, rl, rray, rthi*vecb );
           kindofcube(false,false,RefP,anga,angb,angc,lena,lenb,lenc);
           rigorousdisc( 0, true, fr, fray, -fthi*vecb );
           rigorousdisc( 0, true, rr, rray, -rthi*vecb );
         fi;
       else:
         if auxm > 0:
           rigorousdisc( 0, true, rl, rray, rthi*vecb );
           rigorousdisc( 0, true, fl, fray, fthi*vecb );
           rigorousdisc( 0, true, rr, rray, -rthi*vecb );
           rigorousdisc( 0, true, fr, fray, -fthi*vecb );
           kindofcube(false,false,RefP,anga,angb,angc,lena,lenb,lenc);
         else:
           rigorousdisc( 0, true, fl, fray, fthi*vecb );
           rigorousdisc( 0, true, rl, rray, rthi*vecb );
           rigorousdisc( 0, true, fr, fray, -fthi*vecb );
           rigorousdisc( 0, true, rr, rray, -rthi*vecb );
           kindofcube(false,false,RefP,anga,angb,angc,lena,lenb,lenc);
         fi;
       fi
     endgroup
   enddef;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Differential Equations:

% Oh! Well... I couldn't do without differential equations.
% The point is that I want to draw vectorial field lines in space.
% Keep it simple: second-order Runge-Kutta method.
% This is for solving first order differential equations

   def fieldlinestep( expr Spos, Step )( text VecFunc ) =
     begingroup
       save kone, ktwo;
       color kone, ktwo;
       kone = Step*VecFunc( Spos );
       ktwo = Step*VecFunc( Spos+0.5*kone );
       ( Spos+ktwo )
     endgroup
   enddef;

   def fieldlinepath( expr Numb, Spos, Step )( text VecFunc ) =
     begingroup
       save ind, flpath, prevpos, thispos;
       numeric ind;
       color prevpos, thispos;
       path flpath;
       prevpos = Spos;
       flpath = rp( Spos )
       for ind=1 upto Numb:
         hide( thispos := fieldlinestep( prevpos, Step, VecFunc ) )
         ..rp( thispos )
         hide( prevpos := thispos )
       endfor;
       ( flpath )
     endgroup
   enddef;

% Another point is that I want to draw trajectories in space.
% This is for solving second order differential equations

   def trajectorypath( expr Numb, Spos, Svel, Step )( text VecFunc ) =
     begingroup
       save ind, flpath, prevpos, thispos, prevvel, thisvel;
       save rone, rtwo, vone, vtwo;
       numeric ind;
       color prevpos, thispos, prevvel, thisvel;
       color rone, rtwo, vone, vtwo;
       path flpath;
       prevpos = Spos;
       prevvel = Svel;
       flpath = rp( Spos )
       for ind=1 upto Numb:
         hide(
           vone := Step*VecFunc( prevpos );
           rone := Step*prevvel;
           vtwo := Step*VecFunc( prevpos+0.5*rone );
           rtwo := Step*( prevvel+0.5*vone );
           thisvel := prevvel+vtwo;
           thispos := prevpos+rtwo
           )
         ..rp( thispos )
         hide(
           prevpos := thispos;
           prevvel := thisvel
           )
       endfor;
       ( flpath )
     endgroup
   enddef;

% Another point is that I want to draw trajectories in space and
% dependent on velocity: VecFunc( position, velocity ).
% This time is fourth-order Runge-Kutta.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% CHANGES PrintStep!!!!

   def dragtrajectorypath( expr Spos, Svel, Step )( text VecFunc ) =
     begingroup
       save ind, flpath, prevpos, thispos, prevvel, thisvel;
       save rone, rtwo, rthr, rfou, vone, vtwo, vthr, vfou;
       numeric ind;
       color prevpos, thispos, prevvel, thisvel;
       color rone, rtwo, rthr, rfou, vone, vtwo, vthr, vfou;
       path flpath;
       prevpos = Spos;
       prevvel = Svel;
       flpath = rp( Spos );
       ind = 1;
       forever:
         vone := Step*VecFunc( prevpos , prevvel );
         rone := Step*prevvel;
         vtwo := Step*VecFunc( prevpos+0.5*rone, prevvel+0.5*vone );
         rtwo := Step*( prevvel+0.5*vone );
         vthr := Step*VecFunc( prevpos+0.5*rtwo, prevvel+0.5*vtwo );
         rthr := Step*( prevvel+0.5*vtwo );
         vfou := Step*VecFunc( prevpos+rthr, prevvel+vthr );
         rfou := Step*( prevvel+vthr );
         thisvel := prevvel+(vtwo+vthr)/3+(vone+vfou)/6;
         thispos := prevpos+(rtwo+rthr)/3+(rone+rfou)/6;
         exitif Z( thispos ) < -0.0001;                     %%%%%%%%%% EDIT!
         prevpos := thispos;
         prevvel := thisvel;
         flpath := flpath--rp( thispos );
       endfor;
       PrintStep := Y(thispos);
       ( flpath )
     endgroup
   enddef;

% And now i stop.

   def magnetictrajectorypath( expr Numb, Spos, Svel, Step )( text VecFunc ) =
     begingroup
       save ind, flpath, prevpos, thispos, prevvel, thisvel;
       save rone, rtwo, rthr, rfou, vone, vtwo, vthr, vfou;
       numeric ind;
       color prevpos, thispos, prevvel, thisvel;
       color rone, rtwo, rthr, rfou, vone, vtwo, vthr, vfou;
       path flpath;
       prevpos = Spos;
       prevvel = Svel;
       flpath = rp( Spos )
       for ind=1 upto Numb:
         hide(
           vone := Step*ccrossprod( VecFunc( prevpos ), prevvel );
           rone := Step*prevvel;
           vtwo :=
             Step*ccrossprod(VecFunc(prevpos+0.5*rone),prevvel+0.5*vone);
           rtwo := Step*( prevvel+0.5*vone );
           vthr :=
             Step*ccrossprod(VecFunc(prevpos+0.5*rtwo),prevvel+0.5*vtwo);
           rthr := Step*( prevvel+0.5*vtwo );
           vfou :=
             Step*ccrossprod( VecFunc( prevpos+rthr ), prevvel+vthr );
           rfou := Step*( prevvel+vthr );
           thisvel := prevvel+(vtwo+vthr)/3+(vone+vfou)/6;
           thispos := prevpos+(rtwo+rthr)/3+(rone+rfou)/6
           )
         ..rp( thispos )
         hide(
           prevpos := thispos;
           prevvel := thisvel
           )
       endfor;
       ( flpath )
     endgroup
   enddef;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Part II:
%%%%%%%%%%%%%%%%%%%%%%%%%%%% Advanced 3D-Object Definition Functions %%%%%
% Please check the examples in planpht.mp or the default object below %%%%

   vardef makeline@#( text vertices ) =
     save counter;
     numeric counter;
     counter = 0;
     for ind=vertices:
       counter := incr( counter );
       L@#p[counter] := V[ind];
     endfor;
     npl@# := counter;
     NL := @#
   enddef;

   vardef makeface@#( text vertices ) =
     save counter;
     numeric counter;
     counter = 0;
     for ind=vertices:
       counter := incr( counter );
       F@#p[counter] := V[ind];
     endfor;
     npf@# := counter;
     NF := @#;
     FCD[NF] := false
   enddef;

   vardef getready( expr commstr, refpoi ) =
     Nobjects := incr( Nobjects );
     ostr[Nobjects] := commstr;
     RefDist[Nobjects] := conorm( f - refpoi )
   enddef;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Definition of a 3D-Object
% define vertices
   V1 := (1,0,0);
   V2 := (0,0,0);
   V3 := (0,1,0);
   V4 := (-0.3,0.2,1);
   V5 := (1,0,1);
   V6 := (0,1,1);
   V7 := (0,0,2);
   V8 := (-0.5,0.6,1.2);
   V9 := (0.6,-0.5,1.2);
   makeline1(8,9);
   makeface1(1,2,7);
   makeface2(2,3,7);
   makeface3(5,4,6);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% or the old way below %%%%%%%%%
% define lines
%    NL := 1;     % number of lines
%    npl1 := 2;   % number of vertices of the first line
%    L1p1 := V8;
%    L1p2 := V9;
% define faces
%    NF := 3;     % number of faces
%    npf1 := 3;   % number of vertices of the first face
%    F1p1 := V1;
%    F1p2 := V2;
%    F1p3 := V7;
%    npf2 := 3;   % number of vertices of the second face
%    F2p1 := V2;
%    F2p2 := V3;
%    F2p3 := V7;
%    npf3 := 3;   % number of vertices of the third face
%    F3p1 := V5;
%    F3p2 := V4;
%    F3p3 := V6;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Flip first argument accordingly to the second

   def flipvector(expr A, B) =
       begingroup
           save  nv;
           color nv;
           if cdotprod( A, B) < 0 :
               nv = -A;
           else:
               nv = A;
           fi;
           ( nv )
       endgroup
   enddef;

% Frontside of a face given by three of its vertices

   def facevector(expr A, B, C) =
       begingroup
           save  nv;
           color nv;
           nv = ncrossprod( A-B, B-C );
           ( flipvector( nv, f-B ) )
       endgroup
   enddef;

% Center or inside of a face

   def masscenter(expr Nsides)(suffix Coords) =
       begingroup
           save mc, counter;
           numeric  counter;
           color mc;
           mc = (0,0,0);
           for counter=1 upto Nsides:
               mc := mc + Coords[counter];
           endfor;
           ( mc / Nsides )
       endgroup
   enddef;

% Direction of coverability. The trick is here.
% The condition for visibility is
% that the angle beetween the vector that goes from the side of a face to
% the mark position and the covervector must be greater than 90 degrees.

   def covervector(expr A, B, MassCenter) =
       begingroup
           save  nv;
           color nv;
           nv = ncrossprod( A-f, B-f );
           ( flipvector( nv, MassCenter-B) )
       endgroup
   enddef;

% O.K., the following macro tests the visibility of a point

   def themarkisinview(expr Mark, OwnFace) =
       begingroup
           save  c, faceVec, centerPoint, coverVec, inview, l, m;
           color c, faceVec, centerPoint, coverVec;
           boolean inview;
           numeric l, m;
           l = 0;
           forever:          % cycle over faces until the mark is covered
               l := incr(l);
               if l = OwnFace:
                   l := incr(l);
               fi;
               exitif l > NF;
               faceVec := facevector(F[l]p1,F[l]p2,F[l]p3);
               inview := true;
               if cdotprod(Mark-F[l]p1, faceVec) < 0:
                   centerPoint := masscenter(npf[l], F[l]p);
                   m := 0;
                   forever:              % cycle over segments of a face
                       m := incr(m);
                       exitif m > npf[l];
                       if m < npf[l]:
                           c := F[l]p[m+1];
                       else:
                           c := F[l]p1;
                       fi;
                       coverVec := covervector(F[l]p[m], c, centerPoint);
                       inview := cdotprod(Mark-c,coverVec) <= 0;
                       exitif inview;
                   endfor;
               fi;
               exitif not inview;
           endfor;
           ( inview )
       endgroup
   enddef;

% Check for possible intersection or crossing.

   def maycrossviewplan(expr Ea, Eb, La, Lb) =
       begingroup
           ( abs( cdotprod( ccrossprod(Ea-f,Eb-f), La-Lb ) ) > 0.001 )
       endgroup
   enddef;

% Calculate the intersection of two sides. This is very nice.

   def crossingpoint(expr Ea, Eb, La, Lb) =
       begingroup
           save  thecrossing, perpend, exten, aux;
           color thecrossing, perpend;
           numeric exten, aux;
           if ( Ea = Lb ) or ( Ea = La ):
               thecrossing = Ea;
           elseif ( Eb = Lb ) or ( Eb = La ):
               thecrossing = Eb;
           else:
               perpend = ccrossprod( Ea-f, Eb-f );
               if conorm( perpend ) = 0:
                   thecrossing = Eb;
               else:
                   aux = cdotprod( perpend, f );
                   cdotprod( perpend, thecrossing ) = aux;
                   ( La-Lb )*exten = La-thecrossing;
               fi;
           fi;
           ( thecrossing )
       endgroup
   enddef;

% Calculate the intersection of an edge and a face.

   def crossingpointf(expr Ea, Eb, Fen) =
       begingroup
           save  thecrossing, perpend, exten;
           color thecrossing, perpend;
           numeric exten;
           perpend = ccrossprod( F[Fen]p1-F[Fen]p2, F[Fen]p3-F[Fen]p2 );
           cdotprod(perpend,thecrossing) = cdotprod( perpend, F[Fen]p2 );
           ( Ea-Eb )*exten = Ea-thecrossing;
           ( thecrossing )
       endgroup
   enddef;

% Check for possible intersection of an edge and a face.

   def maycrossviewplanf(expr Ea, Eb, Fen) =
       begingroup
           save perpend;
           color perpend;
           perpend = ccrossprod( F[Fen]p1-F[Fen]p2, F[Fen]p3-F[Fen]p2 );
           ( abs( cdotprod( perpend, Ea-Eb ) ) > 0.001 )
       endgroup
   enddef;

% The intersection point must be within the extremes of the segment.

   def insidedge(expr Point, Ea, Eb) =
       begingroup
           save fract;
           numeric fract;
           fract := cdotprod( Point-Ea, Point-Eb );
           ( fract < 0 )
       endgroup
   enddef;

% Skip edges that are too far away

   def insideviewsphere(expr Ea, Eb, La, Lb) =
       begingroup
           save furthestofedge, nearestofline, flag, exten;
           color nearestofline, furthestofedge;
           boolean flag;
           numeric exten;
           nearestofline = La+exten*(Lb-La);
           cdotprod( nearestofline-f, Lb-La ) = 0;
           if conorm(Ea-f) < conorm(Eb-f):
               furthestofedge := Eb;
           else:
               furthestofedge := Ea;
           fi;
           if conorm(nearestofline-f) < conorm(furthestofedge-f):
               flag := true;
           else:
               flag := false;
           fi;
           ( flag )
       endgroup
   enddef;

% The intersection point must be within the triangle defined by
% three points. Really smart.

   def insidethistriangle(expr Point, A, B, C ) =
       begingroup
           save arep, area, areb, aret, flag;
           color arep, area, areb, aret;
           boolean flag;
           aret = ccrossprod( A-C, B-C );
           arep = flipvector( ccrossprod( C-Point, A-Point ), aret );
           area = flipvector( ccrossprod( A-Point, B-Point ), aret );
           areb = flipvector( ccrossprod( B-Point, C-Point ), aret );
           flag = ( conorm( arep + area + areb ) <= 2*conorm( aret ) );
           ( flag )
       endgroup
   enddef;

% The intersection point must be within the triangle defined by the
% point of view f and the extremes of some edge.

   def insideviewtriangle(expr Point, Ea, Eb) =
       insidethistriangle( Point, Ea, Eb, f )
   enddef;

% The intersection point must be within the face

   def insidethisface(expr Point, FaN) =
       begingroup
           save flag, m, central;
           boolean flag;
           numeric m;
           color central;
           m = npf[FaN];
           central = masscenter( m, F[FaN]p );
           flag = insidethistriangle( Point,
                             central, F[FaN]p[m], F[FaN]p[1] );
           for m=2 upto npf[FaN]:
               exitif flag;
               flag := insidethistriangle( Point,
                             central, F[FaN]p[m-1], F[FaN]p[m] );
           endfor;
           ( flag )
       endgroup
   enddef;

% Draw the visible parts of a straight line in beetween points A and B
% changing the thickness of the line accordingly to the distance from f

   def coarse_line(expr A, B, Facen, Press, Col) =
       begingroup
           save  k, mark, stepVec;
           numeric k;
           color mark, stepVec;
           stepVec := resolvec(A,B);
           k := 0;
           forever:                    % cycle along a whole segment
               mark := A+(k*stepVec);
               exitif cdotprod(B-mark,stepVec) < 0;
               if themarkisinview(mark,Facen):
                   signalvertex(mark, Press, Col);
               fi;
               k := incr(k);
           endfor
       endgroup
   enddef;

% Get the 2D rigorous projection path of a face.
% Don't use SphericalDistortion here.

   def facepath(expr Facen) =
       begingroup
           save thispath, counter;
           path thispath;
           numeric counter;
           thispath = rp(F[Facen]p[1])--
           for counter=2 upto (npf[Facen]):
               rp(F[Facen]p[counter])--
           endfor
           cycle;
           ( thispath )
       endgroup
   enddef;

   def faceshadowpath(expr Facen) =
       begingroup
           save thispath, counter;
           path thispath;
           numeric counter;
           thispath = rp(cb(F[Facen]p[1]))--
           for counter=2 upto (npf[Facen]):
               rp(cb(F[Facen]p[counter]))--
           endfor
           cycle;
           ( thispath )
       endgroup
   enddef;

% FillDraw a face

   def face_invisible( expr Facen )( text LineAtribs ) =
       begingroup
           save ghost;
           path ghost;
           ghost = facepath( Facen );
           unfill ghost;
           draw ghost LineAtribs
       endgroup
   enddef;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Different kinds of renderers:

% Draw only the faces, calculating edge-intersections.
% Mind-blogging kind of thing.
% Only two constraints: i) faces must be planar and
%                      ii) faces must be convex.
% Pay attention: depends on PrintStep (resolvec).

   def sharpraytrace
%%                     ( expr LabelCrossPoints )
                                                 =
      begingroup
         save i, j, k, l, counter, a, b, c, d, currcross;
         save flag, swapc, swapn, somepoint, frac, exten;
         save trythis, refpath, otherpath, intertimes;
         save counter, infolabel;
         numeric i, j, k, l, counter, swapn;
         color a, b, c, d, currcross, swapc;
         boolean flag, trythis;
         path refpath, otherpath;
         pair intertimes;
         string infolabel;
         color crosspoin[];
         numeric sortangle[];
         for i=1 upto NF:                   % scan all faces
            for j=1 upto npf[i]:            % scan all edges
               a := F[i]p[j];
               if j <> npf[i]:
                  b := F[i]p[j+1];
               else:
                  b := F[i]p1;
               fi;
               crosspoin[1] := a;
               counter := 2;
               refpath := rp(a)--rp(b); % The limits a and b of one
                                        % side of one face
               for k=1 upto NF:
                  otherpath := facepath(k);
                  intertimes := refpath intersectiontimes otherpath;
                  trythis := xpart intertimes <> 0;
                  if trythis and (xpart intertimes <> 1) and (k <> i):
                     for l=1 upto npf[k]:
                        c := F[k]p[l];
                        if l < npf[k]:
                           d := F[k]p[l+1];
                        else:
                           d := F[k]p1;
                        fi;
                        if insideviewsphere( a, b, c, d ):
                          if maycrossviewplan( a, b, c, d ):
                            currcross := crossingpoint( a, b, c, d );
                            if insideviewtriangle( currcross, a, b ):
                              if insidedge( currcross, c, d ):
                                swapc := ccrossprod( a-b, f-currcross);
                                swapc := ccrossprod(swapc,f-currcross);
                                color somepo;
                                numeric fract;
                                (b-a)*fract = somepo-a;
                                cdotprod(swapc,somepo) =cdotprod(swapc,f);
                                if (fract>0) and (fract<1):
                                  crosspoin[counter] := somepo;
                                  counter := incr(counter);
                                fi;
                              fi;
                            fi;
                          fi;
                        fi;
                     endfor;
                     if maycrossviewplanf( a, b, k ):
                        currcross := crossingpointf( a, b, k );
                        if insidethisface( currcross, k ):
                           if insidedge( currcross, a, b ):
                              crosspoin[counter] := currcross;
                              counter := incr(counter);
                           fi;
                        fi;
                     fi;
                  fi;
               endfor;
               crosspoin[counter] := b;
               sortangle[1] := 0;
               for k=2 upto counter:
                  sortangle[k] := conorm(crosspoin[k]-a);
               endfor;
               forever:
                  flag := true;
                  for k=2 upto counter:
                     if sortangle[k] < sortangle[k-1]:
                        swapn := sortangle[k-1];
                        sortangle[k-1] := sortangle[k];
                        sortangle[k] := swapn;
                        swapc := crosspoin[k-1];
                        crosspoin[k-1] := crosspoin[k];
                        crosspoin[k] := swapc;
                        flag := false;
                     fi;
                  endfor;
                  exitif flag;
               endfor;
               for k=2 upto counter:
                  swapc := resolvec(crosspoin[k-1],crosspoin[k]);
                  flag := themarkisinview( crosspoin[k-1]+swapc, i );
                  if flag and themarkisinview( crosspoin[k]-swapc, i ):
                     draw rp(crosspoin[k-1])--rp(crosspoin[k]);
                  fi;
               endfor;
%                if LabelCrossPoints:
%                  for k=1 upto counter:
%                    infolabel:=decimal(i)&","&decimal(j)&","&decimal(k);
%                    infolabel := "0";
%                    dotlabelrand(infolabel,rp(crosspoin[k]));
%                  endfor;
%                fi;
            endfor;
         endfor
      endgroup
   enddef;

% Draw three-dimensional lines checking visibility.

   def lineraytrace(expr Press, Col) =
       begingroup
           save  i, j, a, b;
           numeric i, j;
           color a, b;
           for i=1 upto NL:                    % scan all lines
               for j=1 upto npl[i]-1:
                   a := L[i]p[j];
                   b := L[i]p[j+1];
                   coarse_line( a, b, 0, Press, Col);
               endfor;
           endfor
       endgroup
   enddef;

% Draw only the faces, rigorously projecting the edges.

   def faceraytrace(expr Press, Col) =
       begingroup
           save  i, j, a, b;
           numeric i, j;
           color a, b;
           for i=1 upto NF:                    % scan all faces
               for j=1 upto npf[i]:
                   a := F[i]p[j];
                   if j <> npf[i]:
                       b := F[i]p[j+1];
                   else:
                       b := F[i]p1;
                   fi;
                   coarse_line( a, b, i, Press, Col);
               endfor;
           endfor
       endgroup
   enddef;

% Fast test for your three-dimensional object

   def draw_all_test( expr AlsoDrawLines ) =
     begingroup
       save  i, j, a, b;
       numeric i, j;
       color a, b;
       if ShadowOn:
         for i=1 upto NF:
           fill faceshadowpath( i );
         endfor;
         if AlsoDrawLines:
           for i=1 upto NL:                    % scan all lines
             for j=1 upto npl[i]-1:
               a := L[i]p[j];
               b := L[i]p[j+1];
               drawsegment( cb(a), cb(b) );
             endfor;
           endfor;
         fi;
       fi;
       for i=1 upto NF:                    % scan all faces
         for j=1 upto npf[i]:
           a := F[i]p[j];
           if j <> npf[i]:
             b := F[i]p[j+1];
           else:
             b := F[i]p1;
           fi;
           drawsegment( a, b );
         endfor;
       endfor;
       if AlsoDrawLines:
         for i=1 upto NL:                    % scan all lines
           for j=1 upto npl[i]-1:
             a := L[i]p[j];
             b := L[i]p[j+1];
             drawsegment( a, b );
           endfor;
         endfor;
       fi
     endgroup
   enddef;

% Don't use SphericalDistortion here.

   def fill_faces( text LineAtribs ) =
       begingroup
           save  i;
           numeric i;
           if ShadowOn:
             for i=1 upto NF:
               fill faceshadowpath( i );
             endfor;
           fi;
           for i=1 upto NF:
               face_invisible( i, LineAtribs );
           endfor
       endgroup
   enddef;

   def doitnow =
     begingroup
       save i, j, farone;
       numeric i, j, farone[];
       for i=1 upto Nobjects:
         farone[i] := i;
       endfor;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Shell's Method of sorting %%%%%%%%
       save inc, v, vv;
       numeric inc, v, vv;
       inc = 1;
       forever:
         inc := 3*inc+1;
         exitif inc > Nobjects;
       endfor;
       forever:
         inc := round(inc/3);
         for i=inc+1 upto Nobjects:
           v := RefDist[i];
           vv:= farone[i];
           j := i;
           forever:
             exitunless RefDist[j-inc] > v;
             RefDist[j] := RefDist[j-inc];
             farone[j] := farone[j-inc];
             j := j-inc;
             exitif j <= inc;
           endfor;
           RefDist[j] := v;
           farone[j] := vv;
         endfor;
         exitunless inc > 1;
       endfor;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
       for i=Nobjects downto 1:
         j := farone[i];
         scantokens ostr[j];
       endfor
     endgroup
   enddef;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Nematic Liquid Crystal wise:

   def generatedirline(expr Lin, Phi, Theta, Long, Currpos ) =
       begingroup
           save longvec;
           color longvec;
           npl[Lin] := 2;
           longvec := Long*( cosd(Phi)*cosd(Theta),
                             sind(Phi)*cosd(Theta),
                             sind(Theta) );
           L[Lin]p1 := Currpos-0.5*longvec;
           L[Lin]p2 := Currpos+0.5*longvec
       endgroup
   enddef;

   def generatedirface(expr Fen, Phi, Theta, Long, Base, Currpos ) =
       begingroup
           save basevec, longvec;
           color basevec, longvec;
           npf[Fen] := 3;
           longvec := Long*( cosd(Phi)*cosd(Theta),
                             sind(Phi)*cosd(Theta),
                             sind(Theta) );
           basevec := Base*ncrossprod( Currpos-f, longvec );
           F[Fen]p1 := Currpos-0.5*(longvec+basevec);
           F[Fen]p2 := Currpos+0.5*longvec;
           F[Fen]p3 := Currpos-0.5*(longvec-basevec)
       endgroup
   enddef;

   def generateonebiax(expr Lin, Phi, Theta, Long, SndDirAngl, Base, Currpos ) =
       begingroup
           save basevec, longvec, u, v;
           color basevec, longvec, u, v;
           npl[Lin] := 4;
           longvec := Long*( cosd(Phi)*cosd(Theta),
                             sind(Phi)*cosd(Theta),
                             sind(Theta) );
           v = (-sind(Phi), cosd(Phi), 0);
           u = ( cosd(Phi)*cosd(Theta+90),
                 sind(Phi)*cosd(Theta+90),
                 sind(Theta+90) );
           basevec := Base*( v*cosd(SndDirAngl)+u*sind(SndDirAngl) );
           L[Lin]p1 := Currpos-0.5*longvec;
           L[Lin]p2 := Currpos+0.5*basevec;
           L[Lin]p3 := Currpos+0.5*longvec;
           L[Lin]p4 := Currpos-0.5*basevec
       endgroup
   enddef;

   def director_invisible( expr SortEmAll, ThickenFactor, CyclicLines ) =
     begingroup
       save i, j, k, farone, thisfar;
       save outerr, innerr, direc, ounum;
       numeric i, j, k, farone[], dist[], thisfar, ounum;
       pen actualpen, outerr, innerr;
       path direc;
       actualpen = currentpen;
       if SortEmAll:
         for i=1 upto NL:                    % scan all lines
           dist[i] := conorm( masscenter( npl[i], L[i]p ) - f );
           farone[i] := i;
         endfor;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Shell's Method of sorting %%%%%%%%
         save inc, v, vv;
         numeric inc, v, vv;
         inc = 1;
         forever:
           inc := 3*inc+1;
           exitif inc > NL;
         endfor;
         forever:
           inc := round(inc/3);
           for i=inc+1 upto NL:
             v := dist[i];
             vv:= farone[i];
             j := i;
             forever:
               exitunless dist[j-inc] > v;
               dist[j] := dist[j-inc];
               farone[j] := farone[j-inc];
               j := j-inc;
               exitif j <= inc;
             endfor;
             dist[j] := v;
             farone[j] := vv;
           endfor;
           exitunless inc > 1;
         endfor;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
       else:
         for i=1 upto NL:
           farone[i] := i;
         endfor;
       fi;
       for i=NL downto 1:                    % draw all pathes
         j := farone[i];
         direc := rp( L[j]p1 )
         for k=2 upto npl[j]:
           --rp( L[j]p[k] )
         endfor;
         if CyclicLines:
           direc := direc--cycle;
         fi;
         ounum := Spread*ps( masscenter(npl[j],L[j]p), ThickenFactor );
         outerr := pencircle scaled ounum;
         innerr := pencircle scaled (0.8*ounum); %% DANGER %%
         pickup outerr;
         draw direc withcolor black;
         pickup innerr;
         draw direc withcolor background;
       endfor;
       pickup actualpen
     endgroup
   enddef;

% Now two routines to draw "bananas", well, sort of...
% Initially coded by Pedro J. Sebasti�o

   def circularsheet( expr CenterP, Rad, VecX, VecY, StartA, FinisA, Width ) =
     begingroup
       save vecz, ind;
       color vecz;
       numeric ind;
       vecz = ncrossprod( VecX, VecY );
       ( rp( CenterP + Width*vecz + Rad*planarrotation( VecX, VecY, StartA ) )
         for ind=StartA+1 upto FinisA:
           --rp( CenterP + Width*vecz + Rad*planarrotation( VecX, VecY, ind ))
         endfor
         for ind=FinisA downto StartA:
           --rp( CenterP + Rad*planarrotation( VecX, VecY, ind ) )
         endfor
         --cycle )
     endgroup
   enddef;

   def banana( expr CenterPos,  Radius, AngleColor, Wid, Amp  ) =
     begingroup
       save ind, sinbeta, cosbeta, aux, delta, angfvbx, angpos, angneg, au;
       save bx, by, bz, fv, beta, gamma, alfa;
       save outpath, outpathb, outpathc, visneg, vispos;
       numeric ind, sinbeta, cosbeta, aux, delta, angfvbx, angpos, angneg, au;
       numeric beta, gamma, alfa;
       color bx, by, bz, fv;
       path outpath, outpathb, outpathc;
       boolean visneg, vispos;

       alfa = X(AngleColor);
       beta = Y(AngleColor);
       gamma= Z(AngleColor);
       bx = eulerrotation( alfa, beta, gamma, red );
       by = eulerrotation( alfa, beta, gamma, green );
       bz = eulerrotation( alfa, beta, gamma, blue );

       au = cdotprod( f-CenterPos, by );
       if 0 > au:
         by := -by;
       fi;
       fv = cdotprod( f, bx )*bx + cdotprod( f, by )*by;
       aux = conorm( fv-CenterPos );
       if aux > Radius:
         cosbeta = Radius/aux;
         sinbeta = ( aux +-+ Radius )/aux;
         delta = angle( cosbeta, sinbeta );
         angfvbx = getangle( fv - CenterPos, bx );
         if 0 > cdotprod( fv - CenterPos, by ):
           angfvbx := -angfvbx;
         fi;
         angpos = delta + angfvbx;
         angneg = angfvbx - delta;
         if ( angneg > -Amp ) and ( Amp > angneg ):
           visneg = true;
         else:
           visneg = false;
         fi;
         if ( angpos > -Amp ) and ( Amp > angpos ):
           vispos = true;
         else:
           vispos = false;
         fi;
         if visneg and not vispos:
           outpath = circularsheet(CenterPos,Radius,bx,by,-Amp,angneg,Wid);
           unfill outpath;
           draw outpath;
           outpathb = circularsheet(CenterPos,Radius,bx,by,angneg,Amp,Wid);
           unfill outpathb;
           draw outpathb
         fi;
         if vispos and not visneg:
           outpath = circularsheet(CenterPos,Radius,bx,by,-Amp,angpos,Wid);
           unfill outpath;
           draw outpath;
           outpathb = circularsheet(CenterPos,Radius,bx,by,angpos,Amp,Wid);
           unfill outpathb;
           draw outpathb
         fi;
         if (not vispos) and (not visneg):
           outpath = circularsheet(CenterPos,Radius,bx,by,-Amp,Amp,Wid);
           unfill outpath;
           draw outpath;
         fi;
         if vispos and visneg:
           if 0 > cdotprod( f-CenterPos, bx ):
             outpath=circularsheet(CenterPos,Radius,bx,by,angneg,angpos,Wid);
             unfill outpath;
             draw outpath;
             outpathb = circularsheet(CenterPos,Radius,bx,by,-Amp,angneg,Wid);
             unfill outpathb;
             draw outpathb;
             outpathc = circularsheet(CenterPos,Radius,bx,by,angpos,Amp,Wid);
             unfill outpathc;
             draw outpathc;
           else:
             outpathb = circularsheet(CenterPos,Radius,bx,by,-Amp,angneg,Wid);
             unfill outpathb;
             draw outpathb;
             outpathc = circularsheet(CenterPos,Radius,bx,by,angpos,Amp,Wid);
             unfill outpathc;
             draw outpathc;
             outpath=circularsheet(CenterPos,Radius,bx,by,angneg,angpos,Wid);
             unfill outpath;
             draw outpath;
           fi;
         fi;
       else:
         outpath = circularsheet( CenterPos, Radius, bx, by, -Amp, Amp, Wid );
         unfill outpath;
         draw outpath;
       fi;
       draw rp(CenterPos+Radius*bx)--rp(CenterPos+Radius*bx+Wid*bz);
     endgroup
   enddef;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Plotting:

% Define and draw surfaces with a triangular mesh.
% On a hexagonal or triangular area. Without sorting (no need).

   def hexagonaltrimesh( expr BeHexa,theN,SideSize )( text SurFunc ) =
       begingroup
           save i, j, posx, posy, posz, higx, higy,
           counter, stepx, stepy, poi, lowx, lowy,
           newn, bola, bolb, bolc;
           numeric i, j, posx, posy, posz, higx, higy,
                   counter, stepx, stepy, lowx, lowy, newn;
           color poi[][];
           boolean bola, bolb, bolc;
           npf0 := 3;
           FCD0 := true; % this is used in the calls to fillfacewithlight
           ActuC := incr( ActuC );
           if ActuC > TableColors:
             ActuC := 1;
           fi;
           FC0  := ActuC;    %%
           counter = 0;
           stepy = SideSize/theN;
           stepx = 0.5*stepy*sqrt(3);
           lowy = -0.5*SideSize;
           lowx = -sqrt(3)*SideSize/6;
           higy = -lowy;
           higx = sqrt(3)*SideSize/3;
           for i=0 upto theN:
               for j=0 upto theN-i:
                   posx := lowx + i*stepx;
                   posy := lowy + i*stepx/sqrt(3) + j*stepy;
                   posz := SurFunc( posx, posy );
                   poi[i][j] := ( posx, posy, posz );
               endfor;
           endfor;
           if BeHexa:
               newn = round((theN+1)/3)+1;
           else:
               newn = 1;
           fi;
           for j=newn upto theN-newn+1:
               F0p1 := poi[0][j-1];
               F0p2 := poi[0][j];
               F0p3 := poi[1][j-1];
               fillfacewithlight( 0 );   % see below
           endfor;
           for i=1 upto theN-1:
               for j=1 upto theN-i:
                   bola := ( i < newn ) and ( j < newn-i );
                   bolb := ( i < newn ) and ( j > theN-newn+1 );
                   bolc := ( i > theN-newn );
                   if not ( bola or bolb or bolc ):
                       F0p1 := poi[i-1][j];
                       F0p2 := poi[i][j-1];
                       F0p3 := poi[i][j];
                       fillfacewithlight( 0 );
                       F0p1 := poi[i+1][j-1];
                       fillfacewithlight( 0 );
                   fi;
               endfor;
           endfor;
           i := theN-newn+1;
           for j=1 upto newn-1:
               F0p1 := poi[i-1][j];
               F0p2 := poi[i][j-1];
               F0p3 := poi[i][j];
               fillfacewithlight( 0 );
           endfor;
       endgroup
   enddef;

   def fillfacewithlight( expr FaceN ) =
     begingroup
       save perpvec, reflectio, viewvec, inciden, refpos, projincid;
       save shiftv, fcol, lcol, theangle, ghost, pa, pb, pc, j, lowcolor;
       color perpvec, reflectio, viewvec, inciden, refpos, projincid;
       color shiftv, fcol, lcol, pa, pb, pc, lowcolor;
       numeric theangle, j;
       path ghost;
       ghost := rp( F[FaceN]p1 )
         for j=2 upto npf[FaceN]:
           --rp( F[FaceN]p[j] )
         endfor
         --cycle;
       if OverRidePolyhedricColor:
         unfill ghost;
       else:
         refpos = masscenter( npf[FaceN], F[FaceN]p );
         pa = F[FaceN]p1;
         pb = F[FaceN]p2;
         pc = F[FaceN]p3;
         inciden = LightSource - refpos;
         viewvec = f - refpos;
         perpvec = ncrossprod( pa-pb, pb-pc );
         if cdotprod( perpvec, blue ) < 0:
           perpvec := -perpvec;
         fi;
         projincid = perpvec*cdotprod( perpvec, inciden );
         shiftv = inciden - projincid;
         reflectio = projincid - shiftv;
         theangle = getangle( reflectio, viewvec );
         if FCD[FaceN]:
           lowcolor = TableC[FC[FaceN]];
         else:
           lowcolor = TableC0;
         fi;
         lcol = (cosd( theangle ))[lowcolor,HigColor];
         if cdotprod( viewvec, perpvec ) < 0:
           fcol = lcol - SubColor;
         else:
           fcol = lcol;
         fi;
         fill ghost withcolor fcol;
       fi;
       draw ghost;
     endgroup
   enddef;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Part III (parametric plots and another renderer):
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%                                         %%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%   Kindly contributed by Jens Schwaiger  %%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%                                         %%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% dmin_ is the minimal distance of all faces to f;
% dmax_ is the maximal distance of all faces to f;
% (both values are determined in "draw_invisible")
% Facen is the number of the face to be filled;
% ColAtrib is the color used for filling;
% ColAtribone the color used for drawing;
% Colordensity depends on distance of the face from f

   def face_drawfill( expr Facen, dmin_, dmax_ ,ColAtrib, ColAtribone ) =
     begingroup
       save j, ptmp, colfac_, coltmp_;
       path ghost;
       numeric j, colfac_;
       color ptmp, coltmp_;
       ghost := rp( F[Facen]p1 );
       for j=2 upto npf[Facen]:
         ghost := ghost--rp( F[Facen]p[j] );
       endfor;
       ghost := ghost--cycle;
       ptmp:= masscenter( npf[Facen], F[Facen]p ) - f;
       % 0<=colfac_<=1
       colfac_ := ( conorm(ptmp)-dmin_ )/( dmax_ - dmin_ );
       % color should be brighter, if distance to f is smaller
       colfac_ := 1 - colfac_;
       % color should be not to dark, i.e., >=0.1 (and <=1)
       colfac_ := 0.9colfac_ + 0.1;
       % now filling and drawing with appropriate color;
       fill ghost withcolor colfac_*ColAtrib;
       draw ghost withcolor colfac_*ColAtribone;
     endgroup
   enddef;

% Now a much faster faces-only-ray-tracer based upon the unfill
% command and the constraint of non-intersecting faces of similar
% sizes. Faces are sorted by distance from nearest vertex or
% masscenter to the point of view. This routine may be used to
% draw graphs of 3D surfaces (use masscenters in this case).
%
% Option=true: test all vertices
% Option=false: test masscenters of faces
% DoJS=true: use face_drawfill by J. Schwaiger
% DoJS=false: use fillfacewithlight by L. Nobre G.
% ColAtrib=color for filling faces
% ColAtribone=color for drawing edges

   def draw_invisible( expr Option, DoJS, ColAtrib, ColAtribone ) =
     begingroup
       save  i, j, a, b, thisfar, ptmp, farone;
       numeric i, j, farone[], dist[], thisfar, distmin_, distmax_;
       color a, b, ptmp;
       for i=1 upto NF:                         % scan all faces
         if Option:                             % for distances of
           dist[i] = conorm( F[i]p1 - f );      % nearest vertices
           if i=1:
             distmin_ := dist1;                 % initialisation of
             distmax_ := dist1;                 % dmin_ and dmax_
           fi;
           distmin_ := min( distmin_, dist[i] );
           distmax_ := max( distmax_, dist[i] );
           for j=2 upto npf[i]:
             thisfar := conorm( F[i]p[j] - f );
             distmin_ := min( distmin_, thisfar );
             distmax_ := max( distmax_, thisfar );
             if thisfar < dist[i]:
               dist[i] := thisfar;
             fi;
           endfor;
         else:                        % for distances of centers of mass
           dist[i] := conorm( masscenter( npf[i], F[i]p ) - f );
           if i=1:
             distmin_ := dist1;       % initialisation of
             distmax_ := dist1;       % dmin_ and dmax_ in this case
           fi;
           distmin_ := min( distmin_, dist[i] );
           distmax_ := max( distmax_, dist[i] );
         fi;
         farone[i] = i;
       endfor;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Shell's Method of sorting %%%%%%%%
       save inc, v, vv;
       numeric inc, v, vv;
       inc = 1;
       forever:
         inc := 3*inc+1;
         exitif inc > NF;
       endfor;
       forever:
         inc := round(inc/3);
         for i=inc+1 upto NF:
           v := dist[i];
           vv:= farone[i];
           j := i;
           forever:
             exitunless dist[j-inc] > v;
             dist[j] := dist[j-inc];
             farone[j] := farone[j-inc];
             j := j-inc;
             exitif j <= inc;
           endfor;
           dist[j] := v;
           farone[j] := vv;
         endfor;
         exitunless inc > 1;
       endfor;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
       for i=NF downto 1:                     % draw and fill all pathes
         j := farone[i];
         if DoJS:
           face_drawfill( j, distmin_, distmax_, ColAtrib, ColAtribone );
         else:
           fillfacewithlight( j );
         fi;
       endfor;
     endgroup
   enddef;

% Move to the good range (-1,1).

   def bracket( expr low, poi, hig ) =
       begingroup
           save zout;
           numeric zout;
           zout = (2*poi-hig-low)/(hig-low);
           if zout > 1:
               zout := 1;
           fi;
           if zout < -1:
               zout := -1;
           fi;
           ( zout )
       endgroup
   enddef;

% Define parametric surfaces with a triangular mesh... unless a
% quadrangular mesh can do a fine, rigorous job just as well.

   def partrimesh( expr nt,ns,lowt,higt,lows,higs,lowx,higx,lowy,higy,lowz,higz,facz)( text parSurFunc ) =
     begingroup
       save i, j, k, posx, posy, posz;
       save counter, stept, steps, poss, post, tmpaux;
       save veca, vecb, vecc, vecd;
       numeric i, j, k, posx, posy, posz, counter, stept, steps;
       color poi[][], tmpaux, veca, vecb, vecc, vecd;
       counter := NF;                     % <-- NF must be initialized!
       ActuC := incr( ActuC );
       if ActuC > TableColors:
         ActuC := 1;
       fi;
       steps = ( higs - lows )/ns;
       stept = ( higt - lowt )/nt;
       for i=0 upto ns:
         for j=0 upto nt:
           poss := lows + i*steps;
           post := lowt + j*stept;
           tmpaux := parSurFunc( poss, post );
           posz := Z(tmpaux);
           posx := X(tmpaux);
           posy := Y(tmpaux);
           posx := bracket(lowx,posx,higx);
           posy := bracket(lowy,posy,higy);
           posz := bracket(lowz,posz,higz)/facz;
           poi[i][j] := ( posx, posy, posz );
         endfor;
       endfor;
       for i=1 upto ns:
         for j=1 step 1 until nt:
           veca := poi[i][j]-poi[i-1][j];
           vecb := poi[i][j]-poi[i-1][j-1];
           vecc := poi[i][j]-poi[i][j-1];
           if abs(cdotprod(ccrossprod(veca,vecb),vecc))<0.00005: %DANGER!
             counter := incr(counter);
             npf[counter] := 4;
             F[counter]p1 := poi[i-1][j-1];
             F[counter]p2 := poi[i-1][j];
             F[counter]p3 := poi[i][j];
             F[counter]p4 := poi[i][j-1];
             FC[counter] := ActuC;
             FCD[counter] := true;
           else:
             tmpaux:=
               0.25*(poi[i-1][j-1]+poi[i-1][j]+poi[i][j]+poi[i][j-1]);
             veca := poi[i-1][j-1]-tmpaux;
             vecb := poi[i-1][j]-tmpaux;
             vecc := poi[i][j]-tmpaux;
             vecd := poi[i][j-1]-tmpaux;
             if getangle(vecb,vecd)>getangle(veca,vecc):
               for k=-1 upto 0:
                 counter := incr(counter);
                 npf[counter] := 3;
                 F[counter]p1 := poi[i-1][j-1];
                 F[counter]p2 := poi[i+k][j-1-k];
                 F[counter]p3 := poi[i][j];
                 FC[counter] := ActuC;
                 FCD[counter] := true;
               endfor;
             else:
               for k=-1 upto 0:
                 counter := incr(counter);
                 npf[counter] := 3;
                 F[counter]p1 := poi[i+k][j+k];
                 F[counter]p2 := poi[i][j-1];
                 F[counter]p3 := poi[i-1][j];
                 FC[counter] := ActuC;
                 FCD[counter] := true;
               endfor;
             fi;
           fi;
         endfor;
       endfor;
       NF := counter;
     endgroup
   enddef;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Part IV (automatic perspective tuning and minimization):
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

   def randomfear =
     begingroup
       save a, b, c, i, h;
       numeric a, b, c, i, h;
       i = uniformdeviate( 360 );
       h = uniformdeviate( 180 );
       a = cosd( h )*cosd( i );
       b = sind( h )*cosd( i );
       c = sind( i );
       ( (a,b,c) )
     endgroup
   enddef;

   def renormalizevc( expr inF, inVC ) =
     begingroup
       save a;
       color a;
       cdotprod( inF, a ) = 0;
       inF - a = whatever*( inF - inVC );
       ( a )
     endgroup
   enddef;

   def calculatecost( expr TryF, TryVc, TrySp, TrySh ) =
     begingroup
       save sumsquares, i, difpair, xx, yy;
       numeric sumsquares, i, xx, yy;
       pair difpair;
       sumsquares = 0;
       f := TryF;
       viewcentr := TryVc;
       Spread := TrySp;
       ShiftV := TrySh;
       for i=1 upto PhotoMarks:
         difpair := 0.05*(rp(PhotoPoint[i]) - PhotoPair[i]); % DANGER
         xx := ((xpart difpair)**2)/PhotoMarks;
         yy := ((ypart difpair)**2)/PhotoMarks;
         sumsquares := sumsquares + xx + yy;
       endfor;
       ( sumsquares )
     endgroup
   enddef;

   def forcepointinsidefear( text A ) =
     begingroup
       if abs( X(A) ) > 0.5*MaxFearLimit :
         A := 0.5*A*MaxFearLimit/abs( X(A) );
       fi;
       if abs( Y(A) ) > 0.5*MaxFearLimit :
         A := 0.5*A*MaxFearLimit/abs( Y(A) );
       fi;
       if abs( Z(A) ) > 0.5*MaxFearLimit :
         A := 0.5*A*MaxFearLimit/abs( Z(A) );
       fi;
       ( A )
     endgroup
   enddef;

   def forcepairinsidepage( text A ) =
     begingroup
       if xpart A > 2*(xpart OriginProjPagePos):
         A := ( 2*(xpart OriginProjPagePos), ypart A );
       fi;
       if ypart A > 2*(ypart OriginProjPagePos):
         A := ( xpart A, 2*(ypart OriginProjPagePos) );
       fi;
       if xpart A < 0:
         A := ( 0, ypart A );
       fi;
       if ypart A < 0:
         A := ( xpart A, 0 );
       fi;
       ( A )
     endgroup
   enddef;

   def calculatejump( expr AverCost, PrevCost, RandCost, JumpLimit ) =
     begingroup
       save funfact, numer, denom;
       numeric funfact, numer, denom;
       if RandCost+PrevCost > 2*AverCost:
         numer := 3*PrevCost - 4*AverCost + RandCost;
         denom := 4*( RandCost + PrevCost - 2*AverCost );
         if abs( denom )*JumpLimit < abs( numer ):
           funfact := 0;
         else:
           funfact := numer/denom;
         fi;
       else:
         funfact := 0;
       fi;
       ( funfact )
     endgroup
   enddef;

   def photoreverse( expr IterNum, ExpTao, JumpFact ) =
     begingroup
       save j, auxvc, actfact, auxfac;
       save prevf, randf, averf, prevvc, randvc, avervc;
       save prevsp, randsp, aversp, prevsh, randsh, aversh;
       save prevcost, randcost, avercost, spreadlimit, expfact;
       numeric j, prevsp, randsp, aversp, actfact;
       numeric prevcost, randcost, avercost;
       numeric spreadlimit, expfact;
       color prevf, randf, averf, prevvc, randvc, avervc, auxvc;
       pair prevsh, randsh, aversh;
       spreadlimit = 20;                                         % DANGER
       prevf = f;
       prevvc= viewcentr;
       prevsp= Spread;
       prevsh= ShiftV;
       prevcost = calculatecost( prevf, prevvc, prevsp, prevsh );
       show prevcost;
       auxfac = -1280.0/ExpTao/IterNum;
       for j=0 upto IterNum:
         expfact := mexp(auxfac*j);
         randf := prevf + expfact*randomfear;
         randcost := calculatecost( randf, prevvc, prevsp, prevsh );
         averf := 0.5[prevf,randf];
         avercost := calculatecost( averf, prevvc, prevsp, prevsh );
         actfact := calculatejump(avercost,prevcost,randcost,JumpFact);
         prevf := actfact[prevf,randf];
         auxvc := prevvc + expfact*randomfear;
         randvc:= renormalizevc( randf, auxvc );
         randcost := calculatecost( prevf, randvc, prevsp, prevsh );
         auxvc := 0.5[prevvc,randvc];
         avervc:= renormalizevc( averf, auxvc );
         avercost := calculatecost( prevf, avervc, prevsp, prevsh );
         actfact := calculatejump(avercost,prevcost,randcost,JumpFact);
         auxvc := actfact[prevvc,randvc];
         prevvc:= renormalizevc( prevf, auxvc );
         randsp:= prevsp+expfact*spreadlimit*(uniformdeviate( 1 ) - 0.5);
         randcost := calculatecost( prevf, prevvc, randsp, prevsh );
         aversp:= 0.5[prevsp,randsp];
         avercost := calculatecost( prevf, prevvc, aversp, prevsh );
         actfact := calculatejump(avercost,prevcost,randcost,JumpFact);
         prevsp:= actfact[prevsp,randsp];
         randsh:= prevsh + expfact*dir( uniformdeviate( 360 ) ); % DANGER
         randcost := calculatecost( prevf, prevvc, prevsp, randsh );
         aversh:= 0.5[prevsh,randsh];
         avercost := calculatecost( prevf, prevvc, prevsp, aversh );
         actfact := calculatejump(avercost,prevcost,randcost,JumpFact);
         prevsh:= actfact[prevsh,randsh];
         prevcost := calculatecost( prevf, prevvc, prevsp, prevsh );
         %show (prevcost,expfact);
       endfor;
       show prevcost;
     endgroup
   enddef;

% Minimization routine for scalar functions like y=f(x) where an initial
% triplet (x1,x2,x3) with x1<x2<x3 is given as a parabolic squeleton that
% provides a way to search for the smallest value of y (if iterated)

   def minimizestep( expr Abcisscolor )( text PlainFunc ) =
     begingroup
       save xa, xb, xc, xd, ya, yb, yc, yd, aux, coeb, coec, den;
       save colout;
       numeric xa, xb, xc, xd, ya, yb, yc, yd, aux, coeb, coec, den;
       color colout;
       xa = X( Abcisscolor );
       xb = Y( Abcisscolor );
       xc = Z( Abcisscolor );
       ya = PlainFunc(xa);
       yb = PlainFunc(xb);
       yc = PlainFunc(xc);
       if ya = yb:
         colout = (-0.125[xa,xb],xb,xc);
       elseif yb = yc:
         colout = (xa,xb,1.125[xb,xc]);
       else:
         if (yb>ya) or (yb>yc):
           show  Abcisscolor;
           message "                             Unable to minimizestep!";
         fi;
         den = (xb-xc)*((xa**2)-(xb**2))-(xa-xb)*((xb**2)-(xc**2));
         if abs(den) < 0.0005:
           show den;
           message "                             Unable to minimizestep!";
         fi;
         coeb = ((yb-yc)*((xa**2)-(xb**2))-(ya-yb)*((xb**2)-(xc**2)))/den;
         coec = ((xb-xc)*(ya-yb)-(xa-xb)*(yb-yc))/den;
         xd = -0.5*coeb/coec;
         yd = PlainFunc( xd );
         if ((xa<xd) and (xd<xb)):
           if (yd<yb):
             colout = (xa,xd,xb);
           else:
             colout = (xd,xb,xc);
           fi;
         elseif ((xb<xd) and (xd<xc)):
           if (yd<yb):
             colout = (xb,xd,xc);
           else:
             colout = (xa,xb,xd);
           fi;
         else:
           aux := 0.125[xb,xc]-0.125[xb,xa];
           colout = (xa,0.125[xb,xa]+uniformdeviate(aux),xc);
         fi;
       fi;
       ( colout )
     endgroup
   enddef;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Part V:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Intersections:
%%%%%%%%%%%%%%%%%%%%%%%%%%%% Check lineintersectplan above %%%%%%%%%%%%%%%

   def calculatecostver(expr VerA,DisA,VerB,DisB,VerC,DisC,TryV) =
     begingroup
       save a, b, c;
       numeric a, b, c;
       a = conorm( TryV - VerA ) - DisA;
       b = conorm( TryV - VerB ) - DisB;
       c = conorm( TryV - VerC ) - DisC;
       ( ( a ++ b ++ c )**2 )
     endgroup
   enddef;

% Approximation of the intersection of three spheres.
% Be aware of the next three danger parameters.

   def improvertex( expr VerA, DisA, VerB, DisB, VerC, DisC, IniV ) =
     begingroup
       save j, iternum, prevcost, factry, actpoi, actfact, auxa, auxb;
       save jumplim, trypoi, halfpo, expfact, randcost, avercost, auxc;
       numeric j, iternum, prevcost, factry, randcost, avercost, actfact;
       numeric jumplim, expfact;
       color actpoi, trypoi, halfpo, auxa, auxb, auxc;
       trypoi = IniV;
       iternum = 24;                                        % DANGER!
       factry = 0.25;                                       % DANGER!
       jumplim = 2.5;                                       % DANGER!
       prevcost = calculatecostver(VerA,DisA,VerB,DisB,VerC,DisC,trypoi);
       message "improvertex initial cost: " & decimal(prevcost)
                                            & " at point " & cstr(trypoi);
       for j=1 upto 5:
         auxa := VerA-trypoi;
         auxa := N(auxa)*(conorm(auxa)-DisA);
         auxb := VerB-trypoi;
         auxb := N(auxb)*(conorm(auxb)-DisB);
         auxc := VerC-trypoi;
         auxc := N(auxc)*(conorm(auxc)-DisC);
         trypoi := trypoi+factry*(auxa+auxb+auxc);
       endfor;
       prevcost := calculatecostver(VerA,DisA,VerB,DisB,VerC,DisC,trypoi);
       message "phase one cost: " & decimal(prevcost)
                                  & " at point " & cstr(trypoi);
       for j=0 upto iternum:
         expfact := mexp(-j*200/iternum);                    % DANGER!
         actpoi := trypoi+factry*expfact*randomfear;
         randcost :=
           calculatecostver(VerA,DisA,VerB,DisB,VerC,DisC,actpoi);
         halfpo := 0.5[trypoi,actpoi];
         avercost :=
           calculatecostver(VerA,DisA,VerB,DisB,VerC,DisC,halfpo);
         actfact := calculatejump(avercost,prevcost,randcost,jumplim);
         trypoi := actfact[trypoi,actpoi];
         prevcost :=
           calculatecostver(VerA,DisA,VerB,DisB,VerC,DisC,trypoi);
%        show prevcost;
       endfor;
       message "final cost: " & decimal(prevcost)
                              & " at point " & cstr(trypoi);
       ( trypoi )
     endgroup;
   enddef;

% The approximation of the intersection of a plane, an infinite cylinder (tube)
% and a spheroid (an oblate spheroid, a prolate spheroid or a perfect sphere)

   def ultraimprovertex( expr PlanPoi, PlanDir, BaseCenter, Radius, LenVec,
       CentrPoi, NorthPoleVec, Ray, IniV ) =
       begingroup
       save trypoi, auxa, auxb, auxc, auxd, plandi, cyldi, factry, focn, focs;
       save norpoldi, major, majortmp, nordi, tmpdi;
       color trypoi, auxa, auxb, auxc, auxd, plandi, cyldi, focn, focs;
       color norpoldi, nordi, tmpdi;
       numeric factry, major, majortmp;
       trypoi = IniV;
       factry = 0.25;
       plandi = N(PlanDir);
       cyldi = N(LenVec);
       norpoldi = N(NorthPoleVec);
       major = conorm(NorthPoleVec);
       if major>Ray:
         focn := CentrPoi+(major+-+Ray)*norpoldi;
         focs := CentrPoi-(major+-+Ray)*norpoldi;
       fi;
       for j=1 upto 50:
         if major<Ray:
           tmpdi := trypoi-CentrPoi;
           nordi := N(tmpdi-cdotprod(tmpdi,norpoldi)*norpoldi);
           focn := CentrPoi+(Ray+-+major)*nordi;
           focs := CentrPoi-(Ray+-+major)*nordi;
         elseif major=Ray:
           focn := CentrPoi;
           focs := CentrPoi;
         fi;
         auxa := plandi*cdotprod(PlanPoi-trypoi,plandi);
         auxb := cyldi*cdotprod(trypoi-BaseCenter,cyldi)-trypoi+BaseCenter;
         auxb := N(auxb)*(conorm(auxb)-Radius);
         auxc := focn-trypoi;
         auxd := focs-trypoi;
         if major>Ray:
           auxc := (N(auxc)+N(auxd))*(conorm(auxc)+conorm(auxd)-2*major);
         else:
           auxc := (N(auxc)+N(auxd))*(conorm(auxc)+conorm(auxd)-2*Ray);
         fi;
         trypoi := trypoi+factry*(auxa+auxb+auxc);
       endfor;
       ( trypoi )
     endgroup
   enddef;

% The approximation of the intersection of a plane and two prolate spheroids

   def necplusimprovertex( expr PlanPoi, PlanDir,
       CentrPoiA, NorthPoleVecA, RayA,
       CentrPoiB, NorthPoleVecB, RayB, IniV ) =
       begingroup
       save trypoi, auxa, auxb, auxc, auxd, plandi, factry, focni, focsi;
       save norpoldi, norpoldj, maior, major, focnj, focsj, auxe;
       color trypoi, auxa, auxb, auxc, auxd, plandi, focni, focsi, focnj, focsj;
       color norpoldi, norpoldj, auxe;
       numeric factry, maior, major;
       trypoi = IniV;
       factry = 0.25;
       plandi = N(PlanDir);
       norpoldi = N(NorthPoleVecA);
       maior = conorm(NorthPoleVecA);
       norpoldj = N(NorthPoleVecB);
       major = conorm(NorthPoleVecB);
       focni = CentrPoiA+(maior+-+RayA)*norpoldi;
       focsi = CentrPoiA-(maior+-+RayA)*norpoldi;
       focnj = CentrPoiB+(major+-+RayB)*norpoldj;
       focsj = CentrPoiB-(major+-+RayB)*norpoldj;
       for j=1 upto 50:
         auxa := plandi*cdotprod(PlanPoi-trypoi,plandi);
         auxb := focni-trypoi;
         auxe := focsi-trypoi;
         auxc := focnj-trypoi;
         auxd := focsj-trypoi;
         auxb := (N(auxb)+N(auxe))*(conorm(auxb)+conorm(auxe)-2*maior);
         auxc := (N(auxc)+N(auxd))*(conorm(auxc)+conorm(auxd)-2*major);
         trypoi := trypoi+factry*(auxa+auxb+auxc);
       endfor;
       ( trypoi )
     endgroup
   enddef;

% The approximation of the intersection of a straight line and
% one prolate spheroid

   def intersectprolatespheroid( expr CentrPoi, NorthPoleVec, Ray,
       LinePoi, LineDir ) =
       begingroup
       save trypoi, factry, linedi, norpol, focn, focs, maior, j;
       save auxa, auxb, auxc, auxd;
       color trypoi, linedi, norpol, focn, focs;
       color auxa, auxb, auxc, auxd;
       numeric factry, maior, j;
       trypoi = LinePoi;
       factry = 0.25;
       linedi = N(LineDir);
       norpol = N(NorthPoleVec);
       maior = conorm(NorthPoleVec);
       focn = CentrPoi+(maior+-+Ray)*norpol;
       focs = CentrPoi-(maior+-+Ray)*norpol;
       for j=1 upto 50:
         auxb := focn-trypoi;
         auxd := focs-trypoi;
         auxa := (N(auxb)+N(auxd))*(conorm(auxb)+conorm(auxd)-2*maior);
         auxc := linedi*cdotprod(auxa,linedi);
         trypoi := trypoi+factry*auxc;
       endfor;
       ( trypoi )
     endgroup
   enddef;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Part VI (strictly two-dimensional):
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Verify if a path is cyclic (written by Scott Pakin)

   def is_cyclic expr cpath =
     (point 0 of cpath = point (length cpath) of cpath)
   enddef;

% Produce the schematics of a spring.

   def springpath( expr begp, endp, piturnum, piturnproj, spgfrac ) =
       begingroup
           boolean leftside;
           numeric counter, springwidth;
           pair stepdir, leftdir, rightdir, auxil, lastp, endvec;
           path thespring;
           leftside = true;
           stepdir = spgfrac*(endp-begp)/piturnum;
           endvec = (1-spgfrac)*(endp-begp)/2;
           springwidth = piturnproj +-+ abs( stepdir );
           auxil = ( -ypart stepdir, xpart stepdir );
           leftdir = springwidth*unitvector( auxil );
           auxil := ( ypart stepdir, -xpart stepdir );
           rightdir = springwidth*unitvector( auxil );
           leftdir := (stepdir + leftdir)/2;
           rightdir := (stepdir + rightdir)/2;
           lastp = begp+endvec;
           thespring := begp--lastp;
           for counter=1 upto piturnum:
               if leftside:
                   auxil := lastp + leftdir;
               else:
                   auxil := lastp + rightdir;
               fi;
               lastp := lastp + stepdir;
               thespring := thespring--auxil--lastp;
               leftside := not leftside;
           endfor;
           thespring := thespring--endp;
           ( thespring )
       endgroup
   enddef;

% Summarize a great length in a zig-zag frontier line

   def zigzagfrontier( expr begp, endp, nzigs, dev, zthick, tthick, fthick, excol, incol ) =
       begingroup
           interim linecap := squared;
           interim linejoin := mitered;
           boolean leftside;
           numeric counter, tmpval;
           pair stepdir, leftdir, rightdir, auxil, lastp;
           path thefrontier;
           leftside = true;
           stepdir = (endp-begp)/nzigs;
           leftdir = unitvector( ( -ypart stepdir, xpart stepdir ) );
           rightdir = unitvector( ( ypart stepdir, -xpart stepdir ) );
           thefrontier = begp;
           lastp = begp;
           for counter=1 upto nzigs-1:
               lastp := lastp + stepdir;
               tmpval := zthick+normaldeviate*dev;
               if leftside:
                   auxil := lastp + leftdir*tmpval;
               else:
                   auxil := lastp + rightdir*tmpval;
               fi;
               thefrontier := thefrontier--auxil;
               leftside := not leftside;
           endfor;
           thefrontier := thefrontier--endp;
           draw
             thefrontier withcolor excol withpen pencircle scaled tthick;
           draw
             thefrontier withcolor incol withpen pencircle scaled fthick;
           ( thefrontier )
       endgroup
   enddef;

% The name says it all.

   def randomcirc( expr radi, stddev, numpois ) =
       begingroup
           numeric i, astep;
           path ranc;
           astep = 360/numpois;
           ranc = (radi+stddev*normaldeviate)*dir(-180);
           for i= -180+astep step astep until 180:
               ranc := ranc--((radi+stddev*normaldeviate)*dir(i));
           endfor;
           ranc := ranc--cycle;
           ( ranc )
       endgroup
   enddef;

% Just label some 2D-place in a way similar to anglinen.

   def labeln(expr S, Pos, RelPos) =
       begingroup
           if RelPos = 0:
               label.rt( S, Pos );
           elseif RelPos =1:
               label.urt( S, Pos );
           elseif RelPos =2:
               label.top( S, Pos );
           elseif RelPos =3:
               label.ulft( S, Pos );
           elseif RelPos =4:
               label.lft( S, Pos );
           elseif RelPos =5:
               label.llft( S, Pos );
           elseif RelPos =6:
               label.bot( S, Pos );
           elseif RelPos =7:
               label.lrt( S, Pos );
           else:
               label( S, Pos );
           fi
       endgroup
   enddef;

% There must be a sort of planar cross-product. The z coordinate.

   def paircrossprod(expr A, B) =
       ( (xpart A)*(ypart B) - (xpart B)*(ypart A) )
   enddef;

% Just dot-label some 2D-place in a random way.

   def dotlabelrand(expr S, Pos ) =
       begingroup
           save RelPos;
           numeric RelPos;
           RelPos = floor( uniformdeviate(9) );
           if RelPos = 0:
               dotlabel.rt( S, Pos );
           elseif RelPos =1:
               dotlabel.urt( S, Pos );
           elseif RelPos =2:
               dotlabel.top( S, Pos );
           elseif RelPos =3:
               dotlabel.ulft( S, Pos );
           elseif RelPos =4:
               dotlabel.lft( S, Pos );
           elseif RelPos =5:
               dotlabel.llft( S, Pos );
           elseif RelPos =6:
               dotlabel.bot( S, Pos );
           elseif RelPos =7:
               dotlabel.lrt( S, Pos );
           else:
               dotlabel( S, Pos );
           fi
       endgroup
   enddef;

   def radialcross( expr A, la, B, lb, GoUp) =
     begingroup
       numeric x, y, xa, xb, ya, yb, YM, YA, La, Lb;
       numeric AA, BB, CC, auxil, na, nb, norm;
       pair As, Bs, selectedpoint;
       na = abs(A);
       nb = abs(B);
       norm := 0;
       for t = na, nb, la, lb:
         if norm < t:
           norm := t;
         fi;
       endfor;
       xa = xpart A/norm;
       xb = xpart B/norm;
       ya = ypart A/norm;
       yb = ypart B/norm;
       La = la/norm;
       Lb = lb/norm;
       if abs( ya - yb ) < 0.005 :
         x := La**2 - Lb**2 + xb**2 - xa**2;
         x := 0.5*x/( xb - xa );
         auxil := La +-+ (xa-x);
         As  = ( x, ya + auxil );
         Bs = ( x, ya - auxil );
       else:
         YM := (xb-xa)/(ya-yb);
         YA := Lb**2 - La**2 + xa**2 - xb**2;
         YA := 0.5*( YA - (ya-yb)**2 )/(ya-yb);
         AA := 1 + YM**2;
         BB := 2*( YM*YA - xa );
         CC := xa**2 - La**2 + YA**2;
         CC := sqrt( BB**2 - 4*AA*CC );
         x  := -0.5*( BB + CC )/AA;
         y  := YA + ya + YM*x;
         Bs = ( x, y );
         x  := -0.5*( BB - CC )/AA;
         y  := YA + ya + YM*x;
         As = ( x, y );
       fi;
       if ypart As > ypart Bs:
         if GoUp:
           selectedpoint = As;
         else:
           selectedpoint = Bs;
         fi;
       elseif ypart As = ypart Bs:
         if xpart As > xpart Bs:
           if GoUp:
             selectedpoint = As;
           else:
             selectedpoint = Bs;
           fi;
         else:
           if GoUp:
             selectedpoint = Bs;
           else:
             selectedpoint = As;
           fi;
         fi;
       else:
         if GoUp:
           selectedpoint = Bs;
         else:
           selectedpoint = As;
         fi;
       fi;
       ( norm*selectedpoint )
     endgroup
   enddef;

   def ropethread( expr Index ) =
     begingroup
       save aux;
       numeric aux;
       if Index > RopeColors:
         aux = 0;
       else:
         aux = Index;
       fi;
       ( aux )
     endgroup
   enddef;

   def ropepattern( expr BasePath, RopeWidth, Nturns ) =
     begingroup
       save indturns, nmoves, indthread, movelen, turnlen, totlen;
       numeric indturns, nmoves, indthread, movelen, turnlen, totlen;
       save lenpos, timar, steplen, indstep, startdownc, startupcol;
       numeric lenpos, timar, steplen, startdownc, indstep;
       save actuc, actdc, stepwidth;
       numeric actuc, actdc, stepwidth, startupcol;
       save p;
       pair p[];
       save actcolor;
       color actcolor;
       nmoves = 2*(RopeColors+1);
       totlen = arclength BasePath;
       turnlen = totlen/Nturns;
       movelen = turnlen/nmoves;
       steplen = movelen/2;
       startdownc = 0;
       startupcol = RopeColors;
       stepwidth = RopeWidth/RopeColors;
       for indturns=0 upto Nturns-1:
         for indmove=0 upto nmoves-1:
           for indstep=0 upto 3:
             lenpos :=
               indturns*turnlen+indmove*movelen+indstep*steplen;
             timar := arctime lenpos of BasePath;
             p[indstep] := direction timar of BasePath rotated 90;
             p[indstep] := unitvector( p[indstep] );
             p[indstep+4] := point timar of BasePath;
           endfor;
           actdc := startdownc;
           for indthread=0 upto RopeColors:
             p8 := p5-p1*(0.5*RopeWidth-(indthread-0.5)*stepwidth);
             p9 := p4-p0*(0.5*RopeWidth-indthread*stepwidth);
             p10:= p5-p1*(0.5*RopeWidth-(indthread+0.5)*stepwidth);
             p11:= p6-p2*(0.5*RopeWidth-indthread*stepwidth);
             actcolor := TableC[RopeColorSeq[actdc]];
             fill p8--p9--p10--p11--cycle withcolor actcolor;
             actdc := ropethread( incr( actdc ) );
           endfor;
           startdownc := ropethread( incr( startdownc ) );
           actuc := startupcol;
           p9 := p5+p1*0.5*(RopeWidth+stepwidth);
           p10:= p6+p2*0.5*RopeWidth;
           p11:= p7+p3*0.5*(RopeWidth+stepwidth);
           actcolor := TableC[RopeColorSeq[actuc]];
           fill p9--p10--p11--cycle withcolor actcolor;
           actuc := ropethread( incr( actuc ) );
           for indthread=0 upto RopeColors-1:
             p8 := p6+p2*(0.5*RopeWidth-indthread*stepwidth);
             p9 := p5+p1*(0.5*RopeWidth-(indthread+0.5)*stepwidth);
             p10:= p6+p2*(0.5*RopeWidth-(indthread+1)*stepwidth);
             p11:= p7+p3*(0.5*RopeWidth-(indthread+0.5)*stepwidth);
             actcolor := TableC[RopeColorSeq[actuc]];
             fill p8--p9--p10--p11--cycle withcolor actcolor;
             actuc := ropethread( incr( actuc ) );
           endfor;
           p8 := p6-p2*0.5*RopeWidth;
           p9 := p5-0.5*p1*(RopeWidth+stepwidth);
           p11:= p7-0.5*p3*(RopeWidth+stepwidth);
           actcolor := TableC[RopeColorSeq[actuc]];
           fill p8--p9--p11--cycle withcolor actcolor;
           startupcol := ropethread( incr( startupcol ) );
         endfor;
       endfor
     endgroup
   enddef;

   def firsttangencypoint( expr Path, Point, ResolvN ) =
     begingroup
       save auxp, i, cutp, va, vb;
       path auxp;
       numeric i;
       pair cutp, va, vb;
       auxp =
         hide( va := unitvector( point 0 of Path - Point );
         vb := unitvector( direction 0 of Path ); )
         ( paircrossprod( va, vb ), 0 )
         for i=1/ResolvN step 1/ResolvN until length Path:
         hide( va := unitvector( point i of Path - Point );
           vb := unitvector( direction i of Path ); )
           ...( paircrossprod( va, vb ), i )
       endfor;
       cutp = auxp intersectionpoint ( origin--( 0, length Path ) );
       ( point ( ypart cutp ) of Path )
     endgroup
   enddef;

% Shrink or swell a cyclic path without cusp points and without
% coinciding pre and post control points.

   def lasermachine( expr DefinedPath, Beam, CosLimit ) =
     begingroup
       save patlen, j;
       save apoi, bpoi, cpoi, dpoi, epoi;
       save apat, bpat, cpat, dpat, a, b, c;
       save anew, bnew, cnew;
       save aang, bang, cang, dang;
       save newp, pairvector, cou, val;
       numeric patlen, j;
       pair apoi, bpoi, cpoi, dpoi, epoi;
       pair apat, bpat, cpat, dpat, a, b, c;
       pair anew, bnew, cnew, pairvector[];
       numeric aang, bang, cang, dang, cou, val;
       path newp;
       patlen = length DefinedPath;
       cou = 0;
       for j=0 upto patlen-1:
         apoi := precontrol j of DefinedPath;
         bpoi := point j of DefinedPath;
         cpoi := postcontrol j of DefinedPath;
         dpoi := precontrol j+1 of DefinedPath;
         epoi := point j+1 of DefinedPath;
         apat := apoi-bpoi;
         bpat := bpoi-cpoi;
         cpat := cpoi-dpoi;
         dpat := dpoi-epoi;
         aang := angle( apat );
         bang := angle( bpat );
         cang := angle( cpat );
         dang := angle( dpat );
         val := cosd( 0.5*(aang-bang) );
         bnew := cpoi+Beam*dir( 90+0.5*(bang+cang) )/cosd( 0.5*(bang-cang) );
         cnew := dpoi+Beam*dir( 90+0.5*(cang+dang) )/cosd( 0.5*(cang-dang) );
         if ( val > 0 ) and
           ( val < CosLimit ) and
             ( Beam*sind( 0.5*(aang-bang) ) < 0 ):

             a := bpoi+Beam*dir(90+aang);
           b := a+0.5*Beam*dir(aang); %%% DANGER %%%%%0.5%%%%%%%
           %draw b withpen pencircle scaled 3pt withcolor green;
           anew := bpoi+Beam*dir(90+bang);
           c := anew-0.5*Beam*dir(bang); %%% DANGER %%0.5%%%%%%%
           %draw c withpen pencircle scaled 3pt withcolor blue;
           pairvector[3*cou] = a;
           pairvector[3*cou+1] = b;
           pairvector[3*cou+2] = c;
           cou := incr(cou);
           pairvector[3*cou] = anew;
           pairvector[3*cou+1] = bnew;
           pairvector[3*cou+2] = cnew;
           cou := incr(cou);
         else:
           anew := bpoi+Beam*dir( 90+0.5*(aang+bang) )/val;
           pairvector[3*cou] = anew;
           pairvector[3*cou+1] = bnew;
           pairvector[3*cou+2] = cnew;
           cou := incr(cou);
         fi;
       endfor;
       newp = for j=0 upto cou-1:
           pairvector[3*j]..controls pairvector[3*j+1] and pairvector[3*j+2]..
       endfor cycle;
       ( newp )
     endgroup
   enddef;

% Move the starting point of a cyclic path along that path

   def startahead( expr DefinedPath, JumpTime ) =
     begingroup
       save patlen, j;
       save apoi, bpoi, cpoi;
       save newp;
       numeric patlen, j;
       pair apoi, bpoi, cpoi;
       path newp;
       patlen = length DefinedPath;
       newp = for j=0 upto patlen-1:
           hide(
             apoi := point JumpTime+j of DefinedPath;
           bpoi := postcontrol JumpTime+j of DefinedPath;
           cpoi := precontrol JumpTime+j+1 of DefinedPath;
           )
           apoi..controls bpoi and cpoi..
       endfor
       cycle;
       ( newp )
     endgroup
   enddef;

% In order to use a "lasermachine" one needs a single full outline.
% One may have two somewhat concentric cyclic paths intersecting in
% several points.
% The next routine may help but first the paths must be adapted with
% "startahead" and/or "reverse" so that they both rotate in the same
% direction and they start on consecutive "lobes" (hard to explain).
% Now pay attention: given the direction of rotation (clockwise or
% counter-clockwise) the SecondPath must start BEFORE the FirstPath.
% And another problem: there must be at least four intersection points.
% Very nasty routine. All because of finispoi...

   def crossingline( expr FirstPath, SecondPath, TimeTolerance ) =
     begingroup
       save m;
       save its, finispoi;
       save increm, fo, ma, tmpp, mastarter;
       numeric m;
       pair its, finispoi;
       path increm, fo, ma, tmpp, mastarter;
       m = TimeTolerance;
       fo = FirstPath;
       ma = SecondPath;
       its := fo intersectiontimes ma;
       increm := subpath (m, (xpart its) - m ) of fo;
       mastarter = subpath (m, (ypart its) - m ) of ma;
       finispoi = reverse ma intersectionpoint reverse fo;

       forever:

         fo := subpath ( (xpart its)+m, length fo ) of fo;
         ma := subpath ( (ypart its)+m, length ma ) of ma;
         its := ma intersectiontimes fo;
         tmpp := subpath ( m, (xpart its)-m ) of ma;
         increm := increm...tmpp;

         fo := subpath ( (ypart its)+m, length fo ) of fo;
         ma := subpath ( (xpart its)+m, length ma ) of ma;
         its := fo intersectiontimes ma;
         tmpp := subpath ( m, (xpart its)-m ) of fo;
         increm := increm...tmpp;

         exitif abs( point (xpart its) of fo - finispoi ) < m;
       endfor;

       fo := subpath ( (xpart its)+m, length fo ) of fo;
       ma := (subpath ( (ypart its)+m, (length ma)-m ) of ma)...mastarter;
       its := ma intersectiontimes fo;
       tmpp := subpath ( m, (xpart its)-m ) of ma;
       increm := increm...tmpp;

       tmpp := subpath ( (ypart its)+m, (length fo)-m ) of fo;
       ( increm...tmpp...cycle )
     endgroup
   enddef;

% Calculate path areas (contributed by Boguslaw Jackowski
% to the metapost mailing list)

vardef segmentarea( expr Ps ) =
  save xa, xb, xc, xd, ya, yb, yc, yd;
  ( xa, 20ya ) = point 0 of Ps;
  ( xb, 20yb ) = postcontrol 0 of Ps;
  ( xc, 20yc ) = precontrol 1 of Ps;
  ( xd, 20yd ) = point 1 of Ps;
  ( xb - xa )*( 10ya + 6yb + 3yc +   yd )
    + ( xc - xb )*( 4ya + 6yb + 6yc +  4yd )
    + ( xd - xc )*( ya + 3yb + 6yc + 10yd )
enddef;

vardef cyclicpatharea( expr P ) = % result = area of the interior
  segmentarea(subpath (0,1) of P)
    for t=1 upto length(P)-1: + segmentarea(subpath (t,t+1) of P) endfor
enddef;

% Mark bidimensional angles (contributed by Palle J�rgensen
% to the metapost mailing list)

vardef archangle@#( expr _p, _q, _s, archwidth ) text _t =
  begingroup;
    save _a, _b, _w, _arch, _halfangle, _label_origin;
    ( _a, _b ) = _p intersectiontimes _q;
    pair _w;
    _w = whatever[
      point _a of _p +
      archwidth * unitvector direction _a of _p,
      point _a of _p +
      archwidth * unitvector direction _a of _p +
      (ypart.direction _a of _p, -xpart.direction _a of _p)
      ];
    _w  = whatever[point _b of _q, point _b of _q + direction _b of _q];
    path _arch;
    _arch = point _a of _p +
      archwidth * unitvector direction _a of _p{
      (if direction _a of _p dotprod unitvector direction _b of _q > 0:
        1
    else:
      -1
    fi) *
  ( _w - (point _a of _p + archwidth * unitvector direction _a of _p) )
    }..point _b of _q +
    archwidth * unitvector direction _b of _q;
  draw _arch _t;
  path _halfangle;
  _halfangle = point _a of _p - 2*archwidth*
    unitvector( direction _a of _p + direction _b of _q )--point _a of _p +
    2*archwidth*unitvector( direction _a of _p + direction _b of _q );
  pair _label_origin;
  _label_origin = _halfangle intersectionpoint _arch;
  label@#( _s, _label_origin ) _t;
endgroup;
enddef;

% EOF