PartSegCore-compiled-backend 0.15.10__tar.gz → 0.15.12__tar.gz
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.
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/.pre-commit-config.yaml +2 -2
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/PKG-INFO +30 -5
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/Readme.md +25 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/multiscale_opening/mso.h +1 -1
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/triangulation/intersection.hpp +23 -15
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/triangulation/point.hpp +13 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/triangulation/triangulate.hpp +67 -11
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/version.py +9 -4
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/tests/test_triangulate.py +53 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/.gitignore +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/CMakeLists.txt +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/LICENSE.txt +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/MANIFEST.in +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/docs/Makefile +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/docs/calc_bounds.rst +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/docs/conf.py +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/docs/fast_unique.rst +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/docs/index.rst +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/docs/make.bat +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/docs/napari_mapping.rst +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/docs/sprawl_utils.rst +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/pyproject.toml +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/__init__.py +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/_fast_unique.pyi +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/_fast_unique.pyx +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/_napari_mapping.pyx +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/calc_bounds.pyi +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/calc_bounds.pyx +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/color_image_cython.pyi +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/color_image_cython.pyx +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/fast_unique.py +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/multiscale_opening/__init__.py +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/multiscale_opening/mso_bind.pyi +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/multiscale_opening/mso_bind.pyx +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/multiscale_opening/my_queue.h +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/napari_mapping.py +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/sprawl_utils/__init__.py +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/sprawl_utils/distance_utils.pxd +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/sprawl_utils/euclidean_cython.pyi +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/sprawl_utils/euclidean_cython.pyx +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/sprawl_utils/find_split.py +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/sprawl_utils/find_split.pyi +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/sprawl_utils/fuzzy_distance.pyx +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/sprawl_utils/global_consts.h +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/sprawl_utils/my_queue.h +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/sprawl_utils/path_sprawl_cython.pyi +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/sprawl_utils/path_sprawl_cython.pyx +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/sprawl_utils/put_borders_in_queue.pyx +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/sprawl_utils/sprawl_utils.pyi +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/sprawl_utils/sprawl_utils.pyx +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/triangulate.pyx +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/triangulation/debug_util.hpp +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/PartSegCore_compiled_backend/utils.py +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/tests/test_calc_bounds.py +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/tests/test_coloring.py +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/tests/test_fast_unique.py +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/tests/test_napari_mapping.py +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/tests/test_sprawl_utils/__init__.py +0 -0
- {partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/src/tests/test_sprawl_utils/test_euclidean.py +0 -0
|
@@ -4,7 +4,7 @@ default_language_version:
|
|
|
4
4
|
repos:
|
|
5
5
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
6
6
|
# Ruff version.
|
|
7
|
-
rev: v0.9.
|
|
7
|
+
rev: v0.9.10
|
|
8
8
|
hooks:
|
|
9
9
|
# Run the linter.
|
|
10
10
|
- id: ruff
|
|
@@ -30,7 +30,7 @@ repos:
|
|
|
30
30
|
hooks:
|
|
31
31
|
- id: markdownlint
|
|
32
32
|
- repo: https://github.com/codespell-project/codespell
|
|
33
|
-
rev: v2.
|
|
33
|
+
rev: v2.4.1
|
|
34
34
|
hooks:
|
|
35
35
|
- id: codespell
|
|
36
36
|
additional_dependencies:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: PartSegCore_compiled_backend
|
|
3
|
-
Version: 0.15.
|
|
3
|
+
Version: 0.15.12
|
|
4
4
|
Summary: PartSegCore compiled backend,
|
|
5
5
|
Keywords: bioimaging
|
|
6
6
|
Author-Email: Grzegorz Bokota <g.bokota@cent.uw.edu.pl>
|
|
@@ -18,13 +18,13 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
|
18
18
|
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
19
19
|
Project-URL: Homepage, https://4dnucleome.cent.uw.edu.pl/PartSeg/
|
|
20
20
|
Project-URL: Documentation, https://partsegcore-compiled-backend.readthedocs.io/en/stable/
|
|
21
|
-
Project-URL: Source
|
|
21
|
+
Project-URL: Source Code, https://github.com/4DNucleome/PartSegCore-compiled-backend
|
|
22
22
|
Requires-Python: >=3.9
|
|
23
23
|
Requires-Dist: numpy>=1.23.5
|
|
24
|
-
Requires-Dist: pytest>=7.0.0; extra == "test"
|
|
25
|
-
Requires-Dist: sphinx>=5.0.0; extra == "docs"
|
|
26
24
|
Provides-Extra: test
|
|
25
|
+
Requires-Dist: pytest>=7.0.0; extra == "test"
|
|
27
26
|
Provides-Extra: docs
|
|
27
|
+
Requires-Dist: sphinx>=5.0.0; extra == "docs"
|
|
28
28
|
Description-Content-Type: text/markdown
|
|
29
29
|
|
|
30
30
|
# PartSegCore_compiled_backend
|
|
@@ -61,6 +61,31 @@ pip install .
|
|
|
61
61
|
As currently there is no wheel for macOS ARM64
|
|
62
62
|
it is required to build package from source.
|
|
63
63
|
|
|
64
|
+
## Development in Clion
|
|
65
|
+
|
|
66
|
+
To install Python build dependencies, run:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pip install scikit-build-core cython cython-cmake numpy setuptools-scm
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Then, install the package in development mode:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pip install --no-build-isolation --config-settings=editable.rebuild=true\
|
|
76
|
+
-Cbuild-dir=build -ve .
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
vendor `FindCython.cmake` from cython-cmake to `cmake` directory.
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
mkdir cmake && cython-cmake vendor cmake
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Set `CUSTOM_MODULE_PATH` environment variable to full path to cmake directory.
|
|
86
|
+
|
|
87
|
+
Set it in Settings → Build, Execution, Deployment → CMake → Environment
|
|
88
|
+
|
|
64
89
|
## Cite as
|
|
65
90
|
|
|
66
91
|
Bokota, G., Sroka, J., Basu, S. et al. PartSeg: a tool for quantitative feature extraction
|
|
@@ -32,6 +32,31 @@ pip install .
|
|
|
32
32
|
As currently there is no wheel for macOS ARM64
|
|
33
33
|
it is required to build package from source.
|
|
34
34
|
|
|
35
|
+
## Development in Clion
|
|
36
|
+
|
|
37
|
+
To install Python build dependencies, run:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install scikit-build-core cython cython-cmake numpy setuptools-scm
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Then, install the package in development mode:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install --no-build-isolation --config-settings=editable.rebuild=true\
|
|
47
|
+
-Cbuild-dir=build -ve .
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
vendor `FindCython.cmake` from cython-cmake to `cmake` directory.
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
mkdir cmake && cython-cmake vendor cmake
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Set `CUSTOM_MODULE_PATH` environment variable to full path to cmake directory.
|
|
57
|
+
|
|
58
|
+
Set it in Settings → Build, Execution, Deployment → CMake → Environment
|
|
59
|
+
|
|
35
60
|
## Cite as
|
|
36
61
|
|
|
37
62
|
Bokota, G., Sroka, J., Basu, S. et al. PartSeg: a tool for quantitative feature extraction
|
|
@@ -580,7 +580,7 @@ class MSO {
|
|
|
580
580
|
queue_copy = queue;
|
|
581
581
|
// std::cerr << "# Queue1 " << queue.get_size() << " Queue2 " <<
|
|
582
582
|
// queue_copy.get_size() << std::endl;
|
|
583
|
-
// Calculate area which can be reached by monoticall path (
|
|
583
|
+
// Calculate area which can be reached by monoticall path (first part of
|
|
584
584
|
// Morphological neighborhood)
|
|
585
585
|
while (!queue.empty()) {
|
|
586
586
|
coord = queue.front();
|
|
@@ -112,6 +112,12 @@ inline int64_t double_as_hex(double d) {
|
|
|
112
112
|
return result;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
enum Orientation {
|
|
116
|
+
COLLINEAR = 0,
|
|
117
|
+
CLOCKWISE = 1,
|
|
118
|
+
COUNTERCLOCKWISE = 2,
|
|
119
|
+
};
|
|
120
|
+
|
|
115
121
|
/**
|
|
116
122
|
* Determines the orientation of the triplet (p, q, r).
|
|
117
123
|
*
|
|
@@ -119,12 +125,14 @@ inline int64_t double_as_hex(double d) {
|
|
|
119
125
|
* @param q The second point.
|
|
120
126
|
* @param r The third point.
|
|
121
127
|
*
|
|
122
|
-
* @return
|
|
123
|
-
*
|
|
124
|
-
*
|
|
128
|
+
* @return Orientation::COLLINEAR if p, q and r are collinear.
|
|
129
|
+
* Orientation::CLOCKWISE if the triplet (p, q, r) is in a clockwise
|
|
130
|
+
* orientation.
|
|
131
|
+
* Orientation::COUNTERCLOCKWISE if the triplet (p, q, r) is in a
|
|
132
|
+
* counterclockwise orientation.
|
|
125
133
|
*/
|
|
126
|
-
inline
|
|
127
|
-
|
|
134
|
+
inline Orientation _orientation(const point::Point &p, const point::Point &q,
|
|
135
|
+
const point::Point &r) {
|
|
128
136
|
double val1 = ((q.y - p.y) * (r.x - q.x));
|
|
129
137
|
double val2 = ((r.y - q.y) * (q.x - p.x));
|
|
130
138
|
// This commented code if for debugging purposes of differences between macOS
|
|
@@ -142,8 +150,8 @@ inline int _orientation(const point::Point &p, const point::Point &q,
|
|
|
142
150
|
// }
|
|
143
151
|
// Instead of using classical equation, we need to use two variables
|
|
144
152
|
// to handle problem with strange behavior on macOS.
|
|
145
|
-
if (val1 == val2) return
|
|
146
|
-
return (val1 > val2) ?
|
|
153
|
+
if (val1 == val2) return Orientation::COLLINEAR;
|
|
154
|
+
return (val1 > val2) ? Orientation::CLOCKWISE : Orientation::COUNTERCLOCKWISE;
|
|
147
155
|
}
|
|
148
156
|
|
|
149
157
|
/**
|
|
@@ -163,17 +171,17 @@ inline bool _do_intersect(const point::Segment &s1, const point::Segment &s2) {
|
|
|
163
171
|
const point::Point &p2 = s2.bottom;
|
|
164
172
|
const point::Point &q2 = s2.top;
|
|
165
173
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
174
|
+
Orientation o1 = _orientation(p1, q1, p2);
|
|
175
|
+
Orientation o2 = _orientation(p1, q1, q2);
|
|
176
|
+
Orientation o3 = _orientation(p2, q2, p1);
|
|
177
|
+
Orientation o4 = _orientation(p2, q2, q1);
|
|
170
178
|
|
|
171
179
|
if (o1 != o2 && o3 != o4) return true;
|
|
172
180
|
|
|
173
|
-
if (o1 ==
|
|
174
|
-
if (o2 ==
|
|
175
|
-
if (o3 ==
|
|
176
|
-
if (o4 ==
|
|
181
|
+
if (o1 == Orientation::COLLINEAR && _on_segment(p1, p2, q1)) return true;
|
|
182
|
+
if (o2 == Orientation::COLLINEAR && _on_segment(p1, q2, q1)) return true;
|
|
183
|
+
if (o3 == Orientation::COLLINEAR && _on_segment(p2, p1, q2)) return true;
|
|
184
|
+
if (o4 == Orientation::COLLINEAR && _on_segment(p2, q1, q2)) return true;
|
|
177
185
|
|
|
178
186
|
return false;
|
|
179
187
|
}
|
|
@@ -208,6 +208,19 @@ struct Segment {
|
|
|
208
208
|
}
|
|
209
209
|
};
|
|
210
210
|
};
|
|
211
|
+
Point centroid(const std::vector<Point> &point_list) {
|
|
212
|
+
if (point_list.empty()) {
|
|
213
|
+
return {0, 0};
|
|
214
|
+
}
|
|
215
|
+
Point res(0, 0);
|
|
216
|
+
for (auto &point : point_list) {
|
|
217
|
+
res.x += point.x;
|
|
218
|
+
res.y += point.y;
|
|
219
|
+
}
|
|
220
|
+
res.x /= float(point_list.size());
|
|
221
|
+
res.y /= float(point_list.size());
|
|
222
|
+
return res;
|
|
223
|
+
}
|
|
211
224
|
} // namespace partsegcore::point
|
|
212
225
|
|
|
213
226
|
// overload of hash function for
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
#define PARTSEGCORE_TRIANGULATE_H
|
|
3
3
|
|
|
4
4
|
#include <algorithm>
|
|
5
|
+
#include <cmath>
|
|
5
6
|
#include <map>
|
|
6
7
|
#include <memory> // memory header is required on linux, and not on macos
|
|
7
8
|
#include <set>
|
|
@@ -602,6 +603,36 @@ struct MonotonePolygonBuilder {
|
|
|
602
603
|
};
|
|
603
604
|
};
|
|
604
605
|
|
|
606
|
+
/*
|
|
607
|
+
* Check if the polygon, that all angles have the same orientation
|
|
608
|
+
* do not have self-intersections
|
|
609
|
+
*
|
|
610
|
+
* @param begin Iterator to the first point of the polygon
|
|
611
|
+
* @param end Iterator to the end of the polygon
|
|
612
|
+
* @param centroid Centroid of the polygon
|
|
613
|
+
*
|
|
614
|
+
* @return True if the polygon is simple, false otherwise
|
|
615
|
+
*/
|
|
616
|
+
template <typename Iterator>
|
|
617
|
+
bool is_simple_polygon(Iterator begin, Iterator end, point::Point centroid) {
|
|
618
|
+
double start_angle = std::atan2(begin->y - centroid.y, begin->x - centroid.x);
|
|
619
|
+
double prev_angle = 0;
|
|
620
|
+
begin++;
|
|
621
|
+
for (; begin != end; begin++) {
|
|
622
|
+
double angle =
|
|
623
|
+
std::atan2(begin->y - centroid.y, begin->x - centroid.x) - start_angle;
|
|
624
|
+
if (angle < 0) {
|
|
625
|
+
angle += 2 * M_PI;
|
|
626
|
+
}
|
|
627
|
+
if (angle < prev_angle) {
|
|
628
|
+
return false;
|
|
629
|
+
} else {
|
|
630
|
+
prev_angle = angle;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
return true;
|
|
634
|
+
}
|
|
635
|
+
|
|
605
636
|
/**
|
|
606
637
|
* Checks if a given polygon is convex.
|
|
607
638
|
*
|
|
@@ -615,26 +646,47 @@ struct MonotonePolygonBuilder {
|
|
|
615
646
|
* @return True if the polygon is convex, false otherwise.
|
|
616
647
|
*/
|
|
617
648
|
inline bool _is_convex(const std::vector<point::Point> &polygon) {
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
649
|
+
if (polygon.size() < 3) return false;
|
|
650
|
+
if (polygon.size() == 3) return true;
|
|
651
|
+
intersection::Orientation orientation = intersection::Orientation::COLLINEAR;
|
|
652
|
+
intersection::Orientation triangle_orientation;
|
|
653
|
+
size_t idx = 0;
|
|
654
|
+
for (; idx < polygon.size() - 2; idx++) {
|
|
655
|
+
triangle_orientation = intersection::_orientation(
|
|
656
|
+
polygon[idx], polygon[(idx + 1)], polygon[(idx + 2)]);
|
|
657
|
+
if (triangle_orientation != intersection::Orientation::COLLINEAR) {
|
|
625
658
|
orientation = triangle_orientation;
|
|
626
|
-
|
|
659
|
+
break;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
if (orientation == intersection::Orientation::COLLINEAR) {
|
|
663
|
+
return false;
|
|
664
|
+
}
|
|
665
|
+
for (; idx < polygon.size() - 2; idx++) {
|
|
666
|
+
triangle_orientation = intersection::_orientation(
|
|
667
|
+
polygon[idx], polygon[(idx + 1)], polygon[(idx + 2)]);
|
|
668
|
+
if (triangle_orientation != 0 && triangle_orientation != orientation) {
|
|
627
669
|
return false;
|
|
670
|
+
}
|
|
628
671
|
}
|
|
629
672
|
triangle_orientation = intersection::_orientation(
|
|
630
673
|
polygon[polygon.size() - 2], polygon[polygon.size() - 1], polygon[0]);
|
|
631
|
-
if (triangle_orientation != 0 && triangle_orientation != orientation)
|
|
674
|
+
if (triangle_orientation != 0 && triangle_orientation != orientation) {
|
|
632
675
|
return false;
|
|
676
|
+
}
|
|
633
677
|
triangle_orientation = intersection::_orientation(polygon[polygon.size() - 1],
|
|
634
678
|
polygon[0], polygon[1]);
|
|
635
|
-
if (triangle_orientation != 0 && triangle_orientation != orientation)
|
|
679
|
+
if (triangle_orientation != 0 && triangle_orientation != orientation) {
|
|
636
680
|
return false;
|
|
637
|
-
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
point::Point centroid = point::centroid(polygon);
|
|
684
|
+
|
|
685
|
+
if (orientation == intersection::Orientation::COUNTERCLOCKWISE) {
|
|
686
|
+
return is_simple_polygon(polygon.begin(), polygon.end(), centroid);
|
|
687
|
+
} else {
|
|
688
|
+
return is_simple_polygon(polygon.rbegin(), polygon.rend(), centroid);
|
|
689
|
+
}
|
|
638
690
|
}
|
|
639
691
|
|
|
640
692
|
/**
|
|
@@ -1320,6 +1372,9 @@ struct GraphNode {
|
|
|
1320
1372
|
* resulting sub-polygon contains unique edges. This operation can help to
|
|
1321
1373
|
* resolve ambiguities in complex or self-intersecting polygons.
|
|
1322
1374
|
*
|
|
1375
|
+
* Note: Polygons with exactly 2 points are treated as single line segments and
|
|
1376
|
+
* are not processed for edge deduplication between their first and last points.
|
|
1377
|
+
*
|
|
1323
1378
|
* @param polygon The input polygon represented as a list of edges.
|
|
1324
1379
|
*
|
|
1325
1380
|
* @return A vector of sub-polygons, where each sub-polygon is free of repeated
|
|
@@ -1327,6 +1382,7 @@ struct GraphNode {
|
|
|
1327
1382
|
*/
|
|
1328
1383
|
inline std::vector<std::vector<point::Point>> split_polygon_on_repeated_edges(
|
|
1329
1384
|
const std::vector<point::Point> &polygon) {
|
|
1385
|
+
if (polygon.size() < 3) return {polygon};
|
|
1330
1386
|
auto edges_dedup = calc_dedup_edges({polygon});
|
|
1331
1387
|
std::vector<std::vector<point::Point>> result;
|
|
1332
1388
|
point::Segment segment;
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
# file generated by
|
|
1
|
+
# file generated by setuptools-scm
|
|
2
2
|
# don't change, don't track in version control
|
|
3
|
+
|
|
4
|
+
__all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
|
|
5
|
+
|
|
3
6
|
TYPE_CHECKING = False
|
|
4
7
|
if TYPE_CHECKING:
|
|
5
|
-
from typing import Tuple
|
|
8
|
+
from typing import Tuple
|
|
9
|
+
from typing import Union
|
|
10
|
+
|
|
6
11
|
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
7
12
|
else:
|
|
8
13
|
VERSION_TUPLE = object
|
|
@@ -12,5 +17,5 @@ __version__: str
|
|
|
12
17
|
__version_tuple__: VERSION_TUPLE
|
|
13
18
|
version_tuple: VERSION_TUPLE
|
|
14
19
|
|
|
15
|
-
__version__ = version = '0.15.
|
|
16
|
-
__version_tuple__ = version_tuple = (0, 15,
|
|
20
|
+
__version__ = version = '0.15.12'
|
|
21
|
+
__version_tuple__ = version_tuple = (0, 15, 12)
|
|
@@ -208,6 +208,7 @@ def test_triangle_convex_polygon():
|
|
|
208
208
|
],
|
|
209
209
|
)
|
|
210
210
|
def test_triangulate_polygon_py_convex(polygon, expected):
|
|
211
|
+
assert is_convex(polygon)
|
|
211
212
|
assert triangulate_polygon_py(polygon)[0] == expected
|
|
212
213
|
|
|
213
214
|
|
|
@@ -691,3 +692,55 @@ def test_splitting_edges(split_edges, triangles):
|
|
|
691
692
|
)
|
|
692
693
|
triangles_ = triangulate_polygon_with_edge_numpy_li([polygon], split_edges=split_edges)[1][2]
|
|
693
694
|
assert len(triangles_) == triangles
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
def generate_regular_polygon(n: int, reverse: bool, radius: int = 1) -> np.ndarray:
|
|
698
|
+
angles = np.linspace(0, 2 * np.pi, n, endpoint=False)
|
|
699
|
+
if reverse:
|
|
700
|
+
angles = angles[::-1]
|
|
701
|
+
return np.column_stack((radius * np.cos(angles), radius * np.sin(angles)))
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
def generate_self_intersecting_polygon(n: int, reverse: bool, radius: int = 1) -> np.ndarray:
|
|
705
|
+
"""Generate self-intersecting polygon with n vertices
|
|
706
|
+
|
|
707
|
+
The polygon is generated by doubling the angle range of a regular polygon
|
|
708
|
+
"""
|
|
709
|
+
assert n % 2 == 1, 'an odd number is required to generate a self-intersecting polygon'
|
|
710
|
+
angles = np.linspace(0, 4 * np.pi, n, endpoint=False)
|
|
711
|
+
if reverse:
|
|
712
|
+
angles = angles[::-1]
|
|
713
|
+
return np.column_stack((radius * np.cos(angles), radius * np.sin(angles)))
|
|
714
|
+
|
|
715
|
+
|
|
716
|
+
def rotation_matrix(angle: float) -> np.ndarray:
|
|
717
|
+
"""Create a 2D rotation matrix for the given angle in degrees."""
|
|
718
|
+
return np.array(
|
|
719
|
+
[
|
|
720
|
+
[np.cos(np.radians(angle)), -np.sin(np.radians(angle))],
|
|
721
|
+
[np.sin(np.radians(angle)), np.cos(np.radians(angle))],
|
|
722
|
+
]
|
|
723
|
+
)
|
|
724
|
+
|
|
725
|
+
|
|
726
|
+
ANGLES = [0, 5, 75, 95, 355]
|
|
727
|
+
|
|
728
|
+
|
|
729
|
+
@pytest.mark.parametrize('angle', ANGLES, ids=str)
|
|
730
|
+
@pytest.mark.parametrize('n_vertex', [5, 7, 19])
|
|
731
|
+
@pytest.mark.parametrize('reverse', [False, True])
|
|
732
|
+
def test_is_convex_self_intersection(angle, n_vertex, reverse):
|
|
733
|
+
p = generate_self_intersecting_polygon(n_vertex, reverse)
|
|
734
|
+
rot = rotation_matrix(angle)
|
|
735
|
+
data = np.dot(p, rot)
|
|
736
|
+
assert not is_convex(data)
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
@pytest.mark.parametrize('angle', ANGLES, ids=str)
|
|
740
|
+
@pytest.mark.parametrize('n_vertex', [3, 4, 7, 12, 15, 20])
|
|
741
|
+
@pytest.mark.parametrize('reverse', [False, True])
|
|
742
|
+
def test_is_convex_regular_polygon(angle, n_vertex, reverse):
|
|
743
|
+
poly = generate_regular_polygon(n_vertex, reverse=reverse)
|
|
744
|
+
rot = rotation_matrix(angle)
|
|
745
|
+
rotated_poly = np.dot(poly, rot)
|
|
746
|
+
assert is_convex(rotated_poly)
|
|
File without changes
|
{partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/CMakeLists.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/docs/calc_bounds.rst
RENAMED
|
File without changes
|
|
File without changes
|
{partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/docs/fast_unique.rst
RENAMED
|
File without changes
|
{partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/docs/index.rst
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/docs/sprawl_utils.rst
RENAMED
|
File without changes
|
{partsegcore_compiled_backend-0.15.10 → partsegcore_compiled_backend-0.15.12}/pyproject.toml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|