#solution #compute #analytical #piecewise #polyhedra #shapefile #skymask

bin+lib skymask-rs

Compute piecewise analytical solutions of skymask for given polyhedra

2 releases

new 0.1.1 Feb 2, 2025
0.1.0 Jan 31, 2025

#1 in #shapefile

Download history 59/week @ 2025-01-26

59 downloads per month

MIT license

17KB
266 lines

Skymask-rs

Compute piecewise analytical solutions of skymask for given polyhedra.

Python binding available at skymask-py.

Time Complexity

This crate uses an efficient algorithm to compute the piecewise analytical solution of skymask. Its time complexity is

$$ O(k \cdot \log r \cdot n \log n) $$

Where $n$ represents the number of line segments, and $k$ denotes the average number of segments each line overlaps with in the analytical result.

The obtained analytical solution is a RangeMap, therefore the time complexity for sampling skymask is

$$ O(m \cdot \log r) $$

Where $r$ denotes the number of segments in the analytical result, and $m$ refers to the number of discrete sample points taken from the skymask.

Benchmark

Runs on 11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz (8 Physical Cores / 16 Logical Threads) and NVIDIA GeForce RTX 3070 Laptop GPU. The benchmark code is available at benchmark.py.

Method Fps Time Complexity
Parallel sampling in skymask_py 1648.87 $O((k \cdot n \log n + m) \cdot \log r)$
Sequential sampling in skymask_py 176.05 $O((k \cdot n \log n + m) \cdot \log r)$
Naive approach with Cupy 63.40 $O(m \cdot n)$
Naive approach with Numpy 5.44 $O(m \cdot n)$

Install

[dependencies]
skymask-rs = "0.1.1"

Examples

Basic Demo

use kdtree::distance::squared_euclidean;
use ordered_float::OrderedFloat as F;
use skymask_rs::read_shp;
use skymask_rs::ProjSegment;

fn main() {
    println!("{:#?}", skymask_rs::skymask(
        [
            [ 1.0,  1.0,  1.0, -1.0,  1.0, 1.0],
            [-1.0,  1.0,  1.0, -1.0, -1.0, 1.0],
            [-1.0, -1.0,  1.0,  1.0, -1.0, 1.0],
            [ 1.0, -1.0,  1.0,  1.0,  1.0, 1.0],
        ]
        .into_iter()
        .filter_map(|line| {
            ProjSegment::<F<f64>, (F<f64>, F<f64>)>::from_points(
                &[F(line[0]), F(line[1]), F(line[2])],
                &[F(line[3]), F(line[4]), F(line[5])],
            )
        }),
        F(1e-6),
    ));
}

From Shapefile

use kdtree::distance::squared_euclidean;
use ordered_float::OrderedFloat as F;
use skymask_rs::read_shp;
use skymask_rs::ProjSegment;

fn main() {
    let path = "<path-to-shp-file>";
    let max_dist: f64 = 1000.0;
    let eps: f64 = 1e-6;

    let (arr1, xy, kdtree) = read_shp(path);
    let pos = [
        xy[0].iter().sum::<f64>() / xy[0].len() as f64,
        xy[1].iter().sum::<f64>() / xy[1].len() as f64,
    ];

    let lines_iter = kdtree
        .within(&pos, max_dist.powi(2), &squared_euclidean)
        .unwrap()
        .into_iter()
        .filter_map(|(_, &i)| {
            let row = arr1.row(i);
            ProjSegment::<F<f64>, (F<f64>, F<f64>)>::from_points(
                &[F(row[0] - pos[0]), F(row[1] - pos[1]), F(row[2])],
                &[F(row[3] - pos[0]), F(row[4] - pos[1]), F(row[5])],
            )
        });
    println!("{:?}", skymask_rs::skymask(lines_iter, F(eps)));
}

Dependencies

~5MB
~101K SLoC