Table 1. Coordinate systems and parameters
       
      Object coordinates (XM YM ZM) Modeling matrix
      World Coordinates (XW YW ZW) Viewing matrix
      Viewing coordinates (XV YV ZV) Projection matrix
        Normalized projection coordinates  
        (XNPC YNPC ZNPC)
      Screen Matrix
      Device coordinates (XDC YDC ZDC)  -
      Transformation Matrices
      Each transformation of the pipeline is represented by an HT_Matrix object, which specifies a 4 ´transformation matrix. In Heidi, transformation matrices are understood to define transformations as right multipliers of row vectors. Further, transformation matrices are generally handled as 4 ´ 4 matrices to allow for the most general 3D homogeneous transformation. Specifically, if M is a 4 ´ 4 matrix that represents the transformation from a coordinate system (X,Y,Z) to a coordinate system (X¢ ,Y¢ ,Z¢ ), then the coordinates are related by
      X¢ = x/w
      Y¢ = y/w
      Z¢ = z/w

      where

      [x, y, z, w ] = [X,Y,Z, 1] × M

      It follows from this basic convention that transformations are composed (or concatenated) by right multiplication of matrices: that is, if A and B are 4 ´ 4 matrices, then the product matrix AB represents the composite transformation. First apply the transformation represented by A and then the transformation represented by B.

      Modeling Matrix
      In the Heidi 3D graphics model, a scene has a 3D World Coordinate System (WCS) in which all of the objects, lights, and camera can be positioned and related to each other; however, the coordinates that the application uses to specify the geometry of 3D drawing primitives are, by definition, object coordinates, which may differ from the WCS. The rendition specifies a modeling matrix, which represents the first stage of the transformation pipeline and is applied to all 3D geometry by the 3D drawing actions in order to transform them to this WCS.

      The application can set or query the modeling transformation in a rendition by HT_Rendition::(set_)modelling_matrix. The HT_Matrix class also provides some utility methods for constructing modeling matrices from elementary transformations, such as rotations, translations, and scale transformations. Note, that these utility methods assume that the fourth row of the matrix is always { 0, 0, 0 1} that is, there are no perspective projection terms.

      Viewing Matrix

      An essential stage in rendering 3D graphics is a projection of the 3D World space to a plane surface, because all conventional display media, such as workstation screens, paper plots, film in camera, and the retina of the human eye are surfaces with only two dimensions of extent. In Heidi, as in most 3D systems, the transformation from World Coordinates to the results of the viewing projection is divided into several stages for convenience in defining the matrices. A first stage maps from World Coordinates to a reference frame, called viewing coordinates, which is aligned with a model camera.

      Specifically, the XVYV plane is the projection plane (or image plane). The origin of the viewing system is the center of the field of view, a rectangle aligned with the XV and YV axes. The ZV axis gives the projection direction in the case of parallel projection and contains the projection center in the case of perspective projection. From this alignment, it follows that the matrix that represents the projection mapping can take a particularly simple "almost diagonal" form when expressed in viewing coordinates.

      The Heidi application sets the viewing matrix through methods of the class HT_Camera or one of its derived classes. You can query the viewing matrix with the method HT_Rendition::viewing_matrix.

      Projection Matrix
      The projection matrix maps the space of the viewing coordinates to a 3D normalized projection coordinate system. It combines a 3D-to-2D with a 1D-to-1D mapping. The 3D-to-2D mapping from viewing coordinates (XV, YV, ZV) to normalized projection coordinates (XNPC, YNPC) represents a projection of the 3D viewing space to a 2D image plane subspace. This is followed by normalization of the scale to the size of a field rectangle defined by the camera object. The 1D-to-1D mapping normalizes viewing ZV values to ZNPC values, which are normalized to the distance of the projection center to the image plane.

      The projection may be perspective or parallel. In the perspective case, the projection center lies on the negative ZV axis. In the parallel case, the projection direction is parallel to the ZV axis.

      The parameters defining the projection matrix are encapsulated in the HT_Camera class, as described in the following section. You can query the projection matrix with the method HT_Rendition::projection_matrix.

      This also means that Heidi is in the left-handed coordinate system by default. Your driver needs to switch to the appropriate coordinate system based on the HT_Rendition::handedness method.

      Screen Matrix

      The screen matrix represents the last stage of the transformation pipeline, mapping normalized projection coordinates to device coordinates. This consists of a 2D mapping, which maps the field rectangle in the image plane to a rectangle on the display surface combined with a 1D mapping that remaps the ZN to a ZDC coordinate, which is useful in Z buffering.

      The field rectangle in the image plane is determined by the HT_Camera object in the rendition, as described in the following section. The image rectangle on the display surface is determined by the viewport specified in the rendition. The application can set and query this viewport with the method HT_Rendition::viewport. If the aspect ratios of the field rectangle and the viewport are the same, then the field is mapped exactly onto the viewport. If the aspect ratios are different, then the mapping is determined by a stretch flat in the camera object. In the stretched case, the field rectangle is mapped exactly to the viewport, and thus the mapping involves an anisotropic scaling. In the nonstretched case, the scaling of the screen mapping is isotropic and has the maximum value that permits the entire field rectangle to be mapped into the viewport. Of course, in this case, not all the viewport is filled by the image of the field rectangle. In every case, the center of the field rectangle is mapped to the center of the viewport.

      Camera Classes
      The rendition contains an object of class HT_Camera, which defines several of the parameters discussed previously in connection with the viewing matrix, projection matrix, and screen matrix. HT_Camera is a base class that provides a certain amount of flexibility in the way that the application programmer can construct the viewing matrix. The HT_Camera class also provides more service to the programmer to construct the viewing matrix.

      The camera object defines the viewing matrix by several intermediate quantities:

      The point target and the three basis vectors are specified with respect to World Coordinates. The basis vectors must be linearly independent but need not be orthonormal (or even mutually orthogonal). They are orthonormal in many applications. In any case, the view plane is the plane through the point target spanned by the vectors vertical_axis and horizontal_axis.

      To describe how the viewing coordinate system is determined from these data, it is convenient to introduce an intermediate camera system (XC,YC,ZC), whose origin is at the point target and whose basis vectors for the (XC,YC,ZC) coordinates are respectively horizontal_axis, vertical_axis, viewing_axis. The viewing coordinate system (XV,YV,ZV) is obtained from the camera coordinate system by a shear transformation parallel to the plane. The viewing coordinate frame also has its origin at target and has vertical_axis and horizontal_axis as basis vectors. The two shear coefficients are determined from the members of the obliquity object. Namely, HT_Camera_Obliquity.y gives the angle (measured in the metric of the camera system), between the ZC axis and the projection on the XCZC plane of the Z V axis. HT_Camera_Obliquity.x gives the angle between the ZC axis and the projection on the YCZC plane of the Z V axis.

      Note: Except for its specification in terms of angles, the transformation from camera to viewing coordinates is not a rotation. For any constant k, it maps the plane ZC=k onto the plane ZV= k.

      In the case that the camera coordinate system is orthonormal, obliquity has the usual meaning: it measures the deviation of the viewing axis from orthogonal to the view plane.
       
       
      In addition to the foregoing parameters used to define the viewing matrix, the camera object defines these other parameters:

      HT_Camera provides methods to query all of these camera parameters. These data are not all independent, and there are several different reasonable ways of selecting an independent subset from which the remaining ones can be computed. That is why Heidi computes these matrices automatically based on the chosen camera variables and does not let the user assign the camera and projection matrices directly. Your driver can simply query these matrices and construct the appropriate transformation pipeline if you are filling in the 3D routines.

      Composite Matrices
      This section described the transformation pipeline in terms of a sequence of matrices: modeling matrix, viewing matrix, projection matrix, and screen matrix. The HT_Rendition class also has the methods object_to_device, which returns the concatenation of all theses matrices, and world_to_device, which returns the concatenation of the last three.

      Clipping
      The rendition provides the method hard_clip to query and to set the clipping limits in device coordinates. The actual clipping rectangle is the intersection of this hard clip rectangle and viewport used to define the screen transformation, as previously described. The default hard clip rectangle and the default viewport are both equal to the window extent in the renderer configuration options. HT_Rendition has the methods hard_clip, restrict_hard_clip and reset_hard_clip by which the application can query and set the hard clip limits.
       

        Polygonal clipping
        Polygonal clipping of polygons and polylines can also be implemented using HT_Contour_Set.

        A contour set represents an area comprised of one or more closed loops of points, which may overlap, intersect, and/or form holes.  For  example, given the following two arrays of data:
         

          int  counts[] = {4, 3};
          HT_Point  points[] = {
               HT_Point (0,0,0), HT_Point (0,10,0),
               HT_Point (10,10,0), HT_Point (10,0,0),
               HT_Point (5,5,0), HT_Point (7,5,0), HT_Point (5,7,0));

        We can construct a contour set representing a square with a triangular hole:
         

            HT_Contour_Set  region (2, counts, points);

        There is a simplified constructor when the region is a single polygon:
         

            HT_Contour_Set  square (4, points);

        Clipping is performed simply by requesting the area intersection of the clip region and the polygon (both as Contour_Sets), with the result also returned as a Contour_Set:
         

            result = clip_region.area_intersection (polygonal_object);

        The result is a simple polygon if (result.contours() == 1) and it can be handled directly¾points are accessed from result.points() and the count is result.counts[0].  If there is more than one contour, then the result has holes or disjoint pieces.  The easiest way to deal with this case is to, in turn, call the polygonize() member function on the result in order to reduce it to separate, simple, polygonal pieces.

        Polyline clipping is handled in much the same manner.  Until a more appropriate interface can be implemented, the polyline input and result are also packed into the Contour_Set class.

        Note: that the polylines described this way do not close automatically as the polygonal forms do.

        The clip is then simply:
         

            result = clip_region.area_intersection (polyline_object);

        The output can then be drawn using something like this:
         

          int                 contours = result.contours();
          int const *         counts = result.counts();
          HT_Point const *    points = result.points();
          while (contours-- > 0) {
              renderer.draw_2d_polyline (rendition, *counts, points);