Applying drawing and filling operations to the drawable objects described
in the previous chapters isn't enough to produce output. These
operations merely modify the Picture
object that was passed to
them as an argument (current_picture
, by default).
Pictures
in 3DLDF are quite different from pictures
in
MetaPost.
When a drawing or filling operation is applied to an object O, a
copy of O, C, is allocated on the free store, a pointer to
Shape
S is pointed at C, and S is pushed onto
the vector<Shape*> shapes
on the Picture
P, which
was passed as an argument to the drawing or filling command. The
arguments for the pen,
dash pattern, Color
, and any others, are used to set the
corresponding data members of C (not O).
In order to actually
cause MetaPost code to be written to the output file, it is necessary
to invoke P.output()
. Now, the appropriate version of
output()
is applied to each of the objects pointed to
by a pointer on P.shapes
. output()
is a pure
virtual function in Shape
, so all classes derived from
Shape
must have an output()
function. So, if
shapes[0]
points to a Path
,
Path::output()
is called, if
shapes[1]
points to a Point
,
Point::output()
is called, and if shapes[2]
points to an
object of a type derived from Solid
, Solid::output()
is
called.
Point
, Path
, and Solid
are namely the only classes
derived from Shape
for which a version of output()
is defined. All
other Shapes
are derived from one of these classes.
These output()
functions then write the MetaPost code to the
output file through the output file stream out_stream
.
beginfig(1); default_focus.set(0, 0, -10, 0, 0, 10, 10); Circle c(origin, 3, 90); c.draw(); c.shift(1.5); c.draw(); current_picture.output(); endfig(1);
Fig. 50.
The C++
code for [the previous figure]
starts with the command
beginfig(1)
and ends with the command
endfig(1)
.
They simply write "beginfig(<arg>
)
" and
"endfig()
" to
out_stream
,
The optional
unsigned int
argument to endfig()
is not written to
out_stream
, it's merely
"syntactic sugar" for the user.
In MetaPost, the endfig
command causes output and then clears
currentpicture
. This is not the case in 3DLDF, where
Picture::output()
and Picture::clear()
must
be invoked explicitly:
beginfig(1); Point p0; Point p1(1, 2, 3); p0.draw(p1); current_picture.output(); endfig(1); beginfig(2); current_picture.clear(); Circle C(origin, 3); C.fill(); current_picture.output(); endfig(2);
In [next figure]
, two Pictures
are used within a single figure.
beginfig(1); Picture my_picture; default_focus.set(0, 0, -10, 0, 0, 10, 10); Circle c(origin, 3, 90); c.draw(my_picture); my_picture.output(); c.shift(1.5); c.fill(light_gray); current_picture.output(); endfig(1);
Fig. 51.
Multiple objects, or complex objects made up of sub-objects, can be
stored in a Picture
, so that operations can be applied to them
as a group:
default_focus.set(7, 5, -10, 7, 5, 10, 10); Cuboid c0(origin, 5, 5, 5); c0.shift(0, 0, 3); c0.draw(); Circle z0(c0.get_rectangle_center(0), 2.5, 90, 0, 0, 64); z0.draw(); Circle z1(z0); z1.shift(0, 0, -1); z1.draw(); int i; int j = z0.get_size(); for (i = 0; i < 8; ++i) z0.get_point(i * j/8).draw(z1.get_point(i * j/8)); Cuboid c1(c0.get_rectangle_center(4), 5, 3, 3); c1.shift(0, 2.5); c1.draw(); Rectangle r0 = *c1.get_rectangle_ptr(3); Point p[10]; for (i = 0; i < 4; ++i) p[i] = r0.get_point(i); p[4] = r0.get_mid_point(0); p[5] = r0.get_mid_point(2); p[6] = p[4].mediate(p[5], 2/3.0); Circle z2(p[6], 2, 90, 90, 0, 16); z2.draw(); Circle z3 = z2; z3.shift(3); z3.draw(); j = z2.get_size(); for (i = 0; i < 8; ++i) z2.get_point(i * j/8).draw(z3.get_point(i * j/8)); p[7] = c0.get_rectangle_center(2); p[7].shift(-4); p[8] = c0.get_rectangle_center(3); p[8].shift(4); current_picture.output(); current_picture.rotate(45, 45); current_picture.shift(10, 0, 3); current_picture.output();
Fig. 52.
Let's say the complex object in [the previous figure]
represents a
furnace. From the point of view of 3DLDF, however, it's not an object
at all, and the drawing consists of a collection of unrelated
Cuboids
, Circles
, Rectangles
, and Paths
.
If we hadn't put it into a Picture
, we could still have rotated
and shifted it, but only by applying the operations to each of the
sub-objects individually.
One consequence of the way Pictures
are output in 3DLDF is, that
the following code will not work:
beginfig(1); Point p(1, 2); Point q(1, 3); out_stream << "pickup pencircle scaled .5mm;" << endl; origin.draw(p); out_stream << "pickup pensquare xscaled .3mm rotated 30;" << endl; origin.draw(q); current_picture.output(); endfig();
This is the MetaPost code that results:
beginfig(1); pickup pencircle scaled .5mm; pickup pensquare xscaled .3mm rotated 30; draw (0.000000cm, -3.000000cm) -- (1.000000cm, -1.000000cm); draw (0.000000cm, -3.000000cm) -- (1.000000cm, 0.000000cm); endfig;
It's perfectly legitimate to write
raw MetaPost code to out_stream
, as in lines 4 and 6 of this
example. However, the draw()
commands do not cause any output to
out_stream
. The MetaPost drawing commands are written to
out_stream
when current_picture.output()
is called.
Therefore, the pickup
commands are "bunched up" before the
drawing commands.
In this example,
setting currentpen
to pencircle scaled .5mm
has no effect,
because it is immediately reset to
pensquare xscaled .3mm rotated 30
in the MetaPost code, before
the draw
commands.
It is not possible to change currentpen
in this way within a
Picture
.
Since the draw()
commands in the 3DLDF
code didn't specify a pen argument,
currentpen
with its final value is used for both of the MetaPost
draw
commands. For any given invocation of
Picture::output()
, there can only be one value of
currentpen
. All other pens must be passed as arguments to the
drawing commands.