al@gmdzi.UUCP (Alexander Linden) (01/10/89)
My first big question about postscript is Does there anyone have knowledge or access to a greater macro package of postscript procedures, which contains for example - a procedure to plot arbitrary functions in a nice coordinate system. - procedures to plot 3-dimensional functions in different shapes, and to rotate them. - procedure to make pie charts, statistical figures, and so on. - still everything useful to write articles, eg. in chemistry, computer science, biology, medicine, engineering, and so on. Any suggestions would help, thanks in advance, al@gmdzi.uucp Alexander Linden GMD D-5205 Sankt Augustion P.O.Box 1240 West Germany
liam@cs.qmc.ac.uk (William Roberts) (01/11/89)
In article <917@gmdzi.UUCP> al@gmdzi.UUCP (Alexander Linden) writes: > >- procedures to plot 3-dimensional functions in different > shapes, and to rotate them. The following PostScript code handles the calculations for 2D views of 3D objects include 3D paths and Bezier patches. It is an appendix in R. Salmon & M. Slater "Computer Graphics: Systems and Concepts" August 1987, Addison-Wesley. which contains a more detailed explanation of the code and the 3D machinery involved (view planes, view vectors, homogeneous coordinate transforms etc). It is given here with the prior permission of Mel Slater who wrote it. There is an example on the end, which is in fact a figure from the paper "First Impressions of NeWS" that appeared in "Computer Graphics Forum", 7, 39-57, 1988, North-Holland, and which was mailed out to the 400+ people that requested it. Happy New Year! ------------------------------------------------------ %! %% Copyright M. Slater at Queen Mary College, London, 1988. %% This code may be freely distributed and copied %% provided this copyright message is retained. /BEFOREstate save def %Matrix handling package %Here a matrix is a 4*4 matrix represented as a flattened array /MatrixWithValueDict 2 dict def %delivers a matrix with every element equal to value %value MatrixWithValue => [value value ... value] /MatrixWithValue { MatrixWithValueDict begin /value exch def /matrix 16 array def 0 1 15 {matrix exch value put} for matrix end } def /IdentityMatrixDict 1 dict def %delivers an identity matrix % - IdentityMatrix => 4*4 identity matrix /IdentityMatrix { IdentityMatrixDict begin /matrix 0 MatrixWithValue def 0 5 15 {matrix exch 1 put} for matrix end } def /IndexDict 2 dict def %delivers the index into the flattened array corresponding to (i,j) %i j Index => i*4+j /Index { IndexDict begin /j exch def /i exch def i 4 mul j add end } def /MatMultiplyDict 9 dict def %matrix multiplication %MatrixA MatrixB MatMultiply => MatrixA*MatrixB /MatMultiply { MatMultiplyDict begin /B exch def /A exch def /sum 0 def /matrix 16 array def 0 1 3 {/i exch def 0 1 3 {/j exch def /sum 0 def 0 1 3 {/k exch def /sum A i k Index get B k j Index get mul sum add def } for matrix i j Index sum put } for } for matrix end } def /CrossProductDict 3 dict def %vector cross product %a b CrossProduct => a * b (cross product) /CrossProduct { CrossProductDict begin /b exch def /a exch def /c 3 array def c 0 a 1 get b 2 get mul a 2 get b 1 get mul sub put c 1 a 2 get b 0 get mul a 0 get b 2 get mul sub put c 2 a 0 get b 1 get mul a 1 get b 0 get mul sub put c end } def /NormDict 3 dict def %vector normalization %a Norm => b (normalization) /Norm { NormDict begin /a exch def /b 3 array def a b copy pop /n 0 b {dup mul add} forall def %sum of squares /n n sqrt def b {n div} forall b astore end } def /DotProductDict 5 dict def %vector dot product %a b DotProduct => a.b (dot product) /DotProduct { DotProductDict begin /b exch def /a exch def /n 0 def 0 1 2 {/i exch def /n a i get b i get mul n add def } for n end } def /VectorSumDict 4 dict def %vector sum %a b VectorSum => a+b /VectorSum { VectorSumDict begin /b exch def /a exch def /c 0 0 0 3 array astore def 0 1 2 {/i exch def c i a i get b i get add put } for c end } def /VectorDiffDict 4 dict def %vector difference %a b VectorDiff => a-b /VectorDiff { VectorDiffDict begin /b exch def /a exch def /c 0 0 0 3 array astore def 0 1 2 {/i exch def c i a i get b i get sub put } for c end } def /PointByMatrixDict 6 dict def %delivers non-homogeneous point resulting from the multiplication %point Matrix PointByMatrix => point(3D) /PointByMatrix { PointByMatrixDict begin /m exch def /p exch def /hp 4 array def /q 3 array def 0 1 3 { /j exch def %hp[j] = m[3,j] hp j m 3 j Index get put 0 1 2 { /i exch def %hp[j] = hp[j] + p[i]*m[i,j] hp j hp j get p i get m i j Index get mul add put } for } for %for i = 0 to 2 do q[i] = hp[i] hp aload pop pop q astore %for i = 0 to 2 do q[i] = q[i]/hp[3] q {hp 3 get div} forall q astore end } def /PointByMatrixToCoordsDict 6 dict def %delivers x and y coordinates of the point resulting %from the multiplication of the point by the matrix %point matrix PointByMatrixToCoords => x y /PointByMatrixToCoords { PointByMatrixToCoordsDict begin /m exch def /p exch def /hp 4 array def 0 1 3 { /j exch def %hp[j] = m[3,j] hp j m 3 j Index get put 0 1 2 { /i exch def %hp[j] = hp[j] + p[i]*m[i,j] hp j hp j get p i get m i j Index get mul add put } for } for /w hp 3 get def hp 0 get w div hp 1 get w div end } def %Data Structures and Procedures for specifying a 3D view %dictionary to hold the internal representation of the current view /ViewRepresentation 2 dict def ViewRepresentation begin %view matrix /ViewMatrix IdentityMatrix def %matrix for transformation to display /displaymatrix matrix def end %dictionary to hold the current view specification /ViewSpecification 8 dict def ViewSpecification begin /vpn 0 0 -1 3 array astore def %view plane normal /vuv 0 1 0 3 array astore def %view up vector /vrp 0 0 0 3 array astore def %view reference point /cop 0 0 0 3 array astore def %centre of projection /vd 0 def %view distance /dmin -1 def %front clipping plane /dmax 1 def %back clipping plane /vwin 0 1 0 1 4 array astore def %view window end /SetViewportDict 10 dict def %computes factor values assuming the given viewport. %The 2D window is x and y between -1 and +1. %The results saved in ViewRepresentation %xmin xmax ymin ymax SetViewport => - /SetViewport { SetViewportDict begin /ymax exch def /ymin exch def /xmax exch def /xmin exch def /dx xmax xmin sub 2 div def /dy ymax ymin sub 2 div def /f0 xmin dx add def /f1 dx def /f2 ymin dy add def /f3 dy def ViewRepresentation begin f1 0 0 f3 f0 f2 displaymatrix astore pop end end } def /SetViewDict 16 dict def %computes the current view and saves in ViewRepresentation %according to the data in ViewSpecification %- SetView => - /SetView { ViewSpecification begin ViewRepresentation begin SetViewDict begin /n vpn Norm def /u n vuv CrossProduct Norm def /v u n CrossProduct def /VM IdentityMatrix def 0 1 2 { /i exch def %VM[i,0] = u[i] VM i 0 Index u i get put %VM[i,1] = v[i] VM i 1 Index v i get put %VM[i,2] = n[i] VM i 2 Index n i get put %VM[3,0] = VM[3,0] - vrp[i]*u[i] VM 3 0 Index VM 3 0 Index get vrp i get u i get mul sub put %VM[3,1] = VM[3,1] - vrp[i]*v[i] VM 3 1 Index VM 3 1 Index get vrp i get v i get mul sub put %VM[3,2] = VM[3,2] - vrp[i]*n[i] VM 3 2 Index VM 3 2 Index get vrp i get n i get mul sub put } for %transform cop to the origin 0 1 2 { /i exch def %VM[3,i] = VM[3,i] - (cop[i]+vrp[i]) VM 3 i Index VM 3 i Index get cop i get vrp i get add sub put } for %and redefine the view distances %VD = vd - cop[2] /VD vd cop 2 get sub def %DMIN = dmin - cop[2] /DMIN dmin cop 2 get sub def %DMAX = dmax - cop[] /DMAX dmax cop 2 get sub def %set the projection matrix %dx = xmax - xmin etc /dx vwin 1 get vwin 0 get sub def /dy vwin 3 get vwin 2 get sub def /px vwin 1 get vwin 0 get add def /py vwin 3 get vwin 2 get add def /dD DMAX DMIN sub def /P 0 MatrixWithValue def P 0 0 Index 2 VD dx div mul put P 1 1 Index 2 VD dy div mul put P 2 0 Index px dx div neg put P 2 1 Index py dy div neg put P 2 2 Index DMAX dD div put P 2 3 Index 1 put P 3 2 Index DMIN DMAX dD div mul neg put /ViewMatrix VM P MatMultiply store end end end }def /SetVPNDict 3 dict def %sets the View Plane Normal in ViewSpecification %x y z SetVPN => - /SetVPN { ViewSpecification begin SetVPNDict begin /z exch def /y exch def /x exch def /vpn x y z vpn astore store end end } def /SetVUVDict 3 dict def %sets the View Up Vector in ViewSpecification %x y z SetVUV => - /SetVUV { ViewSpecification begin SetVUVDict begin /z exch def /y exch def /x exch def /vuv x y z vuv astore store end end } def /SetVRPDict 3 dict def %sets the View Reference Point in ViewSpecification %x y z SetVRP => - /SetVRP { ViewSpecification begin SetVRPDict begin /z exch def /y exch def /x exch def /vrp x y z vrp astore store end end } def /SetCOPDict 3 dict def %sets the Centre of Projection in ViewSpecification %x y z SetCOP => - /SetCOP { ViewSpecification begin SetCOPDict begin /z exch def /y exch def /x exch def /cop x y z cop astore store end end } def %sets the View Distance, and planes in ViewSpecification %viewDistance backPlane frontPlane SetViewDistance => - /SetViewDistance { ViewSpecification begin /dmax exch store /dmin exch store /vd exch store end } def /SetViewWindowDict 4 dict def %sets the View Window in ViewSpecification %xmin xmax ymin ymax SetViewWindow => - /SetViewWindow { ViewSpecification begin SetViewWindowDict begin /ymax exch def /ymin exch def /xmax exch def /xmin exch def /vwin xmin xmax ymin ymax vwin astore store end end } def %Basic functions for 3D drawing /path3DDict 3 dict def %a path is produced from an array of 3D points %assumes an array of 3D points on stack, delivers path %pointArray path3D => x0 y0 moveto x1 y1 lineto ... xn-1 yn-1 lineto /path3D { ViewRepresentation begin path3DDict begin /parray exch def /qarray parray length 1 sub array def %firstPoint moveto parray 0 get ViewMatrix PointByMatrixToCoords displaymatrix transform moveto /qarray parray aload length -1 roll pop qarray astore def qarray {ViewMatrix PointByMatrixToCoords displaymatrix transform lineto } forall end end } def %Bezier cubic patches %Control point array represented by [[x0 y0 z0] [x1 y1 z1] ... [x3 y3 z3]] /sumPointsDivDict 5 dict def %sums elements of two points and divides by d %p q d sumPointsDiv => (p+q)/d /sumPointsDiv { sumPointsDivDict begin /d exch def /q exch def /p exch def /s 3 array def 0 1 2 { /i exch def s i p i get q i get add d div put } for s end } def /sumPointsDict 4 dict def %sums elements of two points %p q sumPoints => p+q /sumPoints { sumPointsDict begin /q exch def /p exch def /s 3 array def 0 1 2 { /i exch def s i p i get q i get add put } for s end } def /pointDivDict 4 dict def %divides elements of a point %p d pointDiv => p/d /pointDiv { pointDivDict begin /d exch def /p exch def /q 3 array def 0 1 2 { /i exch def q i p i get d div put } for q end } def /splitDict 4 dict def %splits control point sequence (p) into sequences q and r %p split => q r /split { splitDict begin /p exch def /q 4 array def /r 4 array def %initialise return arrays %q[0] = p[0] q 0 p 0 get put %q[1] = (p[0]+p[1])/2 q 1 p 0 get p 1 get 2 sumPointsDiv put %t = (p[1] + p[2])/4 /t p 1 get p 2 get 4 sumPointsDiv 3 array copy def %q[2] = q[1]/2 + (p[1] + p[2])/4 q 2 q 1 get 2 pointDiv t sumPoints put %r[3] = p[3] r 3 p 3 get put %r[2] = (p[2] + p[3])/2 r 2 p 2 get p 3 get 2 sumPointsDiv put %r[1] = (p[1]+p[2])/4 + r[2]/2 r 1 t r 2 get 2 pointDiv sumPoints put %q[3] = (q[2] + r[1])/2 q 3 q 2 get r 1 get 2 sumPointsDiv put %r[0] = q[3] r 0 q 3 get put q r end } def /splitControlGraphDict 9 dict def /decomposeDict 5 dict def %given a 4*4 array of control points p delivers q,r,s,t %each of p,q,r,s,t are organized as [cp0 cp1 cp2 cp3] %where cpi is an array of control points %p splitControlGraph => q r s t /splitControlGraph { splitControlGraphDict begin /p exch def /q 4 array def /r 4 array def /s 4 array def /t 4 array def /u 4 array def /v 4 array def 0 1 3 { /i exch def p i get split %leaves two arrays of control points on stack %assign second to v[i] and first to u[i] v exch i exch put u exch i exch put } for /decompose { %runs through u in column order, and splits it for each %column, leaving the results in q and r decomposeDict begin /r exch def /q exch def /u exch def 0 1 3 { /j exch def 0 1 3 { /i exch def u i get j get } for 4 array astore split r exch j exch put q exch j exch put } for end } def u q r decompose v s t decompose q r s t end } def /pathControlGraphDict 3 dict def %given a control graph, produces a path %p pathControlGraph => - /pathControlGraph { pathControlGraphDict begin /p exch def 0 1 3 { /j exch def p j get path3D 0 1 3 { /i exch def p i get j get } for 4 array astore path3D } for end } def %given a control graph(p) and a non-negative integer(n), recursively %splits and draws the control graph %the recursion is to a depth of n %p n => - /simpleBezier { 4 dict begin /n exch def /p exch def 0 n eq %if n=0 then {p pathControlGraph stroke} %else { /nm1 n 1 sub def /g p splitControlGraph 4 array astore def g {nm1 simpleBezier} forall } ifelse end } def %Example of a Control Graph %defines a control graph, looking like a step %together with appropriate viewing parameters /Step { /G [ [ [0.0 1.0 1.0] [0.3 1.0 1.2] [0.7 1.0 1.4] [1.0 1.0 1.6] ] [ [0.0 1.0 0.5] [0.3 1.0 0.5] [0.7 1.0 0.5] [1.0 1.0 0.5] ] [ [0.0 0.0 0.5] [0.3 0.0 0.3] [0.7 0.0 0.2] [1.0 0.0 0.5] ] [ [0.0 0.0 0.0] [0.3 0.0 0.0] [0.7 0.0 0.0] [1.0 0.0 0.0] ] ] def 0.5 0.5 1.0 SetVRP 1.0 0.5 0.0 SetVPN 0.0 0.0 1.0 SetVUV -4 1 -4 1 SetViewWindow 0 -4 4 SetViewDistance 0 0 -3 SetCOP SetView } def %draws 3D axes /axes { /Xaxis {[[-0.2 -0.2 -0.2] [ 1.5 -0.2 -0.2]] path3D} def /Yaxis {[[-0.2 -0.2 -0.2] [-0.2 1.5 -0.2]] path3D} def /Zaxis {[[-0.2 -0.2 -0.2] [-0.2 -0.2 1.5]] path3D} def gsave 1 setlinewidth Xaxis gsave stroke grestore (X) show Yaxis gsave stroke grestore (Y) show Zaxis gsave stroke grestore (Z) show grestore } def %% Main resolution 72 div dup neg scale % convert to normal coords 0 -500 translate /Times-Roman findfont 10 scalefont setfont 0 500 0 500 SetViewport Step 2 setlinewidth 1 setlinecap 1 setlinejoin G 2 simpleBezier G pathControlGraph gsave 0 setlinewidth [ 2 1 ] 0 setdash stroke grestore 1 setlinewidth axes BEFOREstate restore showpage ------------------------------------------------------ -- William Roberts ARPA: liam@cs.qmc.ac.uk (gw: cs.ucl.edu) Queen Mary College UUCP: liam@qmc-cs.UUCP LONDON, UK Tel: 01-975 5250