ixmilia / dxf-rs

MIT License
95 stars 29 forks source link

LW_POLYLINE total length #62

Open KhalilSelyan opened 6 months ago

KhalilSelyan commented 6 months ago

I'm not really sure where to ask i did some googling etc but i couldn't find a decent way to get the right calculation, this is my current code for it but if anyone has a better solution please help.

Practically all i'm looking for is a way to calculate the total length of a lw_polyline with segments and arcs

fn calculate_lwpolyline_radius(bulge: f64, chord_length: f64) -> f64 {
    let radius = (chord_length * (1.0 + bulge.powi(2))) / (4.0 * bulge.abs());
    radius
}

fn lw_polyline_length(polyline: &dxf::entities::LwPolyline) -> f64 {
    let mut total_length = 0.0;

    for i in 0..polyline.vertices.len() - 1 {
        let start_vertex = &polyline.vertices[i];
        let end_vertex = &polyline.vertices[i + 1];

        // Calculate straight line segment length
        let line_length = ((end_vertex.x - start_vertex.x).powi(2)
            + (end_vertex.y - start_vertex.y).powi(2))
        .sqrt();

        // If the segment is an arc, calculate the arc length
        let bulge = start_vertex.bulge;
        if bulge != 0.0 {
            let theta = 4.0 * bulge.atan();
            let r = calculate_lwpolyline_radius(bulge, line_length);
            let arc_length = r * theta.abs();
            total_length += arc_length;
        } else {
            total_length += line_length;
        }
    }

    total_length
}

If this is seems correct please let me know 😅

brettfo commented 6 months ago

I don't know enough geometry to comment on calculate_lwpolyline_radius(bulge, chord_length), so I'll leave that aside.

I think you'll need to change the calculation of arc_length. A few lines above where you calculate let theta = 4.0 * bulge.atan(); looks correct and that gets you the included angle of the arc, so from there you'd want to find the ratio of that to the entire circle's circumference, 2 * pi, so I would do:

let theta = 4.0 * bulge.atan(); // this is correct; theta is the included angle
let r = calculate_lwpolyline_radius(bulge, line_length); // <-- this is the part I'm not sure about, so I'll leave it alone
let full_circle_circumference = 2.0 * std::f64::consts::PI * r; // this is the circumference of the full circle (if it wasn't an arc)
let arc_fraction = theta / (2.0 * std::f64::consts::PI); // this is how much of the circle we actually have: theta / 2pi
let arc_length = arc_fraction * full_circle_circumference; // we only have a fraction of the full circle's circumference

Finally, the LWPOLYLINE might be closed, which means there's an additional segment between polyline.vertices[polyline.vertices.len() - 1] and polyline.vertices[0]. I think there's a polyline.is_closed flag or similar that you can check.