r12spline.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. # Copyright (c) 2018 Manfred Moitzi
  2. # License: MIT License
  3. """
  4. DXF R12 Splines
  5. ===============
  6. DXF R12 supports 2d B-splines, but Autodesk do not document the usage in the DXF Reference.
  7. The base entity for splines in DXF R12 is the POLYLINE entity.
  8. Transformed Into 3D Space
  9. -------------------------
  10. The spline itself is always in a plane, but as any 2d entity, the spline can be transformed into the 3d object
  11. by elevation, extrusion and thickness/width.
  12. Open Quadratic Spline with Fit Vertices
  13. -------------------------------------
  14. Example: 2D_SPLINE_QUADRATIC.dxf
  15. expected knot vector: open uniform
  16. degree: 2
  17. order: 3
  18. POLYLINE:
  19. flags (70): 4 = SPLINE_FIT_VERTICES_ADDED
  20. smooth type (75): 5 = QUADRATIC_BSPLINE
  21. Sequence of VERTEX
  22. flags (70): SPLINE_VERTEX_CREATED = 8 # Spline vertex created by spline-fitting
  23. This vertices are the curve vertices of the spline (fitted).
  24. Frame control vertices appear after the curve vertices.
  25. Sequence of VERTEX
  26. flags (70): SPLINE_FRAME_CONTROL_POINT = 16
  27. No control point at the starting point, but a control point at the end point,
  28. last control point == last fit vertex
  29. Closed Quadratic Spline with Fit Vertices
  30. -----------------------------------------
  31. Example: 2D_SPLINE_QUADRATIC_CLOSED.dxf
  32. expected knot vector: closed uniform
  33. degree: 2
  34. order: 3
  35. POLYLINE:
  36. flags (70): 5 = CLOSED | SPLINE_FIT_VERTICES_ADDED
  37. smooth type (75): 5 = QUADRATIC_BSPLINE
  38. Sequence of VERTEX
  39. flags (70): SPLINE_VERTEX_CREATED = 8 # Spline vertex created by spline-fitting
  40. Frame control vertices appear after the curve vertices.
  41. Sequence of VERTEX
  42. flags (70): SPLINE_FRAME_CONTROL_POINT = 16
  43. Open Cubic Spline with Fit Vertices
  44. -----------------------------------
  45. Example: 2D_SPLINE_CUBIC.dxf
  46. expected knot vector: open uniform
  47. degree: 3
  48. order: 4
  49. POLYLINE:
  50. flags (70): 4 = SPLINE_FIT_VERTICES_ADDED
  51. smooth type (75): 6 = CUBIC_BSPLINE
  52. Sequence of VERTEX
  53. flags (70): SPLINE_VERTEX_CREATED = 8 # Spline vertex created by spline-fitting
  54. This vertices are the curve vertices of the spline (fitted).
  55. Frame control vertices appear after the curve vertices.
  56. Sequence of VERTEX
  57. flags (70): SPLINE_FRAME_CONTROL_POINT = 16
  58. No control point at the starting point, but a control point at the end point,
  59. last control point == last fit vertex
  60. Closed Curve With Extra Vertices Created
  61. ----------------------------------------
  62. Example: 2D_FIT_CURVE_CLOSED.dxf
  63. POLYLINE:
  64. flags (70): 3 = CLOSED | CURVE_FIT_VERTICES_ADDED
  65. Vertices with bulge values:
  66. flags (70): 1 = EXTRA_VERTEX_CREATED
  67. Vertex 70=0, Vertex 70=1, Vertex 70=0, Vertex 70=1
  68. """
  69. from typing import TYPE_CHECKING, Iterable, List
  70. from ezdxf.lldxf import const
  71. from ezdxf.math.bspline import BSpline, BSplineClosed
  72. if TYPE_CHECKING:
  73. from ezdxf.eztypes import Vertex, GenericLayoutType, Polyline
  74. from ezdxf.math.ucs import UCS
  75. class R12Spline:
  76. def __init__(self, control_points: Iterable['Vertex'], degree: int = 2, closed: bool = True):
  77. self.control_points = list(control_points)
  78. self.degree = degree
  79. self.closed = closed
  80. def approximate(self, segments: int = 40, ucs: 'UCS' = None) -> List['Vertex']:
  81. if self.closed:
  82. spline = BSplineClosed(self.control_points, order=self.degree + 1)
  83. else:
  84. spline = BSpline(self.control_points, order=self.degree + 1)
  85. vertices = spline.approximate(segments)
  86. if ucs is not None:
  87. vertices = (ucs.to_ocs(vertex) for vertex in vertices)
  88. return list(vertices)
  89. def render(self, layout: 'GenericLayoutType', segments: int = 40, ucs: 'UCS' = None,
  90. dxfattribs: dict = None) -> 'Polyline':
  91. polyline = layout.add_polyline2d(points=[], dxfattribs=dxfattribs)
  92. flags = polyline.SPLINE_FIT_VERTICES_ADDED
  93. if self.closed:
  94. flags |= polyline.CLOSED
  95. polyline.dxf.flags = flags
  96. if self.degree == 2:
  97. smooth_type = polyline.QUADRATIC_BSPLINE
  98. elif self.degree == 3:
  99. smooth_type = polyline.CUBIC_BSPLINE
  100. else:
  101. raise ValueError('invalid degree of spline')
  102. polyline.dxf.smooth_type = smooth_type
  103. # set OCS extrusion vector
  104. if ucs is not None:
  105. polyline.dxf.extrusion = ucs.uz
  106. # add fit points in OCS
  107. polyline.append_vertices(
  108. self.approximate(segments, ucs),
  109. dxfattribs={
  110. 'layer': polyline.dxf.layer,
  111. 'flags': const.VTX_SPLINE_VERTEX_CREATED,
  112. })
  113. # add control frame points in OCS
  114. control_points = self.control_points
  115. if ucs is not None:
  116. control_points = list(ucs.points_to_ocs(control_points))
  117. polyline.dxf.elevation = (0, 0, control_points[0].z)
  118. polyline.append_vertices(control_points, dxfattribs={
  119. 'layer': polyline.dxf.layer,
  120. 'flags': const.VTX_SPLINE_FRAME_CONTROL_POINT,
  121. })
  122. return polyline