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.