SMLib Introduction & Installation

SMLib Introduction

SMLib is a full scale library of NURBS and polygonal modeling tools. In other words, it is an engine or kernel upon which a variety of applications can be built. Although SMLib.dll does not actually exist, it is a term which refers to either NMTLib or TSNLib. Both include non manifold topological structures, geometry construction functions, curve and surface intersections, and evaluation functions. NMTLib is our all inclusive flagship product which includes all of TSNLib plus several advanced functions like boolean operations, fillets, shelling, feature removal, etc. TSNLib is a subset of NMTLIb. Both are built with NLib, a curve and surface NURBS library by Wayne Tiller and Les Piegel, as their foundation.

SMLib Installation

SMLib is distributed as a compiled library on Windows, Windows64, Linux, or Mac. If your application has special building requirements, source code distributions are also possible in some cases. For the purposes of this manual we will use Windows instructions as an example. After installing NLib and NMTLib/TSNLib, please refer to our documentation available for download from our FTP site. In addition, we provide examples and release notes on our web site at smlib.com. If any questions remain, please contact support@smlib.com.

Upon receiving the download instructions, unzip the distributed zip file with the given password into a folder called SMLib.

Directory Structure

  • SMLib
    • NLib
      • inc (include files)
      • src (source code distribution only)
      • Project files (.sln, .vcxproj ) (source code distribution only)
    • NMTLib / TSNLib
      • inc (include files)
      • src (source code distribution only)
      • Project files (.sln, .vcxproj ) (source code distribution only)
    • ui_test (Windows only)
      • inc (include files)
      • src (source files)
      • Project files (.sln, .vcxproj )
    • Winlibs/Winlibs64
      • Release (resulting dlls and license file)

Build for Windows using Visual Studio C++ (source code license)

  • Enter the NLib directory and open the project file (NLib.sln). Compile and link all files.
  • Enter the NMTLib directory and open the project file (NMTLib.sln). Compile and link all files.
  • Enter the ui_test directory and open the project file (ui_test.sln). Compile and link all files. You can execute the ui_test program, and make selections with the mouse from the interactive window. See SMLib_ui_test.html for how to navigate ui_test.
  • Each project will place their result (.dll, .lib, .exe) in Winlibs / Winlibs64.

Project Settings:

All projects must be built with consistent settings. Project files are distributed with the following default settings in bold:

Debug / Release

32 / 64

UNICODE / Multibyte Character set

  • Compiler definitions: In the project settings for each project there are two candidates for removal/inclusion:
    IW_NOT_USING_OPENGL should be removed if you are running OpenGL on a PC using our ui_test,  and
    IW_NO_HWLIBS  should be removed if you are using any Harmonyware translators.
  • iwos_config.h: The include file iwos_config.h contains several definitions of common interest:

#define SM_VERSION_NUMBER  displays SMLib/NLib version
#define IW_NO_HWLIBS_IGES 1  should be commented out if you are using the Iges translator
#define IW_NO_HWLIBS_STEP 1  should be commented out if you are using the Step translator
#define IW_NO_HWLIBS_SAT 1  should be commented out if you are using the Sat translator
#define IW_NO_HWLIBS_VDAFS 1  should be commented out if you are using the Vdafs translator
#define SMLib 1   should be commented out if you have not licensed SMLib
#define TSNLib 1   should be commented out if you have not licensed TSNLib

Build for UNIX / LINUX (source code license)

The process of building on Unix / Linux requires using makefiles to build each library. Each project directory contains an example makefile designed to compile and link all the source files and place the resulting lib in the same directory. They are distributed as a starting point. Your specific compiler/environment may require modifications.

Building on Unix / Linux requires a few modifications to definitions in the source code.

MSWINDOWS and BUILDING_MS_DLL should not be defined.

If building a release version, DEBUG, IW_DEBUG_CODE should not be defined.

If not using our graphics, IW_GFX_CODE should not be defined.

If you are not using an ISO C++ compiler you may need to place a comment before #define IW_ISOCPP.

Topology

Introduction

SMLib provides high level topological operations: Solid Booleans, Merge, Make-Manifold, Topology-Based Filleting, Tessellation, Shell Offset, and Feature Removal.

Non-Manifold Topology

This is an introduction to SMLib Geometric Objects, and how the various pieces of geometry are put together. The relationships between the objects is the topology and SMLib topology is similar to that of other systems. This provides a general understanding that will apply to many geometric modeling systems.

When a closed curve lies on a surface, it can be used to trim the surface to that boundary curve and this results in a trimmed surface. If two surfaces share the same segment of a boundary, this is called an edge and the two surfaces are neighbors. If we have a way to keep track of which surfaces share an edge, we have added a topology to our modeling system.

A trimmed surface, together with the information about its neighbors is referred to as a face. A face must have an outer boundary and it may have many inner boundaries or “holes”. When two faces are joined along an edge, the result is then referred to as a shell. A shell is just a set of two or more connected faces.

If a shell is closed then you have a solid. A solid has an outer closed shell and possibly many inner shells that define cavities in the solid. A “region” encloses space from a closed outer shell or between two closed shells (one inside the other) and has volume. The outer region is the infinite region, so a closed box is represented by two regions – the outer infinite region, and the region within the box. If there is a box within the box ( a ‘thick’ box) then there are 3 regions. The inner box may ‘float in space’ without being attached to the outer box….it may not be physically possible, but it is topologically acceptable.

In a closed solid, each boundary has edges which are shared by two neighboring faces, so this is referred to as a boundary representation or “Brep”. The first implementations of a Brep model were Manifold, that is. …they allowed for only two faces to share an edge, hence the name “twin-edge boundary representation”….or brep. If more than two faces can share an Edge, the topology is Non-Manifold.

It turns out that the manifold restriction that an edge may have only two neighbors is very limiting. Any time you intersect two surfaces, if you look in the neighborhood of a point on the intersection curve, there appear to be four neighboring surfaces at that point. That’s what is meant by being “non-manifold”. A manifold edge is restricted to having two neighboring surfaces and a non-manifold edge may have more than two surfaces that share that edge.

Each boundary of a face consists of a closed loop of edges. In the topology structure each loop has a list of edges, and the same edge may be used by two or more faces and that’s what “EdgeUse” refers to. The term edgeuse has real importance since there could be many uses of an edge.

If we construct a box (it defines a region) and then if we add another box right next to it, we have another region. Since they share the same face, you can see why we need to have “FaceUses”. The face is used on one region, and the face is also used in the other region.

There are many methods for extracting topological information that defines a Brep. Down to the FaceUse level, the various methods include:

  • IwBrep::GetRegions()  –  Returns the list of regions with the first region being the infinite region
  • IwBrep::GetInfiniteRegion()  –  May be used to determine which region is the infinite one.
  • IwRegion::GetShells()  –  Returns the list of shells for this region.
  • IwShell::GetFaceuses()   –  Returns the list of faceuses that define this shell.
  • IwFaceuse::GetOtherFaceuse()   –  Returns the faceuse on the other side of this face which will allow us to find which region is next to the original faceuse.

To get to the region from a faceuse you use:

  • IwFaceuse::GetShell() …..then
  • IwShell::GetRegion()

Brep – Boundary Representation

The SMLib Brep model is an instance of a modeling structure called the radial edge structure.

The pieces of the SMLib Brep model include shapes, topology objects, and special connectivity objects called “uses.”

The key idea of Brep modeling is that simple trimmed-shapes connect together through their boundaries to form complex geometry models just as a set of small glass pieces welded together along their edges form a stain glass window.

Shapes are points, curves, and surfaces each of which is a simply connected point set within a 3D space. Shapes can be infinite (planes, cylinders, lines and such) or finite (Bspline curves and surfaces) but have no sense of boundaries. For every shape there is a simple topology object that adds trimming to the shape so that it can be connected into a Brep model. The SMLib simple topology objects are vertices, edges, faces, and regions. Important combinations of simple topology objects forming key boundaries within a geometry model are also represented explicitly; a loop is any closed sequence of connected edges used to bound a face and a shell is any set of faces connected edge-to-edge to bound a region.

We say that a topology object is “used” each time it connects to the geometry model to form a boundary. In general, all of the SMLib simple topology objects participate in a hierarchy of boundaries: regions are bounded by faces (gathered into connected shells), faces are bounded by edges (gathered into connected loops), and edges are bounded by vertices.

Boundaries are used to establish neighbor relations. Topology objects don’t connect directly to their neighbors; rather neighbor relationships are created when two topology objects both use a common boundary.

A geometry model represented as a hierarchy of boundary connections is referred to as a boundary representation or “Brep” model.

In SMLib, each use of a topology object (that can be used more than one time in one model) is represented by a specialized use object. Each use of a face to bound a region is represented by a Faceuse. Each use of a loop to bound a face is represented by a Loopuse. Each use of an edge to bound a face is represented by an Edgeuse, and each use of a vertex to bound an edge is represented by a Vertexuse. There are no Shelluse objects because shells are used just once per model to bound one region and the shell object can act as its own Shelluse object.

In SMLib, a connection between an object and a boundary is represented as a connection between their use objects.

The following diagram shows the SMLib Brep object model.

Object Model of Topology/Geometry

The Brep Object

Now we need to explain some of the details of the SMLib Brep object that is shown in the diagram above. The figures that follow will help us understand how FaceUse, LoopUse, EdgeUse and VertexUse are used in the radial edge structure.

Faces and FaceUses

Within the SMLib Brep, faces are two sided; each side of a face is represented by a Faceuse and each Faceuse (acting as part of a shell) is used to bound one region. The two Faceuses representing the two sides of a single face are called “mates”.

Each Faceuse is contained in just one Shell.
Each Shell has one pointer to its first Faceuse. The first Faceuse has no special significance.
Each Faceuse has a pointer to its owning shell and a doubly linked-list of other Faceuses owned by that shell.
Each Faceuse has a pointer to its owning face.
Each Face has a pointer to a 3D surface, which defines its 3D shape.

The following diagram represents the connections found between topology objects contained in one Faceuse pointer that has two Loopuses.

This represents the topological information used for one Faceuse on one side of a Face. We are looking at this face from inside the region that this Faceuse bounds. The other side of the face sitting on the other side of this image is not displayed. It is associated with a second Faceuse that contains a mirrored set of all the mate Loopuses, Edgeuses, and Vertexuses shown for this Faceuse. If it were shown, we would be looking at that 2nd Faceuse from a point outside of its region located on the other side of this image.

Note that the Edgeuse only points to one Vertexuse, yet an Edge is (usually) connected to two vertices, one at the start and one at the end. To get both vertices attached to one edge walk the Edgeuse’s mate pointer as:

IwVertex *pV1 = pEdge->GetPrimaryEdgeUse()->GetVertexUse()->GetVertex();
IwVertex *pV2 = pEdge->GetPrimaryEdgeUse()->GetMate()->GetVertexUse()->GetVertex();

Shells and Regions

Shells are one sided and are used just once to bound one region. A shell is a set of connected Faceuses all bounding a common region. A region is always bounded by one outer shell defining its overall extent and bounded by any number of inner shells each representing a cavity within the region. There is one special region called the infinite-region that models all space outside of the current model. That one region is an exception in that it has no outer shell, but it may have any number of inner shells marking the distinct pieces of the Brep model as cavities within infinite space.

When a shell is closed it divides all of space into two regions, inside and out. In SMLib a shell is only connected to its inner region and does not pay attention to outer regions. All of the shells of one region, including the outer shell and all the inner shells, treat that region as their inside region.

For models containing only simply closed shells, the two Faceuses of one face will be contained in different shells. For models with open shells, it is possible to create a face whose two Faceuses are contained in just one shell.

Each region points to one shell that is its first outer shell.

Each shell has a pointer to its owning region and a doubly linked list of other shells owned by that region. The first shell on this list is always the outer shell, the rest are inner shells.

You can pass over to an adjacent shell by moving across a face with the pFaceUse->m_pFUMate Faceuse pointers.

Loops and LoopUses

Faces are bounded by loops. A loop is a closed sequence of connected Edgeuses all bounding a common Faceuse. Loops, like shells, are one sided and bound just one face. A face is always bounded by one outer loop defining its overall extent and bounded by any number of inner loops each representing a hole within the face. Unlike shells Loops are always closed, they are never open. Every Faceuse must have one outer boundary, which is sometimes just the surface boundary edges. SMLib does not allow outer-boundaries to be nested within an inner boundary – in that case it would form a new face.

Since faces are used twice, each loop is also used twice to bound both sides of the face. In SMLib, the actual connection between a face and a loop is made between the Faceuse and the Loopuse.

One could imagine a face that had different loops on each of its two sides, but in SMLib every loop on a face is represented equally on both sides of the face.

Each Loop has one pointer to its primary Loopuse.
Each Faceuse has a pointer to its first outer loop.
Each Loopuse has a pointer to its owning Loop and to its mate.
Each Loopuse also has a pointer to its owning Faceuse and a doubly linked list to other Loopuses owned by the same Faceuse. The first Loopuse on this list is always the Faceuse’s outer loop and all other Loopuses on this list are inner loops.

The FaceUse points to EdgeUses, not VertexUses. An EdgeUse only points to one VertexUse, yet an Edge has (usually) two vertices, one at the start and one at the end. In order to get the Vertex at one end of an Edge, we get the primary EdgeUse for the Edge, get its VertexUse, and get its Vertex:

IwVertex *pV1 = pEdge->GetPrimaryEdgeUse()->GetVertexUse()->GetVertex();

To get the other Vertex , we use the mate of the EdgeUse, which is the corresponding oppositely-oriented edge on the other side of the face to provide the VertexUse:

IwVertex *pV2 = pEdge->GetPrimaryEdgeUse()->GetMate()->GetVertexUse()->GetVertex();

Edges and EdgeUses

Edges can be thought of as multi-sided; they may be used to bound any number of faces like a book’s spine can bound any number of pages. Each use of an edge (acting as part of a loop) bounds one face. Since faces are two-sided one could again image an edge that bounds only one side of a face and not the other. But in SMLib an edge bounding a face always bounds both sides of that face. So a pair of Edgeuse objects is created every time an edge is used to bound a face. Each Edgeuse is a member of just one Loopuse. The two Edgeuses lie on opposite sides of the face they are bounding, and are called mates. The connection between a face and its edge is represented in SMLib as a pair of connections between the face’s two Faceuses and the edge’s two Edgeuses.

Edgeuses connect together in two different interesting ways; one is to store the radial/mate information about what faces use this edge (see Radial Edges, below); the other is to store the order of edges ( CW or CCW) that form a Loop boundary (outer or inner) within a face.

Edges themselves are bounded by vertices. An edge is always bounded by two vertices. Edges are always simply connected; there is no concept of a hole in an edge. An edge with a hole is modeled in SMLib as two unconnected edges.

An edge can be open or closed. A closed edge is one where the last point of the edge is the same as the first point of the edge. For closed edges, the start vertex equals the end vertex.

Edges are classified according to how many faces to which they attach.

  • An edge connected to no faces is called a wire.
  • An edge connected to just one face is called a lamina edge.
  • An edge connected to just two faces is called a manifold edge.
  • An edge connected to more than two faces is called a non-manifold edge.

Each Loopuse will point to its first Edgeuse – which has no special significance.
Each Edgeuse points to its owning Loopuse and has a circular doubly linked list of (CCW and CW) connected Edgeuses all owned by the same Loopuse. The Edgeuse is connected to a face through the Loopuse owner.
Each Edgeuse also has a pointer to its owning edge and has a 2nd circular doubly link list of (radial and mate) connected Edgeuses all owned by the same edge.
Each Edge has a pointer to a 3D curve representing its 3D shape.
Each Edgeuse has a pointer to a Trimcurve used to bound its face’s 3D surface.

Note: A manifold modeler assumes every edge will be connected to exactly two faces. The SMLib non-manifold modeler allows any number of faces (from 0 to really large numbers) to be connected to one edge. This difference allows SMLib to model mixed wireframe, surface, manifold, and non-manifold models in one single modeling environment.

Trimmed Surfaces and Trim Curves

Every Edgeuse has one 2D Trimcurve that marks the bound of the edge’s 3D curve in the domain space of the face’s 3D surface. Trimcurves turn a surface into a trimmed-surface by dividing a surface into used and unused portions (when rendering a geometry model only the used portions of a trimmed-surface are displayed). Internally, to save space, a Trimcurve is stored just once for every Edgeuse mate pair to prevent storing unnecessary Trimcurve copies

An Edgeuse’s Trimcurve is computed as the projection of an edge’s 3D curve onto a face’s 3D surface. In SMLib all Trimcurves are represented by 2D Bsplines and care is taken to ensure that the Trimcurve is parameterized in the same manner as the Edge’s 3D curve. So, to within tolerance, the point generated by evaluating the Edge’s 3D curve for a given parameter, will be the same point generated by evaluating the face’s 3D surface at a 2D point found by evaluating the 2D Trimcurve for the common parameter.

