hey, this is my python code
import numpy as np
import matplotlib.pyplot as plt
import cv2
import csv

def read_csv(csv_path):
    """Reads a CSV file and organizes the points into paths."""
    np_path_XYs = np.genfromtxt(csv_path, delimiter=',')
    path_XYs = []
    for i in np.unique(np_path_XYs[:, 0]):
        npXYs = np_path_XYs[np_path_XYs[:, 0] == i][:, 1:]
        XYs = []
        for j in np.unique(npXYs[:, 0]):
            XY = npXYs[npXYs[:, 0] == j][:, 1:]
            XYs.append(XY)
        path_XYs.append(XYs)
    return path_XYs

def detect_shape(XY):
    """Detects the shape type from the provided points."""
    contour = np.array(XY, dtype=np.int32).reshape((-1, 1, 2))
    peri = cv2.arcLength(contour, True)
    approx = cv2.approxPolyDP(contour, 0.04 * peri, True)
    area = cv2.contourArea(contour)
    
    if area < 100:
        return " "

    if is_straight_line(XY):
        return "Straight Line"

    if len(approx) == 4:
        if is_square(contour):
            return "Square"
        else:
            return "Rectangle"

    elif len(approx) > 5:
        if is_star(approx):
            return "Star"
        
        circularity = 4 * np.pi * area / (peri * peri)
        
        if circularity > 0.95:
            return "Circle"
        elif 0.8 <= circularity < 0.95:
            ellipse = cv2.fitEllipse(contour)
            major_axis, minor_axis = max(ellipse[1]), min(ellipse[1])
            if major_axis / minor_axis > 1.05:
                return "Ellipse"
            else:
                return "Circle"
    
    return " "

def is_square(contour, tolerance=0.05):
    """Checks if the contour forms a square."""
    rect = cv2.minAreaRect(contour)
    box = cv2.boxPoints(rect)
    box = np.intp(box)

    width = np.linalg.norm(box[0] - box[1])
    height = np.linalg.norm(box[1] - box[2])
    aspect_ratio = min(width, height) / max(width, height)

    contour_area = cv2.contourArea(contour)
    rect_area = width * height
    area_ratio = contour_area / rect_area

    is_square_shape = abs(1 - aspect_ratio) < tolerance
    is_filled = area_ratio > (1 - tolerance)

    return is_square_shape and is_filled

def is_star(approx):
    """Checks if the contour forms a star shape."""
    num_vertices = len(approx)
    if num_vertices > 8:
        angles = []
        for i in range(num_vertices):
            p1 = approx[i][0]
            p2 = approx[(i + 1) % num_vertices][0]
            p3 = approx[(i + 2) % num_vertices][0]
            angle = calculate_angle(p1, p2, p3)
            angles.append(angle)
        
        sharp_angles = [angle for angle in angles if angle < 60 or angle > 300]
        if len(sharp_angles) >= 5:
            return True

    return False

def calculate_angle(p1, p2, p3):
    """Calculates the angle between three points."""
    a = np.array(p1)
    b = np.array(p2)
    c = np.array(p3)
    ab = b - a
    cb = b - c
    cosine_angle = np.dot(ab, cb) / (np.linalg.norm(ab) * np.linalg.norm(cb))
    angle = np.degrees(np.arccos(cosine_angle))
    return angle

def is_straight_line(XY, tolerance=0.05):
    """Checks if the points form a straight line."""
    if len(XY) < 3:
        return True
    
    total_length = np.sum(np.sqrt(np.sum(np.diff(XY, axis=0)**2, axis=1)))
    start_to_end = np.linalg.norm(XY[-1] - XY[0])
    return (total_length - start_to_end) / total_length < tolerance

def regularize_circle(XY):
    """Regularizes the points to form a perfect circle."""
    xc, yc, r = kasa_circle_fitting(XY)
    t = np.linspace(0, 2 * np.pi, 100)
    x_fit = xc + r * np.cos(t)
    y_fit = yc + r * np.sin(t)
    return np.vstack((x_fit, y_fit)).T

def kasa_circle_fitting(XY):
    """Fits a circle to the points using Kasa's method."""
    x = XY[:, 0]
    y = XY[:, 1]
    A = np.array([x, y, np.ones_like(x)]).T
    b = x**2 + y**2
    c = np.linalg.lstsq(A, b, rcond=None)[0]
    xc = c[0] / 2
    yc = c[1] / 2
    r = np.sqrt(c[2] + xc**2 + yc**2)
    return xc, yc, r

def regularize_ellipse(XY):
    """Regularizes the points to form a perfect ellipse."""
    x_min, y_min = np.min(XY, axis=0)
    x_max, y_max = np.max(XY, axis=0)
    center = [(x_min + x_max) / 2, (y_min + y_max) / 2]
    width = x_max - x_min
    height = y_max - y_min
    t = np.linspace(0, 2 * np.pi, 200)
    x = center[0] + width/2 * np.cos(t)
    y = center[1] + height/2 * np.sin(t)
    
    return np.column_stack((x, y))

def regularize_rectangle(XY):
    """Regularizes the points to form a perfect rectangle."""
    x_min, y_min = np.min(XY, axis=0)
    x_max, y_max = np.max(XY, axis=0)

    regularized = np.array([
        [x_min, y_min],
        [x_max, y_min],
        [x_max, y_max],
        [x_min, y_max],
        [x_min, y_min]  
    ])
    
    return regularized

