Skip to content

Commit

Permalink
Use GitHub Actions with extensive checks in place of Travis CI (#22)
Browse files Browse the repository at this point in the history
* setup GitHub Actions to replace Travis CI

* run cargo fmt

* clippy fixes

* add GitHub Actions badge

* fix cargo fmt
  • Loading branch information
mourner committed Oct 12, 2021
1 parent 830c41f commit b0ae068
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 56 deletions.
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

0 comments on commit b0ae068

Please sign in to comment.