Radial Edges

In non-manifold modeling, an Edge may be shared by many different Faces. As a result, it is necessary that we maintain this information in the form of a list of EdgeUses for each Edge. This list of EdgeUses is ordered since it is critical to know the relationship between the Faces that share the same Edge. An Edge together with the ordering of the Faces that share the Edge is referred to as a Radial Edge and it is one of the most important features of this representation.

This figure shows the radial/mate Edgeuses relationships for one edge shared by two faces.

Here one edge connects to both Face1 and Face2. Face1 and Face2 both have two sides making for 4 Faceuses in total. Each Faceuse is bound by one Edgeuse making for 4 Edgeuses as shown surrounding the Edge between Face1 and Face2. These 4 Edgeuses are connected in a circular sequence of radial and mate relationships.

Note that the 2 EdgeUses of Face1 are associated with each other as Mate so given any EdgeUse

EdgeUse::GetMate()

returns the EdgeUse on the other side of the Face.

EdgeUse2 (eu2) of Face1 and EdgeUse3 (eu3) of Face2 are associated to each other as Radial so given any EdgeUse

EdgeUse::GetRadial()

returns the EdgeUse on the neighboring Face.

The real power of this representation is shown by the case where 3 Faces share the same Edge.

Now we see why we needed to introduce FaceUses, LoopUses and EdgeUses. The relationship between all the Faces that share this Edge is now available. Starting with EdgeUse1 (eu1) if we go Mate, Radial, Mate, Radial, Mate, Radial we are back at EdgeUse1.

Note that a single next/previous value in the EdgeUse is used for both the Mate and the Radial pointers. For each EdgeUse either (the next is the radial, and the previous is the mate) OR (the next is the mate, and the previous is the radial). Which of these two applies depends on the Orientation – if it is the same as the primary edgeuse orientation, then the ‘next’ is the mate.

IwEdgeUse::GetMate(){

IwEdge *pE = pEdgeUse->GetEdge();
IwEdgeuse *pPrimEU = pE->GetPrimaryEdgeuse();
if (pPrimEU->GetOrientation() == pEdgeUse->GetOrientation())

pRet = (IwEdgeuse*) (pEdgeUse->GetNext());

else

pRet = (IwEdgeuse*) (pEdgeUse->GetLast);

}

IwEdgeUse::GetRadial() is as above with next/last interchanged.

Vertices and Vertex Uses

Vertices, like faces and edges, are also multi-sided in that they can be used to bound any number of edges. One Vertexuse object is created each time a vertex is used to bound an edge. The connection between a vertex and an edge is made between the Vertexuse and the Edgeuse. Edgeuses always come in pairs to match the two sides of a face and it would seem that each connection between a vertex and an edge would require two Vertexuses. In the SMLib internals the number of actual Vertexuses is cut in half by only recording a connection between one Vertexuse and one Edgeuse for each vertex-edge connection. The second connection is deduced when needed at run time by examining the Edgeuse mate relationships.

The next figure shows the case of 5 Edgeuses starting at one vertex.

This situation shown in this figure can arise as a planar model in which 5 different faces connect to one vertex through 5 different edges, or it can be thought of, like the last image, as a single vertex attached to one edge that is connected to five different faces oriented so that we see the edge and faces end-on.

In both cases, either 1 edge used 5 times, or 5 edges used 1 time, a total of 10 Edgeuses are bound by this vertex, 5 starting there and 5 ending there. In the figure only those Edgeuses that start at this vertex are shown. The Vertex points to one of the Vertexuses. Each of the Vertexuses points to its owning vertex, to the next and last Vertexuses in the doubly linked list of Vertexuses owned by this vertex, and to an Edgeuse that starts at this vertex.

Walking The Topology Model

There is a set of methods for extracting all of the topological information that defines a Brep. Down to the Faceuse level, the various methods include:

  • IwBrep::GetRegions()  –  Returns the list of regions with one region being the infinite region
  • IwBrep::GetInfiniteRegion()  –  May be used to determine which region is the infinite one
  • IwRegion::GetShells()  –  Returns the list of shells for this region
  • IwShell::GetFaceuses()  –  Returns the list of faceuses that define this shell
  • IwFaceuse::GetOtherFaceuse()  –  Returns the faceuse on the other side of this face and from this other faceuse you are able to determine which region is next to the original faceuse

To get to the region from a Faceuse you use:

  • IwFaceuse::GetShell() …..then
  • IwShell::GetRegion().

Orientation

In SMLib, all curves and surfaces are represented by parametric functions. Each curve and surface inherits from its function a natural orientation. The parametric functions for curves and surfaces can be written as:

C(t) = [x(t), y(t), z(t)] ;                        t_min <= t <= t_max

S(u,v) = [x(u,v), y(u,v), z(u,v)];        u_min <= u <= u_max, v_min <= v <= v_max.

Curves get a direction having a beginning that maps to the curve’s smallest parameter value and an end that maps to its largest parameter value. An observer walking along the length of a curve is moving in the positive direction when walking in direction of increasing parameters. At any point, the curve’s 1st derivative, d(C(t))/dt, points in the curve’s positive direction.

Surfaces get an upward and downward side defined by the parametric surface’s oriented surface normal as,

surface normal = (d(S(u,v))/du x d(S(u,v))/dv).

At any point on the surface, the surface normal points to the surface’s upward side and away from surface’s downward side. An observer located some distance away from a surface at the end of the positive surface normal would be looking back at the upward surface.

Faceuse Orientation

A Face has an upward Faceuse and a downward Faceuse. Each Faceuse has a normal direction that points into the region that it bounds. The upward Faceuse’s normal direction is the SAME as that of Face’s Surface Normal. The downward Faceuse’s normal direction is the OPPOSITE direction.

In SMLib, the primary Faceuse pointer stored on a Face is always the upward Faceuse.

The upward Faceuse’s mate is always a Downward Faceuse. Each Faceuse stores an orientation bit whose values is always IW_OT_SAME for upward Faceuses and IW_OT_OPPOSITE for downward Faceuses.

Loop Orientation

Each Loop stores two Loopuse orientations, one connected to each to the two Faceuses of the face being bounded. By convention the primary Loopuse is always connected to the Face’s primary upward Faceuse. The primary Loopuse’s mate is always connected to the downward Faceuse stored as the primary Faceuse’s mate.

Loopuse Orientation

Inner and outer Loopuses have different orientations. Each Loopuse stores an orientation bit whose value is always IW_OT_SAME for outer loops and IW_OT_SAME for inner loops.

In an outer Loopuse, an observer positioned to look down a Faceuse’s normal watching a point traverse the Loopuse’s Edgeuses from start to end, will see that point move in a counter-clockwise cycle. The same observer watching a point traverse an inner Loopuse will see that point moving in a clockwise cycle.

Edgeuse Orientation

Every Edgeuse has a direction that marks its beginning and ending vertices and marks which side of the Faceuse it bounds is inside or out. Each Edgeuse stores an orientation bit that is set to IW_TO_SAME when the Edgeuse orientation is the same as the Edge’s curve orientation, and IW_TO_OPPOSITE when the Edgeuse’s orientation opposes the curve orientation.

LEFT-HAND RULE: The orientation of each Edgeuse is set so that each Edgeuse divides its owning Faceuse into an inside and an outside. The rule is that an observer moving in the positive direction along the length of the Edgeuse and oriented in the same direction as the owning Faceuse’s normal will always have the inside of the face on his left hand side. This rule defines a bi-normal vector that always points towards the interior of the face for every point on an Edgeuse.

Bi-normal = cross_product(Faceuse_Normal, Edgeuse_direction).

RADIAL/MATE Edgeuse Orientations: A pair of mate Edgeuses will always have opposite orientations to one another. Similarly every pair of radial Edgeuses also have opposite orientations to one another. The primary Edgeuse pointed to by an Edge can be oriented in either direction.

CCW/CW or NEXT/LAST Edgeuse Orientations: The doubly linked list of Edgeuses used to store all the Edgeuses contained in one Loopuse are stored in a pair of pointers called CCW_Edgeuse and CW_Edgeuse. These names are a bit confusing but the pointers are used consistently.

The CCW_Edgeuse pointer always connects the end of this Edgeuse to the start of the next Edgeuse in the loop. The CW_Edgeuse connects the beginning of this Edgeuse to the end of the previous Edgeuse in the loop. Moving from Edgeuse to Edgeuse through the CCW_Edgeuse point on an outer loop will move an observer around in a Counter-Clockwise cycle of the owning Loopuse. The same traversal on an inner Loopuse will move an observer in a Clockwise cycle.

Vertexuse Orientation

An Edgeuse is bound by two vertices, but is only connected to the vertex at its starting end. All Edgeuses connected to a vertex use that vertex as a starting vertex. The following relation for walking the topology structure from the start vertex of one Edgeuse to the common vertex being used as the end vertex for previous Edgeuse in the owning Loopuse is:

Edge->GetPrimaryEdgeuse()->GetVertexuse()->GetVertex() ==

Edge->GetPrimaryEdgeuse()->GetCWEdgeUse()->GetMate()->GetVertexuse->GetVertex().

The similar relation for moving from an common end-vertex to the next Edgeuse’s start-vertex is:

Edge->GetPrimaryEdgeuse()->GetMate()->GetVertexuse()->GetVertex()==

Edge->GetPrimaryEdgeuse()->GetCCWEdgeuse()->GetVertexuse()->GetVertex().

Degenerate Boundaries

In addition to the boundary relationships already discussed, SMLib supports the following boundary relationships,

  • A vertex can bound a face
  • A vertex can bound a region
  • A single open edge can bound a region

These are called degenerate boundary cases because in all these cases some of the intervening levels of the topology model are skipped. In a sense those skipped levels have shrunk to zero area and are now gone.

Vertex/Face Bound Representation

All faces boundaries are represented by Loopuses. When a vertex bounds a face, the Loopuse points to a Vertexuse object (skipping the typical Edgeuse level) and the Vertexuse points back to the Loopuse.

Vertex/Region and Edge/Region Bound Representations

  • All region boundaries are represented by Shells.
  • When a vertex bounds a region, the Shell points to a Vertexuse object (skipping the typical Faceuse, Loopuse, and Edgeuse levels of the topology model) and the Vertexuse points back to the Shell.
  • When an edge bounds a region, the Shell points to an Edgeuse object (skipping the typical Faceuse and Loopuse levels of the topology model) and the Edgeuse points back to the Shell

Solid Creation by Topology Tables

This tutorial shows the software necessary to create a Cube using the topology tables of the IwBrepData object. The IwBrepData object is the primary intermediate structure for input and output of topological objects (Trimmed Surfaces, Solids, Open Shells, Non-Manifold Models, etc.). You can read IwBrepData and write IwBrepData to and from ASCII files. The IwBrepData object is used as the intermediate structure for IGES reading of solids. Now to our example. Below is the code utilized to generate a Cube using the topology tables and an image showing the corresponding faces, edges, vertices, and edgeuses.

See Source code for MakeBox() in:iwtopo_test.cpp

Stitching and sewing

IwMerge::StitchFace

IwBrep::SewAndOrient

Geometry Objects

Curves - IwCurve and IwBSplineCurve

The two primary curve classes of interest are IwBSplineCurve and its parent class IwCurve. The other curve classes (IwCrvOnSurf, IwCompositeCurve, IwOffsetCurve, IwHermiteCurve, IwIsoCurve) are for more specific uses or for internal or advanced use. The IwBSplineCurve class represents a NURBS curve. It provides a method interface for creation, modification, query, and eval of the NURBS representation. The IwCurve class contains the method interface for most of the numerical algorithms. Some common methods include:

  • GetNaturalInterval() - returns the parametric domain of the curve.
  • Evaluate() - evaluates the point and requested derivatives of the curve/surface.
  • EvaluatePoint() - evaluates just the point of the curve/surface.

Figure 5: Curve Properties and Analysis

Figure 6: Curve Properties and Analysis continued

IwCurve

  • point and derivative evaluation
  • evaluation of geometric properties
  • bounding box calculation
  • creation of a mirror curve
  • creation of a curve by projection to a plane
  • transformation with optional scaling
  • high precision curve length computation
  • closure, periodicity and degeneracy testing
  • tessellation using both chord height and angular tolerances
  • find intersections with a plane
  • find inflection points
  • find minimum/maximum magnitude of 1st derivative and radius of curvature
  • find 1st derivative or radius of curvature of a given magnitude
  • find points where tangent is parallel to X, Y, and/or Z axis
  • find points where tangent is parallel to a given vector
  • find silhouette points with either parallel or perspective projection

IwBSplineCurve

  • construction/modification/query/eval
  • continuity calculation, removal of extra knots, conversion to 2D
  • creation by joining connected curve segments
  • creation of a circle segment, ellipse segment line segment, degenerate curve
  • creation of mirror curve
  • editing the end points of a B-Spline
  • extraction of analytical information for lines and arcs
  • curve offset

Surfaces - IwSurface and IwBSplineSurface

The two primary surface classes are IwBSplineSurface and its parent class IwSurface. The other surface classes (IwOffsetSurface, IwCurveBoundedSurface, IwSrfInVolume, etc) are for more specific uses or for internal or advanced use.. The IwBSplineSurface is the class which represents a NURBS surface. It provides a method interface for creation, query and editing of the NURBS representation. The IwBSplineSurface class also contains implementations for abstract IwSurface methods. The IwSurface class contains the method interface for most of the numerical algorithms.

IwSurface

  • point and derivative evaluation, evaluation of geometric properties
  • normal evaluation, evaluation of a normal section
  • high precision surface area computation
  • dropping 3D vectors into parameter space
  • closed and periodic testing, singularity testing
  • validation
  • transformation

IwBSplineSurface

  • construction using STEP format or NLib data structures
  • continuity calculation
  • query
  • creation of planar section curves - see Figure 14
  • creation of parallel projection curves - see Figure 15

Figure 14: Creation of Planar Section Curves

Figure 15: Creation of Curves by Parallel Projection

Trimmed Surfaces - IwFace

  • IwBrep::CreateFaceFromSurface from a parametrically bounded NURBS surface.
  • IwBrep::MakeFaceWithCurves from a surface and 2-D parameter space curves.
  • From a surface and 3-D model space curves. Use IwBrep::MakeFaceWithCurves.
  • From a surface with both 2-D and 3-D space curves. UseIwBrep::MakeFaceWithCurves.
  • Creation of topological elements (vertices, edges, faces) using "Euler like" topological operators. See the tutorial on Solid Creation by Topology Tables.
  • Adjustment of operational tolerances to compensate for gaps. See as an example IwEdge::SetTolerance.
  • Sewing together trimmed surfaces to create shells or solids. Use IwBrep::SewFaces.

This section of the quick start is intended to help you to understand how to create trimmed surfaces such that the subsequent operations performed on them are very reliable. SMLib is designed to enable it to handle trimmed surfaces which are generated from a variety of different CAD and graphics systems. SMLib allows the creation of trimmed surfaces from either parameter space or model space curves. The operational tolerances can be adjusted to match those of the system which originally produced the trimmed surface.

An empty IwBrep is created which will eventually contain one or more trimmed surfaces. Prior to adding faces to the brep ( IwBrep::MakeFaceWithCurves )

The next major decision to make is what to use for trimming curves, 3-D model space curves or 2-D parameter space curves. In cases where only one set of trimming curves are available or one set is clearly the master representation your decision is not a difficult one. However if you have both sets of trimming curves and neither of them is the master representation, than you may have decisions to make. In this case is important to know what happens in each of your three options:

2-D Trimming Curves are used to create Trimmed Surfaces

  • Approximate 3-D Curves are created using the mapping of the 2-D Curves through the surface.
  • The 3-D Curves are approximated to within the IwBrep tolerance ( IwBrep::GetTolerance ) of the corresponding 2-D curves.
  • The 3-D Curves are synchronized in terms of their parameter space and orientation.
  • The 2-D Curves have their end points "joined" through a process which does extension and intersection.

3-D Trimming Curves are used to create Trimmed Surfaces

  • 2-D curves are created using the IwSurface::DropCurve method.
  • The tolerances of the corresponding edges are adjusted relative to the distance between the 3-D curve and its 2-D image.
  • The 2-D Curves have their end points "joined" through a process which does extension and intersection.

Both 2-D and 3-D Curves are used to create Trimmed Surfaces

  • The distance between the 3-D curve and the 3-D image of the 2-D curve is measured and an appropriate tolerance is chosen for the edge.
  • The 2-D Curves have their end points "joined" through a process which does extension and intersection.

Of the three possibilities, #2 is by far the slowest and #3 is somewhat faster than #1. If you are doing primarily 2-D operations, #1 or #3 are probably a little better than #2. If you are doing primarily 3-D operations, #2 or #3 might give you some advantages. We have found that #1 seems to be the best option when we are reading and testing IGES trimmed surfaces generated by the RHINO NURBS Modeler.

