FEATPOST macros L. Nobre G., http://matagalatlante.org December 2004 This document intends to be an explanation of the MetaPost macros defined in the FEATPOST package. Its purpose is to draw two or three-dimensional physics diagrams. The FEATPOST package is supposed to help you draw figures containing 3D dots, vectors, flat arrows, angles, parametric lines, circles, ellipses, cones, cylinders, spheres, globes, hemispheres, toruses, elliptical frusta, polygons, polyhedra, functional and parametric surfaces, direction fields, field lines and trajectories in vector fields, schematic automobiles, electric charges, etc. The fact that it is a programming language instead of Computer Aided Design (CAD) helps the user to experiment different figure layouts without changing specified geometric relationships among figure elements. Two of the intrinsic MetaPost features which may be important for physics diagrams are: (i) the typesetting power of TeX is easily called when needed and (ii) besides a sufficient number of mathematical operators, geometric relationships may be expressed by linear equations, without explicit assignements. Introduction

3D in MetaPost is not a new idea. Denis Roegel contributed related packages to CTAN some years ago. It was the subject of a 1997 TUGboat article (V18, N4, 274-283). Recently he developed . Anthony Phan has been developing a very elegant package called . Due to his skilled coding, m3D can handle larger objects and can produce more realistic renderings than FEATPOST. Other possibilities include , or . In any case we are talking about vector-based abstract diagrams, so the functionality of these packages is nearer to GNUPLOT than to OpenGL. We could also talk about or but these are focused on ray-traced images, a completely different thing.

Regarding software FEATPOST requires only MetaPost but recommends LaTeX, bash, ImageMagick, ghostscript, Linuxdoc, Textutils, dvips, epstopdf, sed, gv, plaympeg, , and . Also, it is highly beneficial to be able to understand and cope with MetaPost error messages as FEATPOST has no protection against mistaken inputs. One probable cause of errors is the use of variables with the name of procedures, like X, Y, Z, N, rp, cb, ps All other procedure names have six or more characters.

The user must be aware that MetaPost has a limited arithmetic power and that the author has limited programming skills, which may lead to unperfect 3D figures, very long processing time or shear bugs. It's advisable not to try very complex diagrams at first and it's recommended to keep 3D coordinates near order 1 (default MetaPost units).

All FEATPOST macros are build apon the MetaPost color variable type. It looks like this: (red,green,blue) Its components may, nevertheless, be arbtitrary numbers, like: (X,Y,Z)So, the color type is adequate to define not only colors but also 3D points and vectors.

Small Tutorial

One very minimalistic example program could be: beginfig(1); cartaxes(1,1,1); endfig; end;where cartaxes is a FEATPOST macro that produces the Cartesian referential.

One small example program may be: f := 5.4*(1.5,0.5,1); Spread := 30; beginfig(1); numeric gridstep, sidenumber, i, j, coord, aa, ab, ac; color pa; gridstep = 0.9; sidenumber = 10; coord = 0.5*sidenumber*gridstep; for i=0 upto sidenumber: for j=0 upto sidenumber: pa := (-coord+j*gridstep,-coord+i*gridstep,0); aa := uniformdeviate(360); ab := uniformdeviate(180); ac := uniformdeviate(90); kindofcube( false, false, pa, aa, ab, ac, 0.4, 0.4, 0.9 ); endfor; endfor; endfig; end.where kindofcube is a FEATPOST macro that produces a rectangular prism (cuboid).

Example that uses kindofcube.
Reference Manual

Some words about notation. The meaning of macro, function, procedure and routine is the same. Global variables are presented like this: vartype var, anothervar anothervartype yetanothervar Explanation of var, anothervar and yetanothervar. vartype can be any one of MetaPost types but the meaning of color is a three-dimensional point or vector, not an actual color like yellow, black or white. If the meaning is an actual color then the type will be colour. All global variables have default values.

