This is 3DLDF.info, produced by makeinfo version 4.5 from 3DLDF.texi.

This is the 3DLDF User and Reference Manual, edition 1.1.5 for 3DLDF
1.1.5.  This manual was last updated on 14 January 2004.  3DLDF is a
GNU package for three-dimensional drawing with MetaPost output.  The
author is Laurence D. Finston.

   Copyright (C) 2003, 2004 Laurence D. Finston.

     Permission is granted to copy, distribute and/or modify this
     document under the terms of the GNU Free Documentation License,
     Version 1.2 or any later version published by the Free Software
     Foundation; with no Invariant Sections, no Front-Cover Texts, and
     no Back-Cover Texts.  A copy of the license is included in the
     section entitled "GNU Free Documentation License".
   
INFO-DIR-SECTION GNU packages
START-INFO-DIR-ENTRY
* 3DLDF: (3DLDF).            3D drawing with MetaPost output.
END-INFO-DIR-ENTRY


File: 3DLDF.info,  Node: Parallel Projections,  Next: The Perspective Projection,  Prev: Projections,  Up: Projections

Parallel Projections
--------------------

   When a `Picture' is projected onto the x-y plane, the x and y-values
from the `world_coordinates' of the `Points' belonging to the objects
on the `Picture' are copied to their `projective_coordinates', which are
used in the MetaPost code written to `out_stream'.  If a `Picture' P
contains an object in the x-y plane, or in a plane parallel to the x-y
plane, then the result of `P.output(Projections::PARALLEL_X_Y)' is
more-or-less equivalent to just using MetaPost without 3DLDF.

     Rectangle r(origin, 3, 3, 90);
     Circle c(origin, 3, 90);
     c *= r.shift(0, 0, 5);
     r.draw();
     c.draw();
     current_picture.output(Projections::PARALLEL_X_Y);

   [Figure 53 not displayed.]

   If the objects do not lie in the x-y plane, or a plane parallel to
the x-y plane, then the projection will be distorted:

     current_picture.output(Projections::PARALLEL_X_Y);

   [Figure 54 not displayed.]

   `Picture::output()' can be called with an additional `real' argument
FACTOR for magnifying or shrinking the `Picture'.

     Rectangle r(origin, 4, 4, 90, 60);
     Circle c(origin, 4, 90, 60);
     c *= r.shift(0, 0, 5);
     r.filldraw(black, gray);
     c.unfilldraw(black);
     current_picture.output(Projections::PARALLEL_X_Y, .5);
     current_picture.shift(2.5);
     current_picture.output(Projections::PARALLEL_X_Y);
     current_picture.shift(1);
     current_picture.output(Projections::PARALLEL_X_Y, 2);

   [Figure 55 not displayed.]

   Parallel projection onto the x-z and z-y planes are completely
analogous to parallel projection onto the x-y plane.


File: 3DLDF.info,  Node: The Perspective Projection,  Prev: Parallel Projections,  Up: Projections

The Perspective Projection
--------------------------

   The perspective projection obeys the laws of "linear perspective".
In 3DLDF, it is performed by means of a transformation, whose effect
is, to the best of my knowledge, exactly equivalent to the result of a
perspective projection done by hand using vanishing points and rulers.

   It is very helpful to the artist to understand the laws of linear
perspective, and to know how to make a perspective drawing by hand.(1)
However, it is a very tedious and error-prone procedure (I know, I've
done it).  One of my main motivations for writing 3DLDF was so I
wouldn't have to do it anymore.

   [next figure]  shows a perspective construction, the way it could be
done by hand.  The point of view, or "focus" is located 6cm from the
picture plane, and 4cm above the ground (or x-z) plane at the point (0,
4, -6).  The rectangle R lies in the ground  plane, with the point r_0
at (2, 0, 1.5).  The right side of R, with length = 2cm lies at an angle
of 40 to the ground line, which corresponds to the intersection line of
the ground plane with the picture plane, and the left side, with length
= 5cm, at an angle of 90 degrees - 40 degrees = 50 degrees to the
ground line.

   [Figure 56 not displayed.]

   While it's possible to use 3DLDF to make a perspective construction
in the traditional way, as [the previous figure]  shows, the code for
[next figure]

   achieves the same result more efficiently:

     default_focus.set(0, 4, -6, 0, 4, 6, 6);
     Rectangle r(origin, 2, 5, 0, 40);
     Point p(2, 0, 1.5);
     r.shift(p - r.get_point(0));
     r.draw();

   [Figure 57 not displayed.]

   In [the second-to-last figure] , it was convenient to start with the
corner point r_0; if we needed the center of  R, it would have to be
found from the corner points.  However, in 3DLDF, `Rectangles' are most
often constructed about the center.  Therefore, in [next figure] , R is
first constructed about the origin, with the rotation about the y-axis
passed as an argument to the constructor.  It is then shifted such that
`*(R.points[0])', the first (or zeroth, if you will) `Point' on R comes
to lie at (2, 0, 1.5).

   Unlike the other transformations currently used in 3DLDF, the
perspective transformation is non-affine.  Affine transformations
maintain parallelity of lines, while the rules of perspective state
that parallel lines, with one exception, appear to recede toward a
"vanishing point".(2)

   In [the second-to-last figure] , the lines from r_0 to r_1 and from
r_3 to r_2 appear to vanish toward the right-hand 40 degrees  vanishing
point, while the lines from r_0 to r_3 and from r_1 to r_2 appear to
vanish toward the left-hand 50 degrees  vanishing point.  The lower the
angle of a vanishing point, the further away it is from the center of
vision, as [next figure]  shows:

   [Figure 58 not displayed.]

   In [the previous figure] , the 0.5 degrees  vanishing point is nearly
5 and 3/4 meters away from the CV, and a line receding to it will be
very nearly horizontal.  However, the distance from the focus to the CV
is only 5cm.  As this distance increases, the distance from the CV to a
given vanishing point increases proportionately.  If the distance is
30cm, a more reasonable value for a drawing, then the x-coordinate of
VP 10 degrees  is 170.138cm, that of VP 5 degrees  is 342.902cm, and
that of VP 0.5 degrees  is 3437.66cm!  This is the reason why
perspective drawings done by hand rarely contain lines receding to the
horizon at low angles.

   This problem doesn't arise when the perspective transformation is
