#########################
Converting B-Rep to NURBS
#########################

HOOPS Exchange provides a rich set of boundary representation (B-rep) features.
However, because some of these features may not be compatible with the requirements of your environment, HOOPS Exchange provides a facility called *B-rep adaptation* that converts any boundary representation model to *Non-uniform rational basis spline* (NURBS), as an alternative designed to help you manipulate more commonly used geometric representations.

The conversion operation can be customized using a fine-grained configuration structure:

.. code-block:: c

   typedef struct
   {
     A3DUns16       m_usStructSize;
     A3DBool        m_bUseSameParam;
     A3DDouble      m_dTol;
     A3DBool        m_bDeleteCrossingUV;
     A3DBool        m_bSplitFaces;
     A3DBool        m_bSplitClosedFaces;
     A3DBool        m_bForceComputeUV;
     A3DBool        m_bAllowUVCrossingSeams;
     A3DBool        m_bForceCompute3D;
     A3DUns32       m_uiAcceptableSurfacesSize;
     A3DUns32*      m_puiAcceptableSurfaces;
     A3DUns32       m_uiAcceptableCurvesSize;
     A3DUns32*      m_puiAcceptableCurves;
     A3DBool        m_bContinueOnError;
     A3DBool        m_bClampTolerantUVCurvesInsideUVDomain;
     A3DBool        m_bForceDuplicateGeometries;
   } A3DCopyAndAdaptBrepModelData;

To use this data structure, initialize an instance just like any other HOOPS Exchange structure:

.. code-block:: c

   A3DCopyAndAdaptBrepModelData data;
   A3D_INITIALIZE_DATA(A3DCopyAndAdaptBrepModelData, data);

   // Customize the NURBS conversion

***********************
Filtering the Converter
***********************

By default, all geometric entities are converted into NURBS. However, you may want to customize this behavior and allow specific geometry types to simply pass through the process without being converted.
These geometric entities are defined as *acceptable*.

You can define your own list of acceptable curves and surface types with :member:`A3DCopyAndAdaptBrepModelData.m_puiAcceptableSurfaces` and
:struct:`A3DCopyAndAdaptBrepModelData` respectively. Each array expects values from :struct:`A3DEEntityType`.

.. code-block:: c

   // Acceptable surfaces
   A3DUns32 surfaces[] = {
     kA3DTypeSurfCone,
     kA3DTypeSurfCylinder,
     kA3DTypeSurfPlane,
     kA3DTypeSurfSphere
   };

   // Acceptable curves
   A3DUns32 curves[] = {
     kA3DTypeCrvCircle,
     kA3DTypeCrvLine
   };

   data.m_uiAcceptableSurfacesSize = 4;
   data.m_puiAcceptableSurfaces    = surfaces;
   data.m_uiAcceptableCurvesSize   = 2;
   data.m_puiAcceptableCurves      = curves;

This example marks the following entity types as *acceptable*:

- Planes: `kA3DTypeSurfPlane`
- Cylinders: `kA3DTypeSurfCylinder`
- Cones: `kA3DTypeSurfCone`
- Spheres: `kA3DTypeSurfSphere`
- Lines: `kA3DTypeCrvLine`
- Circles: `kA3DTypeCrvCircle`

While all other geometry will be converted to NURBS, these geometry types will pass through the conversion and remain unchanged.

************************************************
Matching Parameterization of Curves and Surfaces
************************************************

Geometric constructions based on B-rep and NURBS are said to be "parameterized".
This simply means that their resulting geometry is obtained from the input of parameters into mathematical models.

Converting from original geometry to NURBS implies changing from one mathematical model to another.
As a consequence of these parameterization changes, the same input parameter used in the original CAD model geometry won't provide the same result in the adapted (NURBS) geometry.

You may still want to enforce some level of parameterization expectations after conversion so that parameterization requirements will be respected inside a specified margin of error.

.. figure:: /_assets/images/adapt_brep_same_param.png

   Matching parameterization of curves and surfaces

