1 # SPDX-License-Identifier: GPL-2.0-or-later
7 from bpy
.props
import *
8 from bpy_extras
import object_utils
, view3d_utils
9 from mathutils
import *
12 from . import properties
14 from . import intersections
16 from . import surfaces
17 from . import mathematics
18 from . import internal
22 class OperatorCurveInfo(bpy
.types
.Operator
):
23 bl_idname
= "curvetools.operatorcurveinfo"
25 bl_description
= "Displays general info about the active/selected curve"
29 def poll(cls
, context
):
30 return util
.Selected1Curve()
33 def execute(self
, context
):
34 curve
= curves
.Curve(context
.active_object
)
36 nrSplines
= len(curve
.splines
)
39 for spline
in curve
.splines
:
40 nrSegments
+= spline
.nrSegments
41 if spline
.nrSegments
< 1: nrEmptySplines
+= 1
44 self
.report({'INFO'}, "nrSplines: %d; nrSegments: %d; nrEmptySplines: %d" % (nrSplines
, nrSegments
, nrEmptySplines
))
50 class OperatorCurveLength(bpy
.types
.Operator
):
51 bl_idname
= "curvetools.operatorcurvelength"
53 bl_description
= "Calculates the length of the active/selected curve"
57 def poll(cls
, context
):
58 return util
.Selected1Curve()
61 def execute(self
, context
):
62 curve
= curves
.Curve(context
.active_object
)
64 context
.scene
.curvetools
.CurveLength
= curve
.length
70 class OperatorSplinesInfo(bpy
.types
.Operator
):
71 bl_idname
= "curvetools.operatorsplinesinfo"
73 bl_description
= "Displays general info about the splines of the active/selected curve"
77 def poll(cls
, context
):
78 return util
.Selected1Curve()
81 def execute(self
, context
):
82 curve
= curves
.Curve(context
.active_object
)
83 nrSplines
= len(curve
.splines
)
86 print("OperatorSplinesInfo:", "nrSplines:", nrSplines
)
89 for iSpline
, spline
in enumerate(curve
.splines
):
90 print("--", "spline %d of %d: nrSegments: %d" % (iSpline
+ 1, nrSplines
, spline
.nrSegments
))
92 if spline
.nrSegments
< 1:
94 print("--", "--", "## WARNING: spline has no segments and will therefor be ignored in any further calculations")
97 self
.report({'INFO'}, "nrSplines: %d; nrEmptySplines: %d" % (nrSplines
, nrEmptySplines
) + " -- more info: see console")
103 class OperatorSegmentsInfo(bpy
.types
.Operator
):
104 bl_idname
= "curvetools.operatorsegmentsinfo"
106 bl_description
= "Displays general info about the segments of the active/selected curve"
110 def poll(cls
, context
):
111 return util
.Selected1Curve()
114 def execute(self
, context
):
115 curve
= curves
.Curve(context
.active_object
)
116 nrSplines
= len(curve
.splines
)
120 print("OperatorSegmentsInfo:", "nrSplines:", nrSplines
)
123 for iSpline
, spline
in enumerate(curve
.splines
):
124 nrSegmentsSpline
= spline
.nrSegments
125 print("--", "spline %d of %d: nrSegments: %d" % (iSpline
+ 1, nrSplines
, nrSegmentsSpline
))
127 if nrSegmentsSpline
< 1:
129 print("--", "--", "## WARNING: spline has no segments and will therefor be ignored in any further calculations")
132 for iSegment
, segment
in enumerate(spline
.segments
):
133 print("--", "--", "segment %d of %d coefficients:" % (iSegment
+ 1, nrSegmentsSpline
))
134 print("--", "--", "--", "C0: %.6f, %.6f, %.6f" % (segment
.coeff0
.x
, segment
.coeff0
.y
, segment
.coeff0
.z
))
136 nrSegments
+= nrSegmentsSpline
138 self
.report({'INFO'}, "nrSplines: %d; nrSegments: %d; nrEmptySplines: %d" % (nrSplines
, nrSegments
, nrEmptySplines
))
144 class OperatorOriginToSpline0Start(bpy
.types
.Operator
):
145 bl_idname
= "curvetools.operatororigintospline0start"
146 bl_label
= "OriginToSpline0Start"
147 bl_description
= "Sets the origin of the active/selected curve to the starting point of the (first) spline. Nice for curve modifiers"
151 def poll(cls
, context
):
152 return util
.Selected1Curve()
155 def execute(self
, context
):
158 blCurve
= context
.active_object
159 blSpline
= blCurve
.data
.splines
[0]
160 newOrigin
= blCurve
.matrix_world
@ blSpline
.bezier_points
[0].co
162 origOrigin
= bpy
.context
.scene
.cursor
.location
.copy()
163 self
.report({'INFO'}, "origOrigin: %.6f, %.6f, %.6f" % (origOrigin
.x
, origOrigin
.y
, origOrigin
.z
))
164 self
.report({'INFO'}, "newOrigin: %.6f, %.6f, %.6f" % (newOrigin
.x
, newOrigin
.y
, newOrigin
.z
))
166 current_mode
= bpy
.context
.object.mode
168 bpy
.ops
.object.mode_set(mode
= 'OBJECT')
169 bpy
.context
.scene
.cursor
.location
= newOrigin
170 bpy
.ops
.object.origin_set(type='ORIGIN_CURSOR')
171 bpy
.context
.scene
.cursor
.location
= origOrigin
173 bpy
.ops
.object.mode_set (mode
= current_mode
)
181 class OperatorIntersectCurves(bpy
.types
.Operator
):
182 bl_idname
= "curvetools.operatorintersectcurves"
183 bl_label
= "Intersect"
184 bl_description
= "Intersects selected curves"
188 def poll(cls
, context
):
189 return util
.Selected2OrMoreCurves()
192 def execute(self
, context
):
193 print("### TODO: OperatorIntersectcurves.execute()")
195 algo
= context
.scene
.curvetools
.IntersectCurvesAlgorithm
196 print("-- algo:", algo
)
199 mode
= context
.scene
.curvetools
.IntersectCurvesMode
200 print("-- mode:", mode
)
201 # if mode == 'Split':
202 # self.report({'WARNING'}, "'Split' mode is not implemented yet -- <<STOPPING>>")
203 # return {'CANCELLED'}
205 affect
= context
.scene
.curvetools
.IntersectCurvesAffect
206 print("-- affect:", affect
)
208 selected_objects
= context
.selected_objects
209 lenodjs
= len(selected_objects
)
210 print('lenodjs:', lenodjs
)
211 for i
in range(0, lenodjs
):
212 for j
in range(0, lenodjs
):
214 bpy
.ops
.object.select_all(action
='DESELECT')
215 selected_objects
[i
].select_set(True)
216 selected_objects
[j
].select_set(True)
218 if selected_objects
[i
].type == 'CURVE' and selected_objects
[j
].type == 'CURVE':
219 curveIntersector
= intersections
.CurvesIntersector
.FromSelection()
220 rvIntersectionNrs
= curveIntersector
.CalcAndApplyIntersections()
222 self
.report({'INFO'}, "Active curve points: %d; other curve points: %d" % (rvIntersectionNrs
[0], rvIntersectionNrs
[1]))
224 for obj
in selected_objects
:
229 # ------------------------------------------------------------
232 class OperatorLoftCurves(bpy
.types
.Operator
):
233 bl_idname
= "curvetools.operatorloftcurves"
235 bl_description
= "Lofts selected curves"
239 def poll(cls
, context
):
240 return util
.Selected2Curves()
243 def execute(self
, context
):
244 #print("### TODO: OperatorLoftcurves.execute()")
246 loftedSurface
= surfaces
.LoftedSurface
.FromSelection()
247 loftedSurface
.AddToScene()
249 self
.report({'INFO'}, "OperatorLoftcurves.execute()")
254 # ------------------------------------------------------------
255 # OperatorSweepCurves
257 class OperatorSweepCurves(bpy
.types
.Operator
):
258 bl_idname
= "curvetools.operatorsweepcurves"
260 bl_description
= "Sweeps the active curve along to other curve (rail)"
264 def poll(cls
, context
):
265 return util
.Selected2Curves()
268 def execute(self
, context
):
269 #print("### TODO: OperatorSweepcurves.execute()")
271 sweptSurface
= surfaces
.SweptSurface
.FromSelection()
272 sweptSurface
.AddToScene()
274 self
.report({'INFO'}, "OperatorSweepcurves.execute()")
282 class OperatorBirail(bpy
.types
.Operator
):
283 bl_idname
= "curvetools.operatorbirail"
285 bl_description
= "Generates a birailed surface from 3 selected curves -- in order: rail1, rail2 and profile"
289 def poll(cls
, context
):
290 return util
.Selected3Curves()
293 def execute(self
, context
):
294 birailedSurface
= surfaces
.BirailedSurface
.FromSelection()
295 birailedSurface
.AddToScene()
297 self
.report({'INFO'}, "OperatorBirail.execute()")
303 # 1 OR MORE CURVES SELECTED
304 # #########################
305 class OperatorSplinesSetResolution(bpy
.types
.Operator
):
306 bl_idname
= "curvetools.operatorsplinessetresolution"
307 bl_label
= "SplinesSetResolution"
308 bl_description
= "Sets the resolution of all splines"
312 def poll(cls
, context
):
313 return util
.Selected1OrMoreCurves()
316 def execute(self
, context
):
317 splRes
= context
.scene
.curvetools
.SplineResolution
318 selCurves
= util
.GetSelectedCurves()
320 for blCurve
in selCurves
:
321 for spline
in blCurve
.data
.splines
:
322 spline
.resolution_u
= splRes
326 # ------------------------------------------------------------
327 # OperatorSplinesRemoveZeroSegment
329 class OperatorSplinesRemoveZeroSegment(bpy
.types
.Operator
):
330 bl_idname
= "curvetools.operatorsplinesremovezerosegment"
331 bl_label
= "SplinesRemoveZeroSegment"
332 bl_description
= "Removes splines with no segments -- they seem to creep up, sometimes"
336 def poll(cls
, context
):
337 return util
.Selected1OrMoreCurves()
340 def execute(self
, context
):
341 selCurves
= util
.GetSelectedCurves()
343 for blCurve
in selCurves
:
344 curve
= curves
.Curve(blCurve
)
345 nrSplines
= curve
.nrSplines
348 for spline
in curve
.splines
:
349 if len(spline
.segments
) < 1: splinesToRemove
.append(spline
)
350 nrRemovedSplines
= len(splinesToRemove
)
352 for spline
in splinesToRemove
: curve
.splines
.remove(spline
)
354 if nrRemovedSplines
> 0: curve
.RebuildInScene()
356 self
.report({'INFO'}, "Removed %d of %d splines" % (nrRemovedSplines
, nrSplines
))
360 # ------------------------------------------------------------
361 # OperatorSplinesRemoveShort
363 class OperatorSplinesRemoveShort(bpy
.types
.Operator
):
364 bl_idname
= "curvetools.operatorsplinesremoveshort"
365 bl_label
= "SplinesRemoveShort"
366 bl_description
= "Removes splines with a length smaller than the threshold"
370 def poll(cls
, context
):
371 return util
.Selected1OrMoreCurves()
374 def execute(self
, context
):
375 threshold
= context
.scene
.curvetools
.SplineRemoveLength
376 selCurves
= util
.GetSelectedCurves()
378 for blCurve
in selCurves
:
379 curve
= curves
.Curve(blCurve
)
380 nrSplines
= curve
.nrSplines
382 nrRemovedSplines
= curve
.RemoveShortSplines(threshold
)
383 if nrRemovedSplines
> 0: curve
.RebuildInScene()
385 self
.report({'INFO'}, "Removed %d of %d splines" % (nrRemovedSplines
, nrSplines
))
389 # ------------------------------------------------------------
390 # OperatorSplinesJoinNeighbouring
392 class OperatorSplinesJoinNeighbouring(bpy
.types
.Operator
):
393 bl_idname
= "curvetools.operatorsplinesjoinneighbouring"
394 bl_label
= "SplinesJoinNeighbouring"
395 bl_description
= "Joins neighbouring splines within a distance smaller than the threshold"
399 def poll(cls
, context
):
400 return util
.Selected1OrMoreCurves()
403 def execute(self
, context
):
404 selCurves
= util
.GetSelectedCurves()
406 for blCurve
in selCurves
:
407 curve
= curves
.Curve(blCurve
)
408 nrSplines
= curve
.nrSplines
410 threshold
= context
.scene
.curvetools
.SplineJoinDistance
411 startEnd
= context
.scene
.curvetools
.SplineJoinStartEnd
412 mode
= context
.scene
.curvetools
.SplineJoinMode
414 nrJoins
= curve
.JoinNeighbouringSplines(startEnd
, threshold
, mode
)
415 if nrJoins
> 0: curve
.RebuildInScene()
417 self
.report({'INFO'}, "Applied %d joins on %d splines; resulting nrSplines: %d" % (nrJoins
, nrSplines
, curve
.nrSplines
))
421 # ------------------------------------------------------------
424 def SurfaceFromBezier(surfacedata
, points
, center
):
426 len_points
= len(points
) - 1
428 if len_points
% 2 == 0:
429 h
= mathematics
.subdivide_cubic_bezier(
430 points
[len_points
].co
, points
[len_points
].handle_right
,
431 points
[0].handle_left
, points
[0].co
, 0.5
434 len_points
= len(points
) - 1
435 points
[len_points
- 1].handle_right
= h
[0]
436 points
[len_points
].handle_left
= h
[1]
437 points
[len_points
].co
= h
[2]
438 points
[len_points
].handle_right
= h
[3]
439 points
[0].handle_left
= h
[4]
441 half
= round((len_points
+ 1)/2) - 1
443 surfacespline1
= surfacedata
.splines
.new(type='NURBS')
444 surfacespline1
.points
.add(3)
445 surfacespline1
.points
[0].co
= [points
[0].co
.x
, points
[0].co
.y
, points
[0].co
.z
, 1]
446 surfacespline1
.points
[1].co
= [points
[0].handle_left
.x
, points
[0].handle_left
.y
, points
[0].handle_left
.z
, 1]
447 surfacespline1
.points
[2].co
= [points
[len_points
].handle_right
.x
,points
[len_points
].handle_right
.y
, points
[len_points
].handle_right
.z
, 1]
448 surfacespline1
.points
[3].co
= [points
[len_points
].co
.x
, points
[len_points
].co
.y
, points
[len_points
].co
.z
, 1]
449 for p
in surfacespline1
.points
:
451 surfacespline1
.use_endpoint_u
= True
452 surfacespline1
.use_endpoint_v
= True
454 for i
in range(0, half
):
458 surfacespline2
= surfacedata
.splines
.new(type='NURBS')
459 surfacespline2
.points
.add(3)
460 surfacespline2
.points
[0].co
= [points
[i
].co
.x
, points
[i
].co
.y
, points
[i
].co
.z
, 1]
461 surfacespline2
.points
[1].co
= [(points
[i
].co
.x
+ points
[len_points
- i
].co
.x
)/2,
462 (points
[i
].co
.y
+ points
[len_points
- i
].co
.y
)/2,
463 (points
[i
].co
.z
+ points
[len_points
- i
].co
.z
)/2, 1]
464 surfacespline2
.points
[2].co
= [(points
[len_points
- i
].co
.x
+ points
[i
].co
.x
)/2,
465 (points
[len_points
- i
].co
.y
+ points
[i
].co
.y
)/2,
466 (points
[len_points
- i
].co
.z
+ points
[i
].co
.z
)/2, 1]
467 surfacespline2
.points
[3].co
= [points
[len_points
- i
].co
.x
, points
[len_points
- i
].co
.y
, points
[len_points
- i
].co
.z
, 1]
468 for p
in surfacespline2
.points
:
470 surfacespline2
.use_endpoint_u
= True
471 surfacespline2
.use_endpoint_v
= True
474 surfacespline3
= surfacedata
.splines
.new(type='NURBS')
475 surfacespline3
.points
.add(3)
476 surfacespline3
.points
[0].co
= [points
[i
].handle_right
.x
, points
[i
].handle_right
.y
, points
[i
].handle_right
.z
, 1]
477 surfacespline3
.points
[1].co
= [(points
[i
].handle_right
.x
+ points
[len_points
- i
].handle_left
.x
)/2,
478 (points
[i
].handle_right
.y
+ points
[len_points
- i
].handle_left
.y
)/2,
479 (points
[i
].handle_right
.z
+ points
[len_points
- i
].handle_left
.z
)/2, 1]
480 surfacespline3
.points
[2].co
= [(points
[len_points
- i
].handle_left
.x
+ points
[i
].handle_right
.x
)/2,
481 (points
[len_points
- i
].handle_left
.y
+ points
[i
].handle_right
.y
)/2,
482 (points
[len_points
- i
].handle_left
.z
+ points
[i
].handle_right
.z
)/2, 1]
483 surfacespline3
.points
[3].co
= [points
[len_points
- i
].handle_left
.x
, points
[len_points
- i
].handle_left
.y
, points
[len_points
- i
].handle_left
.z
, 1]
484 for p
in surfacespline3
.points
:
486 surfacespline3
.use_endpoint_u
= True
487 surfacespline3
.use_endpoint_v
= True
490 surfacespline4
= surfacedata
.splines
.new(type='NURBS')
491 surfacespline4
.points
.add(3)
492 surfacespline4
.points
[0].co
= [points
[i
+ 1].handle_left
.x
, points
[i
+ 1].handle_left
.y
, points
[i
+ 1].handle_left
.z
, 1]
493 surfacespline4
.points
[1].co
= [(points
[i
+ 1].handle_left
.x
+ points
[len_points
- i
- 1].handle_right
.x
)/2,
494 (points
[i
+ 1].handle_left
.y
+ points
[len_points
- i
- 1].handle_right
.y
)/2,
495 (points
[i
+ 1].handle_left
.z
+ points
[len_points
- i
- 1].handle_right
.z
)/2, 1]
496 surfacespline4
.points
[2].co
= [(points
[len_points
- i
- 1].handle_right
.x
+ points
[i
+ 1].handle_left
.x
)/2,
497 (points
[len_points
- i
- 1].handle_right
.y
+ points
[i
+ 1].handle_left
.y
)/2,
498 (points
[len_points
- i
- 1].handle_right
.z
+ points
[i
+ 1].handle_left
.z
)/2, 1]
499 surfacespline4
.points
[3].co
= [points
[len_points
- i
- 1].handle_right
.x
, points
[len_points
- i
- 1].handle_right
.y
, points
[len_points
- i
- 1].handle_right
.z
, 1]
500 for p
in surfacespline4
.points
:
502 surfacespline4
.use_endpoint_u
= True
503 surfacespline4
.use_endpoint_v
= True
507 surfacespline5
= surfacedata
.splines
.new(type='NURBS')
508 surfacespline5
.points
.add(3)
509 surfacespline5
.points
[0].co
= [points
[i
+ 1].co
.x
, points
[i
+ 1].co
.y
, points
[i
+ 1].co
.z
, 1]
510 surfacespline5
.points
[1].co
= [(points
[i
+ 1].co
.x
+ points
[len_points
- i
- 1].co
.x
)/2,
511 (points
[i
+ 1].co
.y
+ points
[len_points
- i
- 1].co
.y
)/2,
512 (points
[i
+ 1].co
.z
+ points
[len_points
- i
- 1].co
.z
)/2, 1]
513 surfacespline5
.points
[2].co
= [(points
[len_points
- i
- 1].co
.x
+ points
[i
+ 1].co
.x
)/2,
514 (points
[len_points
- i
- 1].co
.y
+ points
[i
+ 1].co
.y
)/2,
515 (points
[len_points
- i
- 1].co
.z
+ points
[i
+ 1].co
.z
)/2, 1]
516 surfacespline5
.points
[3].co
= [points
[len_points
- i
- 1].co
.x
, points
[len_points
- i
- 1].co
.y
, points
[len_points
- i
- 1].co
.z
, 1]
517 for p
in surfacespline5
.points
:
519 surfacespline5
.use_endpoint_u
= True
520 surfacespline5
.use_endpoint_v
= True
523 surfacespline6
= surfacedata
.splines
.new(type='NURBS')
524 surfacespline6
.points
.add(3)
525 surfacespline6
.points
[0].co
= [points
[half
].co
.x
, points
[half
].co
.y
, points
[half
].co
.z
, 1]
526 surfacespline6
.points
[1].co
= [points
[half
].handle_right
.x
, points
[half
].handle_right
.y
, points
[half
].handle_right
.z
, 1]
527 surfacespline6
.points
[2].co
= [points
[half
+1].handle_left
.x
, points
[half
+1].handle_left
.y
, points
[half
+1].handle_left
.z
, 1]
528 surfacespline6
.points
[3].co
= [points
[half
+1].co
.x
, points
[half
+1].co
.y
, points
[half
+1].co
.z
, 1]
529 for p
in surfacespline6
.points
:
531 surfacespline6
.use_endpoint_u
= True
532 surfacespline6
.use_endpoint_v
= True
534 bpy
.ops
.object.mode_set(mode
= 'EDIT')
535 bpy
.ops
.curve
.make_segment()
537 for s
in surfacedata
.splines
:
545 # ------------------------------------------------------------
546 # Convert selected faces to Bezier
548 class ConvertSelectedFacesToBezier(bpy
.types
.Operator
):
549 bl_idname
= "curvetools.convert_selected_face_to_bezier"
550 bl_label
= "Convert selected faces to Bezier"
551 bl_description
= "Convert selected faces to Bezier"
552 bl_options
= {'REGISTER', 'UNDO'}
555 def poll(cls
, context
):
556 return util
.Selected1Mesh()
558 def execute(self
, context
):
560 bpy
.ops
.object.mode_set(mode
= 'OBJECT')
561 active_object
= context
.active_object
562 meshdata
= active_object
.data
563 curvedata
= bpy
.data
.curves
.new('Curve' + active_object
.name
, type='CURVE')
564 curveobject
= object_utils
.object_data_add(context
, curvedata
)
565 curvedata
.dimensions
= '3D'
567 for poly
in meshdata
.polygons
:
569 newSpline
= curvedata
.splines
.new(type='BEZIER')
570 newSpline
.use_cyclic_u
= True
571 newSpline
.bezier_points
.add(poly
.loop_total
- 1)
573 for loop_index
in range(poly
.loop_start
, poly
.loop_start
+ poly
.loop_total
):
574 newSpline
.bezier_points
[npoint
].co
= meshdata
.vertices
[meshdata
.loops
[loop_index
].vertex_index
].co
575 newSpline
.bezier_points
[npoint
].handle_left_type
= 'VECTOR'
576 newSpline
.bezier_points
[npoint
].handle_right_type
= 'VECTOR'
577 newSpline
.bezier_points
[npoint
].select_control_point
= True
578 newSpline
.bezier_points
[npoint
].select_left_handle
= True
579 newSpline
.bezier_points
[npoint
].select_right_handle
= True
584 # ------------------------------------------------------------
585 # Convert Bezier to Surface
587 class ConvertBezierToSurface(bpy
.types
.Operator
):
588 bl_idname
= "curvetools.convert_bezier_to_surface"
589 bl_label
= "Convert Bezier to Surface"
590 bl_description
= "Convert Bezier to Surface"
591 bl_options
= {'REGISTER', 'UNDO'}
593 Center
: BoolProperty(
596 description
="Consider center points"
599 Resolution_U
: IntProperty(
604 description
="Surface resolution U"
607 Resolution_V
: IntProperty(
612 description
="Surface resolution V"
615 def draw(self
, context
):
619 col
= layout
.column()
620 col
.prop(self
, 'Center')
621 col
.prop(self
, 'Resolution_U')
622 col
.prop(self
, 'Resolution_V')
625 def poll(cls
, context
):
626 return util
.Selected1OrMoreCurves()
628 def execute(self
, context
):
630 bpy
.ops
.object.mode_set(mode
= 'OBJECT')
631 active_object
= context
.active_object
632 curvedata
= active_object
.data
634 surfacedata
= bpy
.data
.curves
.new('Surface', type='SURFACE')
635 surfaceobject
= object_utils
.object_data_add(context
, surfacedata
)
636 surfaceobject
.matrix_world
= active_object
.matrix_world
637 surfaceobject
.rotation_euler
= active_object
.rotation_euler
638 surfacedata
.dimensions
= '3D'
639 surfaceobject
.show_wire
= True
640 surfaceobject
.show_in_front
= True
642 for spline
in curvedata
.splines
:
643 SurfaceFromBezier(surfacedata
, spline
.bezier_points
, self
.Center
)
645 for spline
in surfacedata
.splines
:
646 len_p
= len(spline
.points
)
647 len_devide_4
= round(len_p
/ 4) + 1
648 len_devide_2
= round(len_p
/ 2)
649 bpy
.ops
.object.mode_set(mode
= 'EDIT')
650 for point_index
in range(len_devide_4
, len_p
- len_devide_4
):
651 if point_index
!= len_devide_2
and point_index
!= len_devide_2
- 1:
652 spline
.points
[point_index
].select
= True
654 surfacedata
.resolution_u
= self
.Resolution_U
655 surfacedata
.resolution_v
= self
.Resolution_V
659 # ------------------------------------------------------------
662 class BezierPointsFillet(bpy
.types
.Operator
):
663 bl_idname
= "curvetools.bezier_points_fillet"
664 bl_label
= "Bezier points Fillet"
665 bl_description
= "Bezier points Fillet"
666 bl_options
= {'REGISTER', 'UNDO', 'PRESET'}
668 Fillet_radius
: FloatProperty(
674 Types
= [('Round', "Round", "Round"),
675 ('Chamfer', "Chamfer", "Chamfer")]
676 Fillet_Type
: EnumProperty(
678 description
="Fillet type",
682 def draw(self
, context
):
686 col
= layout
.column()
687 col
.prop(self
, "Fillet_radius")
688 col
.prop(self
, "Fillet_Type", expand
=True)
691 def poll(cls
, context
):
692 return util
.Selected1OrMoreCurves()
694 def execute(self
, context
):
696 if bpy
.ops
.object.mode_set
.poll():
697 bpy
.ops
.object.mode_set(mode
='EDIT')
699 splines
= bpy
.context
.object.data
.splines
700 bpy
.ops
.curve
.spline_type_set(type='BEZIER')
702 bpy
.ops
.curve
.handle_type_set(type='VECTOR')
704 for spline
in splines
:
707 for p
in spline
.bezier_points
:
708 if p
.select_control_point
:
716 for spline
in splines
:
718 bezier_points
= spline
.bezier_points
719 n
= len(bezier_points
)
725 bpy
.ops
.curve
.select_all(action
='DESELECT')
727 if j
!= 0 and j
!= n
- 1:
728 bezier_points
[j
].select_control_point
= True
729 bezier_points
[j
+ 1].select_control_point
= True
730 bpy
.ops
.curve
.subdivide()
731 selected4
= [bezier_points
[j
- 1], bezier_points
[j
],
732 bezier_points
[j
+ 1], bezier_points
[j
+ 2]]
737 bezier_points
[j
].select_control_point
= True
738 bezier_points
[j
+ 1].select_control_point
= True
739 bpy
.ops
.curve
.subdivide()
740 selected4
= [bezier_points
[n
], bezier_points
[0],
741 bezier_points
[1], bezier_points
[2]]
746 bezier_points
[j
].select_control_point
= True
747 bezier_points
[j
- 1].select_control_point
= True
748 bpy
.ops
.curve
.subdivide()
749 selected4
= [bezier_points
[0], bezier_points
[n
],
750 bezier_points
[n
- 1], bezier_points
[n
- 2]]
752 selected4
[2].co
= selected4
[1].co
753 s1
= Vector(selected4
[0].co
) - Vector(selected4
[1].co
)
754 s2
= Vector(selected4
[3].co
) - Vector(selected4
[2].co
)
756 s11
= Vector(selected4
[1].co
) + s1
* self
.Fillet_radius
757 selected4
[1].co
= s11
759 s22
= Vector(selected4
[2].co
) + s2
* self
.Fillet_radius
760 selected4
[2].co
= s22
762 if self
.Fillet_Type
== 'Round':
764 selected4
[2].handle_right_type
= 'VECTOR'
765 selected4
[1].handle_left_type
= 'VECTOR'
766 selected4
[1].handle_right_type
= 'ALIGNED'
767 selected4
[2].handle_left_type
= 'ALIGNED'
769 selected4
[1].handle_right_type
= 'VECTOR'
770 selected4
[2].handle_left_type
= 'VECTOR'
771 selected4
[2].handle_right_type
= 'ALIGNED'
772 selected4
[1].handle_left_type
= 'ALIGNED'
773 if self
.Fillet_Type
== 'Chamfer':
774 selected4
[2].handle_right_type
= 'VECTOR'
775 selected4
[1].handle_left_type
= 'VECTOR'
776 selected4
[1].handle_right_type
= 'VECTOR'
777 selected4
[2].handle_left_type
= 'VECTOR'
782 # ------------------------------------------------------------
783 # BezierDivide Operator
785 class BezierDivide(bpy
.types
.Operator
):
786 bl_idname
= "curvetools.bezier_spline_divide"
787 bl_label
= "Bezier Spline Divide"
788 bl_description
= "Bezier Divide (enters edit mode) for Fillet Curves"
789 bl_options
= {'REGISTER', 'UNDO'}
791 # align_matrix for the invoke
792 align_matrix
: Matrix()
794 Bezier_t
: FloatProperty(
795 name
="t (0% - 100%)",
797 min=0.0, soft_min
=0.0,
798 max=100.0, soft_max
=100.0,
799 description
="t (0% - 100%)"
803 def poll(cls
, context
):
804 return util
.Selected1OrMoreCurves()
806 def execute(self
, context
):
808 if bpy
.ops
.object.mode_set
.poll():
809 bpy
.ops
.object.mode_set(mode
='EDIT')
811 splines
= bpy
.context
.object.data
.splines
813 for spline
in splines
:
814 bpy
.ops
.curve
.spline_type_set(type='BEZIER')
818 for p
in spline
.bezier_points
:
819 if p
.select_control_point
:
827 for spline
in splines
:
829 bezier_points
= spline
.bezier_points
830 n
= len(bezier_points
)
835 bpy
.ops
.curve
.select_all(action
='DESELECT')
837 if (j
in ii
) and (j
+ 1 in ii
):
838 bezier_points
[j
+ jn
].select_control_point
= True
839 bezier_points
[j
+ 1 + jn
].select_control_point
= True
840 h
= mathematics
.subdivide_cubic_bezier(
841 bezier_points
[j
+ jn
].co
, bezier_points
[j
+ jn
].handle_right
,
842 bezier_points
[j
+ 1 + jn
].handle_left
, bezier_points
[j
+ 1 + jn
].co
, self
.Bezier_t
/ 100
844 bpy
.ops
.curve
.subdivide(1)
845 bezier_points
[j
+ jn
].handle_right_type
= 'FREE'
846 bezier_points
[j
+ jn
].handle_right
= h
[0]
847 bezier_points
[j
+ 1 + jn
].co
= h
[2]
848 bezier_points
[j
+ 1 + jn
].handle_left_type
= 'FREE'
849 bezier_points
[j
+ 1 + jn
].handle_left
= h
[1]
850 bezier_points
[j
+ 1 + jn
].handle_right_type
= 'FREE'
851 bezier_points
[j
+ 1 + jn
].handle_right
= h
[3]
852 bezier_points
[j
+ 2 + jn
].handle_left_type
= 'FREE'
853 bezier_points
[j
+ 2 + jn
].handle_left
= h
[4]
856 if j
== n
- 1 and (0 in ii
) and spline
.use_cyclic_u
:
857 bezier_points
[j
+ jn
].select_control_point
= True
858 bezier_points
[0].select_control_point
= True
859 h
= mathematics
.subdivide_cubic_bezier(
860 bezier_points
[j
+ jn
].co
, bezier_points
[j
+ jn
].handle_right
,
861 bezier_points
[0].handle_left
, bezier_points
[0].co
, self
.Bezier_t
/ 100
863 bpy
.ops
.curve
.subdivide(1)
864 bezier_points
[j
+ jn
].handle_right_type
= 'FREE'
865 bezier_points
[j
+ jn
].handle_right
= h
[0]
866 bezier_points
[j
+ 1 + jn
].co
= h
[2]
867 bezier_points
[j
+ 1 + jn
].handle_left_type
= 'FREE'
868 bezier_points
[j
+ 1 + jn
].handle_left
= h
[1]
869 bezier_points
[j
+ 1 + jn
].handle_right_type
= 'FREE'
870 bezier_points
[j
+ 1 + jn
].handle_right
= h
[3]
871 bezier_points
[0].handle_left_type
= 'FREE'
872 bezier_points
[0].handle_left
= h
[4]
878 # ------------------------------------------------------------
879 # CurveScaleReset Operator
881 class CurveScaleReset(bpy
.types
.Operator
):
882 bl_idname
= "curvetools.scale_reset"
883 bl_label
= "Curve Scale Reset"
884 bl_description
= "Curve Scale Reset"
885 bl_options
= {'REGISTER', 'UNDO'}
888 def poll(cls
, context
):
889 return (context
.object is not None and
890 context
.object.type == 'CURVE')
892 def execute(self
, context
):
894 current_mode
= bpy
.context
.object.mode
896 bpy
.ops
.object.mode_set(mode
= 'OBJECT')
898 oldCurve
= context
.active_object
899 oldCurveName
= oldCurve
.name
901 bpy
.ops
.object.duplicate_move(OBJECT_OT_duplicate
=None, TRANSFORM_OT_translate
=None)
902 newCurve
= context
.active_object
903 newCurve
.data
.splines
.clear()
904 newCurve
.scale
= (1.0, 1.0, 1.0)
906 oldCurve
.select_set(True)
907 newCurve
.select_set(True)
908 bpy
.context
.view_layer
.objects
.active
= newCurve
909 bpy
.ops
.object.join()
911 joinCurve
= context
.active_object
912 joinCurve
.name
= oldCurveName
914 bpy
.ops
.object.mode_set (mode
= current_mode
)
918 # ------------------------------------------------------------
921 class Split(bpy
.types
.Operator
):
922 bl_idname
= "curvetools.split"
924 bl_options
= {'REGISTER', 'UNDO'}
927 def poll(cls
, context
):
928 return util
.Selected1OrMoreCurves()
930 def execute(self
, context
):
931 selected_Curves
= util
.GetSelectedCurves()
933 for curve
in selected_Curves
:
936 bezier_spline_points
= []
937 select_bezier_points
= {}
940 for spline
in curve
.data
.splines
:
941 if spline
.type == 'BEZIER':
943 select_bezier_points
[i_bp
] = [len(spline
.bezier_points
)]
944 for i
in range(len(spline
.bezier_points
)):
945 bezier_point
= spline
.bezier_points
[i
]
946 points
[i
]=[bezier_point
.co
[:], bezier_point
.handle_left
[:], bezier_point
.handle_right
[:]]
948 if spline
.bezier_points
[i
].select_control_point
:
949 select_bezier_points
[i_bp
].append(i
)
951 bezier_spline_points
.append(points
)
954 select_points
[i_p
] = [len(spline
.points
)]
955 for i
in range(len(spline
.points
)):
956 point
= spline
.points
[i
]
957 points
[i
]=[point
.co
[:], spline
.type]
958 if spline
.points
[i
].select
:
959 select_points
[i_p
].append(i
)
961 spline_points
.append(points
)
963 curve
.data
.splines
.clear()
965 for key
in select_bezier_points
:
969 if select_bezier_points
[key
][-1] == select_bezier_points
[key
][0]-1:
970 select_bezier_points
[key
].pop()
972 for i
in select_bezier_points
[key
][1:]+[select_bezier_points
[key
][0]-1]:
974 spline
= curve
.data
.splines
.new('BEZIER')
975 spline
.bezier_points
.add(i
-num
)
977 for j
in range(num
, i
):
978 bezier_point
= spline
.bezier_points
[j
-num
]
980 bezier_point
.co
= bezier_spline_points
[key
][j
][0]
981 bezier_point
.handle_left
= bezier_spline_points
[key
][j
][1]
982 bezier_point
.handle_right
= bezier_spline_points
[key
][j
][2]
983 bezier_point
= spline
.bezier_points
[-1]
984 bezier_point
.co
= bezier_spline_points
[key
][i
][0]
985 bezier_point
.handle_left
= bezier_spline_points
[key
][i
][1]
986 bezier_point
.handle_right
= bezier_spline_points
[key
][i
][2]
989 for key
in select_points
:
993 if select_points
[key
][-1] == select_points
[key
][0]-1:
994 select_points
[key
].pop()
996 for i
in select_points
[key
][1:]+[select_points
[key
][0]-1]:
998 spline
= curve
.data
.splines
.new(spline_points
[key
][i
][1])
999 spline
.points
.add(i
-num
)
1001 for j
in range(num
, i
):
1002 point
= spline
.points
[j
-num
]
1004 point
.co
= spline_points
[key
][j
][0]
1005 point
= spline
.points
[-1]
1006 point
.co
= spline_points
[key
][i
][0]
1011 class SeparateOutline(bpy
.types
.Operator
):
1012 bl_idname
= "curvetools.sep_outline"
1013 bl_label
= "Separate Outline"
1014 bl_options
= {'REGISTER', 'UNDO'}
1015 bl_description
= "Makes 'Outline' separate mesh"
1018 def poll(cls
, context
):
1019 return util
.Selected1OrMoreCurves()
1021 def execute(self
, context
):
1022 bpy
.ops
.object.mode_set(mode
= 'EDIT')
1023 bpy
.ops
.curve
.separate()
1027 class CurveBoolean(bpy
.types
.Operator
):
1028 bl_idname
= "curvetools.bezier_curve_boolean"
1029 bl_description
= "Curve Boolean"
1030 bl_label
= "Curve Boolean"
1031 bl_options
= {'REGISTER', 'UNDO'}
1033 operation
: bpy
.props
.EnumProperty(name
='Type', items
=[
1034 ('UNION', 'Union', 'Boolean OR', 0),
1035 ('INTERSECTION', 'Intersection', 'Boolean AND', 1),
1036 ('DIFFERENCE', 'Difference', 'Active minus Selected', 2),
1038 number
: IntProperty(
1039 name
="Spline Number",
1042 description
="Spline Number"
1046 def poll(cls
, context
):
1047 return util
.Selected1OrMoreCurves()
1049 def draw(self
, context
):
1050 layout
= self
.layout
1053 col
= layout
.column()
1054 col
.prop(self
, "operation")
1055 if self
.operation
== 'DIFFERENCE':
1056 col
.prop(self
, "number")
1058 def execute(self
, context
):
1059 current_mode
= bpy
.context
.object.mode
1061 if bpy
.ops
.object.mode_set
.poll():
1062 bpy
.ops
.object.mode_set(mode
= 'OBJECT')
1064 selected_Curves
= util
.GetSelectedCurves()
1065 len_selected_curves
= len(selected_Curves
)
1066 if len_selected_curves
< 2:
1072 for iCurve
in range(0, len_selected_curves
):
1073 len_splines
= len(selected_Curves
[iCurve
].data
.splines
)
1074 max_number
+= len_splines
1076 if self
.number
< min_number
:
1077 self
.number
= min_number
1078 if self
.number
> max_number
:
1079 self
.number
= max_number
1084 for iCurve
in range(0, len_selected_curves
):
1085 len_splines
= len(selected_Curves
[iCurve
].data
.splines
)
1086 for iSpline
in range(0, len_splines
):
1087 if j
== self
.number
:
1088 first_curve
= iCurve
1089 first_spline
= iSpline
1092 bpy
.ops
.object.select_all(action
='DESELECT')
1094 spline1
= selected_Curves
[first_curve
].data
.splines
[first_spline
]
1095 matrix_world1
= selected_Curves
[first_curve
].matrix_world
1097 len_spline1
= len(spline1
.bezier_points
)
1099 dataCurve
= bpy
.data
.curves
.new(self
.operation
, type='CURVE')
1100 dataCurve
.dimensions
= '2D'
1101 newSpline1
= dataCurve
.splines
.new(type='BEZIER')
1102 newSpline1
.use_cyclic_u
= True
1103 newSpline1
.bezier_points
.add(len_spline1
- 1)
1104 for n
in range(0, len_spline1
):
1105 newSpline1
.bezier_points
[n
].co
= matrix_world1
@ spline1
.bezier_points
[n
].co
1106 newSpline1
.bezier_points
[n
].handle_left_type
= spline1
.bezier_points
[n
].handle_left_type
1107 newSpline1
.bezier_points
[n
].handle_left
= matrix_world1
@ spline1
.bezier_points
[n
].handle_left
1108 newSpline1
.bezier_points
[n
].handle_right_type
= spline1
.bezier_points
[n
].handle_right_type
1109 newSpline1
.bezier_points
[n
].handle_right
= matrix_world1
@ spline1
.bezier_points
[n
].handle_right
1111 Curve
= object_utils
.object_data_add(context
, dataCurve
)
1112 bpy
.context
.view_layer
.objects
.active
= Curve
1113 Curve
.select_set(True)
1114 Curve
.location
= (0.0, 0.0, 0.0)
1117 for iCurve
in range(0, len_selected_curves
):
1118 matrix_world
= selected_Curves
[iCurve
].matrix_world
1119 len_splines
= len(selected_Curves
[iCurve
].data
.splines
)
1120 for iSpline
in range(0, len_splines
):
1121 if iCurve
== first_curve
and iSpline
== first_spline
:
1123 spline
= selected_Curves
[iCurve
].data
.splines
[iSpline
]
1124 len_spline
= len(spline
.bezier_points
)
1125 newSpline
= dataCurve
.splines
.new(type='BEZIER')
1126 newSpline
.use_cyclic_u
= True
1127 newSpline
.bezier_points
.add(len_spline
- 1)
1128 for n
in range(0, len_spline
):
1129 newSpline
.bezier_points
[n
].co
= matrix_world
@ spline
.bezier_points
[n
].co
1130 newSpline
.bezier_points
[n
].handle_left_type
= spline
.bezier_points
[n
].handle_left_type
1131 newSpline
.bezier_points
[n
].handle_left
= matrix_world
@ spline
.bezier_points
[n
].handle_left
1132 newSpline
.bezier_points
[n
].handle_right_type
= spline
.bezier_points
[n
].handle_right_type
1133 newSpline
.bezier_points
[n
].handle_right
= matrix_world
@ spline
.bezier_points
[n
].handle_right
1135 bpy
.ops
.object.mode_set(mode
= 'EDIT')
1136 bpy
.ops
.curve
.select_all(action
='SELECT')
1137 splines
= internal
.getSelectedSplines(True, True)
1138 if len(splines
) < 2:
1140 splineA
= splines
[0]
1141 splineB
= splines
[1]
1142 dataCurve
.splines
.active
= newSpline1
1144 if not internal
.bezierBooleanGeometry(splineA
, splineB
, self
.operation
):
1145 self
.report({'WARNING'}, 'Invalid selection.')
1146 return {'CANCELLED'}
1150 bpy
.ops
.object.mode_set(mode
= 'EDIT')
1151 bpy
.ops
.curve
.select_all(action
='SELECT')
1155 # ----------------------------
1156 # Set first points operator
1157 class SetFirstPoints(bpy
.types
.Operator
):
1158 bl_idname
= "curvetools.set_first_points"
1159 bl_label
= "Set first points"
1160 bl_description
= "Set the selected points as the first point of each spline"
1161 bl_options
= {'REGISTER', 'UNDO'}
1164 def poll(cls
, context
):
1165 return util
.Selected1OrMoreCurves()
1167 def execute(self
, context
):
1168 splines_to_invert
= []
1170 curve
= bpy
.context
.object
1172 bpy
.ops
.object.mode_set('INVOKE_REGION_WIN', mode
='EDIT')
1174 # Check non-cyclic splines to invert
1175 for i
in range(len(curve
.data
.splines
)):
1176 b_points
= curve
.data
.splines
[i
].bezier_points
1178 if i
not in self
.cyclic_splines
: # Only for non-cyclic splines
1179 if b_points
[len(b_points
) - 1].select_control_point
:
1180 splines_to_invert
.append(i
)
1182 # Reorder points of cyclic splines, and set all handles to "Automatic"
1184 # Check first selected point
1185 cyclic_splines_new_first_pt
= {}
1186 for i
in self
.cyclic_splines
:
1187 sp
= curve
.data
.splines
[i
]
1189 for t
in range(len(sp
.bezier_points
)):
1190 bp
= sp
.bezier_points
[t
]
1191 if bp
.select_control_point
or bp
.select_right_handle
or bp
.select_left_handle
:
1192 cyclic_splines_new_first_pt
[i
] = t
1193 break # To take only one if there are more
1196 for spline_idx
in cyclic_splines_new_first_pt
:
1197 sp
= curve
.data
.splines
[spline_idx
]
1199 spline_old_coords
= []
1200 for bp_old
in sp
.bezier_points
:
1201 coords
= (bp_old
.co
[0], bp_old
.co
[1], bp_old
.co
[2])
1203 left_handle_type
= str(bp_old
.handle_left_type
)
1204 left_handle_length
= float(bp_old
.handle_left
.length
)
1206 float(bp_old
.handle_left
.x
),
1207 float(bp_old
.handle_left
.y
),
1208 float(bp_old
.handle_left
.z
)
1210 right_handle_type
= str(bp_old
.handle_right_type
)
1211 right_handle_length
= float(bp_old
.handle_right
.length
)
1212 right_handle_xyz
= (
1213 float(bp_old
.handle_right
.x
),
1214 float(bp_old
.handle_right
.y
),
1215 float(bp_old
.handle_right
.z
)
1217 spline_old_coords
.append(
1218 [coords
, left_handle_type
,
1219 right_handle_type
, left_handle_length
,
1220 right_handle_length
, left_handle_xyz
,
1224 for t
in range(len(sp
.bezier_points
)):
1225 bp
= sp
.bezier_points
1227 if t
+ cyclic_splines_new_first_pt
[spline_idx
] + 1 <= len(bp
) - 1:
1228 new_index
= t
+ cyclic_splines_new_first_pt
[spline_idx
] + 1
1230 new_index
= t
+ cyclic_splines_new_first_pt
[spline_idx
] + 1 - len(bp
)
1232 bp
[t
].co
= Vector(spline_old_coords
[new_index
][0])
1234 bp
[t
].handle_left
.length
= spline_old_coords
[new_index
][3]
1235 bp
[t
].handle_right
.length
= spline_old_coords
[new_index
][4]
1237 bp
[t
].handle_left_type
= "FREE"
1238 bp
[t
].handle_right_type
= "FREE"
1240 bp
[t
].handle_left
.x
= spline_old_coords
[new_index
][5][0]
1241 bp
[t
].handle_left
.y
= spline_old_coords
[new_index
][5][1]
1242 bp
[t
].handle_left
.z
= spline_old_coords
[new_index
][5][2]
1244 bp
[t
].handle_right
.x
= spline_old_coords
[new_index
][6][0]
1245 bp
[t
].handle_right
.y
= spline_old_coords
[new_index
][6][1]
1246 bp
[t
].handle_right
.z
= spline_old_coords
[new_index
][6][2]
1248 bp
[t
].handle_left_type
= spline_old_coords
[new_index
][1]
1249 bp
[t
].handle_right_type
= spline_old_coords
[new_index
][2]
1251 # Invert the non-cyclic splines designated above
1252 for i
in range(len(splines_to_invert
)):
1253 bpy
.ops
.curve
.select_all('INVOKE_REGION_WIN', action
='DESELECT')
1255 bpy
.ops
.object.editmode_toggle('INVOKE_REGION_WIN')
1256 curve
.data
.splines
[splines_to_invert
[i
]].bezier_points
[0].select_control_point
= True
1257 bpy
.ops
.object.editmode_toggle('INVOKE_REGION_WIN')
1259 bpy
.ops
.curve
.switch_direction()
1261 bpy
.ops
.curve
.select_all('INVOKE_REGION_WIN', action
='DESELECT')
1263 # Keep selected the first vert of each spline
1264 bpy
.ops
.object.editmode_toggle('INVOKE_REGION_WIN')
1265 for i
in range(len(curve
.data
.splines
)):
1266 if not curve
.data
.splines
[i
].use_cyclic_u
:
1267 bp
= curve
.data
.splines
[i
].bezier_points
[0]
1269 bp
= curve
.data
.splines
[i
].bezier_points
[
1270 len(curve
.data
.splines
[i
].bezier_points
) - 1
1273 bp
.select_control_point
= True
1274 bp
.select_right_handle
= True
1275 bp
.select_left_handle
= True
1277 bpy
.ops
.object.editmode_toggle('INVOKE_REGION_WIN')
1281 def invoke(self
, context
, event
):
1282 curve
= bpy
.context
.object
1284 # Check if all curves are Bezier, and detect which ones are cyclic
1285 self
.cyclic_splines
= []
1286 for i
in range(len(curve
.data
.splines
)):
1287 if curve
.data
.splines
[i
].type != "BEZIER":
1288 self
.report({'WARNING'}, "All splines must be Bezier type")
1290 return {'CANCELLED'}
1292 if curve
.data
.splines
[i
].use_cyclic_u
:
1293 self
.cyclic_splines
.append(i
)
1295 self
.execute(context
)
1296 self
.report({'INFO'}, "First points have been set")
1302 bpy
.utils
.register_class(operators
)
1306 bpy
.utils
.unregister_class(operators
)
1308 if __name__
== "__main__":
1313 OperatorCurveLength
,
1314 OperatorSplinesInfo
,
1315 OperatorSegmentsInfo
,
1316 OperatorOriginToSpline0Start
,
1317 OperatorIntersectCurves
,
1319 OperatorSweepCurves
,
1321 OperatorSplinesSetResolution
,
1322 OperatorSplinesRemoveZeroSegment
,
1323 OperatorSplinesRemoveShort
,
1324 OperatorSplinesJoinNeighbouring
,
1325 ConvertSelectedFacesToBezier
,
1326 ConvertBezierToSurface
,