def regularize_square(XY):
    """Regularizes the points to form a perfect square."""
    x_min, y_min = np.min(XY, axis=0)
    x_max, y_max = np.max(XY, axis=0)

    side_length = min(x_max - x_min, y_max - y_min)

    reg_XY = np.array([
        [x_min, y_min],
        [x_min + side_length, y_min],
        [x_min + side_length, y_min + side_length],
        [x_min, y_min + side_length],
        [x_min, y_min]  
    ])
    
    return reg_XY

def regularize_star(XY, num_points=5):
    """Regularizes the points to form a perfect star."""
    centroid = np.mean(XY, axis=0)

    radii = np.linalg.norm(XY - centroid, axis=1)
    outer_radius = np.max(radii)
    inner_radius = np.min(radii)
    
    angles = np.linspace(0, 2*np.pi, 2*num_points, endpoint=False)
    
    star_points = []
    for i, angle in enumerate(angles):
        r = outer_radius if i % 2 == 0 else inner_radius
        x = centroid[0] + r * np.cos(angle)
        y = centroid[1] + r * np.sin(angle)
        star_points.append([x, y])
    
    star_points.append(star_points[0])  
    
    return np.array(star_points)

def regularize_straight_line(XY):
    """Regularizes the points to form a perfect straight line."""
    return np.array([XY[0], XY[-1]])

def detect_and_regularize_shape(XY):
    """Detects the shape and returns the regularized points."""
    if is_straight_line(XY):
        return "Straight Line", regularize_straight_line(XY)
    
    shape = detect_shape(XY)
    if shape == "Circle":
        regularized_XY = regularize_circle(XY)
    elif shape == "Ellipse":
        regularized_XY = regularize_ellipse(XY)
    elif shape == "Rectangle":
        regularized_XY = regularize_rectangle(XY)
    elif shape == "Square":
        regularized_XY = regularize_square(XY)
    elif shape == "Star":
        regularized_XY = regularize_star(XY)
    else:
        regularized_XY = XY
    return shape, regularized_XY

def calculate_symmetry_lines(XY, shape_type):
    """Calculates symmetry lines for the given shape."""
    if shape_type in ["Circle", "Ellipse", "Square", "Rectangle"]:
        centroid = np.mean(XY, axis=0)
        if shape_type == "Circle" or shape_type == "Square":
            return [
                ([centroid[0], np.min(XY[:, 1])], [centroid[0], np.max(XY[:, 1])]),  # Vertical
                ([np.min(XY[:, 0]), centroid[1]], [np.max(XY[:, 0]), centroid[1]])   # Horizontal
            ]
        elif shape_type == "Ellipse" or shape_type == "Rectangle":
            return [
                ([np.min(XY[:, 0]), centroid[1]], [np.max(XY[:, 0]), centroid[1]])   # Horizontal
            ]
    elif shape_type == "Star":
        centroid = np.mean(XY, axis=0)
        angles = np.linspace(0, 2*np.pi, 5, endpoint=False)
        lines = []
        for angle in angles:
            x = centroid[0] + np.cos(angle) * 1000
            y = centroid[1] + np.sin(angle) * 1000
            lines.append(([centroid[0], centroid[1]], [x, y]))
        return lines
    return []

def plot_shapes(path_XYs, regularized_XYs):
    """Plots original and regularized shapes with symmetry lines, and prints information about detected shapes."""
    fig, ax = plt.subplots(1, 2, tight_layout=True, figsize=(16, 8))
    colors = ['r', 'g', 'b', 'c', 'm', 'y', 'k']
    
    for a in ax:
        a.set_aspect('equal')

    # Print shapes detected and regularized in bold
    print("\033[1mShapes Detected and Regularized:\033[0m")

    for i, (orig_XYs, reg_XYs) in enumerate(zip(path_XYs, regularized_XYs)):
        color = colors[i % len(colors)]
        for XY, reg_XY in zip(orig_XYs, reg_XYs):
            shape_type, _ = detect_and_regularize_shape(XY)
            
            # Print the name of the detected shape
            print(f"- {shape_type}")

            ax[0].plot(XY[:, 0], XY[:, 1], c=color, linewidth=2)
            ax[1].plot(reg_XY[:, 0], reg_XY[:, 1], c=color, linewidth=2)
            
            # Draw symmetry lines and count them
            draw_symmetry_lines(ax[1], shape_type, reg_XY, 'k')

    ax[0].set_title('Original Shapes')
    ax[1].set_title('Regularized Shapes with Symmetry Lines')

    plt.show()

def draw_symmetry_lines(ax, shape, reg_XY, color):
    def plot_line(ax, start, end, color):
        ax.plot([start[0], end[0]], [start[1], end[1]], color=color, linestyle='--', linewidth=1)

    if shape == "Ellipse":
        x_min, y_min = np.min(reg_XY, axis=0)
        x_max, y_max = np.max(reg_XY, axis=0)
        center_x, center_y = (x_min + x_max) / 2, (y_min + y_max) / 2
        plot_line(ax, [x_min, center_y], [x_max, center_y], color)
        plot_line(ax, [center_x, y_min], [center_x, y_max], color)
        
        # Print number of symmetry lines
        print(f"  - {shape} has 2 lines of symmetry.")

    elif shape == "Rectangle":
        x_min, y_min = np.min(reg_XY, axis=0)
        x_max, y_max = np.max(reg_XY, axis=0)
        plot_line(ax, [(x_min + x_max)/2, y_min], [(x_min + x_max)/2, y_max], color)
        plot_line(ax, [x_min, (y_min + y_max)/2], [x_max, (y_min + y_max)/2], color)
        
        # Print number of symmetry lines
        print(f"  - {shape} has 2 lines of symmetry.")

    elif shape == "Star":
        num_points = len(reg_XY) // 2
        for i in range(num_points):
            start_point = reg_XY[i]
            end_point = reg_XY[(i + num_points) % len(reg_XY)]
            plot_line(ax, start_point, end_point, color)
        
        # Print number of symmetry lines
        print(f"  - {shape} has {num_points} lines of symmetry.")

    else:
        print(f"  - No specific symmetry lines defined for {shape}.")

