Skip to content
Closed
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ install
*.ts
*.qm
.vscode
.clangd

package
dist
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ if (NOT DEFINED PROJECT_NAME)
set(NOT_SUBPROJECT ON)
endif()

project(CavalierContours VERSION 0.1)
project(CavalierContours VERSION 0.1.0)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
Expand Down
81 changes: 81 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Changelog

## Unreleased

- Add runtime-configurable geometry tolerances in `mathutils`:
- `setEpsilonConfig`, `getEpsilonConfig`, `resetEpsilonConfig`
- preserve existing defaults (`1e-8`, `1e-5`, `1e-4`, `1e-4`)
- Add point containment classification in C++ API:
- `PointContainment` enum
- `getPointContainment` (Outside/Inside/OnBoundary)
- Add polyline preprocessing in C++ API:
- `ClosedPolylineWinding` enum
- `normalizePolyline` (prune singularities + optional closed winding normalization)
- Expand C API surface with low-risk geometry helpers:
- `cavc_pline_invert_direction`
- `cavc_pline_prune_singularities`
- `cavc_pline_convert_arcs_to_lines`
- `cavc_pline_normalize`
- `cavc_spatial_index_create/delete/item_count/query_count/query`
- `cavc_get_point_containment`
- `cavc_get_tolerances`, `cavc_set_tolerances`, `cavc_reset_tolerances`
- Expand C API test coverage:
- add `TEST_cavc_api_regression` for legacy mutating chains and new API regressions
- fix path-length skip guard in `TEST_cavc_pline_function`
- harden `plineFromVertexes` helper to safely support empty input
- refactor `combine_with_self_invariants` into closed-only parameterized suite (remove stale
runtime skips on open half-circle cases)
- Unify parallel offset options shape for upcoming Join/EndCap work:
- C++: add `ParallelOffsetOptions`, `OffsetJoinType`, `OffsetEndCapType`
- C API: replace offset bit flags with `cavc_parallel_offset_options`
- add `cavc_parallel_offset_default_options`
- keep behavior unchanged: only `round` join/end cap are currently implemented
- Extend join behavior incrementally:
- add `miter/bevel` for line-line joins in `parallelOffset`
- add `miter_limit` option (C++ and C API) with line-line and arc-involved miter clipping
- add arc-involved non-round joins using endpoint tangent intersection
(collapsed-arc joins degrade to bevel)
- add regression coverage for `CAVC_OFFSET_JOIN_MITER` and `CAVC_OFFSET_JOIN_BEVEL`, including
arc-involved join scenarios
- add arc-heavy join stress matrix coverage for high-curvature / near-tangent / degenerate
repeated-vertex inputs (positive and negative deltas), including `miter_limit=1` bevel
equivalence and high-limit miter divergence checks
- Extend end cap behavior incrementally:
- add `square` end cap for open polylines with line start/end segments
(endpoint extension + dual-slice cap-circle shift)
- extend `square` end cap to arc start/end by preserving endpoint arc geometry and inserting
explicit cap line segments when needed
- separate `butt` from `round` for open polylines:
`butt` uses endpoint-normal line clipping while `round` keeps endpoint-circle clipping
- add regression coverage for `CAVC_OFFSET_END_CAP_SQUARE` on arc start/end for both
positive and negative deltas
- add regression coverage for `CAVC_OFFSET_END_CAP_BUTT` on arc start/end clip behavior
- add stress matrix coverage for `CAVC_OFFSET_END_CAP_BUTT` on open polylines:
high-curvature / near-tangent / multi-intersection, each with positive and negative offsets
- add degenerate open-polyline end cap stress matrix across `round/square/butt`:
repeated endpoints / near-zero-radius arcs / high-density self-intersection, each with positive
and negative offsets
- Extend offset-islands engineering:
- add stable geometry ordering for `ParallelOffsetIslands::compute` outputs
- add loop role/tag and topology helpers:
`OffsetLoopRole`, `OffsetLoopTopologyNode`, `buildOffsetLoopTopology`
- expose topology output in C API with ownership-safe handle:
`cavc_offset_loop_topology_build/delete/count/get`
- add dedicated regression test target `TEST_cavc_offset_islands`
- Performance-oriented tuning (no behavior change):
- reuse query buffers for open-polyline end-cap circle clipping in dual-slice offset path
- pre-reserve frequently reused containers in offset-islands slicing/topology flow
- add bounding-box early reject before expensive containment checks in topology parent search
- add batch spatial-index helper `createApproxSpatialIndices`
- switch `ParallelOffsetIslands` loop-index construction to batch path for
offset loops and stitched loops
- add `offsetislandsbenchmarks` for:
`scalar vs batch index build`, large-scale `buildOffsetLoopTopology`,
and large-scale `ParallelOffsetIslands::compute`

## 0.1.0