Functions are presented like this: returntype function() Explanation of function. returntype can be any one of MetaPost types plus global, draw or drawlabel. global means that the function changes some of the global variables. draw means that the function changes the currentpicture. drawlabel means that the function changes the currentpicture and adds text to it. type1 Explanation of the first argument. The type of one argument can be any one of MetaPost types plus suffix or text. type2 Explanation of the second argument. There is the possibility that the function has no arguments. In that case the function is presented like "returntype function". Etc.

Global variables

boolean ParallelProj boolean SphericalDistortion Kind of projection calculated by rp. By default projections are rigorous but if ParallelProj is set true then parallel lines remain parallel in the projection. It is the same as placing the point of view infinitely far without loosing sight. If SphericalDistortion is set true there will be a distortion coming from: (i) the projection being done on a sphere of center f and (ii) this sphere being plaited onto the paper page.

Figure that uses SphericalDistortion:=true and rigorousdisc.
Definitions

global makeline@#( text1) global makeface@#( text1) Both of these functions ease the task of defining lines and polygons. Just provide a list of vertexes in the right sequence for each polygon and/or line. Suppose a tetrahedron V3:=(+1,-1,-1);V2:=(-1,+1,-1); V4:=(+1,+1,+1);V1:=(-1,-1,+1); makeface2(1,2,3);makeface3(1,2,4); makeface1(3,4,1);makeface4(3,4,2);The number in the last makeface or last makeline procedure name must be the number of polygons or lines. All polygons and lines from 1 upto this number must be defined but the sorting may be any of your liking.

Macros Very Basic Macros

numeric X() Returns the first coordinate of a point or vector (of color type). Replaces redpart. numeric Y() Returns the second coordinate of a point or vector. Replaces greenpart. numeric Z() Returns the second coordinate of a point or vector. Replaces bluepart. draw produce_auto_scale The currentpicture is centered in, and adjusted to the size of, an A4 paper page. This avoids the control of Spread and ShiftV. string cstr() Converts a color into its string. Usefull in combination with getready. string bstr() Converts a boolean expression into its string. Usefull in combination with getready.

Vector Calculus

color N() Unit vector. Returns black (the null vector) when the argument has null norm. The "N" means "normalized". numeric cdotprod() Dot product of two vectors. color ccrossprod() Cross product of two vectors. numeric ndotprod() Cossine of the angle beetween two vectors. color ncrossprod() Normalized cross product of twovectors. numeric conorm() Euclidean norm of a vector. numeric getangle() Angle beetween two vectors. pair getanglepair() Orientation angles of a vector. The first angle (xpart) is measured beetween the vector projection on the XY plane and the X axis. The second angle (ypart)is measured beetween the vector and its projection on the XY plane. This may be usefull to find the arguments of kindofcube color eulerrotation() Three-dimensional rotation of a vector. See the figure explaining kindofcube to visualize the following movement: (i) grab the X component of the vector; (ii) rotate it on the XY plane as much as the first argument; (iii) raise it up as much as the second argument; and (iv) turn it around as much as the third argument. numeric Angle of rotation around the Z component. numeric Angle of rotation around the rotated Y component. numeric Angle of rotation around the two times rotated X component. color Vector to be rotated. color randomfear Generates a randomly oriented unit vector.

Projection Macros

pair rp() Converts spatial positions into planar positions on the paper page. The conversion considers the values of the following global variables: viewcentr, ParallelProj, SphericalDistortion, Spread and ShiftV. When both ParallelProj and SphericalDistortion are false it won't work if either (i) the vectors f-viewcentr and f-R are perpendicular (R is the argument) or (ii) f and viewcentr share the same X and Y coordinates. color Spatial position. color cb() Calculates the position of the shadow of a point. Uses HoriZon and LightSource. color Point position. color projectpoint() Calculates the intersection beetween a plane and a straight line. The plane contains a given point and is perpendicular to the line connecting the LightSource and this same point. The line is defined by another given point and the LightSource. Summary: projectpoint returns the projection of the second argument on a plane that contains the first argument. Can be used to draw shadows cast on generic planes. color Origin of the projection plane. color Point to be projected. color lineintersectplan() Calculates the intersection beetween a generic plane and a straight line. The plane contains a given point and is perpendicular to a given vector. The line contains a given point and is parallel to a given vector. color Point of the line. color Vector parallel to the line. color Point of the projection plane. color Vector perpendicular to the projection plane.