def polylines2csv(paths_XYs, csv_path):
    """Writes the regularized shapes to a CSV file."""
    with open(csv_path, 'w', newline='') as f:
        writer = csv.writer(f)
        for path_XYs in paths_XYs:
            for i, XY in enumerate(path_XYs):
                for point in XY:
                    writer.writerow([i] + point.tolist())

def main():
    input_csv = "problems/isolated.csv"
    output_csv = "regularized_shapes.csv"
    path_XYs = read_csv(input_csv)
    regularized_XYs = []

    for path in path_XYs:
        reg_XYs = []
        for XY in path:
            _, reg_XY = detect_and_regularize_shape(XY)
            reg_XYs.append(reg_XY)
        regularized_XYs.append(reg_XYs)

    plot_shapes(path_XYs, regularized_XYs)
    polylines2csv(regularized_XYs, output_csv)

if __name__ == "__main__":
    main()


now i converted this into c++
// shape_regularization.cpp

#include <opencv2/opencv.hpp>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <cmath>
#include <algorithm>
#include <utility>
#include <map>
#include <iomanip>

// Type Definitions
using Point2D = cv::Point2d;
using Path = std::vector<Point2D>;
using Paths = std::vector<Path>;

// Function Prototypes
Paths read_csv(const std::string& csv_path);
double calculate_angle(const Point2D& p1, const Point2D& p2, const Point2D& p3);
bool is_straight_line(const Path& XY, double tolerance = 0.05);
bool kasa_circle_fitting(const Path& XY, double& xc, double& yc, double& r);
Path regularize_circle(const Path& XY);
Path regularize_ellipse(const Path& XY);
Path regularize_rectangle(const Path& XY);
Path regularize_square(const Path& XY);
Path regularize_star(const Path& XY, int num_points = 5);
Path regularize_straight_line(const Path& XY);
bool is_square(const std::vector<cv::Point>& contour, double tolerance = 0.05);
bool is_star(const std::vector<cv::Point>& approx);
std::string detect_shape(const Path& XY);
std::pair<std::string, Path> detect_and_regularize_shape(const Path& XY);
std::vector<std::pair<Point2D, Point2D>> calculate_symmetry_lines(const Path& XY, const std::string& shape_type);
void draw_symmetry_lines(cv::Mat& img, const std::vector<std::pair<Point2D, Point2D>>& lines, const cv::Scalar& color = cv::Scalar(0, 0, 0));
void polylines2csv(const Paths& paths_XYs, const std::string& csv_path);
void plot_shapes(const Paths& path_XYs, const Paths& regularized_XYs, const std::vector<std::string>& detected_shapes);

// Function Implementations

// 1. Read CSV and organize points into paths
Paths read_csv(const std::string& csv_path) {
    Paths path_XYs;
    std::map<int, std::map<int, Path>> paths_map; 

    std::ifstream file(csv_path);
    if (!file.is_open()) {
        std::cerr << "Failed to open " << csv_path << std::endl;
        return path_XYs;
    }

    std::string line;
    int line_num = 0;
    while (std::getline(file, line)) {
        line_num++;
        if (line.empty()) {
            std::cerr << "Empty line at line number " << line_num << ". Skipping.\n";
            continue;
        }

        std::stringstream ss(line);
        std::string item;
        std::vector<std::string> tokens;
        while (std::getline(ss, item, ',')) {
            tokens.emplace_back(item);
        }

        if (tokens.size() >= 4) {
            int path_id, subpath_id;
            try {
                path_id = std::stoi(tokens[0]);
                subpath_id = std::stoi(tokens[1]); 
            } catch (const std::invalid_argument& e) {
                std::cerr << "Invalid Path/SubPath ID at line " << line_num << ": \"" 
                          << tokens[0] << "\", \"" << tokens[1] << "\". Skipping line.\n";
                continue;
            }

            double x, y;
            try {
                x = std::stod(tokens[2]); 
                y = std::stod(tokens[3]); 
            } catch (const std::invalid_argument& e) {
                std::cerr << "Invalid coordinate data at line " << line_num << ". Skipping point.\n";
                continue;
            }

            paths_map[path_id][subpath_id].emplace_back(Point2D(x, y));
        }
        else {
            std::cerr << "Insufficient data at line " << line_num 
                      << ". Expected at least 4 values, got " << tokens.size() 
                      << ". Skipping.\n";
        }
    }

    double y_max_global = std::numeric_limits<double>::lowest();
    for (const auto& [path_id, subpaths] : paths_map) {
        for (const auto& [subpath_id, path] : subpaths) {
            for (const auto& pt : path) {
                if (pt.y > y_max_global) y_max_global = pt.y;
            }
        }
    }

    for (const auto& [path_id, subpaths] : paths_map) {
        for (const auto& [subpath_id, path] : subpaths) {
            if (!path.empty()) {
                Path inverted_path;
                for (const auto& pt : path) {
                    inverted_path.emplace_back(Point2D(pt.x, y_max_global - pt.y));
                }
                path_XYs.emplace_back(inverted_path);
                std::cout << "Loaded Path ID " << path_id << ", SubPath ID " << subpath_id 
                          << " with " << path.size() << " points.\n";
            }
        }
    }

    if (path_XYs.empty()) {
        std::cerr << "No valid paths found in the CSV file.\n";
    }

    return path_XYs;
}