The example above shows two examples where the option is activated (right) or not (left).
As you see, this operation may imply the generation of additional control points.

To enforce parameterization requirements in the resulting NURBS, set the `m_bUseSameParam` field to `A3D_TRUE` along with a specific tolerance value with `m_dTol`:

.. code-block:: c

   data.m_bUseSameParam = A3D_TRUE;
   data.m_dTol = 1.e-3;

************************
Tweaking Closed Surfaces
************************

Periodic faces are closed surfaces that preserve their continuity between the end and the start of the surface.
A common example is a cylindrical surface:

.. figure:: /_assets/images/adapt_brep_cylinder.png

   Splitting periodic faces

When converting to NURBS, a periodic surface can be modified by splitting periodic surface or by splitting closed faces.

Splitting Periodic Faces
========================

A periodic face can be split in order to form a seam, this can be achieved using the `m_bSplitFaces` option.

.. code-block:: c

   data.m_bSplitFaces = A3D_TRUE;

In the below picture, a seam is visible, marking the split of the face.

.. figure:: /_assets/images/adapt_brep_split_faces.png

   Splitting periodic faces

This option can lead to the creation of new faces.
They will then share their surface and 3D curves.

Splitting Closed Faces
======================

Closed faces can be cut into new faces that will share the same surfaces and 3D curves.
If you convert to a format that does not support closed faces, this option will prove useful.

.. figure:: /_assets/images/adapt_brep_split_closed_faces.png

   Splitting closed faces

.. code-block:: c

   data.m_bSplitClosedFaces = A3D_TRUE;

********************
Duplicating Geometry
********************

Sometimes several topological entities are associated to the same geometry. For example, two faces can be built on the same surface.
If needed, exclusivity of the association can be enforced by duplicating the geometry.

.. code-block:: c

   data.m_bForceDuplicateGeometries = A3D_TRUE;

As an important note, the fact that the source model does not initially provide such sharing does not mean your resulting NURBS conversion won't contain shared geometry as well.
Indeed, calling either `m_bSplitFaces` or `m_bSplitClosedFaces` can create faces with shared surfaces and 3D curves.
Hence, the duplication operation will be performed *after* the splitting of faces.

*******************
Manipulating Curves
*******************

Various operations can be performed on curves when converting to NURBS.
HOOPS Exchange makes a distinction between two major sorts of parametric curves: *3D curves* and *UV curves*. They differ in the parametric space used. A 3D curve is defined in the three-dimensional space of the geometry while the UV curve is defined inside the 2D space represented by a surface.

Forcing Either UV or 3D Curves
==============================

Your geometry can contain any set of 3D curves and UV curves it needs. According to your requirements you may prefer having either one type of curve or the other.

For that purpose we provide you with two options which will generate UV or 3D curves for you.
When `m_bForceCompute3D` is set to `A3D_TRUE`, all UV curves will lead to the computation of a new 3D curve. Already existing 3D curves won't be affected.

.. code-block:: c

   data.m_bForceCompute3D = A3D_TRUE;

With the same idea you can use `m_bForceComputeUV` to activate the computation of UV curves from existing 3D curves.

.. code-block:: c

   data.m_bForceComputeUV = A3D_TRUE;

In the image below all the curve are available as UV curves thanks to `m_bForceComputeUV`. The image on the right is a visualization of the UV curves.

.. figure:: /_assets/images/adapt_brep_allow_uv_crossing_false.png

   Generating UV curves from 3D curves

If needed for the computation, this operation may implicitly split the surface as if `m_bSplitFaces` was set. This is the case for the example above.

Tweaking UV Curve Generation
============================

With `m_bForceComputeUV`, the generated UV curves will be closed. If you want to prevent that, use `m_bAllowUVCrossingSeams`.
With this option enabled, the curves cannot be closed and are allowed to lay outside of the parametric domain of its surface.

