Point Cloud Library (PCL)  1.11.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages
bspline_data.hpp
1 /*
2 Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
7 
8 Redistributions of source code must retain the above copyright notice, this list of
9 conditions and the following disclaimer. Redistributions in binary form must reproduce
10 the above copyright notice, this list of conditions and the following disclaimer
11 in the documentation and/or other materials provided with the distribution.
12 
13 Neither the name of the Johns Hopkins University nor the names of its contributors
14 may be used to endorse or promote products derived from this software without specific
15 prior written permission.
16 
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
18 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES
19 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
20 SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
22 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
26 DAMAGE.
27 */
28 
29 #include "poisson_exceptions.h"
30 #include "binary_node.h"
31 
32 namespace pcl
33 {
34  namespace poisson
35  {
36 
37  /////////////////
38  // BSplineData //
39  /////////////////
40  // Support[i]:
41  // Odd: i +/- 0.5 * ( 1 + Degree )
42  // i - 0.5 * ( 1 + Degree ) < 0
43  // <=> i < 0.5 * ( 1 + Degree )
44  // i + 0.5 * ( 1 + Degree ) > 0
45  // <=> i > - 0.5 * ( 1 + Degree )
46  // i + 0.5 * ( 1 + Degree ) > r
47  // <=> i > r - 0.5 * ( 1 + Degree )
48  // i - 0.5 * ( 1 + Degree ) < r
49  // <=> i < r + 0.5 * ( 1 + Degree )
50  // Even: i + 0.5 +/- 0.5 * ( 1 + Degree )
51  // i - 0.5 * Degree < 0
52  // <=> i < 0.5 * Degree
53  // i + 1 + 0.5 * Degree > 0
54  // <=> i > -1 - 0.5 * Degree
55  // i + 1 + 0.5 * Degree > r
56  // <=> i > r - 1 - 0.5 * Degree
57  // i - 0.5 * Degree < r
58  // <=> i < r + 0.5 * Degree
59  template< int Degree > inline bool LeftOverlap( unsigned int depth , int offset )
60  {
61  offset <<= 1;
62  if( Degree & 1 ) return (offset < 1+Degree) && (offset > -1-Degree );
63  else return (offset < Degree) && (offset > -2-Degree );
64  }
65  template< int Degree > inline bool RightOverlap( unsigned int depth , int offset )
66  {
67  offset <<= 1;
68  int r = 1<<(depth+1);
69  if( Degree & 1 ) return (offset > 2-1-Degree) && (offset < 2+1+Degree );
70  else return (offset > 2-2-Degree) && (offset < 2+ Degree );
71  }
72  template< int Degree > inline int ReflectLeft( unsigned int depth , int offset )
73  {
74  if( Degree&1 ) return -offset;
75  else return -1-offset;
76  }
77  template< int Degree > inline int ReflectRight( unsigned int depth , int offset )
78  {
79  int r = 1<<(depth+1);
80  if( Degree&1 ) return r -offset;
81  else return r-1-offset;
82  }
83 
84  template< int Degree , class Real >
86  {
87  vvDotTable = dvDotTable = ddDotTable = NULL;
88  valueTables = dValueTables = NULL;
89  baseFunctions = NULL;
90  baseBSplines = NULL;
91  functionCount = sampleCount = 0;
92  }
93 
94  template< int Degree , class Real >
96  {
97  if( functionCount )
98  {
99  delete[] vvDotTable;
100  delete[] dvDotTable;
101  delete[] ddDotTable;
102 
103  delete[] valueTables;
104  delete[] dValueTables;
105 
106  delete[] baseFunctions;
107  delete[] baseBSplines;
108  }
109  vvDotTable = dvDotTable = ddDotTable = NULL;
110  valueTables = dValueTables=NULL;
111  baseFunctions = NULL;
112  baseBSplines = NULL;
113  functionCount = 0;
114  }
115 
116  template<int Degree,class Real>
117  void BSplineData<Degree,Real>::set( int maxDepth , bool useDotRatios , bool reflectBoundary )
118  {
119  this->useDotRatios = useDotRatios;
120  this->reflectBoundary = reflectBoundary;
121 
122  depth = maxDepth;
123  // [Warning] This assumes that the functions spacing is dual
124  functionCount = BinaryNode< double >::CumulativeCenterCount( depth );
126  baseFunctions = new PPolynomial<Degree>[functionCount];
127  baseBSplines = new BSplineComponents[functionCount];
128 
129  baseFunction = PPolynomial< Degree >::BSpline();
130  for( int i=0 ; i<=Degree ; i++ ) baseBSpline[i] = Polynomial< Degree >::BSplineComponent( i ).shift( double(-(Degree+1)/2) + i - 0.5 );
131  dBaseFunction = baseFunction.derivative();
132  StartingPolynomial< Degree > sPolys[Degree+3];
133 
134  for( int i=0 ; i<Degree+3 ; i++ )
135  {
136  sPolys[i].start = double(-(Degree+1)/2) + i - 1.5;
137  sPolys[i].p *= 0;
138  if( i<=Degree ) sPolys[i].p += baseBSpline[i ].shift( -1 );
139  if( i>=1 && i<=Degree+1 ) sPolys[i].p += baseBSpline[i-1];
140  for( int j=0 ; j<i ; j++ ) sPolys[i].p -= sPolys[j].p;
141  }
142  leftBaseFunction.set( sPolys , Degree+3 );
143  for( int i=0 ; i<Degree+3 ; i++ )
144  {
145  sPolys[i].start = double(-(Degree+1)/2) + i - 0.5;
146  sPolys[i].p *= 0;
147  if( i<=Degree ) sPolys[i].p += baseBSpline[i ];
148  if( i>=1 && i<=Degree+1 ) sPolys[i].p += baseBSpline[i-1].shift( 1 );
149  for( int j=0 ; j<i ; j++ ) sPolys[i].p -= sPolys[j].p;
150  }
151  rightBaseFunction.set( sPolys , Degree+3 );
152  dLeftBaseFunction = leftBaseFunction.derivative();
153  dRightBaseFunction = rightBaseFunction.derivative();
154  leftBSpline = rightBSpline = baseBSpline;
155  leftBSpline [1] += leftBSpline[2].shift( -1 ) , leftBSpline[0] *= 0;
156  rightBSpline[1] += rightBSpline[0].shift( 1 ) , rightBSpline[2] *= 0;
157  double c , w;
158  for( int i=0 ; i<functionCount ; i++ )
159  {
161  baseFunctions[i] = baseFunction.scale(w).shift(c);
162  baseBSplines[i] = baseBSpline.scale(w).shift(c);
163  if( reflectBoundary )
164  {
165  int d , off , r;
167  r = 1<<d;
168  if ( off==0 ) baseFunctions[i] = leftBaseFunction.scale(w).shift(c);
169  else if( off==r-1 ) baseFunctions[i] = rightBaseFunction.scale(w).shift(c);
170  if ( off==0 ) baseBSplines[i] = leftBSpline.scale(w).shift(c);
171  else if( off==r-1 ) baseBSplines[i] = rightBSpline.scale(w).shift(c);
172  }
173  }
174  }
175  template<int Degree,class Real>
177  {
178  clearDotTables( flags );
179  int size = ( functionCount*functionCount + functionCount )>>1;
180  int fullSize = functionCount*functionCount;
181  if( flags & VV_DOT_FLAG )
182  {
183  vvDotTable = new Real[size];
184  memset( vvDotTable , 0 , sizeof(Real)*size );
185  }
186  if( flags & DV_DOT_FLAG )
187  {
188  dvDotTable = new Real[fullSize];
189  memset( dvDotTable , 0 , sizeof(Real)*fullSize );
190  }
191  if( flags & DD_DOT_FLAG )
192  {
193  ddDotTable = new Real[size];
194  memset( ddDotTable , 0 , sizeof(Real)*size );
195  }
196  double vvIntegrals[Degree+1][Degree+1];
197  double vdIntegrals[Degree+1][Degree ];
198  double dvIntegrals[Degree ][Degree+1];
199  double ddIntegrals[Degree ][Degree ];
200  int vvSums[Degree+1][Degree+1];
201  int vdSums[Degree+1][Degree ];
202  int dvSums[Degree ][Degree+1];
203  int ddSums[Degree ][Degree ];
204  SetBSplineElementIntegrals< Degree , Degree >( vvIntegrals );
205  SetBSplineElementIntegrals< Degree , Degree-1 >( vdIntegrals );
206  SetBSplineElementIntegrals< Degree-1 , Degree >( dvIntegrals );
207  SetBSplineElementIntegrals< Degree-1 , Degree-1 >( ddIntegrals );
208 
209  for( int d1=0 ; d1<=depth ; d1++ )
210  for( int off1=0 ; off1<(1<<d1) ; off1++ )
211  {
212  int ii = BinaryNode< Real >::CenterIndex( d1 , off1 );
214  BSplineElements< Degree-1 > db1;
215  b1.differentiate( db1 );
216 
217  int start1 , end1;
218 
219  start1 = -1;
220  for( int i=0 ; i<int(b1.size()) ; i++ ) for( int j=0 ; j<=Degree ; j++ )
221  {
222  if( b1[i][j] && start1==-1 ) start1 = i;
223  if( b1[i][j] ) end1 = i+1;
224  }
225  for( int d2=d1 ; d2<=depth ; d2++ )
226  {
227  for( int off2=0 ; off2<(1<<d2) ; off2++ )
228  {
229  int start2 = off2-Degree;
230  int end2 = off2+Degree+1;
231  if( start2>=end1 || start1>=end2 ) continue;
232  start2 = std::max< int >( start1 , start2 );
233  end2 = std::min< int >( end1 , end2 );
234  if( d1==d2 && off2<off1 ) continue;
235  int jj = BinaryNode< Real >::CenterIndex( d2 , off2 );
236  BSplineElements< Degree > b2( 1<<d2 , off2 , reflectBoundary ? BSplineElements<Degree>::NEUMANN : BSplineElements< Degree>::NONE );
237  BSplineElements< Degree-1 > db2;
238  b2.differentiate( db2 );
239 
240  int idx = SymmetricIndex( ii , jj );
241  int idx1 = Index( ii , jj ) , idx2 = Index( jj , ii );
242 
243  memset( vvSums , 0 , sizeof( int ) * ( Degree+1 ) * ( Degree+1 ) );
244  memset( vdSums , 0 , sizeof( int ) * ( Degree+1 ) * ( Degree ) );
245  memset( dvSums , 0 , sizeof( int ) * ( Degree ) * ( Degree+1 ) );
246  memset( ddSums , 0 , sizeof( int ) * ( Degree ) * ( Degree ) );
247  for( int i=start2 ; i<end2 ; i++ )
248  {
249  for( int j=0 ; j<=Degree ; j++ ) for( int k=0 ; k<=Degree ; k++ ) vvSums[j][k] += b1[i][j] * b2[i][k];
250  for( int j=0 ; j<=Degree ; j++ ) for( int k=0 ; k< Degree ; k++ ) vdSums[j][k] += b1[i][j] * db2[i][k];
251  for( int j=0 ; j< Degree ; j++ ) for( int k=0 ; k<=Degree ; k++ ) dvSums[j][k] += db1[i][j] * b2[i][k];
252  for( int j=0 ; j< Degree ; j++ ) for( int k=0 ; k< Degree ; k++ ) ddSums[j][k] += db1[i][j] * db2[i][k];
253  }
254  double vvDot = 0 , dvDot = 0 , vdDot = 0 , ddDot = 0;
255  for( int j=0 ; j<=Degree ; j++ ) for( int k=0 ; k<=Degree ; k++ ) vvDot += vvIntegrals[j][k] * vvSums[j][k];
256  for( int j=0 ; j<=Degree ; j++ ) for( int k=0 ; k< Degree ; k++ ) vdDot += vdIntegrals[j][k] * vdSums[j][k];
257  for( int j=0 ; j< Degree ; j++ ) for( int k=0 ; k<=Degree ; k++ ) dvDot += dvIntegrals[j][k] * dvSums[j][k];
258  for( int j=0 ; j< Degree ; j++ ) for( int k=0 ; k< Degree ; k++ ) ddDot += ddIntegrals[j][k] * ddSums[j][k];
259  vvDot /= (1<<d2);
260  ddDot *= (1<<d2);
261  vvDot /= ( b1.denominator * b2.denominator );
262  dvDot /= ( b1.denominator * b2.denominator );
263  vdDot /= ( b1.denominator * b2.denominator );
264  ddDot /= ( b1.denominator * b2.denominator );
265  if( fabs(vvDot)<1e-15 ) continue;
266  if( flags & VV_DOT_FLAG ) vvDotTable [idx] = Real( vvDot );
267  if( useDotRatios )
268  {
269  if( flags & DV_DOT_FLAG ) dvDotTable[idx1] = Real( dvDot / vvDot );
270  if( flags & DV_DOT_FLAG ) dvDotTable[idx2] = Real( vdDot / vvDot );
271  if( flags & DD_DOT_FLAG ) ddDotTable[idx ] = Real( ddDot / vvDot );
272  }
273  else
274  {
275  if( flags & DV_DOT_FLAG ) dvDotTable[idx1] = Real( dvDot );
276  if( flags & DV_DOT_FLAG ) dvDotTable[idx2] = Real( dvDot );
277  if( flags & DD_DOT_FLAG ) ddDotTable[idx ] = Real( ddDot );
278  }
279  }
281  b = b1;
282  b.upSample( b1 );
283  b1.differentiate( db1 );
284  start1 = -1;
285  for( int i=0 ; i<int(b1.size()) ; i++ ) for( int j=0 ; j<=Degree ; j++ )
286  {
287  if( b1[i][j] && start1==-1 ) start1 = i;
288  if( b1[i][j] ) end1 = i+1;
289  }
290  }
291  }
292  }
293  template<int Degree,class Real>
295  {
296  if (flags & VV_DOT_FLAG) {
297  delete[] vvDotTable ; vvDotTable = NULL;
298  delete[] dvDotTable ; dvDotTable = NULL;
299  delete[] ddDotTable ; ddDotTable = NULL;
300  }
301  }
302  template< int Degree , class Real >
303  void BSplineData< Degree , Real >::setSampleSpan( int idx , int& start , int& end , double smooth ) const
304  {
305  int d , off , res;
306  BinaryNode< double >::DepthAndOffset( idx , d , off );
307  res = 1<<d;
308  double _start = ( off + 0.5 - 0.5*(Degree+1) ) / res - smooth;
309  double _end = ( off + 0.5 + 0.5*(Degree+1) ) / res + smooth;
310  // (start)/(sampleCount-1) >_start && (start-1)/(sampleCount-1)<=_start
311  // => start > _start * (sampleCount-1 ) && start <= _start*(sampleCount-1) + 1
312  // => _start * (sampleCount-1) + 1 >= start > _start * (sampleCount-1)
313  start = int( floor( _start * (sampleCount-1) + 1 ) );
314  if( start<0 ) start = 0;
315  // (end)/(sampleCount-1)<_end && (end+1)/(sampleCount-1)>=_end
316  // => end < _end * (sampleCount-1 ) && end >= _end*(sampleCount-1) - 1
317  // => _end * (sampleCount-1) > end >= _end * (sampleCount-1) - 1
318  end = int( ceil( _end * (sampleCount-1) - 1 ) );
319  if( end>=sampleCount ) end = sampleCount-1;
320  }
321  template<int Degree,class Real>
322  void BSplineData<Degree,Real>::setValueTables( int flags , double smooth )
323  {
324  clearValueTables();
325  if( flags & VALUE_FLAG ) valueTables = new Real[functionCount*sampleCount];
326  if( flags & D_VALUE_FLAG ) dValueTables = new Real[functionCount*sampleCount];
327  PPolynomial<Degree+1> function;
328  PPolynomial<Degree> dFunction;
329  for( int i=0 ; i<functionCount ; i++ )
330  {
331  if(smooth>0)
332  {
333  function = baseFunctions[i].MovingAverage(smooth);
334  dFunction = baseFunctions[i].derivative().MovingAverage(smooth);
335  }
336  else
337  {
338  function = baseFunctions[i];
339  dFunction = baseFunctions[i].derivative();
340  }
341  for( int j=0 ; j<sampleCount ; j++ )
342  {
343  double x=double(j)/(sampleCount-1);
344  if(flags & VALUE_FLAG){ valueTables[j*functionCount+i]=Real( function(x));}
345  if(flags & D_VALUE_FLAG){dValueTables[j*functionCount+i]=Real(dFunction(x));}
346  }
347  }
348  }
349  template<int Degree,class Real>
350  void BSplineData<Degree,Real>::setValueTables(int flags,double valueSmooth,double normalSmooth){
351  clearValueTables();
352  if(flags & VALUE_FLAG){ valueTables=new Real[functionCount*sampleCount];}
353  if(flags & D_VALUE_FLAG){dValueTables=new Real[functionCount*sampleCount];}
354  PPolynomial<Degree+1> function;
355  PPolynomial<Degree> dFunction;
356  for(int i=0;i<functionCount;i++){
357  if(valueSmooth>0) { function=baseFunctions[i].MovingAverage(valueSmooth);}
358  else { function=baseFunctions[i];}
359  if(normalSmooth>0) {dFunction=baseFunctions[i].derivative().MovingAverage(normalSmooth);}
360  else {dFunction=baseFunctions[i].derivative();}
361 
362  for(int j=0;j<sampleCount;j++){
363  double x=double(j)/(sampleCount-1);
364  if(flags & VALUE_FLAG){ valueTables[j*functionCount+i]=Real( function(x));}
365  if(flags & D_VALUE_FLAG){dValueTables[j*functionCount+i]=Real(dFunction(x));}
366  }
367  }
368  }
369 
370 
371  template<int Degree,class Real>
373  delete[] valueTables;
374  delete[] dValueTables;
375  valueTables=dValueTables=NULL;
376  }
377 
378  template<int Degree,class Real>
379  inline int BSplineData<Degree,Real>::Index( int i1 , int i2 ) const { return i1*functionCount+i2; }
380  template<int Degree,class Real>
381  inline int BSplineData<Degree,Real>::SymmetricIndex( int i1 , int i2 )
382  {
383  if( i1>i2 ) return ((i1*i1+i1)>>1)+i2;
384  else return ((i2*i2+i2)>>1)+i1;
385  }
386  template<int Degree,class Real>
387  inline int BSplineData<Degree,Real>::SymmetricIndex( int i1 , int i2 , int& index )
388  {
389  if( i1<i2 )
390  {
391  index = ((i2*i2+i2)>>1)+i1;
392  return 1;
393  }
394  else
395  {
396  index = ((i1*i1+i1)>>1)+i2;
397  return 0;
398  }
399  }
400 
401 
402  ////////////////////////
403  // BSplineElementData //
404  ////////////////////////
405  template< int Degree >
406  BSplineElements< Degree >::BSplineElements( int res , int offset , int boundary )
407  {
408  denominator = 1;
409  this->resize( res , BSplineElementCoefficients<Degree>() );
410 
411  for( int i=0 ; i<=Degree ; i++ )
412  {
413  int idx = -_off + offset + i;
414  if( idx>=0 && idx<res ) (*this)[idx][i] = 1;
415  }
416  if( boundary!=0 )
417  {
418  _addLeft( offset-2*res , boundary ) , _addRight( offset+2*res , boundary );
419  if( Degree&1 ) _addLeft( offset-res , boundary ) , _addRight( offset+res , boundary );
420  else _addLeft( -offset-1 , boundary ) , _addRight( -offset-1+2*res , boundary );
421  }
422  }
423  template< int Degree >
424  void BSplineElements< Degree >::_addLeft( int offset , int boundary )
425  {
426  int res = int( this->size() );
427  bool set = false;
428  for( int i=0 ; i<=Degree ; i++ )
429  {
430  int idx = -_off + offset + i;
431  if( idx>=0 && idx<res ) (*this)[idx][i] += boundary , set = true;
432  }
433  if( set ) _addLeft( offset-2*res , boundary );
434  }
435  template< int Degree >
436  void BSplineElements< Degree >::_addRight( int offset , int boundary )
437  {
438  int res = int( this->size() );
439  bool set = false;
440  for( int i=0 ; i<=Degree ; i++ )
441  {
442  int idx = -_off + offset + i;
443  if( idx>=0 && idx<res ) (*this)[idx][i] += boundary , set = true;
444  }
445  if( set ) _addRight( offset+2*res , boundary );
446  }
447  template< int Degree >
449  {
450  POISSON_THROW_EXCEPTION (pcl::poisson::PoissonBadArgumentException, "B-spline up-sampling not supported for degree " << Degree);
451  }
452  template<>
454 
455  template<>
457 
458  template< int Degree >
460  {
461  d.resize( this->size() );
462  d.assign( d.size() , BSplineElementCoefficients< Degree-1 >() );
463  for( int i=0 ; i<int(this->size()) ; i++ ) for( int j=0 ; j<=Degree ; j++ )
464  {
465  if( j-1>=0 ) d[i][j-1] -= (*this)[i][j];
466  if( j<Degree ) d[i][j ] += (*this)[i][j];
467  }
468  d.denominator = denominator;
469  }
470  // If we were really good, we would implement this integral table to store
471  // rational values to improve precision...
472  template< int Degree1 , int Degree2 >
473  void SetBSplineElementIntegrals( double integrals[Degree1+1][Degree2+1] )
474  {
475  for( int i=0 ; i<=Degree1 ; i++ )
476  {
478  for( int j=0 ; j<=Degree2 ; j++ )
479  {
481  integrals[i][j] = ( p1 * p2 ).integral( 0 , 1 );
482  }
483  }
484  }
485 
486 
487  }
488 }
virtual void clearDotTables(int flags)
bool LeftOverlap(unsigned int depth, int offset)
static PPolynomial BSpline(double radius=0.5)
Polynomial< Degree > p
Definition: ppolynomial.h:46
static int SymmetricIndex(int i1, int i2)
PPolynomial< Degree+1 > MovingAverage(double radius)
static int CumulativeCenterCount(int maxDepth)
Definition: binary_node.h:44
int Index(int i1, int i2) const
An exception that is thrown when the arguments number or type is wrong/unhandled. ...
static int CenterCount(int depth)
Definition: binary_node.h:42
void _addLeft(int offset, int boundary)
StartingPolynomial shift(double t) const
Definition: ppolynomial.hpp:61
virtual void setValueTables(int flags, double smooth=0)
PPolynomial< Degree-1 > derivative(void) const
int ReflectLeft(unsigned int depth, int offset)
void differentiate(BSplineElements< Degree-1 > &d) const
void _addRight(int offset, int boundary)
virtual void clearValueTables(void)
static void CenterAndWidth(int depth, int offset, Real &center, Real &width)
Definition: binary_node.h:53
virtual void setDotTables(int flags)
bool RightOverlap(unsigned int depth, int offset)
int ReflectRight(unsigned int depth, int offset)
void set(int maxDepth, bool useDotRatios=true, bool reflectBoundary=false)
static int CenterIndex(int depth, int offSet)
Definition: binary_node.h:47
void SetBSplineElementIntegrals(double integrals[Degree1+1][Degree2+1])
static int CornerCount(int depth)
Definition: binary_node.h:43
void setSampleSpan(int idx, int &start, int &end, double smooth=0) const
static Polynomial BSplineComponent(int i)
Definition: polynomial.hpp:312
void upSample(BSplineElements &high) const
static void DepthAndOffset(int idx, int &depth, int &offset)
Definition: binary_node.h:64