Geometry Coordinates

In HOOPS, as in most graphics systems, points in space are specified with Cartesian coordinates. Coordinates in 3D space are commonly called x, y, and z. HOOPS commands always take three coordinates; thus, all points in HOOPS are 3D. If we want to specify something in 2D, we simply use a z coordinate of zero. HOOPS will notice the value and will automatically switch to using 2D routines as appropriate.

Coordinates are specified in HOOPS using single-precision floating-point numbers.

Points

HOOPS is inconsistent in the way that points are specified. A point is always three single-precision floating-point numbers, but points are passed to HOOPS in several different ways:

Some HOOPS commands use three separate arguments to pass the three coordinates of a point. Each argument is a floating-point number. For example,

    float x = 0, y = 0, z = 0;
    float x1 = 1, y1 = 1, z1 = 1, x2 = 3, y2 = 3, z2 = 3;
    HC_Insert_Text(x, y, z, "hello, world!");
    HC_Insert_Line(x1, y1, z1, x2, y2, z2);

Some HOOPS commands take the points themselves as arguments. These routines always expect the address of the point. For example:

    HC_Insert_Circle(&point1, &point2, &point3);

Physically, Visualize requires you to pass it an address of three consecutive single-precision floating-point numbers, but there are several ways that you can create such a pointer. The two most common ways are as the address of an array of three floats, or as the address of a structure containing three floats. In the following code, the point p1 is an array of three floats, and p2 is a structure containing three floats.

    float p1[3]; // array of three floats

    struct Point {
        float x, y, z;
    };

    Point p2; // structure containing three floats

The actual declaration in HOOPS commands for a point argument (in hc.h) is a void *. Because the memory organization of an array of three floats and that of a struct containing three floats are the same, either can be passed (as can any other data structure with the same memory organization). Unfortunately, this flexibility is purchased at the expense of type checking; later in this section, we shall describe a way to restore strong type checking to points.

If you use an array of three floats for a point, rather than a struct, then the “address of” (&) operator is optional when you pass a point to a HOOPS command, because arrays are passed as pointers in C++. The disadvantage of using an array is that the individual x, y, and z components of a point are accessed via a subscript, rather than via a name (for example, the x coordinate is p1[0], rather than p1.x), and assigning one point to another can be tricky because simply assigning one array to another does a pointer assignment.

Some HOOPS commands take an array of points as an argument (usually preceded by a count of how many points are in the array):

    HC_Insert_Polygon(5, array_of_5_points);
    HC_Insert_Polyline(9, array_of_9_points);

In this case, you can use either a 2D array or an array of point structures (or some other data structure that has the same memory organization):

        float array_of_5_points[3][5];
        Point array_of_9_points[9];

You can tell HOOPS which kind of point you will be using, so that it can do type checking. You just #define the preprocessor variable POINT to be the C++ type name that you are using:

struct Point { float x, y, z; };
#define HC_POINT Point
#include <hc.h>

Definition of type Point to be a struct of three floats.

Note that you must define ::POINT before the file hc.h is included. This code example changes the declarations of all the HOOPS routines that take a point argument to expect a struct of three floats. If you define a point to be a struct of three floats in this way, then you cannot use an array of three floats for a point, of course.

This guide will always define a point to be a struct of three floats. Actually, the definition of struct Point that we shall be using also has a member function to allow the coordinates of a point to be set with a single function call (as shown in the program below). Note that the z coordinate defaults to 0.0 so we can use the same function to set 2D points.

class HPoint {
  public:
    float x; // The x-coordinate of a 3-dimensional point.
    float y; // The y-coordinate of a 3-dimensional point.
    float z; // The z-coordinate of a 3-dimensional point.

    HPoint()
    {
        x = 0.0f;
        y = 0.0f;
        z = 0.0f;
    };

    HPoint(float X, float Y, float Z = 0.0f)
    {
        x = X;
        y = Y;
        z = Z;
    };

    HPoint(HPoint const* p)
    {
        x = p->x;
        y = p->y;
        z = p->z;
    };

    HPoint(HPoint const& p)
    {
        x = p.x;
        y = p.y;
        z = p.z;
    };

    /* Set packs an HPoint object with coordinate values.  */
    inline void Set(float X, float Y, float Z = 0.0f)
    {
        x = X;
        y = Y;
        z = Z;
    };

    inline void Set(HPoint* p)
    {
        x = p->x;
        y = p->y;
        z = p->z;
    };

    inline void Set(HPoint const* p)
    {
        x = p->x;
        y = p->y;
        z = p->z;
    };