// 2. Calculate angle between three points
double calculate_angle(const Point2D& p1, const Point2D& p2, const Point2D& p3) {
    cv::Point2d a = p1 - p2;
    cv::Point2d b = p3 - p2;

    double dot_product = a.dot(b);
    double cross_product = a.x * b.y - a.y * b.x;
    double angle = std::atan2(cross_product, dot_product) * 180.0 / CV_PI;
    if (angle < 0) angle += 360.0;
    return angle;
}

// 3. Check if path is a straight line
bool is_straight_line(const Path& XY, double tolerance) {
    if (XY.size() < 3) return true;

    double total_length = 0.0;
    for (size_t i = 1; i < XY.size(); ++i) {
        double dx = XY[i].x - XY[i - 1].x;
        double dy = XY[i].y - XY[i - 1].y;
        total_length += std::sqrt(dx * dx + dy * dy);
    }
    double start_to_end = cv::norm(XY.back() - XY.front());
    return (std::abs(total_length - start_to_end) / total_length) < tolerance;
}

// 4. Kasa's method for circle fitting
bool kasa_circle_fitting(const Path& XY, double& xc, double& yc, double& r) {
    int n = XY.size();
    if (n < 3) return false;

    cv::Mat A(n, 3, CV_64F);
    cv::Mat B(n, 1, CV_64F);
    for (int i = 0; i < n; ++i) {
        A.at<double>(i, 0) = XY[i].x;
        A.at<double>(i, 1) = XY[i].y;
        A.at<double>(i, 2) = 1.0;
        B.at<double>(i, 0) = XY[i].x * XY[i].x + XY[i].y * XY[i].y;
    }

    cv::Mat C;
    if (!cv::solve(A, B, C, cv::DECOMP_SVD)) {
        std::cerr << "Circle fitting failed." << std::endl;
        return false;
    }
    xc = C.at<double>(0, 0) / 2.0;
    yc = C.at<double>(1, 0) / 2.0;
    r = std::sqrt(C.at<double>(2, 0) + xc * xc + yc * yc);

    return true;
}

// 5. Regularize to a perfect circle
Path regularize_circle(const Path& XY) {
    double xc, yc, r;
    if (!kasa_circle_fitting(XY, xc, yc, r)) {
        return XY;
    }

    Path regularized;
    int num_points = 100; // Higher number for smoother circle
    for (int i = 0; i < num_points; ++i) {
        double theta = 2 * CV_PI * i / num_points;
        regularized.emplace_back(cv::Point2d(xc + r * std::cos(theta), yc + r * std::sin(theta)));
    }
    regularized.emplace_back(regularized.front());
    return regularized;
}

// 6. Regularize to a perfect ellipse
Path regularize_ellipse(const Path& XY) {
    if (XY.size() < 5) {
        std::cerr << "Not enough points to fit an ellipse." << std::endl;
        return XY;
    }

    cv::Mat points_mat(XY.size(), 1, CV_32FC2);
    for (size_t i = 0; i < XY.size(); ++i) {
        points_mat.at<cv::Point2f>(i, 0) = cv::Point2f(static_cast<float>(XY[i].x), static_cast<float>(XY[i].y));
    }

    cv::RotatedRect ellipse;
    try {
        ellipse = cv::fitEllipse(points_mat);
    }
    catch (const cv::Exception& e) {
        std::cerr << "Ellipse fitting failed: " << e.what() << std::endl;
        return XY;
    }

    Path regularized;
    int num_points = 200; // Higher number for smoother ellipse
    for (int i = 0; i < num_points; ++i) {
        double theta = 2 * CV_PI * i / num_points;
        double x = ellipse.center.x + (ellipse.size.width / 2.0) * std::cos(theta);
        double y = ellipse.center.y + (ellipse.size.height / 2.0) * std::sin(theta);
        regularized.emplace_back(cv::Point2d(x, y));
    }
    regularized.emplace_back(regularized.front());
    return regularized;
}

// 7. Regularize to a perfect rectangle
Path regularize_rectangle(const Path& XY) {
    double x_min = XY[0].x, y_min = XY[0].y;
    double x_max = XY[0].x, y_max = XY[0].y;

    for (const auto& pt : XY) {
        if (pt.x < x_min) x_min = pt.x;
        if (pt.x > x_max) x_max = pt.x;
        if (pt.y < y_min) y_min = pt.y;
        if (pt.y > y_max) y_max = pt.y;
    }

    Path regularized = {
        cv::Point2d(x_min, y_min),
        cv::Point2d(x_max, y_min),
        cv::Point2d(x_max, y_max),
        cv::Point2d(x_min, y_max),
        cv::Point2d(x_min, y_min)
    };
    return regularized;
}