Trimmed Surface Creation

Creating correctly defined trimmed surfaces is essential. There are two different ways in which trimmed surfaces may be created. First you may create a trimmed surface (IwFace) directly from a surface and its parametric domain (IwBrep::CreateFaceFromSurface). You may also create a trimmed surface from the surface and a set of oriented boundary curves. This tutorial shows how this may be done. Our tutorial demonstrates the creation of a trimmed surface from class (IwCurveBoundedSurface) which mirrors the IGES trimmed surface structure. This code is actually used in our IGES processor to create trimmed surfaces. Note that this code allows you to add a trimmed surface to an already existing brep. It is possible to put multiple trimmed surfaces in a single brep. You need to first create a brep prior to invoking this method. The following code shows how you might set up a brep prior to calling the function:

***Please refer to Example.cpp***

Trimmed Surface/Point Solver Tutorial

The following code demonstrates the use of the IwTopologySolver to solve an operation between a brep and a point. This solver allows the user to find intersections (IW_SO_INTERSECT), minimum/maximum distance between the brep and the point (IW_SO_MINIMIZE/>IW_SO_MAXIMIZE), and points were the tangent plane of the trimmed surface is perpendicular to the vector to the point (IW_SO_NORMALIZE.

See Source code for my_test_tsurface_point_extrema() in: iwtopo_test.cpp

Trimmed Surface/Curve Solver Tutorial

The following code demonstrates the use of the IwTopologySolver to solve an operation between a brep and a curve. This solver allows the user to find intersections (W_SO_INTERSECT, minimum/maximum distance between the brep and the curve (IW_SO_MINIMIZE IW_SO_MAXIMIZE), and points were the tangent plane of the trimmed surface and the tangent of the curve are perpendicular to the vector between the trimmed surface and the curve (IW_SO_NORMALIZE).

See Source code for my_test_tsurface_curve_solve() in: iwtopo_test.cp

Trimmed Surface/Trimmed Surface Solver Tutorial

The following code demonstrates the use of the IwTopologySolver to solve an operation between two breps with trimmed surfaces. This solver allows the user to find the minimum/maximum distance between two breps with trimmed surfaces(IW_SO_MINIMIZE IW_SO_MAXIMIZE, and points were the tangent planes of the trimmed surfaces of each are perpendicular to the vector between the two trimmed surface (IW_SO_NORMALIZE).

See Source code for my_test_tsurface_tsurface_solve()iwtopo_test.cpp

Planar Sectioning Example

This tutorial demonstrates how to do planar sectioning of a brep with trimmed surfaces. It creates a set of planes which span the bounding box of the brep and uses them to section the brep.

See Source code for my_test_tsurf_section() in: iwtopo_test.cpp

Point Classification Example

This tutorial demonstrates the classification of a parameter space point on a surface relative to the surface boundary. The function creates a grid of points in the parameter space of the surface and classifies them.

See Source code for my_test_tsurface_point_classify() iwtopo_test.cpp

Curve Classification Example

This tutorial demonstrates the classification of a 2D curve relative to the boundary of a trimmed surface. Please note that the method being demonstrated here also allows you to specify a 3D curve. The method will automatically choose the correct place to classify the curve based on its dimension.

See Source code for my_test_tsurface_curve_classify() in iwtopo_test.cpp

Analytical Classes

Analytic or exact representations of common curve or surface objects often provide faster results with more precision. The following classes have been implemented along with supporting methods for creation and evaluation.

  • IwLine, IwEllipse, IwCircle, IwHyperbola, IwParabola
  • IwPlane, IwCylinder, IwCone, IwSphere, IwTorus, IwSurfOfRevolution, IwSurfOfExtrusion

Solids - IwBrep

The IwBrep class represents a collection of IwFaces. If the collection, one or more faces, form a closed, water tight object then the object is manifold. Otherwise the object is non manifold. In either case, the object can be merged (boolean) , filleted, offset, shelled, de-featured, sliced, etc. Some of the possible operations on IwBreps are:

*****See SMLibBoolean.html, SMLibFilles.html, SMLibShell.html, etc*****

A word about m_bEditingEnabled. We have added a flag to IwBrep which enables editing. There are many of the creation and a few editing operations which require the m_bEditingEnabled flag to be turned on. An example is sewing. This flag needs to be turned off to make many of the evaluation functions work.

General Usage

Documentation

Documentation is generated by Doxygen from the header and source files. Header documentation is available here.

Source code documentation is available to source code customers.

In addition to documentation, the manual pages available on the web site here may also be helpful as are many examples provided here.

Source code guidelines

  • Class names and most files begin "Iw" with camel case convention (IwVector3d.cpp, class IwVector3d).
  • Files which do not contain classes or methods for a class are lower case with underscores (iwos_error.h, iwos_error.cpp).
  • Naming conventions for parameters begin with a lower case characters.
    • r - reference ( double & rdRadius ).
    • d, l, p, b - double, long, pointer, boolean (double dParameter, ULONG lCount, long lValue, IwCurve * pCurve, BOOL bTrueOrFalse).
    • c - constant (const IwCurve & crCurve).
    • a - array (double adParameters[5];).
    • s - object on the local stack of the current scope ( IwArray sArray ).
    • v - object passed or used by value as opposed to by pointer or by reference ( class List { IwArray vMyArray; } ).
  • Parameters are clearly marked [in], [out], [in/out] in the header.
  • Default values for optional parameters are clearly marked in the header
  • Most Methods/Functions return an IwStatus flag

Interfacing Between NLib and 'Iw' Objects

SMLib provides an interface which allows users to construct 'Iw' objects directly from NLib structures. The IwBSplineCurve and the IwBSplineSurface both contain equivalent NLib NL_CURVE and NL_SURFACE structures. Structurally they are the same but the memory management utilized in each case is significantly different.The 'Iw' object allocates the corresponding NURBS curve or surface as a single block of memory. NLib allocates a NURBS curve or surface as many smaller components. In terms of usage what this means is that they are interchangeable only for operations where no editing or manipulation of these structures is required. For example you may utilize the NL_CURVE pointer inside of a IwBSplineCurve in query operation such as evaluation but you could not add a knot to it. In general we recommend that you do not try to interchange the forms directly but go through existing interfaces which create the corresponding copies. Only interchange the forms when it is absolutely necessary for performance.

We have several utilities which will help you to transfer data between the libraries. Both the IwBSplineCurve and IwBSplineSurfacehave constructors which will take a pointer to a NLib NL_CURVE or NL_SURFACE. The constructors either create a copy or reference the NLib structure directly. We recommend that you create a copy. There is a section of software below that shows how this process works. You may also obtain the internal pointer to the NL_CURVE or NL_SURFACE by invoking the GetGwNurbPointer methods. You can then use this to create a NLib native NURBS using a copy method. This is shown below also.

This first example shows how to construct a IwBSplineCurve that copies the data from a CURVE generated in NLib.

// create and manipulate curve in NLib
NL_CURVE *cur;
cur = ...

// constructs a NMTLib curve by copying the NLib cur.
IwBSplineCurve *pBSplineCurve = new (pContext)IwBSplineCurve(3, (void*)cur);

This second example shows how to construct an IwBSplineCurve which references an existing NL_CURVE from NLib. This is a little dangerous and is recommended only for expert users.

// prevents copy of cur - it utilizes cur directly inside of the IwBSplineCurve object.
BOOLEAN bIsBorrowed = TRUE;
IwBSplineCurve* pBSplineCurve = new (pContext) IwBSplineCurve(void*)cur, 3, bIsBorrowed);

This third example shows how to create a CURVE from a IwBSplineCurve. pcur is an internal pointer used by the IwBSplineCurve and must not be deleted or edited. new_cur may be edited, deleted, etc.

IwBSplineCurve *pBSplineCurve = .....;
NL_CURVE *pcur = pBSplineCurve->GetGwNurbPointer();

STACKS SL;
NL_CURVE new_cur;
N_CrvInitArrays(new_cur);
N_CrvCopy(pcur,new_cur,SL);

The fourth example shows how to convert a IwCurve into a IwBSplineCurve;

IwCurve *pCurve; IwBSplineCurve *pBSC = IW_CAST_PTR(IwBSplineCurve, pCurve);

There are equivalent methods that can be used to transfer data between the IwBSplineSurface, IwSurface, and the NLib NL_SURFACE.

The fifth example shows how to convert an analytic surface (cylinder, cone etc) into a Nurbs form;

IwCylinder *Cyl = ....;

IwBSplineSurface *pNurbsSrf = new (crContext) IwBSplineSurface (*Cyl);

Geometrical Utility Classes

  • two-dimensional vector - IwVector2d, IwPoint2d
  • three-dimensional vector - IwVector3d, IwPoint3d
  • local coordinate system or transformation - IwAxis2Placement
  • n-dimensional matrix used to solve linear equations - IwMatrix
  • polynomial equation representation - IwPolynomial
  • non-axis aligned bounding box - IwPseudoBox
  • one-dimensional interval - IwExtent1d
  • two-dimensional bounding box - IwExtent2d
  • three-dimensional bounding box - IwExtent3d
  • n-dimensional bounding box - IwExtentNd

Vector Classes

The vector classes IwVector3d and IwPoint2d provide a variety of math operators like cross product, dot product, etc. The vector classes also provide a reasonable set of math operator overloading to help simplify and clarify the vector operations. IwVector2d and IwPoint2d are often used in to describe the surface parameter space, where the "X" component of the vector maps to the "U" parameter of the surface and "Y" maps to the "V" parameter.

Intervals and Domains

Many of the curve methods allow the user to specify a portion of the curve's natural interval. Basically this allows the user to limit the area of interest for a given operation to a subset of the natural interval. For example, the user may request the length of a portion of a curve without actually modifying the curve. To do so, IwExtent1d can be used to specify a portion of the curve's parameterization. The entire curve interval can be determined with GetNaturalInterval().
Similarly a portion of a surface domain can be specified using the uv parameterization of the surface in IwExtent2d . The entire surface domain can be determiend with GetNaturalUVDomain().

There are four extent classes: IwExtent1d, IwExtent2d, IwExtent3d, and IwExtentNd. Extent classes define a domain of the corresponding dimension by specifying the minimum and maximum values for the range of the domain. IwExtent1d defines a one-dimensional domain on the real line. IwExtent2d defines a two-dimensional domain in the real plane. IwExtent3d defines a three-dimensional domain in three space. IwExtentNd defines an N-dimensional domain in N-Space. IwExtent1d is often used to define the parameter interval of a curve. IwExtent2d is often used to define the domain of the parameters of a surface. IwExtent3d is often used to define a three-dimensional axis-aligned bounding box. IwExtentNd is used primarily as the domain representation to limit parameters for solving N-dimensional equations.

Axis Placement

The Axis Placement, IwAxis2Placement, defines a local coordinate system. It contains a point representing the origin and two vectors representing the new X and Y axes. It is derived from the STEP representation of the equivalent name with the exception that the X and Y axes must be orthogonal. The IwAxis2Placement is used primarily to represent transformations from one coordinate system to another.

Pseudo-Box

A Pseudo-Box, IwPseudoBox, is a non-axis aligned bounding volume. In many cases it can provide a much tighter bound on curves and surfaces than the IwExtent3d object.

Parsing the IwSolution Object

The IwSolutionArray stores the result of intersections or global solve. The IwSolutionArray is an array of IwSolution objects that represent individual solution points to the global problem being solved. You will need to parse the IwSolution object to extract the required data. The IwSolution contains the objects (face, curve, edge, vertex) that yielded the given solution and corresponding parameteric values. This function demonstrates how to extract that information from the IwSolution and create a corresponding 3D point. Refer to example.

Continuities

A curve or a surface may have internal continuity changes at the knots. Continuity changes may be caused by either duplicate knots or duplicate control vertices. There are methods on the curve and surface to compute the continuities. Except where stated explicitly (DropCurve on IwSurface), all methods will handle curves and surfaces with internal C0 continuity (discontinuous tangent directions within a curve or a tangent plane discontinuity within a surface). In other words they may find answers which lie on the boundary or internal discontinuity. For example, the minimization between a point and a surface may return a point internal to the surface, on the boundary of the surface, or along a discontinuous edge of the surface.

Another question one might answer is what happens if an answer is produced which is very close to a discontinuity. For example: what if a line intersects a surface near an internal discontinuity? The results obtained would be as if the surface or curve was broken up into homogenous pieces and intersected then the results combined. In our example one might get two intersections, one internal to the surface and one on the discontinuity. Because the answers are not identical both are kept. This conforms to our general philosophy of trying to not loose information. Sometimes this may cause us to return more information than the user needs, but this is better than returning not enough.

Memory Management

The software is now multi-processor compliant. The IwContext is very important object. It contains all global data such as the cache manager and temporary working memory for certain functions. Every time you create an object (IwObject and subclasses) on the heap you will need to use the overridden "new" operator that takes a context. The standard new operator has been made private to prevent creation of heap objects without a context.

IwBSplineCurve *pBSC = new IwBSplineCurve(...); // This is will not compile
IwBSplineCurve *pBSC = new(crContext) IwBSplineCurve(...); // This is correct

Any method that creates an object takes a IwContext object as either an argument to a "Create" method or as an argument in the "new". The context must have scope which is outside of the scope of the existence of the object. Therefore creating a global context at the beginning using the following is recommended if you are keeping objects around between commands.

static IwContext *s_pContext = new IwContext();

If you are just using objects locally you can put the context on the stack as follows:

IwContext sContext;

This way all objects created in a given "Context" can be deleted by deletion of the corresponding "pool". Note that we do not provide a pool based memory manager you will have to purchase or develop one yourself. SmartHeap from MicroQuill does come with a pool based memory management system.

All memory management for the library is handled in the iwos_memory.h and iwos_memory.cpp files. You may modify iwos_memory.cpp if you wish to utilize a different memory mechanism. We also provide an interface to pool based management of memory contained in the IwMemPool object. There are a number of different ways to create an object.

On the stack - cleanup happens automatically via. destructor. In general you should avoid creating IwCurve and IwSurface and their subclasses on the stack. The array based objects (IwTArray, IwSArray, IwSolutionArray) typically should be created on the stack. If the array object is created on the heap you will need an additional context argument to the destructor.

IwTArray sDoubleArray; // This is fine

On the heap - cleanup requires an explicit "delete" or utilization of a stack based clean up object

  • IwTArray *pDoubleArray = new IwTArray(); // Not legal - will produce a compile error
  • IwTArray *pDoubleArray = new(crContext) IwTArray(); // Legal but not good for array objects. Legal and good for other types of objects such as curves and surfaces.
  • IwTArray *pDoubleArray = new(crContext) IwTArray(crContext); // This is the correct way to create array based objects on the heap.

Here are some good "Rules of Thumb" to help you make decisions about how to use the Context:

  1. Never declare curves or surfaces on the stack (IwBSplineCurve sCurve(...)).
  2. Never allocate curves or surfaces on the heap without a context

IwBSplineCurve *pCurve = new IwBSplineCurve(....); // This is bad and will not work

IwBSplineCurve *pCurve = new (crContext) IwBSplineCurve(...); // This is good.

  1. Don't allocate IwTArray objects on the heap unless you give the constructor which takes a context.
  2. You will now not have to worry about managing the Cache. A good default size is created in the IwContext. If you have any declarations of cache managers (IwCacheMgrBrep sCache(1000, 100, 100, 100)). You should just remove them.
  3. We suggest that you create one global IwContext when you start up your program and use that whenever you need to create any of our objects. The only time you would want to use a IwContext on the stack is when all of your TSLib objects are created and destroyed within the given function.

There are three objects which are especially useful in the management and automatic clean up of heap based memory and objects. These objects will automatically delete objects when they go out of scope. You have the option of preventing them from deleting their contents by invoking the corresponding Clear method.

  • IwObjDelete - stack based clean up of IwObject subclasses (see IwObject.h).
  • IwObjsDelete - stack based clean up of arrays of IwObject subclasses (see IwTArray.h).
  • IwMemDelete - stack based clean up of memory allocated on heap.

Caching

Note that the Caching Mechanism is now embedded in the IwContext object. The default is for a context to create a cache manager with a maximum of 1000 curve caches, 200 surface caches, 200 trimmed surface caches, and 200 brep caches. This is a fairly good sized cache and should be sufficient for most applications. You can increase it or decrease it by a factor of 10 and still be reasonably good. If you want to conserve memory decrease it by a factor of 10. If you want better performance for larger models, increase it by a factor of 10.

GSLib utilizes a caching system to improve performance. It caches the tessellated Bezier representation of curves and surfaces and constructs a hierarchical tree structure for fast traversal. You should always have caching turned on and it should have a minimum of 10 cache elements. That is it will keep the tessellation for the last 10 curves/surfaces which it has processed. Depending upon the performance/memory requirements for your application you may want to increase this number. The caching mechanism allows us to achieve a level of performance on global algorithms which is near that of the local Newton based algorithms.

Error Handling

GSLib utilizes an error return based error handling convention. We utilize a set of macros to simplify and hide most of the error processing. At the moment an error is detected an error handling function is called (see iwos_error.h). Currently it prints an error either to the debugger output window in Visual C++ or to stderr on other systems. We suggest that you leave the error printing turned on until you have debugged the software you are implementing using GSLib. Knowing where an error has occurred will also help us to diagnose and fix any problems with our software.

The following macros are the most heavily used (see iwos_error.h):

  • SER - Status Error Return - checks the status returned from a method and returns up the call stack if the status is not IW_SUCCESS
  • NER - Null Error Return - checks the value of a pointer and if it is NULL returns an error up the call stack.

Validity of Curves and Surfaces

Use IW_ASSERT_VALID() for debug messages to detemine validity of any object

  • The end points of curves and boundaries of surfaces must be clamped (i.e. they have knot multiplicity equal to the degree+1).
  • Internally the curve must have G0 continuity (must be a single piece)
  • Control points should be distinct.
  • For surfaces, the only requirement should be G1 continuity ( have no internal creases). Curves that are not G1 must be broken up into G1 segments before they are used for sweeping or skinning into surfaces.
  • Curves and surfaces with up to degree 32 are allowed.

Validity of Trimmed Surfaces

  • The definition of a trimmed surface corresponds to the equivalent definition found in STEP and IGES.
  • The curves and surfaces must conform to the validity criteria defined in the Geometry Introduction
  • The surfaces must have continuity of C0 (smooth) or better.
  • The individual trimming curves must have continuity of G1 or better.
  • Polyline trimming curves are not supported.
  • The trimming loops must be closed to within a given model or parameter space tolerance.
  • There may be only one outer loop which is oriented counter-clockwise relative to the surface normal.
  • There may be zero, one or more inner loops which are oriented clockwise relative to the surface normal.
  • The curves of the trim boundary must not have self-intersections (except for closed curves).
  • The curves of the trim boundary must not intersect each other except where they join the loop at their ends.
  • The curves of one loop must not intersect or touch the curves of another loop.
  • When 3-D model space trimming curves are specified, they must lie on the surface (to within the current model space tolerance of the trimmed surface).
  • When 3-D model space trimming curves are specified, the corresponding ends of the curves in a loop must be within tolerance of each other.
  • When 2-D parameter space and 3-D model space curves are specified, their parameterization must match within the tolerance of the trimmed surface. In other words the point created by mapping a given parameter value of the 2D curve through the surface evaluator must be within the tolerance of the same parameter value as applied to the 3-D curve.

Classification Operations inside, outside, on boundary

  • Parameter space points relative to trim boundary. Use IwFace::PointClassify
  • Parameter space or model space curves relative to trim boundary. Use IwFace::Point3DClassify
  • Classification of 3D point relative to a Brep. Use IwBrep::Point3DClassify

Graphics

Interface to Graphics

The ui_test interface is an MFC single window application using simple graphics to display SMS geometry objects for demonstration and debugging purposes. The ui_test application and its approach to graphics management can be used as an example of how to write an NMTLib based geometry application but it is assumed most customers will want to use ui_test as an example and primarily rewrite the graphics interface for their application.

Display Architecture

The ui_test program is a Microsoft MFC windows application using state-based graphics, that is, the user interface is used to specify a set of graphic state bits and values which control the graphics displayed in the output window. The Windows message system framework converts user I/O actions into calls to the methods of the CSMLibView class defined in file ui_test/cubeview.h. Most of the CSMLibView class methods are simple wrapper functions that pass their calls on to an IwView object whose methods do the actual work.

The CSMLibView and IwView methods that control the ui_test graphics make iwgfx_SetCalls() declared in file iwgfx_extern.h to set the global view and drawing parameters. The global viewing and drawing parameters are stored together within two static global objects called s_Disp and s_View of type IwDisplayParameters and IwViewParameters respectively.

Display and view parameters are stored in the following classes:

  • IwDisplayParameters Draw Bits, Parameters, and Colors
  • IwViewParameters Display Draw State and View Orientation
  • IwView Window Size, View Orientation, and Transformation, Pick Object Data, Window handle, and Cursor Operation Type.
  • ViewData View Orientation for Orientation save/restore.

The ui_test 3D graphics are output using the OpenGL DisplayList mechanism. All 3D graphics to be displayed are first compiled into an OpenGL DisplayList and added to a global array of display lists stored in s_View::m_ pActiveLists. All the stored display lists are drawn to the output window with a call to iwgfx_DrawLists(). The iwgfx_DrawLists() function is called either directly or by the IwView::DrawScene() method which clears the OpenGL color and depth buffers, applies the s_View::m_aViewMatrix to the current (ModelView) matrix stack, outputs the display lists stored in s_View::m_pActiveLists with a call to iwgfx_DrawLists(), restores the matrix stack, and swap buffers.

DisplayLists are created and added to the global DisplayList array by Draw() methods implemented for various members of the NMTLib IwObject class hierarchy. The structures of these Draw methods are all supposed to be the same. First they create and add a DisplayList to the global DisplayList array with a call to iwgfx_Open(), followed by a sequence of draw commands designed to display the object which all get compiled into the open DisplayList, and ended with a call to iwgfx_Close() which ends the construction of the DisplayList.

The iwgfx_Open()/iwgfx_Close() commands concatenate nested calls. That is a 2nd call to iwgfx_Open() made before calling iwgfx_Close() does not create a new nested DisplayList but rather continues using the currently open DisplayList and increments a counter. Each iwgfx_Close() call decrements the counter until the matching iwgfx_Close() call is made for the initial iwgfx_Open() call at which time the Display list completed and closed. This property is important in limiting the total number of display lists generated by objects like faces which draw edges and points as part of their own display.

For objects with simple graphics, the draw commands are contained within the Draw() functions otherwise the draw commands for objects with more complicated graphics are isolated in methods called OutputGraphics(). The basic draw commands include the following:

  • iwgfx_DrawPoint(),
  • iwgfx_DrawLine(),
  • iwgfx_DrawPolyLine(),
  • iwgfx_DrawTriangle(), and for drawing meshes
  • iwgfx_NewNurbsRenderer(),
  • iwgfx_NurbsSurface(), and
  • iwgfx_NurbsCurve().

The Draw() and OutputGraphics() methods use the s_Disp IwDisplayParameters object values as needed to control the graphics that they add to the open DisplayList.

The ui_test implementations of these draw command functions make OpenGL calls, however other rendering engines can be used to display the ui_test graphics by rewriting these functions for the desired display engine.

Some of the classes derived from IwObject that have Draw() methods, with or without OutputGraphics() methods, include:

  • IwBrep IwPolyBrep IwBrepCache
  • IwRegion IwPolyFace IwSurfaceCache
  • IwShell IwCPolyFace IwCurveCache
  • IwFace IwPolyLoop
  • IwLoopuse IwPolyEdge IwVector2d
  • IwEdge IwVector3d
  • IwEdgeuse IwSurface IwExtend2d
  • IwVertex IwCubicBezierSurface IwExtend3D
  • IwFilletEdge IwBSplineSurface IwGrid
  • IwCurve IwTangentField
  • IwCurveClassification IwCompositeCurve IwAxis2Placement
  • IwPointClassification IwPolarBox
  • IwLineSegmentClassification IwCutter IwPseudoBox
  • IwPolyPointClassification IwPlaneCutter
  • IwCurveInterval IwGaps
  • IwSolution IwObjGaps
  • IwSolutionArray

The Draw() methods can be called at anytime to place the graphics for an object into a display list that gets added to the global DisplayList array. This feature is very helpful for debugging. Object Draw() methods can be placed within an algorithm to see how it modifies geometry as it progresses. The function my_gfx_loop() can be called at anytime to render the DisplayLists and to temporarily switch the flow of control to the ui_test interface so that the displayed graphics can be rotated, zoomed, and panned before continuing with the algorithm under study.

Standard object display is managed through a set of global lists named:

  • UserBreps contains all Breps to render
  • UserPoints contains all Points to render
  • UserCurves contains all Curves to render
  • UserSurfaces contains all Surfaces to render
  • UserPolyBreps contains all PolyBreps to render
  • UserBooleanTrees contains all BooleanTrees to render
  • UserGaps contains all ObjGaps to render

The user object lists are managed by the following functions:

  • my_Delete_All_Objects() Clear all user object lists.
  • my_Draw_All_Objects() Rebuild global DisplayList array with DisplayLists for every user object list item.
  • my_Dump_All_Objects() Dump a pretty printed description of every user object list item to standard output.
  • UserTest::Draw(IwBrep *) Add Brep copy to UserBreps
  • UserTest::Draw(IwPolyBrep *) Add PolyBrep copy to UserPolyBreps
  • UserTest::Draw(IwCurve *) Add Curve copy to UserCurves
  • UserTest::Draw(IwEdge *) Add Edge->Curve copy to UserCurves
  • UserTest::Draw(IwSurface *) Add Surface copy to UserSurfaces
  • UserTest::Draw(IwFace *) Add Face->Surface copy to UserSurfaces
  • UserTest::Draw(IwVertex *) Add Vertex->Point copy to UserPoints
  • UserTest::Draw(IwPoint3d *) Add Point copy to UserPoints
  • UserTest::Draw(IwPoint3d *, IwPoint3d *) Add Line to UserCurves
  • UserTest::Draw(IwTArray(IwPoint3d)) Add point copies to UserPoints

Functions within the ui_test interface and explicit calls in user programs add and remove objects from the user object lists. A call to my_Draw_All_Objects() clears the old global display list array and replaces it with a new array of display lists generated by calling the Draw() method on every user object list item.

The following picture summarizes the ui_test graphics flow of control:

Figure 1: ui_test graphics flow of control

View Parameters

The parameters that control the size, orientation, and positioning of the view box in model coordinates are stored in s_View, an IwViewParameters object. Currently this object is also used to store global interface state data.

Display Parameters

The parameters that control the output behavior of the Object Draw() functions are stored in s_Disp, an IwDisplayParameters object. Actions by the user change these values that will change the output graphics the next time that the global DisplayList array members are regenerated and redisplayed. The first set of values in this object is a set of draw bits and values that specify how to draw Breps, Faces, and Edges. These draw bits, their default values, and their associated behaviors include:

  • m_bHiddenCurve [FALSE] TRUE = Draw hidden line image
  • m_bDrawWireFrame [TRUE ] TRUE = Draw all Edges and Vertices
  • m_bDrawFacets [FALSE] TRUE = Generate/Output polygon facets
  • m_bDoShading [TRUE ] TRUE = display polygon facets as shaded image
    FALSE= display polygon facets as wires.
  • m_eTessellator [IW_DM_NLIB] Specify Nlib/openGL/NMTLib tessellator
  • m_bUseEdgeTypeColors [TRUE ] TRUE = Select EdgeColor based on type (manifold, lamina, wire)
    FALSE= Select EdgeColor based on attribute or owner color
  • m_bDrawCrossHatch [FALSE] TRUE = Draw UV IsoParameterCurve crosshatch lines
  • m_bDrawKnots [FALSE] TRUE = Draw edge knotPoints (place surface xhatch lines on knot boundaries)
  • m_bDrawPolygon [FALSE] TRUE = Draw surface->ControlNet and curve->ControlPolygon
  • m_bDrawNormals [FALSE] TRUE = Draw Surface midPoint Normal vector
  • m_bDrawCurvature [FALSE] TRUE = Draw Curve Curvature combs
  • m_bDrawDerivatives [FALSE] TRUE = Draw Curve 1st Derivative combs
  • m_bDrawPinCushion [FALSE] TRUE = Draw surface vector fields
  • m_eLastMode Last value stored by iwgfx_SetDrawingMode. note: this value does not determine the draw state - rather when it was set all the drawing bits were set to a specific state, since that time, any draw bit might have been altered. This is saved to be kind of backward compatible with the previous mode based interface style.

The following sets of values within the s_Disp object contain parameters that specify the size and look of the graphical output. These values, their default values, and their behaviors are as follows:

Shading

    • m_bFlatShading // [FALSE] TRUE = When shading use flat shading, not Gouraud shading
    • m_bHIddenPolygon // [FALSE] TRUE = When shading skip back facing polygons

Cross Hatch

    • m_lCrossHatchUCount // [4] How many curves between the Knots in the U cross hatch
    • m_lCrossHatchVCount // [4] How many curves between the Knots in the V cross hatch
    • m_dCrossHatchLineWidth // [1] Line width for the non-knot cross hatch curves
    • m_dCrossHatchKnotLineWidth // [2] Line width for the cross hatch curves on knot values

Hidden Curve

    • m_lHiddenCurveDash // [1] 0 = visible, 1 - dash invisible, 2 - silhouette only
    • m_vHCRView // [Origin=(0,0,0), XAxis=(1,0,0), YAxis=(0,1,0)]
    • m_bPerspective // [FALSE] TRUE = Use Projection View not OrthoGraphic

Tessellation

    • m_dCrvTessAngle [10.0] Angular tolerance used in tessellation in degrees
    • m_dSrfTessAngle [25.0] Angular tolerance used in tessellation in degrees
    • m_dChordHeight [ 0.1] Tolerance that controls curve and polygon tessellation using chord height measurements. If using NURBS this is a pixel space distance (i.e. 25.0). If using Tessellation it is a model space distance.
    • m_dMax3DEdge [-4.0] Expected max EdgeLength to tessellate. When negative, compute dMax3DEdge based on box object BoundingBox size as compute dMax3DEdge based on box object
    • BoundingBox size as dMax3DEdge = -0.025 * BBox.Size * dMax3DEdge
    • m_dMaxAspectRatio [ 0.0] When negative, use viewBased surface tessellation, else use view independent surface tessellation.
    • m_ePolygonOutputType [IW_PO_TRIANGLES] Specify how NMTLib tessellator
    • m_bViewBasedTess [FALSE] TRUE = NOT USED YETCurvature and Derivative Combs and PinCushions
    • m_lSamplePointCount [25] Number of curve sample points
    • m_lUPointCount [10] Number of surface U Sample Points
    • m_lVPointCount [10] Number of surface V Sample Points
    • m_ePinCushionType [IW_DM_UNIT_NORMAL] Select various vector fields
    • m_dPinCushionScale [10.0] Scale applied to PinCushion Vectors
    • m_dCurvatureScale [-20.0] Scale applied to curvature combs

Line and Point Size

    • m_dLineWidth [2.0] number of pixels used to draw lines
    • m_dPointSize [4.0] number of pixels used to draw point icons
    • m_bDashedLines [FALSE] TRUE = draw crosshatched curves dashed

Application Colors

    • m_DefaultColor [0,0,0] normal object default color
    • m_DefaultShadingColor [.7,.7,.7] normal object default color used for Shaded Breps
    • m_InterruptColor [IW_BIG_DOUBLE..] when set to anything but IW_BIG_DOUBLE, overrides normal object color selection

Object Color, Line Width, and PointSize Management

When a display list is played back into the OpenGL pipeline its graphics are output using the current color, line width, and point size values up until the time when the display list itself sets these values. The NMTLib rule for building display lists is that color is always set prior to outputting geometry objects but that line width and point size are only set when a special size is needed. Whenever a line width or point size is set within a display list it is restored to its initial value when that line width or point size value is no longer needed.

Controlling Object Color in an Object DisplayList

The ui_test interface stores a default object color in s_Disp that is used to render all objects without a color specification. The default color can be changed at run time with the iwgfx_SetDefaultColor() function.

At run time, an object?s color is specified by a color attribute attached to the object or to the object?s owning Brep or PolyBrep. Attach a color attribute to an object or its containing Brep or PolyBrep with the calls,

  • IwVector3dAttribute *pColor = new(*s_pContext) IwVector3dAttribute( IW_AI_COLOR, s_VColor);
  • pObject->AddAttribute(pColor);

For debugging purposes, an object?s specified color can be overridden by setting an interrupt color stored in the s_Disp object with the commands,

  • iwgfx_SetColor()
  • iwgfx_ChangeColor(), and
  • iwgfx_ClearColor().

Sometimes drawn objects are rendered using a special application color such as vectors drawn for curvature combs and surface normals, or curves belonging to wire or lamina edges. Under these circumstances, when there is no interrupt or attribute color rather than using the default color for the object, use an application color instead.

The rule for determining which object color to use while building an object DisplayList is executed while building a DisplayList for an object and encoded into the function iwgfx_OutputObjectColor(IwColorRuleType eColorRule).

The rule is

  1. When s_Disp has a set interrupt color ? use it, else
  2. When the object or the object?s Brep or PolyBrep owner has a color attribute ? use it, else
  3. When eColorRule != IW_CR_STANDARD ? use appropriate application color, else
  4. Use the DefaultColor stored in the s_Disp object.

Note the difference between modifying the default color and the interrupt color. Objects are drawn with the interrupt color even when the object has a color attribute or is drawn with an application color rule. Objects are only drawn with the default color when the object does not have a color attribute and the object is not being drawn with an application color rule. Also note shaded objects do not use the default color, they use the default shaded color.

The iwgfx_OutputObjectColor() function outputs the selected object color to OpenGL with a glColor() call which causes the color change to be compiled into the currently open DisplayList so that the color change will execute at the appropriate time when the DisplayList is finally played back by a call to iwgfx_DrawLists(). Only call the iwgfx_OutputObjectColor() function from within object Draw() and OutputGraphics() methods.

Implementing Object Draw() and OutputGraphics() Methods

All Draw() methods open and add a new DisplayList to the global DisplayList array to contain all the draw commands used to render the object. The behavior of many Draw() functions depend on the values stored within s_Disp. Most Draw() methods only use the s_Disp values while others overwrite some of those values as they draw. Two functions exist to fetch the s_Disp values depending on if the parameters are only to be used or if they are to be overwritten. These functions are:

  • iwgfx_RefGlobalDisplayParameters Get s_Disp reference when using parameters without modifications.
  • iwgfx_GetGlobalDisplayParameters Get copy of s_Disp parameters, needed when the called Draw() method overwrites any of the s_Disp parameter values.

A typical Draw() method

void Object::Draw()

const

{

// locals: global display parameters

const IwDisplayParameters &rDisp = iwgfx_RefGlobalDisplayParameters();

// use following instead when view parameter modification is required

// IwDisplayParameters sDisp ;

// Iwgfx_GetGlobalDisplayParameters(sDisp) ;

// start new DisplayList (unless one is already open)

iwgfx_Open() ;

// set Object Color

IwVector3D sColor = iwgfx_OutputObjectColor

(this, rDisp.GetShadedColorRule()) ;

// output Object graphics

OutputGraphics(rDisp) ;

// restore color state and end new DisplayList

iwgfx_OutputColor(sColor) ;

iwgfx_Close() ;

} // end Object::Draw

Writing OutputGraphics() methods

The stream of color, line width, point size, and draw functions that render the object are placed within the OutputGraphics() method. Because a DisplayList is open, these calls do not get output to the graphics pipeline. Instead they get compiled into the open DisplayList to be played back into the graphics pipeline at some future time. Some objects with simple graphics don?t bother to isolate the render functions into a separate OutputGraphics() method choosing to place the draw function calls directly into the Draw() method.

Outputting the color, line width, and point size values is not done with the iwgfx_SetCalls() but with iwgfx_OutputCalls(). The iwgfx_SetCalls() modify the global values stored within s_Disp while the iwgfx_OutputCalls() push a value out to the OpenGL stream. The set of iwgfx_OutputCalls() includes:

  • iwgfx_OutputObjectColor()
  • iwgfx_OutputColor()
  • iwgfx_ChangeOutputColor()
  • iwgfx_OutputPointSize()
  • iwgfx_OutputLineWidth()
  • iwgfx_OutputDashedLines()

Once the color, line width, and point size parameters are set as desired output actual geometry shapes using the iwgfx_DrawCalls() including:

  • iwgfx_DrawPoint(),
  • iwgfx_DrawLine(),
  • iwgfx_DrawPolyLine(),
  • iwgfx_DrawTriangle(), and for drawing meshes
  • iwgfx_NewNurbsRenderer(),
  • iwgfx_NurbsSurface(), and
  • iwgfx_NurbsCurve().

Draw() and OutputGraphics() Architectural Rules

The NMTLib architecture rules for implementing Draw() and OutputGraphics() methods include:

  1. Never use the iwgfx_SetCalls() from within a Draw() or OutputGraphics() method. Abiding by this rule prevents one draw routine from modifying the behavior of other draw routines.
  2. The generalization of rule 1 is: never make a change to the global display parameters stored in s_Disp from a Draw() or OutputGraphics() method. Some OutputGraphics() methods want to override s_Disp global values. Always send such methods a copy of the s_Disp values retrieved with the iwgfx_GetGlobalDisplayParameters() function so that any overrides cannot have side effects on other draw routines.
  3. Avoid calling a Draw() method from a Draw() or OutputGraphics() method, instead make sure the desired object graphics is divided up into a Draw() and OutputGraphics() function and call the OutputGraphics() function directly. This rule is not required as long as all Draw() functions make sure that they make no permanent change to the color, line width, and point size state of the OpenGL pipeline and the values stored in s_Disp.
  4. Whenever the point size or line width is changed it must be changed back to its current value when the need for the new size is done. This allows draw functions to call other Draw() or OutputGraphics() methods without side effects.

DisplayLists in OpenGL, NMTLib, and ui_test

An openGL displayList is a compiled set of openGL commands that can be run repeatedly very quickly. One creates a displayList by

  • opening a new displayList
  • executing any set of openGL rendering commands
  • close displayList

All the executed openGL commands get compiled into a openGL secret format. Applications access the display through an ID, which is implemented as an integer value. The display is rendered by sending the displayList ID to the openGl call, glCallList(DisplayListID).

NMTLib rendering is designed to build and store displaylists while keeping color, line width, point size, and dashed line pattern information outside of the list so that an application can vary those without having to rebuild the displaylist. This makes it possible for NMTlib rendering to support highlighting and the like at interactive rates. One makes iwgfx_Calls() which encapsulate the openGl calls.

NMTLib does this by creating and managing its own display lists in objects called IwDisplayList. The IwDisplayList stores the openGL DisplayList Id and color and draw size parameters. NMTLib stores all the the displayLists in a global array and uses the function iwgfx_DrawLists() to render those. Prior to asking openGL to render the openGL display list the iwgfx_DrawLists() funtion makes openGL calls to set color and draw size as

  • iwgfx_OutputPointSize(rDisplayList.GetPointSize()) ;
  • iwgfx_OutputLineWidth(rDisplayList.GetLineWidth()) ;
  • iwgfx_OutputDashedLines(rDisplayList.GetDashedLine()) ;
  • iwgfx_OutputColor(rDisplayList.GetColor()) ;
  • // output DisplayList
  • GLuint lDisplayListId = rDisplayList.GetDisplayListId() ;
  • glCallList(lDisplayListId); IW_GL_DUMP("glCallList", lDisplayListId);

All one needs to do to change a color or draw size is to modify the rDisplayList member value and leave it to iwgfx_DrawLists() to get that information out at display time.

NMTLib's list of IwDisplayLists is stored in an instance of the class IwViewParameters. This instance should be created and stored by the ui-test application as part of the IwView object. However, due to evolutionary reasons, NMTlib creates one global IwViewParameters object called s_View.

Applications need to control which color and draw sizes are stored in the IwDisplayList objects when the objects are created and when an application wants to change those values.

IwDisplayLists are created and stored in the global list by the call iwgfx_Open(). the current behavior for iwgfx_Open() is to initialize the stored color and draw sizes from values fetched from a passed in color value and the calls,

  • iwgfx_GetLineWidth() and
  • iwgfx_GetPointSize().

The values fetched by iwgfx_GetLineWidth() and iwgfx_GetPointSize() come from a second global graphics value called s_Disp, an instance of IwDisplayParameters. The IwDisplayParameters class is a long list of all the object rendering values that are displayed in ui_test. Values in s_Disp can be set with iwgfx_calls. A typical calling sequence to store the desired color and draw size parameters in an IwDisplayList are:

  • // set pointsize = 1, linesize = 2, color = 0.0, 0.0, 1.0 in global s_Disp object.
  • iwgfx_SetLook(1, 2, 0,0,1) ;
  • // create an IwDisplayList using values fetched from s_Disp and store it in the displayLists array within the global s_View object.
  • iwgfx_Open() ;
  • . . . gl Draw commands get compiled into the drawlist . . .
  • perhaps: pBrep->Draw() ;
  • // closes the openGl display list
  • iwgfx_Close();

New behaviors

The iwgfx_Open() method has been modified to take optional color, line width, and point size arguments. If used, these are stored in the IwDisplayList created and placed on the global displayList array instead of using the ones stored in s_Disp. This allows a special draw function to use special colors and draw sizes rather than the general ones. I used this feature when creating a drawList for the new IwGap object. An example call might look like

  • // start new displayList (unless one is already open)
  • // use gap color and draw sizes rather than default ones
  • double dLineWidth = iwgfx_GetGapLineWidth() ;
  • double dPointSize = iwgfx_GetGapPointSize() ;
  • IwVector3d &rGapColor = iwgfx_GetRuleColor(NULL, IW_CR_GAP) ;
  • iwgfx_Open(rGapColor, &dLineWidth, &dPointSize);

Later when iwgfx_DrawLists() is called, the drawList created by the above call will be rendered after setting the color, line width and Point size values as specified above.

For highlight purposes one will want to fetch the IwDisplayList object stored in the global array and modify the stored colors and draw sizes. This is a little tricky only because the drawlists on the global list array are stored by value, not by reference.

The new function iwgfx_GetDisplayList() returns a pointer to the IwDisplayList taking a GLuint Id value. So to support highlighting in application one has to manage the following challenges:

  1. Some kind of I/O that fetches the IwDisplayList Id value.
  2. IwDisplayList *pDisplayList = iwgfx_GetDisplayList(lDisplayListId) ;
  3. Some combination of pDisplayList->SetColor(), pDisplayList->SetPointSize(), pDisplayList->SetLineWidth() calls.
  4. Redisplay all lists with iwgfx_DrawLists().

Solvers Intersectors

Global Solver Tutorial

This portion of the tutorial is for advanced users who wish to programmatically extend the global solver functionality of SMLib to suit their particular application. The Global Solver is a customizable search engine. It searches one or more binary trees (IwTree) with bounding boxes on the nodes. There are several search criterion which are already implemented, minimization, maximization, intersection, and at distance. By default the global solver (IwGlobalSolver) will return pointers to the nodes which satisfy the search criterion. Typically you will want to subclass IwGlobalSolver to specialize one or more of the following:

  • Removal of branches from the search (BranchMayContainAnswers).
    • This is done automatically for minimize, maximize, at distance, and intersect.
  • Determination of when to invoke a local solver (AreReadyForLocalSolve).
    • This is done automatically when leaf nodes of all trees are found.
  • How to subdivide (Subdivide).
    • By default it only traverses the existing tree.
    • It will optimize minimization and maximization by trying to take the best branch first.
  • Invocation of a method for determining a local solution and if additional subdivision is required (LocalSolve).
    • Without customization this returns only pointers to nodes.
    • Usually this method will be overridden to call an appropriate local solver.
  • When solutions are identical (IdenticalSolutions).
    • By default solutions are identical when the solution value and all of its parameters are within effective zero of each other.

Depending upon what you want to do and how, the details of how to implement these methods can vary greatly. We suggest that you attempt to familiarize yourself with the global solver implementations done in IwCurve.cpp and IwSurface.cpp prior to attempting an implementation.

Local Solver Tutorial

This portion of the tutorial is for advanced users who wish to programatically extend the Local Solver functionality of GSNLib to suit their particular application. GSNLib provides a class interface for determining solutions to one-dimensional functions (IwLocalSolve1d) and N-dimensional functions (IwLocalSolveNd). A majority of the local solving is done utilizing Newton-based itteration. All of the "Local" methods on curves and surfaces invoke one of these solvers. One can create new solvers by providing new function evaluators. This can be done by subclassing the corresponding evaluator object (IwEvalFunctionObject, IwEvalNFunctionsObject). The evaluators must be able to evaluate the function/functions and supply its derivative/Jacobian. If you are unable to compute the Jacobian matrix for the function you are solving, there are publicly available methods for computing solutions to equations which compute their own Jacobian (see Numerical Recipes in C). The basic procedure for creating a new local N-dimensional solver is as follows:

  1. Create a subclass or IwEvalNFunctionsObject. IwFindCCNormENFO is used in our example.
  2. Other than the domains and parameters which are passed into the evaluation method, which fields will be needed to evaluate the function and its Jacobian at a given point? In our example, pointers to the two curves are needed. You may also want to include data which will help you determine when an adequate solution is found. The tolerance is used to do this in our example.
  3. Write a constructor which loads the extra fields and a corresponding destructor.
  4. Implement the virtual Evaluate method.
    • Evaluate the functions (rF) at the given input parameters (rX).
    • If requested, evaluate the Jacobian matrix (pOptJacobian).
    • Test to determine if the current parameters provide an adequate solution. Typically, GSNLib forces total convergence. This step is not really necessary because the solver which calls the evaluator will be able to determine when it converges based on the parameters.
  5. Invoke the new local solver.
    • Load the intervals for each parameter into an IwExtentNd object of appropriate size. Note that it is possible to solve without specifying intervals.
    • Load the periodicity flags into an IwTArray(ULONG) of appropriate size. If the local solver is intended for use in a global solver which breaks down the curve into smaller segments, you may improve performance by always making periodicity FALSE.
    • Load the initial guess parameters. If intervals are used then these parameters must not be outside of the interval domains. If it is possible to get more than one answer out of an interval, you may wish to choose several guess points and check to make sure they all converge to the same solution.
    • Create the customized evaluator object using the constructor which initializes any additional fields. This is usually done on the stack.
    • Create a local solver object (IwLocalSolveNd) using the evaluator object, the intervals and periodicities.
    • Optionally set up the boundary handler and various tolerances.
    • Invoke the SolveIt method on the local solver with the guess parameters and acceptable tolerance.
    • SolveIt returns a flag indicating if a solution was found and a solution vector (IwTArray(double)).
    • If a solution is found, the solution vector will contain the corresponding parameters.

Curve (IwCurve)

- see Figure 5 and Figure 6

  • point and derivative evaluation
  • evaluation of geometric properties
  • bounding box calculation
  • creation of a mirror curve
  • creation of a curve by projection to a plane
  • transformation with optional scaling
  • high precision curve length computation
  • closure, periodicity and degeneracy testing
  • tessellation using both chord height and angular tolerances
  • find intersections with a plane
  • find inflection points
  • find minimum/maximum magnitude of 1st derivative and radius of curvature
  • find 1st derivative or radius of curvature of a given magnitude
  • find points where tangent is parallel to X, Y, and/or Z axis
  • find points where tangent is parallel to a given vector
  • find silhouette points with either parallel or perspective projection

Figure 5: Curve Properties and Analysis

  • 1st Image: Intersection of a curve with an infinite plane
  • 2nd Image: 3D Silhouette Computation (perspective - blue, parallel projection - red)
  • 3rd Image: Points on curve where tangent is parallel to a given vector
  • 4th Image: Inflection points of a curve - points with zero curvature

Figure 6: Curve Properties and Analysis continued

  • 1st Image: Minimum (red) and maximum (blue) first derivative magnitudes
  • 2nd Image: Minimum (red) and maximum (blue) radius of curvature
  • 3rd Image: Find first derivative (blue) and radius of curvature(red) of given magnitude
  • 4th Image: Find points on curve where first derivative is perpendicular to a given axis (blue - X axis, red - Y axis)
  • 5th Image: Find points on a curve where first derivative is perpendicular to any axis

Composite Curve ( IwCompositeCurve)

  • Creation of composite curves from an unordered set of curves
  • Offset of a composite curve with filleting or extension and trimming - see Figure 13
  • Simultaneous offset of multiple composite curve loops (i.e. face with holes)
  • Creation of a single NURBS curve from a composite

Figure 13: Offsetting/Insetting Composite Curves

Shown are linear extension and corner filleting.

B-Spline Curve (IwBSplineCurve)

  • construction/editing/query using STEP format or NLib data structures
  • continuity calculation, removal of extra knots, conversion to 2D
  • creation by joining connected curve segments
  • creation of a circle segment, ellipse segment line segment, degenerate curve
  • creation of mirror curve
  • editing the end points of a B-Spline
  • extraction of analytical information for lines and arcs
  • offseting of smooth curves

Curve/Point - see Figure 1

  • minimization - find point(s) on curve closest to the point
  • maximization - find point(s) on curve farthest from the point
  • normalization - find point(s) on curve where tangent is perpendicular to vector to the point
  • at distance - find point on curve at given distance from point
  • intersection - find parametric value(s) on curve corresponding to the point
  • projected minimization and maximization - as projected to a plane
  • directed minimization and maximization - along a vector as projected to a plane

Figure 1: Curve/Point Solver

  • 1st Image - Minimization of a point and curve. Finds the closest point between a curve and a point. Note that some points have multiple closest points because of curve symmetry.
  • 2nd Image - Normalization of point and curve. Finds position on a curve where vector from a point to a curve is perpendicular to the curve's tangent vector.
  • 3rd Image - Maximization of point and curve. Finds the farthest point between a curve and a point.
  • 4th Image - At Distance between of point and curve. Finds the position on a curve at a given distance from a point.

Curve/Curve - see Figure 2

  • minimization - find points on the two curves where the distance between them is minimal
  • maximization - find points on the two curves where the distance between them is maximal
  • normalization - find points on the curves where the tangents are perpendicular to the vector between the curves
  • at distance - find points on the two curves which have a given distance between them and have the same angle to the vector between them
  • projected solvers - intersection, minimization, maximization, directed minimization, signed directed minimization, angle to plane, angle between, signed pivot angle between
  • 3D directed minimization - collision detection - find first contact point moving along a 3D vector

Figure 2: Curve/Curve Solver

  • 1st and 2nd Image: Minimization and maximization of two symmetric nurbs. Note that multiple answers result when more than one minimum or maximum points fall within the user specified tolerance.
  • 3rd Image: Normalization between two curves finds all points where the vector between two points is perpendicular to the tangent vectors of the curves at those points.
  • 4th Image: At Distance between two curves finds corresponding points on two curves which are at a given distance and the angle between the tangent vectors are equivalent.

3D Curve/Curve Intersection - see Figure 3

  • handles coincidence, tangency, near tangency, and grazing conditions
  • projected curve/curve intersection
  • self-intersection detection is also provided - See Figure 4

Figure 3: Curve/Curve Intersection - produces either single points or coincident segments (3rd Image)

Figure 4: Curve self-intersection - finds points where the curve crosses itself

Surface (IwSurface)

  • point and derivative evaluation, evaluation of geometric properties
  • normal evaluation, evaluation of a normal section
  • high precision surface area computation
  • dropping 3D vectors into parameter space
  • closed and periodic testing, singularity testing
  • validation
  • transformation

B-Spline Surface ( IwBSplineSurface )

  • construction using STEP format or NLib data structures
  • continuity calculation
  • query
  • creation of planar section curves - see Figure 14
  • creation of parallel projection curves - see Figure 15

Figure 14: Creation of Planar Section Curves

Figure 15: Creation of Curves by Parallel Projection

Surface/Point - see Figure 7 and Figure 8

  • minimization - find point(s) on surface closest to point
  • maximization - find point(s) on surface farthest from the point
  • normalization - find point(s) on surface where normal is parallel to vector to point
  • intersection - find parametric value(s) on surface corresponding to the point
  • 3D directed minimization - collision detection - find first contact point moving along a 3D vector

Figure 7: Surface/Point Solver - minimization (closest point) between a point and a surface

Figure 8: Surface/Point Solver - normalization of a point and a surface

Surface/Curve - see Figure 9

  • minimization - finds where distance between curve and surface is minimal
  • maximization - finds where distance between curve and surface is maximal
  • normalization - finds where vector between entities is parallel to surface normal and perpendicular to the curve tangent
  • 3D directed minimization - collision detection - find first contact point moving along a 3D vector

Figure 9: Surface/Curve Solver

Minimization (red) and maximization (blue) of the distance between a curve and a surface.

Curve/Surface Intersection - see Figure 10

  • handles coincidence, tangency, near tangency, and grazing conditions.

Figure 10: Curve/Surface Intersection

Surface/Surface - see Figure 11

Local and global version of the following:

  • minimization - find where distance between surfaces is minimal
  • maximization - find where distance between surfaces is maximal
  • normalization - finds where vector between surfaces is parallel to surface normals
  • 3D directed minimization - collision detection - find first contact point moving along a 3D vector

Figure 11: Surface/Surface Solver

minimization (red) and maximization of the distance between two surfaces.

Dropping Curves - see Figure 12

  • create the 2D parameter space curve(s) corresponding to a 3D curve which lies on or near a surface
  • restricted to surfaces with at least C1 continuity
  • advanced dropping which allows C0 continuity surfaces

Figure 12: Dropping Curves

Produce a 2D parameter space image of a 3D curve.

Numerical utility classes

  • IwIntegrator - provides numerical integration
  • IwLocalSolve1d - provides one-dimensional Newton based local solver algorithm
  • IwLocalSolveNd - provides N-dimensional Newton based local solver algorithm
  • IwGlobalSolver - provides global solver algorithms

Advanced Surfaces

Advanced Surface/Surface Intersection - see Figure 16

Figure 16: Surface/Surface Intersection with curve knots displayed

  • Extension of basic surface/surface intersection found in GSLib
  • Adds the ability to extract intersection curves along surface tangencies - surface normals are parallel along the intersection curve - see Figure 22
  • Adds the ability to process curves where singularities occur - point where surface normal are parallel and intersection curves pass through the point - see Figure 23
  • Adds the ability to produce zero length or NULL curves - point where surfaces touch at a single point but do not intersect near that point - see Figure 24
  • Fast detection of interior closed intersection loops - see Figure 25
  • Enhances the performance of the basic intersector

Figure 22: Surface/Surface Intersection with Tangent Surfaces

Figure 23: Surface/Surface Intersection with Singularity Points

Figure 24: Surface/Surface Intersection

which produces a zero length curve.

Figure 25: Surface/Surface Intersection

which produces multiple intersection loops.

Surface Curve Tracing Abstract Class (IwSurfaceTracer)

  • Provides an Object-Oriented Framework which should enable the fast implementation of additional curve extraction utilities through subclassing and implementation of virtual methods.

Sectioning of a Surface with an Infinite Plane (IwSurfaceSection) - see Figure 26

Figure 26: Surface Sectioning with a Plane

  • Implementation in context of the Curve Tracing Framework.
  • Able to detect curves where surface is tangent to the plane.
  • Able to detect singularity points (points where 4 curves come to a point) - see Figure 27
  • Able to detect tangent points (point where surface touches plane at a single point) - see Figure 27

Figure 27: Surface Sectioning with a Plane

Surface Silhouette Curve Creation (IwSurfaceSilhouette) - see Figure 17

  • Implementation in context of the Curve Tracing Framework.
  • Able to detect all silhouette curves on a surface - see Figure 29
  • Able to detect silhouettes which correspond to iso-parametric curves.
  • Able to detect silhouettes which pass through the poles of the surface.

Figure 17: Surface Silhouette Curve Extraction

Figure 29: Surface Silhouette Curve Extraction

Curve Projection on C0 Surfaces (IwSurfaceDropCurve)

  • Provides for the creation of UV trim curves from 3D curves
  • Surfaces may have discontinuities of the tangent plane (C0 continuity)
  • Requires that the 3D curve lie on or very close to the surface

Functionality

Curve/Point

IwCurve::GlobalPointSolve

  • minimization - IW_SO_MINIMIZE - find points on curve closest to point
  • maximization - IW_SO_MAXIMIZE - find points on curve farthest from point
  • normalization - IW_SO_NORMALIZE - find points on curve where tangent is perpendicular to vector to the point
  • intersection - IW_SO_INTERSECT - find where points intersect a curve
  • at distance - IW_SO_AT_DISTANCE - find point on curve at given distance from point
  • signed directed minimize - IW_SO_SIGNED_DIRECTED_MINIMIZE - projected operation to find where a point touches the curve when being moved along a given vector
  • directed minimization - IW_SO_DIRECTED_MINIMIZE - find points on curve of the minimal projected distance between the point and curve along a given vector which is parallel to the projection plane
  • directed maximization - IW_SO_DIRECTED_MAXIMIZE - find points on curve of the maximum projected distance between the point and curve along given vector which is parallel to the projection plane
  • projected minimization - IW_SO_PROJECTED_MINIMIZE - find points on curve minimum projected distance between the point and curve
  • projected maximization - IW_SO_PROJECTED_MAXIMIZE - find points on curve of the maximum projected distance between the point and curve

IwCurve::GlobalPropertyAnalysis

  • find inflection points, intersection of plane or cylinder, silhouette points, maximum and minimum first derivatives on the curve, angle to plane
  • see documentation on IwCurvePropertyType for additional curve properties

Curve/Curve

IwCurve::GlobalCurveSolve

  • minimization - IW_SO_MINIMIZE - find points where distance between two curves is minimal
  • maximization - IW_SO_MAXIMIZE - find points where distance between two curves is maximal
  • normalization - IW_SO_NORMALIZE - find points on two curves where the tangents are perpendicular to the vector between the curves
  • at distance - IW_SO_AT_DISTANCE - find points on the two curves at given distance and same angle between them
  • signed directed minimize - IW_SO_SIGNED_DIRECTED_MINIMIZE - projected operation to find first point two curves touch when being moved along a given vector
  • directed minimization - IW_SO_DIRECTED_MINIMIZE - find points of the minimal projected distance between two curves along a given vector which is parallel to the projection plane
  • directed maximization - IW_SO_DIRECTED_MAXIMIZE - find points of the maximum projected distance between two curves along given vector which is parallel to the projection plane
  • projected tangency - IW_SO_PROJECTED_TANGENCY - find points where projections of curves become tangent
  • signed angle minimization - IW_SO_SIGNED_ANGLE_MINIMIZE - find first points where projections of curves touch when being rotated about point
  • projected minimization - IW_SO_PROJECTED_MINIMIZE - find points minimum distance between projections of two curves
  • projected maximization - IW_SO_PROJECTED_MAXIMIZE - find points maximum distance between projections of two curves
  • projected intersection - IW_SO_PROJECTED_INTERSECT - find points where projected curves intersect

IwCurve::GlobalCurveIntersect - curve/curve intersection - including coincidence, tangency and grazing conditions

IwCurve::GlobalCurveSelfIntersect - curve self intersection - including coincidence, tangency and grazing conditions

Trimmed Surface(s)/Point

IwTopologySolver::BrepPointSolve

  • minimization - IW_SO_MINIMIZE - find points on the trimmed surface(s) closest to the point
  • maximization - IW_SO_MAXIMIZE - find points on the trimmed surface(s) farthest from the point
  • normalization - IW_SO_NORMALIZE - find points on the trimmed surface(s) where normal is parallel to vector to the point
  • intersection - IW_SO_INTERSECT - find points where the point intersects the trimmed surface(s) to with a given tolerance
  • projected minimization - IW_SO_PROJECTED_MINIMIZE - find points where projection of the point and the trimmed surface(s) are minimized
  • projected maximization - IW_SO_PROJECTED_MAXIMIZE - find points where projection of the point and the trimmed surface(s) are maximized
  • rayfiring - IW_SO_RAYFIRE - find first point(s) where ray intersects the trimmed surface(s)

Trimmed Surface/Curve

IwTopologySolver::BrepCurveSolve

  • minimization - IW_SO_MINIMIZE - find points on the trimmed surface(s) closest to the curve
  • maximization - IW_SO_MAXIMIZE - find points on the trimmed surface(s) farthest from the curve
  • normalization - IW_SO_NORMALIZE - find points on the trimmed surface(s) where normal is parallel to vector to the curve
  • intersection - IW_SO_INTERSECT - find points where the curve intersects the trimmed surface(s) to with a given tolerance
  • projected minimization - IW_SO_PROJECTED_MINIMIZE - find points where projection of the curve and the trimmed surface(s) are minimized
  • projected maximization - IW_SO_PROJECTED_MAXIMIZE - find points where projection of the curve and the trimmed surface(s) are maximized

Trimmed Surface/Trimmed Surface

IwTopologySolver::BrepBrepSolve

  • minimization - IW_SO_MINIMIZE - find closest points between the two sets of trimmed surfaces
  • maximization - IW_SO_MAXIMIZE - find farthest points between the two sets of trimmed surfaces
  • normalization - IW_SO_NORMALIZE - find normal points between the two sets of trimmed surfaces
  • intersection IW_SO_INTERSECT - find points where the two sets of trimmed surfaces intersect to with a given tolerance
  • projected minimization - IW_SO_PROJECTED_MINIMIZE - find points where projection of the two sets of trimmed surfaces are minimized
  • projected maximization - IW_SO_PROJECTED_MAXIMIZE - find points where projection of the two sets of trimmed surfaces are maximized

Surface Silhouette Curves

IwBSplineSurface::CreateSilhouetteCurves

  • detects all silhouette curves, identifies iso-parametric curves and processes singularities

Dropping Curves

  • Create the 2-D parameter space curves corresponding to a 3-D curve which lies on or near a trimmed surface that is at least a C1 surface. Use IwSurface::DropCurve
  • Parallel projection of a curve onto a surface resulting in both the 3-D and 2-D space curves. Use IwBSplineSurface::CreateParallelProjectionCurves
  • Projection with trimming of curve to surface boundary and internal discontinuties. Use IwSurface::DropAndTrimCurve

Topological/Geometrical Query Operations

  • retrieval of topological adjacency relationships between faces IwFace) , loops (>IwLoop), edges IwEdge) and vertices>IwVertex); See as an example IwEdge::FindRadialSector
  • direct retrieval of all faces, edges or vertices in a trimmed surface. See as an example IwFace::GetEdges
  • retrieval of geometric information (point, 2-D and 3-D curve, surface) corresponding to topological elements (vertex, edge, face). For example: IwEdge::GetCurve;and IwFace::GetSurface
  • retrieval of the boundary box of a trimmed surface. Use IwFace::CalculateBoundingBox
  • retrieval of trim boundary with loop and orientation information. Use IwFace::GetLoops

