5 from bpy
.props
import *
6 from bpy_extras
import object_utils
, view3d_utils
7 from mathutils
import *
10 from . import properties
12 from . import intersections
14 from . import surfaces
15 from . import mathematics
16 from . import internal
20 class OperatorCurveInfo(bpy
.types
.Operator
):
21 bl_idname
= "curvetools.operatorcurveinfo"
23 bl_description
= "Displays general info about the active/selected curve"
27 def poll(cls
, context
):
28 return util
.Selected1Curve()
31 def execute(self
, context
):
32 curve
= curves
.Curve(context
.active_object
)
34 nrSplines
= len(curve
.splines
)
37 for spline
in curve
.splines
:
38 nrSegments
+= spline
.nrSegments
39 if spline
.nrSegments
< 1: nrEmptySplines
+= 1
42 self
.report({'INFO'}, "nrSplines: %d; nrSegments: %d; nrEmptySplines: %d" % (nrSplines
, nrSegments
, nrEmptySplines
))
48 class OperatorCurveLength(bpy
.types
.Operator
):
49 bl_idname
= "curvetools.operatorcurvelength"
51 bl_description
= "Calculates the length of the active/selected curve"
55 def poll(cls
, context
):
56 return util
.Selected1Curve()
59 def execute(self
, context
):
60 curve
= curves
.Curve(context
.active_object
)
62 context
.scene
.curvetools
.CurveLength
= curve
.length
68 class OperatorSplinesInfo(bpy
.types
.Operator
):
69 bl_idname
= "curvetools.operatorsplinesinfo"
71 bl_description
= "Displays general info about the splines of the active/selected curve"
75 def poll(cls
, context
):
76 return util
.Selected1Curve()
79 def execute(self
, context
):
80 curve
= curves
.Curve(context
.active_object
)
81 nrSplines
= len(curve
.splines
)
84 print("OperatorSplinesInfo:", "nrSplines:", nrSplines
)
87 for iSpline
, spline
in enumerate(curve
.splines
):
88 print("--", "spline %d of %d: nrSegments: %d" % (iSpline
+ 1, nrSplines
, spline
.nrSegments
))
90 if spline
.nrSegments
< 1:
92 print("--", "--", "## WARNING: spline has no segments and will therefor be ignored in any further calculations")
95 self
.report({'INFO'}, "nrSplines: %d; nrEmptySplines: %d" % (nrSplines
, nrEmptySplines
) + " -- more info: see console")
101 class OperatorSegmentsInfo(bpy
.types
.Operator
):
102 bl_idname
= "curvetools.operatorsegmentsinfo"
104 bl_description
= "Displays general info about the segments of the active/selected curve"
108 def poll(cls
, context
):
109 return util
.Selected1Curve()
112 def execute(self
, context
):
113 curve
= curves
.Curve(context
.active_object
)
114 nrSplines
= len(curve
.splines
)
118 print("OperatorSegmentsInfo:", "nrSplines:", nrSplines
)
121 for iSpline
, spline
in enumerate(curve
.splines
):
122 nrSegmentsSpline
= spline
.nrSegments
123 print("--", "spline %d of %d: nrSegments: %d" % (iSpline
+ 1, nrSplines
, nrSegmentsSpline
))
125 if nrSegmentsSpline
< 1:
127 print("--", "--", "## WARNING: spline has no segments and will therefor be ignored in any further calculations")
130 for iSegment
, segment
in enumerate(spline
.segments
):
131 print("--", "--", "segment %d of %d coefficients:" % (iSegment
+ 1, nrSegmentsSpline
))
132 print("--", "--", "--", "C0: %.6f, %.6f, %.6f" % (segment
.coeff0
.x
, segment
.coeff0
.y
, segment
.coeff0
.z
))
134 nrSegments
+= nrSegmentsSpline
136 self
.report({'INFO'}, "nrSplines: %d; nrSegments: %d; nrEmptySplines: %d" % (nrSplines
, nrSegments
, nrEmptySplines
))
142 class OperatorOriginToSpline0Start(bpy
.types
.Operator
):
143 bl_idname
= "curvetools.operatororigintospline0start"
144 bl_label
= "OriginToSpline0Start"
145 bl_description
= "Sets the origin of the active/selected curve to the starting point of the (first) spline. Nice for curve modifiers"
149 def poll(cls
, context
):
150 return util
.Selected1Curve()
153 def execute(self
, context
):
156 blCurve
= context
.active_object
157 blSpline
= blCurve
.data
.splines
[0]
158 newOrigin
= blCurve
.matrix_world
@ blSpline
.bezier_points
[0].co
160 origOrigin
= bpy
.context
.scene
.cursor
.location
.copy()
161 self
.report({'INFO'}, "origOrigin: %.6f, %.6f, %.6f" % (origOrigin
.x
, origOrigin
.y
, origOrigin
.z
))
162 self
.report({'INFO'}, "newOrigin: %.6f, %.6f, %.6f" % (newOrigin
.x
, newOrigin
.y
, newOrigin
.z
))
164 current_mode
= bpy
.context
.object.mode
166 bpy
.ops
.object.mode_set(mode
= 'OBJECT')
167 bpy
.context
.scene
.cursor
.location
= newOrigin
168 bpy
.ops
.object.origin_set(type='ORIGIN_CURSOR')
169 bpy
.context
.scene
.cursor
.location
= origOrigin
171 bpy
.ops
.object.mode_set (mode
= current_mode
)
179 class OperatorIntersectCurves(bpy
.types
.Operator
):
180 bl_idname
= "curvetools.operatorintersectcurves"
181 bl_label
= "Intersect"
182 bl_description
= "Intersects selected curves"
186 def poll(cls
, context
):
187 return util
.Selected2OrMoreCurves()
190 def execute(self
, context
):
191 print("### TODO: OperatorIntersectcurves.execute()")
193 algo
= context
.scene
.curvetools
.IntersectCurvesAlgorithm
194 print("-- algo:", algo
)
197 mode
= context
.scene
.curvetools
.IntersectCurvesMode
198 print("-- mode:", mode
)
199 # if mode == 'Split':
200 # self.report({'WARNING'}, "'Split' mode is not implemented yet -- <<STOPPING>>")
201 # return {'CANCELLED'}
203 affect
= context
.scene
.curvetools
.IntersectCurvesAffect
204 print("-- affect:", affect
)
206 selected_objects
= context
.selected_objects
207 lenodjs
= len(selected_objects
)
208 print('lenodjs:', lenodjs
)
209 for i
in range(0, lenodjs
):
210 for j
in range(0, lenodjs
):
212 bpy
.ops
.object.select_all(action
='DESELECT')
213 selected_objects
[i
].select_set(True)
214 selected_objects
[j
].select_set(True)
216 if selected_objects
[i
].type == 'CURVE' and selected_objects
[j
].type == 'CURVE':
217 curveIntersector
= intersections
.CurvesIntersector
.FromSelection()
218 rvIntersectionNrs
= curveIntersector
.CalcAndApplyIntersections()
220 self
.report({'INFO'}, "Active curve points: %d; other curve points: %d" % (rvIntersectionNrs
[0], rvIntersectionNrs
[1]))
222 for obj
in selected_objects
:
227 # ------------------------------------------------------------
230 class OperatorLoftCurves(bpy
.types
.Operator
):
231 bl_idname
= "curvetools.operatorloftcurves"
233 bl_description
= "Lofts selected curves"
237 def poll(cls
, context
):
238 return util
.Selected2Curves()
241 def execute(self
, context
):
242 #print("### TODO: OperatorLoftcurves.execute()")
244 loftedSurface
= surfaces
.LoftedSurface
.FromSelection()
245 loftedSurface
.AddToScene()
247 self
.report({'INFO'}, "OperatorLoftcurves.execute()")
252 # ------------------------------------------------------------
253 # OperatorSweepCurves
255 class OperatorSweepCurves(bpy
.types
.Operator
):
256 bl_idname
= "curvetools.operatorsweepcurves"
258 bl_description
= "Sweeps the active curve along to other curve (rail)"
262 def poll(cls
, context
):
263 return util
.Selected2Curves()
266 def execute(self
, context
):
267 #print("### TODO: OperatorSweepcurves.execute()")
269 sweptSurface
= surfaces
.SweptSurface
.FromSelection()
270 sweptSurface
.AddToScene()
272 self
.report({'INFO'}, "OperatorSweepcurves.execute()")
280 class OperatorBirail(bpy
.types
.Operator
):
281 bl_idname
= "curvetools.operatorbirail"
283 bl_description
= "Generates a birailed surface from 3 selected curves -- in order: rail1, rail2 and profile"
287 def poll(cls
, context
):
288 return util
.Selected3Curves()
291 def execute(self
, context
):
292 birailedSurface
= surfaces
.BirailedSurface
.FromSelection()
293 birailedSurface
.AddToScene()
295 self
.report({'INFO'}, "OperatorBirail.execute()")
301 # 1 OR MORE CURVES SELECTED
302 # #########################
303 class OperatorSplinesSetResolution(bpy
.types
.Operator
):
304 bl_idname
= "curvetools.operatorsplinessetresolution"
305 bl_label
= "SplinesSetResolution"
306 bl_description
= "Sets the resolution of all splines"
310 def poll(cls
, context
):
311 return util
.Selected1OrMoreCurves()
314 def execute(self
, context
):
315 splRes
= context
.scene
.curvetools
.SplineResolution
316 selCurves
= util
.GetSelectedCurves()
318 for blCurve
in selCurves
:
319 for spline
in blCurve
.data
.splines
:
320 spline
.resolution_u
= splRes
324 # ------------------------------------------------------------
325 # OperatorSplinesRemoveZeroSegment
327 class OperatorSplinesRemoveZeroSegment(bpy
.types
.Operator
):
328 bl_idname
= "curvetools.operatorsplinesremovezerosegment"
329 bl_label
= "SplinesRemoveZeroSegment"
330 bl_description
= "Removes splines with no segments -- they seem to creep up, sometimes"
334 def poll(cls
, context
):
335 return util
.Selected1OrMoreCurves()
338 def execute(self
, context
):
339 selCurves
= util
.GetSelectedCurves()
341 for blCurve
in selCurves
:
342 curve
= curves
.Curve(blCurve
)
343 nrSplines
= curve
.nrSplines
346 for spline
in curve
.splines
:
347 if len(spline
.segments
) < 1: splinesToRemove
.append(spline
)
348 nrRemovedSplines
= len(splinesToRemove
)
350 for spline
in splinesToRemove
: curve
.splines
.remove(spline
)
352 if nrRemovedSplines
> 0: curve
.RebuildInScene()
354 self
.report({'INFO'}, "Removed %d of %d splines" % (nrRemovedSplines
, nrSplines
))
358 # ------------------------------------------------------------
359 # OperatorSplinesRemoveShort
361 class OperatorSplinesRemoveShort(bpy
.types
.Operator
):
362 bl_idname
= "curvetools.operatorsplinesremoveshort"
363 bl_label
= "SplinesRemoveShort"
364 bl_description
= "Removes splines with a length smaller than the threshold"
368 def poll(cls
, context
):
369 return util
.Selected1OrMoreCurves()
372 def execute(self
, context
):
373 threshold
= context
.scene
.curvetools
.SplineRemoveLength
374 selCurves
= util
.GetSelectedCurves()
376 for blCurve
in selCurves
:
377 curve
= curves
.Curve(blCurve
)
378 nrSplines
= curve
.nrSplines
380 nrRemovedSplines
= curve
.RemoveShortSplines(threshold
)
381 if nrRemovedSplines
> 0: curve
.RebuildInScene()
383 self
.report({'INFO'}, "Removed %d of %d splines" % (nrRemovedSplines
, nrSplines
))
387 # ------------------------------------------------------------
388 # OperatorSplinesJoinNeighbouring
390 class OperatorSplinesJoinNeighbouring(bpy
.types
.Operator
):
391 bl_idname
= "curvetools.operatorsplinesjoinneighbouring"
392 bl_label
= "SplinesJoinNeighbouring"
393 bl_description
= "Joins neighbouring splines within a distance smaller than the threshold"
397 def poll(cls
, context
):
398 return util
.Selected1OrMoreCurves()
401 def execute(self
, context
):
402 selCurves
= util
.GetSelectedCurves()
404 for blCurve
in selCurves
:
405 curve
= curves
.Curve(blCurve
)
406 nrSplines
= curve
.nrSplines
408 threshold
= context
.scene
.curvetools
.SplineJoinDistance
409 startEnd
= context
.scene
.curvetools
.SplineJoinStartEnd
410 mode
= context
.scene
.curvetools
.SplineJoinMode
412 nrJoins
= curve
.JoinNeighbouringSplines(startEnd
, threshold
, mode
)
413 if nrJoins
> 0: curve
.RebuildInScene()
415 self
.report({'INFO'}, "Applied %d joins on %d splines; resulting nrSplines: %d" % (nrJoins
, nrSplines
, curve
.nrSplines
))
419 # ------------------------------------------------------------
422 def SurfaceFromBezier(surfacedata
, points
, center
):
424 len_points
= len(points
) - 1
426 if len_points
% 2 == 0:
427 h
= mathematics
.subdivide_cubic_bezier(
428 points
[len_points
].co
, points
[len_points
].handle_right
,
429 points
[0].handle_left
, points
[0].co
, 0.5
432 len_points
= len(points
) - 1
433 points
[len_points
- 1].handle_right
= h
[0]
434 points
[len_points
].handle_left
= h
[1]
435 points
[len_points
].co
= h
[2]
436 points
[len_points
].handle_right
= h
[3]
437 points
[0].handle_left
= h
[4]
439 half
= round((len_points
+ 1)/2) - 1
441 surfacespline1
= surfacedata
.splines
.new(type='NURBS')
442 surfacespline1
.points
.add(3)
443 surfacespline1
.points
[0].co
= [points
[0].co
.x
, points
[0].co
.y
, points
[0].co
.z
, 1]
444 surfacespline1
.points
[1].co
= [points
[0].handle_left
.x
, points
[0].handle_left
.y
, points
[0].handle_left
.z
, 1]
445 surfacespline1
.points
[2].co
= [points
[len_points
].handle_right
.x
,points
[len_points
].handle_right
.y
, points
[len_points
].handle_right
.z
, 1]
446 surfacespline1
.points
[3].co
= [points
[len_points
].co
.x
, points
[len_points
].co
.y
, points
[len_points
].co
.z
, 1]
447 for p
in surfacespline1
.points
:
449 surfacespline1
.use_endpoint_u
= True
450 surfacespline1
.use_endpoint_v
= True
452 for i
in range(0, half
):
456 surfacespline2
= surfacedata
.splines
.new(type='NURBS')
457 surfacespline2
.points
.add(3)
458 surfacespline2
.points
[0].co
= [points
[i
].co
.x
, points
[i
].co
.y
, points
[i
].co
.z
, 1]
459 surfacespline2
.points
[1].co
= [(points
[i
].co
.x
+ points
[len_points
- i
].co
.x
)/2,
460 (points
[i
].co
.y
+ points
[len_points
- i
].co
.y
)/2,
461 (points
[i
].co
.z
+ points
[len_points
- i
].co
.z
)/2, 1]
462 surfacespline2
.points
[2].co
= [(points
[len_points
- i
].co
.x
+ points
[i
].co
.x
)/2,
463 (points
[len_points
- i
].co
.y
+ points
[i
].co
.y
)/2,
464 (points
[len_points
- i
].co
.z
+ points
[i
].co
.z
)/2, 1]
465 surfacespline2
.points
[3].co
= [points
[len_points
- i
].co
.x
, points
[len_points
- i
].co
.y
, points
[len_points
- i
].co
.z
, 1]
466 for p
in surfacespline2
.points
:
468 surfacespline2
.use_endpoint_u
= True
469 surfacespline2
.use_endpoint_v
= True
472 surfacespline3
= surfacedata
.splines
.new(type='NURBS')
473 surfacespline3
.points
.add(3)
474 surfacespline3
.points
[0].co
= [points
[i
].handle_right
.x
, points
[i
].handle_right
.y
, points
[i
].handle_right
.z
, 1]
475 surfacespline3
.points
[1].co
= [(points
[i
].handle_right
.x
+ points
[len_points
- i
].handle_left
.x
)/2,
476 (points
[i
].handle_right
.y
+ points
[len_points
- i
].handle_left
.y
)/2,
477 (points
[i
].handle_right
.z
+ points
[len_points
- i
].handle_left
.z
)/2, 1]
478 surfacespline3
.points
[2].co
= [(points
[len_points
- i
].handle_left
.x
+ points
[i
].handle_right
.x
)/2,
479 (points
[len_points
- i
].handle_left
.y
+ points
[i
].handle_right
.y
)/2,
480 (points
[len_points
- i
].handle_left
.z
+ points
[i
].handle_right
.z
)/2, 1]
481 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]
482 for p
in surfacespline3
.points
:
484 surfacespline3
.use_endpoint_u
= True
485 surfacespline3
.use_endpoint_v
= True
488 surfacespline4
= surfacedata
.splines
.new(type='NURBS')
489 surfacespline4
.points
.add(3)
490 surfacespline4
.points
[0].co
= [points
[i
+ 1].handle_left
.x
, points
[i
+ 1].handle_left
.y
, points
[i
+ 1].handle_left
.z
, 1]
491 surfacespline4
.points
[1].co
= [(points
[i
+ 1].handle_left
.x
+ points
[len_points
- i
- 1].handle_right
.x
)/2,
492 (points
[i
+ 1].handle_left
.y
+ points
[len_points
- i
- 1].handle_right
.y
)/2,
493 (points
[i
+ 1].handle_left
.z
+ points
[len_points
- i
- 1].handle_right
.z
)/2, 1]
494 surfacespline4
.points
[2].co
= [(points
[len_points
- i
- 1].handle_right
.x
+ points
[i
+ 1].handle_left
.x
)/2,
495 (points
[len_points
- i
- 1].handle_right
.y
+ points
[i
+ 1].handle_left
.y
)/2,
496 (points
[len_points
- i
- 1].handle_right
.z
+ points
[i
+ 1].handle_left
.z
)/2, 1]
497 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]
498 for p
in surfacespline4
.points
:
500 surfacespline4
.use_endpoint_u
= True
501 surfacespline4
.use_endpoint_v
= True
505 surfacespline5
= surfacedata
.splines
.new(type='NURBS')
506 surfacespline5
.points
.add(3)
507 surfacespline5
.points
[0].co
= [points
[i
+ 1].co
.x
, points
[i
+ 1].co
.y
, points
[i
+ 1].co
.z
, 1]
508 surfacespline5
.points
[1].co
= [(points
[i
+ 1].co
.x
+ points
[len_points
- i
- 1].co
.x
)/2,
509 (points
[i
+ 1].co
.y
+ points
[len_points
- i
- 1].co
.y
)/2,
510 (points
[i
+ 1].co
.z
+ points
[len_points
- i
- 1].co
.z
)/2, 1]
511 surfacespline5
.points
[2].co
= [(points
[len_points
- i
- 1].co
.x
+ points
[i
+ 1].co
.x
)/2,
512 (points
[len_points
- i
- 1].co
.y
+ points
[i
+ 1].co
.y
)/2,
513 (points
[len_points
- i
- 1].co
.z
+ points
[i
+ 1].co
.z
)/2, 1]
514 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]
515 for p
in surfacespline5
.points
:
517 surfacespline5
.use_endpoint_u
= True
518 surfacespline5
.use_endpoint_v
= True
521 surfacespline6
= surfacedata
.splines
.new(type='NURBS')
522 surfacespline6
.points
.add(3)
523 surfacespline6
.points
[0].co
= [points
[half
].co
.x
, points
[half
].co
.y
, points
[half
].co
.z
, 1]
524 surfacespline6
.points
[1].co
= [points
[half
].handle_right
.x
, points
[half
].handle_right
.y
, points
[half
].handle_right
.z
, 1]
525 surfacespline6
.points
[2].co
= [points
[half
+1].handle_left
.x
, points
[half
+1].handle_left
.y
, points
[half
+1].handle_left
.z
, 1]
526 surfacespline6
.points
[3].co
= [points
[half
+1].co
.x
, points
[half
+1].co
.y
, points
[half
+1].co
.z
, 1]
527 for p
in surfacespline6
.points
:
529 surfacespline6
.use_endpoint_u
= True
530 surfacespline6
.use_endpoint_v
= True
532 bpy
.ops
.object.mode_set(mode
= 'EDIT')
533 bpy
.ops
.curve
.make_segment()
535 for s
in surfacedata
.splines
:
543 # ------------------------------------------------------------
544 # Convert selected faces to Bezier
546 class ConvertSelectedFacesToBezier(bpy
.types
.Operator
):
547 bl_idname
= "curvetools.convert_selected_face_to_bezier"
548 bl_label
= "Convert selected faces to Bezier"
549 bl_description
= "Convert selected faces to Bezier"
550 bl_options
= {'REGISTER', 'UNDO'}
553 def poll(cls
, context
):
554 return util
.Selected1Mesh()
556 def execute(self
, context
):
558 bpy
.ops
.object.mode_set(mode
= 'OBJECT')
559 active_object
= context
.active_object
560 meshdata
= active_object
.data
561 curvedata
= bpy
.data
.curves
.new('Curve' + active_object
.name
, type='CURVE')
562 curveobject
= object_utils
.object_data_add(context
, curvedata
)
563 curvedata
.dimensions
= '3D'
565 for poly
in meshdata
.polygons
:
567 newSpline
= curvedata
.splines
.new(type='BEZIER')
568 newSpline
.use_cyclic_u
= True
569 newSpline
.bezier_points
.add(poly
.loop_total
- 1)
571 for loop_index
in range(poly
.loop_start
, poly
.loop_start
+ poly
.loop_total
):
572 newSpline
.bezier_points
[npoint
].co
= meshdata
.vertices
[meshdata
.loops
[loop_index
].vertex_index
].co
573 newSpline
.bezier_points
[npoint
].handle_left_type
= 'VECTOR'
574 newSpline
.bezier_points
[npoint
].handle_right_type
= 'VECTOR'
575 newSpline
.bezier_points
[npoint
].select_control_point
= True
576 newSpline
.bezier_points
[npoint
].select_left_handle
= True
577 newSpline
.bezier_points
[npoint
].select_right_handle
= True
582 # ------------------------------------------------------------
583 # Convert Bezier to Surface
585 class ConvertBezierToSurface(bpy
.types
.Operator
):
586 bl_idname
= "curvetools.convert_bezier_to_surface"
587 bl_label
= "Convert Bezier to Surface"
588 bl_description
= "Convert Bezier to Surface"
589 bl_options
= {'REGISTER', 'UNDO'}
591 Center
: BoolProperty(
594 description
="Consider center points"
597 Resolution_U
: IntProperty(
602 description
="Surface resolution U"
605 Resolution_V
: IntProperty(
610 description
="Surface resolution V"
613 def draw(self
, context
):
617 col
= layout
.column()
618 col
.prop(self
, 'Center')
619 col
.prop(self
, 'Resolution_U')
620 col
.prop(self
, 'Resolution_V')
623 def poll(cls
, context
):
624 return util
.Selected1OrMoreCurves()
626 def execute(self
, context
):
628 bpy
.ops
.object.mode_set(mode
= 'OBJECT')
629 active_object
= context
.active_object
630 curvedata
= active_object
.data
632 surfacedata
= bpy
.data
.curves
.new('Surface', type='SURFACE')
633 surfaceobject
= object_utils
.object_data_add(context
, surfacedata
)
634 surfaceobject
.matrix_world
= active_object
.matrix_world
635 surfaceobject
.rotation_euler
= active_object
.rotation_euler
636 surfacedata
.dimensions
= '3D'
637 surfaceobject
.show_wire
= True
638 surfaceobject
.show_in_front
= True
640 for spline
in curvedata
.splines
:
641 SurfaceFromBezier(surfacedata
, spline
.bezier_points
, self
.Center
)
643 for spline
in surfacedata
.splines
:
644 len_p
= len(spline
.points
)
645 len_devide_4
= round(len_p
/ 4) + 1
646 len_devide_2
= round(len_p
/ 2)
647 bpy
.ops
.object.mode_set(mode
= 'EDIT')
648 for point_index
in range(len_devide_4
, len_p
- len_devide_4
):
649 if point_index
!= len_devide_2
and point_index
!= len_devide_2
- 1:
650 spline
.points
[point_index
].select
= True
652 surfacedata
.resolution_u
= self
.Resolution_U
653 surfacedata
.resolution_v
= self
.Resolution_V
657 # ------------------------------------------------------------
660 class BezierPointsFillet(bpy
.types
.Operator
):
661 bl_idname
= "curvetools.bezier_points_fillet"
662 bl_label
= "Bezier points Fillet"
663 bl_description
= "Bezier points Fillet"
664 bl_options
= {'REGISTER', 'UNDO', 'PRESET'}
666 Fillet_radius
: FloatProperty(
672 Types
= [('Round', "Round", "Round"),
673 ('Chamfer', "Chamfer", "Chamfer")]
674 Fillet_Type
: EnumProperty(
676 description
="Fillet type",
680 def draw(self
, context
):
684 col
= layout
.column()
685 col
.prop(self
, "Fillet_radius")
686 col
.prop(self
, "Fillet_Type", expand
=True)
689 def poll(cls
, context
):
690 return util
.Selected1OrMoreCurves()
692 def execute(self
, context
):
694 if bpy
.ops
.object.mode_set
.poll():
695 bpy
.ops
.object.mode_set(mode
='EDIT')
697 splines
= bpy
.context
.object.data
.splines
698 bpy
.ops
.curve
.spline_type_set(type='BEZIER')
700 bpy
.ops
.curve
.handle_type_set(type='VECTOR')
702 for spline
in splines
:
705 for p
in spline
.bezier_points
:
706 if p
.select_control_point
:
714 for spline
in splines
:
716 bezier_points
= spline
.bezier_points
717 n
= len(bezier_points
)
723 bpy
.ops
.curve
.select_all(action
='DESELECT')
725 if j
!= 0 and j
!= n
- 1:
726 bezier_points
[j
].select_control_point
= True
727 bezier_points
[j
+ 1].select_control_point
= True
728 bpy
.ops
.curve
.subdivide()
729 selected4
= [bezier_points
[j
- 1], bezier_points
[j
],
730 bezier_points
[j
+ 1], bezier_points
[j
+ 2]]
735 bezier_points
[j
].select_control_point
= True
736 bezier_points
[j
+ 1].select_control_point
= True
737 bpy
.ops
.curve
.subdivide()
738 selected4
= [bezier_points
[n
], bezier_points
[0],
739 bezier_points
[1], bezier_points
[2]]
744 bezier_points
[j
].select_control_point
= True
745 bezier_points
[j
- 1].select_control_point
= True
746 bpy
.ops
.curve
.subdivide()
747 selected4
= [bezier_points
[0], bezier_points
[n
],
748 bezier_points
[n
- 1], bezier_points
[n
- 2]]
750 selected4
[2].co
= selected4
[1].co
751 s1
= Vector(selected4
[0].co
) - Vector(selected4
[1].co
)
752 s2
= Vector(selected4
[3].co
) - Vector(selected4
[2].co
)
754 s11
= Vector(selected4
[1].co
) + s1
* self
.Fillet_radius
755 selected4
[1].co
= s11
757 s22
= Vector(selected4
[2].co
) + s2
* self
.Fillet_radius
758 selected4
[2].co
= s22
760 if self
.Fillet_Type
== 'Round':
762 selected4
[2].handle_right_type
= 'VECTOR'
763 selected4
[1].handle_left_type
= 'VECTOR'
764 selected4
[1].handle_right_type
= 'ALIGNED'
765 selected4
[2].handle_left_type
= 'ALIGNED'
767 selected4
[1].handle_right_type
= 'VECTOR'
768 selected4
[2].handle_left_type
= 'VECTOR'
769 selected4
[2].handle_right_type
= 'ALIGNED'
770 selected4
[1].handle_left_type
= 'ALIGNED'
771 if self
.Fillet_Type
== 'Chamfer':
772 selected4
[2].handle_right_type
= 'VECTOR'
773 selected4
[1].handle_left_type
= 'VECTOR'
774 selected4
[1].handle_right_type
= 'VECTOR'
775 selected4
[2].handle_left_type
= 'VECTOR'
780 # ------------------------------------------------------------
781 # BezierDivide Operator
783 class BezierDivide(bpy
.types
.Operator
):
784 bl_idname
= "curvetools.bezier_spline_divide"
785 bl_label
= "Bezier Spline Divide"
786 bl_description
= "Bezier Divide (enters edit mode) for Fillet Curves"
787 bl_options
= {'REGISTER', 'UNDO'}
789 # align_matrix for the invoke
790 align_matrix
: Matrix()
792 Bezier_t
: FloatProperty(
793 name
="t (0% - 100%)",
795 min=0.0, soft_min
=0.0,
796 max=100.0, soft_max
=100.0,
797 description
="t (0% - 100%)"
801 def poll(cls
, context
):
802 return util
.Selected1OrMoreCurves()
804 def execute(self
, context
):
806 if bpy
.ops
.object.mode_set
.poll():
807 bpy
.ops
.object.mode_set(mode
='EDIT')
809 splines
= bpy
.context
.object.data
.splines
811 for spline
in splines
:
812 bpy
.ops
.curve
.spline_type_set(type='BEZIER')
816 for p
in spline
.bezier_points
:
817 if p
.select_control_point
:
825 for spline
in splines
:
827 bezier_points
= spline
.bezier_points
828 n
= len(bezier_points
)
833 bpy
.ops
.curve
.select_all(action
='DESELECT')
835 if (j
in ii
) and (j
+ 1 in ii
):
836 bezier_points
[j
+ jn
].select_control_point
= True
837 bezier_points
[j
+ 1 + jn
].select_control_point
= True
838 h
= mathematics
.subdivide_cubic_bezier(
839 bezier_points
[j
+ jn
].co
, bezier_points
[j
+ jn
].handle_right
,
840 bezier_points
[j
+ 1 + jn
].handle_left
, bezier_points
[j
+ 1 + jn
].co
, self
.Bezier_t
/ 100
842 bpy
.ops
.curve
.subdivide(1)
843 bezier_points
[j
+ jn
].handle_right_type
= 'FREE'
844 bezier_points
[j
+ jn
].handle_right
= h
[0]
845 bezier_points
[j
+ 1 + jn
].co
= h
[2]
846 bezier_points
[j
+ 1 + jn
].handle_left_type
= 'FREE'
847 bezier_points
[j
+ 1 + jn
].handle_left
= h
[1]
848 bezier_points
[j
+ 1 + jn
].handle_right_type
= 'FREE'
849 bezier_points
[j
+ 1 + jn
].handle_right
= h
[3]
850 bezier_points
[j
+ 2 + jn
].handle_left_type
= 'FREE'
851 bezier_points
[j
+ 2 + jn
].handle_left
= h
[4]
854 if j
== n
- 1 and (0 in ii
) and spline
.use_cyclic_u
:
855 bezier_points
[j
+ jn
].select_control_point
= True
856 bezier_points
[0].select_control_point
= True
857 h
= mathematics
.subdivide_cubic_bezier(
858 bezier_points
[j
+ jn
].co
, bezier_points
[j
+ jn
].handle_right
,
859 bezier_points
[0].handle_left
, bezier_points
[0].co
, self
.Bezier_t
/ 100
861 bpy
.ops
.curve
.subdivide(1)
862 bezier_points
[j
+ jn
].handle_right_type
= 'FREE'
863 bezier_points
[j
+ jn
].handle_right
= h
[0]
864 bezier_points
[j
+ 1 + jn
].co
= h
[2]
865 bezier_points
[j
+ 1 + jn
].handle_left_type
= 'FREE'
866 bezier_points
[j
+ 1 + jn
].handle_left
= h
[1]
867 bezier_points
[j
+ 1 + jn
].handle_right_type
= 'FREE'
868 bezier_points
[j
+ 1 + jn
].handle_right
= h
[3]
869 bezier_points
[0].handle_left_type
= 'FREE'
870 bezier_points
[0].handle_left
= h
[4]
876 # ------------------------------------------------------------
877 # CurveScaleReset Operator
879 class CurveScaleReset(bpy
.types
.Operator
):
880 bl_idname
= "curvetools.scale_reset"
881 bl_label
= "Curve Scale Reset"
882 bl_description
= "Curve Scale Reset"
883 bl_options
= {'REGISTER', 'UNDO'}
886 def poll(cls
, context
):
887 return (context
.object is not None and
888 context
.object.type == 'CURVE')
890 def execute(self
, context
):
892 current_mode
= bpy
.context
.object.mode
894 bpy
.ops
.object.mode_set(mode
= 'OBJECT')
896 oldCurve
= context
.active_object
897 oldCurveName
= oldCurve
.name
899 bpy
.ops
.object.duplicate_move(OBJECT_OT_duplicate
=None, TRANSFORM_OT_translate
=None)
900 newCurve
= context
.active_object
901 newCurve
.data
.splines
.clear()
902 newCurve
.scale
= (1.0, 1.0, 1.0)
904 oldCurve
.select_set(True)
905 newCurve
.select_set(True)
906 bpy
.context
.view_layer
.objects
.active
= newCurve
907 bpy
.ops
.object.join()
909 joinCurve
= context
.active_object
910 joinCurve
.name
= oldCurveName
912 bpy
.ops
.object.mode_set (mode
= current_mode
)
916 # ------------------------------------------------------------
919 class Split(bpy
.types
.Operator
):
920 bl_idname
= "curvetools.split"
922 bl_options
= {'REGISTER', 'UNDO'}
925 def poll(cls
, context
):
926 return util
.Selected1OrMoreCurves()
928 def execute(self
, context
):
929 selected_Curves
= util
.GetSelectedCurves()
931 for curve
in selected_Curves
:
934 bezier_spline_points
= []
935 select_bezier_points
= {}
938 for spline
in curve
.data
.splines
:
939 if spline
.type == 'BEZIER':
941 select_bezier_points
[i_bp
] = [len(spline
.bezier_points
)]
942 for i
in range(len(spline
.bezier_points
)):
943 bezier_point
= spline
.bezier_points
[i
]
944 points
[i
]=[bezier_point
.co
[:], bezier_point
.handle_left
[:], bezier_point
.handle_right
[:]]
946 if spline
.bezier_points
[i
].select_control_point
:
947 select_bezier_points
[i_bp
].append(i
)
949 bezier_spline_points
.append(points
)
952 select_points
[i_p
] = [len(spline
.points
)]
953 for i
in range(len(spline
.points
)):
954 point
= spline
.points
[i
]
955 points
[i
]=[point
.co
[:], spline
.type]
956 if spline
.points
[i
].select
:
957 select_points
[i_p
].append(i
)
959 spline_points
.append(points
)
961 curve
.data
.splines
.clear()
963 for key
in select_bezier_points
:
967 if select_bezier_points
[key
][-1] == select_bezier_points
[key
][0]-1:
968 select_bezier_points
[key
].pop()
970 for i
in select_bezier_points
[key
][1:]+[select_bezier_points
[key
][0]-1]:
972 spline
= curve
.data
.splines
.new('BEZIER')
973 spline
.bezier_points
.add(i
-num
)
975 for j
in range(num
, i
):
976 bezier_point
= spline
.bezier_points
[j
-num
]
978 bezier_point
.co
= bezier_spline_points
[key
][j
][0]
979 bezier_point
.handle_left
= bezier_spline_points
[key
][j
][1]
980 bezier_point
.handle_right
= bezier_spline_points
[key
][j
][2]
981 bezier_point
= spline
.bezier_points
[-1]
982 bezier_point
.co
= bezier_spline_points
[key
][i
][0]
983 bezier_point
.handle_left
= bezier_spline_points
[key
][i
][1]
984 bezier_point
.handle_right
= bezier_spline_points
[key
][i
][2]
987 for key
in select_points
:
991 if select_points
[key
][-1] == select_points
[key
][0]-1:
992 select_points
[key
].pop()
994 for i
in select_points
[key
][1:]+[select_points
[key
][0]-1]:
996 spline
= curve
.data
.splines
.new(spline_points
[key
][i
][1])
997 spline
.points
.add(i
-num
)
999 for j
in range(num
, i
):
1000 point
= spline
.points
[j
-num
]
1002 point
.co
= spline_points
[key
][j
][0]
1003 point
= spline
.points
[-1]
1004 point
.co
= spline_points
[key
][i
][0]
1009 class SeparateOutline(bpy
.types
.Operator
):
1010 bl_idname
= "curvetools.sep_outline"
1011 bl_label
= "Separate Outline"
1012 bl_options
= {'REGISTER', 'UNDO'}
1013 bl_description
= "Makes 'Outline' separate mesh"
1016 def poll(cls
, context
):
1017 return util
.Selected1OrMoreCurves()
1019 def execute(self
, context
):
1020 bpy
.ops
.object.mode_set(mode
= 'EDIT')
1021 bpy
.ops
.curve
.separate()
1025 class CurveBoolean(bpy
.types
.Operator
):
1026 bl_idname
= "curvetools.bezier_curve_boolean"
1027 bl_description
= "Curve Boolean"
1028 bl_label
= "Curve Boolean"
1029 bl_options
= {'REGISTER', 'UNDO'}
1031 operation
: bpy
.props
.EnumProperty(name
='Type', items
=[
1032 ('UNION', 'Union', 'Boolean OR', 0),
1033 ('INTERSECTION', 'Intersection', 'Boolean AND', 1),
1034 ('DIFFERENCE', 'Difference', 'Active minus Selected', 2),
1036 number
: IntProperty(
1037 name
="Spline Number",
1040 description
="Spline Number"
1044 def poll(cls
, context
):
1045 return util
.Selected1OrMoreCurves()
1047 def draw(self
, context
):
1048 layout
= self
.layout
1051 col
= layout
.column()
1052 col
.prop(self
, "operation")
1053 if self
.operation
== 'DIFFERENCE':
1054 col
.prop(self
, "number")
1056 def execute(self
, context
):
1057 current_mode
= bpy
.context
.object.mode
1059 if bpy
.ops
.object.mode_set
.poll():
1060 bpy
.ops
.object.mode_set(mode
= 'OBJECT')
1062 selected_Curves
= util
.GetSelectedCurves()
1063 len_selected_curves
= len(selected_Curves
)
1064 if len_selected_curves
< 2:
1070 for iCurve
in range(0, len_selected_curves
):
1071 len_splines
= len(selected_Curves
[iCurve
].data
.splines
)
1072 max_number
+= len_splines
1074 if self
.number
< min_number
:
1075 self
.number
= min_number
1076 if self
.number
> max_number
:
1077 self
.number
= max_number
1082 for iCurve
in range(0, len_selected_curves
):
1083 len_splines
= len(selected_Curves
[iCurve
].data
.splines
)
1084 for iSpline
in range(0, len_splines
):
1085 if j
== self
.number
:
1086 first_curve
= iCurve
1087 first_spline
= iSpline
1090 bpy
.ops
.object.select_all(action
='DESELECT')
1092 spline1
= selected_Curves
[first_curve
].data
.splines
[first_spline
]
1093 matrix_world1
= selected_Curves
[first_curve
].matrix_world
1095 len_spline1
= len(spline1
.bezier_points
)
1097 dataCurve
= bpy
.data
.curves
.new(self
.operation
, type='CURVE')
1098 dataCurve
.dimensions
= '2D'
1099 newSpline1
= dataCurve
.splines
.new(type='BEZIER')
1100 newSpline1
.use_cyclic_u
= True
1101 newSpline1
.bezier_points
.add(len_spline1
- 1)
1102 for n
in range(0, len_spline1
):
1103 newSpline1
.bezier_points
[n
].co
= matrix_world1
@ spline1
.bezier_points
[n
].co
1104 newSpline1
.bezier_points
[n
].handle_left_type
= spline1
.bezier_points
[n
].handle_left_type
1105 newSpline1
.bezier_points
[n
].handle_left
= matrix_world1
@ spline1
.bezier_points
[n
].handle_left
1106 newSpline1
.bezier_points
[n
].handle_right_type
= spline1
.bezier_points
[n
].handle_right_type
1107 newSpline1
.bezier_points
[n
].handle_right
= matrix_world1
@ spline1
.bezier_points
[n
].handle_right
1109 Curve
= object_utils
.object_data_add(context
, dataCurve
)
1110 bpy
.context
.view_layer
.objects
.active
= Curve
1111 Curve
.select_set(True)
1112 Curve
.location
= (0.0, 0.0, 0.0)
1115 for iCurve
in range(0, len_selected_curves
):
1116 matrix_world
= selected_Curves
[iCurve
].matrix_world
1117 len_splines
= len(selected_Curves
[iCurve
].data
.splines
)
1118 for iSpline
in range(0, len_splines
):
1119 if iCurve
== first_curve
and iSpline
== first_spline
:
1121 spline
= selected_Curves
[iCurve
].data
.splines
[iSpline
]
1122 len_spline
= len(spline
.bezier_points
)
1123 newSpline
= dataCurve
.splines
.new(type='BEZIER')
1124 newSpline
.use_cyclic_u
= True
1125 newSpline
.bezier_points
.add(len_spline
- 1)
1126 for n
in range(0, len_spline
):
1127 newSpline
.bezier_points
[n
].co
= matrix_world
@ spline
.bezier_points
[n
].co
1128 newSpline
.bezier_points
[n
].handle_left_type
= spline
.bezier_points
[n
].handle_left_type
1129 newSpline
.bezier_points
[n
].handle_left
= matrix_world
@ spline
.bezier_points
[n
].handle_left
1130 newSpline
.bezier_points
[n
].handle_right_type
= spline
.bezier_points
[n
].handle_right_type
1131 newSpline
.bezier_points
[n
].handle_right
= matrix_world
@ spline
.bezier_points
[n
].handle_right
1133 bpy
.ops
.object.mode_set(mode
= 'EDIT')
1134 bpy
.ops
.curve
.select_all(action
='SELECT')
1135 splines
= internal
.getSelectedSplines(True, True)
1136 if len(splines
) < 2:
1138 splineA
= splines
[0]
1139 splineB
= splines
[1]
1140 dataCurve
.splines
.active
= newSpline1
1142 if not internal
.bezierBooleanGeometry(splineA
, splineB
, self
.operation
):
1143 self
.report({'WARNING'}, 'Invalid selection.')
1144 return {'CANCELLED'}
1148 bpy
.ops
.object.mode_set(mode
= 'EDIT')
1149 bpy
.ops
.curve
.select_all(action
='SELECT')
1153 # ----------------------------
1154 # Set first points operator
1155 class SetFirstPoints(bpy
.types
.Operator
):
1156 bl_idname
= "curvetools.set_first_points"
1157 bl_label
= "Set first points"
1158 bl_description
= "Set the selected points as the first point of each spline"
1159 bl_options
= {'REGISTER', 'UNDO'}
1162 def poll(cls
, context
):
1163 return util
.Selected1OrMoreCurves()
1165 def execute(self
, context
):
1166 splines_to_invert
= []
1168 curve
= bpy
.context
.object
1170 bpy
.ops
.object.mode_set('INVOKE_REGION_WIN', mode
='EDIT')
1172 # Check non-cyclic splines to invert
1173 for i
in range(len(curve
.data
.splines
)):
1174 b_points
= curve
.data
.splines
[i
].bezier_points
1176 if i
not in self
.cyclic_splines
: # Only for non-cyclic splines
1177 if b_points
[len(b_points
) - 1].select_control_point
:
1178 splines_to_invert
.append(i
)
1180 # Reorder points of cyclic splines, and set all handles to "Automatic"
1182 # Check first selected point
1183 cyclic_splines_new_first_pt
= {}
1184 for i
in self
.cyclic_splines
:
1185 sp
= curve
.data
.splines
[i
]
1187 for t
in range(len(sp
.bezier_points
)):
1188 bp
= sp
.bezier_points
[t
]
1189 if bp
.select_control_point
or bp
.select_right_handle
or bp
.select_left_handle
:
1190 cyclic_splines_new_first_pt
[i
] = t
1191 break # To take only one if there are more
1194 for spline_idx
in cyclic_splines_new_first_pt
:
1195 sp
= curve
.data
.splines
[spline_idx
]
1197 spline_old_coords
= []
1198 for bp_old
in sp
.bezier_points
:
1199 coords
= (bp_old
.co
[0], bp_old
.co
[1], bp_old
.co
[2])
1201 left_handle_type
= str(bp_old
.handle_left_type
)
1202 left_handle_length
= float(bp_old
.handle_left
.length
)
1204 float(bp_old
.handle_left
.x
),
1205 float(bp_old
.handle_left
.y
),
1206 float(bp_old
.handle_left
.z
)
1208 right_handle_type
= str(bp_old
.handle_right_type
)
1209 right_handle_length
= float(bp_old
.handle_right
.length
)
1210 right_handle_xyz
= (
1211 float(bp_old
.handle_right
.x
),
1212 float(bp_old
.handle_right
.y
),
1213 float(bp_old
.handle_right
.z
)
1215 spline_old_coords
.append(
1216 [coords
, left_handle_type
,
1217 right_handle_type
, left_handle_length
,
1218 right_handle_length
, left_handle_xyz
,
1222 for t
in range(len(sp
.bezier_points
)):
1223 bp
= sp
.bezier_points
1225 if t
+ cyclic_splines_new_first_pt
[spline_idx
] + 1 <= len(bp
) - 1:
1226 new_index
= t
+ cyclic_splines_new_first_pt
[spline_idx
] + 1
1228 new_index
= t
+ cyclic_splines_new_first_pt
[spline_idx
] + 1 - len(bp
)
1230 bp
[t
].co
= Vector(spline_old_coords
[new_index
][0])
1232 bp
[t
].handle_left
.length
= spline_old_coords
[new_index
][3]
1233 bp
[t
].handle_right
.length
= spline_old_coords
[new_index
][4]
1235 bp
[t
].handle_left_type
= "FREE"
1236 bp
[t
].handle_right_type
= "FREE"
1238 bp
[t
].handle_left
.x
= spline_old_coords
[new_index
][5][0]
1239 bp
[t
].handle_left
.y
= spline_old_coords
[new_index
][5][1]
1240 bp
[t
].handle_left
.z
= spline_old_coords
[new_index
][5][2]
1242 bp
[t
].handle_right
.x
= spline_old_coords
[new_index
][6][0]
1243 bp
[t
].handle_right
.y
= spline_old_coords
[new_index
][6][1]
1244 bp
[t
].handle_right
.z
= spline_old_coords
[new_index
][6][2]
1246 bp
[t
].handle_left_type
= spline_old_coords
[new_index
][1]
1247 bp
[t
].handle_right_type
= spline_old_coords
[new_index
][2]
1249 # Invert the non-cyclic splines designated above
1250 for i
in range(len(splines_to_invert
)):
1251 bpy
.ops
.curve
.select_all('INVOKE_REGION_WIN', action
='DESELECT')
1253 bpy
.ops
.object.editmode_toggle('INVOKE_REGION_WIN')
1254 curve
.data
.splines
[splines_to_invert
[i
]].bezier_points
[0].select_control_point
= True
1255 bpy
.ops
.object.editmode_toggle('INVOKE_REGION_WIN')
1257 bpy
.ops
.curve
.switch_direction()
1259 bpy
.ops
.curve
.select_all('INVOKE_REGION_WIN', action
='DESELECT')
1261 # Keep selected the first vert of each spline
1262 bpy
.ops
.object.editmode_toggle('INVOKE_REGION_WIN')
1263 for i
in range(len(curve
.data
.splines
)):
1264 if not curve
.data
.splines
[i
].use_cyclic_u
:
1265 bp
= curve
.data
.splines
[i
].bezier_points
[0]
1267 bp
= curve
.data
.splines
[i
].bezier_points
[
1268 len(curve
.data
.splines
[i
].bezier_points
) - 1
1271 bp
.select_control_point
= True
1272 bp
.select_right_handle
= True
1273 bp
.select_left_handle
= True
1275 bpy
.ops
.object.editmode_toggle('INVOKE_REGION_WIN')
1279 def invoke(self
, context
, event
):
1280 curve
= bpy
.context
.object
1282 # Check if all curves are Bezier, and detect which ones are cyclic
1283 self
.cyclic_splines
= []
1284 for i
in range(len(curve
.data
.splines
)):
1285 if curve
.data
.splines
[i
].type != "BEZIER":
1286 self
.report({'WARNING'}, "All splines must be Bezier type")
1288 return {'CANCELLED'}
1290 if curve
.data
.splines
[i
].use_cyclic_u
:
1291 self
.cyclic_splines
.append(i
)
1293 self
.execute(context
)
1294 self
.report({'INFO'}, "First points have been set")
1300 bpy
.utils
.register_class(operators
)
1304 bpy
.utils
.unregister_class(operators
)
1306 if __name__
== "__main__":
1311 OperatorCurveLength
,
1312 OperatorSplinesInfo
,
1313 OperatorSegmentsInfo
,
1314 OperatorOriginToSpline0Start
,
1315 OperatorIntersectCurves
,
1317 OperatorSweepCurves
,
1319 OperatorSplinesSetResolution
,
1320 OperatorSplinesRemoveZeroSegment
,
1321 OperatorSplinesRemoveShort
,
1322 OperatorSplinesJoinNeighbouring
,
1323 ConvertSelectedFacesToBezier
,
1324 ConvertBezierToSurface
,