#ifndef GEOMETRIC_FITTING_H #define GEOMETRIC_FITTING_H #include "HoleDetectionParams.h" #include "ErrorCodes.h" #include "../include/VZNL_Types.h" /** * @brief Fit ellipse to 2D boundary points * * @param [in] points 3D boundary points (will be projected to 2D) * @param [in] pointCount Number of points * @param [out] outCenter Ellipse center (x, y, z) * @param [out] outRadius Ellipse radius (average of semi-major and semi-minor axes) * @param [out] outEccentricity Eccentricity (0 = circle) * @param [out] errCode Error code output * @return 0 on success, non-zero on error * * @pre points != nullptr * @pre pointCount >= 5 * @pre outCenter != nullptr * @pre outRadius != nullptr * @pre outEccentricity != nullptr * @pre errCode != nullptr */ int FitEllipse( const SVzNLPointXYZ* points, int pointCount, SVzNL3DPointF* outCenter, float* outRadius, float* outEccentricity, int* errCode ); /** * @brief Fit plane to 3D points * * @param [in] points 3D points * @param [in] pointCount Number of points * @param [out] outNormal Plane normal vector (unit length) * @param [out] outD Plane equation constant (ax+by+cz+d=0) * @param [out] errCode Error code output * @return 0 on success, non-zero on error * * @pre points != nullptr * @pre pointCount >= 3 * @pre outNormal != nullptr * @pre outD != nullptr * @pre errCode != nullptr * * @post ||outNormal|| = 1.0 ± 1e-6 */ int FitPlane( const SVzNLPointXYZ* points, int pointCount, SVzNL3DPointF* outNormal, float* outD, int* errCode ); /** * @brief Compute quality score for hole * * @param [in] eccentricity Eccentricity (0 = perfect circle) * @param [in] radiusVariance Radius variance (mm) * @param [in] angularSpan Angular coverage (degrees) * @param [in] boundaryPointCount Number of boundary points * @return Quality score (0-1, higher = better) */ float ComputeQualityScore( float eccentricity, float radiusVariance, float angularSpan, int boundaryPointCount ); /** * @brief Compute rectangularity score using PCA + corner detection * * Uses PCA to align points, then counts points in "corner regions" * (where u² + v² > 1.1 in normalized coordinates). * Ellipse points stay within u² + v² ≤ 1.0, rectangle corners exceed this. * * @param [in] points 3D boundary points (X-Y used for analysis) * @param [in] pointCount Number of points * @return Corner ratio (0-1). Higher = more rectangular. * Returns 0 if pointCount < 4. */ float ComputeRectangularityScore( const SVzNLPointXYZ* points, int pointCount ); /** * @brief Compute angular coverage of points around centroid * * Calculates what percentage of 360° is covered by the point distribution. * Used to detect incomplete/non-closed boundaries. * * @param [in] points 3D boundary points (X-Y used for analysis) * @param [in] pointCount Number of points * @param [in] numBins Number of angular bins (default: 36 = 10° each) * @return Angular coverage in degrees (0-360). * Returns 0 if pointCount < 3. */ float ComputeAngularCoverage( const SVzNLPointXYZ* points, int pointCount, int numBins = 36 ); /** * @brief Compute angular coverage of points around a specified center * * Same as above, but uses the given center instead of computing centroid. * Use this when a fitted center (e.g. from ellipse fitting) is available. * * @param [in] points 3D boundary points (X-Y used for analysis) * @param [in] pointCount Number of points * @param [in] centerX X coordinate of the reference center * @param [in] centerY Y coordinate of the reference center * @param [in] numBins Number of angular bins (default: 36 = 10° each) * @return Angular coverage in degrees (0-360). * Returns 0 if pointCount < 3. */ float ComputeAngularCoverage( const SVzNLPointXYZ* points, int pointCount, float centerX, float centerY, int numBins = 36 ); /** * @brief Check circularity of points in X-Y plane * * Computes the coefficient of variation (CV) of distances from each point * to the centroid. Low CV indicates circular/elliptical distribution. * * @param [in] points 3D boundary points (X-Y used for analysis) * @param [in] pointCount Number of points * @param [in] maxCV Maximum allowed CV (default: 0.4) * @return true if CV <= maxCV (circular enough), false otherwise. * Returns false if pointCount < 3. */ bool CheckCircularity( const SVzNLPointXYZ* points, int pointCount, float maxCV = 0.4f ); /** * @brief Compute inlier ratio for ellipse fit * * Calculates the fraction of points whose distance to the fitted ellipse * center is within tolerance of the fitted radius. * * @param [in] points 2D projected points (X-Y used) * @param [in] pointCount Number of points * @param [in] centerX Fitted ellipse center X * @param [in] centerY Fitted ellipse center Y * @param [in] radius Fitted average radius * @param [in] tolerance Distance tolerance as fraction of radius (default: 0.3) * @return Inlier ratio (0-1). Higher = better fit. */ float ComputeEllipseInlierRatio( const SVzNLPointXYZ* points, int pointCount, float centerX, float centerY, float radius, float tolerance = 0.3f ); #endif // GEOMETRIC_FITTING_H