Numerical Utility Classes

  • integration - IwIntegrator
  • one-dimensional Newton based local solver algorithm - IwLocalSolve1d
  • n-dimensional Newton based local solver algorithm - IwLocalSolveNd

Miscellaneous Classes

  • curve cache decomposition into Bezier segments - IwCurveCache
  • surface cache decomposition into bi-Bezier segments - IwSurfaceCache
  • cache manager - IwCacheMgr
  • fixed sized memory allocation manager - IwMemBlockMgr
  • utility representation of an iso-parametric curve on surface - IwCrvOnSurf

SMLib Plus Classes

  • IwPrimitiveCreation - class and methods for creating solid primitives and performing 2D Boolean operations.
  • IwTopologySweep - class and methods for doing topological portion of Non-Manifold Sweeping.
  • IwSweepGeometryCreation - abstract method for geometric portion of sweeping operations.
    • IwTranslationalSweepGeomtry, IwRotationalSweepGeometry
  • IwTrimmingTools - tools to help convert between different trimming formats including unordered loops, unoriented loops, loops that cross seams.

Booleans (SMLib only)

Boolean operations are an advanced modeling operation only available in SMLib (not TSNLib). Booleans are accomplished with the IwMerge class which takes two objects as Input. Input objects can be manifold or non manifold. In other words, it is possible to boolean solids and surfaces. Some examples are listed below:

  • Solid Union - Solid yields the solid plus the portion of the second solid outside of the first solid
  • Solid Union - Surface yields the solid plus the portion of the surface outside of the solid.
  • Solid Intersect - Surface yields the portion of the surface which is inside of the volume of the solid.
  • Solid Difference - Surface yields the solid plus the portion of the surface inside of the solid. In some cases this may cut the solid into multiple regions.
  • Surface Difference - Solid yields the portion of the surface not inside the solid.
  • Surface Union - Surface yields the two surfaces plus attached by an intersection curve.
  • Surface Intersect - Surface yields just the intersection curves.
  • Surface Difference - Surface yields the first surface with the curves of intersection.

