clipper2 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +165 -0
- data/examples/larb_transform.rb +17 -0
- data/examples/rugl_mesh.rb +33 -0
- data/examples/svg_boolean.rb +28 -0
- data/ext/clipper2/CMakeLists.txt +27 -0
- data/ext/clipper2/clipper2_native.cpp +2 -0
- data/ext/clipper2/extconf.rb +27 -0
- data/ext/clipper2/generate_bindings.rb +73 -0
- data/ext/clipper2/memory_check.cpp +48 -0
- data/ext/clipper2/vendor/LICENSE +23 -0
- data/ext/clipper2/vendor/UPSTREAM.md +11 -0
- data/ext/clipper2/vendor/include/clipper2/clipper.core.h +1159 -0
- data/ext/clipper2/vendor/include/clipper2/clipper.engine.h +635 -0
- data/ext/clipper2/vendor/include/clipper2/clipper.export.h +851 -0
- data/ext/clipper2/vendor/include/clipper2/clipper.h +796 -0
- data/ext/clipper2/vendor/include/clipper2/clipper.minkowski.h +117 -0
- data/ext/clipper2/vendor/include/clipper2/clipper.offset.h +125 -0
- data/ext/clipper2/vendor/include/clipper2/clipper.rectclip.h +80 -0
- data/ext/clipper2/vendor/include/clipper2/clipper.triangulation.h +27 -0
- data/ext/clipper2/vendor/include/clipper2/clipper.version.h +6 -0
- data/ext/clipper2/vendor/src/clipper.engine.cpp +3152 -0
- data/ext/clipper2/vendor/src/clipper.offset.cpp +661 -0
- data/ext/clipper2/vendor/src/clipper.rectclip.cpp +1027 -0
- data/ext/clipper2/vendor/src/clipper.triangulation.cpp +1221 -0
- data/lib/clipper2/api.rb +45 -0
- data/lib/clipper2/c_paths64.rb +80 -0
- data/lib/clipper2/earcut/engine.rb +191 -0
- data/lib/clipper2/earcut/geometry.rb +100 -0
- data/lib/clipper2/earcut/node.rb +18 -0
- data/lib/clipper2/earcut/ring.rb +94 -0
- data/lib/clipper2/earcut.rb +18 -0
- data/lib/clipper2/errors.rb +19 -0
- data/lib/clipper2/generated.rb +62 -0
- data/lib/clipper2/native.rb +38 -0
- data/lib/clipper2/native_memory.rb +64 -0
- data/lib/clipper2/operations.rb +206 -0
- data/lib/clipper2/path.rb +171 -0
- data/lib/clipper2/path_simplifier.rb +61 -0
- data/lib/clipper2/paths.rb +130 -0
- data/lib/clipper2/quantizer.rb +48 -0
- data/lib/clipper2/triangulation.rb +103 -0
- data/lib/clipper2/version.rb +5 -0
- data/lib/clipper2.rb +34 -0
- data/licenses/CLIPPER2-BSL-1.0.txt +23 -0
- data/licenses/EARCUT-ISC.txt +15 -0
- metadata +113 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: e8e374bfe4ee5209b63a97203c8c675d132609b35b008b2ec829241cf7f94063
|
|
4
|
+
data.tar.gz: 7634829ff829a134af6885b75490629c3b8d1ea64301cefcce1a2055560d45fb
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 1864bb0788c3417a00e52e4cb4b7fd7d7b11b3aebf708760ec09852cc2796e0b9328fd983ef7b42be8314f7feb1e85ef67bee41176e38c74d7784255ece21a15
|
|
7
|
+
data.tar.gz: 3dd05a3210852de65e99bdf0f64da9e0e6f38dd7d1cbd07b1f47aa0f16ebd6d220eaf929870a547d4e3e6365cecb5f9ee0406f97b8ad602e4562d803b68340da
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yudai Takada
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Clipper2
|
|
2
|
+
|
|
3
|
+
Clipper2 brings robust 2D polygon clipping and offsetting to Ruby. It wraps Angus Johnson's
|
|
4
|
+
[Clipper2](https://github.com/AngusJohnson/Clipper2) C export API with FFI and keeps all native
|
|
5
|
+
geometry on a quantized signed-integer grid.
|
|
6
|
+
|
|
7
|
+
The gem supports union, intersection, difference, XOR, open paths, polygon and line offsets,
|
|
8
|
+
rectangle clipping, Minkowski sums, point containment, path simplification, and dependency-free
|
|
9
|
+
Earcut triangulation into GPU-ready buffers.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
Add the gem to your bundle:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bundle add clipper2
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or install it directly:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
gem install clipper2
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Platform gems contain a precompiled shared library. When a platform gem is unavailable, the
|
|
26
|
+
source gem builds the pinned Clipper2 C++17 sources with Ruby's standard extension toolchain and
|
|
27
|
+
therefore requires a C++ compiler and `make`. CMake is used by maintainers to build platform gems.
|
|
28
|
+
|
|
29
|
+
## Coordinate model
|
|
30
|
+
|
|
31
|
+
Ruby callers use ordinary numeric coordinates. `Path` quantizes them to signed 64-bit integers
|
|
32
|
+
using a scale and restores floats when enumerated:
|
|
33
|
+
|
|
34
|
+
```ruby
|
|
35
|
+
require "clipper2"
|
|
36
|
+
|
|
37
|
+
Clipper2.default_scale = 1_000 # 0.001 coordinate-unit precision
|
|
38
|
+
|
|
39
|
+
path = Clipper2::Path[[0, 0], [10, 0], [10, 10], [0, 10]]
|
|
40
|
+
path.to_a # => [[0.0, 0.0], [10.0, 0.0], [10.0, 10.0], [0.0, 10.0]]
|
|
41
|
+
path.area # => 100.0 (signed; positive is counter-clockwise)
|
|
42
|
+
path.orientation # => :ccw
|
|
43
|
+
path.bounds # => [0.0, 0.0, 10.0, 10.0]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Every `Paths` collection owns one scale. Operations between different scales raise
|
|
47
|
+
`Clipper2::ScaleError`; coordinates that cannot be represented safely raise
|
|
48
|
+
`Clipper2::RangeError`. Choose a scale that gives the precision you need without making
|
|
49
|
+
`coordinate * scale` unnecessarily large.
|
|
50
|
+
|
|
51
|
+
## Boolean operations
|
|
52
|
+
|
|
53
|
+
All convenience methods accept a `Path`, `Paths`, one raw point array, or an array of raw paths,
|
|
54
|
+
and return `Clipper2::Paths`:
|
|
55
|
+
|
|
56
|
+
```ruby
|
|
57
|
+
a = Clipper2::Path[[0, 0], [10, 0], [10, 10], [0, 10]]
|
|
58
|
+
b = Clipper2::Path[[5, 0], [15, 0], [15, 10], [5, 10]]
|
|
59
|
+
|
|
60
|
+
Clipper2.union(a, b)
|
|
61
|
+
Clipper2.intersect(a, b, fill_rule: :non_zero)
|
|
62
|
+
Clipper2.difference(a, b)
|
|
63
|
+
Clipper2.xor(a, b)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Fill rules are `:even_odd`, `:non_zero`, `:positive`, and `:negative`. Clipper2 intentionally
|
|
67
|
+
handles self-intersecting input according to the selected fill rule.
|
|
68
|
+
|
|
69
|
+
For open subject paths, use the general API. It returns closed and open solutions separately:
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
line = Clipper2::Path[[-5, 5], [15, 5]]
|
|
73
|
+
closed, open = Clipper2.boolean(:intersection, [], a, subject_open: line)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Offsets and clipping
|
|
77
|
+
|
|
78
|
+
```ruby
|
|
79
|
+
grown = Clipper2.inflate(a, 2.5, join: :round, end_type: :polygon)
|
|
80
|
+
shrunk = Clipper2.inflate(a, -1.0)
|
|
81
|
+
|
|
82
|
+
open_line = Clipper2::Path[[0, 0], [10, 0]]
|
|
83
|
+
stroke = Clipper2.inflate(open_line, 3.0, join: :round, end_type: :round)
|
|
84
|
+
|
|
85
|
+
inside = Clipper2.rect_clip(grown, [0, 0, 12, 12])
|
|
86
|
+
line_segments = Clipper2.rect_clip_lines(open_line, [2, -1, 8, 1])
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Join types are `:square`, `:bevel`, `:round`, and `:miter`. End types are `:polygon`, `:joined`,
|
|
90
|
+
`:butt`, `:square`, and `:round`. `arc_tolerance` defaults to `0.25` public coordinate units and
|
|
91
|
+
is scaled automatically.
|
|
92
|
+
|
|
93
|
+
Minkowski sums and differences use the same integer path representation:
|
|
94
|
+
|
|
95
|
+
```ruby
|
|
96
|
+
pattern = Clipper2::Path[[0, 0], [1, 0], [1, 1], [0, 1]]
|
|
97
|
+
Clipper2.minkowski_sum(pattern, a)
|
|
98
|
+
Clipper2.minkowski_difference(pattern, a)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Path utilities
|
|
102
|
+
|
|
103
|
+
```ruby
|
|
104
|
+
path.contains?([5, 5]) # boundary points are included
|
|
105
|
+
path.reverse
|
|
106
|
+
path.simplify(0.05)
|
|
107
|
+
path.to_packed # little-endian float32 x/y pairs
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`Paths#simplify` applies the same tolerance to each member.
|
|
111
|
+
|
|
112
|
+
## Packed triangulation
|
|
113
|
+
|
|
114
|
+
`Clipper2.triangulate` uses the bundled pure Ruby Earcut implementation. Counter-clockwise rings
|
|
115
|
+
are outer contours and clockwise rings contained by an outer contour are holes:
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
outer = Clipper2::Path[[0, 0], [10, 0], [10, 10], [0, 10]]
|
|
119
|
+
hole = Clipper2::Path[[2, 2], [2, 8], [8, 8], [8, 2]] # clockwise
|
|
120
|
+
|
|
121
|
+
mesh = Clipper2.triangulate(Clipper2::Paths[outer, hole])
|
|
122
|
+
mesh.indices # little-endian packed u32 triangle indices
|
|
123
|
+
mesh.vertices # little-endian packed float32 x/y vertices
|
|
124
|
+
mesh.index_count
|
|
125
|
+
mesh.vertex_count
|
|
126
|
+
|
|
127
|
+
indices, vertices = mesh # array destructuring is also supported
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Every emitted triangle is counter-clockwise in a y-up coordinate system. The two packed strings
|
|
131
|
+
can be uploaded directly by renderers that accept raw buffers. See
|
|
132
|
+
[`examples/rugl_mesh.rb`](examples/rugl_mesh.rb) and
|
|
133
|
+
[`examples/larb_transform.rb`](examples/larb_transform.rb).
|
|
134
|
+
|
|
135
|
+
## Empty input and errors
|
|
136
|
+
|
|
137
|
+
Empty inputs return empty `Paths` or an empty `Triangulation`. Invalid options and rectangles
|
|
138
|
+
raise `ArgumentError`; scale, range, codec, triangulation, and native failures use subclasses of
|
|
139
|
+
`Clipper2::Error`.
|
|
140
|
+
|
|
141
|
+
Native arrays are always copied once into Ruby and released through Clipper2's matching
|
|
142
|
+
`DisposeArray64` export.
|
|
143
|
+
|
|
144
|
+
## Development
|
|
145
|
+
|
|
146
|
+
Ruby 3.2 or newer, CMake, and a C++17 compiler are required.
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
bundle install
|
|
150
|
+
bundle exec rake # generated-binding check, native build, and specs
|
|
151
|
+
bundle exec rake native:build # native library only
|
|
152
|
+
bundle exec rake native:gem # platform gem in pkg/
|
|
153
|
+
bundle exec rake build # source gem in pkg/
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`ext/clipper2/generate_bindings.rb` derives enum values and export names from the vendored headers.
|
|
157
|
+
Run `bundle exec rake bindings:generate` after updating Clipper2 and commit the generated Ruby file.
|
|
158
|
+
|
|
159
|
+
The SVG boolean demo is available at [`examples/svg_boolean.rb`](examples/svg_boolean.rb).
|
|
160
|
+
|
|
161
|
+
## Licenses
|
|
162
|
+
|
|
163
|
+
The Ruby wrapper is MIT licensed. Vendored Clipper2 2.0.1 is distributed under the Boost Software
|
|
164
|
+
License 1.0, and the Ruby Earcut port carries Mapbox's ISC license. The corresponding notices are
|
|
165
|
+
included in `LICENSE.txt`, `licenses/CLIPPER2-BSL-1.0.txt`, and `licenses/EARCUT-ISC.txt`.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "clipper2"
|
|
4
|
+
require "larb"
|
|
5
|
+
|
|
6
|
+
vectors = [
|
|
7
|
+
Larb::Vec2.new(0, 0),
|
|
8
|
+
Larb::Vec2.new(10, 0),
|
|
9
|
+
Larb::Vec2.new(10, 10),
|
|
10
|
+
Larb::Vec2.new(0, 10)
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
path = Clipper2::Path.new(vectors.map(&:to_a))
|
|
14
|
+
outline = Clipper2.inflate(path, 1.5, join: :round)
|
|
15
|
+
larb_paths = outline.map { |ring| ring.map { |x, y| Larb::Vec2.new(x, y) } }
|
|
16
|
+
|
|
17
|
+
puts "#{larb_paths.sum(&:length)} transformed outline vertices"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "clipper2"
|
|
4
|
+
require "rugl"
|
|
5
|
+
|
|
6
|
+
polygon = Clipper2::Path[[-0.8, -0.7], [0.8, -0.7], [0.35, 0.0], [0.8, 0.7], [-0.8, 0.7]]
|
|
7
|
+
mesh = Clipper2.triangulate(polygon)
|
|
8
|
+
|
|
9
|
+
rugl = Rugl.create(width: 800, height: 600, title: "Clipper2 packed mesh")
|
|
10
|
+
positions = rugl.buffer(data: mesh.vertices, type: :float)
|
|
11
|
+
elements = rugl.elements(data: mesh.indices.unpack("L<*"), type: :uint32, primitive: :triangles)
|
|
12
|
+
|
|
13
|
+
draw = rugl.command(
|
|
14
|
+
vert: <<~GLSL,
|
|
15
|
+
#version 410 core
|
|
16
|
+
layout(location = 0) in vec2 position;
|
|
17
|
+
void main() { gl_Position = vec4(position, 0.0, 1.0); }
|
|
18
|
+
GLSL
|
|
19
|
+
frag: <<~GLSL,
|
|
20
|
+
#version 410 core
|
|
21
|
+
out vec4 color;
|
|
22
|
+
void main() { color = vec4(0.15, 0.7, 0.55, 1.0); }
|
|
23
|
+
GLSL
|
|
24
|
+
attributes: { position: positions },
|
|
25
|
+
elements: elements
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
rugl.frame do
|
|
29
|
+
rugl.clear(color: [0.03, 0.04, 0.08, 1.0])
|
|
30
|
+
draw.call
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
rugl.destroy
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "clipper2"
|
|
4
|
+
|
|
5
|
+
subject = Clipper2::Path[[20, 20], [180, 20], [180, 140], [20, 140]]
|
|
6
|
+
clip = Clipper2::Path[[80, 0], [220, 80], [80, 160]]
|
|
7
|
+
result = Clipper2.difference(subject, clip)
|
|
8
|
+
|
|
9
|
+
polygon = lambda do |path, attributes|
|
|
10
|
+
points = path.map { |x, y| "#{x},#{y}" }.join(" ")
|
|
11
|
+
%(<polygon points="#{points}" #{attributes} />)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
layers = []
|
|
15
|
+
layers << polygon.call(subject, 'fill="#4f46e5" fill-opacity="0.18" stroke="#4f46e5"')
|
|
16
|
+
layers << polygon.call(clip, 'fill="#e11d48" fill-opacity="0.18" stroke="#e11d48"')
|
|
17
|
+
result.each { |path| layers << polygon.call(path, 'fill="#059669" fill-opacity="0.55" stroke="#047857"') }
|
|
18
|
+
|
|
19
|
+
svg = <<~SVG
|
|
20
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="240" height="180" viewBox="0 0 240 180">
|
|
21
|
+
<rect width="100%" height="100%" fill="white" />
|
|
22
|
+
#{layers.join("\n ")}
|
|
23
|
+
</svg>
|
|
24
|
+
SVG
|
|
25
|
+
|
|
26
|
+
output = ARGV.fetch(0, "clipper2-boolean.svg")
|
|
27
|
+
File.write(output, svg)
|
|
28
|
+
puts "wrote #{output}"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.15)
|
|
2
|
+
project(clipper2_native LANGUAGES CXX)
|
|
3
|
+
|
|
4
|
+
set(VENDOR_DIR "${CMAKE_CURRENT_SOURCE_DIR}/vendor")
|
|
5
|
+
set(CLIPPER2_SOURCES
|
|
6
|
+
"${VENDOR_DIR}/src/clipper.engine.cpp"
|
|
7
|
+
"${VENDOR_DIR}/src/clipper.offset.cpp"
|
|
8
|
+
"${VENDOR_DIR}/src/clipper.rectclip.cpp"
|
|
9
|
+
"${VENDOR_DIR}/src/clipper.triangulation.cpp"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
add_library(clipper2_native MODULE clipper2_native.cpp ${CLIPPER2_SOURCES})
|
|
13
|
+
target_compile_features(clipper2_native PRIVATE cxx_std_17)
|
|
14
|
+
target_include_directories(clipper2_native PRIVATE "${VENDOR_DIR}/include")
|
|
15
|
+
set_target_properties(clipper2_native PROPERTIES PREFIX "")
|
|
16
|
+
|
|
17
|
+
set(RUBY_EXTENSION_SUFFIX "" CACHE STRING "Ruby-loadable library suffix including the leading dot")
|
|
18
|
+
if(RUBY_EXTENSION_SUFFIX)
|
|
19
|
+
set_target_properties(clipper2_native PROPERTIES SUFFIX "${RUBY_EXTENSION_SUFFIX}")
|
|
20
|
+
endif()
|
|
21
|
+
|
|
22
|
+
option(CLIPPER2_BUILD_MEMORY_CHECK "Build the native allocation disposal check" OFF)
|
|
23
|
+
if(CLIPPER2_BUILD_MEMORY_CHECK)
|
|
24
|
+
add_executable(clipper2_memory_check memory_check.cpp ${CLIPPER2_SOURCES})
|
|
25
|
+
target_compile_features(clipper2_memory_check PRIVATE cxx_std_17)
|
|
26
|
+
target_include_directories(clipper2_memory_check PRIVATE "${VENDOR_DIR}/include")
|
|
27
|
+
endif()
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "mkmf"
|
|
4
|
+
|
|
5
|
+
vendor = File.expand_path("vendor", __dir__)
|
|
6
|
+
headers = File.join(vendor, "include")
|
|
7
|
+
sources = File.join(vendor, "src")
|
|
8
|
+
|
|
9
|
+
abort "Clipper2 vendored headers are missing" unless File.file?(File.join(headers, "clipper2", "clipper.export.h"))
|
|
10
|
+
|
|
11
|
+
$CPPFLAGS = "-I#{headers.shellescape} #{$CPPFLAGS}"
|
|
12
|
+
$VPATH << sources
|
|
13
|
+
$srcs = %w[
|
|
14
|
+
clipper2_native.cpp
|
|
15
|
+
clipper.engine.cpp
|
|
16
|
+
clipper.offset.cpp
|
|
17
|
+
clipper.rectclip.cpp
|
|
18
|
+
clipper.triangulation.cpp
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
if /mswin|msvc/ =~ RUBY_PLATFORM
|
|
22
|
+
$CXXFLAGS = "/std:c++17 /EHsc /utf-8 #{$CXXFLAGS}"
|
|
23
|
+
else
|
|
24
|
+
$CXXFLAGS = "-std=c++17 #{$CXXFLAGS}"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
create_makefile("clipper2/clipper2_native")
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
ROOT = File.expand_path("../..", __dir__)
|
|
4
|
+
HEADERS = File.join(__dir__, "vendor", "include", "clipper2")
|
|
5
|
+
OUTPUT = File.join(ROOT, "lib", "clipper2", "generated.rb")
|
|
6
|
+
|
|
7
|
+
ENUMS = {
|
|
8
|
+
"CLIP_TYPES" => ["clipper.engine.h", "ClipType"],
|
|
9
|
+
"FILL_RULES" => ["clipper.core.h", "FillRule"],
|
|
10
|
+
"JOIN_TYPES" => ["clipper.offset.h", "JoinType"],
|
|
11
|
+
"END_TYPES" => ["clipper.offset.h", "EndType"]
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
def snake_case(name)
|
|
15
|
+
name.gsub(/([a-z\d])([A-Z])/, "\\1_\\2").downcase
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def parse_enum(file_name, enum_name)
|
|
19
|
+
source = File.read(File.join(HEADERS, file_name))
|
|
20
|
+
body = source.match(/enum\s+class\s+#{Regexp.escape(enum_name)}(?:\s*:\s*[^\{]+)?\s*\{([^}]+)\}/m)&.[](1)
|
|
21
|
+
abort "Unable to find #{enum_name} in #{file_name}" unless body
|
|
22
|
+
|
|
23
|
+
next_value = 0
|
|
24
|
+
body.split(",").to_h do |entry|
|
|
25
|
+
name, explicit_value = entry.gsub(%r{//.*}, "").strip.split("=", 2).map(&:strip)
|
|
26
|
+
next_value = Integer(explicit_value, 0) if explicit_value
|
|
27
|
+
pair = [snake_case(name).to_sym, next_value]
|
|
28
|
+
next_value += 1
|
|
29
|
+
pair
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def parse_exports
|
|
34
|
+
source = File.read(File.join(HEADERS, "clipper.export.h"))
|
|
35
|
+
source.scan(/EXTERN_DLL_EXPORT\s+[^;\{]+?\b(\w+)\s*\(/m).flatten.uniq.sort - ["__declspec"]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def parse_version
|
|
39
|
+
source = File.read(File.join(HEADERS, "clipper.version.h"))
|
|
40
|
+
source.match(/CLIPPER2_VERSION\s*=\s*"([^"]+)"/)&.[](1) || abort("Unable to find Clipper2 version")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def render
|
|
44
|
+
constants = ENUMS.map do |constant, (file_name, enum_name)|
|
|
45
|
+
values = parse_enum(file_name, enum_name).map { |name, value| " #{name.inspect} => #{value}" }.join(",\n")
|
|
46
|
+
" #{constant} = {\n#{values}\n }.freeze"
|
|
47
|
+
end.join("\n\n")
|
|
48
|
+
exports = parse_exports.map { |name| " :#{name}" }.join(",\n")
|
|
49
|
+
|
|
50
|
+
<<~RUBY
|
|
51
|
+
# frozen_string_literal: true
|
|
52
|
+
|
|
53
|
+
# Generated by ext/clipper2/generate_bindings.rb. Do not edit by hand.
|
|
54
|
+
module Clipper2
|
|
55
|
+
module Generated
|
|
56
|
+
UPSTREAM_VERSION = #{parse_version.inspect}
|
|
57
|
+
|
|
58
|
+
#{constants}
|
|
59
|
+
|
|
60
|
+
NATIVE_EXPORTS = [
|
|
61
|
+
#{exports}
|
|
62
|
+
].freeze
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
RUBY
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
generated = render
|
|
69
|
+
if ARGV.include?("--check")
|
|
70
|
+
abort "#{OUTPUT} is out of date; run #{__FILE__}" unless File.file?(OUTPUT) && File.read(OUTPUT) == generated
|
|
71
|
+
else
|
|
72
|
+
File.write(OUTPUT, generated)
|
|
73
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#include "clipper2/clipper.h"
|
|
2
|
+
#include "clipper2/clipper.export.h"
|
|
3
|
+
|
|
4
|
+
#include <cstdint>
|
|
5
|
+
|
|
6
|
+
namespace {
|
|
7
|
+
|
|
8
|
+
constexpr std::uint8_t kUnion = 2;
|
|
9
|
+
constexpr std::uint8_t kNonZero = 1;
|
|
10
|
+
constexpr std::uint8_t kRound = 2;
|
|
11
|
+
constexpr std::uint8_t kPolygon = 0;
|
|
12
|
+
|
|
13
|
+
std::int64_t subject[] = {
|
|
14
|
+
12, 1, 4, 0,
|
|
15
|
+
0, 0, 100, 0, 100, 100, 0, 100,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
std::int64_t clip[] = {
|
|
19
|
+
12, 1, 4, 0,
|
|
20
|
+
50, -20, 120, -20, 120, 80, 50, 80,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
} // namespace
|
|
24
|
+
|
|
25
|
+
int main() {
|
|
26
|
+
using namespace Clipper2Lib;
|
|
27
|
+
|
|
28
|
+
constexpr int kIterations = 500;
|
|
29
|
+
const CRect64 rectangle{0, 0, 100, 100};
|
|
30
|
+
|
|
31
|
+
for (int iteration = 0; iteration < kIterations; ++iteration) {
|
|
32
|
+
CPaths64 closed = nullptr;
|
|
33
|
+
CPaths64 open = nullptr;
|
|
34
|
+
if (BooleanOp64(kUnion, kNonZero, subject, nullptr, clip, closed, open) != 0) {
|
|
35
|
+
return 1;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
CPaths64 inflated = InflatePaths64(closed, 3.0, kRound, kPolygon);
|
|
39
|
+
CPaths64 clipped = RectClip64(rectangle, inflated);
|
|
40
|
+
|
|
41
|
+
DisposeArray64(clipped);
|
|
42
|
+
DisposeArray64(inflated);
|
|
43
|
+
DisposeArray64(open);
|
|
44
|
+
DisposeArray64(closed);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return 0;
|
|
48
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Boost Software License - Version 1.0 - August 17th, 2003
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person or organization
|
|
4
|
+
obtaining a copy of the software and accompanying documentation covered by
|
|
5
|
+
this license (the "Software") to use, reproduce, display, distribute,
|
|
6
|
+
execute, and transmit the Software, and to prepare derivative works of the
|
|
7
|
+
Software, and to permit third-parties to whom the Software is furnished to
|
|
8
|
+
do so, all subject to the following:
|
|
9
|
+
|
|
10
|
+
The copyright notices in the Software and this entire statement, including
|
|
11
|
+
the above license grant, this restriction and the following disclaimer,
|
|
12
|
+
must be included in all copies of the Software, in whole or in part, and
|
|
13
|
+
all derivative works of the Software, unless such copies or derivative
|
|
14
|
+
works are solely in the form of machine-executable object code generated by
|
|
15
|
+
a source language processor.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
|
20
|
+
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
|
21
|
+
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
|
22
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
23
|
+
DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Vendored Clipper2
|
|
2
|
+
|
|
3
|
+
- Upstream: https://github.com/AngusJohnson/Clipper2
|
|
4
|
+
- Version: 2.0.1
|
|
5
|
+
- Commit: `f9c5eb6e14a59f6f5d65fbfb3564519a561cf4fd`
|
|
6
|
+
- License: Boost Software License 1.0 (see `LICENSE`)
|
|
7
|
+
|
|
8
|
+
Only `CPP/Clipper2Lib/include/clipper2` and `CPP/Clipper2Lib/src` are vendored. The exported C API
|
|
9
|
+
is compiled once by `clipper2_native.cpp`. That translation unit includes `clipper.h` before
|
|
10
|
+
`clipper.export.h` because the upstream export header uses Minkowski declarations without including
|
|
11
|
+
them itself.
|