160 return solveSpecificAxisFit_( points, cone );
163 return solveHemisphereSearchFit_( points, cone );
166 return solveApproximationPCM_( points, cone );
169 return std::numeric_limits<T>::max();
178 Cone3ApproximationParams params_;
182 Cone3<T>& cone,
bool useConeInputAsInitialGuess =
false )
185 ConeFittingFunctor<T> coneFittingFunctor;
186 coneFittingFunctor.setPoints( points );
187 Eigen::LevenbergMarquardt<ConeFittingFunctor<T>, T> lm( coneFittingFunctor );
188 lm.parameters.maxfev = params_.levenbergMarquardtMaxIteration;
191 computeCenterAndNormal_( points, center, U );
194 if ( useConeInputAsInitialGuess )
200 cone = computeInitialCone_( points, center, U );
203 Eigen::VectorX<T> fittedParams( 6 );
204 coneToFitParams_( cone, fittedParams );
205 [[maybe_unused]] Eigen::LevenbergMarquardtSpace::Status result = lm.minimize( fittedParams );
210 fitParamsToCone_( fittedParams, cone );
212 T
const one_v =
static_cast< T
>( 1 );
213 auto cosAngle = std::clamp( one_v / coneAxis.
length(),
static_cast< T
>( 0 ), one_v );
214 cone.angle = std::acos( cosAngle );
215 cone.direction() = cone.direction().normalized();
216 cone.height = calculateConeHeight_( points, cone );
218 return getApproximationRMS_( points, cone );
223 return solveFixedAxis_( points, cone,
false );
228 return solveFixedAxis_( points, cone,
true );
234 Vector3<T> center = computeCenter_( points );
235 ConeFittingFunctor<T> coneFittingFunctor;
236 coneFittingFunctor.setPoints( points );
238 constexpr T pi2 =
static_cast< T
>( PI2 );
239 T
const theraStep =
static_cast< T
>( 2 * PI ) / params_.hemisphereSearchPhiResolution;
240 T
const phiStep = pi2 / params_.hemisphereSearchPhiResolution;
244 T minError = std::numeric_limits<T> ::max();
246 std::vector<BestCone> bestCones;
247 bestCones.resize( params_.hemisphereSearchPhiResolution + 1 );
249 tbb::parallel_for( tbb::blocked_range<size_t>(
size_t( 0 ), params_.hemisphereSearchPhiResolution + 1 ),
250 [&] (
const tbb::blocked_range<size_t>& range )
252 for ( size_t j = range.begin(); j < range.end(); ++j )
255 T cosPhi = std::cos( phi );
256 T sinPhi = std::sin( phi );
257 for ( size_t i = 0; i < params_.hemisphereSearchThetaResolution; ++i )
259 T theta = theraStep * i;
260 T cosTheta = std::cos( theta );
261 T sinTheta = std::sin( theta );
264 Vector3<T> U( cosTheta * sinPhi, sinTheta * sinPhi, cosPhi );
266 auto tmpCone = computeInitialCone_( points, center, U );
268 Eigen::VectorX<T> fittedParams( 6 );
269 coneToFitParams_( tmpCone, fittedParams );
272 Eigen::LevenbergMarquardt<ConeFittingFunctor<T>, T> lm( coneFittingFunctor );
273 lm.parameters.maxfev = params_.levenbergMarquardtMaxIteration;
274 [[maybe_unused]] Eigen::LevenbergMarquardtSpace::Status result = lm.minimize( fittedParams );
279 fitParamsToCone_( fittedParams, tmpCone );
281 T const one_v = static_cast< T >( 1 );
282 auto cosAngle = std::clamp( one_v / tmpCone.direction().length(), static_cast< T >( 0 ), one_v );
283 tmpCone.angle = std::acos( cosAngle );
284 tmpCone.direction() = tmpCone.direction().normalized();
287 T error = getApproximationRMS_( points, tmpCone );
288 if ( error < bestCones[j].minError )
290 bestCones[j].minError = error;
291 bestCones[j].bestCone = tmpCone;
298 auto bestAppox = std::min_element( bestCones.begin(), bestCones.end(), [] (
const BestCone& a,
const BestCone& b )
300 return a.minError < b.minError;
303 cone = bestAppox->bestCone;
306 cone.height = calculateConeHeight_( points, cone );
308 return bestAppox->minError;
314 T
length =
static_cast< T
> ( 0 );
315 for (
auto i = 0; i < points.size(); ++i )
317 length = std::max(
length, std::abs( MR::dot( points[i] - cone.apex(), cone.direction() ) ) );
324 if ( points.size() == 0 )
325 return std::numeric_limits<T>::max();
328 for (
auto p : points )
329 error = error +
distanceSq( cone.projectPoint( p ), p );
331 return error / points.size();
338 for (
auto i = 0; i < points.size(); ++i )
342 center = center /
static_cast< T
>( points.size() );
349 center = computeCenter_( points );
353 for (
auto i = 0; i < points.size(); ++i )
355 Vector3<T>
Z = points[i] - center;
356 U +=
Z * MR::dot(
Z,
Z );
367 result.direction() = axis;
369 T& coneAngle = result.
angle;
376 std::vector<Vector2<T>> hrPairs( points.size() );
377 T hMin = std::numeric_limits<T>::max(), hMax = -hMin;
378 for (
auto i = 0; i < points.size(); ++i )
381 T h = MR::dot( U, delta );
382 hMin = std::min( hMin, h );
383 hMax = std::max( hMax, h );
384 Vector3<T> projection = delta - MR::dot( U, delta ) * U;
385 T r = projection.
length();
386 hrPairs[i] = { h, r };
394 findBestFitLine_( hrPairs, a, b, &avgPoint );
395 T hAverage = avgPoint.
x;
396 T rAverage = avgPoint.
y;
406 std::swap( hMin, hMax );
412 T rMin = rAverage + hrSlope * ( hMin - hAverage );
413 T rMax = rAverage + hrSlope * ( hMax - hAverage );
414 T hRange = hMax - hMin;
415 T rRange = rMax - rMin;
418 T tanAngle = rRange / hRange;
419 coneAngle = std::atan2( rRange, hRange );
422 T offset = rMax / tanAngle - hMax;
423 coneApex = center - offset * U;
430 auto numPoints = xyPairs.size();
431 Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> A( numPoints, 2 );
432 Eigen::Vector<T, Eigen::Dynamic> b( numPoints );
434 for (
auto i = 0; i < numPoints; ++i )
436 A( i, 0 ) = xyPairs[i].x;
438 b( i ) = xyPairs[i].y;
440 *avg = *avg + xyPairs[i];
444 *avg = *avg /
static_cast < T
> ( xyPairs.size() );
447 Eigen::Matrix<T, Eigen::Dynamic, 1> x = A.bdcSvd( Eigen::ComputeThinU | Eigen::ComputeThinV ).solve( b );
454 *avg = *avg /
static_cast < T
> ( xyPairs.size() );
455 avg->y = lineA * avg->x + lineB;
460 void fitParamsToCone_( Eigen::Vector<T, Eigen::Dynamic>& fittedParams,
Cone3<T>& cone )
462 cone.apex().x = fittedParams[0];
463 cone.apex().y = fittedParams[1];
464 cone.apex().z = fittedParams[2];
466 cone.direction().x = fittedParams[3];
467 cone.direction().y = fittedParams[4];
468 cone.direction().z = fittedParams[5];
472 void coneToFitParams_(
Cone3<T>& cone, Eigen::Vector<T, Eigen::Dynamic>& fittedParams )
475 fittedParams[0] = cone.apex().x;
476 fittedParams[1] = cone.apex().y;
477 fittedParams[2] = cone.apex().z;
480 T coneCosAngle = std::cos( cone.angle );
481 fittedParams[3] = cone.direction().x / coneCosAngle;
482 fittedParams[4] = cone.direction().y / coneCosAngle;
483 fittedParams[5] = cone.direction().z / coneCosAngle;