The primary operators are IwMerge::ManifoldBoolean or IwMerge::NonManifoldBoolean. A flag is used to specify the operation type union, difference, intersection, etc. The possible operation types are listed below.

IW_BO_UNION, // A Union B
IW_BO_INTERSECTION // A Intersection B
IW_BO_DIFFERENCE // A minus B
IW_BO_EXCLUSIVE_OR // ExclusiveOr = (A Union B) - (A intersect B)
IW_BO_MERGE // Merge B into A
IW_BO_PARTIAL_MERGE // Merge subset of B parts into A.
IW_BO_IMPRINT // A plus (A Intersect B)
IW_BO_EXTRACT_SEPARATE // Merge B into A placing the separate regions in separate breps.
IW_BO_SLICE // Slice a solid with a sheet and

The following code demonstrates how to set up and difference two Breps:

// base brep in difference
IwBrep *pBrep1 = ...;

// brep to subtract
IwBrep *pBrep2 = ...;

// Initialize merge object
IwMerge sMerge(crContext, pBrep1, pBrep2);

// Perform Boolean operation
IwBrep *pResult = NULL;
SER (sMerge.ManifoldBoolean(IW_BO_DIFFERENCE, pResult));

One great thing about having an underlying NMT representation is that the Boolean operation is now a closed operation. In other words you can not do a Boolean that would produce something you can not represent topologically. It is possible to union two solids which just touch along an edge or at a vertex whereas this is not a valid representation with traditional manifold operators.