used.  In this case, any angle can be calculated as easily as any other:

     default_focus.set(0, 4, -6, 0, 4, 6, 6);
     Rectangle r;
     Point center(0, 2);
     r.set(center, 2, 5, 0, 0, 0.5);
     r.draw();
     
     r.set(center, 2, 5, 0, 0, 2.5);
     r.draw();
     
     r.set(center, 2, 5, 0, 0, 5);
     r.draw();
     current_picture.output();

   [Figure 59 not displayed.]

   ---------- Footnotes ----------

   (1) There are many books on linear perspective for artists.  I've
found Gwen White's `Perspective.  A Guide for Artists, Architects and
Designers' to be particularly good.  Vredeman de Vries, `Perspective'
contains beautiful examples of perspective constructions.

   (2) (I believe the following to be correct, but I'm not entirely
sure.)  Let vector v be the line of sight.  By definition, the plane of
projection will be a plane p, such that vector v is normal to p.  Let
q_0 and q_1 be planes such that q_0 == q_1 or q_0 || q_1, and  q_0 is
perpendicular to p.  It follows that q_1 is perpendicular to p.  Let
l_0 and l_1 be lines, such that l_0 != l_1, l_0 || l_1, l_0 lies within
q_0, l_1 lies within q_1, l_0 is perpendicular to vector v, and l_1 is
perpendicular to vector v.  Under these circumstances, the projections
of l_0 and l_1 in p will also be parallel.


File: 3DLDF.info,  Node: Focuses Getstart,  Next: Surface Hiding,  Prev: Projections,  Up: Pictures

Focuses
=======

   The perspective transformation requires a focus;  as a consequence,
outputting a `Picture' requires an object of class `Focus'.
`Picture::output()' takes an optional pointer-to-`Focus' argument,
which is 0 by default.  If the default is used, (or 0 is passed
explicitly), the global variable `default_focus' is used.  *Note Focus
Reference; Global Variables: Focus Global Variables.

   A `Focus' can be thought of as the observer of a scene, or a camera.
It contains a `Point position' for its location with respect to
3DLDF's coordinate system, and a `Point direction', specifying the
direction where the observer is looking, or where the camera is
pointed.  The `Focus' can be rotated freely about the line PD, where P
stands for `position' and D for `direction', so a `Focus' contains a
third `Point up', to indicate which direction will be "up" on the
projection, when a `Picture' is projected.

   The projection plane q will always be perpendicular to the line PD,
or to put it another way, the line PD, is normal to q.

   Unlike the traditional perspective construction, where the distance
from the focus to the center of vision fixes both the location of the
focus in space, and its distance to the picture plane,(1) these two
parameters can be set independently when the perspective transformation
is used.  The distance from a `Focus' to the picture plane is stored in
the data member `distance', of type `real'.

   A `Focus' can be declared using two `Point' arguments for `position'
and `direction', and a `real' argument for `distance', in that order.

     Point pos(0, 5, -10);
     Point dir(0, 5, 10);
     Focus f(pos, dir, 10);
     
     Point center(2, 0, 3);
     Rectangle r(center, 3, 3);
     r.draw();
     current_picture.output(f);

   [Figure 60 not displayed.]

   The "up" direction is calculated by the `Focus' constructor
automatically.  An optional argument can be used to specify the angle by
which to rotate the `Focus' about the line PD.

     Point pos(0, 5, -10);
     Point dir(0, 5, 10);
     Focus f(pos, dir, 10, 30);
     Point center(2, 0, 3);
     Rectangle r(center, 3, 3);
     r.draw();
     current_picture.output(f);

   [Figure 61 not displayed.]

   Alternatively, a `Focus' can be declared using three `real'
arguments each for the x, y, and z-coordinates of `position' and
`direction', respectively, followed by the `real' arguments for
`distance' and the angle of rotation:

     Focus f(3, 5, -5, 0, 3, 0, 10, 10);
     Point center(2, 0, 3);
     Rectangle r(center, 3, 3);
     r.draw();
     current_picture.output(f);

   [Figure 62 not displayed.]

   `Focuses' contain two `Transforms', `transform' and `persp'.  A
`Focus' can be located anywhere in 3DLDF's coordinate system.  However,
performing the perspective projection is more convenient, if `position'
and `direction' both lie on one of the major axes, and the plane of
projection corresponds to one of the major planes.  `transform' is the
transformation which would have this affect on the `Focus', and is
calculated by the `Focus' constructor.  When a `Picture' is output
using that `Focus', `transform' is applied to all of the `Shapes' on the
`Picture', maintaining the relationship between the `Focus' and the
`Shapes', while making it easier to calculate the projection.  The
`Focus' need never be transformed by `transform'.  The actual
perspective transformation is stored in `persp'.

   `Focuses' can be moved by using one of the setting functions, which
take the same arguments as the constructors.  Currently, there are no
affine transformation functions for moving `Focuses', but I plan to add
them soon.  If 3DLDF is used for making animation, resetting the
`Focus' can be used to simulate camera movements:

     beginfig(1);
     Point pos(2, 10, 3);
     Point dir(2, -10, 3);
     Focus f;
     Point center(2, 0, 3);
     for (int i = 0; i < 5; ++i)
       {
         f.set(pos, dir, 10, (15 * i));
         Rectangle r(center, 3, 3);
         r.draw();
         current_picture.output(f);
         current_picture.clear();
         pos.shift(1, 1, 0);
         dir.rotate(0, 0, 10);
       }
     endfig(1);

   [Figure 63 not displayed.]

   In [the previous figure] , `current_picture' is output 5 times
within a single MetaPost figure.  Since the file passed to MetaPost is
called `persp.mp', the file of Encapsulated PostScript (EPS) code
containing [the previous figure]  is called `persp.1'.  To use this
technique for making an animation, it's necessary to output the
`Picture' into multiple MetaPost figures.

     Point pos(2, 10, 3);
     Point dir(2, -10, 3);
     Focus f;
     Point center(2, 0, 3);
     for (int i = 0; i < 5; ++i)
       {
         f.set(pos, dir, 10, (15 * i));
         Rectangle r(center, 3, 3);
         r.draw();
         beginfig(i+1);
         current_picture.output(f);
         endfig();
         current_picture.clear();
         pos.shift(1, 1, 0);
         dir.rotate(0, 0, 10);
       }

   Now, running MetaPost on `persp.mp' generates the EPS files
`persp.1', `persp.2', `persp.3', `persp.4', and `persp.5', containing
the five separate drawings of R.

   ---------- Footnotes ----------

   (1) I believe this to be true, but I'm not 100% certain.