// 8. Regularize to a perfect square
Path regularize_square(const Path& XY) {
    double x_min = XY[0].x, y_min = XY[0].y;
    double x_max = XY[0].x, y_max = XY[0].y;

    for (const auto& pt : XY) {
        if (pt.x < x_min) x_min = pt.x;
        if (pt.x > x_max) x_max = pt.x;
        if (pt.y < y_min) y_min = pt.y;
        if (pt.y > y_max) y_max = pt.y;
    }

    double side_length = std::min(x_max - x_min, y_max - y_min);

    Path regularized = {
        cv::Point2d(x_min, y_min),
        cv::Point2d(x_min + side_length, y_min),
        cv::Point2d(x_min + side_length, y_min + side_length),
        cv::Point2d(x_min, y_min + side_length),
        cv::Point2d(x_min, y_min)
    };
    return regularized;
}

// 9. Regularize to a perfect star
Path regularize_star(const Path& XY, int num_points) {
    cv::Point2d centroid(0.0, 0.0);
    for (const auto& pt : XY) {
        centroid += pt;
    }
    centroid *= (1.0 / XY.size());

    double outer_radius = 0.0, inner_radius = std::numeric_limits<double>::max();
    for (const auto& pt : XY) {
        double dist = cv::norm(pt - centroid);
        if (dist > outer_radius) outer_radius = dist;
        if (dist < inner_radius) inner_radius = dist;
    }
    inner_radius = outer_radius * 0.5; // Typically, inner radius is half of outer

    Path regularized;
    double angle_step = CV_PI / num_points; 
    for (int i = 0; i < 2 * num_points; ++i) {
        double angle = i * angle_step;
        double r = (i % 2 == 0) ? outer_radius : inner_radius;
        double x = centroid.x + r * std::cos(angle);
        double y = centroid.y + r * std::sin(angle);
        regularized.emplace_back(cv::Point2d(x, y));
    }
    regularized.emplace_back(regularized.front()); // Close the star
    return regularized;
}

// 10. Regularize to a straight line
Path regularize_straight_line(const Path& XY) {
    return { XY.front(), XY.back() };
}

// 11. Check if a contour is a square
bool is_square(const std::vector<cv::Point>& contour, double tolerance) {
    if (contour.size() != 4) return false;

    cv::RotatedRect rect = cv::minAreaRect(contour);
    double width = rect.size.width;
    double height = rect.size.height;
    double aspect_ratio = std::min(width, height) / std::max(width, height);

    double contour_area = cv::contourArea(contour);
    double rect_area = width * height;
    double area_ratio = contour_area / rect_area;

    bool is_square_shape = std::abs(1.0 - aspect_ratio) < tolerance;
    bool is_filled = area_ratio > (1.0 - tolerance);

    return is_square_shape && is_filled;
}

// 12. Check if a contour is a star
bool is_star(const std::vector<cv::Point>& approx) {
    int num_vertices = approx.size();
    if (num_vertices > 8) {
        std::vector<double> angles;
        for (int i = 0; i < num_vertices; ++i) {
            cv::Point2d p1 = cv::Point2d(approx[i].x, approx[i].y);
            cv::Point2d p2 = cv::Point2d(approx[(i + 1) % num_vertices].x, approx[(i + 1) % num_vertices].y);
            cv::Point2d p3 = cv::Point2d(approx[(i + 2) % num_vertices].x, approx[(i + 2) % num_vertices].y);
            double angle = calculate_angle(p1, p2, p3);
            angles.push_back(angle);
        }

        int sharp_angles = 0;
        for (double angle : angles) {
            if (angle < 60.0 || angle > 300.0) {
                sharp_angles++;
            }
        }
        if (sharp_angles >= 5) {
            return true;
        }
    }
    return false;
}

// 13. Detect shape type
std::string detect_shape(const Path& XY) {
    std::vector<cv::Point2f> contour_floats;
    for (const auto& pt : XY) {
        contour_floats.emplace_back(cv::Point2f(static_cast<float>(pt.x), static_cast<float>(pt.y)));
    }

    double peri = cv::arcLength(contour_floats, true);
    std::vector<cv::Point2f> approx_floats;
    double epsilon = 0.04 * peri;  
    cv::approxPolyDP(contour_floats, approx_floats, static_cast<float>(epsilon), true);
    double area = cv::contourArea(contour_floats);

    if (area < 100.0) {
        return " ";
    }

    if (is_straight_line(XY)) {
        return "Straight Line";
    }

    if (approx_floats.size() == 4) {
        std::vector<cv::Point> approx_int;
        for (const auto& pt : approx_floats) {
            approx_int.emplace_back(cv::Point(static_cast<int>(pt.x), static_cast<int>(pt.y)));
        }

        if (is_square(approx_int, 0.05)) {
            return "Square";
        }
        else {
            return "Rectangle";
        }
    }
    else if (approx_floats.size() > 5) {
        std::vector<cv::Point> approx_int;
        for (const auto& pt : approx_floats) {
            approx_int.emplace_back(cv::Point(static_cast<int>(pt.x), static_cast<int>(pt.y)));
        }

        if (is_star(approx_int)) {
            return "Star";
        }

        double circularity = 4 * CV_PI * area / (peri * peri);

        if (circularity > 0.95) {
            return "Circle";
        }
        else if (circularity >= 0.8 && circularity < 0.95) {
            if (contour_floats.size() < 5) return " ";
            cv::RotatedRect ellipse;
            try {
                ellipse = cv::fitEllipse(contour_floats);
            }
            catch (const cv::Exception& e) {
                std::cerr << "Ellipse fitting failed: " << e.what() << std::endl;
                return " ";
            }
            double major_axis = std::max(ellipse.size.width, ellipse.size.height);
            double minor_axis = std::min(ellipse.size.width, ellipse.size.height);
            if ((major_axis / minor_axis) > 1.05) {
                return "Ellipse";
            }
            else {
                return "Circle";
            }
        }
    }

    return " ";
}

