% 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