File: 3DLDF.info,  Node: Surface Hiding,  Prev: Focuses Getstart,  Up: Pictures

Surface Hiding
==============

   In [next figure] , `Circle' C lies in front of `Rectangle' R.  Since
C is drawn and not filled, R is visible behind C.

     default_focus.set(1, 3, -5, 0, 3, 5, 10);
     Point p(0, -2, 5);
     Rectangle r(p, 3, 4, 90);
     r.draw();
     Point q(2, -2, 3);
     Circle c(q, 3, 90);
     c.draw();
     current_picture.output();

   [Figure 64 not displayed.]

   If instead, C is filled or filldrawn, only the parts of R that are
not covered by C should be visible:

     r.draw();
     c.filldraw();

   [Figure 65 not displayed.]

   What parts of `r' are covered depend on the point of view, i.e., the
position and direction of the `Focus' used for outputting the `Picture':

     default_focus.set(8, 0, -5, 5, 3, 5, 10);

   [Figure 66 not displayed.]

   Determining what objects cover other objects in a program for 3D
graphics is called "surface hiding", and is performed by a "hidden
surface algorithm".  3DLDF currently has a very primitive hidden
surface algorithm that only works for the most simple cases.

   The hidden surface algorithm used in 3DLDF is a "painter's
algorithm", which means that the objects that are furthest away from
the `Focus' are drawn first, followed by the objects that are closer,
which may thereby cover them.  In order to make this possible, the
`Shapes' on a `Picture' must be sorted before they are output.  They
are sorted according to the z-values in the `projective_coordinates' of
the `Points' belonging to the `Shape'.  This may seem strange, since the
projection is two-dimensional and only the x and y-values from
`projective_coordinates' are written to `out_stream'.  However, the
perspective transformation also produces a z-coordinate, which
indicates the distance of the `Points' from the `Focus' in the
z-dimension.

   The problem is, that all `Shapes', except `Points' themselves,
consist of multiple `Points', that may have different z-coordinates.
3DLDF currently does not yet have a satisfactory way of dealing with
this situtation.  In order to try to cope with it, the user can specify
four different ways of sorting the `Shapes':  They can be sorted
according to the maximum z-coordinate, the minimum z-coordinate, the
mean of the maximum and minimum z-coordinate (max + min) / 2, and not
sorted.  In the last case, the `Shapes' are output in the order of the
drawing and filling commands in the user code.  The z-coordinates
referred to are those in `projective_coordinates', and will have been
calculated for a particular `Focus'.

   The function `Picture::output()' takes a `const unsigned short'
SORT_VALUE argument that specifies which style of sorting should be
used.  The namespace `Sorting' contains the following constants which
should be used for SORT_VALUE: `MAX_Z', `MIN_Z', `MEAN_Z', and
`NO_SORT'.  The default is `MAX_Z'.

   3DLDF's primitive hidden surface algorithm _cannot_ work for objects
