offset-poly 0.3.3__tar.gz → 0.4.0__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.
- {offset_poly-0.3.3/src/offset_poly.egg-info → offset_poly-0.4.0}/PKG-INFO +134 -128
- {offset_poly-0.3.3 → offset_poly-0.4.0}/README.md +113 -113
- offset_poly-0.4.0/pyproject.toml +112 -0
- {offset_poly-0.3.3 → offset_poly-0.4.0}/src/offset_poly/__init__.py +21 -21
- {offset_poly-0.3.3 → offset_poly-0.4.0}/src/offset_poly/offset.py +178 -165
- {offset_poly-0.3.3 → offset_poly-0.4.0}/src/offset_poly/offset_corner.py +159 -159
- {offset_poly-0.3.3 → offset_poly-0.4.0}/src/offset_poly/prepare_poly.py +60 -60
- offset_poly-0.4.0/src/offset_poly/py.typed +5 -0
- offset_poly-0.3.3/.github/workflows/pypi-project.yml +0 -110
- offset_poly-0.3.3/.pre-commit-config.yaml +0 -144
- offset_poly-0.3.3/CHANGELOG.md +0 -41
- offset_poly-0.3.3/PKG-INFO +0 -128
- offset_poly-0.3.3/pyproject.toml +0 -53
- offset_poly-0.3.3/requirements.txt +0 -8
- offset_poly-0.3.3/setup.cfg +0 -4
- offset_poly-0.3.3/src/offset_poly/py.typed +0 -0
- offset_poly-0.3.3/src/offset_poly.egg-info/SOURCES.txt +0 -20
- offset_poly-0.3.3/src/offset_poly.egg-info/dependency_links.txt +0 -1
- offset_poly-0.3.3/src/offset_poly.egg-info/requires.txt +0 -7
- offset_poly-0.3.3/src/offset_poly.egg-info/top_level.txt +0 -1
- offset_poly-0.3.3/tests/__init__.py +0 -0
- offset_poly-0.3.3/tests/test_offset.py +0 -340
- offset_poly-0.3.3/tests/test_offset_corner.py +0 -93
- offset_poly-0.3.3/tox.ini +0 -6
|
@@ -1,128 +1,134 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
2
|
-
Name:
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: offset a polyline or polygon in 2d space
|
|
5
|
-
Author
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
:
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
:
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: offset-poly
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: offset a polyline or polygon in 2d space
|
|
5
|
+
Author: Shay Hill
|
|
6
|
+
Author-email: Shay Hill <shay_public@hotmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
Requires-Dist: vec2-math
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# offset_poly
|
|
23
|
+
|
|
24
|
+
Move a polyline or polygon (left) by a given offset.
|
|
25
|
+
|
|
26
|
+
A counterclockwise polygon will be offset to the inside. I clockwise polygon will be offset to the outside.
|
|
27
|
+
|
|
28
|
+
## offset_polygon
|
|
29
|
+
|
|
30
|
+
~~~python
|
|
31
|
+
def offset_polygon(polyline: Sequence[_Vec2], offset: float) -> list[_Vec2]:
|
|
32
|
+
"""Offset polygon edges (to the left) by a constant amount.
|
|
33
|
+
|
|
34
|
+
:param polyline: polyline
|
|
35
|
+
:param offset: distance to offset from each edge
|
|
36
|
+
:return: polygon offset by offset
|
|
37
|
+
"""
|
|
38
|
+
~~~
|
|
39
|
+
|
|
40
|
+
## offset_polyline
|
|
41
|
+
|
|
42
|
+
~~~python
|
|
43
|
+
def offset_polyline(polyline: Sequence[_Vec2], offset: float) -> list[_Vec2]:
|
|
44
|
+
"""Offset polygon edges (to the left) by a constant amount.
|
|
45
|
+
|
|
46
|
+
:param polyline: polyline
|
|
47
|
+
:param offset: distance to offset from each edge
|
|
48
|
+
:return: polyline offset by offset
|
|
49
|
+
"""
|
|
50
|
+
~~~
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
The difference between the two is that `offset_polygon` will close the polygon if it is not already closed, whereas `offset_polyline` will leave the polyline open even if the first and last points are identical.
|
|
54
|
+
|
|
55
|
+
This package is the simplest version of polyline offsetting, it does not anticipate or account for self intersections that may come up when offsetting a polyline.
|
|
56
|
+
|
|
57
|
+
This is not nearly as sophisticated as curve offsetting, but you can use this for control polygon offsetting, which will be nearly as good in some instances.
|
|
58
|
+
|
|
59
|
+
* multiple points (knots in your control points) are preserved.
|
|
60
|
+
* if input[0] == input[-1], output[0] will equal output[-1]
|
|
61
|
+
|
|
62
|
+
## You may see (nan, nan) in the result.
|
|
63
|
+
|
|
64
|
+
If you pass two adjacent, opposite, parallel edges, you will get a (nan, nan) in the result. With points A -> B -> A, for instance, there is no point that would be any given distance (except 0) left of both A B and B A.
|
|
65
|
+
|
|
66
|
+
## More complex functions
|
|
67
|
+
|
|
68
|
+
There are a few more complex functions,`offset_poly_per_vert` and `offset_poly_per_edge`
|
|
69
|
+
|
|
70
|
+
~~~python
|
|
71
|
+
def gap_corner(
|
|
72
|
+
pnt_a: _Vec, pnt_b: _Vec, pnt_c: _Vec, gap_1: float, gap_2: float | None = None
|
|
73
|
+
) -> GapCorner:
|
|
74
|
+
"""Offset a corner (to the left) defined by three points.
|
|
75
|
+
|
|
76
|
+
:param pnt_a: previous point
|
|
77
|
+
:param pnt_b: corner point
|
|
78
|
+
:param pnt_c: next point
|
|
79
|
+
:param gap_1: distance to offset from vec12
|
|
80
|
+
:param gap_2: optional distance to offset from vec23. If not given, gap_1 is used
|
|
81
|
+
:return: the unique point gap_1 distance from ab and gap_2 distance from bc
|
|
82
|
+
:raise ValueError: if angle abc is zero and gap_1 != gap_2
|
|
83
|
+
"""
|
|
84
|
+
~~~
|
|
85
|
+
|
|
86
|
+
~~~python
|
|
87
|
+
def offset_poly_per_vert(
|
|
88
|
+
polyline: Sequence[_Vec2],
|
|
89
|
+
vert_offsets: Iterable[tuple[float, float]],
|
|
90
|
+
poly_type: PolyType,
|
|
91
|
+
) -> list[GapCorner]:
|
|
92
|
+
"""Offset each corner of a polyline or polygon.
|
|
93
|
+
|
|
94
|
+
:param polyline: polyline
|
|
95
|
+
:param vert_offsets: iterable of (gap_1, gap_2) tuples. One pair per corner.
|
|
96
|
+
:param poly_type: PolyType.POLYGON or PolyType.POLYLINE
|
|
97
|
+
:return: polyline offset by vert_offsets
|
|
98
|
+
:raise ValueError: if fewer than three points are given for a polygon
|
|
99
|
+
:raise ValueError: if fewer than two points are given for a polyline
|
|
100
|
+
|
|
101
|
+
This is the engine of the offset_polyline and offset_polygon
|
|
102
|
+
functions. You can use it directly, but it's going to be tricky if
|
|
103
|
+
you have zero-length segments or closed points. These points will
|
|
104
|
+
be removed before the gaps are applied, so you'll have to pass
|
|
105
|
+
exactly enough for gap pairs for the segments that are retained.
|
|
106
|
+
"""
|
|
107
|
+
~~~
|
|
108
|
+
|
|
109
|
+
~~~python
|
|
110
|
+
def offset_poly_per_edge(
|
|
111
|
+
polyline: Sequence[_Vec2], edge_offsets: Iterable[float], poly_type: PolyType
|
|
112
|
+
) -> list[GapCorner]:
|
|
113
|
+
"""Offset each edge of a polyline or polygon.
|
|
114
|
+
|
|
115
|
+
:param polyline: polyline
|
|
116
|
+
:param edge_offsets: iterable of offsets. One per edge.
|
|
117
|
+
:param poly_type: PolyType.POLYGON or PolyType.POLYLINE
|
|
118
|
+
:return: polyline offset by edge_offsets
|
|
119
|
+
|
|
120
|
+
This function allows each edge of a polygon or polyline to be offset by
|
|
121
|
+
a different amount. You will end up with a ValueError in gap_corner
|
|
122
|
+
if you try to offset consecutive, parallel edges by different amounts.
|
|
123
|
+
"""
|
|
124
|
+
~~~
|
|
125
|
+
|
|
126
|
+
These allow a little more control, like putting in a different offset for each edge
|
|
127
|
+
|
|
128
|
+
## return value
|
|
129
|
+
|
|
130
|
+
The return value will be a GapCorner instance or a list of GapCorner instances. These have three attributes:
|
|
131
|
+
|
|
132
|
+
* .xsect -> a point at each corner where segment ab offset to the left by gap_1 intersects segment bc offset to the left by gap_2.
|
|
133
|
+
* .angle -> the signed ccw angle at corner abc
|
|
134
|
+
* .cpts -> quadratic Bezier control points for a rounded corner at abc. There is more than one way to handle this when gap_1 != gap_2, but these should give a good result in most situations.
|
|
@@ -1,113 +1,113 @@
|
|
|
1
|
-
# offset_poly
|
|
2
|
-
|
|
3
|
-
Move a polyline or polygon (left) by a given offset.
|
|
4
|
-
|
|
5
|
-
A counterclockwise polygon will be offset to the inside. I clockwise polygon will be offset to the outside.
|
|
6
|
-
|
|
7
|
-
## offset_polygon
|
|
8
|
-
|
|
9
|
-
~~~python
|
|
10
|
-
def offset_polygon(polyline: Sequence[_Vec2], offset: float) -> list[_Vec2]:
|
|
11
|
-
"""Offset polygon edges (to the left) by a constant amount.
|
|
12
|
-
|
|
13
|
-
:param polyline: polyline
|
|
14
|
-
:param offset: distance to offset from each edge
|
|
15
|
-
:return: polygon offset by offset
|
|
16
|
-
"""
|
|
17
|
-
~~~
|
|
18
|
-
|
|
19
|
-
## offset_polyline
|
|
20
|
-
|
|
21
|
-
~~~python
|
|
22
|
-
def offset_polyline(polyline: Sequence[_Vec2], offset: float) -> list[_Vec2]:
|
|
23
|
-
"""Offset polygon edges (to the left) by a constant amount.
|
|
24
|
-
|
|
25
|
-
:param polyline: polyline
|
|
26
|
-
:param offset: distance to offset from each edge
|
|
27
|
-
:return: polyline offset by offset
|
|
28
|
-
"""
|
|
29
|
-
~~~
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
The difference between the two is that `offset_polygon` will close the polygon if it is not already closed, whereas `offset_polyline` will leave the polyline open even if the first and last points are identical.
|
|
33
|
-
|
|
34
|
-
This package is the simplest version of polyline offsetting, it does not anticipate or account for self intersections that may come up when offsetting a polyline.
|
|
35
|
-
|
|
36
|
-
This is not nearly as sophisticated as curve offsetting, but you can use this for control polygon offsetting, which will be nearly as good in some instances.
|
|
37
|
-
|
|
38
|
-
* multiple points (knots in your control points) are preserved.
|
|
39
|
-
* if input[0] == input[-1], output[0] will equal output[-1]
|
|
40
|
-
|
|
41
|
-
## You may see (nan, nan) in the result.
|
|
42
|
-
|
|
43
|
-
If you pass two adjacent, opposite, parallel edges, you will get a (nan, nan) in the result. With points A -> B -> A, for instance, there is no point that would be any given distance (except 0) left of both A B and B A.
|
|
44
|
-
|
|
45
|
-
## More complex functions
|
|
46
|
-
|
|
47
|
-
There are a few more complex functions,`offset_poly_per_vert` and `offset_poly_per_edge`
|
|
48
|
-
|
|
49
|
-
~~~python
|
|
50
|
-
def gap_corner(
|
|
51
|
-
pnt_a: _Vec, pnt_b: _Vec, pnt_c: _Vec, gap_1: float, gap_2: float | None = None
|
|
52
|
-
) -> GapCorner:
|
|
53
|
-
"""Offset a corner (to the left) defined by three points.
|
|
54
|
-
|
|
55
|
-
:param pnt_a: previous point
|
|
56
|
-
:param pnt_b: corner point
|
|
57
|
-
:param pnt_c: next point
|
|
58
|
-
:param gap_1: distance to offset from vec12
|
|
59
|
-
:param gap_2: optional distance to offset from vec23. If not given, gap_1 is used
|
|
60
|
-
:return: the unique point gap_1 distance from ab and gap_2 distance from bc
|
|
61
|
-
:raise ValueError: if angle abc is zero and gap_1 != gap_2
|
|
62
|
-
"""
|
|
63
|
-
~~~
|
|
64
|
-
|
|
65
|
-
~~~python
|
|
66
|
-
def offset_poly_per_vert(
|
|
67
|
-
polyline: Sequence[_Vec2],
|
|
68
|
-
vert_offsets: Iterable[tuple[float, float]],
|
|
69
|
-
poly_type: PolyType,
|
|
70
|
-
) -> list[GapCorner]:
|
|
71
|
-
"""Offset each corner of a polyline or polygon.
|
|
72
|
-
|
|
73
|
-
:param polyline: polyline
|
|
74
|
-
:param vert_offsets: iterable of (gap_1, gap_2) tuples. One pair per corner.
|
|
75
|
-
:param poly_type: PolyType.POLYGON or PolyType.POLYLINE
|
|
76
|
-
:return: polyline offset by vert_offsets
|
|
77
|
-
:raise ValueError: if fewer than three points are given for a polygon
|
|
78
|
-
:raise ValueError: if fewer than two points are given for a polyline
|
|
79
|
-
|
|
80
|
-
This is the engine of the offset_polyline and offset_polygon
|
|
81
|
-
functions. You can use it directly, but it's going to be tricky if
|
|
82
|
-
you have zero-length segments or closed points. These points will
|
|
83
|
-
be removed before the gaps are applied, so you'll have to pass
|
|
84
|
-
exactly enough for gap pairs for the segments that are retained.
|
|
85
|
-
"""
|
|
86
|
-
~~~
|
|
87
|
-
|
|
88
|
-
~~~python
|
|
89
|
-
def offset_poly_per_edge(
|
|
90
|
-
polyline: Sequence[_Vec2], edge_offsets: Iterable[float], poly_type: PolyType
|
|
91
|
-
) -> list[GapCorner]:
|
|
92
|
-
"""Offset each edge of a polyline or polygon.
|
|
93
|
-
|
|
94
|
-
:param polyline: polyline
|
|
95
|
-
:param edge_offsets: iterable of offsets. One per edge.
|
|
96
|
-
:param poly_type: PolyType.POLYGON or PolyType.POLYLINE
|
|
97
|
-
:return: polyline offset by edge_offsets
|
|
98
|
-
|
|
99
|
-
This function allows each edge of a polygon or polyline to be offset by
|
|
100
|
-
a different amount. You will end up with a ValueError in gap_corner
|
|
101
|
-
if you try to offset consecutive, parallel edges by different amounts.
|
|
102
|
-
"""
|
|
103
|
-
~~~
|
|
104
|
-
|
|
105
|
-
These allow a little more control, like putting in a different offset for each edge
|
|
106
|
-
|
|
107
|
-
## return value
|
|
108
|
-
|
|
109
|
-
The return value will be a GapCorner instance or a list of GapCorner instances. These have three attributes:
|
|
110
|
-
|
|
111
|
-
* .xsect -> a point at each corner where segment ab offset to the left by gap_1 intersects segment bc offset to the left by gap_2.
|
|
112
|
-
* .angle -> the signed ccw angle at corner abc
|
|
113
|
-
* .cpts -> quadratic Bezier control points for a rounded corner at abc. There is more than one way to handle this when gap_1 != gap_2, but these should give a good result in most situations.
|
|
1
|
+
# offset_poly
|
|
2
|
+
|
|
3
|
+
Move a polyline or polygon (left) by a given offset.
|
|
4
|
+
|
|
5
|
+
A counterclockwise polygon will be offset to the inside. I clockwise polygon will be offset to the outside.
|
|
6
|
+
|
|
7
|
+
## offset_polygon
|
|
8
|
+
|
|
9
|
+
~~~python
|
|
10
|
+
def offset_polygon(polyline: Sequence[_Vec2], offset: float) -> list[_Vec2]:
|
|
11
|
+
"""Offset polygon edges (to the left) by a constant amount.
|
|
12
|
+
|
|
13
|
+
:param polyline: polyline
|
|
14
|
+
:param offset: distance to offset from each edge
|
|
15
|
+
:return: polygon offset by offset
|
|
16
|
+
"""
|
|
17
|
+
~~~
|
|
18
|
+
|
|
19
|
+
## offset_polyline
|
|
20
|
+
|
|
21
|
+
~~~python
|
|
22
|
+
def offset_polyline(polyline: Sequence[_Vec2], offset: float) -> list[_Vec2]:
|
|
23
|
+
"""Offset polygon edges (to the left) by a constant amount.
|
|
24
|
+
|
|
25
|
+
:param polyline: polyline
|
|
26
|
+
:param offset: distance to offset from each edge
|
|
27
|
+
:return: polyline offset by offset
|
|
28
|
+
"""
|
|
29
|
+
~~~
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
The difference between the two is that `offset_polygon` will close the polygon if it is not already closed, whereas `offset_polyline` will leave the polyline open even if the first and last points are identical.
|
|
33
|
+
|
|
34
|
+
This package is the simplest version of polyline offsetting, it does not anticipate or account for self intersections that may come up when offsetting a polyline.
|
|
35
|
+
|
|
36
|
+
This is not nearly as sophisticated as curve offsetting, but you can use this for control polygon offsetting, which will be nearly as good in some instances.
|
|
37
|
+
|
|
38
|
+
* multiple points (knots in your control points) are preserved.
|
|
39
|
+
* if input[0] == input[-1], output[0] will equal output[-1]
|
|
40
|
+
|
|
41
|
+
## You may see (nan, nan) in the result.
|
|
42
|
+
|
|
43
|
+
If you pass two adjacent, opposite, parallel edges, you will get a (nan, nan) in the result. With points A -> B -> A, for instance, there is no point that would be any given distance (except 0) left of both A B and B A.
|
|
44
|
+
|
|
45
|
+
## More complex functions
|
|
46
|
+
|
|
47
|
+
There are a few more complex functions,`offset_poly_per_vert` and `offset_poly_per_edge`
|
|
48
|
+
|
|
49
|
+
~~~python
|
|
50
|
+
def gap_corner(
|
|
51
|
+
pnt_a: _Vec, pnt_b: _Vec, pnt_c: _Vec, gap_1: float, gap_2: float | None = None
|
|
52
|
+
) -> GapCorner:
|
|
53
|
+
"""Offset a corner (to the left) defined by three points.
|
|
54
|
+
|
|
55
|
+
:param pnt_a: previous point
|
|
56
|
+
:param pnt_b: corner point
|
|
57
|
+
:param pnt_c: next point
|
|
58
|
+
:param gap_1: distance to offset from vec12
|
|
59
|
+
:param gap_2: optional distance to offset from vec23. If not given, gap_1 is used
|
|
60
|
+
:return: the unique point gap_1 distance from ab and gap_2 distance from bc
|
|
61
|
+
:raise ValueError: if angle abc is zero and gap_1 != gap_2
|
|
62
|
+
"""
|
|
63
|
+
~~~
|
|
64
|
+
|
|
65
|
+
~~~python
|
|
66
|
+
def offset_poly_per_vert(
|
|
67
|
+
polyline: Sequence[_Vec2],
|
|
68
|
+
vert_offsets: Iterable[tuple[float, float]],
|
|
69
|
+
poly_type: PolyType,
|
|
70
|
+
) -> list[GapCorner]:
|
|
71
|
+
"""Offset each corner of a polyline or polygon.
|
|
72
|
+
|
|
73
|
+
:param polyline: polyline
|
|
74
|
+
:param vert_offsets: iterable of (gap_1, gap_2) tuples. One pair per corner.
|
|
75
|
+
:param poly_type: PolyType.POLYGON or PolyType.POLYLINE
|
|
76
|
+
:return: polyline offset by vert_offsets
|
|
77
|
+
:raise ValueError: if fewer than three points are given for a polygon
|
|
78
|
+
:raise ValueError: if fewer than two points are given for a polyline
|
|
79
|
+
|
|
80
|
+
This is the engine of the offset_polyline and offset_polygon
|
|
81
|
+
functions. You can use it directly, but it's going to be tricky if
|
|
82
|
+
you have zero-length segments or closed points. These points will
|
|
83
|
+
be removed before the gaps are applied, so you'll have to pass
|
|
84
|
+
exactly enough for gap pairs for the segments that are retained.
|
|
85
|
+
"""
|
|
86
|
+
~~~
|
|
87
|
+
|
|
88
|
+
~~~python
|
|
89
|
+
def offset_poly_per_edge(
|
|
90
|
+
polyline: Sequence[_Vec2], edge_offsets: Iterable[float], poly_type: PolyType
|
|
91
|
+
) -> list[GapCorner]:
|
|
92
|
+
"""Offset each edge of a polyline or polygon.
|
|
93
|
+
|
|
94
|
+
:param polyline: polyline
|
|
95
|
+
:param edge_offsets: iterable of offsets. One per edge.
|
|
96
|
+
:param poly_type: PolyType.POLYGON or PolyType.POLYLINE
|
|
97
|
+
:return: polyline offset by edge_offsets
|
|
98
|
+
|
|
99
|
+
This function allows each edge of a polygon or polyline to be offset by
|
|
100
|
+
a different amount. You will end up with a ValueError in gap_corner
|
|
101
|
+
if you try to offset consecutive, parallel edges by different amounts.
|
|
102
|
+
"""
|
|
103
|
+
~~~
|
|
104
|
+
|
|
105
|
+
These allow a little more control, like putting in a different offset for each edge
|
|
106
|
+
|
|
107
|
+
## return value
|
|
108
|
+
|
|
109
|
+
The return value will be a GapCorner instance or a list of GapCorner instances. These have three attributes:
|
|
110
|
+
|
|
111
|
+
* .xsect -> a point at each corner where segment ab offset to the left by gap_1 intersects segment bc offset to the left by gap_2.
|
|
112
|
+
* .angle -> the signed ccw angle at corner abc
|
|
113
|
+
* .cpts -> quadratic Bezier control points for a rounded corner at abc. There is more than one way to handle this when gap_1 != gap_2, but these should give a good result in most situations.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "offset-poly"
|
|
3
|
+
version = "0.4.0"
|
|
4
|
+
description = "offset a polyline or polygon in 2d space"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "Shay Hill", email = "shay_public@hotmail.com" }
|
|
9
|
+
]
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 2 - Pre-Alpha",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"License :: OSI Approved :: MIT License",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Programming Language :: Python :: 3.10",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Programming Language :: Python :: 3.14",
|
|
21
|
+
"Typing :: Typed",
|
|
22
|
+
]
|
|
23
|
+
dependencies = ["vec2_math"]
|
|
24
|
+
|
|
25
|
+
[build-system]
|
|
26
|
+
requires = ["uv_build>=0.11.25,<0.12.0"]
|
|
27
|
+
build-backend = "uv_build"
|
|
28
|
+
|
|
29
|
+
[dependency-groups]
|
|
30
|
+
dev = [
|
|
31
|
+
"commitizen>=4.16.5",
|
|
32
|
+
"pre-commit>=4.6.1",
|
|
33
|
+
"pytest>=9.1.1",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
[tool.commitizen]
|
|
38
|
+
name = "cz_conventional_commits"
|
|
39
|
+
version = "0.4.0"
|
|
40
|
+
tag_format = "$version"
|
|
41
|
+
major-version-zero = true
|
|
42
|
+
version_files = ["pyproject.toml:^version"]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
[tool.isort]
|
|
46
|
+
profile = "black"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
[tool.pytest.ini_options]
|
|
50
|
+
addopts = "--doctest-modules"
|
|
51
|
+
pythonpath = "tests"
|
|
52
|
+
testpaths = ["tests"]
|
|
53
|
+
log_cli = true
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
[tool.ruff]
|
|
57
|
+
target-version = "py310"
|
|
58
|
+
exclude = ["typings", ".venv"]
|
|
59
|
+
[tool.ruff.lint.pydocstyle]
|
|
60
|
+
convention = "pep257"
|
|
61
|
+
|
|
62
|
+
[tool.ruff.lint.per-file-ignores]
|
|
63
|
+
"tests/*.py" = [
|
|
64
|
+
"S101",
|
|
65
|
+
"D",
|
|
66
|
+
"F401",
|
|
67
|
+
] # Ignore assertions, docstrings, unused imports in test files
|
|
68
|
+
|
|
69
|
+
[tool.ruff.format]
|
|
70
|
+
docstring-code-line-length = 88
|
|
71
|
+
skip-magic-trailing-comma = true
|
|
72
|
+
|
|
73
|
+
[tool.ruff.lint.isort]
|
|
74
|
+
# keep isort consistent with format.skip-magic-trailing-comma
|
|
75
|
+
split-on-trailing-comma = false
|
|
76
|
+
|
|
77
|
+
[tool.ruff.lint]
|
|
78
|
+
select = ["ALL"]
|
|
79
|
+
# only auto-fix __all__ sorting (RUF022) and import sorting (I)
|
|
80
|
+
fixable = ["RUF022", "I"]
|
|
81
|
+
ignore = [
|
|
82
|
+
"COM812", # trailing comma missing (does not agree with Black)
|
|
83
|
+
"D203", # 1 blank line before class docstring (incompatible with D211)
|
|
84
|
+
"D213", # multi-line summary second line (incompatible with D212)
|
|
85
|
+
"ISC003", # explicitly concatenated string should be implicit
|
|
86
|
+
"FLY", # Use f-string instead of ...
|
|
87
|
+
"S311", # Standard pseudo-random generators are not suitable for security/cryptographic purposes
|
|
88
|
+
"S301", # don't use pickle
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
[tool.pyright]
|
|
93
|
+
include = ["src", "tests"]
|
|
94
|
+
exclude = ["**/__pycache__.py"]
|
|
95
|
+
ignore = ["typings", ".venv"]
|
|
96
|
+
|
|
97
|
+
pythonVersion = "3.10"
|
|
98
|
+
pythonPlatform = "All"
|
|
99
|
+
|
|
100
|
+
typeCheckingMode = "strict"
|
|
101
|
+
reportCallInDefaultInitializer = true
|
|
102
|
+
reportImplicitStringConcatenation = true
|
|
103
|
+
reportPropertyTypeMismatch = true
|
|
104
|
+
reportUninitializedInstanceVariable = true
|
|
105
|
+
reportUnnecessaryTypeIgnoreComment = true
|
|
106
|
+
reportUnusedCallResult = true
|
|
107
|
+
|
|
108
|
+
venvPath = "."
|
|
109
|
+
venv = "./.venv"
|
|
110
|
+
|
|
111
|
+
[[tool.pyright.executionEnvironments]]
|
|
112
|
+
root = "tests"
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
"""Import
|
|
2
|
-
|
|
3
|
-
:author: Shay Hill
|
|
4
|
-
:created: 2023-08-19
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from offset_poly.offset import (
|
|
8
|
-
offset_poly_per_edge,
|
|
9
|
-
offset_poly_per_vert,
|
|
10
|
-
offset_polygon,
|
|
11
|
-
offset_polyline,
|
|
12
|
-
)
|
|
13
|
-
from offset_poly.offset_corner import gap_corner
|
|
14
|
-
|
|
15
|
-
__all__ = [
|
|
16
|
-
"gap_corner",
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
]
|
|
1
|
+
"""Import functions into the package namespace.
|
|
2
|
+
|
|
3
|
+
:author: Shay Hill
|
|
4
|
+
:created: 2023-08-19
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from offset_poly.offset import (
|
|
8
|
+
offset_poly_per_edge,
|
|
9
|
+
offset_poly_per_vert,
|
|
10
|
+
offset_polygon,
|
|
11
|
+
offset_polyline,
|
|
12
|
+
)
|
|
13
|
+
from offset_poly.offset_corner import gap_corner
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"gap_corner",
|
|
17
|
+
"offset_poly_per_edge",
|
|
18
|
+
"offset_poly_per_vert",
|
|
19
|
+
"offset_polygon",
|
|
20
|
+
"offset_polyline",
|
|
21
|
+
]
|