use ndarray::Array1;
use ndarray::Array2;
use ndarray::Axis;
use ndarray_stats::QuantileExt;
fn searchsorted_left(y_column: &[f64], y_values: &[f64]) -> Vec<usize> {
y_values
.iter()
.map(|&y| {
y_column
.binary_search_by(|&val| val.partial_cmp(&y).unwrap())
.unwrap_or_else(|x| x)
})
.collect()
}
fn searchsorted_right(y_column: &[f64], y_values: &[f64]) -> Vec<usize> {
y_values
.iter()
.map(|&y| {
match y_column.binary_search_by(|&val| val.partial_cmp(&y).unwrap()) {
Ok(pos) => {
let mut pos = pos;
while pos < y_column.len() && y_column[pos] == y {
pos += 1;
}
pos
}
Err(pos) => pos,
}
})
.collect()
}
#[allow(non_snake_case)]
fn _inpoly(
vert: &Array2<f64>,
node: &Array2<f64>,
edge: &Array2<usize>,
ftol: &f64,
lbar: &f64,
) -> (Array1<bool>, Array1<bool>) {
let feps = ftol * (lbar.powf(1.0));
let veps = ftol * (lbar.powf(1.0));
let vnum = vert.nrows();
let mut stat = Array1::from_elem(vnum, false);
let mut bnds = Array1::from_elem(vnum, false);
let XONE = node
.select(Axis(0), &edge.column(0).to_owned().into_raw_vec())
.column(0)
.to_owned();
let XTWO = node
.select(Axis(0), &edge.column(1).to_owned().into_raw_vec())
.column(0)
.to_owned();
let YONE = node
.select(Axis(0), &edge.column(0).to_owned().into_raw_vec())
.column(1)
.to_owned();
let YTWO = node
.select(Axis(0), &edge.column(1).to_owned().into_raw_vec())
.column(1)
.to_owned();
let XMIN: Vec<f64> = XONE
.iter()
.zip(XTWO.iter())
.map(|(a, b)| a.min(*b))
.collect();
let XMAX: Vec<f64> = XONE
.iter()
.zip(XTWO.iter())
.map(|(a, b)| a.max(*b))
.collect();
let XMIN: Vec<f64> = XMIN.iter().map(|x| x - veps).collect();
let XMAX: Vec<f64> = XMAX.iter().map(|x| x + veps).collect();
let YMIN = &YONE.mapv(|y| y - veps);
let YMAX = &YTWO.mapv(|y| y + veps);
let YDEL = &YTWO - &YONE;
let XDEL = &XTWO - &XONE;
let EDEL = &XDEL.mapv(|x| x.abs()) + &YDEL;
let y_column = vert.column(1).to_vec();
let mut ivec: Vec<usize> = (0..vert.nrows()).collect();
ivec.sort_by(|&a, &b| y_column[a].partial_cmp(&y_column[b]).unwrap());
let ione = searchsorted_left(&y_column, &YMIN.to_vec());
let itwo = searchsorted_right(&y_column, &YMAX.to_vec());
for epos in 0..edge.nrows() {
let xone = XONE[epos];
let xtwo = XTWO[epos];
let yone = YONE[epos];
let ytwo = YTWO[epos];
let xmin = XMIN[epos];
let xmax = XMAX[epos];
let edel = EDEL[epos];
let xdel = XDEL[epos];
let ydel = YDEL[epos];
for jpos in ione[epos]..itwo[epos] {
let jvrt = ivec[jpos];
if bnds[jvrt] == true {
continue;
}
let xpos = vert[[jvrt, 0]];
let ypos = vert[[jvrt, 1]];
if xpos >= xmin {
if xpos <= xmax {
let mul1 = ydel * (xpos - xone);
let mul2 = xdel * (ypos - yone);
if (feps * edel) >= (mul2 - mul1).abs() {
bnds[jvrt] = true;
stat[jvrt] = true;
} else if (ypos == yone) && (xpos == xone) {
bnds[jvrt] = true;
stat[jvrt] = true;
} else if (ypos == ytwo) && (xpos == xtwo) {
bnds[jvrt] = true;
stat[jvrt] = true;
} else if (mul1 <= mul2) && (ypos >= yone) && (ypos < ytwo) {
stat[jvrt] = !stat[jvrt]
}
};
} else if (ypos >= yone) && (ypos < ytwo) {
stat[jvrt] = !stat[jvrt]
}
}
}
(stat, bnds)
}
#[allow(non_snake_case)]
pub fn inpoly2(
vert: &Array2<f64>,
node: &Array2<f64>,
edge: Option<&Array2<usize>>,
ftol: Option<&f64>,
) -> (Array1<bool>, Array1<bool>) {
let vnum = vert.nrows();
let mut STAT = Array1::from_elem(vnum, false);
let mut BNDS = Array1::from_elem(vnum, false);
if node.is_empty() {
return (STAT, BNDS);
}
let edge = match edge {
None => {
let n = node.nrows();
let mut e = Array2::zeros((n, 2));
for i in 0..n - 1 {
e[[i, 0]] = i as usize;
e[[i, 1]] = (i + 1) as usize;
}
e[[n - 1, 0]] = (n - 1) as usize;
Some(e)
}
Some(edge) => Some(edge.clone()),
};
let edge = edge.unwrap();
let used = edge.iter().map(|&e| e as usize).collect::<Vec<_>>();
let sel0 = node.select(Axis(0), &used);
let col0 = sel0.column(0);
let xmin = col0.min().unwrap();
let xmax = col0.max().unwrap();
let col1 = sel0.column(1);
let ymin = col1.min().unwrap();
let ymax = col1.max().unwrap();
let xdel = xmax - xmin;
let ydel = ymax - ymin;
let lbar = (xdel + ydel) / 2.0;
let ftol = ftol.unwrap_or(&5.0e-14);
let feps = ftol * lbar;
let mask = vert
.outer_iter()
.map(|row| {
row[0] >= xmin - feps
&& row[1] >= ymin - feps
&& row[0] <= xmax + feps
&& row[1] <= ymax + feps
})
.collect::<Vec<bool>>();
let selected_indices: Vec<usize> = mask
.iter()
.enumerate()
.filter_map(|(i, &m)| if m { Some(i) } else { None })
.collect();
let mut vert: Array2<f64> = vert.select(Axis(0), &selected_indices);
if vert.is_empty() {
return (STAT, BNDS);
}
let mut node = node.clone();
if xdel > ydel {
vert.invert_axis(Axis(1));
node.invert_axis(Axis(1));
};
let swap = edge
.column(1)
.iter()
.zip(edge.column(0))
.map(|(&y1, &y0)| node[(y1, 1)] < node[(y0, 1)])
.collect::<Vec<bool>>();
let mut edge = edge.clone();
for (i, &swap) in swap.iter().enumerate() {
if swap {
edge.swap([i, 0], [i, 1]);
}
}
let (stat, bnds) = _inpoly(&vert, &node, &edge, ftol, &lbar);
for (i, &si) in selected_indices.iter().enumerate() {
STAT[si] = stat[i];
BNDS[si] = bnds[i];
}
(STAT, BNDS)
}
#[cfg(test)]
mod tests {
use crate::inpoly2;
use gnuplot::Caption;
use gnuplot::Color;
use gnuplot::Figure;
use gnuplot::PointSymbol;
use meshgridrs;
use ndarray::array;
use ndarray::stack;
use ndarray::Array;
use ndarray::Axis;
#[test]
fn example_1() {
let node = array![
[4.0, 0.0],
[8.0, 4.0],
[4.0, 8.0],
[0.0, 4.0],
[3.0, 3.0],
[5.0, 3.0],
[5.0, 5.0],
[3.0, 5.0]
];
let edge = array![
[0, 1],
[1, 2],
[2, 3],
[3, 0],
[4, 5],
[5, 6],
[6, 7],
[7, 4]
];
let x = Array::linspace(-1., 9., 51);
let y = Array::linspace(-1., 9., 51);
let xi = vec![x, y];
let grids = meshgridrs::meshgrid(&xi, meshgridrs::Indexing::Xy).unwrap();
let xpos = &grids[0];
let ypos = &grids[1];
let xpos_flat = xpos.to_shape((xpos.len(), 1)).unwrap();
let ypos_flat = ypos.to_shape((ypos.len(), 1)).unwrap();
let points = stack![Axis(1), xpos_flat, ypos_flat].remove_axis(Axis(2));
let (inpoly, onedge) = inpoly2(&points, &node, Some(&edge), None);
let points_in: Vec<(f64, f64)> = points
.outer_iter()
.zip(&inpoly)
.filter(|&(_, &in_set)| in_set)
.map(|(row, _)| (row[0], row[1]))
.collect();
let points_out: Vec<(f64, f64)> = points
.outer_iter()
.zip(&inpoly)
.filter(|&(_, &in_set)| !in_set)
.map(|(row, _)| (row[0], row[1]))
.collect();
let points_on: Vec<(f64, f64)> = points
.outer_iter()
.zip(&onedge)
.filter(|&(_, &on_set)| on_set)
.map(|(row, _)| (row[0], row[1]))
.collect();
let x_in: Vec<f64> = points_in.iter().map(|&(x, _)| x).collect();
let y_in: Vec<f64> = points_in.iter().map(|&(_, y)| y).collect();
let x_out: Vec<f64> = points_out.iter().map(|&(x, _)| x).collect();
let y_out: Vec<f64> = points_out.iter().map(|&(_, y)| y).collect();
let x_on: Vec<f64> = points_on.iter().map(|&(x, _)| x).collect();
let y_on: Vec<f64> = points_on.iter().map(|&(_, y)| y).collect();
let mut fg = Figure::new();
{
let axes = fg.axes2d();
axes
.points(
&x_in,
&y_in,
&[Caption("IN==1"), Color("blue"), PointSymbol('O')],
)
.points(
&x_out,
&y_out,
&[Caption("IN==0"), Color("red"), PointSymbol('O')],
)
.points(
&x_on,
&y_on,
&[Caption("ON==1"), Color("magenta"), PointSymbol('S')],
);
}
fg.show().unwrap();
}
}