Plain Basic Macros

draw signalvertex() Draws a dot sized inversely proportional to its distance from the viewpoint f. color Location. numeric Factor of proportionality ("size of the dot"). colour Colour of the dot.

Figure that uses signalvertex.
path pathofstraightline() When using SphericalDistortion:=true, straight lines look like curves. This macro returns the curved path of a straight line beetween two points. This path will have a greater length ("time") when PrintStep is made smaller. draw drawsegment() Alternative pathofstraightline that avoids the calculation of all the intermediate points when SphericalDistortion:=false. drawlabel cartaxes() Cartesean axis with prescribed lenghtes and apropriate labels. numeric Length of the X axis. numeric Length of the Y axis. numeric Length of the Z axis. draw emptyline() This procedure produces a sort of a tube that can cross over itself. It facilitates the drawing of, for instance, thick helical curves but it won't look right if the curves are drawn getting apart from the point of view. Please, accept this inconveniance. As like many other FEATPOST macros this one can produce visually correct diagrams only in limited conditions. Can cast a shadow. boolean Choose true to join this line with a previously drawn line. numeric Factor of proportionality ("diameter of the tube"). The tubes are just sequences of dots drawn by signalvertex. colour Colour of the tube border. colour Colour of the tube. numeric Total number of dots on the tube line. numeric Fraction of the tube diameter that is drawn with the tube colour. numeric This is the number of dots that are redrawn with the colour of the tube for each drawn dot with the color of the tube border. Usually 1 or 2 are enough. text This is the name a function that returns a 3D point of the line for each value of a parameter in beetween 0 and 1.
Figure that uses emptyline. The junction point of two different lines is indicated by an arrow. Note the unperfection on the top right, inside the upper turn.
draw closedline() This procedure produces a tube that can cross over itself. It facilitates the drawing of, for instance, thick helical curves but it won't look right as its thickness does not change with the distance from the point of view. The drawing is entirely done in two dimensions, so the tube diameter depends on the global variables ForePen and BackPen. There can be more than one line in a figure but all get the same diameter. When calling closedline() in different figures of the same program you must reinitialize both NCL and Nobjects (because closedline() uses getready()). boolean Value of "the line is closed". numeric Total number of path segments on the tube line. numeric Use 0.5 or more. numeric Use 0.75 or more. text This is the name of a function that returns a 3D point of the line for each value of a parameter in beetween 0 and 1. drawlabel angline() Draws an arch beetween two straight lines with a common point and places a label near the middle of the arch (marks an angle). Note that the arch is not circular. color Point of one line. color Point ot the other line. color Common point. numeric Distance beetween the arch and the common point. picture Label. suffix Position of the label relatively to the middle of the arch. May be one of lft, rt, top, bot, ulft, urt, llft and lrt. drawlabel anglinen() The same as the previous function but the sixth argument is numeric: 0=rt; 1=urt; 2=top; 3=ulft; 4=lft; 5=llft; 6=bot; 7=lrt; any other number places the label on the middle of the arch. draw squareangline() This is supposed to mark 90 degree angles but works for any angle value. color Point of one line. color Point ot the other line. color Common point. numeric Distance beetween the "arch" and the common point. path rigorouscircle() 3D circle. The total "time" of this path is 8. This small number makes it easy to select parts of the path. The circle is drawn using the "left-hand-rule". If you put your left-hand thumb parallel the circle axis then the other left-hand fingers curl in the same sense as the circle path. This path allways starts, approching the view point, from a point on a diameter of the circle that projects orthogonaly to its axis, and rotating around the axis in the way of the left-hand-rule. color Center of the circle. color Direction orthogonal to the circle (circle axis). numeric Radius of the circle.
Figure that uses anglinen and rigorouscircle.
draw tdarrow() Draws a flat arrow that begins at the first argument and ends at the second. The shape of the arrow is controled by the global variables TDAtiplen, TDAhalftipbase, TDAhalfthick. path twocyclestogether() This macro allows you to draw any solid that has no vertexes and that has two, exactly two, planar cyclic edges. In fact, it doesn't need to be a solid. Just provide the pathes of both cyclic edges as arguments but note that the returned path is polygonal. In order to complete the drawing of this solid you have to choose one of the edges to be drawn immediatly afterwards. This is done automatically by the whatisthis macro for the case of two parallel and concentric ellipses. path ellipticpath() Produces an elliptic path in 3D space. color Position of the center. color Major or minor axis. color The other axis. drawlabel labelinspace() Draw some 2D picture on some 3D plane (only when ParallelProj:=true). color Position for the lower-left corner. color Orientation of the picture's bottom edge. color Orientation of the picture's letf edge. text 2D picture's name.
Example that uses labelinspace.

