% - This is ps3d.inc ---------------------------------- % - It requires matrix.inc to be loaded first % - and will give an error message if it is not. % - Copyright Bill Casselman, November 13, 1998 ------- % - Thanks to Jim Blinn's book `A trip down the graphics pipeline' % - for several suggestions that (I hope) made this code cleaner % - Table of contents -------------------------------------------- % gsave3d % grestore3d % origin % set-display % translate3d % scale3d % rotate3d % reflect3d % transform3d % dual-transform3d % moveto3d % lineto3d % curveto3d % mkpath3d % mkpolypth3d % plainarrow3d % ---------------------------------------------------------------- /matrixinc where { pop } { (\n) print (WARNING from ps3d.inc: you must load matrix.inc\n) print (\n) print quit } ifelse % - Defining PostScript commands' equivalents --------- /ps3ddict 16 dict def ps3ddict begin /LINETO { lineto } bind def /MOVETO { moveto } bind def /CLOSEPATH { closepath } bind def /CURVETO { curveto } bind def end % - Coordinates in three dimensions ------------------- % There are three steps to drawing something in 3D: % 1. Converting from user 3d coords to default 3d coords % 2. Projecting onto (x, y)-plane % 3. Drawing the image on that plane % These are more or less independent. % The last step is completely controlled by the usual PS stuff. % - Initialize and manipulate the 3d gstack ----------- /gmax 18 def % gstack = [n [T1 D1] [T2 D2] ... [T(gmax) D(gmax)] ] % n = gstack3d[0] % the ctm is at gstack[n] % the dual ctm is at gstack[n+1] /gstack3d gmax 1 add array def gstack3d 0 1 put % start with orthogonal projection gstack3d 1 [ [1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1] [1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1] ] put /gsave3d { 1 dict begin /n gstack3d 0 get def n gmax eq { (3d graphics stack overflow!) == quit } if gstack3d n 1 add gstack3d n get put gstack3d 0 n 1 add put end } def /grestore3d { gstack3d 0 gstack3d 0 get dup 2 lt { (3d graphics stack underflow!) == quit } if 1 sub put } def % [T T*]: sets ctm3d = [T T*] /gset3d { gstack3d dup % [T T*] g g 0 get % [T T*] g n 3 2 roll % g n [T T*] put } def % => [T T*] /ctm3d { gstack3d dup 0 get get } def /currentpoint3d { cpt3d ctm3d 1 get transform3d aload pop % x y z w pop % should be 1 } def % - Sets up projection into 2D ------------------- /display-matrix [1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1] def % O = [0 0 x 0] (x > 0) or [0 0 a 1] (a > 0) /set-display { 3 dict begin /O exch def /a O 2 get def /x O 3 get def [a 0 0 0 0 a 0 0 0 0 0 0 0 0 x neg a] end /display-matrix exch def } def /origin { [0 0 display-matrix 15 get display-matrix 14 get neg] } def % - Manipulate the current transformation matrix ----- % x y z /translate3d { 8 dict begin /v2 exch def /v1 exch def /v0 exch def /ctm ctm3d def /T ctm 0 get def [ [ T 0 get T 1 get T 2 get T 0 get v0 mul T 1 get v1 mul add T 2 get v2 mul add T 3 get add T 4 get T 5 get T 6 get T 4 get v0 mul T 5 get v1 mul add T 6 get v2 mul add T 7 get add T 8 get T 9 get T 10 get T 8 get v0 mul T 9 get v1 mul add T 10 get v2 mul add T 11 get add T 12 get T 13 get T 14 get T 12 get v0 mul T 13 get v1 mul add T 14 get v2 mul add T 15 get add ] /T ctm 1 get def [ T 0 get T 12 get v0 mul sub T 1 get T 13 get v0 mul sub T 2 get T 14 get v0 mul sub T 3 get T 15 get v0 mul sub T 4 get T 12 get v1 mul sub T 5 get T 13 get v1 mul sub T 6 get T 14 get v1 mul sub T 7 get T 15 get v1 mul sub T 8 get T 12 get v2 mul sub T 9 get T 13 get v2 mul sub T 10 get T 14 get v2 mul sub T 11 get T 15 get v2 mul sub T 12 get T 13 get T 14 get T 15 get ] ] end gset3d } def % ------------------------------------------------------ % axis A /rotate3d { 4 dict begin rotation-matrix3d /R exch def /T ctm3d 0 get def [ [ % first row T 0 get R 0 get mul T 1 get R 1 get mul add T 2 get R 2 get mul add T 0 get R 3 get mul T 1 get R 4 get mul add T 2 get R 5 get mul add T 0 get R 6 get mul T 1 get R 7 get mul add T 2 get R 8 get mul add T 3 get % second row T 4 get R 0 get mul T 5 get R 1 get mul add T 6 get R 2 get mul add T 4 get R 3 get mul T 5 get R 4 get mul add T 6 get R 5 get mul add T 4 get R 6 get mul T 5 get R 7 get mul add T 6 get R 8 get mul add T 7 get % third row T 8 get R 0 get mul T 9 get R 1 get mul add T 10 get R 2 get mul add T 8 get R 3 get mul T 9 get R 4 get mul add T 10 get R 5 get mul add T 8 get R 6 get mul T 9 get R 7 get mul add T 10 get R 8 get mul add T 11 get % fourth row T 12 get R 0 get mul T 13 get R 1 get mul add T 14 get R 2 get mul add T 12 get R 3 get mul T 13 get R 4 get mul add T 14 get R 5 get mul add T 12 get R 6 get mul T 13 get R 7 get mul add T 14 get R 8 get mul add T 15 get ] /T ctm3d 1 get def % T = T^-1 % => R^-1 T^-1 [ R 0 get T 0 get mul R 1 get T 4 get mul add R 2 get T 8 get mul add R 0 get T 1 get mul R 1 get T 5 get mul add R 2 get T 9 get mul add R 0 get T 2 get mul R 1 get T 6 get mul add R 2 get T 10 get mul add R 0 get T 3 get mul R 1 get T 7 get mul add R 2 get T 11 get mul add % ------------------------ R 3 get T 0 get mul R 4 get T 4 get mul add R 5 get T 8 get mul add R 3 get T 1 get mul R 4 get T 5 get mul add R 5 get T 9 get mul add R 3 get T 2 get mul R 4 get T 6 get mul add R 5 get T 10 get mul add R 3 get T 3 get mul R 4 get T 7 get mul add R 5 get T 11 get mul add % ------------------------ R 6 get T 0 get mul R 7 get T 4 get mul add R 8 get T 8 get mul add R 6 get T 1 get mul R 7 get T 5 get mul add R 8 get T 9 get mul add R 6 get T 2 get mul R 7 get T 6 get mul add R 8 get T 10 get mul add R 6 get T 3 get mul R 7 get T 7 get mul add R 8 get T 11 get mul add T 12 get T 13 get T 14 get T 15 get ] ] end gset3d } def % [A B C] v /reflection-transform3d { 5 dict begin /v exch def aload pop /C exch def /B exch def /A exch def /fv v 0 get A mul v 1 get B mul add v 2 get C mul add dup add def [ v 0 get fv A mul sub v 1 get fv B mul sub v 2 get fv C mul sub ] end } def % f = [A B C D] u % f = 0 is the *affine* reflection plane % v = v* + v0 with v0 in u-direction => v* - v0 % The map is Q => f(P)*Q - 2*f(Q)P % It is of order two. % % f(P) I - % % 2A*P[0] 2B*P[0] 2C*P[0] 2D*P[0] % 2A*P[1] 2B*P[1] 2C*P[1] 2D*P[1] % 2A*P[2] 2B*P[2] 2C*P[2] 2D*P[2] % 2A*P[3] 2B*P[3] 2C*P[3] 2D*P[3] % % Matrix = f(P) I - P f % set s0 = (T row 0)*P % T x this = % f(P)T[0,0]-A*s -B*s -C*s -D*s /reflect3d { 4 dict begin aload pop /P3 exch def /P2 exch def /P1 exch def /P0 exch def aload pop /D exch def /C exch def /B exch def /A exch def /fP A P0 mul B P1 mul add C P2 mul add D P3 mul add def /A A dup add def /B B dup add def /C C dup add def /D D dup add def [ /T ctm3d 0 get def [ /s % = (T row 1)*P T 0 get P0 mul T 1 get P1 mul add T 2 get P2 mul add T 3 get P3 mul add def fP T 0 get mul A s mul sub fP T 1 get mul B s mul sub fP T 2 get mul C s mul sub fP T 3 get mul D s mul sub /s % = (T row 1)*P T 4 get P0 mul T 5 get P1 mul add T 6 get P2 mul add T 7 get P3 mul add def fP T 4 get mul A s mul sub fP T 5 get mul B s mul sub fP T 6 get mul C s mul sub fP T 7 get mul D s mul sub /s % = (T row 2)*P T 8 get P0 mul T 9 get P1 mul add T 10 get P2 mul add T 11 get P3 mul add def fP T 8 get mul A s mul sub fP T 9 get mul B s mul sub fP T 10 get mul C s mul sub fP T 11 get mul D s mul sub /s % = (T row 3)*P T 12 get P0 mul T 13 get P1 mul add T 14 get P2 mul add T 15 get P3 mul add def fP T 12 get mul A s mul sub fP T 13 get mul B s mul sub fP T 14 get mul C s mul sub fP T 15 get mul D s mul sub ] /T ctm3d 1 get def /f0 % f paired with columns of T T 0 get A mul T 4 get B mul add T 8 get C mul add T 12 get D mul add def /f1 % f paired with columns of T T 1 get A mul T 5 get B mul add T 9 get C mul add T 13 get D mul add def /f2 % f paired with columns of T T 2 get A mul T 6 get B mul add T 10 get C mul add T 14 get D mul add def /f3 % f paired with columns of T T 3 get A mul T 7 get B mul add T 11 get C mul add T 15 get D mul add def [ fP T 0 get mul f0 P0 get mul sub fP T 1 get mul f1 P0 get mul sub fP T 2 get mul f2 P0 get mul sub fP T 3 get mul f3 P0 get mul sub fP T 4 get mul f0 P1 get mul sub fP T 5 get mul f1 P1 get mul sub fP T 6 get mul f2 P1 get mul sub fP T 7 get mul f3 P1 get mul sub fP T 8 get mul f0 P2 get mul sub fP T 9 get mul f1 P2 get mul sub fP T 10 get mul f2 P2 get mul sub fP T 11 get mul f3 P2 get mul sub fP T 12 get mul f0 P3 get mul sub fP T 13 get mul f1 P3 get mul sub fP T 14 get mul f2 P3 get mul sub fP T 15 get mul f3 P3 get mul sub ] ] end gset3d } def % [x y z w] [a00 a01 a02 a03 ... ] % but the vector is a linear function /dual-transform3d { 4 dict begin /T exch def /v exch def [ v 0 get T 0 get mul v 1 get T 4 get mul add v 2 get T 8 get mul add v 3 get T 12 get mul add v 0 get T 1 get mul v 1 get T 5 get mul add v 2 get T 9 get mul add v 3 get T 13 get mul add v 0 get T 2 get mul v 1 get T 6 get mul add v 2 get T 10 get mul add v 3 get T 14 get mul add v 0 get T 3 get mul v 1 get T 7 get mul add v 2 get T 11 get mul add v 3 get T 15 get mul add ] end } def % [x y z w] [a00 a01 a02 a03 ... ] /transform3d { 4 dict begin /T exch def /v exch def [ T 0 get v 0 get mul T 1 get v 1 get mul add T 2 get v 2 get mul add T 3 get v 3 get mul add T 4 get v 0 get mul T 5 get v 1 get mul add T 6 get v 2 get mul add T 7 get v 3 get mul add T 8 get v 0 get mul T 9 get v 1 get mul add T 10 get v 2 get mul add T 11 get v 3 get mul add T 12 get v 0 get mul T 13 get v 1 get mul add T 14 get v 2 get mul add T 15 get v 3 get mul add ] end } def % sx sy sz /scale3d { 8 dict begin /sz exch def /sy exch def /sx exch def /T ctm3d 0 get def [ [ T 0 get sx mul T 1 get sy mul T 2 get sz mul T 3 get T 4 get sx mul T 5 get sy mul T 6 get sz mul T 7 get T 8 get sx mul T 9 get sy mul T 10 get sz mul T 11 get T 12 get sx mul T 13 get sy mul T 14 get sz mul T 15 get ] /T ctm3d 1 get def [ T 0 get sx div T 1 get sx div T 2 get sx div T 3 get sx div T 4 get sy div T 5 get sy div T 6 get sy div T 7 get sy div T 8 get sz div T 9 get sz div T 10 get sz div T 11 get sz div T 12 get T 13 get T 14 get T 15 get ] ] end gset3d } def % s /shade { 2 dict begin 1 add 0.25 mul 0.5 add end } def % f = [A B C D] P % The map is Q => f(P)*Q - f(Q)P % It is idempotent. % % f(P) I - % % A*P[0] B*P[0] C*P[0] D*P[0] % A*P[1] B*P[1] C*P[1] D*P[1] % A*P[2] B*P[2] C*P[2] D*P[2] % A*P[3] B*P[3] C*P[3] D*P[3] % % Matrix = f(P) I - P f % set s0 = (T row 0)*P % T x this = % f(P)T[0,0]-A*s -B*s -C*s -D*s /plane-project { 12 dict begin aload pop /P3 exch def /P2 exch def /P1 exch def /P0 exch def aload pop /D exch def /C exch def /B exch def /A exch def /fP A P0 mul B P1 mul add C P2 mul add D P3 mul add def [ /T ctm3d 0 get def [ /s % = (T row 1)*P T 0 get P0 mul T 1 get P1 mul add T 2 get P2 mul add T 3 get P3 mul add def fP T 0 get mul A s mul sub fP T 1 get mul B s mul sub fP T 2 get mul C s mul sub fP T 3 get mul D s mul sub /s % = (T row 1)*P T 4 get P0 mul T 5 get P1 mul add T 6 get P2 mul add T 7 get P3 mul add def fP T 4 get mul A s mul sub fP T 5 get mul B s mul sub fP T 6 get mul C s mul sub fP T 7 get mul D s mul sub /s % = (T row 2)*P T 8 get P0 mul T 9 get P1 mul add T 10 get P2 mul add T 11 get P3 mul add def fP T 8 get mul A s mul sub fP T 9 get mul B s mul sub fP T 10 get mul C s mul sub fP T 11 get mul D s mul sub /s % = (T row 3)*P T 12 get P0 mul T 13 get P1 mul add T 14 get P2 mul add T 15 get P3 mul add def fP T 12 get mul A s mul sub fP T 13 get mul B s mul sub fP T 14 get mul C s mul sub fP T 15 get mul D s mul sub ] /T ctm3d 1 get def [ /s T 0 get T 1 get add T 2 get add T 3 get add def s A mul s B mul s C mul s D mul /s T 4 get T 5 get add T 6 get add T 7 get add def s A mul s B mul s C mul s D mul /s T 8 get T 9 get add T 10 get add T 11 get add def s A mul s B mul s C mul s D mul /s T 12 get T 13 get add T 14 get add T 15 get add def s A mul s B mul s C mul s D mul ] ] gset3d end } def % - Drawing commands in 3D -------------------------- % P displayed = [x y z w] => x/w y/w /project { 8 dict begin /v exch def /T display-matrix def /x T 0 get v 0 get mul T 1 get v 1 get mul add T 2 get v 2 get mul add T 3 get v 3 get mul add def /y T 4 get v 0 get mul T 5 get v 1 get mul add T 6 get v 2 get mul add T 7 get v 3 get mul add def /w T 12 get v 0 get mul T 13 get v 1 get mul add T 14 get v 2 get mul add T 15 get v 3 get mul add def w 0 eq { (Perspective: division by zero!) == quit } if x w div y w div end } def /cpt3d 4 array def /lm3d 4 array def % cpt3d is a point in the "real" 3d world % Should we build the current 3d path for reuse? % x y z /moveto3d { ps3ddict begin 1 [ 5 1 roll ] ctm3d 0 get transform3d aload pop cpt3d astore project MOVETO end cpt3d aload pop lm3d astore pop } def % x y z /lineto3d { ps3ddict begin 1 [ 5 1 roll ] ctm3d 0 get transform3d aload pop cpt3d astore project LINETO end } def % x y z /rmoveto3d { ps3ddict begin 0 [ 5 1 roll ] ctm3d 0 get transform3d % [dx dy dz 0] dup 0 get cpt3d 0 get add % [dx dy dz 0] x+dx exch dup 1 get cpt3d 1 get add % x+dx [dx dy dz dw] y+dy exch dup 2 get cpt3d 2 get add % x+dx x+dy [dx dy dz dw] z+dz exch % x+dx y+dy z+dz [dx dy dz dw] 3 get cpt3d 3 get add % x+dx x+dy z+dz w+dw cpt3d astore project MOVETO end cpt3d aload pop lm3d astore pop } def % x y z /rlineto3d { ps3ddict begin 0 [ 5 1 roll ] ctm3d 0 get transform3d % [dx dy dz 0] dup 0 get cpt3d 0 get add % [dx dy dz 0] x+dx exch dup 1 get cpt3d 1 get add % x+dx [dx dy dz dw] y+dy exch dup 2 get cpt3d 2 get add % x+dx x+dy [dx dy dz dw] z+dz exch % x+dx y+dy z+dz [dx dy dz dw] 3 get cpt3d 3 get add % x+dx x+dy z+dz w+dw cpt3d astore project LINETO end } def % x1 y1 z1 x2 y2 z2 x3 y3 z3 /curveto3d { ps3ddict begin 16 dict begin /z3 exch def /y3 exch def /x3 exch def /z2 exch def /y2 exch def /x2 exch def /z1 exch def /y1 exch def /x1 exch def % F(t) /P0 cpt3d display-matrix transform3d def /P1 [x1 y1 z1 1] ctm3d 0 get transform3d display-matrix transform3d def /w P0 3 get def /c P1 3 get w div 1 sub def /x0 P0 0 get w div def /x1 P1 0 get w div def /y0 P0 1 get w div def /y1 P1 1 get w div def x1 x0 c mul sub y1 y0 c mul sub [x3 y3 z3 1] ctm3d 0 get transform3d aload pop cpt3d astore display-matrix transform3d /P3 exch def /P2 [x2 y2 z2 1] ctm3d 0 get transform3d display-matrix transform3d def % We are assuming the display-matrix has images on { z = 0 } /w P3 3 get def /c P2 3 get w div 1 sub def /x3 P3 0 get w div def /x2 P2 0 get w div def /y3 P3 1 get w div def /y2 P2 1 get w div def x2 x3 c mul sub y2 y3 c mul sub x3 y3 CURVETO end end } def % - are the next two used? -------------------------------- % dP dQ Q /dcurveto3d { ps3ddict begin 16 dict begin /z3 exch def /y3 exch def /x3 exch def /dz3 exch def /dy3 exch def /dx3 exch def /dz0 exch def /dy0 exch def /dx0 exch def /P0 cpt3d display-matrix transform3d def /dP0 [dx0 dy0 dz0 0] ctm3d 0 get transform3d display-matrix transform3d def /w P0 3 get def % c = 1 - w'/w /c 1 dP0 3 get w div sub def P0 0 get w div c mul dP0 0 get w div add P0 1 get w div c mul dP0 1 get w div add [x3 y3 z3 1] ctm3d 0 get transform3d aload pop cpt3d astore display-matrix transform3d /P3 exch def /dP3 [dx3 dy3 dz3 0] ctm3d 0 get transform3d display-matrix transform3d def /w P3 3 get def /c 1 dP3 3 get w div add def P3 0 get w div c mul dP3 0 get w div sub P3 1 get w div c mul dP3 1 get w div sub P3 0 get w div P3 1 get w div CURVETO end end } def % dP dQ Q /dcurveto { ps3ddict begin 16 dict begin /y3 exch def /x3 exch def /dy3 exch def /dx3 exch def /dy0 exch def /dx0 exch def currentpoint dy0 add exch dx0 add exch x3 dx3 sub y3 dy3 sub x3 y3 CURVETO end end } def % - are the above two used? ---------------------------- /closepath3d { ps3ddict begin CLOSEPATH end lm3d aload pop cpt3d astore pop } def % - Conversion from 2d to 3D --------------------------- /simple-convert { [ { % x y [ 3 1 roll 0 {moveto3d}] } { % x y [ 3 1 roll 0 {lineto3d}] } { % x1 y1 x2 y2 x3 y3 [ 7 1 roll 0 5 1 roll 0 3 1 roll 0 {curveto3d} ] } { [ {closepath3d} ] } pathforall ] newpath { aload pop exec } forall } def % ----------------------------------------------- % For a simple currentpoint: /mkpath3dDict 8 dict def mkpath3dDict begin /pathcount { 0 {pop pop pop 1 exit} {pop pop pop 1 exit} {pop pop pop pop pop pop pop 1 exit} { pop 1 exit} pathforall } def /thereisacurrentpoint { pathcount 0 gt {true} {false} ifelse } def end % --------------------------------------------- % stack: [parameters] /f t0 t1 N /mkpath3d { ps3ddict begin mkpath3dDict begin 12 dict begin /N exch def /t1 exch def /t exch def /f exch cvx def /pars exch def /h t1 t sub N div def /h3 h 0.333333 mul def pars t f aload pop /velocity exch def /position exch def /p [ position aload pop 1 ] ctm3d 0 get transform3d display-matrix transform3d def /v [ velocity aload pop 0 ] ctm3d 0 get transform3d display-matrix transform3d def % p v = homogeneous position and velocity % p/p[3] v/p[3] - (v[3]/p[3])(p/p[3]) = inhomogeneous ones % = p/w v/w - c*p/w /w p 3 get def /c v 3 get w div def thereisacurrentpoint { p 0 get w div p 1 get w div LINETO } { p 0 get w div p 1 get w div MOVETO } ifelse N { % x y = currentpoint p 0 get v 0 get p 0 get c mul sub h3 mul add w div p 1 get v 1 get p 1 get c mul sub h3 mul add w div /t t h add def pars t f aload pop /velocity exch def /position exch def /p [ position aload pop 1 ] ctm3d 0 get transform3d display-matrix transform3d def /v [ velocity aload pop 0 ] ctm3d 0 get transform3d display-matrix transform3d def /w p 3 get def /c v 3 get w div def p 0 get v 0 get p 0 get c mul sub h3 mul sub w div p 1 get v 1 get p 1 get c mul sub h3 mul sub w div p 0 get w div p 1 get w div CURVETO } repeat end % local dict end % mkpath3d dict end % ps3ddict } def % makes polygon out of control points /mkcontrolpath3d { mkpath3dDict begin 12 dict begin /N exch def /t1 exch def /t exch def /f exch cvx def /pars exch def /h t1 t sub N div def /h3 h 0.333333 mul def pars t f aload pop /velocity exch def /position exch def position aload pop thereisacurrentpoint { lineto3d } { moveto3d } ifelse N { % x y = currentpoint % currentpoint pixel pop position 0 get velocity 0 get % x dx/dt h3 mul add position 1 get velocity 1 get % y dy/dt h3 mul add position 2 get velocity 2 get % z dz/dt h3 mul add lineto3d /t t h add def pars t f aload pop /velocity exch def /position exch def position 0 get velocity 0 get h3 mul sub position 1 get velocity 1 get h3 mul sub position 2 get velocity 2 get h3 mul sub lineto3d position 0 get position 1 get position 2 get lineto3d } repeat end % local dict end % mkpath3d dict } def % ----------------------------------------------- % makes polygon from endpoints /mkpolypath3d { mkpath3dDict begin 12 dict begin /N exch def /t1 exch def /t exch def /f exch cvx def /pars exch def /h t1 t sub N div def /h3 h 0.333333 mul def pars t f aload pop /velocity exch def /position exch def position aload pop thereisacurrentpoint { lineto3d } { moveto3d } ifelse N { % x y = currentpoint % currentpoint pixel pop /t t h add def pars t f aload pop /velocity exch def /position exch def position 0 get position 1 get position 2 get lineto3d } repeat end % local dict end % mkpath3d dict } def % --------------------------------------------- /plainarrow3d { 5 dict begin /shaftwidth exch def /arrowlength exch def /headwidth shaftwidth 3 mul def /headlength headwidth def /shaftlength arrowlength shaftwidth 2.5 mul sub def 0 0 0 moveto3d 0 shaftwidth 0.5 mul 0 lineto3d % shaftlength 0 0 rlineto3d shaftlength shaftwidth 0.5 mul 0 lineto3d arrowlength headlength sub headwidth 0.5 mul 0 lineto3d arrowlength 0 0 lineto3d arrowlength headlength sub headwidth -0.5 mul 0 lineto3d shaftlength shaftwidth -0.5 mul 0 lineto3d 0 shaftwidth -0.5 mul 0 lineto3d 0 0 0 lineto3d end } def