Circles and Ellipses

A circle is defined by three points on the edge of the circle using Insert_Circle (using non colinear points), or via a center point and radius using Insert_Circle_By_Radius. An ellipse is defined by a center point, and major and minor axis points.

In addition to commands to insert circles, HOOPS also has commands to insert circular arcs ( Insert_Circular_Arc ), elliptical arcs ( Insert_Elliptical_Arc ), circular chords ( Insert_Circular_Chord ) and circular wedges ( Insert_Circular_Wedge ).

Circle and Ellipse Attributes

Like a polygon, circles and ellipses have an edge and a face. All attributes that apply to polygons also apply to circles and ellipses (including edge pattern, but not including the “line join” part of the edge pattern, since the edges of circles/ellipses do not contain separate segments with line joins).

Wire Circles

If you wish to represent a wire circle, one valid approach is to use the ‘circle’ primitive and turn off the visibility of ‘faces’ for the segment that the circle resides in. But that would turn off all faces in the segment,which might not be desirable. The next logical thing would be to put such circle primitives in a subsegment and make the appropriate visibility setting in there (“faces = off, edges = on”). However, there’s a better solution. Instead of creating a subsegment, you can use an elliptical arc (with start=0.0 and end=1.0) to represent a ‘wire’ circle, and therefore would not have to turn faces off or place the geometry in a subsegment. The following code snippet is an example of how to create a unit wire circle centered at the origin in the xy plane using Insert_Elliptical_Arc:

    Point center, major, minor;

    center.x = 0;
    center.y = 0;
    center.z = 0;
    major.x = 1;
    major.y = 0;
    major.z = 0;
    minor.x = 0;
    minor.y = 1;
    minor.z = 0;

    HC_Open_Segment("circle");
    HC_Insert_Elliptical_Arc(&center, &major, &minor, 0, 1);
    HC_Close_Segment();

Precision Control

There is a trade off between performance and rendering quality for all ‘curve’ primitives. To manage this balance, one can utilize the ‘general curve’ settings in Set_Rendering_Options to allow the user to try to maintain an appropriate number of vertices for the ‘curve’ component of each primitive. The settings available primarily depend on whether or not the curves are view-dependent or view independent.

View-Independent Curves

View-independent curves are pre-tessellated into a group of static line segments which are stored and redrawn on each update, and are the fastest way to render curves. They are enabled by setting the ‘general curve = (view independent)’ option of Set_Rendering_Options:

    HC_Set_Rendering_Options("general curve = (view independent)");

The drawback is that there are no run-time checks to make sure that no visual artifacts are popping in, which could occur with extreme zoom-in levels. Refer to the ‘general curve’ docs for details on other suboptions which control how HOOPS/3dGS tessellated view-independent curves

View-Dependent Curves

View-dependent curves are re-tessellated on every update. It is an expensive process, it is the only way to be completely certain that no artifacts will appear. They are enabled by setting the “general curve = (view dependent)” option of Set_Rendering_Options:

    HC_Set_Rendering_Options("general curve = (view dependent)");

The tolerance is dependent on what driver is currently active, but there is currently no interface to specify tolerance (read: it’s hard coded until someone asks us to do otherwise). Curves put through OpenGL are tessellated until each line segment is no longer than 5 pixels long. All other drivers use upper-level HOOPS to calculate the tessellation, which recursively subdivides until adjacent control points are within 2 pixels of one another.

Clock Example Using Circles

In the next version of the clock, we use two circles: one to replace the face of the clock, and one to replace the central hub. We use the Insert_2D_Circle routine defined above.

The segment containing the hands is the same, so we shall look at only the rest of the clock, which draws the clock face and the hour numerals:

    Point origin = {0, 0, 0};

    HC_Open_Segment("clock");
    HC_Set_Color("dark blue");
    HC_Set_Text_Font("transforms, size = 0.1 oru");
    float const rimr = 0.98; // radius of rim
    float const numr = 0.80; // radius of numbers

    // for each hour
    for (int hour = 1; hour <= 12; hour++) {
        double angle = hour * (3.14159 / 6.0);
        float x = (float)sin(angle);
        float y = (float)cos(angle);
        // hour numerals
        char buffer[8];
        sprintf(buffer, "%d", hour);
        HC_Insert_Text(numr * x, numr * y, 0.0, buffer);
    }

    // rim and interior of clock face is drawn using a circle
    HC_Set_Color("faces=light silver");
    HC_Set_Edge_Weight(3.0);
    HC_Insert_Circle_By_Radius(&origin, rimr, NULL);

    // central hub is drawn using a circle
    HC_Open_Segment("hub");
    HC_Set_Color("reddish blue");
    float const markr = 0.03;
    HC_Insert_Circle_By_Radius(&origin, markr, NULL);
    HC_Close_Segment();

    // the remainder of the clock is drawn
    HC_Open_Segment("hands");
    // define hands here...
    HC_Close_Segment(); // hands
    HC_Close_Segment(); // clock