// 14. Detect and regularize shape
std::pair<std::string, Path> detect_and_regularize_shape(const Path& XY) {
    if (is_straight_line(XY)) {
        std::cout << "Detected: Straight Line.\n";
        return { "Straight Line", regularize_straight_line(XY) };
    }

    std::string shape = detect_shape(XY);
    Path regularized_XY = XY;

    if (shape != " ") {
        std::cout << "Detected shape: " << shape << "\n";
    } else {
        std::cout << "Detected shape: Unknown\n";
    }

    if (shape == "Circle") {
        regularized_XY = regularize_circle(XY);
        std::cout << "Regularized to Circle.\n";
    }
    else if (shape == "Ellipse") {
        regularized_XY = regularize_ellipse(XY);
        std::cout << "Regularized to Ellipse.\n";
    }
    else if (shape == "Rectangle") {
        regularized_XY = regularize_rectangle(XY);
        std::cout << "Regularized to Rectangle.\n";
    }
    else if (shape == "Square") {
        regularized_XY = regularize_square(XY);
        std::cout << "Regularized to Square.\n";
    }
    else if (shape == "Star") {
        regularized_XY = regularize_star(XY);
        std::cout << "Regularized to Star.\n";
    }
    else {
        std::cout << "Shape not recognized for regularization. Keeping original path.\n";
    }

    return { shape, regularized_XY };
}

// 15. Calculate symmetry lines
std::vector<std::pair<Point2D, Point2D>> calculate_symmetry_lines(const Path& XY, const std::string& shape_type) {
    std::vector<std::pair<Point2D, Point2D>> lines;
    if (shape_type == "Circle" || shape_type == "Square") {
        // Calculate centroid
        cv::Point2d centroid(0.0, 0.0);
        for (const auto& pt : XY) {
            centroid += pt;
        }
        centroid *= (1.0 / XY.size());

        // Vertical and Horizontal symmetry lines through centroid
        lines.emplace_back(std::make_pair(
            cv::Point2d(centroid.x, std::numeric_limits<double>::lowest()),
            cv::Point2d(centroid.x, std::numeric_limits<double>::max())
        ));

        lines.emplace_back(std::make_pair(
            cv::Point2d(std::numeric_limits<double>::lowest(), centroid.y),
            cv::Point2d(std::numeric_limits<double>::max(), centroid.y)
        ));

        // For squares, add diagonal symmetry lines
        if (shape_type == "Square") {
            lines.emplace_back(std::make_pair(
                cv::Point2d(std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest()),
                cv::Point2d(std::numeric_limits<double>::max(), std::numeric_limits<double>::max())
            ));
            lines.emplace_back(std::make_pair(
                cv::Point2d(std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max()),
                cv::Point2d(std::numeric_limits<double>::max(), std::numeric_limits<double>::lowest())
            ));
        }
    }
    else if (shape_type == "Ellipse" || shape_type == "Rectangle") {
        // Calculate centroid
        cv::Point2d centroid(0.0, 0.0);
        for (const auto& pt : XY) {
            centroid += pt;
        }
        centroid *= (1.0 / XY.size());

        // Horizontal symmetry line through centroid
        lines.emplace_back(std::make_pair(
            cv::Point2d(std::numeric_limits<double>::lowest(), centroid.y),
            cv::Point2d(std::numeric_limits<double>::max(), centroid.y)
        ));

        // For ellipses, you might also want to add major and minor axes lines
        if (shape_type == "Ellipse") {
            // Fit ellipse to get orientation
            cv::Mat points_mat(XY.size(), 1, CV_32FC2);
            for (size_t i = 0; i < XY.size(); ++i) {
                points_mat.at<cv::Point2f>(i, 0) = cv::Point2f(static_cast<float>(XY[i].x), static_cast<float>(XY[i].y));
            }

            try {
                cv::RotatedRect ellipse = cv::fitEllipse(points_mat);
                double angle_deg = ellipse.angle;
                double angle_rad = angle_deg * CV_PI / 180.0;

                // Major axis
                double dx = std::cos(angle_rad);
                double dy = std::sin(angle_rad);
                double line_length = 1000.0; // Extend sufficiently

                cv::Point2d major_start = centroid - cv::Point2d(dx * line_length, dy * line_length);
                cv::Point2d major_end = centroid + cv::Point2d(dx * line_length, dy * line_length);
                lines.emplace_back(std::make_pair(major_start, major_end));

                // Minor axis
                double angle_minor_rad = angle_rad + CV_PI / 2.0;
                double dx_minor = std::cos(angle_minor_rad);
                double dy_minor = std::sin(angle_minor_rad);
                cv::Point2d minor_start = centroid - cv::Point2d(dx_minor * line_length, dy_minor * line_length);
                cv::Point2d minor_end = centroid + cv::Point2d(dx_minor * line_length, dy_minor * line_length);
                lines.emplace_back(std::make_pair(minor_start, minor_end));
            }
            catch (const cv::Exception& e) {
                std::cerr << "Ellipse fitting failed during symmetry calculations: " << e.what() << std::endl;
            }
        }
    }
    else if (shape_type == "Star") {
        // Calculate centroid
        cv::Point2d centroid(0.0, 0.0);
        for (const auto& pt : XY) {
            centroid += pt;
        }
        centroid *= (1.0 / XY.size());

        // 5 equally spaced symmetry lines from centroid
        double angle_step = 2 * CV_PI / 5;
        double line_length = 1000.0; // Arbitrary large value
        for (int i = 0; i < 5; ++i) {
            double angle = i * angle_step;
            cv::Point2d end_point = centroid + cv::Point2d(std::cos(angle) * line_length, std::sin(angle) * line_length);
            cv::Point2d start_point = centroid - cv::Point2d(std::cos(angle) * line_length, std::sin(angle) * line_length);
            lines.emplace_back(std::make_pair(start_point, end_point));
        }
    }
    else if (shape_type == "Straight Line") {
        // Calculate a long line along the direction of the straight line
        cv::Point2d start = XY.front();
        cv::Point2d end = XY.back();
        cv::Point2d centroid = (start + end) * 0.5;
        cv::Point2d direction = end - start;
        double norm_dir = cv::norm(direction);
        if (norm_dir == 0) norm_dir = 1.0;
        cv::Point2d unit_dir = direction / norm_dir;

        double line_length = 1000.0; // Extend sufficiently
        cv::Point2d extended_start = centroid - unit_dir * line_length;
        cv::Point2d extended_end = centroid + unit_dir * line_length;

        lines.emplace_back(std::make_pair(extended_start, extended_end));
    }
    return lines;
}

