/
r12spline.py
236 lines (186 loc) · 7.47 KB
/
r12spline.py
1
2
3
4
5
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# Copyright (c) 2018-2022 Manfred Moitzi
# License: MIT License
"""
DXF R12 Splines
===============
DXF R12 supports 2d B-splines, but Autodesk do not document the usage in the
DXF Reference. The base entity for splines in DXF R12 is the POLYLINE entity.
Transformed Into 3D Space
-------------------------
The spline itself is always in a plane, but as any 2D entity, the spline can be
transformed into the 3D object by elevation, extrusion and thickness/width.
Open Quadratic Spline with Fit Vertices
-------------------------------------
Example: 2D_SPLINE_QUADRATIC.dxf
expected knot vector: open uniform
degree: 2
order: 3
POLYLINE:
flags (70): 4 = SPLINE_FIT_VERTICES_ADDED
smooth type (75): 5 = QUADRATIC_BSPLINE
Sequence of VERTEX
flags (70): SPLINE_VERTEX_CREATED = 8 # Spline vertex created by spline-fitting
This vertices are the curve vertices of the spline (fitted).
Frame control vertices appear after the curve vertices.
Sequence of VERTEX
flags (70): SPLINE_FRAME_CONTROL_POINT = 16
No control point at the starting point, but a control point at the end point,
last control point == last fit vertex
Closed Quadratic Spline with Fit Vertices
-----------------------------------------
Example: 2D_SPLINE_QUADRATIC_CLOSED.dxf
expected knot vector: closed uniform
degree: 2
order: 3
POLYLINE:
flags (70): 5 = CLOSED | SPLINE_FIT_VERTICES_ADDED
smooth type (75): 5 = QUADRATIC_BSPLINE
Sequence of VERTEX
flags (70): SPLINE_VERTEX_CREATED = 8 # Spline vertex created by spline-fitting
Frame control vertices appear after the curve vertices.
Sequence of VERTEX
flags (70): SPLINE_FRAME_CONTROL_POINT = 16
Open Cubic Spline with Fit Vertices
-----------------------------------
Example: 2D_SPLINE_CUBIC.dxf
expected knot vector: open uniform
degree: 3
order: 4
POLYLINE:
flags (70): 4 = SPLINE_FIT_VERTICES_ADDED
smooth type (75): 6 = CUBIC_BSPLINE
Sequence of VERTEX
flags (70): SPLINE_VERTEX_CREATED = 8 # Spline vertex created by spline-fitting
This vertices are the curve vertices of the spline (fitted).
Frame control vertices appear after the curve vertices.
Sequence of VERTEX
flags (70): SPLINE_FRAME_CONTROL_POINT = 16
No control point at the starting point, but a control point at the end point,
last control point == last fit vertex
Closed Curve With Extra Vertices Created
----------------------------------------
Example: 2D_FIT_CURVE_CLOSED.dxf
POLYLINE:
flags (70): 3 = CLOSED | CURVE_FIT_VERTICES_ADDED
Vertices with bulge values:
flags (70): 1 = EXTRA_VERTEX_CREATED
Vertex 70=0, Vertex 70=1, Vertex 70=0, Vertex 70=1
"""
from __future__ import annotations
from typing import TYPE_CHECKING, Iterable, Optional
from ezdxf.lldxf import const
from ezdxf.math import BSpline, closed_uniform_bspline, Vec3, UCS, UVec
if TYPE_CHECKING:
from ezdxf.layouts import BaseLayout
from ezdxf.entities import Polyline
class R12Spline:
"""DXF R12 supports 2D B-splines, but Autodesk do not document the usage
in the DXF Reference. The base entity for splines in DXF R12 is the POLYLINE
entity. The spline itself is always in a plane, but as any 2D entity, the
spline can be transformed into the 3D object by elevation and extrusion
(:ref:`OCS`, :ref:`UCS`).
This way it was possible to store the spline parameters in the DXF R12 file,
to allow CAD applications to modify the spline parameters and rerender the
B-spline afterward again as polyline approximation. Therefore, the result is
not better than an approximation by the :class:`Spline` class, it is also
just a POLYLINE entity, but maybe someone need exact this tool in the
future.
"""
def __init__(
self,
control_points: Iterable[UVec],
degree: int = 2,
closed: bool = True,
):
"""
Args:
control_points: B-spline control frame vertices
degree: degree of B-spline, only 2 and 3 is supported
closed: ``True`` for closed curve
"""
self.control_points = Vec3.list(control_points)
self.degree = degree
self.closed = closed
def approximate(
self, segments: int = 40, ucs: Optional[UCS] = None
) -> list[UVec]:
"""Approximate the B-spline by a polyline with `segments` line segments.
If `ucs` is not ``None``, ucs defines an :class:`~ezdxf.math.UCS`, to
transform the curve into :ref:`OCS`. The control points are placed
xy-plane of the UCS, don't use z-axis coordinates, if so make sure all
control points are in a plane parallel to the OCS base plane
(UCS xy-plane), else the result is unpredictable and depends on the CAD
application used to open the DXF file - it may crash.
Args:
segments: count of line segments for approximation, vertex count is
`segments` + 1
ucs: :class:`~ezdxf.math.UCS` definition, control points in ucs
coordinates
Returns:
list of vertices in :class:`~ezdxf.math.OCS` as
:class:`~ezdxf.math.Vec3` objects
"""
if self.closed:
spline = closed_uniform_bspline(
self.control_points, order=self.degree + 1
)
else:
spline = BSpline(self.control_points, order=self.degree + 1)
vertices = spline.approximate(segments)
if ucs is not None:
vertices = (ucs.to_ocs(vertex) for vertex in vertices)
return list(vertices)
def render(
self,
layout: BaseLayout,
segments: int = 40,
ucs: Optional[UCS] = None,
dxfattribs=None,
) -> Polyline:
"""Renders the B-spline into `layout` as 2D :class:`~ezdxf.entities.Polyline`
entity. Use an :class:`~ezdxf.math.UCS` to place the 2D spline in the
3D space, see :meth:`approximate` for more information.
Args:
layout: :class:`~ezdxf.layouts.BaseLayout` object
segments: count of line segments for approximation, vertex count is
`segments` + 1
ucs: :class:`~ezdxf.math.UCS` definition, control points in ucs
coordinates.
dxfattribs: DXF attributes for :class:`~ezdxf.entities.Polyline`
"""
polyline = layout.add_polyline2d(points=[], dxfattribs=dxfattribs)
flags = polyline.SPLINE_FIT_VERTICES_ADDED
if self.closed:
flags |= polyline.CLOSED
polyline.dxf.flags = flags
if self.degree == 2:
smooth_type = polyline.QUADRATIC_BSPLINE
elif self.degree == 3:
smooth_type = polyline.CUBIC_BSPLINE
else:
raise ValueError("invalid degree of spline")
polyline.dxf.smooth_type = smooth_type
# set OCS extrusion vector
if ucs is not None:
polyline.dxf.extrusion = ucs.uz
# add fit points in OCS
polyline.append_vertices(
self.approximate(segments, ucs),
dxfattribs={
"layer": polyline.dxf.layer,
"flags": const.VTX_SPLINE_VERTEX_CREATED,
},
)
# add control frame points in OCS
control_points = self.control_points
if ucs is not None:
control_points = list(ucs.points_to_ocs(control_points))
polyline.dxf.elevation = (0, 0, control_points[0].z)
polyline.append_vertices(
control_points,
dxfattribs={
"layer": polyline.dxf.layer,
"flags": const.VTX_SPLINE_FRAME_CONTROL_POINT,
},
)
return polyline