Skip to content

Commit c68c883

Browse files
committed
polish the readme, update logo
1 parent b7d570d commit c68c883

5 files changed

Lines changed: 50 additions & 77 deletions

File tree

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Build and test project
1+
name: CI
22

33
on:
44
push:

CHANGELOG.md

Lines changed: 0 additions & 27 deletions
This file was deleted.

README.md

Lines changed: 40 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
## Earcut
22

33
A fast, [header-only](https://github.com/mapbox/earcut.hpp/blob/master/include/mapbox/earcut.hpp) C++ port of [earcut.js](https://github.com/mapbox/earcut), the fastest and smallest JavaScript polygon triangulation library.
4-
5-
[![Build](https://github.com/mapbox/earcut.hpp/actions/workflows/build.yml/badge.svg)](https://github.com/mapbox/earcut.hpp/actions/workflows/build.yml)
4+
[![CI status](https://github.com/mapbox/earcut.hpp/actions/workflows/build.yml/badge.svg)](https://github.com/mapbox/earcut.hpp/actions/workflows/build.yml)
65
[![Volodymyr Agafonkin's projects](https://img.shields.io/badge/simply-awesome-brightgreen.svg)](https://github.com/mourner/projects)
76

7+
![Earcut triangulation example](earcut.png)
8+
89
Earcut favors raw speed and simplicity over triangulation quality, while being robust enough to handle most practical datasets without crashing or producing garbage, with an option to [refine](#refinement-optional-delaunay-post-pass) the result to [Delaunay](https://en.wikipedia.org/wiki/Delaunay_triangulation) quality at a small cost. Originally built for [Mapbox GL](https://www.mapbox.com/), it's a good fit for real-time triangulation of geographical shapes and other practical data.
910

1011
It implements a modified ear slicing algorithm, optimized by [z-order curve](http://en.wikipedia.org/wiki/Z-order_curve) and spatial hashing and extended to handle holes, twisted polygons, degeneracies and self-intersections in a way that doesn't _guarantee_ correctness of triangulation, but attempts to always produce acceptable results for practical data. It's based on ideas from [FIST: Fast Industrial-Strength Triangulation of Polygons](http://www.cosy.sbg.ac.at/~held/projects/triang/triang.html) by Martin Held and [Triangulation by Ear Clipping](http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf) by David Eberly.
@@ -15,62 +16,42 @@ It implements a modified ear slicing algorithm, optimized by [z-order curve](htt
1516
#include <earcut.hpp>
1617
```
1718
```cpp
18-
// The number type to use for tessellation
19-
using Coord = double;
19+
// A point is any type with x/y accessors; std::array works out of the box.
20+
using Point = std::array<double, 2>;
21+
22+
// A polygon is a list of rings. The first ring is the outer boundary; the rest are holes.
23+
// Winding order doesn't matter, and rings can be given in any order.
24+
std::vector<std::vector<Point>> polygon = {
25+
{{100, 0}, {100, 100}, {0, 100}, {0, 0}}, // outer ring
26+
{{75, 25}, {75, 75}, {25, 75}, {25, 25}}, // hole
27+
};
2028

21-
// The index type. Defaults to uint32_t, but you can also pass uint16_t if you know that your
22-
// data won't have more than 65536 vertices.
29+
// The index type. Defaults to uint32_t; pass uint16_t if your data never exceeds 65536 vertices.
2330
using N = uint32_t;
2431

25-
// Create array
26-
using Point = std::array<Coord, 2>;
27-
std::vector<std::vector<Point>> polygon;
28-
29-
// Fill polygon structure with actual data. Any winding order works.
30-
// The first polyline defines the main polygon.
31-
polygon.push_back({{100, 0}, {100, 100}, {0, 100}, {0, 0}});
32-
// Following polylines define holes.
33-
polygon.push_back({{75, 25}, {75, 75}, {25, 75}, {25, 25}});
34-
35-
// Run tessellation
36-
// Returns array of indices that refer to the vertices of the input polygon.
37-
// e.g: the index 6 would refer to {25, 75} in this example.
38-
// Three subsequent indices form a triangle. Output triangles have a consistent winding order
39-
// regardless of the input winding: counter-clockwise in a y-up coordinate system (clockwise in
32+
// Triangulate. The result is a flat list of indices into the input vertices (numbered ring after
33+
// ring, so index 6 is {25, 75} here), three per triangle. Output triangles have a consistent
34+
// winding regardless of the input: counter-clockwise in a y-up coordinate system (clockwise in
4035
// y-down/screen space). Call std::reverse on the result if you need the opposite orientation.
4136
std::vector<N> indices = mapbox::earcut<N>(polygon);
4237
```
4338

4439
Earcut can triangulate a simple, planar polygon of any winding order including holes. It will even return a robust, acceptable solution for non-simple polygons. Earcut works on a 2D plane: only `x` and `y` are used, so if you have three or more dimensions, project them onto a 2D surface before triangulation, or use a more suitable library for the task (e.g. [CGAL](https://doc.cgal.org/latest/Triangulation_3/index.html)).
4540

46-
It is also possible to use your custom point type as input. There are default accessors defined for `std::tuple`, `std::pair`, and `std::array`. For a custom type (like Clipper's `IntPoint` type), do this:
41+
Any point type works as input — earcut reads coordinates through the `nth` accessor. Accessors for `std::tuple`, `std::pair`, and `std::array` ship by default; for a custom type (like Clipper's `IntPoint`), specialize `nth` for it:
4742

4843
```cpp
49-
// struct IntPoint {
50-
// int64_t X, Y;
51-
// };
52-
53-
namespace mapbox {
54-
namespace util {
55-
56-
template <>
57-
struct nth<0, IntPoint> {
58-
inline static auto get(const IntPoint &t) {
59-
return t.X;
60-
};
61-
};
62-
template <>
63-
struct nth<1, IntPoint> {
64-
inline static auto get(const IntPoint &t) {
65-
return t.Y;
66-
};
67-
};
44+
struct IntPoint { int64_t X, Y; };
45+
46+
namespace mapbox { namespace util {
47+
48+
template <> struct nth<0, IntPoint> { static auto get(const IntPoint& p) { return p.X; } };
49+
template <> struct nth<1, IntPoint> { static auto get(const IntPoint& p) { return p.Y; } };
6850

69-
} // namespace util
70-
} // namespace mapbox
51+
}} // namespace mapbox::util
7152
```
7253
73-
You can also use a custom container type for your polygon. Similar to std::vector<T>, it has to meet the requirements of [Container](https://en.cppreference.com/w/cpp/named_req/Container), in particular `size()`, `empty()` and `operator[]`.
54+
The polygon and ring containers are just as flexible: any type that meets the [Container](https://en.cppreference.com/w/cpp/named_req/Container) requirements (`size()`, `empty()`, `operator[]`) works in place of `std::vector`.
7455
7556
### Refinement (optional Delaunay post-pass)
7657
@@ -87,10 +68,6 @@ mapbox::refine(indices, coords);
8768

8869
It assumes a valid manifold triangulation, such as the output of `earcut` (though any manifold triangle-index array works), and reads `coords` through the same `nth<0>`/`nth<1>` accessors. It doesn't repair invalid polygon input or make the mesh conforming. Note also that `refine` uses **non-robust** predicates: float input is fine, and the worst case is a not-quite-Delaunay edge, never an invalid mesh — but unlike `earcut` it does not promise bit-identical output across compilers.
8970

90-
<p align="center">
91-
<img src="https://camo.githubusercontent.com/01836f8ba21af844c93d8d3145f4e9976025a696/68747470733a2f2f692e696d6775722e636f6d2f67314e704c54712e706e67" alt="example triangulation"/>
92-
</p>
93-
9471
## Performance
9572

9673
Earcut is heavily optimized for its primary workload — triangulating polygons from
@@ -113,7 +90,7 @@ triangulation even on bad data, use a library like [CGAL](https://www.cgal.org/)
11390

11491
The output is also not _conforming_ — a vertex may land in the middle of another triangle's edge (a
11592
T-junction). This is harmless for rendering but can break navmesh or FEM use; if you need a
116-
conforming mesh, remove T-junctions in a post-process.
93+
conforming mesh, [remove T-junctions in a post-process](https://github.com/mapbox/earcut/issues/74#issuecomment-4826113682).
11794

11895
## Additional build instructions
11996
In case you just want to use the earcut triangulation library; copy and include the header file [`<earcut.hpp>`](https://github.com/mapbox/earcut.hpp/blob/master/include/mapbox/earcut.hpp) in your project and follow the steps documented in the section [Usage](#usage).
@@ -151,6 +128,20 @@ Build options (all default to their common value): `-DEARCUT_BUILD_TESTS=ON`,
151128
CMake can also generate IDE projects (Visual Studio, Xcode, etc.) — e.g.
152129
`cmake -B build -G "Visual Studio 17 2022"` — or import the folder directly in CLion / VS.
153130

131+
### Visualizer
132+
133+
There's an interactive OpenGL viewer for inspecting the triangulation of the bundled test fixtures.
134+
It's off by default (it needs an OpenGL SDK and GLFW); enable and run it with:
135+
136+
```bash
137+
cmake -B build -DCMAKE_BUILD_TYPE=Release -DEARCUT_BUILD_VIZ=ON
138+
cmake --build build --target viz -j
139+
./build/viz
140+
```
141+
142+
Controls: **←/→** switch fixture, **↑/↓** switch tessellator (earcut / earcut + refine / scanline fill),
143+
**F/M/O** toggle fill/mesh/outline, **WASD** pan, **+/−** or scroll to zoom, **R** reset, **Q**/**Esc** quit.
144+
154145
## Status
155146

156147
This is currently based on [earcut 3.2.3](https://github.com/mapbox/earcut/releases/tag/v3.2.3).

earcut.png

55 KB
Loading

test/viz.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,15 @@ mapbox::fixtures::FixtureTester* getFixture(std::size_t i) {
347347
}
348348

349349
int main() {
350+
// Open on the "earcut" fixture to match the JS visualizer's default, falling back to the first.
351+
const auto& fixtures = mapbox::fixtures::FixtureTester::collection();
352+
for (std::size_t i = 0; i < fixtures.size(); ++i) {
353+
if (fixtures[i]->name == "earcut") {
354+
shapeIndex = i;
355+
break;
356+
}
357+
}
358+
350359
if (!glfwInit()) {
351360
return 1;
352361
}

0 commit comments

Comments
 (0)