$Id: G4NURBS.txt,v 1.1 1999/01/07 16:09:09 gunter Exp $ Olivier Crumeyrolle, 17th September 1996. The G4NURBS C++ class library Documentation (September 6 '96) Introduction In GEANT4, there is a need for visual representations. Non Uniform Rational B-Spline (NURBS) are an interesting choice as they allow an exact representation of conics, unlike others, and have a lot of other convenient properties. Furthermore NURBS are an industry standard in Computer Aided Design (CAD) systems. The goal of the G4NURBS C++ class library is to handle NURBS and to provide all the necessary methods so they can be succesfully used. Summary The G4NURBS class handles the data needed to define a NURBS surface, i.e. the orders of the NURBS used, the knot values and control points (explanations later). G4NURBS provides an important number of access functions to allow any graphics model (GM) to build its own NURBS. Although building the GM's NURBS with G4NURBS data (or vice versa) without any copy would be the more efficient way, different GMs typically use different data types, so copying and converting is required. For that G4NURBS provides some basic functions and also some more clever iterators to simplify the user's task. Some GMs support NURBS (OpenGL, GPHIGS), but for others G4NURBS (will) provide some adaptative tessellation functionalities, if possible as a convertion to G4Polyhedron. For the GEANT4 geometry user, G4NURBSbox, G4NURBScylinder, G4NURBStube and G4NURBStubesector are implemented. Some transformation functions (partial revolution in particular) will may be added so anyone can build almost any shape with G4NURBS, starting with some basic ones only, and without complication. If everything's OK, G4NURBS could become a nice kind of primitive. 1 Building a G4NURBS instance This section is essentialy dedicated to the GEANT4 geometry user. 1.1 From a G4VSolid to a G4NURBS 1.1.1 Box It's straightforward with G4NURBSbox. If DX, DY and DZ are the half-lengths in the X, Y an Z directions respectively, then G4NURBSbox * pmybox = new G4NURBSbox(DX, DY, DZ); builds the corresponding box. Thanks to polymorphism, this object is a G4NURBS like any other. 1.1.2 Tubs In Geant4, tubs are used to represent some sectors of tube, but also complete tubes and cylinders. To keep it simple, there is no G4NURBStubs, but a G4NURBScylinder, a G4NURBStube and a G4NURBStubesector. If RMIN, RMAX, DZ, PHI1, PHI2 are the tubs parameter (but with angles in radians), then something like G4NURBS * pmynurbs; if (RMIN != 0) { if ( ((PHI2-PHI1) >= 2*M_PI) || (PHI2==PHI1) ) pmynurbs = new G4NURBStube(RMIN, RMAX, DZ); else pmynurbs = new G4NURBStubesector(RMIN, RMAX, DZ, PHI1, PHI2); } else { if ( ((PHI2-PHI1) >= 2*M_PI) || (PHI2==PHI1) ) pmynurbs = new G4NURBScylinder(RMAX, DZ); else pmynurbs = new G4NURBStubesector(0.0001, RMAX, DZ, PHI1, PHI2); }; builds the appropriate representation. NB: as you can see there is no sector of cylinder, so we use G4NURBStubesector(epsilon, RMAX, DZ, PHI1, PHI2). A new release will include G4NURBScylindersector one day. Note that G4NURBStubesector can in principle handle all possible tubs, (it even works with negative radius, negative length and more than two pi angle difference) but the graphics system will probably have some difficulties with the NURBS, and even if it display it, you will not like the result. 1.2 In general One cannot build directly a G4NURBS object. G4NURBS is just an handling class. To have some instance you must derive from G4NURBS and use one of its protected constructors in the child class constructor initialisation list. The first G4NURBS constructor is able to produce a regular knot vector for an unallocated knot vector, and the second one (the most easy to use) handles allocation of all arrays and is able to produce some "linear" but also some "circular" knots. G4NURBSbox and G4NURBStube use the second constructor and are good examples, G4NURBStubs is slightly more complex but shows how to build automatically a G4NURBS. G4NURBScylinder is the only one written with the first constructor, to provide an example. See also comments in G4NURBS.hh. This file contains in the appendix an introduction to NURBS, with some examples. You should also follow the recommendations in 4.2.4. 1.3 Future prospects A child class with an istream based constructor might be written which allows the user to load a NURBS Definition file (as the << overload currently generates them). Some transformation functions might be written in order to help the user to build a NURBS from basic one. (suggestions are welcomed) A child class G4NURBStrimmingcurve might be written with some methods to help the user to clip NURBS or to convert some boolean solids into clipped NURBS. However the GMs must support surface trimming, as it's not straightforward to generate an equivalent representation of a trimmed NURBS (set of smaller NURBS?) 2 Access functions This section will more interest the graphics model devloper. 2.1 Simple data access with and without a direction selector To retrieve NURBS simple data (order, various numbers) a first set of access functions is provided : G4int getUorder() const; G4int getVorder() const; G4int getUnbrKnots() const; G4int getVnbrKnots() const; G4int getUnbrCtrlPts() const; G4int getVnbrCtrlPts() const; where nbr is short for number and CtrlPts for control points. Another set of functions using a _selector_ is provided : G4int getorder(t_direction in_dir) const; G4int getnbrKnots(t_direction in_dir) const; G4int getnbrCtrlPts(t_direction in_dir) const; The selector in_dir is an enum in the G4NURBS scope. So the complete type name is G4NURBS::t_direction, and values are G4NURBS::U for retrieving data related to the U direction, and G4NURBS::V for V. Hence the following calls are equivalent : my_nurbs.getUorder(); my_nurbs.getorder(G4NURBS::U); The first may be slightly faster than the second (cf inline code in G4NURBS.hh). G4NURBS::NofD is also defined, where NofD means nbr of directions. One could use it for loops over direction. However as enum can't be incremented this is not as useful as it could be. See the << overload in G4NURBS.cc for a loop example or the constuctors (still in G4NURBS.cc) for another example. 2.2 Access to knots and control points 2.2.1 A set of basic functions is provided : G4float getfloatKnot( t_direction in_dir, t_indKnot in_index) const; G4double getdoubleKnot( t_direction in_dir, t_indKnot in_index) const; t_floatCtrlPt* getfloatCtrlPt( t_indCtrlPt in_onedimindex) const; t_floatCtrlPt* getfloatCtrlPt( t_inddCtrlPt in_Uindex, t_inddCtrlPt in_Vindex) const; t_doubleCtrlPt* getdoubleCtrlPt(t_indCtrlPts in_onedimindex) const; t_doubleCtrlPt* getdoubleCtrlPt(t_inddCtrlPts in_Uindex, t_inddCtrlPts in_Vindex) const; The first two return the in_index th knot (with a 0 based numbering, so the valid index goes from 0 to nbrKnots-1) after converting it into G4float or G4double. The four others functions do the same with control points. The returned pointer points to a brand-new allocated array with the control points coordinates. You can do what you want with it, it's _your_ copy, (non-const) and you must delete it after use. You can give the U and V index or a global index, as control points are stored U increasing first. The followings calls are equivalent : // suppose a 5 by 9 net of ctrlpts (5 along U, 9 along V) // and t_doubleCtrlPt * pmycp; pmycp = my_nurbs.getdoubleCtrlPt(3, 2); pmycp = my_nurbs.getdoubleCtrlPt(13); However, iterators are an easier way to retrieve the knots or the control points in a sequential manner. 2.2.2 Iterators Iterators are defined in the G4NURBS public scope, to avoid the global namespace pollution. Therefore their type name is G4NURBS::KnotsIterator, G4NURBS::CtrlPtsCoordsIterator, G4NURBS::CtrlPtsIterator. Iterators are independent objects : you can allocate as many iterators as you want and make them work concurrently. However the G4NURBS object MUST be defined when you use iterators over it (and must not be changed). See the Possible improvements 1.2 section for more about that. 2.2.2.1 G4NURBS::KnotsIterator allows the user to retrieve knots one after the other. It can be used as follows to get all the knots along the U direction as 'float' numbers : // assuming mynurb is a G4NURBS, for instance G4NURBSbox my_nurb(1,2,3); G4float * my_array, * my_float_p; my_float_p = my_array = new float [my_nurb.getnbrKnots(G4NURBS::U)]; G4NURBS::KnotsIterator my_iterator(my_nurb, G4NURBS::U); while (my_iterator.pick(my_float_p++)); Then my_array contain all the knots, and my_float_p point just after the array. The trick is that pick functions return false when they pick the last knot. The constructor has an optional third argument, in_startIndex, preseted to zero. It's possible to give another value to retrieve half of the knots for instance. (will probably be used in splitting) 2.2.2.2 G4NURBS::CtrlPtsCoordsIterator is very similar to the Knots one. The control points are retrieved coordinate after coordinate, and, if we note i the index along U and j the one along V, control points P_i_j themselves are obtained i increasing first, j after : P_1_1, P_2_1, P_3_1, P_4_1, ....., P_1_2, P_2_2, P_3_2, P_4_2, .... G4float * my_array, * my_float_p; my_float_p = my_array = new float [my_nurb.gettotalnbrCtrlPts() *G4NURBS::NofC*sizeof(float)]; G4NURBS::CtrlPtsCoordsIterator my_iterator(my_nurb); while (my_iterator.pick(my_float_p++)); 2.2.2.3 G4NURBS::CtrlPtsIterator work control point by control point, instead of control point coordinate by control point coordinate. Remember that control points are given U index increasing first, V after. The << overload in G4NURBS.cc contains an exemple where the retrieved control point is used immediately. 2.2.3 Some "complete-copy" functions are also provided. G4float * getfloatAllKnots(t_direction in_dir) const; G4double * getdoubleAllKnots(t_direction in_dir) const; G4float * getfloatAllCtrlPts() const; G4double * getdoubleAllCtrlPts() const; These functions return a pointer over a new array with all the knots or control points copied. You do not control the allocation nor the copy, but you own the result and will have to delete it. They are more easy to use than iterators, but less flexible. Be careful with these functions, as _you_ choose the type, whereas with iterators the compiler choose the good pick function for you. If G4double or G4float change, you must change your code, instead of just recompile it. 3 Use in GEANT4 3.1 NURBS manipulations The public interface of G4NURBS does not allow the user to modify a NURBS. The user is expected to create G4NURBS via child classes. Splitting, tesslation, or other non-conservative processes should occur only as a conversion to a child class, via its constructor, or eventually with a conversion operator. e.g. G4NURBSashape mynurbs(foo, goo, hoo); // for splitting G4NURBSsplit mypieceofnurbs(mynurbs, splitdirection, splitpointvalue); // for tessellation, if // class G4PolyhedronedNURBS : public G4Polyhedron // has a constructor G4PolyhedronedNURBS(const G4NURBS &, G4Float tolerance) G4PolyhedronedNURBS mytessellatednurbs(mynurbs, thetolerance); // (a multiple inheritance could be used to have a "dual-face" // object Polyhedron/NURBS, with the advantage of keeping // a NURBS and its tessellation together, and the disadvantge // of duplicating the original NURBS into this child) The resulting instance is not necessary constant, G4NURBSsplit could provide some sub-splitting and refinement methods that act on itself. 3.2 Copying policy A copy constructor is provided, but in the private part. A warning message ( "WARNING: G4NURBS::G4NURBS(const G4NURBS &) used" ) is printed on cerr each time the copy constructor is called. As long as there is no sophisticated memory management scheme that reduce useless duplications, copying should be avoided. We have already to duplicate data for the GM, once is enough. 4 Possible internal improvements that could have consequences 4.1 Memory management : 4.1.1 G4NURBS use the minimal memory management scheme possible. Control points, knots, are stored in basic arrays. NURBS characteristics are stored in simple structure. All those internal data could be handled in protected classes with ad hoc constructors, which would be more reliable. The G4NURBS copy constructor would be more elegant. However performance may decrease. This point is related to typing. See 2.1. 4.1.2 Some reference counting facilities should be added for internal arrays so that iterators could correctly work after a G4NURBS is deleted/changed. This point could be solved with 1.1 in a elegant way. We should at least warn the user when a nurbs is deleted before the corresponding iterators. However if we want to do the things in a good way references counters must be associated to arrays. Once again 1.1 is the solution for that. To put references as members, we need the future "mutable" keyword planed by the ANSI C++ committee, or the counting will be uncompatible with const instances. 4.2 Internal data type 4.2.1 Knots and control points types are just typedefs, idem for index types. They could be strong types (i.e. classes), but performance costs will may be significant. 4.2.2 enum type are useful to enforce the user to do good things (like using G4NURBS::U instead of 0), but are not really convenient for loops. Some internal alias should be defined to allow some more easy loops. [this concern the geom user only if he/she writes some G4NURBS children] 4.2.3 As the user doesn't know the internal data type, it could be possible for G4NURBS to adapt itself to the GM currently used, so this GM could use directly the G4NURBS data. This adaptation concern the control points storage order and knot values (some fast algorithms exists for knots which differe of 0 or 1 only). It would be necessary to write some inline functions for control points initialization that spare the child class writer to know the storage order, see 4.2.4. But once again, what about performance ? The G4NURBS constructors and the initializations functions used in child constructors would have to change their target type, and thus we need a very clever scheme to be compatible with the use of different GMs in the same time. 4.2.4 For the momment the control points storage order is publicly known, that could change, with a formal class level between G4NURBS and G4NURBSashape children. Calls like CP(mpCtrlPts[..], .... will have to be rewritten; a tool will may be provided to automaticaly do that. Use if possible explicit U & V index values like CP(mpCtrlPts[to1d(U_index, V_index)], ....... [this concerns the geometry user only if he/she writes some G4NURBSashape]. 4.3 External interfacing types 4.3.1 For the moment G4int, G4double, G4float, are just alias for int, double, float. These G4 types are used in the G4NURBS interface (the public part of G4NURBS). If their definitions are changed, the significance of the access functions will change too, user should take care about that when using access functions with some others types (e.g. GLfloat ) In a more general way this type interfacing problem will have to be solved at a general level as different GS are used. For the moment G4Float is used internally and interfaced to G4float and G4double. 4.3.2 Child constructors take all their arguments as G4double. This may have to change, depending on the exact work that G4VSolid children have to do. Length arguments should be unsigned (but unsigned G4double is illegal), and angles in radians should have a different type from those in degrees. 4.4 Names 4.4.1 G4NURBS could become G4VNurbs, and G4NURBSbox could become G4NurbsBox. 5 Prospects 5.1 For NURBS in general NURBS are likely to become more and more widely supported by software, but also by hardware. Nvidia NV1 PCI card for PCs already accelerate quadratic curves (and may be more) . The new SPARCstation 20TurboZX provide dynamic tessellation for NURBS in firmware. 5.2 Births G4NURBS will probably have others children in the future, in particular for splitting. However the object oriented programming allows anyone to create some child classes. Don't hesitate to enlarge the family. Appendix Non Uniform Rational B-Spline (NURBS). 1 About Representation A curve or a surface are mathematicaly a set of points. However the number of points is infinite, what is not convenient at all. We need a more operational way for describing the set of points. Here we use a parametric representation : the curve is described by a function of 1 real parameter t : C(t), the surface with two parameters called u and v : S(u,v). As the parameter(s) go(es) from a minimal value to a maximal one, the function describes the curve/surface. Now we need a representation for this function itself. To achieve this, the function is projected over a set of basis functions. Of course the kind of functions we can represent in this way is determined by the kind of basis functions used. We will work with real-valued polynomial function, as they are easy to represent, can approximate any continuous function as well as we want and are convenient in general. Once basis functions are chosen, the kind of 'coordinates' that a function will have over such a basis is determined : the function describe a set of point whereas basis functions chosen are real-valued, so the 'coordinates' are necessary some points, called control points, noted P_i (or P_{i,j}), represented by their position-vector, and multipied by the basis functions. A curve will then be expressed as C(t) = sum(i = 1, n) { P_i * B_i(t) } where B_i is the ith basis functions. A surface could be expressed as S(u,v) = sum(i = 1, n) { P_i * B_i(u,v) }, but we will use a more easy scheme, explained in 2.5 An interesting point is that if T is a linear transformation, T[C(t)] = sum(i = 1, n) { T[P_i] * B_i(t) } 2 B-Spline curves and surfaces 2.1 B-Spline basis functions They are defined as following : B_{i,1} = 1 if t_i <= t <= t_{i+1}, 0 otherwise B_{i,k}(t) = ((t-t_i)/(t_{i+k-1}-t_i))*B_{i,k-1}(t) + ((t_{i+k}-t)/(t_{i+k}-t_{i+1}))*B_{i+1,k-1} i goes from 1 to n, the number of basis functions. k is the order of the B-Spline. (t_i)_{1<=i<=n+k} a sequence of non-decreasing values called knots that defines the B_{i,k}, and is globaly designed as the knot vector. The B-Spline is said to be uniform if t_{i+1}-t_i = d > 0 for any i, non-uniform (NU) otherwise, with the convention 0/0 = 0. We concentrate first on uniform B-Splines (UBS) 2.2 Some UBS properties The recursive relation defines B_{i,k} as a linear interpolation between B_{i,k-1} and B_{i+1,k-1}. Thus B_{i,k} is a polynomial of degree k-1, strictly positive in [t_i, t_{i+k}], nul elsewhere. For a given t / t_i <= t < t_{i+1}, only B_{i-k+1,k} to B_{i,k} are non-nul. The (endknot-startknot) divisors in the relation imply that sum(i = 1,n){B_{i,k}(t)} = 1. [picture of UBS basis functions from k=1 to 2 or 3] 2.3 Change of basis : the Oslo algorithm It is possible to increase the number of basis functions used. If (t_i)_{1<=i<=n+k} is the old knot vector and (t'_i)_{1<=i<=n'+k} the new one, then P'_i = sum (j = 1, n) { alpha_{i,j,k} * P_j } where alpha_{i,j,k} is given by a recursive relations similar to B_i,k ones : alpha_{i,j,k} = ((t'_{j+k-1} - t_i) / (t_{i+k-1} - t_i)) * alpha_{i,j,k-1} + ((t_{i+k} - t'_{j+k-1}) / (t_{i+k} - t_{i+1})) * alpha_{i+1,j,k-1} The Oslo algorithm is not used in the G4NURBS class library at the moment, but it will when splitting functionalities will be added. 2.4 UBS curves Once we have defined the basis function, a curve is expressed as following : C(t) = sum(i = 1, n) { P_i * B_{i,k}(t) } where P_i are the control points and k the order. (This maps [t_min, t_max] to the curve.) If we note C^n the class of functions that are continuous up to the nth derivative (included), then for t_i < t < t_{i+1}, C(t) is C^infinite, and at t = t_i, C(t) is C^{k-2}. [pictures ?] If a control points is repeated k-1 time or more, the curve goes through it. 2.5 UBS surfaces Surfaces are expressed as the tensor product of two curves : S(u,v) = sum(i=1,n) sum(j=1,m) { P_{i,j}*B_{i,k}(u)*B_{j,l}(v) } where P_{i,j} is the net of control points. (This maps [u_min,u_max] x [v_min,v_max] to the surface.) This is more convenient than starting from 2D splines, or similar functions, but you must keep in mind that with such a scheme the curve along one direction must be sufficently complexe to handle the shape complexity all along this direction. However it's sometime possible to choose a direction for a parameter in such a way that the surface is more simple along this direction. 2.6 Choice of parameter domain In the G4NURBS class library, the default is zero as the minimal parameter value, one for the maximal, and t_{i+1}-t_i = ( 0 or constant) ). However examples in section 4 will use t_i = integer for non-coincident knots, as it's more legible. 3 NURBS curves and surfaces 3.1 NU As previously said, the knots interval can vary. As we still impose t_{i+1} >= t_i, the limit case is t_{i+1} = t_i. In such a case, B_{i,1} is reduced to a point, B_{i,2} and B_{i+1,2} overlap on a knot segment reduced to a point, and at this point C(t) will be only C^{k-3}, making the curve less smooth. More generally if a knot appears M times, C(t) is C^{k-M-1}. If M = k-1, C(t) is C^0, the curve is still continuous but can have a corner at t = t_i = ... = t_{i+k-2} and go through the corresponding control point P_{i-1}, thanks to B_{i-1,k} that cover t_i to t_{i+k-2,k}. If M = k, the curve is no longer continous : all the basis B-Splines from B_{i-k,k} to B_{i-1,k} stop at t = t_i = ... = t_{i+k-1} and the curve stop a the corresponding control point P_{i-1}. If there are other knots and control points after, the curve restarts from next control point P_i, as B_{i,k} to B_{i+k-1,k} start at t_{i+k-1} = t'. 3.2 R The control points are no longer usual position vectors in the plane (isomorphic with R^2) or in the space (isomorphic with R^3) but an element of the projective space P^n, which as one dimension more that the corresponding usual space. P^n, isomorphic with R^n, is associated with the usual space isomorphic with R^{n-1} : (x, y, w) in P^3 corresponds to (x/w, y/w) in R^2, (x, y, z, w) in P^4 corresponds to (x/w, y/w, z/w) in R^3 and so on. If w = 0, the point goes to infinity, defining a direction. Thus working in the projective space allows us to work with points and vectors, and all affine transformations in the usual space can be combined into a linear ones in the projective space, as well as projections. The coordinates in P^n are said to be homogeneous or rational, and items expressed with such coordinates are highlighted by an ^h. 3.3 NU R BS NURBS are NUBS in the projective space. Hence to transform a NURBS, one just has to transform the control points, even for projections. The last homogeneous coordinate w is called the "weight". 3.4 NURBS curves If P_i = (x_i, y_i), then P^h_i = (w_i*x_i, w_i*y_i, w_i) and a curve is expressed in P^3 by C^h(t) = sum(i = 1, n) { P^h_i * B_{i,k}(t) } and in R^2 by C(t) = sum(i = 1, n){ w_i* P_i * B_{i,k}(t) } / sum(i = 1, n){ w_i* B_{i,k}(t) } 3.5 NURBS surfaces If P_i = (x_i, y_i, z_i), then P^h_i = (w_i*x_i, w_i*y_i, w_i*z_i, w_i) and a surface is expressed in P^4 by S^h(u,v) = sum(i=1,n) sum(j=1,m) { P^h_{i,j} * B_{i,k}(u) * B_{j,l}(v) } and in R^3 by S(u,v) = sum(i=1,n) sum(j=1,m) { w_{i,j} * P_{i,j} * B_{i,k}(u) * B_{j,l}(v) } /sum(i=1,n) sum(j=1,m) { w_{i,j} * B_{i,k}(u) * B_{j,l}(v) } 3.6 NURBS advantages/disadvantages 3.6.1 Advantages : Common representation for shapes, even with discontinuities. Exact representation of conics section (and for all shapes that have a polynomial parametrisation in the projective space). Invariant under affine and perspective tranformations. Flexible (lot of manipulations possible, cf 4). Generalization of others B-splines / Bezier shapes. 3.6.2 Drawbacks : Today need some calculations to be converted to triangles and/or quadrilaterals as these are the only kind of shapes directly handled by graphics systems at low-level (OpenGL is able to do that but it really generate too many triangles/squares). Extra storage for simple shapes. We must choose a good parameterisation. 4 Examples of NURBS 4.1 Linear With 2.1 definition, linear B-Spline are order 2 : k = 2. All the weigths are equal to ones in what follows. 4.1.1 Segment We want a strait line from P_1 to P_2. To start the curve at P_1, we need a knot repeated k times at the begining and at the end of the knot vector. Thus the knot vector looks like { 0 0 ... 1 1 }. Do we need more than four knots ? No, two control points, a second order NURBS, 2 + 2 = 4, number of knots. (Remember (t_i)_{1<=i<=n+k}) 4.1.2 Polyline We could just put some lines in such a way that they connect each other in a head to tail fashion, { 0 0 1 1 } [ P_1, P_2 ] Set P_3 = P_2 { 0 0 1 1 } [ P_3, P_4 ] Set P_5 = P_4 { 0 0 1 1 } [ P_5, P_6 ] and union them in one NURBS : { 0 0 1 1 2 2 3 3 } [ P_1, P_2, P_3, P_4, P_5, P_6 ] still with P_3 = P_2 and P_5 = P_4. Then we can simplify the NURBS : { 0 0 1 2 3 3 } [ P_1, P_3, P_4, P_6 ] 4.1.3 Square If P_1, P_2, P_3, P_4 are the four corners, we can do a NURBS with a polyline that starts and stop at P_1 : { 0 0 1 2 3 4 4 } [ P_1 P_2 P_3 P_4 P_1 ] 4.1.4 Box If we take a square and move it (=extrude) along a perpendicular edge, we get a box which lack to faces, the two ones that are perpendicular to the edge along which we extruded the square. Note that if you change the order in which you are considering the tensor product, this surface is a segment (the edge) moved along a square. { 0 0 1 2 3 4 4 } by [ P_1 P_2 P_3 P_4 P_1 ] { 0 0 [ P'_1 P'_2 P'_3 P'_4 P'_1 ] 1 1 } To close the sides, we repeat the extremeties, { 0 0 1 2 3 4 4 } by [ P_1 P_2 P_3 P_4 P_1 ] { 0 0 [ P_1 P_2 P_3 P_4 P_1 ] 1 [ P'_1 P'_2 P'_3 P'_4 P'_1 ] 2 [ P'_1 P'_2 P'_3 P'_4 P'_1 ] 3 3 } and then reduce the first and last square to a line or a point. For instance if P_0 is the center of the P_i side and P'_0 the one of the P'_i, { 0 0 1 2 3 4 4 } by [ P_0 P_0 P_0 P_0 P_0 ] { 0 0 [ P_1 P_2 P_3 P_4 P_1 ] 1 [ P'_1 P'_2 P'_3 P'_4 P'_1 ] 2 [ P'_0 P'_0 P'_0 P'_0 P'_0 ] 3 3 } (Put differently, we take half of a rectangle and "rotate" it along a square.) The G4NURBSbox use something like { 0 0 1 2 3 4 4 } by [ P_1 P_1 P_4 P_4 P_1 ] { 0 0 [ P_1 P_2 P_3 P_4 P_1 ] 1 [ P'_1 P'_2 P'_3 P'_4 P'_1 ] 2 [ P'_1 P'_1 P'_4 P'_4 P'_1 ] 3 3 } rectangles reduced to lines, to avoid a "star" effect on display. 4.1.4 Trap There is no realy difference with the box. One just have to check that P_i and P'_i are on the same edge, or the trap will be twisted. 4.2 Quadratic : Quadratic B-Splines are order 3 : k = 3. 4.2.1 Arc We start at P_1 = (1,0) in R^2, and stop at P_3 = ( cos phi, sin phi ). With the knot vector { 0 0 0 1 1 1 }, we just need another control point, P_2. P_2 defines with P_1 the tangent at P_1, and with P_2 the tangent at P_2, thus P_2 is at ( 1, tan phi/2 ) and its weight is not 1, unlike P_1 and P_3, but cos phi/2. 4.2.2 Circle To avoid infinite values, a circle is made by many arcs, for instance three 120 degrees arcs or four 90 degrees arcs. They are joined and simplified as for the polyline. ex: the circle used in G4NURBScylinder is made by four 90 degree arc : (s = sqrt(2)/2) { 0 0 0 1 1 2 2 3 3 4 4 4 } [ 1,0,1 s,s,s 0,1,1 -s,s,s -1,0,1 -s,-s,s -1,0,1 -s,s,s 1,0,1 ] 4.2.3 Cylinder We build a cylinder from the circle as we made the box from the square : extrusion + closing 4.2.3 Tube The tube is obtained by revolving a rectangle around the tube axis. For suggestion/change/English corrections : contact me or e-mail (via olivierc@h2.ph.man.ac.uk) ( I'm sure it needs some English corrections )