    inline void Set(HPoint& p)
    {
        x = p.x;
        y = p.y;
        z = p.z;
    };

    inline void Set(HPoint const& p)
    {
        x = p.x;
        y = p.y;
        z = p.z;
    };

    /* Add increases or decreases the coordinate values of an existing HPoint object.  */
    inline void Add(float X, float Y, float Z = 0.0)
    {
        x += X;
        y += Y;
        z += Z;
    };
};

Definition of type HPoint.

Double-Precision Data

HOOPS Visualize offers double-precision variants of the Insert/Show/Edit routines for the various graphical primitives, along with double-precision variants of camera, modelling matrix, and computation functions. Starting with v20, the double-precision functions will maintain double-precision data through the entire rendering pipeline, in order to ensure correct rendering and computational precision for such data. Prior to v20, double-precision values were truncated to single-precision, potentially resulting in incorrect rendering or computation results.

Double-precision function variants are named with the prefix D but are otherwise identical to the traditional name. For example:

Function name

C++ function

C# function

Java function

Insert_Circle

HC_DInsert_Circle

HCS.DInsert_Circle

HJ.DInsert_Circle

Compute_Coordinates

HC_DCompute_Coordinates

HCS.DCompute_Coordinates

HJ.DCompute_Coordinates

Set_Camera

HC_DSet_Camera

HCS.DSet_Camera

HJ.DSet_Camera

A list of double-precision function variants can be found in the double-precision function index. Additionally, the types HC_POINT, HC_VECTOR, and HC_PLANE have corresponding variants HC_DPOINT, HC_DVECTOR, and HC_DPLANE.

You may notice that some single-precision functions, such as Insert_Ink(), accept double-precision values as parameters. However, this is only a convenience as the values will be truncated to single-precision internally. Some functions, such as DShow_Shell(), are listed as double-precision but take no formal double parameters. In this case, you may see “out” parameters like HC_DPOINT which HOOPS will populate with doubles.

Heterogeneous Data Support

HOOPS 3DF allows you to use heterogeneous data for single and double precision information, where you can mix both single and double-precision entities in the same scene graph. Double-precision functions that operate on an HC_KEY will also correctly handle single-precision geometry. This includes the various double-precision functions. For example, a double-precision shell can be used to detect collisions with single-precision primitives.

If you need to check whether a certain key represents double-precision data, you may do so using the function Check_Double_Precision(). If necessary, you may then use Convert_Precision() to convert the entity between double and single-precision data. This may be necessary in order to conform to the performance recommendations (see Performance and memory notes, below). Specific uses of both functions are covered in the reference manual.

Cameras and Modeling Matrices

If your scene contains double precision data, you might require a double-precision camera if you either want to provide a high degree of ‘fractional’ resolution when zooming extremely close, or a high degree of ‘exponent’ resolution when zooming far out. However, if you do not require either level of resolution, you should use a single-precision camera. (For example, you may not require a high degree of fractional resolution when zooming very far in.)

If a double-precision camera is contained in the scene and it exercises the double-precision range, this will require any data (even if it is single-precision) to be treated as double-precision data at draw time. This is because single-precision data, by necessity, must be ‘promoted’ to double-precision information after being transformed through a double-precision camera.

The same considerations above also apply to double-precision modeling matrices.

Performance and Memory Notes

Visualize will take advantage of GPU acceleration when rendering double-precision data on certain GPUs. If GPU acceleration is not available and a segment contains double-precision primitives, or if any primitives (either single or double) are being viewed by a double-precision camera or multiplied by double-precision modelling matrices, the primitives are drawn in immediate mode (specifically, all camera/matrix operations such as 3d->dc conversion will be performed on the CPU). Therefore, it is important to remain aware of performance for single-precision vs double-precision data. Visualize’s ability to utilize GPU acceleration can boost performance significantly, depending on the scene graph and graphics card.

In order to help ensure the best combination of performance and visual results, one should consider employing the various framerate maintenance strategies supported by either HOOPS/MVO (fixed-framerate), or HOOPS/3DGS (Update_Display_Timed combined with the various ‘culling’ heuristics)

Technical Notes on Single vs. Double-Precision Floating Point Values

  • Single-precision requires 32 bits, and gives you about 6 significant digits. 6 significant digits can store 3.14159, 0.0000314159, 314159000, but not 3000.014159. Numbers with greater than 6 significant digits stored as single-precision are rounded to the nearest legal value.

  • Double-precision requires 64 bits, and gives you about 14 significant digits.

  • Floating point is governed by IEEE standard 754.

  • Many non-integers cannot be stored by computers with exact precision. For example the number PI is 3.14159265358979323846……..