Merge

The Merge operation is like the Boolean in that it combines the topologies from two separate Breps. The major difference being that the Merge only adds topology it does not remove any topology. For example the Merge of a box and a cylinder will contain all of the original edges and faces of both the cylinder and box plus any intersection edges. The Merge of two solids will typically create edges which have four or more faces and may have several regions.

The Non-Manifold Topology enables us to represent the results of the Merge completely and explicitly with a single topological structure. We do not need to break things up into separate volumes. You may merge any combination of wireframe, surfaces, open shells, solids, and non-manifold topology models. To invoke the merge operation you do the same thing as show above in the Boolean except that the operation you send in is "IW_BO_MERGE " instead of "IW_BO_DIFFERENCE ".

IwMerge::StitchFace

This operations allows the user to start with a brep and add a new surface one by one merging, intersecting, and sewing after every addition.

IwMerge::merge_breps

This is a convenience function to perform repeated boolean operations on a list of breps.

IwBrep::MakeManifold

The MakeManifold operation produces a manifold solid from a non-manifold topology. It deletes excess faces, edges and vertices to get to either a manifold topology. The following shows how to utilize the operation.

// Make manifold by default uses all regions
// except the infinite region.
// Do not keep interior faces.
SER(pResult->MakeManifold(NULL, FALSE));

