{"record":{"id":"1fb2ce8b7ac5fbf3","repo":"louis-e/arnis","slug":"nan-encountered-while-sorting-scanline-intersectio","errorCode":null,"errorMessage":"NaN encountered while sorting scanline intersections","messagePattern":"NaN encountered while sorting scanline intersections","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/element_processing/water_areas.rs","lineNumber":357,"sourceCode":"    max_x: i32,\n) -> Vec<(i32, i32)> {\n    let mut xs: Vec<f64> = Vec::new();\n    for edge in edges {\n        // Crossing test: (z1 > z) != (z2 > z)\n        // Matches geo's convention (bottom-inclusive, top-exclusive).\n        if (edge.z1 > z) != (edge.z2 > z) {\n            let t = (z - edge.z1) / (edge.z2 - edge.z1);\n            xs.push(edge.x1 + t * (edge.x2 - edge.x1));\n        }\n    }\n\n    if xs.is_empty() {\n        return Vec::new();\n    }\n\n    xs.sort_unstable_by(|a, b| {\n        a.partial_cmp(b)\n            .expect(\"NaN encountered while sorting scanline intersections\")\n    });\n\n    debug_assert!(\n        xs.len().is_multiple_of(2),\n        \"Odd number of scanline crossings ({}) at z={}, possible malformed polygon\",\n        xs.len(),\n        z\n    );\n\n    // Pair consecutive crossings into fill spans (even-odd rule)\n    let mut spans = Vec::with_capacity(xs.len() / 2);\n    let mut i = 0;\n    while i + 1 < xs.len() {\n        let start = (xs[i].ceil() as i32).max(min_x);\n        let end = (xs[i + 1].floor() as i32).min(max_x);\n        if start <= end {\n            spans.push((start, end));\n        }","sourceCodeStart":339,"sourceCodeEnd":375,"githubUrl":"https://github.com/louis-e/arnis/blob/34048924d9365795fb0d832e76140a3fbdc413d9/src/element_processing/water_areas.rs#L339-L375","documentation":"`compute_scanline_spans` sorts the x-coordinates where polygon edges cross a scanline using `a.partial_cmp(b).expect(\"NaN encountered while sorting scanline intersections\")`. Since f64 has no total order, a NaN intersection coordinate makes `partial_cmp` return None and the code panics — NaN can only come from upstream geometry (malformed polygon vertices or a degenerate edge producing 0/0).","triggerScenarios":"A water-area polygon with NaN or infinite vertex coordinates (bad/missing OSM tags, division by zero when intersecting a horizontal scanline with a degenerate vertical edge), producing NaN scanline intersections passed into the sort.","commonSituations":"Importing map data where a node coordinate is NaN; zero-length edges in the polygon; a coordinate transform (projection) producing NaN for points outside its domain.","solutions":["Filter or fix polygon vertices upstream: reject/repair polygons containing NaN or infinite coordinates before scanline conversion","Skip degenerate edges (zero height) when computing intersections so no 0/0 occurs","Validate the projection/transform output for NaN before building polygons","As a stopgap, replace the expect with a total-order fallback (e.g. `a.partial_cmp(b).unwrap_or(Ordering::Equal)`) and log the offending polygon"],"exampleFix":"// before\n.expect(\"NaN encountered while sorting scanline intersections\")\n// after\n.unwrap_or_else(|| {\n    eprintln!(\"NaN in scanline at z={z}; skipping polygon\");\n    std::cmp::Ordering::Equal\n})","handlingStrategy":"validation","validationCode":"fn polygon_has_finite_coords(poly: &[[f64; 2]]) -> bool { poly.iter().all(|p| p[0].is_finite() && p[1].is_finite()) }","typeGuard":"fn is_finite_point(p: &[f64; 2]) -> bool { p[0].is_finite() && p[1].is_finite() }","tryCatchPattern":"// If you must sort possibly-NaN floats: xs.sort_unstable_by(|a,b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));","preventionTips":["Reject polygons containing NaN/inf coordinates at import time","Skip zero-height edges when computing scanline intersections","Validate projection outputs for finiteness before geometry ops"],"tags":["panic","nan","polygon","geometry"],"backgroundTag":"nan-coordinate-in-geometry","analyzedSha":"34048924d9365795fb0d832e76140a3fbdc413d9","analyzedAt":"2026-09-03T14:05:17.283Z","contentChangedAt":"2026-09-03T14:05:17.283Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}