use super::mic::{
maximum_inscribed_circle, maximum_inscribed_circle_multipolygon, MicOptions, MicUsedEngine,
};
use geo_types::{Coord, LineString, Polygon};
use libc::{c_double, c_int, size_t};
use std::slice;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct HoleInput {
pub coords: *const c_double,
pub coords_len: size_t,
}
#[repr(C)]
pub struct MicResultC {
pub center_x: c_double,
pub center_y: c_double,
pub radius: c_double,
pub radius_sq: c_double,
pub used_engine: c_int,
pub candidate_count: size_t,
pub component_index: c_int,
}
#[repr(C)]
#[derive(Clone, Copy, Default)]
pub struct MicOptionsC {
_reserved: c_int,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct MicPolyInput {
pub coords: *const c_double,
pub coords_len: size_t,
pub holes: *const HoleInput,
pub holes_len: size_t,
}
pub unsafe fn decode_coords(
coords: *const c_double,
coords_len: size_t,
) -> Option<Vec<Coord<f64>>> {
if coords.is_null() {
return None;
}
if coords_len < 6 || !coords_len.is_multiple_of(2) {
return None;
}
let s = slice::from_raw_parts(coords, coords_len);
let mut out = Vec::with_capacity(coords_len / 2);
for i in (0..coords_len).step_by(2) {
let (x, y) = (s[i], s[i + 1]);
if !x.is_finite() || !y.is_finite() {
return None;
}
out.push(Coord { x, y });
}
Some(out)
}
pub unsafe fn decode_polygon(
coords: *const c_double,
coords_len: size_t,
holes: *const HoleInput,
holes_len: size_t,
) -> Option<Polygon<f64>> {
let ext = decode_coords(coords, coords_len)?;
let mut interiors = Vec::new();
if !holes.is_null() {
let hs = slice::from_raw_parts(holes, holes_len);
for h in hs {
let ring = decode_coords(h.coords, h.coords_len)?;
if ring.len() < 3 {
return None;
}
interiors.push(LineString::from(ring));
}
}
Some(Polygon::new(LineString::from(ext), interiors))
}
pub fn guard(f: impl FnOnce() -> c_int + std::panic::UnwindSafe) -> c_int {
std::panic::catch_unwind(f).unwrap_or(-1)
}
fn engine_to(v: MicUsedEngine) -> c_int {
match v {
MicUsedEngine::Grid => 1,
MicUsedEngine::Geos => 2,
}
}
fn fill(r: &super::mic::MicResult, out: *mut MicResultC) -> c_int {
if out.is_null() {
return -1;
}
unsafe {
*out = MicResultC {
center_x: r.center.x(),
center_y: r.center.y(),
radius: r.radius,
radius_sq: r.radius_sq,
used_engine: engine_to(r.used_engine),
candidate_count: r.candidate_count as size_t,
component_index: r.component_index.map(|x| x as c_int).unwrap_or(-1),
};
}
0
}
fn opts_from(_raw: MicOptionsC) -> MicOptions {
MicOptions::default()
}
#[no_mangle]
pub unsafe extern "C" fn mic_solve(
coords: *const c_double,
coords_len: size_t,
holes: *const HoleInput,
holes_len: size_t,
options: *const MicOptionsC,
result: *mut MicResultC,
) -> c_int {
guard(|| {
if result.is_null() {
return -1;
}
let poly = match decode_polygon(coords, coords_len, holes, holes_len) {
Some(p) => p,
None => return -1,
};
let raw = if options.is_null() {
MicOptionsC::default()
} else {
*options
};
match maximum_inscribed_circle(&poly, &opts_from(raw)) {
Ok(r) => fill(&r, result),
Err(_) => -1,
}
})
}
#[no_mangle]
pub unsafe extern "C" fn mic_solve_multipolygon(
polys: *const MicPolyInput,
polys_len: size_t,
options: *const MicOptionsC,
result: *mut MicResultC,
) -> c_int {
guard(|| {
if polys.is_null() || result.is_null() {
return -1;
}
let s = std::slice::from_raw_parts(polys, polys_len);
let mut vec = Vec::with_capacity(s.len());
for p in s {
let poly = match decode_polygon(p.coords, p.coords_len, p.holes, p.holes_len) {
Some(v) => v,
None => return -1,
};
vec.push(poly);
}
let multi = geo_types::MultiPolygon(vec);
let raw = if options.is_null() {
MicOptionsC::default()
} else {
*options
};
match maximum_inscribed_circle_multipolygon(&multi, &opts_from(raw)) {
Ok(r) => fill(&r, result),
Err(_) => -1,
}
})
}
#[no_mangle]
pub extern "C" fn mic_options_default() -> MicOptionsC {
MicOptionsC::default()
}