Skip to content

Releases: andywiecko/BurstTriangulator

v3.9.1

18 Sep 16:37

Choose a tag to compare

📝 Changelog

Fixed

  • Fixes a false positive result in the edge–edge intersection test for non-intersecting, nearly collinear edges (#384).
    This fix primarily addresses float2 precision issues (when using double2 the issue was not detected).
    These checks will be improved in the future with robust-predicates implementation.

Full Changelog: v3.9.0...v3.9.1

v3.9.0

01 Jul 18:05

Choose a tag to compare

✨ What's new?

The UnsafeTriangulator<T>.PlantHoleSeeds extension now provides also an option to generate a mapping from the initial triangles (t1) to the triangles after planting seeds (t2). The condition t1[3*i + k] == t2[mapping[3*i + k]] (for $k\in{0, 1, 2}$) should hold for triangles that still exist in t2. If mapping[i] == -1, then the corresponding triangle i no longer exists in t2.
See example below:

var t = new UnsafeTriangulator<double2>();

using var triangles1 = new NativeList<int>(Allocator.Persistent);
t.Triangulate(input, output: new(){ Triangles = triangles1, ... }, args.Default(), Allocator.Persistent);

using var mapping = new NativeList<int>(Allocator.Persistent);
using var triangles2 = new NativeList<int>(Allocator.Persistent);
triangles2.CopyFrom(triangles1);
t.PlantHoleSeeds(new(){ Triangles = triangles2, ... }, Allocator.Persistent, mapping: mapping);

// The result:
// - `triangles1`: the initial triangles buffer before planting seeds.
// - `triangles2`: the triangles after planting seeds.
// - `mapping`: `triangles1` → `triangles2`.

For more changes, see the changelog below.

📝 Changelog

Added

  • Added mapping for the PlantingHoleSeeds extension in UnsafeTriangulator<T2>. This optional buffer provides a mapping between the initial triangles and triangles after planting hole seeds.
  • Added a utility for calculating the axis-aligned bounding box of a given collection of points (Utilities.BoundingBox).
  • Added a utility for calculating the center of mass (COM) of a given collection of points, assuming equal weights for all positions (Utilities.CenterOfMass).

Changed

  • Extended UnsafeTriangulator<T2>.ConstrainEdge to replace the args parameter with explicit options: sloanMaxIters and verbose. The previous overload using args is now marked with the [Obsolete] attribute.
  • Extended UnsafeTriangulator<T2>.PlantHoleSeeds to replace the args and input parameters with explicit options: autoHolesAndBoundary, restoreBoundary, and holeSeeds. The previous overload using args and input is now marked with the [Obsolete] attribute.

Fixed

  • "Support" for triangulation with empty position buffer with enabled pre-processors.

Full Changelog: v3.8.0...v3.9.0

v3.8.0

30 Apr 17:27

Choose a tag to compare

✨ What's new?

The package has officially reached 300 stars ⭐ — thank you all for the incredible support!

image

This release introduces a powerful new feature: the α-shape filter.
It's especially useful for mesh reconstruction or procedurally generated maps.

👉 Read the full blog post for more insights.

Example usage:

using var positions = new NativeArray<double2>(..., Allocator.Persistent);
using var triangulator = new Triangulator(Allocator.Persistent)
{
  Input = { Positions = positions },
  Settings = { UseAlphaShapeFilter = true, AlphaShapeSettings = { Alpha = 0.1f, ... }},
};

triangulator.Run();

📘 Check the documentation for detailed usage and examples.

📝 Changelog

Added

  • $\alpha$-shape filter for Triangulator<T> and corresponding extensions for UnsafeTriangulator<T>. Enable this via the UseAlphaShapeFilter in TriangulatorSettings. Additional configuration is available through AlphaShapeSettings. Options include:
    • ProtectPoints prevents triangle removal if it would leave any of its points unassigned to a triangle;
    • ProtectConstraints ensures triangles with constrained edges are preserved;
    • PreventWindmills avoids formation of windmill structures.
  • GeneratePointTriangleCount utility in Utilities. This method fills an input buffer with the count of triangles each vertex index participates in, based on a given triangle index buffer.

Full Changelog: v3.7.0...v3.8.0

v3.7.0

23 Mar 13:39

Choose a tag to compare

✨ What's new?

This release introduces several triangle mesh-related utilities. For more details, refer to the utilities documentation:

The most notable addition is the Retriangulate extension, along with the corresponding RetriangulateMeshJob. This utility allows you to retriangulate an existing UnityEngine.Mesh, which is particularly useful for mesh refinement.

Example usage:

mesh.Retriangulate(
    settings: new()
    {
        AutoHolesAndBoundary = true,
        RefineMesh = true,
        RefinementThresholds = { Angle = 0, Area = 1e-2f }
    },
    axisInput: Axis.XZ,
    uvMap: UVMap.Planar,
);

📝 Changelog

Added

  • Several triangle mesh related utilities, including:
    • GenerateHalfedges;
    • GenerateTriangleColors;
    • InsertSubMesh;
    • NextHalfedge;
    • Retriangulate, an extension for UnityEngine.Mesh;
    • RetriangulateMeshJob.
  • Triangulator and Triangulator<T> now implement INativeDisposable.

Fixed

  • Safety handles for NativeLists in UnsafeTriangulator.

Full Changelog: v3.6.0...v3.7.0

v3.6.0

01 Feb 10:25

Choose a tag to compare

✨ What's new?

Welcome to 2025! After the first month of the year, a new release of BurstTriangulator has arrived! This update finally supplements dynamic triangulation with the constrain edges method. Refinement performance has improved, and several small fixes have been made.

📈 Enhanced refinement performance

By replacing NativeQueue with NativeQueueList, I've managed to increase refinement performance by up to 50% (depending on the input). See the benchmarks attached below for details. Read the manual for test case specification.

image

image

🆕 ConstrainEdge

The ConstrainEdge extension allows you to constrain the edge (pi, pj). This is particularly useful for dynamic triangulation, enabling users to insert a path dynamically while constraining its edges.

Warning

This method is restricted to bulk mesh, meaning the constrained edge must not intersect any holes.

Additionally, the optional parameter ignoreForPlantingSeeds ignores the halfedges corresponding to (pi, pj) from during the seed planting step.

Here's an example demonstrating how to use the ConstrainEdge extension:

using var inputPositions = new NativeArray<double2>(..., Allocator.Persistent);
var output = new NativeOutputData<double2>
{
    Positions = inputPositions,
};

using var status = new NativeReference<Status>(Status.OK, Allocator.Persistent);
using var outputPositions = new NativeList<double2>(Allocator.Persistent);
using var triangles = new NativeList<int>(Allocator.Persistent);
using var halfedges = new NativeList<int>(Allocator.Persistent);
using var constrainedHalfedges = new NativeList<bool>(Allocator.Persistent);
using var ignoredHalfedgesForPlantingSeeds = new NativeList<bool>(Allocator.Persistent);
var output = new NativeOutputData<double2>
{
    Status = status,
    Positions = outputPositions,
    Triangles = triangles,
    Halfedges = halfedges,
    ConstrainedHalfedges = constrainedHalfedges,
    IgnoredHalfedgesForPlantingSeeds = ignoredHalfedgesForPlantingSeeds,
};

var args = Args.Default();

var t = new UnsafeTriangulator<double2>();
t.Triangulate(input, output, args, Allocator.Persistent);
t.ConstrainEdge(output, pi: 0, pj: 1, args, allocator: Allocator.Persistent, ignoreForPlantingSeeds: true);

📝 Changelog

Added

  • ConstrainEdge extension for UnsafeTriangulator<T>. Edges can now be constrained dynamically.
  • NativeInputData<T> and NativeOutputData<T>, which are essentially LowLevel.Unsafe.InputData<T> and LowLevel.Unsafe.OutputData<T>. This change resolves ambiguity between managed and unmanaged input/output data.
  • (Internal) NativeQueueList<T>, an alternative implementation to Unity.Collections.NativeQueue<T>. This wrapper is more stable and faster, though it consumes more memory.

Changed

  • Improved the performance of the refinement step. Depending on the input, speedups of up to 50% can be observed!
  • Various documentation (summary/manual/scripting API) tweaks.

Deprecated

  • LowLevel.Unsafe.InputData<T> and LowLevel.Unsafe.OutputData<T> have been deprecated. Use NativeInputData<T> and NativeOutputData<T> instead.

Fixed

  • A rare issue causing an editor crash when reloading the domain, apparently caused by NativeQueue. This has been resolved by replacing NativeQueue with the internal NativeQueueList<T> implementation.

Full Changelog: v3.5.0...v3.6.0

v3.5.0

06 Dec 16:41

Choose a tag to compare

❄️🎅🎄 Winter Update ❄️🎅🎄

Ho ho ho! This is the final release of BurstTriangulator for 2024, but don't worry—more updates are coming in 2025! Thank you all for your support; it’s wonderful to see the community actively using this package.

Looking ahead to next year, I'm excited to announce the planned release of the first BurstTriangulator addon package, which will introduce utilities for pathfinding in triangular meshes. Additionally, I plan to resume development of my PBD2D engine. Stay tuned for more updates, and I wish you all success with your fantastic projects!

Best,
Andrzej

✨ What's new?

🆕 Dynamic Triangulation Completed

This update introduces two essential methods for the UnsafeTriangulator, enabling full support for dynamic triangulation:

  • DynamicSplitHalfedge allows point insertion by splitting a selected halfedge ᵈᵒᶜˢ,
  • DynamicRemoveBulkPoint facilitates the removal of points from the mesh bulk ᵈᵒᶜˢ.

Learn more in the documentation.

📈 Enhanced refinement performance

The Bowyer-Watson point insertion algorithm has been refactored, resulting in an approximate 25% performance improvement during the refinement step and dynamic triangulation routines.

Performance Benchmarks:

image

In addition, a new benchmark has been introduced using Lake Superior as the input dataset—a commonly used test case for this package. This more general test suite provides better benchmarking for arbitrary input data compared to the original unit box refinement test case.

image

📝 Changelog

Added

  • DynamicSplitHalfedge extension for UnsafeTriangulator, enabling point insertion by splitting a selected halfedge.
  • DynamicRemoveBulkPoint extension for UnsafeTriangulator, facilitating the removal of points from the mesh bulk.

Changed

  • Refactor Bower-Watson point insertion algorithm to achieve approximately a 25% performance improvement during the refinement step and dynamic triangulation routines.
  • Optimized planting seeds job for slightly increased performance and reduced memory allocation.
  • (Internal) Various refactors to improve code clarity and maintainability.

Full Changelog: v3.4.0...v3.5.0

v3.4.0

25 Oct 16:43

Choose a tag to compare

🎃 Halloween Update 🎃

✨ What's new?

🆕 Dynamic triangulation

This release introduces basic support for dynamic triangulation with the new DynamicInsertPoint extension. It allows for dynamically inserting a point into a triangulation within a specified triangle using barycentric coordinates. Future updates will expand functionality to include additional extensions for splitting a halfedge and removing specific points, as well as, updates will increase performance related to point insertion/removal.

Here's an example snippet showcasing triangulation followed by a dynamic point insertion:

var t = new UnsafeTriangulator<float2>();

using var positions = new NativeArray<float2>(..., Allocator.Persistent);
using var constraints = new NativeArray<int>(..., Allocator.Persistent);
var input = new InputData<float2> { Positions = positions, ConstraintEdges = constraints };

using var outputPositions = new NativeList<float2>(Allocator.Persistent);
using var triangles = new NativeList<int>(Allocator.Persistent);
using var halfedges = new NativeList<int>(Allocator.Persistent);
using var constrainedHalfedges = new NativeList<bool>(Allocator.Persistent);
var output = new OutputData<float2> { Positions = outputPositions, Triangles = triangles, Halfedges = halfedges, ConstrainedHalfedges = constrainedHalfedges };

t.Triangulate(input, output, args: Args.Default(autoHolesAndBoundary: true), Allocator.Persistent);

// Insert a new point in triangle with index 42 at the center (barycentric coordinates: [⅓, ⅓, ⅓]).
t.DynamicInsertPoint(output, tId: 42, bar: 1f / 3, allocator: Allocator.Persistent);

Learn more in the manual.

🆕 Online demo

To better demonstrate the package's capabilities, we've prepared a small online demo, available here.

image

📈 Enhanced refinement performance

To support dynamic triangulation, we refactored portions of the refinement process, resulting in a nearly 2x performance improvement after recent fixes and optimizations.

image

📈 Improved editor compatibility

TriangulationSettings is now better suited with UnityEditor, enhancing the user experience when configuring settings in the editor.

image

🔧 Important refinement fixes

This release addresses two key issues related to refinement.

  1. Fixed a rare issue that could produce invalid results, especially in cases where the input data included clustered subsegments, which occasionally caused points to appear outside the triangulation domain.
  2. Resolved an issue where refinement with constraints would ignore boundaries if no holes were present.

📝 Changelog

Added

  • Dynamic triangulation support. Introduced DynamicInsertPoint extension for UnsafeTriangulator to support dynamic point insertion.
  • A new demo scene to better illustrate the functionality in the documentation.

Changed

  • Improved support for TriangulatorSettings in UnityEditor by adding a missing backing field, a setter, and editor-related attributes for properties.
  • Refinement step optimization. Refactored the refinement step to enhance performance and simplify the code.

Fixed

  • Refinement with constraints without holes. Corrected an issue where refinement with constraints would ignore boundaries when holes were absent.
  • Invalid refinement results. Fixed a rare issue where refinement could produce invalid results, especially when input data included subsegment clusters, leading to points appearing outside the triangulation domain.

Full Changelog: v3.3.0...v3.4.0

v3.3.0

23 Sep 15:25

Choose a tag to compare

✨ What's new?

🆕 Ignored constraints for seed planting

This feature is especially useful when the user wants to include a constraint but does not wish to enable any planting hole mechanism for that edge. Consider the following example input:

feat1

using var positions = new NativeArray<double2>(..., Allocator.Persistent);
using var constraintEdges = new NativeArray<int>(..., Allocator.Persistent);
using var ignoreConstraint = new NativeArray<bool>(..., Allocator.Persistent);
using var triangulator = new Triangulator(Allocator.Persistent)
{
  Input = {
    Positions = positions,
    ConstraintEdges = constraintEdges,
    IgnoreConstraintForPlantingSeeds = ignoreConstraint,
  },
  Settings = { AutoHolesAndBoundary = true, },
};

triangulator.Run();

var triangles = triangulator.Output.Triangles;

In this example, the red constraint is set to true in IgnoreConstraintForPlantingSeeds. As a result, a hole is not generated from red constraint, and the edge remains part of the final triangulation.

📈 Faster hole planting

The complexity has improved from $\mathcal O(n^2)$ to $\mathcal O(n)$, making hole planting almost free compared to the Delaunay step.
Below is a benchmark for Constrained Delaunay triangulation with the auto-holes option enabled. As a test case, we use a square containing hole squares (denoted by #holes). Reference timings for the auto-holes option disabled are marked with a dashed line.

image

Co-authored-by: @HalfVoxel

🔧Fix overflow for int2

Overflow for int2 coordinates for large coordinates with differences around $\sim 2^{20}$ was resolved.
Example input which failed to triangulate correctly before this release can be seen below:

image

Co-authored-by: @HalfVoxel

🆕 Status error codes

New flags have been added to the Status enum for enhanced error handling during triangulation. Users can now catch errors during validation more effectively. Note: The Status enum is now decorated with the [Flags]. To check if no errors occurred, use

if (status != Status.OK)
{
    return;
}

Read more at project's documentation.

📝 Changelog

Added

  • Ignored constraints for seed planting. Users can now ignore specific constraints during the seed planting process. This is especially useful when constraining edges without creating hole boundaries. This option can be set using Input.IgnoreConstraintForPlantingSeeds. Additionally, post-triangulation verification can be done with Output.IgnoredHalfedgesForPlantingSeeds, which provides a list of booleans indicating whether a given halfedge was ignored during seed planting.
  • Status error codes. New flags have been added to the Status enum for enhanced error handling during triangulation. Users can now catch errors during validation more effectively. Note: The Status enum is now decorated with the [Flags]. To check if no errors occurred, use status == Status.OK.

Changed

  • Faster hole planting. The complexity has improved from 𝒪(n²) to 𝒪(n), making hole planting almost free compared to the Delaunay step.
  • Improved validation. All input data buffers are now validated. Additionally, some unconfigured settings can trigger log warnings.

Fixed

  • Integer overflow for int2 coordinates. Resolved an overflow issue for large coordinates with differences around ~2²⁰.

Full Changelog: v3.2.1...v3.3.0

v3.2.1

03 Sep 20:23

Choose a tag to compare

🌿 Green Release1

Hi!

It's me again! Today's small release focuses on updates to the API documentation, with many additional lines of code (~0.6k LOC) added for improvements.

Best,
Andrzej

📝 Changelog

Changed

  • Significant updates to the API documentation.
  • (Internal) Miscellaneous changes.

Deprecated

  • The OutputData(Triangulator<T2>) constructor is now obsolete. It will be made internal in future versions.

Fixed

  • Resolved a potential issue causing an infinite loop during the PlantingSeedStep with AutoHolesAndBoundary.

Full Changelog: v3.2.0...v3.2.1

  1. I hope that you also have comments in green in your IDE.

v3.2.0

28 Aug 16:32

Choose a tag to compare

✨ What's new?

As the holidays come to an end, we're excited to announce a new release of the Burst Triangulator!

🆕 New generic types support!

In this version, there is finally support for the int2 and fp2 (fixed-point Q31.32) types. These types are crucial as they ensure that the triangulation result is independent of the hardware architecture, making them suitable for use in scenarios such as lockstep simulations (e.g., in multiplayer RTS games).

The supported features for each type in this version are as follows:

type delaunay constraints holes refinement preprocessors notes
float2 ✔️ ✔️ ✔️ ✔️ ✔️
Vector2 ✔️ ✔️ ✔️ ✔️ ✔️ Via float2 reinterpret
double2 ✔️ ✔️ ✔️ ✔️ ✔️
fp2 ✔️ ✔️ ✔️ ✔️ ✔️ Requires additional package1
int2 ✔️ ✔️ 🟡2 🟡3 Support up to $\sim 2^{20}$

Below the benchmark:

image

📝 Changelog

Added

  • Support for additional types: Vector2, int2, and fp2 (fixed-point in Q31.32 format). Note: fp2 requires an optional dependency. Refer to the manual for more details.
  • (Internal) Introduced TrianglesComparer to simplify triangle assertions in tests.
  • Args is now blittable and can be used in Burst-compiled static methods.
  • Enhanced validation logs to include position information.

Changed

  • (Internal) Various simplifications, minor performance improvements, refactoring, and additional code comments.

Deprecated

  • AsNativeArray() and ManagedInput have been deprecated for safety reasons. Use AsNativeArray(out Handle handle) instead. Refer to the manual for more information.

Fixed

  • Corrected the refinement of concentric shells segment splitting factor alpha.
  • Fixed safety issues with AsNativeArray.
  • Fully collinear input is now handled correctly.


Full Changelog: v3.1.0...v3.2.0


  1. This feature is available through an optional dependency. Users must install com.danielmansson.mathematics.fixedpoint. More info in the documentation.

  2. In the current implementation, holes are fully supported with Settings.AutoHolesAndBoundary. However, manual holes with int2 coordinates may not guarantee that the given hole can be created. An additional extension is planned in the future to support holes with manual floating-point precision for int2.

  3. Support for Preprocessor.COM with translation only is available.