162 return solveSpecificAxisFit_( points, cone );
165 return solveHemisphereSearchFit_( points, cone );
168 return solveApproximationPCM_( points, cone );
171 return std::numeric_limits<T>::max();
180 Cone3ApproximationParams params_;
184 Cone3<T>& cone,
bool useConeInputAsInitialGuess =
false )
187 ConeFittingFunctor<T> coneFittingFunctor;
188 coneFittingFunctor.setPoints( points );
189 Eigen::LevenbergMarquardt<ConeFittingFunctor<T>, T> lm( coneFittingFunctor );
190 lm.parameters.maxfev = params_.levenbergMarquardtMaxIteration;
193 computeCenterAndNormal_( points, center, U );
196 if ( useConeInputAsInitialGuess )
202 cone = computeInitialCone_( points, center, U );
205 Eigen::VectorX<T> fittedParams( 6 );
206 coneToFitParams_( cone, fittedParams );
207 [[maybe_unused]] Eigen::LevenbergMarquardtSpace::Status result = lm.minimize( fittedParams );
212 fitParamsToCone_( fittedParams, cone );
214 T
const one_v =
static_cast< T
>( 1 );
215 auto cosAngle = std::clamp( one_v / coneAxis.
length(),
static_cast< T
>( 0 ), one_v );
216 cone.angle = std::acos( cosAngle );
217 cone.direction() = cone.direction().normalized();
218 cone.height = calculateConeHeight_( points, cone );
220 return getApproximationRMS_( points, cone );
225 return solveFixedAxis_( points, cone,
false );
230 return solveFixedAxis_( points, cone,
true );
236 Vector3<T> center = computeCenter_( points );
237 ConeFittingFunctor<T> coneFittingFunctor;
238 coneFittingFunctor.setPoints( points );
240 constexpr T pi2 =
static_cast< T
>( PI2 );
241 T
const theraStep =
static_cast< T
>( 2 * PI ) / params_.hemisphereSearchPhiResolution;
242 T
const phiStep = pi2 / params_.hemisphereSearchPhiResolution;
246 T minError = std::numeric_limits<T> ::max();
248 std::vector<BestCone> bestCones;
249 bestCones.resize( params_.hemisphereSearchPhiResolution + 1 );
251 tbb::parallel_for( tbb::blocked_range<size_t>(
size_t( 0 ), params_.hemisphereSearchPhiResolution + 1 ),
252 [&] (
const tbb::blocked_range<size_t>& range )
254 for ( size_t j = range.begin(); j < range.end(); ++j )
257 T cosPhi = std::cos( phi );
258 T sinPhi = std::sin( phi );
259 for ( size_t i = 0; i < params_.hemisphereSearchThetaResolution; ++i )
261 T theta = theraStep * i;
262 T cosTheta = std::cos( theta );
263 T sinTheta = std::sin( theta );
266 Vector3<T> U( cosTheta * sinPhi, sinTheta * sinPhi, cosPhi );
268 auto tmpCone = computeInitialCone_( points, center, U );
270 Eigen::VectorX<T> fittedParams( 6 );
271 coneToFitParams_( tmpCone, fittedParams );
274 Eigen::LevenbergMarquardt<ConeFittingFunctor<T>, T> lm( coneFittingFunctor );
275 lm.parameters.maxfev = params_.levenbergMarquardtMaxIteration;
276 [[maybe_unused]] Eigen::LevenbergMarquardtSpace::Status result = lm.minimize( fittedParams );
281 fitParamsToCone_( fittedParams, tmpCone );
283 T const one_v = static_cast< T >( 1 );
284 auto cosAngle = std::clamp( one_v / tmpCone.direction().length(), static_cast< T >( 0 ), one_v );
285 tmpCone.angle = std::acos( cosAngle );
286 tmpCone.direction() = tmpCone.direction().normalized();
289 T error = getApproximationRMS_( points, tmpCone );
290 if ( error < bestCones[j].minError )
292 bestCones[j].minError = error;
293 bestCones[j].bestCone = tmpCone;
300 auto bestAppox = std::min_element( bestCones.begin(), bestCones.end(), [] (
const BestCone& a,
const BestCone& b )
302 return a.minError < b.minError;
305 cone = bestAppox->bestCone;
308 cone.height = calculateConeHeight_( points, cone );
310 return bestAppox->minError;
316 T
length =
static_cast< T
> ( 0 );
317 for (
auto i = 0; i < points.size(); ++i )
319 length = std::max(
length, std::abs( MR::dot( points[i] - cone.apex(), cone.direction() ) ) );
326 if ( points.size() == 0 )
327 return std::numeric_limits<T>::max();
330 for (
auto p : points )
331 error = error +
distanceSq( cone.projectPoint( p ), p );
333 return error / points.size();
340 for (
auto i = 0; i < points.size(); ++i )
344 center = center /
static_cast< T
>( points.size() );
351 center = computeCenter_( points );
355 for (
auto i = 0; i < points.size(); ++i )
357 Vector3<T>
Z = points[i] - center;
358 U +=
Z * MR::dot(
Z,
Z );
369 result.direction() = axis;
371 T& coneAngle = result.
angle;
378 std::vector<Vector2<T>> hrPairs( points.size() );
379 T hMin = std::numeric_limits<T>::max(), hMax = -hMin;
380 for (
auto i = 0; i < points.size(); ++i )
383 T h = MR::dot( U, delta );
384 hMin = std::min( hMin, h );
385 hMax = std::max( hMax, h );
386 Vector3<T> projection = delta - MR::dot( U, delta ) * U;
387 T r = projection.
length();
388 hrPairs[i] = { h, r };
396 findBestFitLine_( hrPairs, a, b, &avgPoint );
397 T hAverage = avgPoint.
x;
398 T rAverage = avgPoint.
y;
408 std::swap( hMin, hMax );
414 T rMin = rAverage + hrSlope * ( hMin - hAverage );
415 T rMax = rAverage + hrSlope * ( hMax - hAverage );
416 T hRange = hMax - hMin;
417 T rRange = rMax - rMin;
420 T tanAngle = rRange / hRange;
421 coneAngle = std::atan2( rRange, hRange );
424 T offset = rMax / tanAngle - hMax;
425 coneApex = center - offset * U;
432 auto numPoints = xyPairs.size();
433 Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> A( numPoints, 2 );
434 Eigen::Vector<T, Eigen::Dynamic> b( numPoints );
436 for (
auto i = 0; i < numPoints; ++i )
438 A( i, 0 ) = xyPairs[i].x;
440 b( i ) = xyPairs[i].y;
442 *avg = *avg + xyPairs[i];
446 *avg = *avg /
static_cast < T
> ( xyPairs.size() );
449 MR_SUPPRESS_WARNING_PUSH
450 MR_SUPPRESS_WARNING(
"-Wdeprecated-declarations", 4996 )
451 Eigen::Matrix<T, Eigen::Dynamic, 1> x = A.bdcSvd( Eigen::ComputeThinU | Eigen::ComputeThinV ).solve( b );
452 MR_SUPPRESS_WARNING_POP
459 *avg = *avg /
static_cast < T
> ( xyPairs.size() );
460 avg->y = lineA * avg->x + lineB;
465 void fitParamsToCone_( Eigen::Vector<T, Eigen::Dynamic>& fittedParams,
Cone3<T>& cone )
467 cone.apex().x = fittedParams[0];
468 cone.apex().y = fittedParams[1];
469 cone.apex().z = fittedParams[2];
471 cone.direction().x = fittedParams[3];
472 cone.direction().y = fittedParams[4];
473 cone.direction().z = fittedParams[5];
477 void coneToFitParams_(
Cone3<T>& cone, Eigen::Vector<T, Eigen::Dynamic>& fittedParams )
480 fittedParams[0] = cone.apex().x;
481 fittedParams[1] = cone.apex().y;
482 fittedParams[2] = cone.apex().z;
485 T coneCosAngle = std::cos( cone.angle );
486 fittedParams[3] = cone.direction().x / coneCosAngle;
487 fittedParams[4] = cone.direction().y / coneCosAngle;
488 fittedParams[5] = cone.direction().z / coneCosAngle;