Special Evaluation Classes

The following section describes classes in the AcGe library with which you can evaluate points on curves and surfaces. These classes are AcGePointOnCurve2d, AcGePointOnCurve3d, and AcGePointOnSurface.

A parametriccurve is defined by a continuous function that maps some interval of the real line (possibly the entire real line) into either 2D or 3D space, depending on whether the curve is 2D or 3D. A parametric surface is defined by a continuous function that maps some connected subset of the uv plane (possibly the entire uv plane) into 3D space. The point on a parametric curve or surface that corresponds to a particular parameter value can be obtained by evaluating the function at that parameter value. For curves the parameter value is a scalar, and for surfaces the parameter value is a 2D point.

Many geometric modeling systems that support parametric curves and surfaces contain evaluator functions for computing points on parametric curves and surfaces. These evaluators typically have input arguments for the parameter value at which the curve or surface is to be evaluated and for the number of derivatives that are to be returned. They also have output arguments for the evaluated point and an array of vectors for the derivatives. Sometimes evaluators contain additional parameters for requesting and returning the normal vector at a particular parameter value.

In addition to such evaluator functions (methods called evalPoint()) for every curve and surface class, the AcGe library contains the evaluator classes AcGePointOnCurve2d, AcGePointOnCurve3d, and AcGePointOnSurface, through which the curve and surface evaluators can be accessed. These classes serve two main purposes:

The public interface to the AcGePointOnCurve2d, AcGePointOnCurve3d, and AcGePointOnSurface classes is identical except for minor differences in the member function names. For example, the AcGePointOnCurve3d class contains the function deriv(), which returns the derivative vector, while the AcGePointOnSurface class contains two functions, uDeriv() and vDeriv(), to return the uand vpartial derivatives. The remainder of this section describes how to use the AcGePointOnSurface class, but this description applies to the AcGePointOnCurve2d and AcGePointOnCurve3d classes as well, because their interface is very similar to that of the AcGePointOnSurface class.

To use the AcGePointOnSurface class to evaluate points and derivatives, you must specify which surface is to be evaluated and the parameter value at which the evaluation is to be done. The following two member functions set the surface and parameter value of an AcGePointOnSurface object:

AcGePointOnSurface&  setSurface (const AcGeSurface&);
 
AcGePointOnSurface&  setParameter (const AcGePoint2d&); 

After you call setSurface(), all subsequent evaluations are performed on that surface until you call setSurface() again for a different surface. Similarly, after you call setParameter(), all subsequent query functions return information pertaining to that parameter value until setParameter() is called again for a different parameter value. For example, consider if srf is an AcGeSurface object, param is an AcGePoint2d object, and pntOnSrf is an AcGePointOnSurface object, then the following code evaluates the point and first derivatives on srf at the parameter value param:

pntOnSrf.setSurface (srf);
pntOnSrf.setParameter (param);
AcGePoint3d    pnt3d = pntOnSrf.point();
AcGeVector3d   uFirstPartial = pntOnSrf.uDeriv(1),
               vFirstPartial = pntOnSrf.vDeriv(1);

In practice, you rarely, if ever, call setSurface() or setParameter() directly. Instead you call these functions indirectly through member functions of the AcGePointOnSurface class. For example, the point() function, which returns the model space point at a particular parameter value, has three different signatures:

AcGePoint3d    point ()  const;
 
AcGePoint3d    point (const AcGePoint2d& param);
 
AcGePoint3d    point (
    const AcGeSurface& srf, 
    const AcGePoint2d& param);

The first signature takes no parameters and assumes that the surface and parameter value have already been set by previous calls to setSurface() and setParameter(). The second signature assumes that the surface has already been set by a previous call to setSurface(), but it calls setParameter(param) to set the parameter value before evaluating. The third signature calls setSurface(srf) and setParameter(param) to set the surface and parameter value before evaluating. Only the first member function is declared as const; the other two modify the object by setting the surface and/or parameter value. The direct calls to setSurface() and setParameter() can now be removed from the previous code as follows:

AcGePoint3d   pnt3d = pntOnSrf.point ( srf, param );
AcGeVector3d  uFirstPartial = pntOnSrf.uDeriv(1), 
              vFirstPartial = pntOnSrf.vDeriv(1);

