1 /* -------------------------------------------------------------------------- *
2  *                        Simbody(tm): SimTKmath                              *
3  * -------------------------------------------------------------------------- *
4  * This is part of the SimTK biosimulation toolkit originating from           *
5  * Simbios, the NIH National Center for Physics-Based Simulation of           *
6  * Biological Structures at Stanford, funded under the NIH Roadmap for        *
7  * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody.  *
8  *                                                                            *
9  * Portions copyright (c) 2011-12 Stanford University and the Authors.        *
10  * Authors: Matthew Millard, Michael Sherman                                  *
11  * Contributors:                                                              *
12  *                                                                            *
13  * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
14  * not use this file except in compliance with the License. You may obtain a  *
15  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
16  *                                                                            *
17  * Unless required by applicable law or agreed to in writing, software        *
18  * distributed under the License is distributed on an "AS IS" BASIS,          *
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
20  * See the License for the specific language governing permissions and        *
21  * limitations under the License.                                             *
22  * -------------------------------------------------------------------------- */
23 
24 /**@file
25 This file contains the library-side implementations of classes
26 BicubicSurface, BicubicSurface::Guts, and BicubicSurface::PatchHint. **/
27 
28 #include "SimTKcommon.h"
29 #include "simmath/internal/common.h"
30 #include "simmath/internal/BicubicSurface.h"
31 #include "simmath/internal/SplineFitter.h"
32 #include "simmath/internal/ContactGeometry.h"
33 
34 #include "BicubicSurface_Guts.h"
35 
36 #include <algorithm>
37 
38 using namespace SimTK;
39 using namespace std;
40 
41 //==============================================================================
42 //                              BICUBIC SURFACE
43 //==============================================================================
44 
45 // This is just a handle for BicubicSurface::Guts, which is the shared,
46 // underlying surface representation. Here we just manage the reference
47 // count and otherwise forward all requests to the Guts class.
48 
49 // Default constructor is inline; just sets guts pointer to null.
50 
51 // Destructor: delete guts if this is the last referencing handle.
~BicubicSurface()52 BicubicSurface::~BicubicSurface() {clear();}
53 
54 // Copy constructor is shallow.
BicubicSurface(const BicubicSurface & source)55 BicubicSurface::BicubicSurface(const BicubicSurface& source) {
56     guts = source.guts; // copy pointer
57     if (guts) guts->incrReferenceCount();
58 }
59 
60 // Copy assignment is shallow.
operator =(const BicubicSurface & source)61 BicubicSurface& BicubicSurface::operator=(const BicubicSurface& source) {
62     if (guts != source.guts) {
63         clear();
64         guts = source.guts; // copy pointer
65         if (guts) guts->incrReferenceCount();
66     }
67     return *this;
68 }
69 
70 // Return handle to its default-constructed state, deleting guts if this was
71 // the last reference.
clear()72 void BicubicSurface::clear() {
73     if (guts && guts->decrReferenceCount()==0)
74         delete guts;
75     guts = 0;
76 }
77 
78 // Constructor for irregularly spaced samples.
BicubicSurface(const Vector & x,const Vector & y,const Matrix & f,Real smoothness)79 BicubicSurface::BicubicSurface
80    (const Vector& x, const Vector& y, const Matrix& f, Real smoothness)
81 :   guts(0) {
82     guts = new BicubicSurface::Guts(x,y,f,smoothness);
83     guts->incrReferenceCount(); // will be 1
84 }
85 
86 // Constructor for regularly spaced samples.
BicubicSurface(const Vec2 & XY,const Vec2 & spacing,const Matrix & f,Real smoothness)87 BicubicSurface::BicubicSurface
88    (const Vec2& XY, const Vec2& spacing, const Matrix& f, Real smoothness)
89 :   guts(0) {
90     guts = new BicubicSurface::Guts(XY,spacing,f,smoothness);
91     guts->incrReferenceCount(); // will be 1
92 }
93 
94 // Constructor from known patch derivatives.
BicubicSurface(const Vector & x,const Vector & y,const Matrix & f,const Matrix & fx,const Matrix & fy,const Matrix & fxy)95 BicubicSurface::BicubicSurface
96    (const Vector& x, const Vector& y, const Matrix& f,
97     const Matrix& fx, const Matrix& fy, const Matrix& fxy)
98 :   guts(0) {
99     guts = new BicubicSurface::Guts(x,y,f,fx,fy,fxy);
100     guts->incrReferenceCount(); // will be 1
101 }
102 
103 // Constructor from known patch derivatives with regular spacing.
BicubicSurface(const Vec2 & XY,const Vec2 & spacing,const Matrix & f,const Matrix & fx,const Matrix & fy,const Matrix & fxy)104 BicubicSurface::BicubicSurface
105    (const Vec2& XY, const Vec2& spacing, const Matrix& f,
106     const Matrix& fx, const Matrix& fy, const Matrix& fxy)
107 :   guts(0) {
108     guts = new BicubicSurface::Guts(XY,spacing,f,fx,fy,fxy);
109     guts->incrReferenceCount(); // will be 1
110 }
111 
112 // calcValue(), the fast version.
calcValue(const Vec2 & XY,PatchHint & hint) const113 Real BicubicSurface::calcValue(const Vec2& XY, PatchHint& hint) const {
114     SimTK_ERRCHK_ALWAYS(!isEmpty(), "BicubicSurface::calcValue()",
115         "This method can't be called on an empty handle.");
116     return guts->calcValue(XY, hint);
117 }
118 
119 // calcValue(), the slow version.
calcValue(const Vec2 & XY) const120 Real BicubicSurface::calcValue(const Vec2& XY) const {
121     PatchHint hint; // create an empty hint
122     return calcValue(XY, hint);
123 }
124 
125 // calcDerivative(), the fast version.
calcDerivative(const Array_<int> & components,const Vec2 & XY,PatchHint & hint) const126 Real BicubicSurface::calcDerivative
127    (const Array_<int>& components, const Vec2& XY, PatchHint& hint) const {
128     SimTK_ERRCHK_ALWAYS(!isEmpty(), "BicubicSurface::calcDerivative()",
129         "This method can't be called on an empty handle.");
130     return guts->calcDerivative(components, XY, hint);
131 }
132 
133 // calcDerivative(), the slow version.
calcDerivative(const Array_<int> & components,const Vec2 & XY) const134 Real BicubicSurface::calcDerivative
135    (const Array_<int>& components, const Vec2& XY) const {
136     PatchHint hint; // create an empty hint
137     return calcDerivative(components, XY, hint);
138 }
139 
calcUnitNormal(const Vec2 & XY,PatchHint & hint) const140 UnitVec3 BicubicSurface::calcUnitNormal(const Vec2& XY, PatchHint& hint) const {
141     const Array_<int> dx(1,0), dy(1,1);
142     const Real fx = calcDerivative(dx,XY,hint);
143     const Real fy = calcDerivative(dy,XY,hint);
144     return UnitVec3(-fx, -fy, 1); // (1,0,fx) X (0,1,fy)
145 }
calcUnitNormal(const Vec2 & XY) const146 UnitVec3 BicubicSurface::calcUnitNormal(const Vec2& XY) const {
147     PatchHint hint;
148     return calcUnitNormal(XY,hint);
149 }
150 
calcParaboloid(const Vec2 & XY,PatchHint & hint,Transform & X_SP,Vec2 & k) const151 void BicubicSurface::calcParaboloid
152    (const Vec2& XY, PatchHint& hint, Transform& X_SP, Vec2& k) const
153 {
154     SimTK_ERRCHK_ALWAYS(!isEmpty(), "BicubicSurface::calcParaboloid()",
155         "This method can't be called on an empty handle.");
156     guts->calcParaboloid(XY, hint, X_SP, k);
157 }
158 
calcParaboloid(const Vec2 & XY,Transform & X_SP,Vec2 & k) const159 void BicubicSurface::calcParaboloid
160    (const Vec2& XY, Transform& X_SP, Vec2& k) const
161 {
162     PatchHint hint;
163     calcParaboloid(XY, hint, X_SP, k);
164 }
165 
getNumPatches(int & nx,int & ny) const166 void BicubicSurface::getNumPatches(int& nx, int& ny) const
167 {   guts->getNumPatches(nx,ny); }
168 
169 Geo::BicubicHermitePatch BicubicSurface::
calcHermitePatch(int x,int y) const170 calcHermitePatch(int x, int y) const
171 {   PatchHint hint; return guts->calcHermitePatch(x,y, hint); }
172 
173 Geo::BicubicBezierPatch BicubicSurface::
calcBezierPatch(int x,int y) const174 calcBezierPatch(int x, int y) const
175 {   PatchHint hint; return guts->calcBezierPatch(x,y, hint); }
176 
isSurfaceDefined(const Vec2 & XY) const177 bool BicubicSurface::isSurfaceDefined(const Vec2& XY) const
178 {   return getGuts().isSurfaceDefined(XY); }
179 
getMinXY() const180 Vec2 BicubicSurface::getMinXY() const {
181     const BicubicSurface::Guts& guts = getGuts();
182     return Vec2(guts._x[0],guts._y[0]);
183 }
184 
getMaxXY() const185 Vec2 BicubicSurface::getMaxXY() const {
186     const BicubicSurface::Guts& guts = getGuts();
187     return Vec2(guts._x[guts._x.size()-1],guts._y[guts._y.size()-1]);
188 }
189 
createPolygonalMesh(Real resolution) const190 PolygonalMesh BicubicSurface::createPolygonalMesh(Real resolution) const {
191     PolygonalMesh mesh;
192     getGuts().createPolygonalMesh(resolution, mesh);
193     return mesh;
194 }
195 
getNumAccesses() const196 int BicubicSurface::getNumAccesses() const
197 {   return getGuts().numAccesses; }
198 
getNumAccessesSamePoint() const199 int BicubicSurface::getNumAccessesSamePoint() const
200 {   return getGuts().numAccessesSamePoint; }
201 
getNumAccessesSamePatch() const202 int BicubicSurface::getNumAccessesSamePatch() const
203 {   return getGuts().numAccessesSamePatch; }
204 
getNumAccessesNearbyPatch() const205 int BicubicSurface::getNumAccessesNearbyPatch() const
206 {   return getGuts().numAccessesNearbyPatch; }
207 
resetStatistics() const208 void BicubicSurface::resetStatistics() const
209 {   return getGuts().resetStatistics(); }
210 
211 
212 
213 //==============================================================================
214 //                          BICUBIC SURFACE :: GUTS
215 //==============================================================================
216 
217 // This is the constructor for irregularly-spaced samples.
Guts(const Vector & aX,const Vector & aY,const Matrix & af,Real smoothness)218 BicubicSurface::Guts::Guts(const Vector& aX, const Vector& aY,
219                            const Matrix& af, Real smoothness)
220 {
221     construct();
222 
223     _x.resize(aX.size()); _y.resize(aY.size());
224     _x = aX; _y = aY;
225 
226     _hasRegularSpacing = false;
227 
228     constructFromSplines(af, smoothness);
229 }
230 
231 
232 // This is the constructor for regularly spaced samples.
Guts(const Vec2 & XY,const Vec2 & spacing,const Matrix & af,Real smoothness)233 BicubicSurface::Guts::Guts
234    (const Vec2& XY, const Vec2& spacing, const Matrix& af, Real smoothness)
235 {
236     construct();
237 
238     // Check for reasonable spacing.
239     SimTK_ERRCHK2_ALWAYS(spacing > 0,
240         "BicubicSurface::BicubicSurface(XY,spacing,af,smoothness)",
241         "A BicubicSurface requires positive spacing in both x and y"
242         " but spacing was %g and %g.", spacing[0], spacing[1]);
243 
244     const int nx = af.nrow(), ny = af.ncol();
245     _x.resize(nx); _y.resize(ny);
246 
247     for (int i=0; i < nx; ++i)
248         _x[i] = XY[0] + i*spacing[0];
249     for (int j=0; j < ny; ++j)
250         _y[j] = XY[1] + j*spacing[1];
251 
252     _hasRegularSpacing = true;
253     _spacing = spacing;
254 
255     constructFromSplines(af, smoothness);
256 }
257 
258 // This implementation is shared by the regular and irregular constructors.
259 // We expect _x and _y already to have been filled in with the grid sample
260 // locations (either as supplied or as generated from regular spacing).
261 void BicubicSurface::Guts::
constructFromSplines(const Matrix & af,Real smoothness)262 constructFromSplines(const Matrix& af, Real smoothness)
263 {
264     const int nx = af.nrow(), ny = af.ncol();
265 
266     // Check for sufficient sample size.
267     SimTK_ERRCHK2_ALWAYS(af.nrow() >= 4 && af.ncol() >= 4,
268         "BicubicSurface::BicubicSurface()",
269         "A BicubicSurface requires at least 4 sample in x and y directions"
270         " but grid dimensions were %d and %d.", nx, ny);
271 
272     SimTK_ERRCHK4_ALWAYS(_x.size() == nx && _y.size() == ny,
273         "BicubicSurface::BicubicSurface()",
274         "Number of samples must match the grid dimension (%d X %d) but"
275         "the number of supplied sample points was %d X %d.",
276         nx, ny, _x.size(), _y.size());
277 
278     // We're checking this even for generated regularly-spaced samples
279     // to catch the rare case that the spacing was so small that it
280     // produced two identical sample locations.
281 
282     SimTK_ERRCHK_ALWAYS(   isMonotonicallyIncreasing(_x)
283                         && isMonotonicallyIncreasing(_y),
284         "BicubicSurface::BicubicSurface()",
285         "Sample vectors must each be monotonically increasing.");
286 
287     // The grid data stores a Vec4 at each grid point with (possibly smoothed)
288     // function value f, and derivatives fx, fy, fxy in that order.
289     _ff.resize(nx, ny);
290 
291     _debug = false;
292 
293     // These temporaries are needed for indexing into the Spline functions.
294     Vector            coord(1);
295     const Array_<int> deriv1(1,0); // just one zero to pick 1st derivative
296 
297     // This temporary will hold either the original function values or the
298     // smoothed ones.
299     Matrix fSmooth(nx,ny);
300 
301     // Smoothing strategy: we want something that is symmetric in x and y
302     // so that you will get the same surface if you rotate the grid 90 degrees
303     // to exchange the meaning of x and y. To accomplish that, we smooth
304     // the grid separately along the rows and columns, and then average the
305     // results to produce a new grid to which we fit the surface.
306 
307     if (smoothness > 0) {
308         // This temp holds the function values as they look after smoothing
309         // in the x direction (that is, down the columns of constant y).
310         Matrix xf(nx,ny);
311 
312         // Smooth position data along lines of constant y.
313         for(int j=0; j < ny; ++j){
314             Spline_<Real> xspline = SplineFitter<Real>::fitForSmoothingParameter
315                                             (3,_x,af(j),smoothness).getSpline();
316             for(int i=0; i < nx; ++i){
317                 coord[0] = _x[i];
318                 xf(i,j) = xspline.calcValue(coord);
319             }
320         }
321 
322         // Smooth position data along lines of constant x, then average the
323         // result with the corresponding value from smoothing the other way.
324         for(int i=0; i < nx; ++i){
325             Spline_<Real> yspline = SplineFitter<Real>::fitForSmoothingParameter
326                                            (3,_y,~af[i],smoothness).getSpline();
327             for(int j=0; j < ny; ++j){
328                 coord[0] = _y[j];
329                 const Real yfij = yspline.calcValue(coord);
330                 fSmooth(i,j) = (xf(i,j) + yfij) / 2; // average xf,xy
331             }
332         }
333     } else {
334         // Not smoothing.
335         fSmooth = af;
336     }
337 
338     // Now fill in the f and fx entries in our internal grid by exactly
339     // fitting a spline to the already-smoothed data.
340     for(int j=0; j < ny; ++j){
341         Spline_<Real> xspline = SplineFitter<Real>::fitForSmoothingParameter
342                                         (3,_x,fSmooth(j),0).getSpline();
343         for(int i=0; i < nx; ++i){
344             coord[0] = _x[i];
345             Vec4& fij = _ff(i,j);
346             fij[F]  = fSmooth(i,j);
347             fij[Fx] = xspline.calcDerivative(deriv1,coord);
348         }
349     }
350 
351     // Compute fy and fxy by fitting splines along the rows.
352     // Note that we are using the already-smoothed value of f here.
353     Vector tmpRow(ny);
354     for(int i=0; i < nx; ++i){
355         // Fit splines along rows of constant x to go exactly through the
356         // already-smoothed sample points in order to get fy=Df/Dy.
357         Spline_<Real> yspline = SplineFitter<Real>::fitForSmoothingParameter
358                                                (3,_y,~fSmooth[i],0).getSpline();
359 
360         // Fit splines along rows of constant x to interpolate fx in the y
361         // direction to give fxy=Dfx/Dy.
362         for (int j=0; j<_y.size(); ++j) tmpRow[j] = _ff(i,j)[Fx];
363         Spline_<Real> ydxspline = SplineFitter<Real>::fitForSmoothingParameter
364                                                  (3,_y,tmpRow,0).getSpline();
365 
366         for(int j=0; j < ny; ++j){
367             coord[0] = _y[j];
368             Vec4& fij = _ff(i,j);
369             fij[Fy]  = yspline.calcDerivative(deriv1,coord);
370             fij[Fxy] = ydxspline.calcDerivative(deriv1,coord);
371         }
372     }
373 }
374 
375 // This is the advanced constructor where everything is known already.
Guts(const Vector & aX,const Vector & aY,const Matrix & af,const Matrix & afx,const Matrix & afy,const Matrix & afxy)376 BicubicSurface::Guts::Guts
377    (const Vector& aX, const Vector& aY, const Matrix& af,
378     const Matrix& afx, const Matrix& afy, const Matrix& afxy)
379 {
380     construct();
381 
382     _x.resize(aX.size()); _y.resize(aY.size());
383     _x = aX; _y = aY;
384 
385     _hasRegularSpacing = false;
386 
387     constructFromKnownFunction(af, afx, afy, afxy);
388 }
389 
Guts(const Vec2 & XY,const Vec2 & spacing,const Matrix & af,const Matrix & afx,const Matrix & afy,const Matrix & afxy)390 BicubicSurface::Guts::Guts
391    (const Vec2& XY, const Vec2& spacing, const Matrix& af,
392     const Matrix& afx, const Matrix& afy, const Matrix& afxy)
393 {
394     construct();
395 
396     // Check for reasonable spacing.
397     SimTK_ERRCHK2_ALWAYS(spacing > 0,
398         "BicubicSurface::BicubicSurface(XY,spacing,af,smoothness)",
399         "A BicubicSurface requires positive spacing in both x and y"
400         " but spacing was %g and %g.", spacing[0], spacing[1]);
401 
402     const int nx = af.nrow(), ny = af.ncol();
403     _x.resize(nx); _y.resize(ny);
404 
405     for (int i=0; i < nx; ++i)
406         _x[i] = XY[0] + i*spacing[0];
407     for (int j=0; j < ny; ++j)
408         _y[j] = XY[1] + j*spacing[1];
409 
410     _hasRegularSpacing = true;
411     _spacing = spacing;
412 
413     constructFromKnownFunction(af, afx, afy, afxy);
414 }
415 
416 // Expects _x and _y already to be filled in.
417 void BicubicSurface::Guts::
constructFromKnownFunction(const Matrix & af,const Matrix & afx,const Matrix & afy,const Matrix & afxy)418 constructFromKnownFunction
419    (const Matrix& af, const Matrix& afx, const Matrix& afy,
420     const Matrix& afxy)
421 {
422     const int nx = af.nrow(), ny = af.ncol();
423 
424     // Check for sufficient sample size.
425     SimTK_ERRCHK2_ALWAYS(nx >= 2 && ny >= 2,
426         "BicubicSurface::BicubicSurface(f,fx,fy,fxy)",
427         "A BicubicSurface requires at least 2 sample in x and y directions"
428         " but grid dimensions were %d and %d.", nx, ny);
429 
430     SimTK_ERRCHK4_ALWAYS(_x.size() == nx && _y.size() == ny,
431         "BicubicSurface::BicubicSurface(f,fx,fy,fxy)",
432         "Number of samples must match the grid dimension (%d X %d) but"
433         "the number of supplied sample points was %d X %d.",
434         nx, ny, _x.size(), _y.size());
435 
436     SimTK_ERRCHK2_ALWAYS(   afx.nrow()  == nx && afx.ncol()  == ny
437                          && afy.nrow()  == nx && afy.ncol()  == ny
438                          && afxy.nrow() == nx && afxy.ncol() == ny,
439         "BicubicSurface::BicubicSurface(f,fx,fy,fxy)",
440         "All the derivative sample matrices must match the grid dimension"
441         " (%d X %d).", nx, ny);
442 
443     SimTK_ERRCHK_ALWAYS(   isMonotonicallyIncreasing(_x)
444                         && isMonotonicallyIncreasing(_y),
445         "BicubicSurface::BicubicSurface(f,fx,fy,fxy)",
446         "Sample vectors must each be monotonically increasing.");
447 
448     _ff.resize(nx,ny);
449 
450     _debug = false;
451 
452     //Copy the data into our packed data structure.
453     for(int i=0; i < nx; ++i) {
454         for(int j=0; j < ny; ++j){
455             Vec4& fij = _ff(i,j);
456             fij[F]    = af(i,j);
457             fij[Fx]   = afx(i,j);
458             fij[Fy]   = afy(i,j);
459             fij[Fxy]  = afxy(i,j);
460         }
461     }
462 }
463 
464 //_____________________________________________________________________________
465 
calcValue(const Vec2 & aXY,PatchHint & hint) const466 Real BicubicSurface::Guts::calcValue(const Vec2& aXY, PatchHint& hint) const
467 {
468     getFdF(aXY,0,hint); // just function value
469     const PatchHint::Guts& h = hint.getGuts();
470     assert(h.xy == aXY && h.level >= 0);
471     return h.f;
472 }
473 
474 
calcDerivative(const Array_<int> & aDerivComponents,const Vec2 & aXY,PatchHint & hint) const475 Real BicubicSurface::Guts::calcDerivative
476    (const Array_<int>& aDerivComponents, const Vec2& aXY, PatchHint& hint) const
477 {
478     const int wantLevel = (int)aDerivComponents.size();
479 
480     if (wantLevel == 0)
481         return calcValue(aXY, hint);  // "0th" deriv is the function value
482 
483     for (int i=0; i < wantLevel; ++i) {
484         SimTK_ERRCHK2_ALWAYS(aDerivComponents[i]==0 || aDerivComponents[i]==1,
485             "BicubicSurface::calcDerivative()",
486             "Component %d was %d but must be 0 or 1 for x or y.",
487             i, aDerivComponents[i]);
488     }
489 
490     if (wantLevel > 3)
491         return 0;   // 4th and higher derivatives are all zero
492 
493     getFdF(aXY, wantLevel, hint);
494     const PatchHint::Guts& h = hint.getGuts();
495     assert(h.xy == aXY && h.level >= wantLevel);
496 
497     if (aDerivComponents.size() == 1)
498         return aDerivComponents[0]==0 ? h.fx : h.fy;            // fx : fy
499 
500     if (aDerivComponents.size() == 2) {
501         if (aDerivComponents[0]==0) //x
502             return aDerivComponents[1]==0 ? h.fxx : h.fxy;      // fxx:fxy
503         else //y (fyx==fxy)
504             return aDerivComponents[1]==0 ? h.fxy : h.fyy;      // fyx:fyy
505     }
506 
507     // Third derivative.
508     if (aDerivComponents[0]==0) { //x
509         if (aDerivComponents[1]==0) // xx
510             return aDerivComponents[2]==0 ? h.fxxx : h.fxxy;    // fxxx:fxxy
511         else // xy (fxyx==fxxy)
512             return aDerivComponents[2]==0 ? h.fxxy : h.fxyy;    // fxyx:fxyy
513     } else { //y (fyx==fxy)
514         if (aDerivComponents[1]==0) // yx (fyxx==fxxy, fyxy==fxyy)
515             return aDerivComponents[2]==0 ? h.fxxy : h.fxyy;    // fyxx:fyxy
516         else // yy (fyyx==fxyy)
517             return aDerivComponents[2]==0 ? h.fxyy : h.fyyy;    // fyyx:fyyy
518     }
519 }
520 
521 // Cost is patch evaluation + about 200 flops.
calcParaboloid(const Vec2 & aXY,PatchHint & hint,Transform & X_SP,Vec2 & k) const522 void BicubicSurface::Guts::calcParaboloid
523    (const Vec2& aXY, PatchHint& hint, Transform& X_SP, Vec2& k) const
524 {
525     getFdF(aXY, 2, hint); // calculate through 2nd derivatives
526     const PatchHint::Guts& h = hint.getGuts();
527     assert(h.xy == aXY && h.level >= 2);
528 
529     const Vec3 P = aXY.append1(h.f);
530     const Vec3 dPdx(1,0,h.fx);
531     const Vec3 dPdy(0,1,h.fy);
532     const UnitVec3 nn(-h.fx, -h.fy, 1); // dPdx X dPdy (normalizing, ~35 flops)
533     const Vec3 d2Pdx2(0,0,h.fxx);
534     const Vec3 d2Pdy2(0,0,h.fyy);
535     const Vec3 d2Pdxdy(0,0,h.fxy);
536 
537     // TODO: could save a little time here by taking advantage of the known
538     // sparsity of these vectors (probably not worth the trouble).
539     k = ContactGeometry::evalParametricCurvature
540                                 (P,nn,dPdx,dPdy,d2Pdx2,d2Pdy2,d2Pdxdy,X_SP);
541 }
542 
isSurfaceDefined(const Vec2 & XYval) const543 bool BicubicSurface::Guts::isSurfaceDefined(const Vec2& XYval) const
544 {
545     const bool valueDefined =
546             (_x[0] <= XYval[0] &&  XYval[0] <= _x[_x.size()-1])
547         &&  (_y[0] <= XYval[1] &&  XYval[1] <= _y[_y.size()-1]);
548 
549     return valueDefined;
550 }
551 
552 /* This function ensures that the given hint contains correct information for
553 the patch indexed (x0,y0). */
554 void BicubicSurface::Guts::
getPatchInfoIfNeeded(int x0,int y0,BicubicSurface::PatchHint::Guts & h) const555 getPatchInfoIfNeeded(int x0, int y0, BicubicSurface::PatchHint::Guts& h) const {
556     const int x1 = x0+1, y1 = y0+1;
557 
558     // Compute Bicubic coefficients only if we're in a new patch
559     // else use the old ones, because this is an expensive step!
560     if( !(h.x0 == x0 && h.y0 == y0) ) {
561         // The hint is no good at all since it is for the wrong patch.
562         h.clear();
563         h.x0 = x0; h.y0 = y0;
564 
565         // Compute the scaling of the new patch. Note that neither patch
566         // dimension can be zero since we don't allow duplicates in x or y.
567         h.xS = _x(x1)-_x(x0);
568         h.yS = _y(y1)-_y(y0);
569         h.ooxS = 1/h.xS; h.ooxS2 = h.ooxS*h.ooxS; h.ooxS3=h.ooxS*h.ooxS2;
570         h.ooyS = 1/h.yS; h.ooyS2 = h.ooyS*h.ooyS; h.ooyS3=h.ooyS*h.ooyS2;
571 
572         // Form the vector f and multiply Ainv*f to form coefficient vector a.
573 
574         const Vec4& f00 = _ff(x0,y0);
575         const Vec4& f01 = _ff(x0,y1);
576         const Vec4& f10 = _ff(x1,y0);
577         const Vec4& f11 = _ff(x1,y1);
578 
579         h.fV[0] = f00[F];
580         h.fV[1] = f10[F];
581         h.fV[2] = f01[F];
582         h.fV[3] = f11[F];
583 
584         // Can't precalculate these scaled values because the same grid point
585         // is used for up to four different patches, each scaled differently.
586         h.fV[4] = f00[Fx]*h.xS;
587         h.fV[5] = f10[Fx]*h.xS;
588         h.fV[6] = f01[Fx]*h.xS;
589         h.fV[7] = f11[Fx]*h.xS;
590 
591         h.fV[8]  = f00[Fy]*h.yS;
592         h.fV[9]  = f10[Fy]*h.yS;
593         h.fV[10] = f01[Fy]*h.yS;
594         h.fV[11] = f11[Fy]*h.yS;
595 
596         h.fV[12]  = f00[Fxy]*h.xS*h.yS;
597         h.fV[13]  = f10[Fxy]*h.xS*h.yS;
598         h.fV[14]  = f01[Fxy]*h.xS*h.yS;
599         h.fV[15]  = f11[Fxy]*h.xS*h.yS;
600 
601         getCoefficients(h.fV,h.a);
602     }
603 }
604 
605 
606 /**
607 This function computes the surface value and all derivatives (because it is
608 cheap to compute these derivatives once the bicubic interpolation
609 coefficients have been solved for). These values are stored in mutable
610 function members so that if a user repeatedly asks for information about
611 the same location the stored data is supplied. Additionally, if the user
612 is staying within a single patch, the the coefficients required for this
613 patch are only computed once, and they are re-used until a location outside
614 the patch is requested.
615 
616 @param aXY the X,Y location of interest
617 @param fV   an empty vector that is set to the values of f,fx,fy and fxy of
618             the patch corners
619                 f(0,0)   f(1,0)   f(0,1)   f(1,1),
620                 fx(0,0)  fx(1,0)  fx(0,1)  fx(1,1),
621                 fy(0,0)  fy(1,0)  fy(0,1)  fy(1,1),
622                 fxy(0,0) fxy(1,0) fxy(0,1) fxy(1,1)
623 @param aijV an empty vector that is set to the coefficient values of the
624             bicubic surface polynomials for the patch that the current
625             XY location resides in. The coefficients are returned in this
626             order
627 
628             a00,a10,a20,a30,a01,a11,a21,a31,a02,a12,a22,a32,a03,a13,a23,a33
629 
630 @param aFdF an empty vector that is set to the 10 values of f and its partial
631 derivatives at the point XY. These values are stored in the following order:
632             f(x,y) fx(x,y)  fy(x,y)  fxy(x,y)  fxx(x,y)  fyy(x,y)
633             fxxx(x,y) fxxy(x,y) fyyy(x,y) fxyy(x,y)
634 */
635 void BicubicSurface::Guts::
getFdF(const Vec2 & aXY,int wantLevel,PatchHint & hint) const636 getFdF(const Vec2& aXY, int wantLevel, PatchHint& hint) const {
637     ++numAccesses; // All surface accesses come through here.
638 
639     //0. Check if the surface is defined for the XY value given.
640     SimTK_ERRCHK6_ALWAYS(isSurfaceDefined(aXY),
641         "BicubicSurface::getFdF (private fcn)",
642         "BicubicSurface is not defined at requested location (%g,%g)."
643         " The surface is valid from x[%g %g], y[%g %g].", aXY(0), aXY(1),
644         _x[0], _x[_x.size()-1], _y[0], _y[_y.size()-1]);
645 
646     // -1 means just do the patch
647     // 0 means patch and function value
648     // 1 means add 1st deriv, 2 is 2nd, 3 is 3rd
649     assert(-1 <= wantLevel && wantLevel <= 3);
650 
651     BicubicSurface::PatchHint::Guts& h = hint.updGuts();
652 
653     //1. Check to see if we have already computed values for the requested point.
654     if(h.level >= wantLevel && aXY == h.xy){
655         ++numAccessesSamePoint;
656         return;
657     }
658 
659     // Nope. We're at least changing points.
660     h.xy = aXY;
661     h.level = -1; // we don't know anything about this point
662 
663     // We're going to feed calcLowerBoundIndex() our best guess as to the
664     // patch this point is on. For regularly-spaced grid points we can find
665     // it exactly, except for some possible roundoff. Otherwise the best we
666     // can do is supply the current index from the hint.
667     int pXidx = h.x0, pYidx = h.y0;
668     if (_hasRegularSpacing) {
669         pXidx = clamp(0, (int)std::floor((h.xy[0]-_x[0])/_spacing[0]),
670                       _x.size()-2); // can't be last index
671         pYidx = clamp(0, (int)std::floor((h.xy[1]-_y[0])/_spacing[1]),
672                       _y.size()-2);
673     }
674 
675     // Compute the indices that define the patch containing this value.
676     int howResolvedX, howResolvedY;
677     const int x0 = calcLowerBoundIndex(_x,aXY[0],pXidx,howResolvedX);
678     const int x1 = x0+1;
679     const int y0 = calcLowerBoundIndex(_y,aXY[1],pYidx,howResolvedY);
680     const int y1 = y0+1;
681 
682     // 0->same patch, 1->nearby patch, 2->had to search
683     const int howResolved = std::max(howResolvedX, howResolvedY);
684     if      (howResolved == 0) ++numAccessesSamePatch;
685     else if (howResolved == 1) ++numAccessesNearbyPatch;
686 
687     // Compute Bicubic coefficients only if we're in a new patch
688     // else use the old ones, because this is an expensive step!
689     getPatchInfoIfNeeded(x0,y0,h);
690 
691     // At this point we know that the hint contains valid patch information,
692     // but it contains no valid point information.
693 
694     if (wantLevel == -1)
695         return; // caller just wanted patch info
696 
697     const Vec<16>& a = h.a; // abbreviate for convenience
698 
699 
700     // Compute where in the patch we are. This has to
701     // be done everytime the location within the patch changes
702     // ... which has happened if this code gets executed.
703 
704     //--------------------------------------------------------------------------
705     // Evaluate function value f (38 flops).
706 
707     // 8 flops
708     const Real xpt = (aXY(0)-_x(x0))*h.ooxS, xpt2=xpt*xpt, xpt3=xpt*xpt2;
709     const Real ypt = (aXY(1)-_y(y0))*h.ooyS, ypt2=ypt*ypt, ypt3=ypt*ypt2;
710     // 12 flops
711     const Mat44 mx(a[ 0],   a[ 1]*xpt,   a[ 2]*xpt2,   a[ 3]*xpt3,
712                    a[ 4],   a[ 5]*xpt,   a[ 6]*xpt2,   a[ 7]*xpt3,
713                    a[ 8],   a[ 9]*xpt,   a[10]*xpt2,   a[11]*xpt3,
714                    a[12],   a[13]*xpt,   a[14]*xpt2,   a[15]*xpt3);
715     // 12 flops
716     const Vec4 xsum = mx.rowSum();
717     // 6 flops
718     h.f = xsum[0] + ypt*xsum[1] + ypt2*xsum[2] + ypt3*xsum[3];
719     h.level = 0; // function value is ready
720     if (wantLevel == 0)
721         return;
722 
723     //--------------------------------------------------------------------------
724     // Evaluate first derivatives fx, fy (43 flops).
725 
726     // fy is 9 flops
727     const Real dypt = h.ooyS, dypt2= 2*ypt*h.ooyS, dypt3= 3*ypt2*h.ooyS;
728     h.fy = dypt*xsum[1] + dypt2*xsum[2] + dypt3*xsum[3];
729 
730     // fx is 34 flops
731     const Real dxpt=h.ooxS, dxpt2=2*xpt*h.ooxS, dxpt3=3*xpt2*h.ooxS;
732     const Mat43 mdx(a[ 1]*dxpt,    a[ 2]*dxpt2,    a[ 3]*dxpt3,
733                     a[ 5]*dxpt,    a[ 6]*dxpt2,    a[ 7]*dxpt3,
734                     a[ 9]*dxpt,    a[10]*dxpt2,    a[11]*dxpt3,
735                     a[13]*dxpt,    a[14]*dxpt2,    a[15]*dxpt3);
736     const Vec4 dxsum = mdx.rowSum();
737     h.fx   = dxsum[0] + ypt*dxsum[1] + ypt2*dxsum[2] + ypt3*dxsum[3];
738     h.level = 1; // first derivatives are ready
739     if (wantLevel == 1)
740         return;
741 
742 
743     //--------------------------------------------------------------------------
744     // Evaluate second derivatives fxy, fxx, fyy (40 flops).
745 
746     // fxy, fyy are 11 flops
747     h.fxy = dypt*dxsum[1] + dypt2*dxsum[2] + dypt3*dxsum[3];
748     const Real dyypt2=2*h.ooyS2, dyypt3=6*ypt*h.ooyS2;
749     h.fyy = dyypt2*xsum[2] + dyypt3*xsum[3];
750 
751     // fxx is 29 flops
752     const Real dxxpt2=2*h.ooxS2, dxxpt3=6*xpt*h.ooxS2;
753     const Mat42 mdxx(a[ 2]*dxxpt2,    a[ 3]*dxxpt3,
754                      a[ 6]*dxxpt2,    a[ 7]*dxxpt3,
755                      a[10]*dxxpt2,    a[11]*dxxpt3,
756                      a[14]*dxxpt2,    a[15]*dxxpt3);
757     const Vec4 dxxsum = mdxx.rowSum();
758     h.fxx  = dxxsum[0] + ypt*dxxsum[1] + ypt2*dxxsum[2] + ypt3*dxxsum[3];
759     h.level = 2; // second derivatives are ready
760     if (wantLevel == 2)
761         return;
762 
763     //--------------------------------------------------------------------------
764     // Evaluate third derivatives fxxx, fxxy, fyyy, fxyy (21 flops).
765 
766     // 10 flops
767     const Real dyyypt3=6*h.ooyS3;
768     h.fyyy = dyyypt3*xsum[3];
769     h.fxyy = dyypt2*dxsum[2] + dyypt3*dxsum[3];
770     h.fxxy = dypt*dxxsum[1] + dypt2*dxxsum[2] + dypt3*dxxsum[3];
771 
772     // 11 flops
773     const Real dxxxpt3=6*h.ooxS3;
774     const Vec4 mdxxx(a[ 3]*dxxxpt3,
775                      a[ 7]*dxxxpt3,
776                      a[11]*dxxxpt3,
777                      a[15]*dxxxpt3);
778     h.fxxx = mdxxx[0] + ypt* mdxxx[1] + ypt2*mdxxx[2] + ypt3*mdxxx[3];
779     h.level = 3; // third derivatives are ready
780 
781     if(_debug == true){
782         cout<<" getFdF" << endl;
783         cout <<"Member variables" <<endl;
784         cout << "_x" << _x << endl;
785         cout << "\n"<<endl;
786         cout << "_y" << _y << endl;
787         cout << "\n"<<endl;
788         cout << "_ff" << _ff << endl;
789         cout << "\n"<<endl;
790 
791         cout <<" Intermediate variables " << endl;
792         cout <<"XY: " << aXY << endl;
793         printf("[x0 x1], [y0 y1]: [%d %d],[%d %d]\n",x0,x1,y0,y1);
794         printf("[x0V x1V], [y0V y1V]: [%f %f],[%f %f]\n",_x(x0),_x(x1),_y(y0),_y(y1));
795         cout <<" xS " << h.xS << " yS " << h.yS << endl;
796         printf("(xp,yp): (%f %f)\n",xpt,ypt);
797         cout << "\n\n\n"<<endl;
798     }
799 }
800 
801 
802 /** Given a search value aVal and an n-vector aVec (n>=2) containing
803 monotonically increasing values (no duplicates), this method finds the unique
804 pair of indices (i,i+1) such that either
805     - aVec[i] <= aVal < aVec[i+1], or
806     - aVec[n-2] < aVal == aVec[n-1].
807 
808 The following preconditions must be satisfied by the arguments to this
809 method:
810     - aVec.size() >= 2
811     - aVal >= aVec[0]
812     - aVal <= aVec[n-1]
813 
814 
815 aVec = [0.1 0.2 0.3 0.4 0.5];
816 aVal = 0.125
817 idxLB = calcLowerBoundIndex(aVec,aVal,-1);
818 
819 Then idxLB should be 0. Some effort has been put into making this code
820 efficient, as  it is expected that very large vectors could be used in this
821 function. If the data is evenly spaced, the index is computed. If not data
822 near a previous index (set by the user) is searched. If the data is still
823 not found a binary search is performed
824 
825 @params aVec: A SimTK vector containing monotonically increasing data
826 @params aVal: A value bounded by the numbers in the vector
827 @params pIdx: A hint index provided by the user (of this function)
828 @returns int: The index of the entry in the vector that is the as close to
829             aVal without exceeding it.
830 
831 */
832 
833 // Return true if aVal is on the patch (really the line) between
834 // aVec[indxL] and aVec[indxL+1]. By "on the patch" we mean that
835 // aVec[indxL] <= aVal < aVec[indxL+1] unless this is the last patch in
836 // which case we allow aVec[indxL] < aVal <= aVec[indxL+1].
isOnPatch(const Vector & aVec,int indxL,Real aVal)837 static bool isOnPatch(const Vector& aVec, int indxL, Real aVal) {
838     assert(aVec.size() >= 2);
839     const int maxLB = aVec.size() - 2;
840     assert(0 <= indxL && indxL <= maxLB);
841 
842     const Real low = aVec[indxL], high = aVec[indxL+1];
843 
844     if (aVal < low || aVal > high)
845         return false;
846 
847     // Here we know low <= aVal <= high.
848     if (aVal < high)
849         return true;
850 
851     // Here we know low < aVal == high. This is only allowed on the last patch.
852     return indxL == maxLB;
853 }
854 
855 // howResolved: 0->same patch, 1->nearby patch, 2->search
856 int BicubicSurface::Guts::
calcLowerBoundIndex(const Vector & aVec,Real aVal,int pIdx,int & howResolved) const857 calcLowerBoundIndex(const Vector& aVec, Real aVal, int pIdx,
858                     int& howResolved) const {
859     assert(aVec.size() >= 2);
860 
861     // Because we're trying to find the lower index, it can't be the very
862     // last knot.
863     const int maxLB = aVec.size() - 2;
864 
865     // 1. Do a local search around the previous index, if one is given.
866     if(0 <= pIdx && pIdx <= maxLB) {
867         // Are we still on the same patch? Caution -- can't be equal to the
868         // upper knot unless it is the last one.
869         if (isOnPatch(aVec, pIdx, aVal)) {
870             howResolved = 0;
871             return pIdx;
872         }
873 
874         // Not on the same patch, how about adjacent patches?
875         if (aVal < aVec[pIdx]) {
876             // Value moved below the current patch.
877             if (pIdx > 0 && isOnPatch(aVec, pIdx-1, aVal)) {
878                 howResolved = 1;
879                 return pIdx-1; // found it next door!
880             }
881         } else if (aVal >= aVec[pIdx+1]) {
882             // Value moved above the current patch.
883             if (pIdx < maxLB && isOnPatch(aVec, pIdx+1, aVal)) {
884                 howResolved = 1;
885                 return pIdx+1; // found it next door!
886             }
887         }
888     }
889 
890     // Either we didn't have a previous index to try, or it didn't help.
891 
892     // 2. Check the end points. We'll count these as "nearby patches" since
893     // they are about the same amount of work.
894     if (aVal <= aVec[0]) {
895         howResolved = 1;
896         return 0;
897     }
898     if (aVal >= aVec[maxLB+1])  {
899         howResolved = 1;
900         return maxLB;
901     }
902 
903     // 3. If still not found, use binary search to find the appropriate index.
904 
905     // std::upper_bound returns the index of the first element that
906     // is strictly greater than aVal (one after the last element
907     // if aVal is exactly equal to the last knot).
908     const Real* upper =
909         std::upper_bound(&aVec[0], &aVec[0] + aVec.size(), aVal);
910     const int upperIx = clamp(0, (int)(upper-&aVec[1]), maxLB);
911 
912     howResolved = 2;
913     return upperIx;
914 }
915 
916 /**
917 This function will compute the coefficients aij for a bicubic patch that has
918 the values of f, fx, fy and fxy defined at its four corners. This function is
919 relatively expensive, as it involves multiplying a 16x16 matrix by a 16x1,
920 so it is called only when absolutely necessary.
921 
922 @param f  Vector f defining the values of f, fx, fy and fxy at the four
923             corners of the patch. Vector f is in this order:
924             f(0,0)   f(1,0)   f(0,1)   f(1,1),
925             fx(0,0)  fx(1,0)  fx(0,1)  fx(1,1),
926             fy(0,0)  fy(1,0)  fy(0,1)  fy(1,1),
927             fxy(0,0) fxy(1,0) fxy(0,1) fxy(1,1)
928 @param aV  An empty vector for which the coefficients aij are written into
929                 in the following order:
930 
931                 a00,a10,a20,a30,a01,a11,a21,a31,a02,a12,a22,a32,a03,a13,a23,a33
932 */
933 void BicubicSurface::Guts::
getCoefficients(const Vec<16> & fV,Vec<16> & aV) const934 getCoefficients(const Vec<16>& fV, Vec<16>& aV) const {
935     // This is what the full matrix inverse looks like (copied here from
936     // Wikipedia). It is very sparse and contains only a few unique values
937     // so the matrix-vector product can be done very cheaply if worked out
938     // in painstaking detail (with some help from Maple). The full
939     // matrix-vector product takes 496 flops; we can do it in 80.
940     /*
941     const Real Ainv[] = {
942         1, 0, 0, 0,     0, 0, 0, 0,     0, 0, 0, 0,     0, 0, 0, 0,
943         0, 0, 0, 0,     1, 0, 0, 0,     0, 0, 0, 0,     0, 0, 0, 0,
944        -3, 3, 0, 0,    -2,-1, 0, 0,     0, 0, 0, 0,     0, 0, 0, 0,
945         2,-2, 0, 0,     1, 1, 0, 0,     0, 0, 0, 0,     0, 0, 0, 0,
946         0, 0, 0, 0,     0, 0, 0, 0,     1, 0, 0, 0,     0, 0, 0, 0,
947         0, 0, 0, 0,     0, 0, 0, 0,     0, 0, 0, 0,     1, 0, 0, 0,
948         0, 0, 0, 0,     0, 0, 0, 0,    -3, 3, 0, 0,    -2,-1, 0, 0,
949         0, 0, 0, 0,     0, 0, 0, 0,     2,-2, 0, 0,     1, 1, 0, 0,
950        -3, 0, 3, 0,     0, 0, 0, 0,    -2, 0,-1, 0,     0, 0, 0, 0,
951         0, 0, 0, 0,    -3, 0, 3, 0,     0, 0, 0, 0,    -2, 0,-1, 0,
952         9,-9,-9, 9,     6, 3,-6,-3,     6,-6, 3,-3,     4, 2, 2, 1,
953        -6, 6, 6,-6,    -3,-3, 3, 3,    -4, 4,-2, 2,    -2,-2,-1,-1,
954         2, 0,-2, 0,     0, 0, 0, 0,     1, 0, 1, 0,     0, 0, 0, 0,
955         0, 0, 0, 0,     2, 0,-2, 0,     0, 0, 0, 0,     1, 0, 1, 0,
956        -6, 6, 6,-6,    -4,-2, 4, 2,    -3, 3,-3, 3,    -2,-1,-2,-1,
957         4,-4,-4, 4,     2, 2,-2,-2,     2,-2, 2,-2,     1, 1, 1, 1
958     };
959     Mat<16,16> AinvM(Ainv);
960     aV = AinvM*fV; // So cool that I can do this in C++! Go Sherm!
961     */
962 
963 
964     // Matt's masterful Maple work, plus a little manual hacking by Sherm
965     // produced this version:
966 
967     // Caution: note index change.
968     Real f1=fV[0], f2=fV[1], f3=fV[2], f4=fV[3], f5=fV[4], f6=fV[5],
969          f7=fV[6], f8=fV[7], f9=fV[8], f10=fV[9], f11=fV[10], f12=fV[11],
970          f13=fV[12], f14=fV[13], f15=fV[14], f16=fV[15];
971 
972     // 54 add/subtract
973     // 26 multiplies
974     Real f86 = f8-f6, f1211=f12-f11, f109=f10-f9, f75=f7-f5;
975     Real f1234 = f1-f2-f3+f4;
976     Real t312 = 2*f86;
977     Real t311 = 2*f1211;
978     Real t310 = 3*f86 - 2*f14;
979     Real t309 = 3*f1211 - 2*f15;
980     Real t10 = 2*f9;
981     Real t308 = f14 - 2*f10 + t10;
982     Real t307 = 3*f109 - f14;
983     Real t306 = 3*f75 - f15;
984     Real t15 = 2*f5;
985     Real t305 = t15 - 2*f7 + f13 + f15;
986     Real t289 = -2*f13;
987     Real t304 = t289 - 6*f1234 - f16;
988     Real t302 = -3*f1;
989     Real t301 = 2*f1;
990     aV(0) = f1;
991     aV(1) = f5;
992     aV(2) = t302 + 3*f2 - t15 - f6;
993     aV(3) = t301 - 2*f2 + f5 + f6;
994     aV(4) = f9;
995     aV(5) = f13;
996     aV(6) = t289 + t307;
997     aV(7) = f13 + t308;
998     aV(8) = t302 + 3*f3 - t10 - f11;
999     aV(9) = t289 + t306;
1000     aV(10) = 9*f1234 + 6*(f5-f7+f9-f10) + 4*f13 + f16 - t309 - t310;
1001     aV(11) = 4*f109 + t304 + t306 + t310 + t311;
1002     aV(12) = t301 - 2*f3 + f9 + f11;
1003     aV(13) = t305;
1004     aV(14) = 4*f75  + t304 + t307 + t309 + t312;
1005     aV(15) = 4*f1234 + f16 + t305 + t308 - t311 - t312;
1006 
1007     if(_debug == true){
1008         cout << "getCoefficients" << endl;
1009         cout << "\tfV" << fV << endl;
1010         cout << "\taijV" << aV << endl;
1011     }
1012 }
1013 
1014 // We're going to generate quads in strips like this:
1015 //    *  <- *  <-  *  <-   *
1016 //    v    ^ v    ^ v      ^
1017 //    * ->  *  ->  *  ->   *
1018 // Each patch will generate the same number of quads so there will be more
1019 // quads where the surface is denser.
1020 void BicubicSurface::Guts::
createPolygonalMesh(Real resolution,PolygonalMesh & mesh) const1021 createPolygonalMesh(Real resolution, PolygonalMesh& mesh) const {
1022     PatchHint hint;
1023     // Number of patches in x and y direction.
1024     const int nxpatch = _x.size()-1;
1025     const int nypatch = _y.size()-1;
1026     // n is the number of subdivisions per patch
1027     const int n = std::max(1 +  (int)(resolution+.5), 1); // round
1028     const int nx = nxpatch*n + 1; // number of vertices along each row
1029     const int ny = nypatch*n + 1;
1030     // These will alternately serve as previous and current.
1031     Array_<int> row1(ny), row2(ny);
1032     Array_<int>* prevRowVerts = &row1;
1033     Array_<int>* curRowVerts  = &row2;
1034     Vector xVals(nx), yVals(ny);
1035 
1036     // Calculate the x and y sample values.
1037     int nxt = 0;
1038     for (int px=0; px < nxpatch; ++px) {
1039         const Real xlo = _x[px], xhi = _x[px+1];
1040         const Real xs = xhi - xlo, width = xs/n;
1041         // For each quad within patch px
1042         for (int qx=0; qx < n; ++qx)
1043             xVals[nxt++] = xlo + qx*width;
1044     }
1045     xVals[nxt++] = _x[_x.size()-1];
1046     assert(nxt == nx);
1047 
1048     nxt = 0;
1049     for (int py=0; py < nypatch; ++py) {
1050         const Real ylo = _y[py], yhi = _y[py+1];
1051         const Real ys = yhi - ylo, width = ys/n;
1052         // For each quad within patch py
1053         for (int qx=0; qx < n; ++qx)
1054             yVals[nxt++] = ylo + qx*width;
1055     }
1056     yVals[nxt++] = _y[_y.size()-1];
1057     assert(nxt == ny);
1058 
1059     // Fill in the zeroth row.
1060     Vec2 pt; pt[0] = xVals[0];
1061     for (int j=0; j < ny; ++j) {
1062         pt[1] = yVals[j];
1063         const Real z = calcValue(pt, hint);
1064         (*prevRowVerts)[j] = mesh.addVertex(Vec3(pt[0],pt[1],z));
1065     }
1066 
1067     // For each remaining row, generate vertices and then a strip of faces.
1068     Array_<int> face(4);
1069     for (int i=1; i < nx; ++i) {
1070         Vec2 pt; pt[0] = xVals[i];
1071         for (int j=0; j < ny; ++j) {
1072             pt[1] = yVals[j];
1073             const Real z = calcValue(pt, hint);
1074             (*curRowVerts)[j] = mesh.addVertex(Vec3(pt[0],pt[1],z));
1075         }
1076         for (int j=1; j < ny; ++j) {
1077             face[0] = (*curRowVerts)[j-1]; // counterclockwise
1078             face[1] = (*curRowVerts)[j];
1079             face[2] = (*prevRowVerts)[j];
1080             face[3] = (*prevRowVerts)[j-1];
1081             mesh.addFace(face);
1082         }
1083         std::swap(prevRowVerts, curRowVerts);
1084     }
1085 }
1086 
1087 //==============================================================================
1088 //                     BICUBIC SURFACE :: PATCH HINT
1089 //==============================================================================
1090 
1091 // Here we ensure that there is always a valid PatchHint::Guts object present
1092 // and forward all operations to it.
1093 
PatchHint()1094 BicubicSurface::PatchHint::PatchHint() : guts(new PatchHint::Guts) {}
~PatchHint()1095 BicubicSurface::PatchHint::~PatchHint() {delete guts;}
1096 
PatchHint(const PatchHint & src)1097 BicubicSurface::PatchHint::PatchHint(const PatchHint& src)
1098 :   guts(new PatchHint::Guts(*src.guts)) {}
1099 
1100 BicubicSurface::PatchHint&
operator =(const PatchHint & src)1101 BicubicSurface::PatchHint::operator=(const PatchHint& src)
1102 {   *guts = *src.guts; return *this; }
1103 
isEmpty() const1104 bool BicubicSurface::PatchHint::isEmpty() const {return guts->isEmpty();}
clear()1105 void BicubicSurface::PatchHint::clear()         {guts->clear();}
1106 
1107 
1108 
1109 
1110