that intersect.  The following examples demonstrate why not:

     using namespace Sorting;
     using namespace Colors;
     using namespace Projections;
     default_focus.set(5, 3, -10, 3, 1, 1, 10, 180);
     Rectangle r0(origin, 3, 4, 45);
     Rectangle r1(origin, 2, 6, -45);
     r0.draw();
     r1.draw();
     current_picture.output(default_focus, PERSP, 1, MAX_Z);
     r0.show("r0:");
     -| r0:
     fill_draw_value == 0
     (-1.5, -1.41421, -1.41421) -- (1.5, -1.41421, -1.41421) --
     (1.5, 1.41421, 1.41421) -- (-1.5, 1.41421, 1.41421)
     -- cycle;
     
     r0.show("r0:", 'p');
     -| r0:
     fill_draw_value == 0
     Perspective coordinates.
     (-5.05646, -4.59333, -0.040577) -- (-2.10249, -4.86501, -0.102123) --
     (-1.18226, -1.33752, 0.156559) -- (-3.51276, -1.2796, 0.193084)
     -- cycle;
     
     r1.show("r1:");
     -| r1:
     fill_draw_value == 0
     (-1, 2.12132, -2.12132) -- (1, 2.12132, -2.12132) --
     (1, -2.12132, 2.12132) -- (-1, -2.12132, 2.12132)
     -- cycle;
     
     r1.show("r1:", 'p');
     -| r1:
     fill_draw_value == 0
     Perspective coordinates.
     (-5.09222, -0.995681, -0.133156) -- (-2.98342, -1.03775, -0.181037) --
     (-1.39791, -4.05125, 0.208945) -- (-2.87319, -3.93975, 0.230717)
     -- cycle;

   [Figure 67 not displayed.]

   In [the previous figure] , the `Rectangles' r_0 and r_1 intersect
along the x-axis.  The z-values of the `world_coordinates' of r_0 are
-1.41421 and 1.41421 (two `Points' each), while those of r_1 are
2.12132 and  -2.12132.  So r_1 has two `Points' with z-coordinates
greater than the z-coordinate of any `Point' on r_0, and two `Points'
with z-coordinates less than the z-coordinate of any `Point' on r_0.
The `Points' on r_0 and r_1 all have different z-values in their
`projective_coordinates', but r_1 still has a `Point' with a
z-coordinate greater than that of any of the `Points' on r_0, and one
with a z-coordinate less than that of any of the `Points' on r_0.

   In [next figure] , the `Shapes' on `current_picture' are sorted
according to the maximum z-values of the `projective_coordinates' of
the `Points' belonging to the `Shapes'.  r_1 is filled and drawn first,
because it has the `Point' with the positive z-coordinate of greatest
magnitude.  When subsequently r_0 is drawn, it covers part of the top of
r_1, which lies in front of r_0, and should be visible:

     current_picture.output(default_focus, PERSP, 1, MAX_Z);

   [Figure 68 not displayed.]

   In [next figure] , the `Shapes' on `current_picture' are sorted
according to the minimum z-values of the `projective_coordinates' of
the `Points' belonging to the `Shapes'.  `r1' is drawn and filled last,
because it has the `Point' with the negative z-coordinate of greatest
magnitude.  It thereby covers the bottom part of `r0', which lies in
front of `r1', and should be visible.

     current_picture.output(default_focus, PERSP, 1, MIN_Z);

   [Figure 69 not displayed.]

   Neither sorting by the mean z-value in the `projective_coordinates',
nor suppressing sorting does any good.  In each case, one `Rectangle'
is always drawn and filled last, covering parts of the other that lie
in front of the first.

   3DLDF's hidden surface algorithm will fail wherever objects
intersect, not just where one extends past the other in both the
positive and negative z-directions.

     Rectangle r(origin, 3, 4, 45);
     Circle c(origin, 2, -45);
     r.filldraw();
     c.filldraw(black, gray);
     current_picture.output(default_focus, PERSP, 1, NO_SORT);

   [Figure 70 not displayed.]

   Even where objects don't intersect, their projections may.  In order
to handle these cases properly, it is necessary to break up the
`Shapes' on a `Picture' into smaller `Shapes', until there are none
that intersect or whose projections intersect.  Then, any of the three
methods of sorting described above can be used to sort the `Shapes',
and they can be output.

   Before this can be done, 3DLDF must be able to find the
intersections of all of the different kinds of `Shapes'.  If 3DLDF
converted solids to polyhedra and curves to sequences of line segments,
this would reduce to the problem of finding the intersections of lines
and planes, however it does not yet do this.

   Even if it did, a fully functional hidden surface algorithm must
compare each `Shape' on a `Picture' with every other `Shape'.
Therefore, for n `Shapes', there will be n! / ((n - r)! r!)  (possibly
time-consuming) comparisons.

   [Figure 71 not displayed.]

   Clearly, such a hidden surface algorithm would considerably increase
run-time.

   Currently, all of the `Shapes' on a `Picture' are output, as long as
they lie completely within the boundaries passed as arguments to
`Picture::output()'.  *Note Pictures; Outputting: Outputting Pictures.
It would be more efficient to suppress output for them, if they are
completely covered by other objects.  This also requires comparisions,
and could be implemented together with a fully-functional hidden surface
algorithm.

   Shadows, reflections, highlights and shading are all effects
requiring comparing each `Shape' with every other `Shape', and could
greatly increase run-time.


File: 3DLDF.info,  Node: Intersections,  Next: Installing and Running 3DLDF,  Prev: Pictures,  Up: Top

Intersections
*************

   There are no functions for finding the intersection points of two (or
more) arbitrary `Paths'.  This is impossible, so long as 3DLDF outputs
MetaPost code.  3DLDF only "knows" about the `Points' on a `Path';  it
doesn't actually generate the curve or other figure that passes through
the `Points', and consequently doesn't "know" how it does this.

   In addition, an arbitrary `Path' can contain connectors.  In 3DLDF,
the connectors are merely `strings' and are written verbatim to the
output file, however, in MetaPost they influence the form of a `Path'.

   3DLDF can, however, find the intersection points of some
_non-arbitrary_ `Paths'.  So far, it can find the intersection point of
the following combinations of `Paths':

  1. Two linear `Paths', i.e., `Paths' for which `Path::is_linear()'
     returns `true' (*note Path Reference; Querying: Querying Paths.).
     In addition, the `static Point' member function
     `Point::intersection_points()' can be called with four `Point'
     arguments.  The first and second arguments are treated as the end
     points of one line, and the third and fourth arguments as the end
     points of the other.

  2. A line and a `Polygon'.  Currently, `Reg_Polygon' and `Rectangle'
     are the only classes derived from `Polygon'.

  3. Two `Polygons'.

  4. A line and a Regular Closed Plane Curve (`Reg_Cl_Plane_Curve',
     *note Regular Closed Plane Curve Reference; Intersections: Regular
     Closed Plane Curve Intersections.).  Currently, `Ellipse' and
     `Circle' are the only classes derived from `Reg_Cl_Plane_Curve'.

  5. Two `Ellipses'.  Since a `Circle' is also an `Ellipse', one or
     both of the `Ellipses' may be a `Circle'.  *Note Ellipse
     Reference; Intersections: Ellipse Intersections.

   Adding more functions for finding the intersections of various
geometric figures is one of my main priorities with respect to
extending 3DLDF.

   There are currently no special functions for finding the
intersection points of a line and a `Circle' or two `Circles'.  Since
the `class Circle' is derived from `class Ellipse',
`Circle::intersection_points()' resolves to
`Ellipse::intersection_points()', which, in turn, calls
`Reg_Cl_Plane_Curve::intersection_points()'.  This does the trick, but
it's much easier to find the intersections for `Circles' that it is for
`Ellipses'.  In particular, the intersections of two coplanar `Circles'
can be found algebraically, whereas I've had to implement a numerical
solution for the case of two coplanar `Ellipses' with different centers
and/or axis orientation.  It may also be worthwhile to write a
specialization for finding the intersection points of a `Circle' and an
`Ellipse'.

   The theory of intersections is a fascinating and non-trivial branch
of mathematics.(1) As I learn more about it, I plan to define more
`classes' to represent various curves (two-dimensional ones to start
with) and functions for finding their intersection points.

   ---------- Footnotes ----------

   (1) The books on computer graphics and the fairly elementary
mathematics books that I own or have referred to don't go into
intersections very deeply.  One that does is Fischer, Gerd.  `Ebene
Algebraische Kurven', which is a bit over my head.


File: 3DLDF.info,  Node: Installing and Running 3DLDF,  Next: Typedefs and Utility Structures,  Prev: Intersections,  Up: Top

Installing and Running 3DLDF
****************************

* Menu:

* Installing 3DLDF::
* Running 3DLDF::


File: 3DLDF.info,  Node: Installing 3DLDF,  Next: Running 3DLDF,  Prev: Installing and Running 3DLDF,  Up: Installing and Running 3DLDF

Installing 3DLDF
================

   3DLDF is available for downloading from
`http://ftp.gnu.org/gnu/3dldf'.  The official 3DLDF website is
`http://www.gnu.org/software/3dldf'.  The "tarball", i.e., the
compressed archive file `3DLDF-1.1.5.tar.gz' unpacks into a directory
called `/3DLDF-1.1.5/'.

   On a typical Unix-like system, entering the following commands at
the command line in a shell will unpack the 3DLDF distribution.  Please
note that the form of the commands may differ on your system.

     gunzip 3DLDF-1.1.5.tar.gz
     tar xpvf 3DLDF-1.1.5.tar

   The `p' option to `tar' ensures that the files will have the same
permissions as when they were packed.

   The directory `3DLDF-1.1.5/' contains a `configure' script, which
should be called from the command line in the shell, using the absolute
path of `3DLDF-1.1.5/' as the prefix argument.  For example, if the
path is `/usr/local/mydir/3DLDF-1.1.5/', `configure' should be invoked
as follows:

     cd 3DLDF-1.1.5
     configure --prefix=/usr/local/mydir/3DLDF-1.1.5/

   `configure' generates a `Makefile' from the `Makefile.in' in
`3DLDF-1.1.5/', and in each of the subdirectories `3DLDF-1.1.5/CWEB',
`3DLDF-1.1.5/DOC', and `3DLDF-1.1.5/DOC/TEXINFO'.  Now, `make install'
causes the 3DLDF to be built.  The executable is called `3dldf'.

   See the files `README' and `INSTALL' in the 3DLDF distribution for
more information.

* Menu:

* Template Functions::


File: 3DLDF.info,  Node: Template Functions,  Prev: Installing 3DLDF,  Up: Installing 3DLDF

Template Functions
------------------

   3DLDF 1.1.5 is the first release that contains "template functions",
namely `template <class C> C* create_new()', which is defined in
`creatnew.web', and `template <class Real> Real get_second_largest()',
which is defined in `gsltmplt.web'.  *Note Dynamic Allocation of
Shapes::, and *Note Get Second Largest Real::.

   In order for template functions to be instantiated correctly, their
_definitions_ must be available in each compilation unit where
specializations are declared or used.  For non-template functions, it
suffices for their _declarations_ to be available, and their
definitions are found at link-time.  For this reason, the definitions
of `create_new()' and `get_second_largest()' are in their own CWEB
files, and are written to their own header files.  The latter are
included in the other CWEB files that need them.

   In addition, `AM_CXXFLAGS = -frepo' has been added to the file
`Makefile.am' in `3DLDF-1.1.5/CWEB/', so that the C++

   compiler is called using the `-frepo' option.  The manual `Using and
Porting the GNU Compiler Collection' explains this as follows:

     "Compile your template-using code with `-frepo'. The compiler will
     generate files with the extension `.rpo' listing all of the
     template instantiations used in the corresponding object files
     which could be instantiated there; the link wrapper, `collect2',
     will then update the `.rpo' files to tell the compiler where to
     place those instantiations and rebuild any affected object files.
     The link-time overhead is negligible after the first pass, as the
     compiler will continue to place the instantiations in the same
     files."(1)

   The first time the executable `3dldf' is built, the files that use
the template functions are recompiled one or more times, and the linker
is also called several times.  This doesn't happen anymore, once the
`.rpo' files exist.

   Template instantiation differs from compiler to compiler, so using
template functions will tend to make 3DLDF less portable.  I am no
longer able to compile it on the DECalpha Personal Workstation I had
been using with the DEC C++  compiler.  *Note Ports::, for more
information.

   ---------- Footnotes ----------

   (1) Stallman, Richard M. `Using and Porting the GNU Compiler
Collection', p. 285.


File: 3DLDF.info,  Node: Running 3DLDF,  Prev: Installing 3DLDF,  Up: Installing and Running 3DLDF

Running 3DLDF
=============

   To use 3DLDF, call `make run' from the command line in the shell.
The working directory should be `3DLDF-1.1.5/' or `3DLDF-1.1.5/CWEB'.
Either will work, but the latter may be more convenient, because this
is the location of the CWEB, TeX and MetaPost files that you'll be
editing.  Alternatively, call `ldfr', which is merely a shell script
that calls `make run'.  This takes care of running `3dldf', MetaPost,
TeX, and `dvips', producing a PostScript file containing your drawings.
You can display the latter on your terminal using Ghostview or some
other PostScript viewer, print it out, and whatever else you like to do
with PostScript files.

   However, you can also perform the actions performed by `make run' by
hand, by writing your own shell scripts, by defining Emacs-Lisp
commands, or in other ways.  Even if you choose to use `make run', it's
important to understand what it does.  The following explains how to do
this by hand.

   The CWEB source files for 3DLDF are in the subdirectory
`3DLDF-1.1.5/CWEB/'.  They must be `ctangled', and the resulting C++
files must be compiled and linked, in order to create the executable
file `3dldf'.  The C++  files and header files generated by `ctangle',
the object files generated by the compiler, and the executable `3dldf'
all  reside in `3DLDF-1.1.5/CWEB/'. Therefore, the latter must be your
working directory.

   Since 3DLDF has no input routine as yet, as explained in *Note No
Input Routine::, it is necessary to add C++  code to the function
`main()' in `main.web', and/or in a separate function in another file.
In the latter case, the function containing the user code must be
invoked in `main()'.  Look for the line "Your code here!" in `main.web'.

   This is an example of what you could write in `main()'.  Feel free
to make it more complicated, if you wish.

     beginfig(1);
     default_focus.set(2, 3, -10, 2, 3, 10, 20);
     Rectangle R(origin, 5, 3);
     Circle C(origin, 3, 90);
     C.half(180).filldraw(black, light_gray);
     R.filldraw();
     C.half().filldraw(black, light_gray);
     Point p = C.get_point(4);
     p.shift(0, -.5 * p.get_y());
     p.label("$C$", "");
     Point q = R.get_mid_point(0);
     q.shift(0, 0, -.5 * q.get_z());
     q.label("$R$", "");
     current_picture.output(default_focus, PERSP, 1, NO_SORT);
     endfig(1);

   [Figure 72 not displayed.]

  1. Save `main.web', and any other CWEB files you've changed.  Since
     these files have changed, they must be `ctangled', and the
     resulting C++  files must be recompiled.  If you've changed any
     files other than `main.web', `ctangle' will also generate a header
     file for each of these files.  If a header file differs from the
     version that existed before `ctangle' was run, all of the C++
     files that depend on it must be recompiled.  Then `3dldf' must be
     relinked.  To do this, call `make 3dldf' from the command line.

     If you've made any errors in typing your code, the compiler should
     have issued error messages, so go back into the appropriate CWEB
     file and correct your errors.  Then call `make 3dldf' again.

  2. Call `CWEB/3dldf' at the command line.  It writes a file of
     MetaPost code called `3DLDFput.mp'.

  3. Run MetaPost on the file `3DLDFmp.mp', which inputs `3DLDFput.mp'.

          mpost 3DLDFput

     The result is an Encapsulated PostScript file `3DLDFput.'<integer>
     for each figure in your drawing.

  4. The file `3DLDFtex.tex' should contain code for including the
     `3DLDFput.'<integer> files.  This is an example taken from the
     `3DLDFtex.tex' included in the distribution.  You may change it to
     suit your purposes.

          \vbox to \vsize{\vskip 2cm
          \line{\hskip 2cm Figure 1.\hss}%
          \vfil
          \line{\hskip 2cm\epsffile{3DLDFmp.1}\hss}%
          \vss}

  5.  Run TeX on `3DLDFtex.tex' to produce the DVI file, `3DLDFtex.dvi'.

          tex 3DLDFtex

  6. Run `dvips' on the DVI file to produce the PostScript file,
     `3DLDFtex.ps'.

          dvips -o 3DLDFtex.ps 3DLDFtex

  7. `3DLDFtex.ps' can be viewed using Ghostview, it can be printed
     using `lpr' (on a Unix-like system),  you can convert it to PDF
     with `ps2pdf', or to some other format using the appropriate
     program.

   I sincerely hope that it worked.  If it didn't, ask your local
computer wizard for help.

   On the computer I'm using, I found that `special' arguments for
setting `landscape' and `papersize' in TeX files for DIN A3 landscape
didn't work.  Ghostview cut off the right sides of the drawings.  Nor
did it work to call `dvips -t landscape -t a3'.  This caused an error
message which said that `landscape' would be ignored.  When I called
`dvips' with the `-t landscape' option alone, it worked, and Ghostview
showed the entire drawing.

   Another problem was Adobe Acrobat.  It would display the entire DIN
A3 page, but not always in landscape format.  I was unable to find a
way of rotating the pages in Acrobat.  I finally found out, that if I
included even a single letter of text in a label, Acrobat would display
the document correctly.

* Menu:

* Converting EPS Files::
* Command Line Arguments::


File: 3DLDF.info,  Node: Converting EPS Files,  Next: Command Line Arguments,  Prev: Running 3DLDF,  Up: Running 3DLDF

Converting EPS Files
--------------------

   ImageMagick (http://www.imagemagick.org) is a "collection of tools
and libraries" for image manipulation.  It provides a `convert' utility
which can convert images from one format to another.  It can convert
structured PostScript (PS) to to Portable Network Graphics (PNG)
(http://www.libpng.org/pub/png/index.html), but _not_ EPS (Encapsulated
PostScript) to PNG.  Nor can it convert EPS to structured PostScript.

   It is possible to have MetaPost generate structured PostScript
directly by including the command `prologues:=1;' at the beginning of
the MetaPost input.  However, this "generally doesn't work when you use
TeX fonts."(1) This is a significant problem if your labels contain
math mode material, and you haven't already taken steps to ensure that
appropriate fonts will be used in the PS output.

   In the following, I describe the _only way_ I've found to convert an
EPS image to PNG format while still using TeX fonts.  There may be
other and better ways of doing this, but I haven't found them.

  1. Assume the EPS image is in the file `3DLDFmp.1' Include the EPS
     image in a TeX file which looks like this:

          \advance\voffset by -1in
          \advance\hoffset by -1in
          \nopagenumbers
          \input epsf
          \epsfverbosetrue
          \def\epsfsize#1#2{#1}
          \setbox0=\vbox{\epsffile{3DLDFmp.1}}
          \vsize=\ht0
          \hsize=\wd0
          \special{papersize=\the\wd0,\the\ht0}
          \box0
          \bye

     Do not name this file `3DLDFmp.1.tex'!  While this worked fine for
     me on a DECalpha Personal Workstation running under Tru64 Unix
     5.1, with TeX, Version 3.1415 (C version 6.1), and dvipsk 5.58f,
     it failed on a PC Pentium II XEON under Linux 2.4, with TeX,
     Version 3.14159 (Web2C 7.4.5), and dvips(k) 5.92b, kpathsea
     version 3.4.5, with the following error message:
          ``No BoundingBox comment found in file examples.1; using defaults''

     The resulting PS image had the wrong size and the the graphic was
     positioned improperly.

     Apparently, it confuses the EPSF macros when the name of an
     included image is the same as `\jobname'.  So, for this example,
     let's call it `3DLDFmp.1_.tex'.

     You don't really need to call the macro `\epsfverbosetrue'.  If you
     do, it will print the measurements of the bounding box and other
     information to standard output.(2)

  2. Run `tex 3DLDFmp.1_.tex'.

  3. Run `dvips -o 3DLDF.1.ps 3DLDFmp.1_.dvi'.

  4. Run `convert 3DLDF.1.ps 3DLDFmp.1.png'.

   ImageMagick supplies a ``display'' utility, which can be used to
display the PNG image:

     display 3DLDFmp.1.png

   It can be included in an HTML document as follows:

     <img src="3DLDFmp.1.png"
              alt="[Fig. 1]."

* Menu:

* Converting EPS Files ELISP::

   ---------- Footnotes ----------

   (1) Hobby, `A User's Manual for MetaPost', pp. 21-22.

   (2) Rokicki, `Dvips: A DVI-to-PostScript Translator', p. 24.


File: 3DLDF.info,  Node: Converting EPS Files ELISP,  Prev: Converting EPS Files,  Up: Converting EPS Files

Emacs-Lisp Functions
....................

   The file `3DLDF-1.1.5/CWEB/cnepspng.el' contains definitions of two
Emacs-Lisp functions that can be used to convert Encapsulated
PostScript (EPS) files to structured PostScript (PS) and Portable
Network Graphics (PNG) files.

 - Emacs-Lisp function: convert-eps filename do-not-delete-files
     Converts an EPS image file to the PS and PNG formats.

     If called interactively, `convert-eps' prompts for the filename,
     including the extension, of an EPS image file.  It follows the
     procedure described above in *Note Converting EPS Files::, to
     create `FILENAME.ps' and `FILENAME.png'.

     If DO-NOT-DELETE-FILES is `nil', the `.tex', `.dvi', and `.log'
     files will be deleted.  This is the case when `convert-eps' is
     called interactively with no prefix argument.  If `convert-eps' is
     called interactively with a prefix argument, or non-interactively
     with a non-`nil' DO-NOT-DELETE-FILES argument, these files will
     not be deleted.

 - Emacs-Lisp function: convert-eps-loop arg start end
     Converts a set of EPS image files to the PS and PNG formats.  The
     files must all have the same filename, and the extensions must
     form a range of positive integers.  For example,
     `convert-eps-loop' can be used to convert the files `3DLDFmp.1',
     `3DLDFmp.2', and `3DLDFmp.3' to `3DLDFmp.1.ps', `3DLDFmp.2.ps', and
     `3DLDFmp.3.ps' on the one hand, and `3DLDFmp.1.png',
     `3DLDFmp.2.png', `3DLDFmp.3.png' on the other.

     If `convert-eps-loop' is called interactively, it prompts for
     FILENAME _with no extension_ and the starting and ending numbers
     of the range.

     For all i \in \INT and START \le i \le END, `convert-eps-loop'
     checks whether a file named `FILENAME.I' exists.  If it does, it
     calls `convert-eps', passing `FILENAME.I' as the latter's FILENAME
     argument.

     DO-NOT-DELETE-FILES is also passed to `convert-eps'.  If it's
     `nil', the `.tex', `.dvi', and `.log' files will be deleted.  This
     is the case when `convert-eps-loop' is called interactively with
     no prefix argument.  If `convert-eps-loop' is called interactively
     with a prefix argument, or non-interactively with a non-`nil'
     DO-NOT-DELETE-FILES argument, these files will not be deleted.


File: 3DLDF.info,  Node: Command Line Arguments,  Prev: Converting EPS Files,  Up: Running 3DLDF

Command Line Arguments
----------------------

   `3dldf' can be called with the following "command line arguments".

`--help'
     Prints information about the valid command line options to standard
     output and exits with return value 0.

`--silent'
     Suppresses some output to standard output and standard error when
     `3dldf' is run

`--verbose'
     Causes status information to be printed to standard output when
     `3dldf' is run.

`--version'
     Prints the version number of 3DLDF to standard output and exits
     with return value 0.

   Currently, `3dldf' can only handle long options.  `-' cannot be
substituted for `--'.  However, the names of the options themselves can
be abbreviated, as long as the abbreviation is unambigous.  For
example, `3dldf --h' and `3dldf --verb' are valid, but `3dldf --ver' is
not.


File: 3DLDF.info,  Node: Typedefs and Utility Structures,  Next: Global Constants and Variables,  Prev: Installing and Running 3DLDF,  Up: Top

Typedefs and Utility Structures
*******************************

   3DLDF defines a number of data types for various reasons, e.g., for
the sake of convenience, for use in conditional compilation, or as
return values of functions.  Some of these data types can be defined
using `typedef', while others are defined as `structs'.

   The typedefs and utility structures described in this chapter are
found in `pspglb.web'.  Others, that contain objects of types defined
in 3DLDF, are described in subsequent chapters.

 - typedef: real
     Synonymous either with `float' or `double', depending on the
     values of the preprocessor variables `LDF_REAL_FLOAT' and
     `LDF_REAL_DOUBLE'.  The meaning of `real' is determined by means
     of conditional compilation.  If `real' is `float', 3DLDF will
     require less memory than if `real' is `double', but its
     calculations will be less precise.  `real' is "typedeffed" to
     `float' by default.

 - typedef: real_pair first second
     Synonymous with `pair<real, real>'.

 - struct: real_triple first second third
     All three data elements of `real_triple' are `reals'.  It also has
     two constructors, described below.  There are no other member
     functions.

 - Constructor: void real_triple (void)
 - Constructor: void real_triple (real A, real B, real C)
     The constructor taking no arguments sets `first', `second', and
     `third' to 0.  The constructor taking three `real' arguments sets
     `first' to A, `second' to B, and `third' to C.

 - typedef: Matrix
     A `Matrix' is a 4 X 4  array of `real', e.g., `Matrix M;' == `real
     M[4][4]'.  It is used in `class Transform' for storing
     transformation matrices.  *Note Transforms::, and *Note Transform
     Reference::, for more information.

 - typedef: real_short first second
     Synonymous with `pair<real, signed short>'.  It is the return type
     of `Plane::get_distance()'.

 - typedef: bool_pair first second
     Synonymous with `pair<bool, bool>'.

 - typedef: bool_real first second
     Synonymous with `pair<bool, real>'.


File: 3DLDF.info,  Node: Global Constants and Variables,  Next: Dynamic Allocation of Shapes,  Prev: Typedefs and Utility Structures,  Up: Top

Global Constants and Variables
******************************

   The global constants and variables described in this chapter are
found in `pspglb.web'.  Others, of types defined in 3DLDF, are
described in subsequent chapters.

 - Constants: bool ldf_real_float
 - : bool ldf_real_double
     Set to 0 or 1 to match the values of the preprocessor macros
     `LDF_REAL_FLOAT' and `LDF_REAL_DOUBLE'.  The latter are used for
     conditional compilation and determine whether `real' is
     "typedeffed" to `float' or `double', i.e., whether `real' is made
     to be a synonym of `float' or `double' using `typedef'.

     `ldf_real_float' and `ldf_real_double' can be used to control
     conditional expressions in non-conditionally compiled code.

 - Constant: real PI
     The value of `PI' is calculated as 4.0 * arctan(1.0).  I believe
     that a preprocessor macro "`PI'" was available when I compiled
     3DLDF using the DEC C++  compiler, and that it wasn't, when I used
     GNU CC under Linux, but I'm no longer sure.

 - Variable: valarray <real> null_coordinates
     Contains four elements, all 0.  Used for resetting the sets of
     coordinates belonging to `Points', but only when the DEC C++

     compiler is used.  This doesn't work when GCC is used.

 - Constant: real INVALID_REAL
     Actually, `INVALID_REAL' is the largest possible `real' value
     (i.e., `float' or `double') on a given machine.  So, from the
     point of view of the compiler, it's not invalid at all.  However,
     3DLDF uses it to indicate failure of some kind.  For example, the
     return value of a function returning `real' can be compared with
     `INVALID_REAL' to check whether the function succeeded or failed.

     An alternative approach would be to use the exception handling
     facilities of C++ .  I do use these, but only in a couple of
     places, so far.

 - Constant: real_pair INVALID_REAL_PAIR
     `first' and `second' are both `INVALID_REAL'.

 - Constant: real INVALID_REAL_SHORT
     `first' is `INVALID_REAL' and `second' is 0.

 - Variable: real MAX_REAL
     The largest `real' value permitted in the the elements of
     `Transforms' and the coordinates of `Points'.  It is the second
     largest `real' value (i.e., `float' or `double') on a given
     machine (`INVALID_REAL' is the largest).

     `MAX_REAL' is a variable, but it should be used like a constant.
     In other words, users should never reset its value.  It can't be
     declared `const' because its value must be calculated using
     function calls, which can't be done before the entry point of the
     program, i.e., `main()'.  Therefore, the value of `MAX_REAL' is
     calculated at the beginning of `main()'.

 - Variable: real MAX_REAL_SQRT
     The square root of `MAX_REAL'.

     `MAX_REAL_SQRT' is a variable, but it should be used like a
     constant. In other words, users should never reset its value.  It
     can't be declared `const' because its value is calculated using the
     `sqrt()' function, which can't be done before the entry point of
     the program, i.e., `main()'.  Therefore, the value of
     `MAX_REAL_SQRT' is set after `MAX_REAL' is calculated, at the
     beginning of `main()'.

     `MAX_REAL_SQRT' is used in `Point::magnitude()' (*note Vector
     Operations::).  The magnitude of a `Point' is found by using the
     formula \sqrtx^2 + y^2 + z^2.  x, y, and z are all tested against
     `MAX_REAL_SQRT' to ensure that x^2, y^2, and z^2 will all be less
     than or equal to `MAX_REAL' before trying to calculate them.

     Metafont implements an operation called "Pythagorean addition",
     notated as "`++'"which can be used to calculate distances without
     first squaring and then taking square roots:(1) a++b == \sqrt(a^2
     + b^2) and a++b++c == \sqrt(a^2 + b^2 + c^2).  This makes it
     possible to calculate distances for greater values of a, b, and c,
     that would otherwise cause floating point errors.  Metafont also
     implements the inverse operation "Pythagorean subtraction",
     notated as "`+-+'": a+-+b == \sqrt(a^2 - b^2).  Unfortunately,
     3DLDF implements neither Pythagorean addition nor subtraction as
     yet, but it's on my list of "things to do".

   ---------- Footnotes ----------

   (1) Knuth, Donald E. `The Metafontbook', p. 66.


File: 3DLDF.info,  Node: Dynamic Allocation of Shapes,  Next: System Information,  Prev: Global Constants and Variables,  Up: Top

Dynamic Allocation of Shapes
****************************

 - Template function: `template <class C> C*' create_new (const C* ARG)
 - Template function: `template <class C> C*' create_new (const C& ARG)
     These functions dynamically allocate an object derived from
     `Shape' on the free store, returning a pointer to the type of the
     `Shape' and setting `on_free_store' to `true'.

     If a non-zero pointer or a reference is passed to `create_new()',
     the new object will be a copy of ARG.

     It is not possible to instantiate more than one specialization of
     `create_new()' that takes no argument, because calls to these
     functions would be ambiguous.  If the new object is not meant to
     be a copy of an existing one, `0' must be passed to `create_new()'
     as its argument.

     `create_new' is called like this:

          Point* p = create_new<Point>(0);
          p->show("*p:");
          -| *p: (0, 0, 0)
          
          Color c(.3, .5, .25);
          Color* d = create_new<Color>(c);
          d->show("*d:");
          -|
          *d:
          name ==
          use_name == 0
          red_part == 0.3
          green_part == 0.5
          blue_part == 0.25
          
          
          Point a0(3, 2.5, 6);
          Point a1(10, 11, 14);
          Path q(a0, a1);
          Path* r = create_new<Path>(&q);
          r->show("*r:");
          -|
          *r:
          points.size() == 2
          connectors.size() == 1
          (3, 2.5, 6) -- (10, 11, 14);

     Specializations of this template function are currently declared
     for `Color', `Point', `Path', `Reg_Polygon', `Rectangle',
     `Ellipse', `Circle', `Solid', and `Cuboid'.


File: 3DLDF.info,  Node: System Information,  Next: Color Reference,  Prev: Dynamic Allocation of Shapes,  Up: Top

System Information
******************

   The functions described in this chapter are all declared in the
`namespace System'.  They are for finding out information about the
system on which 3DLDF is being run.  They are declared and defined in
`pspglb.web', except for the template function `get_second_largest()',
which is declared and defined in `gsltmplt.web'.

   There are two reasons for this.  The first is that template
definitions must be available in the compilation units where
specializations are instantiated.  I therefore write the template
definition of `get_second_largest()' to `gsltmplt.h', so it can be
included by the CWEB files that need it, currently `main.web' only.  If
I wrote it to `pspglb.h', it would be included by all of the CWEB files
except for `loader.web', causing unnecessarily bloated object code.

   The other reason is because of the way way 3DLDF is built using
Automake and make.  I originally tried to define `get_second_largest()'
in `pspglb.web' and wrote the definition to `gsltmplt.cc', which is no
problem with CWEB.  However, I was unable to express the dependencies
among the CWEB, C++ , and object files in such a way that 3DLDF was
built properly.

   Therefore all template functions will be put into files either by
themselves, or in small groups.

* Menu:

* Endianness::
* Register Width::
* Get Second Largest Real::