The first statement causes setSurface(srf) and setParameter(param) to be called before the evaluation is performed. Subsequent evaluations are performed on the same surface and at the same parameter value until setSurface() or setParameter() is called again, either directly or indirectly. Therefore, the second statement does not need to respecify either the srf or param arguments. All the evaluation functions of the AcGePointOnSurface class follow the same pattern of having three different signatures:

AcGeVector3d   uDeriv (int order)  const;
 
AcGeVector3d   uDeriv (int order, const AcGePoint2d& param);
 
AcGeVector3d   uDeriv (
    int order, 
    const AcGeSurface& srf,
    const AcGePoint2d& param);
 
AcGeVector3d   vDeriv (int order)  const;
 
AcGeVector3d   vDeriv (int order, const AcGePoint2d& param);
 
AcGeVector3d   vDeriv (
    int order, 
    const AcGeSurface& srf,
    const AcGePoint2d& param);
 
AcGeVector3d   mixedPartial ()  const;
 
AcGeVector3d   mixedPartial (const AcGePoint2d& param);
 
AcGeVector3d   mixedPartial (
    const AcGeSurface& srf, 
    const AcGePoint2d& param);
 
AcGeVector3d   normal ()  const;
 
AcGeVector3d   normal (const AcGePoint2d& param);
 
AcGeVector3d   normal (
    const AcGeSurface& srf, 
    const AcGePoint2d& param);

Similarly, there are three constructors for the AcGePointOnSurface class:

AcGePointOnSurface ();
 
AcGePointOnSurface (const AcGeSurface& srf);
 
AcGePointOnSurface (
    const AcGeSurface& srf, 
    const AcGePoint2d& param);

When using the first constructor, you do not specify a surface or parameter value. Presumably, you set the surface and parameter value before the first evaluation. To prevent the construction of an uninitialized object, the first constructor sets the surface to AcGePlane::kXYPlane, which is just the XY plane, and sets the parameter value to the default value (0,0). The second constructor calls setSurface(srf) and sets the parameter value to the default value of (0,0). The third constructor calls setSurface(srf) and setParameter(param). The second constructor is especially useful in functions in which a surface is passed in as an argument:

void func (const AcGeSurface& srf)
{
    AcGePointOnSurface  	pntOnSrf (srf);
    .
    .
    .
}

The constructor calls setSurface(srf) so that all subsequent evaluations in this function are performed on srf.

Because the AcGePointOnSurface class encapsulates both the parametric and model space information about a particular point on a surface, it is useful for functions that need to return information about one or more distinct points on a surface. For instance, the AcGeSurface class contains the member function:

void 
getClosestPointTo (
    const AcGePoint3d& pnt3d, 
    AcGePointOnSurface& closestPoint, 
    const AcGeTol& tol = AcGeContext::gTol) const;

This function returns the closest point on the surface to the input point pnt3d. The closest point is returned as an AcGePointOnSurface object, which contains the parameter value, model space point, and other information about that particular point on the surface. All functions in the AcGe library that return an AcGePointOnSurface object as an output argument (nonconst) have already called setSurface() and setParameter() for that argument. Therefore, after calling such a function, you do not need to reset the surface or parameter value. For example, the following code obtains the parameter value, model space point, and first derivatives of the closest point on the surface srf to the point pnt3d:

// Compute the closest point on the surface to pnt3d.
AcGePointOnSurface    closestPoint;
srf.getClosestPointTo (pnt3d, closestPoint);
// Get parameter value, model space point, and first derivative
// vectors of closest point.
AcGePoint2d   param = closestPoint.parameter();
AcGePoint3d   pnt3d = closestPoint.point();
AcGeVector3d  uFirstPartial = closestPoint.uDeriv(1), 
              vFirstPartial = closestPoint.vDeriv(1);

None of the calls to point(), uDeriv(), or vDeriv() needs to specify the surface or parameter value, because they were already set by getClosestPointTo(). In general, setSurface() and setParameter() should not be called unless you explicitly intend to change the surface or parameter value of the AcGePointOnSurface object. For example, the first statement in the following code indirectly calls setSurface() and setParameter(). The second and third statements are inefficient because they make unnecessary calls to setSurface() and setParameter(), using the exact same arguments as the first statement.