- Add option to enable clipper library for benchmarks: `CAVC_ENABLE_CLIPPER_BENCHMARKS`
- Rename variables whose declarations shadow local/previous variables
- if parameter shadows the local variable, the local variable is renamed to `p_<old_name>`
- if variable in function shadows the local variable, the parameter is renamed to `l_<old_name>`
114 changes: 57 additions & 57 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,73 +133,73 @@ All offsets were performed with rounded joins (maintaining exact offset distance

1e-2 (0.01) and 1e-3 (0.001) at the end of "arcs approx." refers to the error when approximating an arc as a series of line segments, it is the maximum allowed distance between the approximating line segments and the original arc. Note the profile vertex distances are in the range of 0-50 so the 1e-2 (0.01) error is intolerable for many applications, but significantly cuts down on the number of generated segments.

| benchmark | vertex count | arcs approx. 1e-2 vertex count | arcs approx. 1e-3 vertex count |
|-----------------------------|--------------|--------------------------------|--------------------------------|
| BM_square | 4 | 4 | 4 |
| BM_circle | 2 | 142 | 446 |
| BM_roundedRectangle | 8 | 56 | 164 |
| BM_Profile1 | 6 | 80 | 241 |
| BM_Profile2 | 11 | 162 | 494 |
| BM_PathologicalProfile1/10 | 10 | 400 | 1240 |
| BM_PathologicalProfile1/25 | 25 | 625 | 1975 |
| BM_PathologicalProfile1/50 | 50 | 900 | 2800 |
| BM_PathologicalProfile1/100 | 100 | 1300 | 4000 |
| benchmark | vertex count | arcs approx. 1e-2 vertex count | arcs approx. 1e-3 vertex count |
| --------------------------- | ------------ | ------------------------------ | ------------------------------ |
| BM_square | 4 | 4 | 4 |
| BM_circle | 2 | 142 | 446 |
| BM_roundedRectangle | 8 | 56 | 164 |
| BM_Profile1 | 6 | 80 | 241 |
| BM_Profile2 | 11 | 162 | 494 |
| BM_PathologicalProfile1/10 | 10 | 400 | 1240 |
| BM_PathologicalProfile1/25 | 25 | 625 | 1975 |
| BM_PathologicalProfile1/50 | 50 | 900 | 2800 |
| BM_PathologicalProfile1/100 | 100 | 1300 | 4000 |

All times are wall time. Exact details of the benchmark profiles and offsets run can be found under the dev project [here](https://github.com/jbuckmccready/CavalierContoursDev). For more on the clipper library see [[11]](#references).

### CavalierContours (Arcs Approximated) vs. Clipper

| benchmark | cavc arcs approx. 1e-2 (ms) | clipper arcs approx. 1e-2 (ms) | cavc vs. clipper |
|-----------------------------|-----------------------------|--------------------------------|------------------|
| BM_square | **0.21** | 0.72 | 3.5x |
| BM_circle | **9.11** | 9.47 | 1.0x |
| BM_roundedRectangle | **3.92** | 8.46 | 2.2x |
| BM_Profile1 | 8.55 | **6.33** | 0.7x |
| BM_Profile2 | 17.57 | **16.59** | 0.9x |
| BM_PathologicalProfile1/10 | **34.97** | 136.46 | 3.9x |
| BM_PathologicalProfile1/25 | **70.93** | 241.95 | 3.4x |
| BM_PathologicalProfile1/50 | **138.17** | 514.95 | 3.7x |
| BM_PathologicalProfile1/100 | **336.37** | 1347.09 | 4.0x |

| benchmark | cavc arcs approx. 1e-3 (ms) | clipper arcs approx. 1e-3 (ms) | cavc vs. clipper |
|-----------------------------|-----------------------------|--------------------------------|------------------|
| BM_square | **0.21** | 1.71 | 8.1x |
| BM_circle | **38.04** | 61.37 | 1.6x |
| BM_roundedRectangle | **13.14** | 61.61 | 4.7x |
| BM_Profile1 | 27.07 | **24.44** | 0.9x |
| BM_Profile2 | **52.64** | 73.64 | 1.4x |
| BM_PathologicalProfile1/10 | **109.96** | 1383.46 | 12.6x |
| BM_PathologicalProfile1/25 | **227.51** | 2824.67 | 12.4x |
| BM_PathologicalProfile1/50 | **385.46** | 6170.26 | 16.0x |
| BM_PathologicalProfile1/100 | **821.15** | 15797.60 | 19.2x |
| benchmark | cavc arcs approx. 1e-2 (ms) | clipper arcs approx. 1e-2 (ms) | cavc vs. clipper |
| --------------------------- | --------------------------- | ------------------------------ | ---------------- |
| BM_square | **0.21** | 0.72 | 3.5x |
| BM_circle | **9.11** | 9.47 | 1.0x |
| BM_roundedRectangle | **3.92** | 8.46 | 2.2x |
| BM_Profile1 | 8.55 | **6.33** | 0.7x |
| BM_Profile2 | 17.57 | **16.59** | 0.9x |
| BM_PathologicalProfile1/10 | **34.97** | 136.46 | 3.9x |
| BM_PathologicalProfile1/25 | **70.93** | 241.95 | 3.4x |
| BM_PathologicalProfile1/50 | **138.17** | 514.95 | 3.7x |
| BM_PathologicalProfile1/100 | **336.37** | 1347.09 | 4.0x |

| benchmark | cavc arcs approx. 1e-3 (ms) | clipper arcs approx. 1e-3 (ms) | cavc vs. clipper |
| --------------------------- | --------------------------- | ------------------------------ | ---------------- |
| BM_square | **0.21** | 1.71 | 8.1x |
| BM_circle | **38.04** | 61.37 | 1.6x |
| BM_roundedRectangle | **13.14** | 61.61 | 4.7x |
| BM_Profile1 | 27.07 | **24.44** | 0.9x |
| BM_Profile2 | **52.64** | 73.64 | 1.4x |
| BM_PathologicalProfile1/10 | **109.96** | 1383.46 | 12.6x |
| BM_PathologicalProfile1/25 | **227.51** | 2824.67 | 12.4x |
| BM_PathologicalProfile1/50 | **385.46** | 6170.26 | 16.0x |
| BM_PathologicalProfile1/100 | **821.15** | 15797.60 | 19.2x |

The above benchmarks were taken by converting all arcs to line segments before running the profile through the offsetting algorithm. Even with no arcs in the input CavalierContours is competitive with a noticeable speedup in many cases. This is in part due to CavalierContours not having to construct rounded joins from line segments (an exact arc can be constructed instead).

### CavalierContours (Arcs Included) vs. Clipper

| benchmark | cavc w/ arcs (ms) | clipper arcs approx. 1e-2 (ms) | cavc vs. clipper |
|-----------------------------|-------------------|--------------------------------|------------------|
| BM_square | **0.21** | 0.72 | 3.4x |
| BM_circle | **0.12** | 9.47 | 76.4x |
| BM_roundedRectangle | **0.49** | 8.46 | 17.3x |
| BM_Profile1 | **0.76** | 6.33 | 8.4x |
| BM_Profile2 | **1.55** | 16.59 | 10.7x |
| BM_PathologicalProfile1/10 | **1.61** | 136.46 | 84.9x |
| BM_PathologicalProfile1/25 | **7.17** | 241.95 | 33.8x |
| BM_PathologicalProfile1/50 | **23.98** | 514.95 | 21.5x |
| BM_PathologicalProfile1/100 | **85.10** | 1347.09 | 15.8x |

| benchmark | cavc w/ arcs (ms) | clipper arcs approx. 1e-3 (ms) | cavc vs. clipper |
|-----------------------------|-------------------|--------------------------------|------------------|
| BM_square | **0.21** | 1.71 | 8.1x |
| BM_circle | **0.12** | 61.37 | 494.7x |
| BM_roundedRectangle | **0.49** | 61.61 | 126.1x |
| BM_Profile1 | **0.76** | 24.44 | 32.3x |
| BM_Profile2 | **1.55** | 73.64 | 47.6x |
| BM_PathologicalProfile1/10 | **1.61** | 1383.46 | 861.0x |
| BM_PathologicalProfile1/25 | **7.17** | 2824.67 | 394.2x |
| BM_PathologicalProfile1/50 | **23.98** | 6170.26 | 257.3x |
| BM_PathologicalProfile1/100 | **85.10** | 15797.60 | 185.6x |
| benchmark | cavc w/ arcs (ms) | clipper arcs approx. 1e-2 (ms) | cavc vs. clipper |
| --------------------------- | ----------------- | ------------------------------ | ---------------- |
| BM_square | **0.21** | 0.72 | 3.4x |
| BM_circle | **0.12** | 9.47 | 76.4x |
| BM_roundedRectangle | **0.49** | 8.46 | 17.3x |
| BM_Profile1 | **0.76** | 6.33 | 8.4x |
| BM_Profile2 | **1.55** | 16.59 | 10.7x |
| BM_PathologicalProfile1/10 | **1.61** | 136.46 | 84.9x |
| BM_PathologicalProfile1/25 | **7.17** | 241.95 | 33.8x |
| BM_PathologicalProfile1/50 | **23.98** | 514.95 | 21.5x |
| BM_PathologicalProfile1/100 | **85.10** | 1347.09 | 15.8x |

| benchmark | cavc w/ arcs (ms) | clipper arcs approx. 1e-3 (ms) | cavc vs. clipper |
| --------------------------- | ----------------- | ------------------------------ | ---------------- |
| BM_square | **0.21** | 1.71 | 8.1x |
| BM_circle | **0.12** | 61.37 | 494.7x |
| BM_roundedRectangle | **0.49** | 61.61 | 126.1x |
| BM_Profile1 | **0.76** | 24.44 | 32.3x |
| BM_Profile2 | **1.55** | 73.64 | 47.6x |
| BM_PathologicalProfile1/10 | **1.61** | 1383.46 | 861.0x |
| BM_PathologicalProfile1/25 | **7.17** | 2824.67 | 394.2x |
| BM_PathologicalProfile1/50 | **23.98** | 6170.26 | 257.3x |
| BM_PathologicalProfile1/100 | **85.10** | 15797.60 | 185.6x |

The above benchmarks compare CavalierContours taking in the original input (arcs included) vs. Clipper (arcs must be approximated). The performance benefits of processing the arcs directly is quickly realized.

Expand Down
Loading
Loading