172 return solveSpecificAxisFit_( points, cone );
175 return solveHemisphereSearchFit_( points, cone );
178 return solveApproximationPCM_( points, cone );
181 return std::numeric_limits<T>::max();
190 Cone3ApproximationParams params_;
194 Cone3<T>& cone,
bool useConeInputAsInitialGuess =
false )
197 ConeFittingFunctor<T> coneFittingFunctor;
198 coneFittingFunctor.setPoints( points );
199 Eigen::LevenbergMarquardt<ConeFittingFunctor<T>, T> lm( coneFittingFunctor );
200 lm.parameters.maxfev = params_.levenbergMarquardtMaxIteration;
203 computeCenterAndNormal_( points, center, U );
206 if ( useConeInputAsInitialGuess )
212 cone = computeInitialCone_( points, center, U );
215 Eigen::VectorX<T> fittedParams( 6 );
216 coneToFitParams_( cone, fittedParams );
217 [[maybe_unused]] Eigen::LevenbergMarquardtSpace::Status result = lm.minimize( fittedParams );
222 fitParamsToCone_( fittedParams, cone );
224 T
const one_v =
static_cast< T
>( 1 );
225 auto cosAngle = std::clamp( one_v / coneAxis.
length(),
static_cast< T
>( 0 ), one_v );
226 cone.angle = std::acos( cosAngle );
227 cone.direction() = cone.direction().normalized();
228 cone.height = calculateConeHeight_( points, cone );
230 return getApproximationRMS_( points, cone );
235 return solveFixedAxis_( points, cone,
false );
240 return solveFixedAxis_( points, cone,
true );
247 ConeFittingFunctor<T> coneFittingFunctor;
248 coneFittingFunctor.setPoints( points );
250 constexpr T pi2 =
static_cast< T
>( PI2 );
251 T
const theraStep =
static_cast< T
>( 2 * PI ) / params_.hemisphereSearchPhiResolution;
252 T
const phiStep = pi2 / params_.hemisphereSearchPhiResolution;
256 T minError = std::numeric_limits<T> ::max();
258 std::vector<BestCone> bestCones;
259 bestCones.resize( params_.hemisphereSearchPhiResolution + 1 );
261 tbb::parallel_for( tbb::blocked_range<size_t>(
size_t( 0 ), params_.hemisphereSearchPhiResolution + 1 ),
262 [&] (
const tbb::blocked_range<size_t>& range )
264 for ( size_t j = range.begin(); j < range.end(); ++j )
267 T cosPhi = std::cos( phi );
268 T sinPhi = std::sin( phi );
269 for ( size_t i = 0; i < params_.hemisphereSearchThetaResolution; ++i )
271 T theta = theraStep * i;
272 T cosTheta = std::cos( theta );
273 T sinTheta = std::sin( theta );
276 Vector3<T> U( cosTheta * sinPhi, sinTheta * sinPhi, cosPhi );
278 auto tmpCone = computeInitialCone_( points, center, U );
280 Eigen::VectorX<T> fittedParams( 6 );
281 coneToFitParams_( tmpCone, fittedParams );
284 Eigen::LevenbergMarquardt<ConeFittingFunctor<T>, T> lm( coneFittingFunctor );
285 lm.parameters.maxfev = params_.levenbergMarquardtMaxIteration;
286 [[maybe_unused]] Eigen::LevenbergMarquardtSpace::Status result = lm.minimize( fittedParams );
291 fitParamsToCone_( fittedParams, tmpCone );
293 T const one_v = static_cast< T >( 1 );
294 auto cosAngle = std::clamp( one_v / tmpCone.direction().length(), static_cast< T >( 0 ), one_v );
295 tmpCone.angle = std::acos( cosAngle );
296 tmpCone.direction() = tmpCone.direction().normalized();
299 T error = getApproximationRMS_( points, tmpCone );
300 if ( error < bestCones[j].minError )
302 bestCones[j].minError = error;
303 bestCones[j].bestCone = tmpCone;
310 auto bestAppox = std::min_element( bestCones.begin(), bestCones.end(), [] (
const BestCone& a,
const BestCone& b )
312 return a.minError < b.minError;
315 cone = bestAppox->bestCone;
318 cone.height = calculateConeHeight_( points, cone );
320 return bestAppox->minError;
326 T
length =
static_cast< T
> ( 0 );
327 for (
auto i = 0; i < points.size(); ++i )
329 length = std::max(
length, std::abs( MR::dot( points[i] - cone.apex(), cone.direction() ) ) );
336 if ( points.size() == 0 )
337 return std::numeric_limits<T>::max();
340 for (
auto p : points )
341 error = error + ( cone.projectPoint( p ) - p ).
lengthSq();
343 return error / points.size();
350 for (
auto i = 0; i < points.size(); ++i )
354 center = center /
static_cast< T
>( points.size() );
361 center = computeCenter_( points );
365 for (
auto i = 0; i < points.size(); ++i )
368 U +=
Z * MR::dot(
Z,
Z );
379 result.direction() = axis;
381 T& coneAngle = result.
angle;
388 std::vector<Vector2<T>> hrPairs( points.size() );
389 T hMin = std::numeric_limits<T>::max(), hMax = -hMin;
390 for (
auto i = 0; i < points.size(); ++i )
393 T h = MR::dot( U, delta );
394 hMin = std::min( hMin, h );
395 hMax = std::max( hMax, h );
396 Vector3<T> projection = delta - MR::dot( U, delta ) * U;
397 T r = projection.
length();
398 hrPairs[i] = { h, r };
406 findBestFitLine_( hrPairs, a, b, &avgPoint );
407 T hAverage = avgPoint.
x;
408 T rAverage = avgPoint.
y;
418 std::swap( hMin, hMax );
424 T rMin = rAverage + hrSlope * ( hMin - hAverage );
425 T rMax = rAverage + hrSlope * ( hMax - hAverage );
426 T hRange = hMax - hMin;
427 T rRange = rMax - rMin;
430 T tanAngle = rRange / hRange;
431 coneAngle = std::atan2( rRange, hRange );
434 T offset = rMax / tanAngle - hMax;
435 coneApex = center - offset * U;
442 auto numPoints = xyPairs.size();
443 Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> A( numPoints, 2 );
444 Eigen::Vector<T, Eigen::Dynamic> b( numPoints );
446 for (
auto i = 0; i < numPoints; ++i )
448 A( i, 0 ) = xyPairs[i].x;
450 b( i ) = xyPairs[i].y;
452 *avg = *avg + xyPairs[i];
456 *avg = *avg /
static_cast < T
> ( xyPairs.size() );
459 Eigen::Matrix<T, Eigen::Dynamic, 1> x = A.bdcSvd( Eigen::ComputeThinU | Eigen::ComputeThinV ).solve( b );
466 *avg = *avg /
static_cast < T
> ( xyPairs.size() );
467 avg->y = lineA * avg->x + lineB;
472 void fitParamsToCone_( Eigen::Vector<T, Eigen::Dynamic>& fittedParams,
Cone3<T>& cone )
474 cone.apex().x = fittedParams[0];
475 cone.apex().y = fittedParams[1];
476 cone.apex().z = fittedParams[2];
478 cone.direction().x = fittedParams[3];
479 cone.direction().y = fittedParams[4];
480 cone.direction().z = fittedParams[5];
484 void coneToFitParams_(
Cone3<T>& cone, Eigen::Vector<T, Eigen::Dynamic>& fittedParams )
487 fittedParams[0] = cone.apex().x;
488 fittedParams[1] = cone.apex().y;
489 fittedParams[2] = cone.apex().z;
492 T coneCosAngle = std::cos( cone.angle );
493 fittedParams[3] = cone.direction().x / coneCosAngle;
494 fittedParams[4] = cone.direction().y / coneCosAngle;
495 fittedParams[5] = cone.direction().z / coneCosAngle;