AcGePoint3d	   pnt3d = pntOnSrf.point (srf, param);
AcGeVector3d	  uFirstPartial = pntOnSrf.uDeriv (1, srf, param);
AcGeVector3d	  vFirstPartial = pntOnSrf.uDeriv (1, param);

This code executes correctly; however, it is more efficient to write it as follows:

AcGePoint3d	   pnt3d = pntOnSrf.point (srf, param);
AcGeVector3d	  uFirstPartial = pntOnSrf.uDeriv ();
AcGeVector3d	  vFirstPartial = pntOnSrf.uDeriv ();

The AcGePointOnCurve2d, AcGePointOnCurve3d, and AcGePointOnSurface classes not only provide a way to encapsulate the parameter space and model space information of a point on a curve or surface, they also provide a simpler and more natural interface to the curve and surface evaluators than the traditional evaluators. A typical C-style surface evaluator looks something like the following:

void evaluate (
    int numDeriv, 
    double u, 
    double v, 
    Point& pnt,
    Vector[] derivArray);

Here, you specify the parameter value (the parameter value of a surface is the 2D point whose coordinates are u, v) and request how many derivatives are to be returned. The evaluator then computes the point and requested derivatives at the specified parameter value. If requesting derivatives, you must know the order in which they are returned. For example, is the mixed partial stored in the fourth or fifth element of the array? You must also make sure that you do not pass in an array that is too small, or else memory overwrite will occur. This can be a problem when the evaluator is originally called for zero derivatives or one derivative (with an array size of 2 for derivArray) and is later changed to return two derivatives. If you forget to increase the size of derivArray, then memory overwrite occurs because the evaluator returns five derivative vectors (two first derivatives and three second derivatives) into an array that can only hold two vectors.

With the AcGePointOnSurface class, you request point, derivative, and normal information in a simple fashion using the point(), uDeriv(), vDeriv(), mixedPartial(), and normal() functions. The names of these functions indicate clearly which values they are returning, and there is no danger of memory overwrite. You do not have to index into an array to obtain derivative vectors and run the risk of making a mistake and using the wrong index for one or more of the vectors. The AcGePointOnSurface class provides an interface to the surface evaluator, which results in simpler code that is also more readable and understandable to other programmers.

In addition to providing a simpler and more natural interface to the curve and surface evaluators, the AcGePointOnCurve2d, AcGePointOnCurve3d, and AcGePointOnSurface classes provide a more efficient interface as well over the traditional evaluators. This is because each of these classes contains a pointer to a data area that can be used by the evaluators to store information between evaluations. For instance, the NURBS evaluator uses this area to store power basis matrices, which are not stored as part of the surface definition. By using this data area, the evaluators can avoid recomputing the same data that was computed in a previous evaluation and thus operate more efficiently. This data is not part of the curve or surface classes because evaluations might take place in more than one area in an alternating way, which would result in inefficient loss of the local evaluation data in switching context.

This data area also allows the evaluators to be much more efficient when a transformation has been applied to the AcGePointOnSurface object. If the transformBy() function is invoked on an AcGePointOnSurface object, it causes subsequent evaluations to be transformed by the specified transformation without actually transforming the underlying surface. This means that the evaluators must apply the transformation to each point, derivative, and normal vector that they compute. By using the data area of the AcGePointOnSurface object, the evaluators can avoid having actually to apply this transformation for each evaluation. For instance, the AcGePlane class contains the data members mPoint, mUAxis, and mVAxis, which define the origin and axes of the plane. The AcGePlane evaluator evaluates a point with the following statement:

AcGePoint3d pnt3d = mPoint + param.x * mUAxis + 
                    param.y * mVAxis;

If transformBy() has been called for the AcGePointOnSurface object, then this transformation must be applied to pnt3d before it is returned to the caller. The evaluator can avoid the expense of a matrix multiply by storing the transformed mPoint, mUAxis, and mVAxis in the AcGePointOnSurface data area. Then the above statement will evaluate the point in the transformed location without the extra expense of a matrix multiply. This is an especially useful ability in applications such as assembly modeling, where curves and surfaces have been transformed into assembly space by a positioning transformation.