Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use GitHub Actions with extensive checks in place of Travis CI #22

Merged
merged 5 commits into from
Oct 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
on: [push, pull_request]
name: Tests
jobs:
check:
name: Check
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2

- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true

- name: Run cargo check
uses: actions-rs/cargo@v1
with:
command: check

test:
name: Test
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2

- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true

- name: Run cargo test
uses: actions-rs/cargo@v1
with:
command: test

lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2

- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy

- name: Run cargo fmt
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check

- name: Run cargo clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: -- -D warnings
10 changes: 0 additions & 10 deletions .travis.yml

This file was deleted.

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
An incredibly fast and robust Rust library for [Delaunay triangulation](https://en.wikipedia.org/wiki/Delaunay_triangulation) of 2D points. A port of [Delaunator](https://github.com/mapbox/delaunator).

[![delaunator on Crates.io](https://img.shields.io/crates/v/delaunator.svg)](https://crates.io/crates/delaunator)
[![Tests](https://github.com/mourner/delaunator-rs/actions/workflows/test.yml/badge.svg)](https://github.com/mourner/delaunator-rs/actions/workflows/test.yml)

## [Documentation](https://docs.rs/delaunator)

Expand Down
2 changes: 1 addition & 1 deletion benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ extern crate rand;

use criterion::{AxisScale, Criterion, ParameterizedBenchmark, PlotConfiguration};
use delaunator::{triangulate, Point};
use rand::{Rng, SeedableRng, rngs::StdRng};
use rand::{rngs::StdRng, Rng, SeedableRng};
use std::iter::repeat_with;

const COUNTS: &[usize] = &[100, 1000, 10_000, 100_000];
Expand Down
67 changes: 49 additions & 18 deletions examples/svg.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use delaunator::{next_halfedge, Point, Triangulation, EMPTY};
use std::{env, fs::File, io::Write};
use delaunator::{EMPTY, Point, Triangulation, next_halfedge};
const CANVAS_SIZE: f64 = 800.;
const POINT_SIZE: usize = 4;
const LINE_WIDTH: usize = 1;
Expand All @@ -16,7 +16,9 @@ fn main() -> std::io::Result<()> {
let args = env::args().collect::<Vec<String>>();
let path = args.get(1).unwrap_or(&default_path);
let points: Vec<Point> = serde_json::from_reader::<_, Vec<(f64, f64)>>(File::open(path)?)?
.iter().map(|p| Point { x: p.0, y: p.1 }).collect();
.iter()
.map(|p| Point { x: p.0, y: p.1 })
.collect();

// triangulate and scale points for display
let triangulation = delaunator::triangulate(&points);
Expand Down Expand Up @@ -45,37 +47,66 @@ fn main() -> std::io::Result<()> {
}
})
);
File::create("triangulation.svg")?
.write_all(contents.as_bytes())
File::create("triangulation.svg")?.write_all(contents.as_bytes())
}


/// Finds the center point and farthest point from it, then generates a new vector of
/// scaled and offset points such that they fit between [0..SIZE]
fn center_and_scale(points: &Vec<Point>, t: &Triangulation) -> Vec<Point> {
let center = &points[*t.triangles.get(0).unwrap_or(&0)];
let farthest_distance = points.iter().map(|p| {
let (x, y) = (center.x - p.x, center.y - p.y);
x*x + y*y
}).reduce(f64::max).unwrap().sqrt();
let farthest_distance = points
.iter()
.map(|p| {
let (x, y) = (center.x - p.x, center.y - p.y);
x * x + y * y
})
.reduce(f64::max)
.unwrap()
.sqrt();
let scale = CANVAS_SIZE / (farthest_distance * 2.0);
let offset = ((CANVAS_SIZE / 2.0) - (scale * center.x), (CANVAS_SIZE / 2.0) - (scale * center.y));
points.iter().map(|p| Point { x: scale * p.x + offset.0, y: scale * p.y + offset.1 }).collect()
let offset = (
(CANVAS_SIZE / 2.0) - (scale * center.x),
(CANVAS_SIZE / 2.0) - (scale * center.y),
);
points
.iter()
.map(|p| Point {
x: scale * p.x + offset.0,
y: scale * p.y + offset.1,
})
.collect()
}


fn render_point(points: &[Point], triangulation: &Triangulation) -> String {
let mut circles = points.iter().enumerate().fold(String::new(), |acc, (i, p)| {
let color = if triangulation.hull.contains(&i) { HULL_POINT_COLOR } else { POINT_COLOR };
acc + &format!(r#"<circle cx="{x}" cy="{y}" r="{size}" fill="{color}"/>"#, x = p.x, y = p.y, size = POINT_SIZE, color = color)
});
let mut circles = points
.iter()
.enumerate()
.fold(String::new(), |acc, (i, p)| {
let color = if triangulation.hull.contains(&i) {
HULL_POINT_COLOR
} else {
POINT_COLOR
};
acc + &format!(
r#"<circle cx="{x}" cy="{y}" r="{size}" fill="{color}"/>"#,
x = p.x,
y = p.y,
size = POINT_SIZE,
color = color
)
});

// show ids for points if input is relatively small
if points.len() < 100 {
circles = points.iter().enumerate().fold(circles, |acc, (i, p)| {
acc + &format!(r#"<text x="{x}" y="{y}" font-size="20" fill="black">{i}</text>"#, i = i, x = p.x + 10., y = p.y - 5.)
acc + &format!(
r#"<text x="{x}" y="{y}" font-size="20" fill="black">{i}</text>"#,
i = i,
x = p.x + 10.,
y = p.y - 5.
)
})
}

circles
}
}
31 changes: 16 additions & 15 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,17 @@ println!("{:?}", result.triangles); // [0, 2, 1, 0, 3, 2]
*/

#![no_std]
#![allow(clippy::many_single_char_names)]

#[cfg(feature = "std")]
extern crate std;

#[macro_use]
extern crate alloc;

use robust::orient2d;
use core::{f64, fmt};
use alloc::vec::Vec;
use core::{f64, fmt};
use robust::orient2d;

/// Near-duplicate points (where both `x` and `y` only differ within this value)
/// will not be included in the triangulation for robustness.
Expand All @@ -48,12 +49,9 @@ impl fmt::Debug for Point {
}
}

impl Into<robust::Coord<f64>> for &Point {
fn into(self) -> robust::Coord<f64> {
robust::Coord::<f64> {
x: self.x,
y: self.y,
}
impl From<&Point> for robust::Coord<f64> {
fn from(p: &Point) -> robust::Coord<f64> {
robust::Coord::<f64> { x: p.x, y: p.y }
}
}

Expand Down Expand Up @@ -160,11 +158,7 @@ pub struct Triangulation {

impl Triangulation {
fn new(n: usize) -> Self {
let max_triangles = if n > 2 {
2 * n - 5
} else {
0
};
let max_triangles = if n > 2 { 2 * n - 5 } else { 0 };

Self {
triangles: Vec::with_capacity(max_triangles * 3),
Expand All @@ -178,6 +172,10 @@ impl Triangulation {
self.triangles.len() / 3
}

pub fn is_empty(&self) -> bool {
self.triangles.is_empty()
}

fn add_triangle(
&mut self,
i0: usize,
Expand Down Expand Up @@ -481,7 +479,8 @@ pub fn triangulate(points: &[Point]) -> Triangulation {
}

let n = points.len();
let (i0, i1, i2) = seed_triangle.expect("At this stage, points are guaranteed to yeild a seed triangle");
let (i0, i1, i2) =
seed_triangle.expect("At this stage, points are guaranteed to yeild a seed triangle");
let center = (&points[i0]).circumcenter(&points[i1], &points[i2]);

let mut triangulation = Triangulation::new(n);
Expand Down Expand Up @@ -617,7 +616,9 @@ fn f64_sqrt(f: f64) -> f64 {
#[cfg(not(feature = "std"))]
#[inline]
fn f64_sqrt(f: f64) -> f64 {
if f < 2.0 { return f; };
if f < 2.0 {
return f;
};

let sc = f64_sqrt(f / 4.0) * 2.0;
let lc = sc + 1.0;
Expand Down
61 changes: 49 additions & 12 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,10 @@ fn bad_input() {
assert!(triangles.is_empty(), "Expected no triangles (2 points)");
assert!(halfedges.is_empty(), "Expected no edges (2 points)");
assert!(hull.len() == 2, "Expected two points on hull (2 point)");
assert!(hull.iter().enumerate().all(|(i, v)| i == *v), "Expected ordered hull points (2 point)");
assert!(
hull.iter().enumerate().all(|(i, v)| i == *v),
"Expected ordered hull points (2 point)"
);

points.push(Point { x: 2., y: 0. });
let Triangulation {
Expand All @@ -88,10 +91,22 @@ fn bad_input() {
hull,
} = triangulate(&points);

assert!(triangles.is_empty(), "Expected no triangles (3 collinear points)");
assert!(halfedges.is_empty(), "Expected no edges (3 collinear points)");
assert!(hull.len() == 3, "Expected three points on hull (3 collinear points)");
assert!(hull.iter().enumerate().all(|(i, v)| i == *v), "Expected ordered hull points (3 collinear points)");
assert!(
triangles.is_empty(),
"Expected no triangles (3 collinear points)"
);
assert!(
halfedges.is_empty(),
"Expected no edges (3 collinear points)"
);
assert!(
hull.len() == 3,
"Expected three points on hull (3 collinear points)"
);
assert!(
hull.iter().enumerate().all(|(i, v)| i == *v),
"Expected ordered hull points (3 collinear points)"
);

points.push(Point { x: 1., y: 1. });
validate(&points);
Expand All @@ -101,7 +116,10 @@ fn bad_input() {
fn unordered_collinear_points_input() {
let points: Vec<Point> = [10, 2, 4, 4, 1, 0, 3, 6, 8, 5, 7, 9]
.iter()
.map(|y| Point { x: 0.0, y: *y as f64 })
.map(|y| Point {
x: 0.0,
y: *y as f64,
})
.collect();
let duplicated = 1;

Expand All @@ -111,10 +129,24 @@ fn unordered_collinear_points_input() {
hull,
} = triangulate(&points);

assert!(triangles.is_empty(), "Expected no triangles (unordered collinear points)");
assert!(halfedges.is_empty(), "Expected no edges (unordered collinear points)");
assert!(hull.len() == points.len() - duplicated, "Expected all non-coincident points on hull (unordered collinear points)");
assert!(hull.iter().enumerate().all(|(i, v)| points[*v].y == (i as f64)), "Expected ordered hull points (unordered collinear points)");
assert!(
triangles.is_empty(),
"Expected no triangles (unordered collinear points)"
);
assert!(
halfedges.is_empty(),
"Expected no edges (unordered collinear points)"
);
assert!(
hull.len() == points.len() - duplicated,
"Expected all non-coincident points on hull (unordered collinear points)"
);
assert!(
hull.iter()
.enumerate()
.all(|(i, v)| points[*v].y == (i as f64)),
"Expected ordered hull points (unordered collinear points)"
);
}

fn scale_points(points: &[Point], scale: f64) -> Vec<Point> {
Expand All @@ -123,7 +155,8 @@ fn scale_points(points: &[Point], scale: f64) -> Vec<Point> {
.map(|p| Point {
x: p.x * scale,
y: p.y * scale,
}).collect();
})
.collect();
scaled
}

Expand Down Expand Up @@ -164,7 +197,11 @@ fn validate(points: &[Point]) {
let p0 = &points[hull[j]];
let p = &points[hull[i]];

if !convex(p0, &points[hull[(j + 1) % hull.len()]], &points[hull[(j + 3) % hull.len()]]) {
if !convex(
p0,
&points[hull[(j + 1) % hull.len()]],
&points[hull[(j + 3) % hull.len()]],
) {
panic!("Hull is not convex at {}", j);
}

Expand Down