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.
See Focus Reference; 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);
Fig. 60.
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);
Fig. 61.
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);
Fig. 62.
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);
Fig. 63.
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.