Cleanup: trailing space
[blender-addons.git] / curve_tools / __init__.py
blob4512ab93141ae461c26bfc558a4813aa95b18e65
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 # Contributed to by guy lateur, Alexander Meißner (Lichtso),
4 # Dealga McArdle (zeffii), Marvin.K.Breuer (MKB),
5 # Spivak Vladimir (cwolf3d)
6 # Originally an addon by Mackraken
9 bl_info = {
10 "name": "Curve Tools",
11 "description": "Adds some functionality for bezier/nurbs curve/surface modeling",
12 "author": "Mackraken",
13 "version": (0, 4, 5),
14 "blender": (2, 80, 0),
15 "location": "View3D > Tool Shelf > Edit Tab",
16 "warning": "WIP",
17 "doc_url": "{BLENDER_MANUAL_URL}/addons/add_curve/curve_tools.html",
18 "category": "Add Curve",
22 import os, bpy, importlib, math
23 from bpy.types import (
24 Operator,
25 Panel,
26 PropertyGroup,
28 from bpy.props import (
29 BoolProperty,
30 IntProperty,
31 FloatProperty,
32 EnumProperty,
33 CollectionProperty,
34 StringProperty,
35 FloatVectorProperty,
37 from . import properties, operators, auto_loft, outline, remove_doubles
38 from . import path_finder, show_resolution, splines_sequence, fillet
39 from . import internal, cad, toolpath, exports
41 if 'bpy' in locals():
42 importlib.reload(properties)
43 importlib.reload(operators)
44 importlib.reload(auto_loft)
45 importlib.reload(outline)
46 importlib.reload(remove_doubles)
47 importlib.reload(path_finder)
48 importlib.reload(show_resolution)
49 importlib.reload(splines_sequence)
50 importlib.reload(fillet)
51 importlib.reload(internal)
52 importlib.reload(cad)
53 importlib.reload(toolpath)
54 importlib.reload(exports)
56 from bpy.types import (
57 AddonPreferences,
61 def UpdateDummy(object, context):
62 scene = context.scene
63 SINGLEDROP = scene.UTSingleDrop
64 MOREDROP = scene.UTMOREDROP
65 LOFTDROP = scene.UTLoftDrop
66 ADVANCEDDROP = scene.UTAdvancedDrop
67 EXTENDEDDROP = scene.UTExtendedDrop
68 UTILSDROP = scene.UTUtilsDrop
71 class curvetoolsSettings(PropertyGroup):
72 # selection
73 SelectedObjects: CollectionProperty(
74 type=properties.curvetoolsSelectedObject
76 NrSelectedObjects: IntProperty(
77 name="NrSelectedObjects",
78 default=0,
79 description="Number of selected objects",
80 update=UpdateDummy
82 # curve
83 CurveLength: FloatProperty(
84 name="CurveLength",
85 default=0.0,
86 precision=6
88 # splines
89 SplineResolution: IntProperty(
90 name="SplineResolution",
91 default=64,
92 min=2, max=1024,
93 soft_min=2,
94 description="Spline resolution will be set to this value"
96 SplineRemoveLength: FloatProperty(
97 name="SplineRemoveLength",
98 default=0.001,
99 precision=6,
100 description="Splines shorter than this threshold length will be removed"
102 SplineJoinDistance: FloatProperty(
103 name="SplineJoinDistance",
104 default=0.001,
105 precision=6,
106 description="Splines with starting/ending points closer to each other "
107 "than this threshold distance will be joined"
109 SplineJoinStartEnd: BoolProperty(
110 name="SplineJoinStartEnd",
111 default=False,
112 description="Only join splines at the starting point of one and the ending point of the other"
114 splineJoinModeItems = (
115 ('At_midpoint', 'At midpoint', 'Join splines at midpoint of neighbouring points'),
116 ('Insert_segment', 'Insert segment', 'Insert segment between neighbouring points')
118 SplineJoinMode: EnumProperty(
119 items=splineJoinModeItems,
120 name="SplineJoinMode",
121 default='At_midpoint',
122 description="Determines how the splines will be joined"
124 # curve intersection
125 LimitDistance: FloatProperty(
126 name="LimitDistance",
127 default=0.0001,
128 precision=6,
129 description="Displays the result of the curve length calculation"
132 intAlgorithmItems = (
133 ('3D', '3D', 'Detect where curves intersect in 3D'),
134 ('From_View', 'From View', 'Detect where curves intersect in the RegionView3D')
136 IntersectCurvesAlgorithm: EnumProperty(
137 items=intAlgorithmItems,
138 name="IntersectCurvesAlgorithm",
139 description="Determines how the intersection points will be detected",
140 default='3D'
142 intModeItems = (
143 ('Insert', 'Insert', 'Insert points into the existing spline(s)'),
144 ('Split', 'Split', 'Split the existing spline(s) into 2'),
145 ('Empty', 'Empty', 'Add empty at intersections')
147 IntersectCurvesMode: EnumProperty(
148 items=intModeItems,
149 name="IntersectCurvesMode",
150 description="Determines what happens at the intersection points",
151 default='Split'
153 intAffectItems = (
154 ('Both', 'Both', 'Insert points into both curves'),
155 ('Active', 'Active', 'Insert points into active curve only'),
156 ('Other', 'Other', 'Insert points into other curve only')
158 IntersectCurvesAffect: EnumProperty(
159 items=intAffectItems,
160 name="IntersectCurvesAffect",
161 description="Determines which of the selected curves will be affected by the operation",
162 default='Both'
164 PathFinderRadius: FloatProperty(
165 name="PathFinder detection radius",
166 default=0.2,
167 precision=6,
168 description="PathFinder detection radius"
170 curve_vertcolor: FloatVectorProperty(
171 name="OUT",
172 default=(0.2, 0.9, 0.9, 1),
173 size=4,
174 subtype="COLOR",
175 min=0,
176 max=1
178 path_color: FloatVectorProperty(
179 name="OUT",
180 default=(0.2, 0.9, 0.9, 0.1),
181 size=4,
182 subtype="COLOR",
183 min=0,
184 max=1
186 path_thickness: IntProperty(
187 name="Path thickness",
188 default=10,
189 min=1, max=1024,
190 soft_min=2,
191 description="Path thickness (px)"
193 sequence_color: FloatVectorProperty(
194 name="OUT",
195 default=(0.2, 0.9, 0.9, 1),
196 size=4,
197 subtype="COLOR",
198 min=0,
199 max=1
201 font_thickness: IntProperty(
202 name="Font thickness",
203 default=2,
204 min=1, max=1024,
205 soft_min=2,
206 description="Font thickness (px)"
208 font_size: FloatProperty(
209 name="Font size",
210 default=0.1,
211 precision=3,
212 description="Font size"
216 # Curve Info
217 class VIEW3D_PT_curve_tools_info(Panel):
218 bl_space_type = "VIEW_3D"
219 bl_region_type = "UI"
220 bl_category = "Curve Edit"
221 bl_label = "Curve Info"
222 bl_options = {'DEFAULT_CLOSED'}
224 def draw(self, context):
225 scene = context.scene
226 layout = self.layout
228 col = layout.column(align=True)
229 col.operator("curvetools.operatorcurveinfo", text="Curve")
230 row = col.row(align=True)
231 row.operator("curvetools.operatorsplinesinfo", text="Spline")
232 row.operator("curvetools.operatorsegmentsinfo", text="Segment")
233 row = col.row(align=True)
234 row.operator("curvetools.operatorcurvelength", icon = "DRIVER_DISTANCE", text="Length")
235 row.prop(context.scene.curvetools, "CurveLength", text="")
237 # Curve Edit
238 class VIEW3D_PT_curve_tools_edit(Panel):
239 bl_space_type = "VIEW_3D"
240 bl_region_type = "UI"
241 bl_category = "Curve Edit"
242 bl_label = "Curve Edit"
245 def draw(self, context):
246 scene = context.scene
247 layout = self.layout
249 col = layout.column(align=True)
250 col.operator("curvetools.bezier_points_fillet", text='Fillet/Chamfer')
251 row = col.row(align=True)
252 row.operator("curvetools.outline", text="Outline")
253 row.operator("curvetools.add_toolpath_offset_curve", text="Recursive Offset")
254 col.operator("curvetools.sep_outline", text="Separate Offset/Selected")
255 col.operator("curvetools.bezier_cad_handle_projection", text='Extend Handles')
256 col.operator("curvetools.bezier_cad_boolean", text="Boolean Splines")
257 row = col.row(align=True)
258 row.operator("curvetools.bezier_spline_divide", text='Subdivide')
259 row.operator("curvetools.bezier_cad_subdivide", text="Multi Subdivide")
261 col.operator("curvetools.split", text='Split at Vertex')
262 col.operator("curvetools.add_toolpath_discretize_curve", text="Discretize Curve")
263 col.operator("curvetools.bezier_cad_array", text="Array Splines")
265 # Curve Intersect
266 class VIEW3D_PT_curve_tools_intersect(Panel):
267 bl_space_type = "VIEW_3D"
268 bl_region_type = "UI"
269 bl_category = "Curve Edit"
270 bl_label = "Intersect"
271 bl_options = {'DEFAULT_CLOSED'}
273 def draw(self, context):
274 scene = context.scene
275 layout = self.layout
277 col = layout.column(align=True)
278 col.operator("curvetools.bezier_curve_boolean", text="2D Curve Boolean")
279 col.operator("curvetools.operatorintersectcurves", text="Intersect Curves")
280 col.prop(context.scene.curvetools, "LimitDistance", text="Limit Distance")
281 col.prop(context.scene.curvetools, "IntersectCurvesAlgorithm", text="Algorithm")
282 col.prop(context.scene.curvetools, "IntersectCurvesMode", text="Mode")
283 col.prop(context.scene.curvetools, "IntersectCurvesAffect", text="Affect")
285 # Curve Surfaces
286 class VIEW3D_PT_curve_tools_surfaces(Panel):
287 bl_space_type = "VIEW_3D"
288 bl_region_type = "UI"
289 bl_category = "Curve Edit"
290 bl_label = "Surfaces"
291 bl_options = {'DEFAULT_CLOSED'}
293 def draw(self, context):
294 wm = context.window_manager
295 scene = context.scene
296 layout = self.layout
298 col = layout.column(align=True)
299 col.operator("curvetools.operatorbirail", text="Birail")
300 col.operator("curvetools.convert_bezier_to_surface", text="Convert Bezier to Surface")
301 col.operator("curvetools.convert_selected_face_to_bezier", text="Convert Faces to Bezier")
303 # Curve Path Finder
304 class VIEW3D_PT_curve_tools_loft(Panel):
305 bl_space_type = "VIEW_3D"
306 bl_region_type = "UI"
307 bl_category = "Curve Edit"
308 bl_parent_id = "VIEW3D_PT_curve_tools_surfaces"
309 bl_label = "Loft"
310 bl_options = {'DEFAULT_CLOSED'}
312 def draw(self, context):
313 wm = context.window_manager
314 scene = context.scene
315 layout = self.layout
317 col = layout.column(align=True)
318 col.operator("curvetools.create_auto_loft")
319 lofters = [o for o in scene.objects if "autoloft" in o.keys()]
320 for o in lofters:
321 col.label(text=o.name)
322 # layout.prop(o, '["autoloft"]', toggle=True)
323 col.prop(wm, "auto_loft", toggle=True)
324 col.operator("curvetools.update_auto_loft_curves")
325 col = layout.column(align=True)
328 # Curve Sanitize
329 class VIEW3D_PT_curve_tools_sanitize(Panel):
330 bl_space_type = "VIEW_3D"
331 bl_region_type = "UI"
332 bl_category = "Curve Edit"
333 bl_label = "Sanitize"
334 bl_options = {'DEFAULT_CLOSED'}
336 def draw(self, context):
337 scene = context.scene
338 layout = self.layout
340 col = layout.column(align=True)
341 col.operator("curvetools.operatororigintospline0start", icon = "OBJECT_ORIGIN", text="Set Origin to Spline Start")
342 col.operator("curvetools.scale_reset", text='Reset Scale')
344 col.label(text="Cleanup:")
345 col.operator("curvetools.remove_doubles", icon = "TRASH", text='Remove Doubles')
346 col.operator("curvetools.operatorsplinesremovezerosegment", icon = "TRASH", text="0-Segment Splines")
347 row = col.row(align=True)
348 row.operator("curvetools.operatorsplinesremoveshort", text="Short Splines")
349 row.prop(context.scene.curvetools, "SplineRemoveLength", text="Threshold remove")
351 col.label(text="Join Splines:")
352 col.operator("curvetools.operatorsplinesjoinneighbouring", text="Join Neighbouring Splines")
353 row = col.row(align=True)
354 col.prop(context.scene.curvetools, "SplineJoinDistance", text="Threshold")
355 col.prop(context.scene.curvetools, "SplineJoinStartEnd", text="Only at Ends")
356 col.prop(context.scene.curvetools, "SplineJoinMode", text="Join Position")
358 # Curve Utilities
359 class VIEW3D_PT_curve_tools_utilities(Panel):
360 bl_space_type = "VIEW_3D"
361 bl_region_type = "UI"
362 bl_category = "Curve Edit"
363 bl_label = "Utilities"
364 bl_options = {'DEFAULT_CLOSED'}
366 def draw(self, context):
367 scene = context.scene
368 layout = self.layout
370 col = layout.column(align=True)
371 row = col.row(align=True)
372 row.label(text="Curve Resolution:")
373 row = col.row(align=True)
374 row.operator("curvetools.show_resolution", icon="HIDE_OFF", text="Show [ESC]")
375 row.prop(context.scene.curvetools, "curve_vertcolor", text="")
376 row = col.row(align=True)
377 row.operator("curvetools.operatorsplinessetresolution", text="Set Resolution")
378 row.prop(context.scene.curvetools, "SplineResolution", text="")
381 row = col.row(align=True)
382 row.label(text="Spline Order:")
383 row = col.row(align=True)
384 row.operator("curvetools.show_splines_sequence", icon="HIDE_OFF", text="Show [ESC]")
385 row.prop(context.scene.curvetools, "sequence_color", text="")
386 row = col.row(align=True)
387 row.prop(context.scene.curvetools, "font_size", text="Font Size")
388 row.prop(context.scene.curvetools, "font_thickness", text="Font Thickness")
389 row = col.row(align=True)
390 oper = row.operator("curvetools.rearrange_spline", text = "<")
391 oper.command = 'PREV'
392 oper = row.operator("curvetools.rearrange_spline", text = ">")
393 oper.command = 'NEXT'
394 row = col.row(align=True)
395 row.operator("curve.switch_direction", text="Switch Direction")
396 row = col.row(align=True)
397 row.operator("curvetools.set_first_points", text="Set First Points")
399 # Curve Path Finder
400 class VIEW3D_PT_curve_tools_pathfinder(Panel):
401 bl_space_type = "VIEW_3D"
402 bl_region_type = "UI"
403 bl_category = "Curve Edit"
404 bl_parent_id = "VIEW3D_PT_curve_tools_utilities"
405 bl_label = "Path Finder"
406 bl_options = {'DEFAULT_CLOSED'}
408 def draw(self, context):
409 scene = context.scene
410 layout = self.layout
412 col = layout.column(align=True)
413 col.operator("curvetools.pathfinder", text="Path Finder [ESC]")
414 col.prop(context.scene.curvetools, "PathFinderRadius", text="PathFinder Radius")
415 col.prop(context.scene.curvetools, "path_color", text="")
416 col.prop(context.scene.curvetools, "path_thickness", text="Thickness")
418 col = layout.column(align=True)
419 col.label(text="ESC or TAB - Exit PathFinder")
420 col.label(text="X or DEL - Delete")
421 col.label(text="Alt + Mouse Click - Select Spline")
422 col.label(text="Alt + Shift + Mouse click - Add Spline to Selection")
423 col.label(text="A - Deselect All")
425 # Add-ons Preferences Update Panel
427 # Define Panel classes for updating
428 panels = (
429 VIEW3D_PT_curve_tools_info, VIEW3D_PT_curve_tools_edit,
430 VIEW3D_PT_curve_tools_intersect, VIEW3D_PT_curve_tools_surfaces,
431 VIEW3D_PT_curve_tools_loft, VIEW3D_PT_curve_tools_sanitize,
432 VIEW3D_PT_curve_tools_utilities, VIEW3D_PT_curve_tools_pathfinder
436 def update_panel(self, context):
437 message = "Curve Tools: Updating Panel locations has failed"
438 try:
439 for panel in panels:
440 if "bl_rna" in panel.__dict__:
441 bpy.utils.unregister_class(panel)
443 for panel in panels:
444 panel.bl_category = context.preferences.addons[__name__].preferences.category
445 bpy.utils.register_class(panel)
447 except Exception as e:
448 print("\n[{}]\n{}\n\nError:\n{}".format(__name__, message, e))
449 pass
452 class CurveAddonPreferences(AddonPreferences):
453 # this must match the addon name, use '__package__'
454 # when defining this in a submodule of a python package.
455 bl_idname = __name__
457 category: StringProperty(
458 name="Tab Category",
459 description="Choose a name for the category of the panel",
460 default="Edit",
461 update=update_panel
464 def draw(self, context):
465 layout = self.layout
467 row = layout.row()
468 col = row.column()
469 col.label(text="Tab Category:")
470 col.prop(self, "category", text="")
472 # Context MENU
473 def curve_tools_context_menu(self, context):
474 bl_label = 'Curve tools'
476 self.layout.operator("curvetools.bezier_points_fillet", text="Fillet")
477 self.layout.operator("curvetools.bezier_cad_handle_projection", text='Handle Projection')
478 self.layout.operator("curvetools.bezier_spline_divide", text="Divide")
479 self.layout.operator("curvetools.add_toolpath_offset_curve", text="Offset Curve")
480 self.layout.operator("curvetools.remove_doubles", text='Remove Doubles')
481 self.layout.separator()
483 def curve_tools_object_context_menu(self, context):
484 bl_label = 'Curve tools'
486 if context.active_object.type == "CURVE":
487 self.layout.operator("curvetools.scale_reset", text="Scale Reset")
488 self.layout.operator("curvetools.add_toolpath_offset_curve", text="Offset Curve")
489 self.layout.operator("curvetools.remove_doubles", text='Remove Doubles')
490 self.layout.separator()
492 # Import-export 2d svg
493 def menu_file_export(self, context):
494 for operator in exports.operators:
495 self.layout.operator(operator.bl_idname)
497 def menu_file_import(self, context):
498 for operator in imports.operators:
499 self.layout.operator(operator.bl_idname)
501 # REGISTER
502 classes = cad.operators + \
503 toolpath.operators + \
504 exports.operators + \
505 operators.operators + \
506 properties.operators + \
507 path_finder.operators + \
508 show_resolution.operators + \
509 splines_sequence.operators + \
510 outline.operators + \
511 fillet.operators + \
512 remove_doubles.operators + \
514 CurveAddonPreferences,
515 curvetoolsSettings,
518 def register():
519 bpy.types.Scene.UTSingleDrop = BoolProperty(
520 name="One Curve",
521 default=False,
522 description="One Curve"
524 bpy.types.Scene.UTMOREDROP = BoolProperty(
525 name="Curves",
526 default=False,
527 description="Curves"
529 bpy.types.Scene.UTLoftDrop = BoolProperty(
530 name="Two Curves Loft",
531 default=False,
532 description="Two Curves Loft"
534 bpy.types.Scene.UTAdvancedDrop = BoolProperty(
535 name="Advanced",
536 default=True,
537 description="Advanced"
539 bpy.types.Scene.UTExtendedDrop = BoolProperty(
540 name="Extended",
541 default=False,
542 description="Extended"
544 bpy.types.Scene.UTUtilsDrop = BoolProperty(
545 name="Curves Utils",
546 default=True,
547 description="Curves Utils"
550 for cls in classes:
551 bpy.utils.register_class(cls)
553 for panel in panels:
554 bpy.utils.register_class(panel)
556 auto_loft.register()
558 bpy.types.TOPBAR_MT_file_export.append(menu_file_export)
560 bpy.types.Scene.curvetools = bpy.props.PointerProperty(type=curvetoolsSettings)
562 update_panel(None, bpy.context)
564 bpy.types.VIEW3D_MT_edit_curve_context_menu.prepend(curve_tools_context_menu)
565 bpy.types.VIEW3D_MT_object_context_menu.prepend(curve_tools_object_context_menu)
568 def unregister():
569 del bpy.types.Scene.UTSingleDrop
570 del bpy.types.Scene.UTMOREDROP
571 del bpy.types.Scene.UTLoftDrop
572 del bpy.types.Scene.UTAdvancedDrop
573 del bpy.types.Scene.UTExtendedDrop
574 del bpy.types.Scene.UTUtilsDrop
576 auto_loft.unregister()
578 bpy.types.TOPBAR_MT_file_export.remove(menu_file_export)
580 bpy.types.VIEW3D_MT_edit_curve_context_menu.remove(curve_tools_context_menu)
581 bpy.types.VIEW3D_MT_object_context_menu.remove(curve_tools_object_context_menu)
583 for panel in panels:
584 bpy.utils.unregister_class(panel)
586 for cls in classes:
587 bpy.utils.unregister_class(cls)
590 if __name__ == "__main__":
591 register()