![]() |
The C++ AffPoint Class |
Instances of class AffPoint encapsulate the notion of 3D points in an affine space:
Definition: An n-dimensional affine space consists of a set of points, an associated n-dimensional vector space, and two operations: subtraction of two points in the set and addition of a point in the set and a vector in the associated vector space. The former produces a vector in the associated vector space, and the latter produces another point in the affine space. Unlike a vector space, which has the distinguished vector 0, an affine space does not include a distinguished point. |
The methods specified here provide an implementation of these and other operations.
Points and vector operations lie at the heart of many common operations in computer graphics and geometric modeling. Since OpenGL is a common API used when visualizing such models, the descriptions below point out how to best make certain common connections between OpenGL and objects defined in terms of points and vectors. You should also review the Point, Vector, Matrix Utilities page to see more extensive examples of interfaces to OpenGL.
You may also wish to review the definition of an n-dimensional vector space.
Just because a C++ expression involving instances of the AffPoint class is syntactically valid from the perspective of the compiler does not imply that it is semantically valid. That is, it may or may not be well-defined. Be sure you read and understand the relevant concepts as described in the references (especially the discussion beginning in the middle of column 1 on the fifth page of reference #1).
While every attempt is made to keep these html pages current as new releases of the utilities are made available, there is no guarantee that all operations in a given release will be documented here. While all the fundamental operations are, look around the header files for additional methods that may have been added but not yet documented here.
Don't forget the "caution" note above. Recall that many of these operators are defined here for convenience and are not individually meaningful. It is up to the user of these tools to make sure that the overall expression is valid. The midpoint operation immediately preceding this paragraph uses invalid operations, for example. Using only the operations defined for points and vectors in affine spaces, we should have computed the midpoint as:
Allowing these and similar liberties for individual operations is often convenient. Multiplication of a point by a scalar, adding points, and similar invalid operations are defined below simply because using them is oftentimes the most convenient, clear, and even computationally efficient way to compute desired quantities. The price for this convenience, however, is that care must be taken to ensure that quantities ultimately computed for subsequent use are meaningful.
In the following, assume that instances of AffPoint called P, Q, R, and S have been declared and initialized. Similarly, we will assume that an instance of AffVector called v has been declared.
While this a read-only operator, note that the fields of AffPoint are public and can be modified directly. This should be done rarely, however, since the maximum use of this and related classes is obtained by using the overloaded arithmetic operators defined for the classes.
... Dxyz c; // equivalent to "double c[3];" // Refer to header: Basic.h Q.aCoords(c); ... |
... double c[3]; // equivalent to "Dxyz c;" // Refer to header: Basic.h ... |
Calling this method as: "Q.aCoords(c);" is equivalent to: "Q.aCoords(c, 0);".
... Fxyz c; // equivalent to "float c[3];" // Refer to header: Basic.h Q.aCoords(c); ... |
... float c[3]; // equivalent to "Fxyz c;" // Refer to header: Basic.h ... |
coords[0] = w*this->x; coords[1] = w*this->y; coords[2] = w*this->z; coords[3] = w;
This method is frequently used in OpenGL applications when defining control points for rational Bezier and B-Spline (i.e., NURBS) curves and surfaces. For example, the following implementation of circularArc can be used to compute the control points for a rational Bezier curve representing a 90 degree section of the unit circle centered at the origin of the xy plane. (Notice that pCoords allows a default of w = 1.0. This default behavior is also illustrated in this example.)
void circularArc() { // define the affine representation of the control points AffPoint P0(1.0, 0.0, 0.0); AffPoint P1(1.0, 1.0, 0.0); AffPoint P2(0.0, 1.0, 0.0); // create the projective space representation of the control points // which will then be passed to OpenGL GLdouble cPts[3][4]; P0.pCoords(cPts[0]); P1.pCoords(cPts[1], 0.5*sqrt(2.0)); P2.pCoords(cPts[2]); glMap1d(GL_MAP1_VERTEX_4, 0.0, 1.0, 4, 3, (const GLdouble*) cPts); int nPoints = 20; GLdouble u = 0.0; GLdouble du = 1.0 / (GLdouble(nPoints-1); glBegin(GL_LINE_STRIP); for (int i=0 ; i<=nPoints ; i++) { glEvalCoord1d(u); u += du; } glEnd(); } |
(See also the method aCoords above.)
void circularArc() { // define the affine representation of the control points AffPoint P0(1.0, 0.0, 0.0); AffPoint P1(1.0, 1.0, 0.0); AffPoint P2(0.0, 1.0, 0.0); // create the projective space representation of the control points // which will then be passed to OpenGL GLfloat cPts[3][4]; P0.pCoords(cPts[0]); P1.pCoords(cPts[1], 0.5*sqrt(2.0)); P2.pCoords(cPts[2]); glMap1f(GL_MAP1_VERTEX_4, 0.0, 1.0, 4, 3, (const GLfloat*) cPts); int nPoints = 20; GLfloat u = 0.0; GLfloat du = 1.0 / (GLfloat(nPoints-1); glBegin(GL_LINE_STRIP); for (int i=0 ; i<=nPoints ; i++) { glEvalCoord1d(u); u += du; } glEnd(); } |
This convention is different from that used in some contexts such as image synthesis where the roles of theta and phi are often reversed, probably because theta has historically been used in Image Synthesis to characterize an angle of incidence. That is, to describe the angle between the outward pointing normal vector to a surface (a local "z" axis) and an incoming light direction.
The following constants are AffPoint class variables that can be used in your programs.
For example:
AffPoint R = AffPoint::xAxisPoint + (AffPoint::yAxisPoint - AffPoint::zAxisPoint); cout << "R = " << R << '\n';
Would produce: R = (1, 1, -1)
See the description of the class methods ioFormat, inputFormat, and outputFormat below for an example of the use of these public constants.
AffPoint List[100]; for (int i=0 ; i<100 ; i++) cin >> List[i]; AffPoint C = AffPoint::centroid(List, 100); cout << "The centroid is C = " << C << '\n';
AffPoint Q = AffPoint::fromCylindrical(83.4, -0.3*PI, 139.32);
AffPoint Q = AffPoint::fromSpherical(873.4, -0.3*PI, 0.1*PI);
cin >> myPnt; cout << "myPnt = " << myPnt << '\n';
Assuming the default conditions specified, if the input is:
13.2 -1.2 -0.334
then the output will be:
myPnt = (13.2, -1.2, -0.334)
The methods inputFormat and outputFormat allow you to change these assumptions for the input and output operators, respectively. The ioFormat method is equivalent to invoking both inputFormat and outputFormat with the given parameters.
The two valid values for "ioAspect" are AffPoint::openDelimiter and AffPoint::separator. The former allows you to specify which, if any, opening and closing symbols are used. The latter allows you to specify what separator is to appear between the x, y, and z coordinates. Consider the following code:
// On input, assume a comma between coordinates of a point: AffPoint::inputFormat(AffPoint::separator, ','); // On output, use square brackets around the coordinates AffPoint::outputFormat(AffPoint::openDelimiter, '['); AffPoint myPnt; cin >> myPnt; cout << "myPnt = " << myPnt << '\n';
If the input is:
13.2, -1.2, -0.334
then the output will be:
myPnt = [13.2, -1.2, -0.334]
Any character string can be specified for any of these formatting options (subject to a maximum length). For the "open" strings, a matching closing character string will be generated by matching each input character with an "inverse". If, for example, the string "(*\n" is specified as the openDelimiter, then "\n*)" is generated as the closing delimiter.
The input operator does not actually check for an exact match on delimiters and separators. Instead it knows the number of nonblank characters in each, and it simply skips that many nonblank characters on input. While it is not recommended that you exploit this, the implication is that, given the code in the first column of the table below, the input as indicated in the second column, the output will be as shown in the third column.
AffPoint::ioFormat( AffPoint::separator,", "); AffPoint::ioFormat( AffPoint::openDelimiter, "(** "); AffPoint P; cin >> P; cout << P; |
x yz 1.1 r -2.1 # 3.7 @ $ % |
(** 1.1, -2.1, 3.7 **) |
As with the open delimiter, more general character strings can be specified such as " ,\n".
For example:
double theRatio = AffPoint::ratio(P,Q,R);
If any two of the given three points are coincident, 0.0 is returned.
AffPoint::setCoincidenceTolerance(0.0002);