Standard Objects

path goodcirclepath() Another 3D circle macro. More rigorous than rigorouscircle but when the direction ortogonal to the circle is almost orthogonal to the line viewpoint--center it doesn't work correctly. The total "time" of this path is 36. color Center of the circle. color Direction ortogonal to the circle. numeric Radius of the circle. draw spatialhalfsfear() An hemisphere. Doesn't work with f inside it. color Center. color Vector ortogonal to the frontier circle and pointing out of the concavity. numeric Radius of the (hemi)sphere. path spatialhalfcircle() And yet another 3D circle macro. Only the visible or the hidden part. This is usefull to mark sections of cylinders or spherical major circles. color Center of the circle. color Direction ortogonal to the circle. numeric Radius of the circle. boolean The visible part is selected with true and the hidden with false. draw rigorousdisc() 3D opaque cylinder with/without a hole. Can cast a shadow (without the hole). numeric Ray of an axial hole. boolean Option for completly opaque cylinder (true) or partial pipe (false) when there is no hole. When the cylinder has an hole this option should be true. color Center of one circular base. numeric Radius of both circular bases. color Vector that defines the length and orientation of the cylinder. The addition the third and fifth arguments should give the position of the center of the other circular base. draw verygoodcone() 3D cone. Can cast a shadow. bolean Option to draw dashed evenly the invisible edge (true) or not (false). color Center of the circular base. color Direction ortogonal to the circular base. numeric Radius of the circular base. color Position of the vertex path rigorousfearpath() 3D sphere. Simple but hard. color Center position. numeric Radius. draw tropicalglobe() Globe with minor circles. Can cast a shadow. numeric Number of marked latitudes. color Center position. numeric Radius color Axis orientation.

Figure that uses tropicalglobe.
draw whatisthis() An elliptic frustum. Both edges are elliptic an have the same orientation but one may be greater than the other. Can cast a shadow. color Reference edge center. color Major or minor axis. color The other axis. numeric Length of the original cylinder. numeric Edges axis length ratio. draw kindofcube() Polyhedron with six orthogonal faces (cuboid). boolean Also draw the invisible edges dashed evenly (true) or do not. boolean The reference point may be a vertex (true) or the center(false). color Reference point. numeric Alpha1. numeric Alpha2. numeric Alpha3. numeric L1. Length of the first side. numeric L2. Length of the second side. numeric L3. Length of the third side. These arguments are represented in the next figure.
Figure that uses and explains kindofcube. Note that the three indicated angles may be used as arguments of eulerrotation.
draw setthestage() Produces an horizontal square made of squares. Its Z coordinate is defined by HoriZon. numeric Number of squares in each side. numeric Size of each side. draw setthearena() Produces an horizontal circle made of circles. Its Z coordinate is defined by HoriZon. Due to the fact that the center of a circle is not on the center of its central perspective projection, this may look a bit strange. numeric Number of circles on a diameter. numeric Diameter. draw smoothtorus() Toxic donut (not to be eaten). Produces an error message when f is close to the table. color Center. color Direction orthogonal to the torus plane. numeric Big ray. numeric Small ray.