// 16. Draw symmetry lines on image
void draw_symmetry_lines(cv::Mat& img, const std::vector<std::pair<Point2D, Point2D>>& lines, const cv::Scalar& color) {
    for (const auto& line : lines) {
        cv::line(img,
                 cv::Point(static_cast<int>(std::round(line.first.x)),
                           static_cast<int>(std::round(line.first.y))),
                 cv::Point(static_cast<int>(std::round(line.second.x)),
                           static_cast<int>(std::round(line.second.y))),
                 color, 1, cv::LINE_AA);
    }
}

// 17. Write regularized paths to CSV
void polylines2csv(const Paths& paths_XYs, const std::string& csv_path) {
    std::ofstream file(csv_path);
    if (!file.is_open()) {
        std::cerr << "Failed to open " << csv_path << " for writing." << std::endl;
        return;
    }

    file << std::fixed << std::setprecision(6); // Consistent decimal places

    for (size_t i = 0; i < paths_XYs.size(); ++i) {
        const Path& path = paths_XYs[i];
        for (size_t j = 0; j < path.size(); ++j) {
            file << i << "," << j << "," << path[j].x << "," << path[j].y << "\n";
        }
    }
}

// 18. Plot original and regularized shapes with symmetry lines
void plot_shapes(const Paths& path_XYs, const Paths& regularized_XYs, const std::vector<std::string>& detected_shapes) {
    // Determine global bounding box
    double x_min = std::numeric_limits<double>::max(), y_min = std::numeric_limits<double>::max();
    double x_max = std::numeric_limits<double>::lowest(), y_max = std::numeric_limits<double>::lowest();

    for (const auto& path : path_XYs) {
        for (const auto& pt : path) {
            if (pt.x < x_min) x_min = pt.x;
            if (pt.y < y_min) y_min = pt.y;
            if (pt.x > x_max) x_max = pt.x;
            if (pt.y > y_max) y_max = pt.y;
        }
    }

    for (const auto& reg_path : regularized_XYs) {
        for (const auto& pt : reg_path) {
            if (pt.x < x_min) x_min = pt.x;
            if (pt.y < y_min) y_min = pt.y;
            if (pt.x > x_max) x_max = pt.x;
            if (pt.y > y_max) y_max = pt.y;
        }
    }

    std::cout << "\nCoordinate Ranges:\n";
    std::cout << "X: [" << x_min << ", " << x_max << "]\n";
    std::cout << "Y: [" << y_min << ", " << y_max << "]\n";

    // Add padding and determine scale
    int padding = 50;
    double bbox_width = x_max - x_min;
    double bbox_height = y_max - y_min;
    double scale_x = (1000.0 - 2.0 * padding) / bbox_width;
    double scale_y = (1000.0 - 2.0 * padding) / bbox_height;
    double scale = std::min(scale_x, scale_y); 

    int width = static_cast<int>(std::ceil(bbox_width * scale)) + 2 * padding;
    int height = static_cast<int>(std::ceil(bbox_height * scale)) + 2 * padding;

    const int min_size = 500;
    if (width < min_size) width = min_size;
    if (height < min_size) height = min_size;

    std::cout << "Image Size: " << width << "x" << height << "\n";

    // Create blank white images
    cv::Mat original_img(height, width, CV_8UC3, cv::Scalar(255, 255, 255));
    cv::Mat regularized_img(height, width, CV_8UC3, cv::Scalar(255, 255, 255));

    // Define colors
    std::vector<cv::Scalar> colors = { cv::Scalar(0,0,255), cv::Scalar(0,255,0),
                                      cv::Scalar(255,0,0), cv::Scalar(255,255,0),
                                      cv::Scalar(255,0,255), cv::Scalar(0,255,255),
                                      cv::Scalar(0,0,0) };

    std::cout << "\033[1mShapes Detected and Regularized:\033[0m\n";

    // Process each shape
    for (size_t i = 0; i < path_XYs.size(); ++i) {
        const Path& orig_path = path_XYs[i];
        const Path& reg_path = regularized_XYs[i];
        const std::string& shape_type = detected_shapes[i];
        cv::Scalar color = colors[i % colors.size()];

        // Transform original path points to image coordinates
        std::vector<cv::Point> orig_contour;
        for (const auto& pt : orig_path) {
            int x = static_cast<int>((pt.x - x_min) * scale) + padding;
            int y = static_cast<int>((pt.y - y_min) * scale) + padding;
            orig_contour.emplace_back(cv::Point(x, y));
        }
        if (orig_contour.size() > 1) {
            cv::polylines(original_img, orig_contour, false, color, 2);
            for (const auto& pt : orig_contour) {
                cv::circle(original_img, pt, 3, color, -1);
            }
        }

        // Transform regularized path points to image coordinates
        std::vector<cv::Point> reg_contour;
        for (const auto& pt : reg_path) {
            int x = static_cast<int>((pt.x - x_min) * scale) + padding;
            int y = static_cast<int>((pt.y - y_min) * scale) + padding;
            reg_contour.emplace_back(cv::Point(x, y));
        }
        if (reg_contour.size() > 1) {
            cv::polylines(regularized_img, reg_contour, false, color, 2);
            for (const auto& pt : reg_contour) {
                cv::circle(regularized_img, pt, 3, color, -1);
            }
        }

        // Print detected shape
        std::cout << "- " << (shape_type.empty() || shape_type == " " ? "Unknown" : shape_type) << "\n";

        // Calculate symmetry lines based on regularized shape
        std::vector<std::pair<Point2D, Point2D>> symmetry_lines = calculate_symmetry_lines(reg_path, shape_type);
        std::vector<std::pair<cv::Point2d, cv::Point2d>> transformed_lines;

        for (auto& line : symmetry_lines) {
            // Transform symmetry lines to image coordinates
            cv::Point2d transformed_start = cv::Point2d((line.first.x - x_min) * scale + padding, 
                                                        (line.first.y - y_min) * scale + padding);
            cv::Point2d transformed_end = cv::Point2d((line.second.x - x_min) * scale + padding, 
                                                      (line.second.y - y_min) * scale + padding);
            transformed_lines.emplace_back(std::make_pair(transformed_start, transformed_end));
        }
        draw_symmetry_lines(regularized_img, transformed_lines, cv::Scalar(0, 0, 0));

        // Print symmetry information
        if (shape_type == "Ellipse" || shape_type == "Rectangle") {
            std::cout << "  - " << shape_type << " has 1 line of symmetry.\n";
        }
        else if (shape_type == "Circle" || shape_type == "Square") {
            if (shape_type == "Square") {
                std::cout << "  - " << shape_type << " has 4 lines of symmetry.\n";
            }
            else {
                std::cout << "  - " << shape_type << " has 2 lines of symmetry.\n";
            }
        }
        else if (shape_type == "Star") {
            std::cout << "  - " << shape_type << " has 5 lines of symmetry.\n";
        }
        else if (shape_type == "Straight Line") {
            std::cout << "  - " << shape_type << " has infinite lines of symmetry.\n";
        }
        else {
            std::cout << "  - No specific symmetry lines defined for " 
                      << (shape_type.empty() || shape_type == " " ? "Unknown" : shape_type) 
                      << ".\n";
        }

        // Optionally, print regularized shape points
        /*
        std::cout << "  Regularized Shape Points:\n";
        for (const auto& pt : reg_path) {
            std::cout << "(" << pt.x << ", " << pt.y << ") ";
        }
        std::cout << "\n";
        */
    }

    // Save images
    bool success1 = cv::imwrite("original_shapes.png", original_img);
    bool success2 = cv::imwrite("regularized_shapes.png", regularized_img);
    if (success1 && success2) {
        std::cout << "Images saved successfully as 'original_shapes.png' and 'regularized_shapes.png'.\n";
    }
    else {
        std::cerr << "Failed to save images.\n";
    }

    // Display images
    cv::imshow("Original Shapes", original_img);
    cv::imshow("Regularized Shapes with Symmetry Lines", regularized_img);
    cv::waitKey(0);
}