.. figure:: /_assets/images/adapt_brep_allow_uv_crossing_true.png

   Generating UV curves from 3D curves

.. code-block:: c

   data.m_bAllowUVCrossingSeams = A3D_TRUE;

Please note that this option is mutually exclusive with `m_bSplitFaces` and `m_bSplitClosedFaces`.
If any of these parameters is `A3D_TRUE`, `m_bAllowUVCrossingSeams` will be considered as `A3D_FALSE`.

Deleting Crossing UV Curves
===========================

Sometimes having UV curves that cross the seams of a periodic surface is not desirable. With `m_bDeleteCrossingUV` set to `A3D_TRUE` such curves will be converted to 3D curves.

.. figure:: /_assets/images/adapt_brep_delete_crossing_uv.png

   Deleting crossing UV curves

.. code-block:: c

   data.m_bDeleteCrossingUV = A3D_TRUE;

In the geometry from the previous example, a UV curve was crossing a seam. It has been converted to 3D curve.

Clamping UV Curves
==================

By default UV curves are allowed to stray outside the UV domain.
By setting `m_bClampTolerantUVCurvesInsideUVDomain` to `A3D_TRUE`, UV curves will be clamped to the UV domain as long as the 3D edge tolerance is respected.

.. code-block:: c

   data.m_bClampTolerantUVCurvesInsideUVDomain = A3D_TRUE;

# Code example

This example uses some of the options above to prevent any closed surfaces during conversion:

.. code-block:: c

   void no_closed_surfaces(A3DAsmModelFile* in_pModelFile)
   {
       A3DCopyAndAdaptBrepModelData sConfig;
       A3D_INITIALIZE_DATA(A3DCopyAndAdaptBrepModelData, sConfig);

       sConfig.m_bSplitFaces               = A3D_TRUE;
       sConfig.m_bSplitClosedFaces         = A3D_TRUE;
       sConfig.m_bForceComputeUV           = A3D_TRUE;
       sConfig.m_bForceDuplicateGeometries = A3D_TRUE;

       A3DAdaptAndReplaceAllBrepInModelFile(
         in_pModelFile,
         &sConfig
       );
   }

:func:`A3DAdaptAndReplaceAllBrepInModelFile` takes two parameters, which are the `A3DAsmModelFile` instance to convert and the settings.

This function makes the conversion in-place, which means that the original Model File will be destroyed. If you want to leave the original file unchanged and create a converted copy instead, use \ref `A3DCopyAndAdaptBrepModel()` function instead.

***************
Managing Errors
***************

Converting your geometry to NURBS is a long process and errors can occur at any step of the operation.
By default the first error encountered when converting will be returned by the function, thus stopping the conversion.

You can force the conversion to resume even if errors occur:

.. code-block:: c

   m_data.m_bContinueOnError = A3D_TRUE;

To let you maintain control on the error flow, the conversion functions come with an advanced mode where all errors encountered while converting are stored for being returned.
This is only available if `m_bContinueOnError` is set:

.. code-block:: c

   void no_closed_surfaces(A3DEntity* in_pSourceEntity, A3DEntity** out_pDestEntity)
   {
       A3DCopyAndAdaptBrepModelData sConfig;
       A3D_INITIALIZE_DATA(A3DCopyAndAdaptBrepModelData, sConfig);

       sConfig.m_bContinueOnError          = A3D_TRUE;

       A3DUns32  uiNErrors = 0;
       A3DInt32* piErrors = NULL;

       A3DCopyAndAdaptBrepModelAdvanced(
         in_pSourceEntity,
         &sConfig,
         &out_pDestEntity,
         &uiNErrors, &piErrors
       );

       for(A3DUns32 e = 0;  e < uiNErrors ; ++e)
       {
           printf("Error Code: %x\n", piErrors[e]);
       }
   }

The in-place conversion also provides this error checking mechanism with :func:`A3DAdaptAndReplaceAllBrepInModelFileAdvanced`.