Composed Objects

draw positivecharge() Draws a sphere with a plus or minus sign on the surface. The horizontal segment of the sign is drawn on the horizontal plane that contains the sphere center. The middle point of this segment is on a vertical plane containing the viewpoint. boolean Selects the sign (true means positive). color Position of the center. numeric Sphere ray.

Figure that uses positivecharge, getready and doitnow.
draw simplecar() Draws a cuboid and four discs in a configuration ressembling an automobile. The first three arguments of simplecar are the same as the the last seven arguments of kindofcube but grouped in colors. color Center of the cuboid that constitutes the body of the car.. color Angles defining the orientation of the car (see kindofcube). color Dimensions of the car. color Characteristics of the front wheels. redpart-distance from the front. greenpart-width of the front wheels (length of the cylinders). bluepart-wheel ray. color Same as above for the rear wheels
Figure that uses setthearena and simplecar.

Shadow Pathes

draw signalshadowvertex() Draws the shadow of a signalvertex dot. color Location of the light-blocking dot. numeric Factor of proportionality ("size of the dot"). colour Colour of the dot. path ellipticshadowpath() Produces the shadow of an elliptic path. color Position of the center. color Major or minor axis. color The other axis. path circleshadowpath() Produces the shadow of a circle. color Center of the circle. color Direction ortogonal to the circle. numeric Radius of the circle. path rigorousfearshadowpath() 3D sphere shadow. color Center position. numeric Radius.

Differential Equations

Before we proceed, be aware that solving differential equations (DE) is mainly an experimental activity. The most probable result of a procedure that atempts to solve a DE is garbage. The procedure may be unstable, the solution may be littered with singularities or something may go wrong. If you don't have a basic understanding of differential equations then skip this section, please.

path fieldlinepath() A vectorial field line is everywhere tangent to the field vectors. Two different parallel fields have the same field lines. So the field only constrains the direction of the field lines, not any kind of "speed" and, therefore, it is recommended to normalize the field before using this macro that contains a second-order Runge-Kutta method implementation. numeric Total number of steps. color Initial position. numeric Step (arc)length. text Name of the function that returns a field vector for each 3D position. path trajectorypath() The acceleration of a particle in a conservative force field is equal to the ratio (conservative force)/(particle mass). The acceleration is also equal to the second order time derivative of the particle position. This produces a second order differential equation that we solve using a second-order Runge-Kutta method implementation. numeric Total number of steps. color Initial position. color Initial velocity. numeric Time step. text Name of the function that returns a (force/mass) vector for each 3D position. path magnetictrajectorypath() The acceleration of a charged particle in a magnetic field is equal to the ratio (magnetic force)/(particle mass) but the magnetic force depends on both the velocity and the magnetic field. The acceleration is also equal to the second order time derivative of the particle position. This produces a second order differential equation that we solve using a fourth-order Runge-Kutta method implementation. numeric Total number of steps. color Initial position. color Initial velocity. numeric Time step. text Name of the function that returns a (charge)*(magnetic field)/(partcle mass) vector for each 3D position.

Renderers