// 19. Main Function
int main(int argc, char* argv[]) {
    // Handle command-line arguments for flexibility (optional)
    std::string input_csv = "problems/isolated.csv"; 
    std::string output_csv = "regularized_shapes.csv";

    // Optional: Parse command-line arguments
    if (argc >= 2) {
        input_csv = argv[1];
    }
    if (argc >= 3) {
        output_csv = argv[2];
    }

    // Read CSV
    Paths path_XYs = read_csv(input_csv);
    if (path_XYs.empty()) {
        std::cerr << "No paths found in the CSV file." << std::endl;
        return -1;
    }

    // Detect and regularize shapes
    Paths regularized_XYs;
    std::vector<std::string> detected_shapes; 

    for (const auto& path : path_XYs) {
        auto [shape, reg_XY] = detect_and_regularize_shape(path);
        regularized_XYs.push_back(reg_XY);
        detected_shapes.push_back(shape);
    }

    // Plot shapes and calculate symmetry lines
    plot_shapes(path_XYs, regularized_XYs, detected_shapes);

    // Write regularized shapes to CSV
    polylines2csv(regularized_XYs, output_csv);
    std::cout << "Regularized shapes have been written to " << output_csv << std::endl;

    return 0;
}


is there any function from python that is missing in my c++ script
if yes tell me which