// Somehow we select which regions to keep in
// a cellular topology.
IwTArray<IwRegion*> sRegionsToKeep = ....;

// Remove all faces not bounding one of
// sRegionsToKeep. Note that any faces which
// between regions will also be kept.
SER (pResult->MakeManifold(sRegionsToKeep, TRUE));

Fillets (SMLib only)

Filleting is an advanced operation that is only available in SMLib (not TSNlib). Filleting is the insertion of a connecting surface, called the fillet surface, between two other surfaces to control the shape of how the two surfaces connect to one another. The common fillet sequence is to replace an existing edge between two surfaces with a new fillet surface and two new rail curves marking where the new fillet surface connects to the original base surfaces. Typically the original surfaces need to be trimmed back to the new rail curves. This trimming operation, along with the creation of the fillet surface, the rail curves, and the modification of the topology connections to make sure that all the pieces are connected together within the Brep model, are all parts of the filleting functionality. Additionally, when working with Brep models, extra care is taken at the ends of the fillets to connect the new fillet geometry (the fillet surface and the rail curves) to the rest of the Brep. The fillet operation manages this by creating special-case fillet end-cap surfaces, edges, and vertices as necessary.

SMLib supports several different kinds of fillet surface shapes to enable several different styles for connecting one surface to another. All but the linear cross section blend of these styles connect the surfaces together smoothly, replacing sharp edges with rounded-smooth edges and corners. The linear cross section blend is used to insert chamfers. These styles include constant radius blends, variable radius blends, circular cross section blends, linear cross section blends (chamfers), and G1, G2 and G3 continuous blends. SMLib filleting has been implemented in a modular fashion making it straightforward to add additional fillet styles, potentially including things like elliptical, parabolic, and cubic blends.

Filleting Framework

SMLib has implemented a filleting framework which handles a wide range of topological cases with a reasonable set of geometric cases. The framework handles the common bookkeeping tasks and has object-oriented handlers to accommodate difficult conditions. Examples of difficult conditions include fillets rolling off the edge of a surface, and fillet rail curves self-intersecting. SMLib implements a basic set of default handlers for each of these conditions. If necessary, end users can change the default behavior by implementing a subclass of the particular handler. For example, if the fillet rolls off the surface, the user might want to change the default behavior of rolling the fillet over onto the adjacent surface to cutting the fillet, rolling a ball along the edge, or just quitting. Users can also implement a subclass handler to change the default behavior of correcting for self-intersecting rail curves

The Filleting Framework supports both Surface-Based Filleting and Topology-Based (or Solid-Based) Filleting. Surface-Based Filleting takes two surfaces and produces a separate fillet surface between them and optionally trims the original surfaces to the new rail curves of the fillet surface. Topology-Based Filleting takes a single Brep and inserts new topology to round edges and corners. Topology-Based Filleting can add fillets to any manifold edge within a solid, open shell, or non-manifold Brep. Because SMLib represents Non-Manifold Topology, SMLib is able to represent results of situations that would normally cause failures in other systems.

SMLib's Filleting Framework is extremely flexible. Adding new functionality to the Framework is the real power of this software. It is designed to make adding seamless extensions as easy as possible. For example, the addition of variable-radius filleting required the implementation of only five methods totaling less than 200 lines of code. Should you decide to extend the Filleting capabilities of SMLib in your system, there are examples to guide you in extending the existing functionality in the Framework. In addition, SMS will be glad to offer you guidance in implementing the extension that you want.

The Filleting Framework also supports a simplified filleting interface that removes most of the complexity for an application to add fillets to Brep models for most typical filleting cases. With a constructor call and two subroutine calls, an application can fillet any selection of edges in a model, with any combination of radius functions and cross section shapes.

This document contains everything you need to know to both utilize existing filleting functionality and to customize the existing functionality to your specific needs. Let us begin by introducing the basic concepts and terminology used in SMLib.

Terminology

The following terms will be used in this document:

Base Surfaces: The two original surfaces that will be joined by the fillet surface.

Fillet Surface: A new surface that is added to the model during the filleting operation.

Filleted Edge, or Edge to be Filleted: The edge of the original Brep to which the fillet is applied. This edge will disappear during filleting, and be replaced with fillet surfaces. One filleted edge is usually replaced by one fillet surface, but it can be more than one, or zero, depending on the radius of the fillet and the surrounding geometry and topology.

Rail Curves: The "long edges" of a fillet surface, which run along the two original faces that meet at the filleted edge. The rail curves are calculated first, before the fillet surface itself, and their intersections with other body topology, and with themselves, largely determine the overall filleting situation.

Fillet Corner: The filleting activity at the vertices of the filleted edges. In topology-based filleting, this will depend on the number of body edges incident at the vertex, which of those are to be filleted, their relative convexity, and how the rail curves intersect with the surrounding topology. New edges and faces can be created at fillet corners.

Cross Section: The shape of the cross section of the fillet surface. This is created after the rail curves are traced out, and is independent of the radius of the fillet. Fillet surfaces typically have a circular cross section for constant and variable radius rolling ball blends or linear cross sections for chamfers, but other shapes are possible.

Using the Filleting Framework

SMLib uses three concepts to specify how to place the rail curves, form the fillet surface shape, and connect the ends of the fillets to the rest of the Brep geometry. These concepts are the radius of the fillet, the fillet surface cross section shape, and the fillet corner generation methodology.

Fillet Solvers: Radius Definition

Surface-Surface filleting in TSNLib supports only constant-radius fillets. Variable-radius fillets are not supported due to the problem of defining a radius function without an existing edge to run the fillet along. Constant-width fillets could be added, but are not implemented at present. TSNlib does support differential offsets however: different radius values may be specified for the two surfaces, resulting in a non-circular blend.

Fillet Surface Generators: Cross Section Shapes

The following cross-section shapes are implemented in TSNlib. Examples of these are pictured in the Figures, below.

  • Circular cross section – Rolling-ball Fillet: produces a surface whose cross sections are either an exact rational circle, or an approximation of a circle (IwCircularCrossSectionFSG)
  • Linear cross section – Chamfer: produces a surface with a linear cross section. (IwLinearCrossSectionFSG)

Higher-continuity blend-curve sections have not currently been implemented in surface-surface filleting. TSNLib does support two other variations on the circular cross-section shapes:

  • Mirrored: the circular cross section can be "mirrored" across the line between the rail curves. This fillet surface is not tangent to the base surfaces.
  • Complementary: the complementary portion of the circular cross section can be used instead of the usual blend section. This fillet surface meets the base surfaces in a cusp.

Radius Definition: The radius definition controls where SMLib generates the shape of the rail curves on the two underlying surfaces of the edge being filleted. These rail curves are used both as the fillet surface bounds and to connect the fillet surface to the original underlying surfaces. Examples of SMLib radius definitions include Constant Radius Rolling Ball, Variable Radius Rolling Ball, and Constant Distance Between Rail Curves. The Radius Definition is implemented in the classes derived from IwFilletSolver.

Cross Section Type: The cross section type controls the cross section shape of the fillet surface between the two rail curves. Examples of SMLib cross section types include Circular Cross Section (Round), Linear Cross Section (Chamfer), and G1, G2 and G3 continuous blends. Other possible cross sections could include elliptical, parabolic, and cubic blends. Cross section shape is implemented in classes derived from IwFilletSurfaceGenerator. Note that the Radius Definition and the fillet surface Cross Section Type are independent and can be mixed and matched in any way.

Fillet Corner Generation: The fillet corner generation methodology specifies what happens at the ends of topological fillets. At vertices where only a single edge is filleted, this specifies how the end of the fillet joins the original Topology. For example, fillets may be extended to intersect adjacent surfaces, or there may be an extension of an adjacent surface to fill a gap. At vertices where more than one edge is filleted, this specifies how the fillets will join each other. For example, fillets may be joined by a Bevel or by a Corner Blend. Fillet corner generation is handled by classes derived from IwFilletCorner. It is generally done automatically by the system.

Fillet Corners

In TSNLib, this describes how the ends of the fillet surface are trimmed. The following options are available:

  • No trimming: The fillet surface extends beyond the boundaries of the base surfaces. It is defined on extensions of the base surfaces.
  • Minimal Trim: The fillet surface is trimmed to the first intersection with a boundary of one of the base surfaces. This is an isoparametric trim, running straight along the cross section.
  • Maximal Trim: The fillet surface is trimmed to the farther intersection with a boundary of one of the base surfaces. This is also an isoparametric trim.
  • Beveled Trim: The fillet surface is trimmed at the intersections with the boundaries of both base surfaces. This trim is a straight line in the parameter space of the fillet surface, connecting the two points of intersection.
  • Tangency Trim: The fillet surface is trimmed such that its boundaries are tangent to the boundaries of the base surfaces where they intersect.

Filleting Process

The filleting process for Surface-Surface is encapsulated in a single method of IwFilletExecutive called SurfaceSurfaceFillet(). The various parameters for the fillet are passed as arguments, and the method does all of the work required to create the fillet surface. The arguments passed are:

  • The two surfaces to be filleted.
  • The two radii for the surfaces (generally the same value).
  • A tolerance value for the offset-intersection.
  • Cross-section type: linear (chamfer), approximate circular, or exact (rational) circular.
  • Accuracy of approximate-circular cross section, expressed as a percent of radius.
  • How to trim the two ends of the fillet surface.
  • How to trim the base surfaces, along the fillet rail.
  • Whether to mirror a circular fillet.
  • Whether to use the complement of a circular fillet.

If these options are not sufficient for a specific fillet, the user could probably find what is desired by inspecting the source code of IwFilletExecutive::SurfaceSurfaceFillet(). In addition, SMLib will be happy to assist, perhaps adding optional arguments to the method, or adding functionality to surface-surface filleting.

Surface-Based Filleting

Surface Filleting

Perhaps the best way to show you how to use filleting is to walk you through the little filleting application that is in iwfillet_test.cpp. It takes two trimmed surfaces and computes the starting points for the surface fillet solver to use in tracing out the rail curves. It does a relatively good job of computing all possible fillets and finding good starting points by intersecting the offset surfaces to get seed points for the surface fillet solver. It tests offsets in both directions for each surface. In some cases there are multiple correct answers to this problem. To further refine this function, it should take offset directions (positive or negative relative to the surface normal) for each surface.

As you can see there are various tools that need to be put together to make to make surface filleting work. Here is an outline of the process:

  1. Create a fillet executive object
  2. For each surface/surface pair to fillet
    • Create surface/surface fillet solver
    • Create fillet cross section curve generator
    • Attach cross section generator to the fillet solver
    • Set the trim type of the fillet solver
    • Attach the fillet solver to the fillet executive
  3. Test the resulting Brep to see if any surfaces are produced

Surface Filleting Figures

Closed Fillet of Sphere and NURBS

Topology-Based Filleting

Topology-Based Filleting takes as input a single Brep model and modifies that representation to insert fillet topology and geometry. The algorithm is an edge- and vertex-filleting algorithm. You choose edges to fillet and attach Fillet Solvers to the edges. The Fillet Solvers tell the Filleting Framework how the filleting should be done. A default mechanism is used to either blend or bevel the joints between edge fillets that meet at a vertex. It is possible to fillet both solids and open shells.

Filleting Process

Applications can fillet any combination of manifold edges within solid, shell, and non-manifold Breps using either SMLib's simplified filleting interface or by calling filleting class methods directly. The simplified filleting interface should be sufficient for most common filleting operations.

The Simplified Filleting Interface process:

  1. Create an IwFilletExecutive object
  2. Call one of the versions of the Fillet Executive's SetFilletParameters() method, to specify the edges to be filleted, the radius and cross section information for each, and tolerances.
  3. Call the DoFillet() method of the Fillet Executive to perform the filleting operation

The Direct Fillet Class Interface process:

  1. Create an IwFilletExecutive object
  2. Specify the tolerances for the filleting
  3. For each edge to be filleted:
    • Create a Fillet Solver of the appropriate sublcass of IwFilletSolver to specify the radius
    • Create a Fillet Surface Generator of the appropriate subclass of IwFilletSurfaceGenerator to specify the cross section
    • Attach the Fillet Surface Generator to the Fillet Solver
    • Load the Fillet Solver into the Fillet Executive
  4. Call the CreateFilletCorners() method of the Fillet Executive to set up the fillet corners
  5. Call the DoFilleting() method of the Fillet Executive to perform the filleting operation

There are several examples of using both approaches in SMLib's Examples set, and in the source code.

Status of Topology-Based Filleting

In this section we discuss the current state of the three basics of SMLib filleting: radius definition, cross section shapes, and fillet corners. We also discuss the capabilities of SMLib in the case where the specified radius is too large to fit into a rounded inside corner, or interferes with other topology.

Fillet Solvers: Radius Definition

The first step of topology-based filleting is to define and associate a fillet solver to a manifold edge to specify how to construct the fillet's rail curves. Any manifold edge can be filleted whether that edge is in a manifold, a non-manifold, or shell Brep. Manifold edges are edges connected to exactly two adjacent faces. The three currently implemented fillet solver types that generate rail curves are:

  • Constant Radius Rolling Ball: a theoretical sphere of constant radius is rolled along between two surfaces. The points where the ball touches the surfaces defines the rail curves. In some cases the ball may actually roll onto an adjacent surface or onto adjacent edges. (See "Constant Radius" in the Figures below.)
  • Variable Radius Rolling Ball: a theoretical sphere that changes radius according to some predefined law is rolled between two surfaces to trace out rail curves. The law is typically defined relative to the edge being filleted. There are several ways to define the radius function. (See "Variable Radius" in the Figures below.) It is possible to have the fillet radius go to zero at one or both ends of the edge.
  • Constant Distance Between Rail Curves: a theoretical sphere that changes radius to keep the distance between two rail curves constant is rolled along to trace the rail curves. (See "Constant Distance" in the Figures below.)

Fillet Surface Generators: Cross Section Shapes

The second step of topology-based filleting is to define and associate a fillet surface generator to the edge being filleted. After the edge's fillet solver has been used to define the rail curves, the edge's fillet surface generator is used to specify the shape of the fillet surface that fills in the space between the two rail curves. The following fillet surface generation techniques have been implemented:

  • Circular cross section – Rolling-ball Fillet: produces a surface whose cross sections are either an exact rational circle, or an approximation of a circle (IwCircularCrossSectionFSG)
  • Linear cross section – Chamfer: produces a surface with a linear cross section. (IwLinearCrossSectionFSG)
  • Higher-continuity blend-curve section: produces a surface that meets the base surfaces with G1, G2, or G3 continuity across the rail curves. The actual fillet surface cross section shapes are created by skinning the rail curve boundary constraints. (IwBlendCurveCrossSectionFSG)

Cross section shapes: Circular (black); G1 continuity (red); G2 (green); G3 (blue)

Fillet Corners

The final step of topology-based filleting is to define and associate a fillet corner object (IwFilletCorner) to every vertex attached to an edge being filleted. This controls how the edge fillets get connected to pre-existing and other newly created fillet geometry. SMLib automatically selects the type of fillet corner object to associate with each vertex depending on the number of edges connected to the vertex and the relative convexities of those edges.