draw sharpraytrace Heavy procedure that draws only the visible part of all edges of all defined faces. There's no point in using this procedure when there are no intersections beetween faces. Any how this will not work for non-convex faces nor when SphericalDistortion:=true. draw lineraytrace() Draws only the visible part of all defined lines using sequences of dots (signalvertex and PrintStep). numeric Dot size. colour Dot colour. draw faceraytrace() Draws only the visible part of all edges of all defined faces using sequences of dots (signalvertex and PrintStep). numeric Dot size. colour Dot colour. draw draw_all_test() Draws all defined edges (and lines) in a correct way independently of the kind of projection used. Can cast a shadow (but the shadow is not correct when SphericalDistortion:=true). boolean If true the lines are also drawn. draw fill_faces() Unfills and draws all faces in the order they were defined (without sorting). Can cast a shadow. text Like the argument of drawoptions but used only inside this macro and only for the edges. draw draw_invisible() This is a fast way of removing hidden lines that doesn't allow for intersecting polygons nor polygons of very different area. It works by sorting all polygons by distance to f and then by "filling" the polygons. This routine may be used to draw graphs of 3D surfaces. boolean If true polygons are sorted relatively to nearest vertex and, if false, relatively to their mass center. Choose false for surface plots. boolean If false then the polygons are painted with their FC colour modified by LightSource. If true then the next two arguments are used and the polygons are darkened proportionaly to their distance from f. colour Colour of faces. colour Colour of the edges. global getready() When you don't want to edit the source of the MetaPost program, to resort the objects so they'll be drawn correctly, use this macro and the next. string Command line that would draw some object. For instance: "rigorousfearpath(black,1);". color Reference position of that object. draw doitnow The reference positions given as arguments of previous getready calls are used to sort and draw the objects also given as string arguments to previous getready calls. Remember to initialize Nobjects:=0; before a second figure.

Nematics (Direction Fields)

Nematics are the least ordered liquid crystals. Their configurations can be described by direction fields (vector fields without arrows). The two following routines ease the task of representing their configurations.

global generatedirline() Defines a single straight line segment in a given position and with a given orientation. numeric Line index number. numeric Angle beetween the X axis and the projection of the line on the XY plane. numeric Angle beetween the line and the XY plane. numeric Line (arc)length. color Position of the line middle point. draw director_invisible() This is a direction field renderer that can sort direction lines. This routine draws straight lines of given "thickness" beetween the first all the points of all the L[]p[] lines. It is supposed to help you draw vector fields without arrows but taking care of invisibility. The lines may be generated by generatedirline or by other macros. boolean When there is no need to sort lines you may use false here. numeric "Thickness" of the direction lines boolean Use true for cyclic "direction" lines.

Figure that uses director_invisible and generatedirline.

Surface Plots

Many powerfull plotting packages like and are freely available. Because of this, FEATPOST surface plots are geared towards unusual features like equilateral triangular grid, hexagonal domain and merging together functional and parametric surface descriptions.

draw hexagonaltrimesh() Plots a functional surface on a triangular or hexagonal domain. Uses the LightSource. boolean Select the kind of domain. true for hexagonal and false for triangular. The domain is centered on the origin (black). When the domain is hexagonal two of its corners are on the -YY axis. When the domain is triangular one of its corners is on the X axis. numeric Number of small triangles on each side of the triangular domain or three times the number of small triangles on each side of the hexagonal domain. numeric Length of the triangular domain side or three times the hexagonal domain side. text Name of the function that returns the Z coordinate of a surface point of coordinates X and Y.

Figure that uses hexagonaltrimesh.
global partrimesh() Defines a parametric surface that can be drawn with draw_invisible. In the following descriptions S and T are the parameters. Remember to initialize NF. The surface is defined so that quadrangles are used whenever possible. If impossible, two triangles are used but their orientation is selected to maximize the surface smoothness. Also note that, unlike hexagonaltrimesh(), the spatial range you require to be visible is always first reshaped into a cube and second compressed or extended vertically. How much the cube is compressed or extended depends on the last numeric argument, the compression factor for Z, meaning that the final height of the cube is 2/(compression factor). Thanks to Sebastian Sturm for pointing the need to explain this. numeric Number of T steps. numeric Number of S steps. numeric Minimal T value. numeric Maximal T value. numeric Minimal S value. numeric Maximal S value. numeric Minimal X value. numeric Maximal X value. numeric Minimal Y value. numeric Maximal Y value. numeric Minimal Z value. numeric Maximal Z value. numeric Compression factor for Z values. text Name of the function that returns a surface point (of color type) for each pair (S,T).