gdsdiff 0.1.0__py3-none-any.whl
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.
- gdsdiff/__init__.py +148 -0
- gdsdiff/__main__.py +7 -0
- gdsdiff/_environment.py +12 -0
- gdsdiff/_version.py +34 -0
- gdsdiff/acceptance_evidence.py +530 -0
- gdsdiff/api.py +2955 -0
- gdsdiff/backends/__init__.py +26 -0
- gdsdiff/backends/base.py +77 -0
- gdsdiff/backends/boundary_loop_ctypes.py +191 -0
- gdsdiff/backends/cpu.py +20 -0
- gdsdiff/backends/cpu_ctypes.py +255 -0
- gdsdiff/backends/cuda.py +40 -0
- gdsdiff/backends/cuda_ctypes.py +1297 -0
- gdsdiff/backends/exact.py +424 -0
- gdsdiff/backends/geometry_ctypes.py +252 -0
- gdsdiff/backends/identity.py +53 -0
- gdsdiff/backends/metal.py +198 -0
- gdsdiff/backends/metal_ctypes.py +866 -0
- gdsdiff/backends/polygon_extract_ctypes.py +233 -0
- gdsdiff/backends/structural_ctypes.py +130 -0
- gdsdiff/boundary_loops.py +616 -0
- gdsdiff/cache.py +544 -0
- gdsdiff/canonicalize.py +176 -0
- gdsdiff/cli.py +352 -0
- gdsdiff/config.py +257 -0
- gdsdiff/cuda_setup.py +174 -0
- gdsdiff/design_history.py +65 -0
- gdsdiff/diagnostics.py +42 -0
- gdsdiff/diff_gds.py +66 -0
- gdsdiff/exact_diff_markdown.py +979 -0
- gdsdiff/exact_oracle.py +649 -0
- gdsdiff/extract.py +376 -0
- gdsdiff/gds_metadata.py +43 -0
- gdsdiff/geometry.py +112 -0
- gdsdiff/grid.py +143 -0
- gdsdiff/hierarchy.py +215 -0
- gdsdiff/hierarchy_extract.py +263 -0
- gdsdiff/inventory.py +153 -0
- gdsdiff/multiprocessing_support.py +39 -0
- gdsdiff/native.py +135 -0
- gdsdiff/native_cache.py +265 -0
- gdsdiff/native_src/cpu_prefilter.cpp +349 -0
- gdsdiff/native_src/gds_boundary_loops.cpp +505 -0
- gdsdiff/native_src/gds_geometry_scan.cpp +601 -0
- gdsdiff/native_src/gds_polygon_extract.cpp +594 -0
- gdsdiff/native_src/gds_structural_scan.cpp +335 -0
- gdsdiff/native_src/gdsdiff/CMakeLists.txt +74 -0
- gdsdiff/native_src/gdsdiff/core.cpp +191 -0
- gdsdiff/native_src/gdsdiff/core.hpp +96 -0
- gdsdiff/native_src/gdsdiff/cuda_assignment.cu +304 -0
- gdsdiff/native_src/gdsdiff/cuda_assignment.cuh +33 -0
- gdsdiff/native_src/gdsdiff/cuda_candidates.cu +133 -0
- gdsdiff/native_src/gdsdiff/cuda_candidates.cuh +17 -0
- gdsdiff/native_src/gdsdiff/cuda_ctypes.cu +4528 -0
- gdsdiff/native_src/gdsdiff/cuda_memory.cu +194 -0
- gdsdiff/native_src/gdsdiff/cuda_memory.cuh +34 -0
- gdsdiff/native_src/gdsdiff/metal_ctypes.mm +2791 -0
- gdsdiff/native_src/gdsdiff/python_bindings.cpp +21 -0
- gdsdiff/native_src/gdsdiff/test_core.cpp +93 -0
- gdsdiff/native_src/gdsdiff/test_cuda_assignment.cu +109 -0
- gdsdiff/native_src/gdsdiff/test_cuda_candidates.cu +94 -0
- gdsdiff/native_src/gdsdiff/test_cuda_memory.cu +97 -0
- gdsdiff/policy.py +305 -0
- gdsdiff/polygon_components.py +860 -0
- gdsdiff/profiles.py +152 -0
- gdsdiff/py.typed +1 -0
- gdsdiff/rect_coverage.py +384 -0
- gdsdiff/report.py +354 -0
- gdsdiff/schemas/gdsdiff_report-v1.schema.json +92 -0
- gdsdiff/semantics.py +75 -0
- gdsdiff/source_edge_diff.py +1120 -0
- gdsdiff/spatial.py +71 -0
- gdsdiff/structural_guard.py +161 -0
- gdsdiff/surplus_coverage.py +255 -0
- gdsdiff/synthetic_suite.py +1643 -0
- gdsdiff/templates.py +709 -0
- gdsdiff/tiling.py +153 -0
- gdsdiff/windowed_exact.py +270 -0
- gdsdiff/windows.py +184 -0
- gdsdiff-0.1.0.dist-info/METADATA +135 -0
- gdsdiff-0.1.0.dist-info/RECORD +85 -0
- gdsdiff-0.1.0.dist-info/WHEEL +5 -0
- gdsdiff-0.1.0.dist-info/entry_points.txt +4 -0
- gdsdiff-0.1.0.dist-info/licenses/LICENSE +674 -0
- gdsdiff-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
#include <algorithm>
|
|
2
|
+
#include <cstdint>
|
|
3
|
+
#include <cstdlib>
|
|
4
|
+
#include <cstring>
|
|
5
|
+
#include <limits>
|
|
6
|
+
#include <map>
|
|
7
|
+
#include <numeric>
|
|
8
|
+
#include <set>
|
|
9
|
+
#include <stdexcept>
|
|
10
|
+
#include <string>
|
|
11
|
+
#include <tuple>
|
|
12
|
+
#include <vector>
|
|
13
|
+
|
|
14
|
+
namespace {
|
|
15
|
+
|
|
16
|
+
thread_local std::string g_last_error;
|
|
17
|
+
|
|
18
|
+
struct Point {
|
|
19
|
+
std::int64_t x = 0;
|
|
20
|
+
std::int64_t y = 0;
|
|
21
|
+
|
|
22
|
+
bool operator<(const Point& other) const {
|
|
23
|
+
return std::tie(x, y) < std::tie(other.x, other.y);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
bool operator==(const Point& other) const {
|
|
27
|
+
return x == other.x && y == other.y;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
struct Edge {
|
|
32
|
+
Point start;
|
|
33
|
+
Point stop;
|
|
34
|
+
|
|
35
|
+
bool operator<(const Edge& other) const {
|
|
36
|
+
return std::tie(start.x, start.y, stop.x, stop.y)
|
|
37
|
+
< std::tie(other.start.x, other.start.y, other.stop.x, other.stop.y);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
struct LineKey {
|
|
42
|
+
std::int64_t ux = 0;
|
|
43
|
+
std::int64_t uy = 0;
|
|
44
|
+
__int128 c = 0;
|
|
45
|
+
|
|
46
|
+
bool operator<(const LineKey& other) const {
|
|
47
|
+
if (ux != other.ux) return ux < other.ux;
|
|
48
|
+
if (uy != other.uy) return uy < other.uy;
|
|
49
|
+
return c < other.c;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
struct Fragment {
|
|
54
|
+
std::vector<Point> points;
|
|
55
|
+
std::int64_t bbox[4] = {0, 0, 0, 0};
|
|
56
|
+
std::int64_t twice_area = 0;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
std::int64_t cross(Point a, Point b, Point c) {
|
|
60
|
+
return (b.x - a.x) * (c.y - b.y) - (b.y - a.y) * (c.x - b.x);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
bool collinear(Point a, Point b, Point c) {
|
|
64
|
+
return cross(a, b, c) == 0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
std::int64_t abs_i64(std::int64_t value) {
|
|
68
|
+
return value < 0 ? -value : value;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
LineKey line_key(const Edge& edge) {
|
|
72
|
+
const std::int64_t dx = edge.stop.x - edge.start.x;
|
|
73
|
+
const std::int64_t dy = edge.stop.y - edge.start.y;
|
|
74
|
+
const auto scale = std::gcd(abs_i64(dx), abs_i64(dy));
|
|
75
|
+
if (scale == 0) {
|
|
76
|
+
throw std::runtime_error("zero-length segment has no line key");
|
|
77
|
+
}
|
|
78
|
+
std::int64_t ux = dx / scale;
|
|
79
|
+
std::int64_t uy = dy / scale;
|
|
80
|
+
if (ux < 0 || (ux == 0 && uy < 0)) {
|
|
81
|
+
ux = -ux;
|
|
82
|
+
uy = -uy;
|
|
83
|
+
}
|
|
84
|
+
const __int128 c = -static_cast<__int128>(uy) * edge.start.x + static_cast<__int128>(ux) * edge.start.y;
|
|
85
|
+
return LineKey{ux, uy, c};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
__int128 projection(Point point, const LineKey& key) {
|
|
89
|
+
return static_cast<__int128>(point.x) * key.ux + static_cast<__int128>(point.y) * key.uy;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
std::vector<Edge> split_collinear_edges(const std::vector<Edge>& edges) {
|
|
93
|
+
std::map<LineKey, std::vector<Edge>> by_line;
|
|
94
|
+
std::map<LineKey, std::map<__int128, Point>> points_by_line;
|
|
95
|
+
for (const auto& edge : edges) {
|
|
96
|
+
if (edge.start == edge.stop) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const auto key = line_key(edge);
|
|
100
|
+
by_line[key].push_back(edge);
|
|
101
|
+
points_by_line[key][projection(edge.start, key)] = edge.start;
|
|
102
|
+
points_by_line[key][projection(edge.stop, key)] = edge.stop;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
std::vector<Edge> split_edges;
|
|
106
|
+
for (const auto& item : by_line) {
|
|
107
|
+
const auto& key = item.first;
|
|
108
|
+
const auto& line_edges = item.second;
|
|
109
|
+
std::vector<__int128> line_points;
|
|
110
|
+
line_points.reserve(points_by_line[key].size());
|
|
111
|
+
for (const auto& point_item : points_by_line[key]) {
|
|
112
|
+
line_points.push_back(point_item.first);
|
|
113
|
+
}
|
|
114
|
+
for (const auto& edge : line_edges) {
|
|
115
|
+
const auto start_t = projection(edge.start, key);
|
|
116
|
+
const auto end_t = projection(edge.stop, key);
|
|
117
|
+
const auto low = std::min(start_t, end_t);
|
|
118
|
+
const auto high = std::max(start_t, end_t);
|
|
119
|
+
std::vector<__int128> breakpoints;
|
|
120
|
+
for (const auto value : line_points) {
|
|
121
|
+
if (low <= value && value <= high) {
|
|
122
|
+
breakpoints.push_back(value);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (start_t > end_t) {
|
|
126
|
+
std::reverse(breakpoints.begin(), breakpoints.end());
|
|
127
|
+
}
|
|
128
|
+
for (std::size_t index = 0; index + 1 < breakpoints.size(); ++index) {
|
|
129
|
+
if (breakpoints[index] == breakpoints[index + 1]) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
split_edges.push_back(Edge{points_by_line[key][breakpoints[index]], points_by_line[key][breakpoints[index + 1]]});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
std::sort(split_edges.begin(), split_edges.end());
|
|
137
|
+
return split_edges;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
std::vector<Edge> boundary_edges_from_polygons(
|
|
141
|
+
const std::int64_t* vertices_xy,
|
|
142
|
+
const std::uint64_t* fragment_offsets,
|
|
143
|
+
std::uint64_t fragment_count
|
|
144
|
+
) {
|
|
145
|
+
std::vector<Edge> raw_edges;
|
|
146
|
+
for (std::uint64_t fragment_index = 0; fragment_index < fragment_count; ++fragment_index) {
|
|
147
|
+
const std::uint64_t start = fragment_offsets[fragment_index];
|
|
148
|
+
const std::uint64_t stop = fragment_offsets[fragment_index + 1];
|
|
149
|
+
if (stop <= start + 2) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
for (std::uint64_t vertex_index = start; vertex_index < stop; ++vertex_index) {
|
|
153
|
+
const std::uint64_t next_index = vertex_index + 1 == stop ? start : vertex_index + 1;
|
|
154
|
+
Edge edge{
|
|
155
|
+
Point{vertices_xy[vertex_index * 2], vertices_xy[vertex_index * 2 + 1]},
|
|
156
|
+
Point{vertices_xy[next_index * 2], vertices_xy[next_index * 2 + 1]},
|
|
157
|
+
};
|
|
158
|
+
if (!(edge.start == edge.stop)) {
|
|
159
|
+
raw_edges.push_back(edge);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
std::map<Edge, std::uint64_t> counts;
|
|
165
|
+
for (const auto& edge : split_collinear_edges(raw_edges)) {
|
|
166
|
+
const Edge reverse{edge.stop, edge.start};
|
|
167
|
+
auto found = counts.find(reverse);
|
|
168
|
+
if (found != counts.end() && found->second > 0) {
|
|
169
|
+
--found->second;
|
|
170
|
+
if (found->second == 0) {
|
|
171
|
+
counts.erase(found);
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
++counts[edge];
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
std::vector<Edge> boundary;
|
|
179
|
+
for (const auto& item : counts) {
|
|
180
|
+
for (std::uint64_t repeat = 0; repeat < item.second; ++repeat) {
|
|
181
|
+
boundary.push_back(item.first);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
std::sort(boundary.begin(), boundary.end());
|
|
185
|
+
return boundary;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
std::pair<int, __int128> turn_key(Point previous, Point current, Point next) {
|
|
189
|
+
const __int128 in_x = static_cast<__int128>(current.x) - previous.x;
|
|
190
|
+
const __int128 in_y = static_cast<__int128>(current.y) - previous.y;
|
|
191
|
+
const __int128 out_x = static_cast<__int128>(next.x) - current.x;
|
|
192
|
+
const __int128 out_y = static_cast<__int128>(next.y) - current.y;
|
|
193
|
+
const __int128 cross_value = in_x * out_y - in_y * out_x;
|
|
194
|
+
const __int128 dot = in_x * out_x + in_y * out_y;
|
|
195
|
+
int turn_class = 3;
|
|
196
|
+
if (cross_value < 0) {
|
|
197
|
+
turn_class = 0;
|
|
198
|
+
} else if (cross_value == 0 && dot > 0) {
|
|
199
|
+
turn_class = 1;
|
|
200
|
+
} else if (cross_value > 0) {
|
|
201
|
+
turn_class = 2;
|
|
202
|
+
}
|
|
203
|
+
const __int128 abs_cross = cross_value < 0 ? -cross_value : cross_value;
|
|
204
|
+
return {turn_class, -abs_cross * 2 - dot};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
std::vector<std::vector<Point>> trace_loops(const std::vector<Edge>& edges) {
|
|
208
|
+
std::map<Point, std::vector<Point>> outgoing;
|
|
209
|
+
std::set<Edge> remaining;
|
|
210
|
+
for (const auto& edge : edges) {
|
|
211
|
+
outgoing[edge.start].push_back(edge.stop);
|
|
212
|
+
remaining.insert(edge);
|
|
213
|
+
}
|
|
214
|
+
for (auto& item : outgoing) {
|
|
215
|
+
std::sort(item.second.begin(), item.second.end());
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
std::vector<std::vector<Point>> loops;
|
|
219
|
+
while (!remaining.empty()) {
|
|
220
|
+
const auto seed = *remaining.begin();
|
|
221
|
+
remaining.erase(seed);
|
|
222
|
+
std::vector<Point> loop{seed.start, seed.stop};
|
|
223
|
+
Point previous = seed.start;
|
|
224
|
+
Point current = seed.stop;
|
|
225
|
+
while (!(current == seed.start)) {
|
|
226
|
+
const auto found = outgoing.find(current);
|
|
227
|
+
if (found == outgoing.end()) {
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
Point best{};
|
|
231
|
+
bool have_best = false;
|
|
232
|
+
for (const auto& candidate : found->second) {
|
|
233
|
+
Edge next_edge{current, candidate};
|
|
234
|
+
if (remaining.find(next_edge) == remaining.end()) {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
if (!have_best) {
|
|
238
|
+
best = candidate;
|
|
239
|
+
have_best = true;
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
const auto left_key = turn_key(previous, current, candidate);
|
|
243
|
+
const auto right_key = turn_key(previous, current, best);
|
|
244
|
+
if (left_key < right_key || (left_key == right_key && candidate < best)) {
|
|
245
|
+
best = candidate;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (!have_best) {
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
remaining.erase(Edge{current, best});
|
|
252
|
+
loop.push_back(best);
|
|
253
|
+
previous = current;
|
|
254
|
+
current = best;
|
|
255
|
+
}
|
|
256
|
+
if (!loop.empty() && loop.back() == seed.start) {
|
|
257
|
+
loop.pop_back();
|
|
258
|
+
}
|
|
259
|
+
loops.push_back(std::move(loop));
|
|
260
|
+
}
|
|
261
|
+
std::sort(loops.begin(), loops.end());
|
|
262
|
+
return loops;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
std::vector<Point> clean_loop(const std::vector<Point>& input) {
|
|
266
|
+
std::vector<Point> cleaned;
|
|
267
|
+
cleaned.reserve(input.size());
|
|
268
|
+
for (const auto& point : input) {
|
|
269
|
+
if (cleaned.empty() || !(cleaned.back() == point)) {
|
|
270
|
+
cleaned.push_back(point);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (cleaned.size() > 1 && cleaned.front() == cleaned.back()) {
|
|
274
|
+
cleaned.pop_back();
|
|
275
|
+
}
|
|
276
|
+
bool changed = true;
|
|
277
|
+
while (changed && cleaned.size() >= 3) {
|
|
278
|
+
changed = false;
|
|
279
|
+
std::vector<Point> next;
|
|
280
|
+
next.reserve(cleaned.size());
|
|
281
|
+
for (std::size_t index = 0; index < cleaned.size(); ++index) {
|
|
282
|
+
const auto& previous = cleaned[(index + cleaned.size() - 1) % cleaned.size()];
|
|
283
|
+
const auto& point = cleaned[index];
|
|
284
|
+
const auto& following = cleaned[(index + 1) % cleaned.size()];
|
|
285
|
+
if (collinear(previous, point, following)) {
|
|
286
|
+
changed = true;
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
next.push_back(point);
|
|
290
|
+
}
|
|
291
|
+
cleaned = std::move(next);
|
|
292
|
+
}
|
|
293
|
+
return cleaned;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
std::int64_t twice_area(const std::vector<Point>& points) {
|
|
297
|
+
__int128 area = 0;
|
|
298
|
+
for (std::size_t index = 0; index < points.size(); ++index) {
|
|
299
|
+
const auto& point = points[index];
|
|
300
|
+
const auto& next = points[(index + 1) % points.size()];
|
|
301
|
+
area += static_cast<__int128>(point.x) * next.y - static_cast<__int128>(next.x) * point.y;
|
|
302
|
+
}
|
|
303
|
+
if (area > std::numeric_limits<std::int64_t>::max() || area < std::numeric_limits<std::int64_t>::min()) {
|
|
304
|
+
throw std::runtime_error("boundary loop area overflow");
|
|
305
|
+
}
|
|
306
|
+
if (area < 0) {
|
|
307
|
+
area = -area;
|
|
308
|
+
}
|
|
309
|
+
return static_cast<std::int64_t>(area);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
void fill_bbox(Fragment& fragment) {
|
|
313
|
+
fragment.bbox[0] = fragment.points[0].x;
|
|
314
|
+
fragment.bbox[1] = fragment.points[0].y;
|
|
315
|
+
fragment.bbox[2] = fragment.points[0].x;
|
|
316
|
+
fragment.bbox[3] = fragment.points[0].y;
|
|
317
|
+
for (const auto& point : fragment.points) {
|
|
318
|
+
fragment.bbox[0] = std::min(fragment.bbox[0], point.x);
|
|
319
|
+
fragment.bbox[1] = std::min(fragment.bbox[1], point.y);
|
|
320
|
+
fragment.bbox[2] = std::max(fragment.bbox[2], point.x);
|
|
321
|
+
fragment.bbox[3] = std::max(fragment.bbox[3], point.y);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
std::vector<Fragment> fragments_from_edges(const std::int64_t* raw_edges, std::uint64_t edge_count, std::uint64_t* dropped) {
|
|
326
|
+
std::vector<Edge> edges;
|
|
327
|
+
edges.reserve(static_cast<std::size_t>(edge_count));
|
|
328
|
+
for (std::uint64_t index = 0; index < edge_count; ++index) {
|
|
329
|
+
const auto base = index * 4;
|
|
330
|
+
Edge edge{Point{raw_edges[base], raw_edges[base + 1]}, Point{raw_edges[base + 2], raw_edges[base + 3]}};
|
|
331
|
+
if (!(edge.start == edge.stop)) {
|
|
332
|
+
edges.push_back(edge);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
auto loops = trace_loops(edges);
|
|
336
|
+
std::vector<Fragment> fragments;
|
|
337
|
+
for (const auto& loop : loops) {
|
|
338
|
+
auto points = clean_loop(loop);
|
|
339
|
+
if (points.size() < 3) {
|
|
340
|
+
++(*dropped);
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
auto area = twice_area(points);
|
|
344
|
+
if (area == 0) {
|
|
345
|
+
++(*dropped);
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
Fragment fragment;
|
|
349
|
+
fragment.points = std::move(points);
|
|
350
|
+
fragment.twice_area = area;
|
|
351
|
+
fill_bbox(fragment);
|
|
352
|
+
fragments.push_back(std::move(fragment));
|
|
353
|
+
}
|
|
354
|
+
std::sort(fragments.begin(), fragments.end(), [](const Fragment& left, const Fragment& right) {
|
|
355
|
+
return std::tie(left.bbox[0], left.bbox[1], left.bbox[2], left.bbox[3], left.twice_area, left.points)
|
|
356
|
+
< std::tie(right.bbox[0], right.bbox[1], right.bbox[2], right.bbox[3], right.twice_area, right.points);
|
|
357
|
+
});
|
|
358
|
+
return fragments;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
} // namespace
|
|
362
|
+
|
|
363
|
+
extern "C" {
|
|
364
|
+
|
|
365
|
+
struct GdsBoundaryEdgesResult {
|
|
366
|
+
std::uint64_t edge_count;
|
|
367
|
+
std::int64_t* edges;
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
struct GdsBoundaryLoopResult {
|
|
371
|
+
std::uint64_t fragment_count;
|
|
372
|
+
std::uint64_t vertex_count;
|
|
373
|
+
std::int64_t* vertices_xy;
|
|
374
|
+
std::uint64_t* fragment_offsets;
|
|
375
|
+
std::int64_t* bboxes;
|
|
376
|
+
std::int64_t* twice_areas;
|
|
377
|
+
std::uint64_t dropped_zero_area_loop_count;
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
const char* gds_boundary_loops_last_error() {
|
|
381
|
+
return g_last_error.c_str();
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
void gds_boundary_edges_free(GdsBoundaryEdgesResult* result) {
|
|
385
|
+
if (!result) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
std::free(result->edges);
|
|
389
|
+
std::memset(result, 0, sizeof(GdsBoundaryEdgesResult));
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
void gds_boundary_loops_free(GdsBoundaryLoopResult* result) {
|
|
393
|
+
if (!result) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
std::free(result->vertices_xy);
|
|
397
|
+
std::free(result->fragment_offsets);
|
|
398
|
+
std::free(result->bboxes);
|
|
399
|
+
std::free(result->twice_areas);
|
|
400
|
+
std::memset(result, 0, sizeof(GdsBoundaryLoopResult));
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
int gds_boundary_edges_from_polygons(
|
|
404
|
+
const std::int64_t* vertices_xy,
|
|
405
|
+
const std::uint64_t* fragment_offsets,
|
|
406
|
+
std::uint64_t fragment_count,
|
|
407
|
+
GdsBoundaryEdgesResult* out
|
|
408
|
+
) {
|
|
409
|
+
try {
|
|
410
|
+
if (!out) {
|
|
411
|
+
throw std::invalid_argument("boundary edge output pointer is null");
|
|
412
|
+
}
|
|
413
|
+
std::memset(out, 0, sizeof(GdsBoundaryEdgesResult));
|
|
414
|
+
if (fragment_count && (!vertices_xy || !fragment_offsets)) {
|
|
415
|
+
throw std::invalid_argument("boundary edge polygon input pointer is null");
|
|
416
|
+
}
|
|
417
|
+
if (!fragment_count) {
|
|
418
|
+
return 0;
|
|
419
|
+
}
|
|
420
|
+
const auto edges = boundary_edges_from_polygons(vertices_xy, fragment_offsets, fragment_count);
|
|
421
|
+
out->edge_count = static_cast<std::uint64_t>(edges.size());
|
|
422
|
+
out->edges = static_cast<std::int64_t*>(std::calloc(edges.size() * 4, sizeof(std::int64_t)));
|
|
423
|
+
if (edges.size() && !out->edges) {
|
|
424
|
+
throw std::runtime_error("boundary edge allocation failed");
|
|
425
|
+
}
|
|
426
|
+
for (std::size_t index = 0; index < edges.size(); ++index) {
|
|
427
|
+
const auto& edge = edges[index];
|
|
428
|
+
out->edges[index * 4] = edge.start.x;
|
|
429
|
+
out->edges[index * 4 + 1] = edge.start.y;
|
|
430
|
+
out->edges[index * 4 + 2] = edge.stop.x;
|
|
431
|
+
out->edges[index * 4 + 3] = edge.stop.y;
|
|
432
|
+
}
|
|
433
|
+
return 0;
|
|
434
|
+
} catch (const std::exception& exc) {
|
|
435
|
+
g_last_error = exc.what();
|
|
436
|
+
if (out) {
|
|
437
|
+
gds_boundary_edges_free(out);
|
|
438
|
+
}
|
|
439
|
+
return 1;
|
|
440
|
+
} catch (...) {
|
|
441
|
+
g_last_error = "unknown boundary edge error";
|
|
442
|
+
if (out) {
|
|
443
|
+
gds_boundary_edges_free(out);
|
|
444
|
+
}
|
|
445
|
+
return 1;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
int gds_boundary_loops_from_edges(const std::int64_t* edges, std::uint64_t edge_count, GdsBoundaryLoopResult* out) {
|
|
450
|
+
try {
|
|
451
|
+
if (!out) {
|
|
452
|
+
throw std::invalid_argument("boundary loop output pointer is null");
|
|
453
|
+
}
|
|
454
|
+
std::memset(out, 0, sizeof(GdsBoundaryLoopResult));
|
|
455
|
+
if (edge_count && !edges) {
|
|
456
|
+
throw std::invalid_argument("boundary loop edge pointer is null");
|
|
457
|
+
}
|
|
458
|
+
std::uint64_t dropped = 0;
|
|
459
|
+
const auto fragments = fragments_from_edges(edges, edge_count, &dropped);
|
|
460
|
+
std::uint64_t vertex_count = 0;
|
|
461
|
+
for (const auto& fragment : fragments) {
|
|
462
|
+
vertex_count += static_cast<std::uint64_t>(fragment.points.size());
|
|
463
|
+
}
|
|
464
|
+
out->fragment_count = static_cast<std::uint64_t>(fragments.size());
|
|
465
|
+
out->vertex_count = vertex_count;
|
|
466
|
+
out->dropped_zero_area_loop_count = dropped;
|
|
467
|
+
out->fragment_offsets = static_cast<std::uint64_t*>(std::calloc(fragments.size() + 1, sizeof(std::uint64_t)));
|
|
468
|
+
out->vertices_xy = static_cast<std::int64_t*>(std::calloc(vertex_count * 2, sizeof(std::int64_t)));
|
|
469
|
+
out->bboxes = static_cast<std::int64_t*>(std::calloc(fragments.size() * 4, sizeof(std::int64_t)));
|
|
470
|
+
out->twice_areas = static_cast<std::int64_t*>(std::calloc(fragments.size(), sizeof(std::int64_t)));
|
|
471
|
+
if (!out->fragment_offsets || (vertex_count && !out->vertices_xy) || (fragments.size() && (!out->bboxes || !out->twice_areas))) {
|
|
472
|
+
throw std::runtime_error("boundary loop allocation failed");
|
|
473
|
+
}
|
|
474
|
+
std::uint64_t vertex_offset = 0;
|
|
475
|
+
for (std::size_t index = 0; index < fragments.size(); ++index) {
|
|
476
|
+
const auto& fragment = fragments[index];
|
|
477
|
+
out->fragment_offsets[index] = vertex_offset;
|
|
478
|
+
for (const auto& point : fragment.points) {
|
|
479
|
+
out->vertices_xy[vertex_offset * 2] = point.x;
|
|
480
|
+
out->vertices_xy[vertex_offset * 2 + 1] = point.y;
|
|
481
|
+
++vertex_offset;
|
|
482
|
+
}
|
|
483
|
+
for (int axis = 0; axis < 4; ++axis) {
|
|
484
|
+
out->bboxes[index * 4 + axis] = fragment.bbox[axis];
|
|
485
|
+
}
|
|
486
|
+
out->twice_areas[index] = fragment.twice_area;
|
|
487
|
+
}
|
|
488
|
+
out->fragment_offsets[fragments.size()] = vertex_offset;
|
|
489
|
+
return 0;
|
|
490
|
+
} catch (const std::exception& exc) {
|
|
491
|
+
g_last_error = exc.what();
|
|
492
|
+
if (out) {
|
|
493
|
+
gds_boundary_loops_free(out);
|
|
494
|
+
}
|
|
495
|
+
return 1;
|
|
496
|
+
} catch (...) {
|
|
497
|
+
g_last_error = "unknown boundary loop error";
|
|
498
|
+
if (out) {
|
|
499
|
+
gds_boundary_loops_free(out);
|
|
500
|
+
}
|
|
501
|
+
return 1;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
}
|