Node: Pictures, Next: , Previous: Solid Figures, Up: Top



Pictures

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);
     


[Figure 50. Not displayed.]

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);
     


[Figure 51. Not displayed.]

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();
     


[Figure 52. Not displayed.]

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.