Import_3ds: Improved distance cue node setup
[blender-addons.git] / curve_tools / __init__.py
blob09b1c762ec0e2e91661411ffcae0e1a98fadeb30
1 # SPDX-FileCopyrightText: 2019-2023 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 bl_info = {
6 "name": "Curve Tools",
7 "description": "Adds some functionality for bezier/nurbs curve/surface modeling",
8 "author": "Mackraken, Spivak Vladimir (cwolf3d)",
9 "version": (0, 4, 6),
10 "blender": (2, 80, 0),
11 "location": "View3D > Tool Shelf > Edit Tab",
12 "doc_url": "{BLENDER_MANUAL_URL}/addons/add_curve/curve_tools.html",
13 "category": "Add Curve",
17 import os, bpy, importlib, math
18 from bpy.types import (
19 Operator,
20 Panel,
21 PropertyGroup,
23 from bpy.props import (
24 BoolProperty,
25 IntProperty,
26 FloatProperty,
27 EnumProperty,
28 CollectionProperty,
29 StringProperty,
30 FloatVectorProperty,
32 from . import properties, operators, auto_loft, outline, remove_doubles
33 from . import path_finder, show_resolution, splines_sequence, fillet
34 from . import internal, cad, toolpath, exports
36 if 'bpy' in locals():
37 importlib.reload(properties)
38 importlib.reload(operators)
39 importlib.reload(auto_loft)
40 importlib.reload(outline)
41 importlib.reload(remove_doubles)
42 importlib.reload(path_finder)
43 importlib.reload(show_resolution)
44 importlib.reload(splines_sequence)
45 importlib.reload(fillet)
46 importlib.reload(internal)
47 importlib.reload(cad)
48 importlib.reload(toolpath)
49 importlib.reload(exports)
51 from bpy.types import (
52 AddonPreferences,
56 def UpdateDummy(object, context):
57 scene = context.scene
58 SINGLEDROP = scene.UTSingleDrop
59 MOREDROP = scene.UTMOREDROP
60 LOFTDROP = scene.UTLoftDrop
61 ADVANCEDDROP = scene.UTAdvancedDrop
62 EXTENDEDDROP = scene.UTExtendedDrop
63 UTILSDROP = scene.UTUtilsDrop
66 class curvetoolsSettings(PropertyGroup):
67 # selection
68 SelectedObjects: CollectionProperty(
69 type=properties.curvetoolsSelectedObject
71 NrSelectedObjects: IntProperty(
72 name="NrSelectedObjects",
73 default=0,
74 description="Number of selected objects",
75 update=UpdateDummy
77 # curve
78 CurveLength: FloatProperty(
79 name="CurveLength",
80 default=0.0,
81 precision=6
83 # splines
84 SplineResolution: IntProperty(
85 name="SplineResolution",
86 default=64,
87 min=2, max=1024,
88 soft_min=2,
89 description="Spline resolution will be set to this value"
91 SplineRemoveLength: FloatProperty(
92 name="SplineRemoveLength",
93 default=0.001,
94 precision=6,
95 description="Splines shorter than this threshold length will be removed"
97 SplineJoinDistance: FloatProperty(
98 name="SplineJoinDistance",
99 default=0.001,
100 precision=6,
101 description="Splines with starting/ending points closer to each other "
102 "than this threshold distance will be joined"
104 SplineJoinStartEnd: BoolProperty(
105 name="SplineJoinStartEnd",
106 default=False,
107 description="Only join splines at the starting point of one and the ending point of the other"
109 splineJoinModeItems = (
110 ('At_midpoint', 'At midpoint', 'Join splines at midpoint of neighbouring points'),
111 ('Insert_segment', 'Insert segment', 'Insert segment between neighbouring points')
113 SplineJoinMode: EnumProperty(
114 items=splineJoinModeItems,
115 name="SplineJoinMode",
116 default='At_midpoint',
117 description="Determines how the splines will be joined"
119 # curve intersection
120 LimitDistance: FloatProperty(
121 name="LimitDistance",
122 default=0.0001,
123 precision=6,
124 description="Displays the result of the curve length calculation"
127 intAlgorithmItems = (
128 ('3D', '3D', 'Detect where curves intersect in 3D'),
129 ('From_View', 'From View', 'Detect where curves intersect in the RegionView3D')
131 IntersectCurvesAlgorithm: EnumProperty(
132 items=intAlgorithmItems,
133 name="IntersectCurvesAlgorithm",
134 description="Determines how the intersection points will be detected",
135 default='3D'
137 intModeItems = (
138 ('Insert', 'Insert', 'Insert points into the existing spline(s)'),
139 ('Split', 'Split', 'Split the existing spline(s) into 2'),
140 ('Empty', 'Empty', 'Add empty at intersections')
142 IntersectCurvesMode: EnumProperty(
143 items=intModeItems,
144 name="IntersectCurvesMode",
145 description="Determines what happens at the intersection points",
146 default='Split'
148 intAffectItems = (
149 ('Both', 'Both', 'Insert points into both curves'),
150 ('Active', 'Active', 'Insert points into active curve only'),
151 ('Other', 'Other', 'Insert points into other curve only')
153 IntersectCurvesAffect: EnumProperty(
154 items=intAffectItems,
155 name="IntersectCurvesAffect",
156 description="Determines which of the selected curves will be affected by the operation",
157 default='Both'
159 PathFinderRadius: FloatProperty(
160 name="PathFinder detection radius",
161 default=0.2,
162 precision=6,
163 description="PathFinder detection radius"
165 curve_vertcolor: FloatVectorProperty(
166 name="OUT",
167 default=(0.2, 0.9, 0.9, 1),
168 size=4,
169 subtype="COLOR",
170 min=0,
171 max=1
173 path_color: FloatVectorProperty(
174 name="OUT",
175 default=(0.2, 0.9, 0.9, 0.1),
176 size=4,
177 subtype="COLOR",
178 min=0,
179 max=1
181 path_thickness: IntProperty(
182 name="Path thickness",
183 default=10,
184 min=1, max=1024,
185 soft_min=2,
186 description="Path thickness (px)"
188 sequence_color: FloatVectorProperty(
189 name="OUT",
190 default=(0.2, 0.9, 0.9, 1),
191 size=4,
192 subtype="COLOR",
193 min=0,
194 max=1
196 font_thickness: IntProperty(
197 name="Font thickness",
198 default=2,
199 min=1, max=1024,
200 soft_min=2,
201 description="Font thickness (px)"
203 font_size: FloatProperty(
204 name="Font size",
205 default=0.1,
206 precision=3,
207 description="Font size"
211 # Curve Info
212 class VIEW3D_PT_curve_tools_info(Panel):
213 bl_space_type = "VIEW_3D"
214 bl_region_type = "UI"
215 bl_category = "Curve Edit"
216 bl_label = "Curve Info"
217 bl_options = {'DEFAULT_CLOSED'}
219 def draw(self, context):
220 scene = context.scene
221 layout = self.layout
223 col = layout.column(align=True)
224 col.operator("curvetools.operatorcurveinfo", text="Curve")
225 row = col.row(align=True)
226 row.operator("curvetools.operatorsplinesinfo", text="Spline")
227 row.operator("curvetools.operatorsegmentsinfo", text="Segment")
228 row = col.row(align=True)
229 row.operator("curvetools.operatorcurvelength", icon = "DRIVER_DISTANCE", text="Length")
230 row.prop(context.scene.curvetools, "CurveLength", text="")
232 # Curve Edit
233 class VIEW3D_PT_curve_tools_edit(Panel):
234 bl_space_type = "VIEW_3D"
235 bl_region_type = "UI"
236 bl_category = "Curve Edit"
237 bl_label = "Curve Edit"
240 def draw(self, context):
241 scene = context.scene
242 layout = self.layout
244 col = layout.column(align=True)
245 col.operator("curvetools.bezier_points_fillet", text='Fillet/Chamfer')
246 row = col.row(align=True)
247 row.operator("curvetools.outline", text="Outline")
248 row.operator("curvetools.add_toolpath_offset_curve", text="Recursive Offset")
249 col.operator("curvetools.sep_outline", text="Separate Offset/Selected")
250 col.operator("curvetools.bezier_cad_handle_projection", text='Extend Handles')
251 col.operator("curvetools.bezier_cad_boolean", text="Boolean Splines")
252 row = col.row(align=True)
253 row.operator("curvetools.bezier_spline_divide", text='Subdivide')
254 row.operator("curvetools.bezier_cad_subdivide", text="Multi Subdivide")
256 col.operator("curvetools.split", text='Split at Vertex')
257 col.operator("curvetools.add_toolpath_discretize_curve", text="Discretize Curve")
258 col.operator("curvetools.bezier_cad_array", text="Array Splines")
260 # Curve Intersect
261 class VIEW3D_PT_curve_tools_intersect(Panel):
262 bl_space_type = "VIEW_3D"
263 bl_region_type = "UI"
264 bl_category = "Curve Edit"
265 bl_label = "Intersect"
266 bl_options = {'DEFAULT_CLOSED'}
268 def draw(self, context):
269 scene = context.scene
270 layout = self.layout
272 col = layout.column(align=True)
273 col.operator("curvetools.bezier_curve_boolean", text="2D Curve Boolean")
274 col.operator("curvetools.operatorintersectcurves", text="Intersect Curves")
275 col.prop(context.scene.curvetools, "LimitDistance", text="Limit Distance")
276 col.prop(context.scene.curvetools, "IntersectCurvesAlgorithm", text="Algorithm")
277 col.prop(context.scene.curvetools, "IntersectCurvesMode", text="Mode")
278 col.prop(context.scene.curvetools, "IntersectCurvesAffect", text="Affect")
280 # Curve Surfaces
281 class VIEW3D_PT_curve_tools_surfaces(Panel):
282 bl_space_type = "VIEW_3D"
283 bl_region_type = "UI"
284 bl_category = "Curve Edit"
285 bl_label = "Surfaces"
286 bl_options = {'DEFAULT_CLOSED'}
288 def draw(self, context):
289 wm = context.window_manager
290 scene = context.scene
291 layout = self.layout
293 col = layout.column(align=True)
294 col.operator("curvetools.operatorbirail", text="Birail")
295 col.operator("curvetools.convert_bezier_to_surface", text="Convert Bezier to Surface")
296 col.operator("curvetools.convert_selected_face_to_bezier", text="Convert Faces to Bezier")
298 # Curve Path Finder
299 class VIEW3D_PT_curve_tools_loft(Panel):
300 bl_space_type = "VIEW_3D"
301 bl_region_type = "UI"
302 bl_category = "Curve Edit"
303 bl_parent_id = "VIEW3D_PT_curve_tools_surfaces"
304 bl_label = "Loft"
305 bl_options = {'DEFAULT_CLOSED'}
307 def draw(self, context):
308 wm = context.window_manager
309 scene = context.scene
310 layout = self.layout
312 col = layout.column(align=True)
313 col.operator("curvetools.create_auto_loft")
314 lofters = [o for o in scene.objects if "autoloft" in o.keys()]
315 for o in lofters:
316 col.label(text=o.name)
317 # layout.prop(o, '["autoloft"]', toggle=True)
318 col.prop(wm, "auto_loft", toggle=True)
319 col.operator("curvetools.update_auto_loft_curves")
320 col = layout.column(align=True)
323 # Curve Sanitize
324 class VIEW3D_PT_curve_tools_sanitize(Panel):
325 bl_space_type = "VIEW_3D"
326 bl_region_type = "UI"
327 bl_category = "Curve Edit"
328 bl_label = "Sanitize"
329 bl_options = {'DEFAULT_CLOSED'}
331 def draw(self, context):
332 scene = context.scene
333 layout = self.layout
335 col = layout.column(align=True)
336 col.operator("curvetools.operatororigintospline0start", icon = "OBJECT_ORIGIN", text="Set Origin to Spline Start")
337 col.operator("curvetools.scale_reset", text='Reset Scale')
339 col.label(text="Cleanup:")
340 col.operator("curvetools.remove_doubles", icon = "TRASH", text='Remove Doubles')
341 col.operator("curvetools.operatorsplinesremovezerosegment", icon = "TRASH", text="0-Segment Splines")
342 row = col.row(align=True)
343 row.operator("curvetools.operatorsplinesremoveshort", text="Short Splines")
344 row.prop(context.scene.curvetools, "SplineRemoveLength", text="Threshold remove")
346 col.label(text="Join Splines:")
347 col.operator("curvetools.operatorsplinesjoinneighbouring", text="Join Neighbouring Splines")
348 row = col.row(align=True)
349 col.prop(context.scene.curvetools, "SplineJoinDistance", text="Threshold")
350 col.prop(context.scene.curvetools, "SplineJoinStartEnd", text="Only at Ends")
351 col.prop(context.scene.curvetools, "SplineJoinMode", text="Join Position")
353 # Curve Utilities
354 class VIEW3D_PT_curve_tools_utilities(Panel):
355 bl_space_type = "VIEW_3D"
356 bl_region_type = "UI"
357 bl_category = "Curve Edit"
358 bl_label = "Utilities"
359 bl_options = {'DEFAULT_CLOSED'}
361 def draw(self, context):
362 scene = context.scene
363 layout = self.layout
365 col = layout.column(align=True)
366 row = col.row(align=True)
367 row.label(text="Curve Resolution:")
368 row = col.row(align=True)
369 row.operator("curvetools.show_resolution", icon="HIDE_OFF", text="Show [ESC]")
370 row.prop(context.scene.curvetools, "curve_vertcolor", text="")
371 row = col.row(align=True)
372 row.operator("curvetools.operatorsplinessetresolution", text="Set Resolution")
373 row.prop(context.scene.curvetools, "SplineResolution", text="")
376 row = col.row(align=True)
377 row.label(text="Spline Order:")
378 row = col.row(align=True)
379 row.operator("curvetools.show_splines_sequence", icon="HIDE_OFF", text="Show [ESC]")
380 row.prop(context.scene.curvetools, "sequence_color", text="")
381 row = col.row(align=True)
382 row.prop(context.scene.curvetools, "font_size", text="Font Size")
383 row.prop(context.scene.curvetools, "font_thickness", text="Font Thickness")
384 row = col.row(align=True)
385 oper = row.operator("curvetools.rearrange_spline", text = "<")
386 oper.command = 'PREV'
387 oper = row.operator("curvetools.rearrange_spline", text = ">")
388 oper.command = 'NEXT'
389 row = col.row(align=True)
390 row.operator("curve.switch_direction", text="Switch Direction")
391 row = col.row(align=True)
392 row.operator("curvetools.set_first_points", text="Set First Points")
394 # Curve Path Finder
395 class VIEW3D_PT_curve_tools_pathfinder(Panel):
396 bl_space_type = "VIEW_3D"
397 bl_region_type = "UI"
398 bl_category = "Curve Edit"
399 bl_parent_id = "VIEW3D_PT_curve_tools_utilities"
400 bl_label = "Path Finder"
401 bl_options = {'DEFAULT_CLOSED'}
403 def draw(self, context):
404 scene = context.scene
405 layout = self.layout
407 col = layout.column(align=True)
408 col.operator("curvetools.pathfinder", text="Path Finder [ESC]")
409 col.prop(context.scene.curvetools, "PathFinderRadius", text="PathFinder Radius")
410 col.prop(context.scene.curvetools, "path_color", text="")
411 col.prop(context.scene.curvetools, "path_thickness", text="Thickness")
413 col = layout.column(align=True)
414 col.label(text="ESC or TAB - Exit PathFinder")
415 col.label(text="X or DEL - Delete")
416 col.label(text="Alt + Mouse Click - Select Spline")
417 col.label(text="Alt + Shift + Mouse click - Add Spline to Selection")
418 col.label(text="A - Deselect All")
420 # Add-ons Preferences Update Panel
422 # Define Panel classes for updating
423 panels = (
424 VIEW3D_PT_curve_tools_info, VIEW3D_PT_curve_tools_edit,
425 VIEW3D_PT_curve_tools_intersect, VIEW3D_PT_curve_tools_surfaces,
426 VIEW3D_PT_curve_tools_loft, VIEW3D_PT_curve_tools_sanitize,
427 VIEW3D_PT_curve_tools_utilities, VIEW3D_PT_curve_tools_pathfinder
431 def update_panel(self, context):
432 message = "Curve Tools: Updating Panel locations has failed"
433 try:
434 for panel in panels:
435 if "bl_rna" in panel.__dict__:
436 bpy.utils.unregister_class(panel)
438 for panel in panels:
439 panel.bl_category = context.preferences.addons[__name__].preferences.category
440 bpy.utils.register_class(panel)
442 except Exception as e:
443 print("\n[{}]\n{}\n\nError:\n{}".format(__name__, message, e))
444 pass
447 class CurveAddonPreferences(AddonPreferences):
448 # this must match the addon name, use '__package__'
449 # when defining this in a submodule of a python package.
450 bl_idname = __name__
452 category: StringProperty(
453 name="Tab Category",
454 description="Choose a name for the category of the panel",
455 default="Edit",
456 update=update_panel
459 def draw(self, context):
460 layout = self.layout
462 row = layout.row()
463 col = row.column()
464 col.label(text="Tab Category:")
465 col.prop(self, "category", text="")
467 # Context MENU
468 def curve_tools_context_menu(self, context):
469 bl_label = 'Curve tools'
471 self.layout.operator("curvetools.bezier_points_fillet", text="Fillet")
472 self.layout.operator("curvetools.bezier_cad_handle_projection", text='Handle Projection')
473 self.layout.operator("curvetools.bezier_spline_divide", text="Divide")
474 self.layout.operator("curvetools.add_toolpath_offset_curve", text="Offset Curve")
475 self.layout.operator("curvetools.remove_doubles", text='Remove Doubles')
476 self.layout.separator()
478 def curve_tools_object_context_menu(self, context):
479 bl_label = 'Curve tools'
481 if context.active_object.type == "CURVE":
482 self.layout.operator("curvetools.scale_reset", text="Scale Reset")
483 self.layout.operator("curvetools.add_toolpath_offset_curve", text="Offset Curve")
484 self.layout.operator("curvetools.remove_doubles", text='Remove Doubles')
485 self.layout.separator()
487 # Import-export 2d svg
488 def menu_file_export(self, context):
489 for operator in exports.operators:
490 self.layout.operator(operator.bl_idname)
492 def menu_file_import(self, context):
493 for operator in imports.operators:
494 self.layout.operator(operator.bl_idname)
496 # REGISTER
497 classes = cad.operators + \
498 toolpath.operators + \
499 exports.operators + \
500 operators.operators + \
501 properties.operators + \
502 path_finder.operators + \
503 show_resolution.operators + \
504 splines_sequence.operators + \
505 outline.operators + \
506 fillet.operators + \
507 remove_doubles.operators + \
509 CurveAddonPreferences,
510 curvetoolsSettings,
513 def register():
514 bpy.types.Scene.UTSingleDrop = BoolProperty(
515 name="One Curve",
516 default=False,
517 description="One Curve"
519 bpy.types.Scene.UTMOREDROP = BoolProperty(
520 name="Curves",
521 default=False,
522 description="Curves"
524 bpy.types.Scene.UTLoftDrop = BoolProperty(
525 name="Two Curves Loft",
526 default=False,
527 description="Two Curves Loft"
529 bpy.types.Scene.UTAdvancedDrop = BoolProperty(
530 name="Advanced",
531 default=True,
532 description="Advanced"
534 bpy.types.Scene.UTExtendedDrop = BoolProperty(
535 name="Extended",
536 default=False,
537 description="Extended"
539 bpy.types.Scene.UTUtilsDrop = BoolProperty(
540 name="Curves Utils",
541 default=True,
542 description="Curves Utils"
545 for cls in classes:
546 bpy.utils.register_class(cls)
548 for panel in panels:
549 bpy.utils.register_class(panel)
551 auto_loft.register()
553 bpy.types.TOPBAR_MT_file_export.append(menu_file_export)
555 bpy.types.Scene.curvetools = bpy.props.PointerProperty(type=curvetoolsSettings)
557 update_panel(None, bpy.context)
559 bpy.types.VIEW3D_MT_edit_curve_context_menu.prepend(curve_tools_context_menu)
560 bpy.types.VIEW3D_MT_object_context_menu.prepend(curve_tools_object_context_menu)
563 def unregister():
564 del bpy.types.Scene.UTSingleDrop
565 del bpy.types.Scene.UTMOREDROP
566 del bpy.types.Scene.UTLoftDrop
567 del bpy.types.Scene.UTAdvancedDrop
568 del bpy.types.Scene.UTExtendedDrop
569 del bpy.types.Scene.UTUtilsDrop
571 auto_loft.unregister()
573 bpy.types.TOPBAR_MT_file_export.remove(menu_file_export)
575 bpy.types.VIEW3D_MT_edit_curve_context_menu.remove(curve_tools_context_menu)
576 bpy.types.VIEW3D_MT_object_context_menu.remove(curve_tools_object_context_menu)
578 for panel in panels:
579 bpy.utils.unregister_class(panel)
581 for cls in classes:
582 bpy.utils.unregister_class(cls)
585 if __name__ == "__main__":
586 register()