1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use std::slice;

extern crate libc;
use self::libc::{c_void, c_double, size_t};

use super::geo::{Point, Polygon, LineString};

use super::polylabel;
use super::num::Float;

/// Wrapper for a void pointer to a sequence of [`Array`](struct.Array.html)s, and the sequence length
///
/// Used for inner Polygon rings
#[repr(C)]
pub struct WrapperArray {
    pub data: *const Array,
    pub len: size_t,
}

/// Wrapper for a void pointer to a sequence of 2-element arrays representing points, and the sequence length
///
/// Used for outer Polygon rings. `data` is a `Vec<[c_double; 2]>`
#[repr(C)]
pub struct Array {
    pub data: *const c_void,
    pub len: size_t,
}

/// FFI struct for returned optimum Polygon label position
#[repr(C)]
pub struct Position {
    pub x_pos: c_double,
    pub y_pos: c_double,
}

// convert a Polylabel result Point into values that can be sent across the FFI boundary
impl<T> From<Point<T>> for Position
    where T: Float
{
    fn from(point: Point<T>) -> Position {
        Position {
            x_pos: point.x().to_f64().unwrap() as c_double,
            y_pos: point.y().to_f64().unwrap() as c_double,
        }
    }
}

fn reconstitute(arr: &Array) -> Vec<[f64; 2]> {
    unsafe { slice::from_raw_parts(arr.data as *mut [f64; 2], arr.len).to_vec() }
}

fn reconstitute2(arr: WrapperArray) -> Vec<Vec<[f64; 2]>> {
    let arrays = unsafe { slice::from_raw_parts(arr.data as *mut Array, arr.len) };
    arrays.into_iter().map(|x| reconstitute(x)).collect()
}

/// FFI access to the [`polylabel`](fn.polylabel.html) function
///
/// Accepts three arguments:
///
/// - an exterior ring [`Array`](struct.Array.html)
/// - an interior rings [`WrapperArray`](struct.WrapperArray.html)
/// - a tolerance `c_double`.
#[no_mangle]
pub extern "C" fn polylabel_ffi(outer: Array,
                                inners: WrapperArray,
                                tolerance: c_double)
                                -> Position {
    let exterior: Vec<[f64; 2]> =
        unsafe { slice::from_raw_parts(outer.data as *mut [c_double; 2], outer.len).to_vec() };
    let interior: Vec<Vec<[f64; 2]>> = reconstitute2(inners);
    let ls_ext = LineString(exterior.iter().map(|e| Point::new(e[0], e[1])).collect());
    let ls_int: Vec<LineString<c_double>> = interior.iter()
        .map(|vec| LineString(vec.iter().map(|e| Point::new(e[0], e[1])).collect()))
        .collect();
    let poly = Polygon::new(ls_ext, ls_int);
    polylabel(&poly, &tolerance).into()
}

#[cfg(test)]
mod tests {
    use super::{Array, WrapperArray, polylabel_ffi, reconstitute2};
    use super::libc::{c_void, size_t};
    use geo::Point;
    use std::mem;

    // Only used for testing
    fn gen_array(v: Vec<[f64; 2]>) -> Array {
        let array = Array {
            data: v.as_ptr() as *const c_void,
            len: v.len() as size_t,
        };
        mem::forget(v);
        array
    }
    // only used for testing
    fn gen_wrapperarray(v: Vec<Vec<[f64; 2]>>) -> WrapperArray {
        let converted: Vec<Array> = v.into_iter().map(|x| gen_array(x)).collect();
        let array2 = WrapperArray {
            data: converted.as_ptr() as *const Array,
            len: converted.len() as size_t,
        };
        mem::forget(converted);
        array2
    }
    #[test]
    fn test_array() {
        let i_a = vec![[0.5, 0.5], [1.0, 1.0], [1.5, 0.5]];
        let i_b = vec![[0.55, 0.55], [0.8, 0.8], [1.2, 0.55]];
        let inners = vec![i_a, i_b];
        let array = gen_wrapperarray(inners);
        let rec_inners = reconstitute2(array);
        assert_eq!(rec_inners[0][2], [1.5, 0.5])
    }
    #[test]
    fn test_ffi() {
        let ext_vec = vec![[4.0, 1.0], [5.0, 2.0], [5.0, 3.0], [4.0, 4.0], [3.0, 4.0], [2.0, 3.0],
                           [2.0, 2.0], [3.0, 1.0], [4.0, 1.0]];
        let int_vec = vec![vec![[3.5, 3.5], [4.4, 2.0], [2.6, 2.0], [3.5, 3.5]],
                           vec![[4.0, 3.0], [4.0, 3.2], [4.5, 3.2], [4.0, 3.0]]];

        let outer = gen_array(ext_vec);
        let inners = gen_wrapperarray(int_vec);
        let res = polylabel_ffi(outer, inners, 0.1);
        let res_point = Point::new(res.x_pos, res.y_pos);
        assert_eq!(res_point, Point::new(3.125, 2.875));
    }

}