SMLib uses the following notation: a fillet corner where N total edges are incident, and M of them are to be filleted, is denoted an NxM corner. The following fillet corner cases have been implemented:

  • Nx1 Closed Corner (IwFilletNx1ClosedCorner) Denotes a case where the edge to be filleted is a closed edge (i.e. top edge of a cylinder) that attaches to the vertex two times. It can have either zero or one additional edges coming into the vertex. If the edge meets itself tangentially (as with a cylinder), the fillet surface will be closed and joined without any trimming. In the non-tangent case (e.g., teardrop shape, extruded), the fillet surface is split in the middle, and the pieces are intersected where they meet.
  • 3x1 Corner (IwFilletNx1Corner) Denotes a case where there are 3 edges adjacent to a vertex and one of them needs to be filleted. There are three cases here, depending on the relative convexity of the three edges, and which edge is to be filleted.
    • If all three edges have the same convexity ("univex"), the rail curves intersect nicely with the adjacent Brep edges, and the fillet surface intersects with the untrimmed end face. The end face will be trimmed back to the fillet surface.
    • If one of the three edges has the opposite convexity to the other two ("mixed convexity"), and the filleted edge is the one with opposite convexity, then the rail curves will still intersect nicely with the adjacent Brep edges, but the end face must be extended to be intersected with the fillet surface. The end face will be extended out to the fillet surface.
    • In the mixed-convexity case where the filleted edge is one of the two with the same convexity, then one of the rail curves will not intersect with the adjacent Brep edge; that edge must be extended to intersect the rail curve. Again, the end face must be extended to be intersected with the fillet surface, and will be extended out to the fillet surface.
  • 3x2 Corner (IwFilletNx2Corner) Denotes a case where three edges are adjacent to a vertex and two of them are to be filleted.
    • If the filleted edges are tangent at the vertex and have the same radius and cross section, the fillet surfaces will be joined tangentially without any trimming or intersection. (If they have a different radius or cross section, they cannot be filleted.)
    • At a univex corner (all edges with same convexity), adjacent rail curves will intersect each other, the other two rail curves will intersect body edges, and the fillet surfaces will intersect each other. In this case the fillet surfaces will be intersected and a bevel created.
    • At a mixed-convexity corner where the two filleted edges have opposite convexities, adjacent rail curves will intersect, the fillet surfaces will intersect, and a corner patch will be created.
    • At a mixed-convexity corner where the two filleted edges have the same convexities, the rail curves that are in the same face must be extended and/or blended to meet. A corner patch will also be created.
  • NxN Same-Convexity Corner (IwFilletConvexNxNCorner) Denotes a case where all edges adjacent to a vertex are to be filleted and all of the rail curves intersect. This case will create a 3, 4 or N-sided tangential corner blend between all of the adjacent fillet surfaces. In some cases the corner will be an analytical surface (Sphere). In the N-sided corner case, when N > 3, multiple four-sided patches will be created to fill the corner. It is also possible to utilize this corner to produce a beveled case instead of a blend by setting the SetBevelFlag to TRUE for the corner.
  • NxN Mixed-Convexity Corner (IwFilletConcaveNxNCorner) Denotes a case where all edges adjacent to a vertex are to be filleted and not all of the rail curves intersect. In this case a blend curve will be made between the rail curves on the given face. The tangential corner blend will then be made between adjacent fillet surfaces and the blend curves. In some cases the corner will be an analytical surface (Torus).
  • Open Shell Corner (IwFilletOpenCorner) Denotes a case where there is no ending face. Two of the edges are lamina edges and the edge to be filleted is the only manifold edge at the vertex. The fillet will be trimmed by a line in the parameter space of the fillet surface between the vertices.
  • Tangent Surface Corner A corner where the faces adjacent to the filleted edge become tangent. The fillet surface becomes degenerate where the adjacent faces become tangent. See the Figure entitled "Disappearing Fillets with Tangent Faces".
  • Degenerate Rail Fillets The case where one of the rail curves degenerates to a point. An example of this would be filleting the top of a cylinder with a radius the same as the cylinder.

Examples of 3xN fillets at a mixed-convexity corner. Note the extensions of body faces and edges in some cases. A 3x3 (NxN) case is pictured for completeness.

Example: Chamfer, and NxN corners, same- and mixed-convexity cases.

Large Radius Filleting

Standard filleting algorithms assume that the size of the fillet is relatively small compared to the size of the features of the object being filleting. For example, they assume that the radius of the fillet is smaller than the radius of curvature of the surfaces of the edge being filleted. They also assume that the rail curves of a fillet surface do not intersect interior features of the face, and that they do intersect the adjacent topological edges at each vertex. These assumptions make the implementation of filleting easier, but they do not cover an adequate number of cases required by real-world engineering problems. Therefore SMLib has algorithms to handle large-radius situations. SMLib's large-radius capabilities in the Filleting Framework include:

  1. Self-Intersecting Rail Curves: If the rail curves of a fillet self-intersect, this generally means that the radius of the fillet is larger than that of one of the original base surfaces. In this case, the fillet is trimmed back from intersection and a blend surface is inserted. See the results in the Figure "Self-Intersecting Rail Curves", below.
  2. Small Adjacent Edges: If the rail curve fails to intersect the adjacent edge at a vertex of the edge to be filleted, SMLib utilizes classification to determine the intersection.
  3. Rail Curves Intersecting Features: this occurs when a fillet rail curve intersects any edge of the original body other than the one that is adjacent at the vertex. There are several classes of this problem, which are pictured below.
    • Tangent Surface Rollover: fillet rolls onto another surface which is tangent to base surface.
    • Cliff Rollover: fillet rolls off the edge of a cliff and gets trimmed by an extension of the cliff face.
    • Positive Fillet/Positive Feature: a positive fillet rolls into positive object like a Boss – fillet is created and flows around the boss.
    • Negative Fillet/Negative Feature: fillet cuts away part of an interior negative feature like a hole in a box near the edge.
    • Negative Fillet/Positive Feature Interaction: a negative fillet runs into a positive feature and the positive feature gets extended down to meet the fillet surface.
    • Positive Fillet/Negative Feature Interaction: fillet rolls over a hole the hole surface gets extended and the fillet gets trimmed just like the Cliff Rollover.
    • Fillet/Fillet Intersection: two fillets applied during the same operation intersect each other – intersection computed and trimming applied to produce a valid solid.

Topology-Based Filleting Figures

Constant Radius

Variable Radius

Self Intersecting Rail Curves

Tangent Surface Rollover

Negative Fillet/Positive Feature

Positive Fillet/Positive Feature

Filleting used in Offsetting: Original Solid

Offset Solid/Negative Offset

Constant Distance

Linear Cross section

Cliff Rollover

Negative Fillet/Negative Feature

Positive Fillet/Negative Feature

Fillet/Fillet Intersections

Offset Solid/Positive Offset

Shelled Solid

More Filleting Figures

Submitting a Filleting Bug

This could be a difficult task if you have added substantial functionality using the customization features. In that case you may have to ship us some software to test along with model geometry.

For the current time and utilization of trimmed surface filleting the best thing to do is to dump out the two original Breps and send us the information used to generate the surface fillet (radius, surface orientations, tolerances, guess points, etc.). Optionally if you have created a high level function like the example given above, you should also send us that to utilize with the corresponding arguments. If the arguments are complex, it might be easier to write them to a Fillet Definition File.

Non-Manifold Sweep (SMLib only)

SMLib has the ability to sweep faces, edges, and vertices to the same dimension or to a higher dimension.

  • The sweep of a face to the same dimension produces a copy of the face at the terminal position.
  • The sweep of a face to a higher dimension produces a solid volume between the original face and the terminal position face.
  • The sweep of an edge to the same dimension produces a copy of the edge at the terminal position.<
  • The sweep of an edge to a higher dimension produces a face between the original edge and the terminal position edge.
  • The sweep of a vertex to the same dimension produces a copy of the vertex at the terminal position.
  • The sweep of a vertex to a higher dimension produces an edge between the original vertex and the terminal position vertex.

The Non-Manifold Sweeping is designed as an Object-Oriented Framework. Application developers can easily create new geometric sweeps by implementing a new subclass that produces the geometry for the sweep operation while the Framework builds the topology for the sweep.

Shell Offset (SMLib only)

Shell offset is an advanced operation that is only available in SMLib (not TSNLib). One of the more difficult operations in solid modeling is the process of offsetting a solid object by a specified distance to create the new offset shell. As the offset distance increases, there may be massive changes in the topology of the object. By using the Merge operation, the SMLib shell offset process is able to correctly process these massive changes in the topology. Access to this functionality is available via the: IwOffsetExecutive, IwOffsetGeometry - two classes that support offsetting and shelling of solids. See usage examples in iwoffset_test.cpp.

Offset of a Solid

The implementation of the Solid Offsetting Algorithm is relatively simple given the powerful tools available within SMLib. Offsetting utilizes facilities from the SMLib Merge and SMLib Filleting to do most of the difficult work. This unique approach also keeps special case handling to a minimum. The entire algorithm was implemented with two new classes (IwOffsetExecutive and IwOffsetGeometryCreation) and less than 20 new methods of moderate size. The basic approach is as follows:

  1. Generate faces representing the offsets of existing faces of a solid.
  2. Insert self-intersection edges of offset faces.
  3. Generate faces representing the offsets of edges of the solid. This utilizes filleting tools to generate circular blends between corresponding edges of offset faces. In cases where the offset faces have self-intersections, a single edge may require several blends and corner patches.
  4. Generate offset faces for vertices. We use a triangular trimmed piece of a sphere for simple cases and a complete sphere for some more difficult cases.
  5. Utilize the “Partial Merge” to put all of the offset faces together. Note that we utilize existing topological relationships eliminate unnecessary surface/surface intersections and speed up the process several orders of magnitude over a straight “Merge”.
  6. After the “Partial Merge” we need to identify which regions (i.e. volumes) to keep. In most cases this is just the shell bounding the outer region. However, it is possible to create void regions within a solid.
  7. Call “Make Manifold” with the selected regions to remove all topology elements not bounding the given regions.

This algorithm lends itself to the creation of very large offsets where the offsets of features of the solid intersect each other. This type of offset is ideal for various NC operations. Right now the algorithm works quite well with analytical parts that don’t have freeform fillets, blends, and corner patches. The proper handling of these things requires identification and special handling. The best way to do it is to regenerate the fillets, blends, and corner patches directly from the offset surfaces instead of trying to offset the surface directly. There are also some problems with self-intersecting surfaces that are basically impossible to solve in a reasonable way. For solids with these type of surfaces, it is only possible to create offsets of relatively small radius.

Shelling of a Solid

The implementation of a shelling algorithm required only a few slight modifications to the Offset Solid algorithm:

  1. We allow the specification of a zero offset distance for a face (currently required to be planar).
  2. If an edge belongs to the shelling face, we connect the corresponding offsets using a plane instead of a circular blend.
  3. If a vertex belongs to a shelled face. We blend it using a piece of a plane instead of a sphere.

Once the shelled solid is created it can be used in a number of ways to do very powerful operations.

  1. Subtraction of original solid or smaller radius offset to create a shelled solid.
  2. Ribs can be easily created by subtracting thin boxes from inner solid prior to the final difference operation.

Polygon Modeling

In addition to using NURBS and analytic representations for geometric models, SMLib also has polygonal data structures, polygonal construction and modeling tools, polygonal refinement tools, and conversion functions. Advanced functions include polygon booleans, decimation, hole filling, ray firing, etc.
In addition to constructing polygonal breps from scratch, customers can easily create polygonal models by importing STL files or by using one of several tessellators to convert NURBS models. SMLib features topology-based tessellation to produce "Crack-Free" polygonal meshes.
Because of our attention to both NURBS and polygons, hybrid CAD systems will find SMlib particularly useful.

Advanced polygon refinement tools can be found in PolyMLib. Refer to PolyMLib manual page.

Import Polygonal Model

Refer to IwPolyBrep::ReadFromSTLFile

ReadFromSTLFile exists in SMLib. HW translators are not required for importing STIL files.

Tessellation

Refer to example
Refer to IwBrep::CreatePolyBrep

SmLib contain three tessellators each designed for different purposes.

Tessellation is the process of converting smooth curves, surfaces and faces into linear or polygonal segments.
This has many uses, primarily in graphics, where curves are represented by a large number of small line segments, and surfaces get converted into planar triangles, which are then passed to a graphics engine for display.

The criteria for tessellation may be selected by the user. In general they relate to curvature, (the flatter the curve, the less line segments are needed), and absolute size (very tiny curves would require less lines than very large curves of the same shape).

  • Tessellation Criteria
    • Chord Height Tolerance
    • Angular Tolerance
    • Maximum Aspect Ratio
    • Maximum Polygon Edge Length
    • Minimum UV Polygon Edge Length
  • Dynamic tessellation
    • Object Oriented Curve and Surface Subdivision Classes allow the user to modify the tessellation criteria during the tessellation process. For example, in a graphical application the user could adjust tessellation criteria based on distance from an "Eye Point" of the geometry being subdivided. This customizability feature enables the Tessellation Library to easily be adapted to different applications. See the view dependent tessellation example in the "Tessellation Criteria" image.
  • Tessellation Output:
    • As a topological data structure which contains a connected set of polygons with 3D points, UV points and surface normals.
    • User Call Backs with 3D points and surface normals. You will need to subclass the IwPolygonOutputCallback class and implement the OutputPolygon method on to implement the callback.
  • "Crack-free" Tessellation:
    • SMLib offers a significant upgrade to NLib and OpenGL tessellation, in that it ensures that there are no cracks between tessellations of faces that share a common edge. This ensures that the shaded image will not contain ugly black cracks along the joins in the shaded image. This process is complex, and may account for some lack of performance, but the results show a significant improvement over competing products.

Polygon Booleans

Boolean operations like Union, Difference, Intersection can be performed on polygonal breps (IwPolyBrep).

Boolean operations are contained in the class IwPolyMerge

Decimation

Decimation is the process of removing polygons to achieve a significant data reduction with an acceptable loss of accuracy.

This allows very large models to be reduced to a size where the performance of some of these complex operations can be run in real-time.

Two decimation algorithms are available:

  • a vertex removal decimation algorithm that utilizes accumulated error measurement
  • an edge squeezing decimation algorithm that utilizes a fast quadric error distance to choose optimal points.

Ray Firing

The process of ray firing is useful for selection, position dependent projection, among other applications. In most cases it is very desirable for ray firing to be extremely fast so it can be performed as the mouse is dragged across an object, for example. For this reason, ray firing is generally divided into an initial setup and a faster execution that takes advantage of the knowledge that the next iteration is close to the last.

ui_test - SMLib Viewer and Testing Environment

ui_test is a graphics viewer valuable for testing and debugging SMLib functionality. It is relatively straightforward to construct simple test programs that are used to validate customer data and reproduce potential bugs. Supplied examples can also be executed to visualize and evaluate results. This is only a simple viewer, and not a modeling tool. There is no picking and no general menu for the construction of geometric objects.

In ui_test, there are two possible drawing mathods to display objects during the execution of a test program. One is a temporary draw (pBrep->Draw()), where it is possible to control color, line thickness and many other display parameters with the intent of clarifying the changing geometric situation during an operation. Secondly, ui_test can also display the results of an operation by adding the results to the display list (UserTest::Draw( pBrep)). The display list is kept in it's original form, so that it can be repeated, only this time as a hidden-curve display, or a shaded display.

To enable HW translators:

  1. Place the distriuted HWLibs folder in the same folder with ui_test.
  2. After compiling your HW projects (core, step, for example) the resulting dlls should be present in Winlibs/Winlibs64.
  3. To link the dlls with ui_test, open the ui_test project properties C/C++ tab and add ../HWLibs/inc to your list of include directories.
  4. Add ../Winlibs64/Debug/hw_smlib_core.dll and each trandlator dll to the list of input libraires in Linker->Input.
  5. Set IW_HWLIBS_STEP and/or related definitions in user_test.h
  6. Finally in the NMTLIb project, set IW_NO_HWLIBS in iwos_config.h

To run a test program or example:

  1. Add the source .cpp file to the ui_test project
  2. Declare the main function in Test.cpp. Make sure the function is IW_EXPORT
  3. Call the function in the switch statement in Test.cpp
  4. The iBugNumber is controlled at runtime in the Test Dialog box.
  5. Click the left most icon called "Run Test Program" to execute.

ui_test menu items:

  • View Options
  • Display Options
  • File Import or Export (Read/Write)
  • Test Programs, Demos, and User application
  • Modify Data for healing
  • Boolean operations
  • Parameters (inactive)
  • Debugg
  • Tests, Specify user test number, Regression tests
  • Toggle graphics on/off
  • Demos
  • VSLib (inactive)
  • About the Version and Build Date of the Source Code

ui_test toolbar features:

  • Run User Test program
  • Get previously set View
  • Center View
  • Front View
  • Top View
  • Side View
  • Reset View
  • Rotate
  • Zoom
  • Pan
  • Display Outline
  • Display Normals
  • Display Crosshatch
  • Display Knots
  • Display Control Polygon
  • Display Tessellation
  • Display Shade
  • Display Hidden Curve
  • Display Coordinate Axes
  • Display Bounding Box
  • Erase
  • Dump
  • Continue
  • Exit from debug mode