diff options
Diffstat (limited to 'curve_tools/__init__.py')
-rw-r--r-- | curve_tools/__init__.py | 563 |
1 files changed, 563 insertions, 0 deletions
diff --git a/curve_tools/__init__.py b/curve_tools/__init__.py new file mode 100644 index 00000000..04d63c09 --- /dev/null +++ b/curve_tools/__init__.py @@ -0,0 +1,563 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### +# Contributed to by guy lateur, Alexander Meißner (Lichtso), +# Dealga McArdle (zeffii), Marvin.K.Breuer (MKB), +# Spivak Vladimir (cwolf3d) +# Origunally an addon by Mackraken + + +bl_info = { + "name": "Curve Tools 2", + "description": "Adds some functionality for bezier/nurbs curve/surface modeling", + "author": "Mackraken", + "version": (0, 3, 3), + "blender": (2, 80, 0), + "location": "View3D > Tool Shelf > Addons Tab", + "warning": "WIP", + "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/" + "Scripts/Curve/Curve_Tools", + "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/", + "category": "Add Curve"} + + +import os, bpy, importlib, math +from bpy.types import ( + Operator, + Panel, + PropertyGroup, + ) +from bpy.props import ( + BoolProperty, + IntProperty, + FloatProperty, + EnumProperty, + CollectionProperty, + StringProperty, + FloatVectorProperty, + ) +from . import Properties +from . import Operators +from . import auto_loft +from . import curve_outline +from . import curve_remove_doubles +from . import PathFinder +from . import ShowCurveResolution +from . import internal, cad, toolpath, exports + +if 'internal' in locals(): + importlib.reload(internal) + importlib.reload(cad) + importlib.reload(toolpath) + importlib.reload(exports) + + +from bpy.types import ( + AddonPreferences, + ) + + +def UpdateDummy(object, context): + scene = context.scene + SINGLEDROP = scene.UTSingleDrop + MOREDROP = scene.UTMOREDROP + LOFTDROP = scene.UTLoftDrop + ADVANCEDDROP = scene.UTAdvancedDrop + EXTENDEDDROP = scene.UTExtendedDrop + UTILSDROP = scene.UTUtilsDrop + + +class SeparateOutline(Operator): + bl_idname = "object.sep_outline" + bl_label = "Separate Outline" + bl_options = {'REGISTER', 'UNDO'} + bl_description = "Makes 'Outline' separate mesh" + + @classmethod + def poll(cls, context): + return (context.object is not None and + context.object.type == 'CURVE') + + def execute(self, context): + bpy.ops.object.mode_set(mode = 'EDIT') + bpy.ops.curve.separate() + + return {'FINISHED'} + + +class CurveTools2Settings(PropertyGroup): + # selection + SelectedObjects: CollectionProperty( + type=Properties.CurveTools2SelectedObject + ) + NrSelectedObjects: IntProperty( + name="NrSelectedObjects", + default=0, + description="Number of selected objects", + update=UpdateDummy + ) + # curve + CurveLength: FloatProperty( + name="CurveLength", + default=0.0, + precision=6 + ) + # splines + SplineResolution: IntProperty( + name="SplineResolution", + default=64, + min=2, max=1024, + soft_min=2, + description="Spline resolution will be set to this value" + ) + SplineRemoveLength: FloatProperty( + name="SplineRemoveLength", + default=0.001, + precision=6, + description="Splines shorter than this threshold length will be removed" + ) + SplineJoinDistance: FloatProperty( + name="SplineJoinDistance", + default=0.001, + precision=6, + description="Splines with starting/ending points closer to each other " + "than this threshold distance will be joined" + ) + SplineJoinStartEnd: BoolProperty( + name="SplineJoinStartEnd", + default=False, + description="Only join splines at the starting point of one and the ending point of the other" + ) + splineJoinModeItems = ( + ('At midpoint', 'At midpoint', 'Join splines at midpoint of neighbouring points'), + ('Insert segment', 'Insert segment', 'Insert segment between neighbouring points') + ) + SplineJoinMode: EnumProperty( + items=splineJoinModeItems, + name="SplineJoinMode", + default='At midpoint', + description="Determines how the splines will be joined" + ) + # curve intersection + LimitDistance: FloatProperty( + name="LimitDistance", + default=0.0001, + precision=6, + description="Displays the result of the curve length calculation" + ) + + intAlgorithmItems = ( + ('3D', '3D', 'Detect where curves intersect in 3D'), + ('From View', 'From View', 'Detect where curves intersect in the RegionView3D') + ) + IntersectCurvesAlgorithm: EnumProperty( + items=intAlgorithmItems, + name="IntersectCurvesAlgorithm", + description="Determines how the intersection points will be detected", + default='3D' + ) + intModeItems = ( + ('Insert', 'Insert', 'Insert points into the existing spline(s)'), + ('Split', 'Split', 'Split the existing spline(s) into 2'), + ('Empty', 'Empty', 'Add empty at intersections') + ) + IntersectCurvesMode: EnumProperty( + items=intModeItems, + name="IntersectCurvesMode", + description="Determines what happens at the intersection points", + default='Split' + ) + intAffectItems = ( + ('Both', 'Both', 'Insert points into both curves'), + ('Active', 'Active', 'Insert points into active curve only'), + ('Other', 'Other', 'Insert points into other curve only') + ) + IntersectCurvesAffect: EnumProperty( + items=intAffectItems, + name="IntersectCurvesAffect", + description="Determines which of the selected curves will be affected by the operation", + default='Both' + ) + PathFinderRadius: FloatProperty( + name="PathFinder detection radius", + default=0.2, + precision=6, + description="PathFinder detection radius" + ) + curve_vertcolor: FloatVectorProperty( + name="OUT", + default=(0.2, 0.9, 0.9, 1), + size=4, + subtype="COLOR", + min=0, + max=1 + ) + path_color: FloatVectorProperty( + name="OUT", + default=(0.2, 0.9, 0.9, 0.1), + size=4, + subtype="COLOR", + min=0, + max=1 + ) + path_thickness: IntProperty( + name="Path thickness", + default=10, + min=1, max=1024, + soft_min=2, + description="Path thickness (px)" + ) + + +class VIEW3D_PT_CurvePanel(Panel): + bl_label = "Curve Tools 2" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + bl_options = {'DEFAULT_CLOSED'} + bl_category = "Tools" + + @classmethod + def poll(cls, context): + return context.scene is not None + + def draw(self, context): + scene = context.scene + SINGLEDROP = scene.UTSingleDrop + MOREDROP = scene.UTMOREDROP + LOFTDROP = scene.UTLoftDrop + ADVANCEDDROP = scene.UTAdvancedDrop + EXTENDEDDROP = scene.UTExtendedDrop + UTILSDROP = scene.UTUtilsDrop + layout = self.layout + + # Single Curve options + box1 = self.layout.box() + col = box1.column(align=True) + row = col.row(align=True) + row.prop(scene, "UTSingleDrop", icon="TRIA_DOWN") + if SINGLEDROP: + # A. 1 curve + row = col.row(align=True) + + # A.1 curve info/length + row.operator("curvetools2.operatorcurveinfo", text="Curve info") + row = col.row(align=True) + row.operator("curvetools2.operatorcurvelength", text="Calc Length") + row.prop(context.scene.curvetools, "CurveLength", text="") + + # A.2 splines info + row = col.row(align=True) + row.operator("curvetools2.operatorsplinesinfo", text="Curve splines info") + + # A.3 segments info + row = col.row(align=True) + row.operator("curvetools2.operatorsegmentsinfo", text="Curve segments info") + + # A.4 origin to spline0start + row = col.row(align=True) + row.operator("curvetools2.operatororigintospline0start", text="Set origin to spline start") + + # Double Curve options + box2 = self.layout.box() + col = box2.column(align=True) + row = col.row(align=True) + row.prop(scene, "UTMOREDROP", icon="TRIA_DOWN") + + if MOREDROP: + # B. 2 curves + row = col.row(align=True) + + # B.1 curve intersections + row = col.row(align=True) + row.operator("curvetools2.operatorintersectcurves", text="Intersect curves") + + row = col.row(align=True) + row.prop(context.scene.curvetools, "LimitDistance", text="LimitDistance") + # row.active = (context.scene.curvetools.IntersectCurvesAlgorithm == '3D') + + row = col.row(align=True) + row.prop(context.scene.curvetools, "IntersectCurvesAlgorithm", text="Algorithm") + + row = col.row(align=True) + row.prop(context.scene.curvetools, "IntersectCurvesMode", text="Mode") + + row = col.row(align=True) + row.prop(context.scene.curvetools, "IntersectCurvesAffect", text="Affect") + + # Loft options + box1 = self.layout.box() + col = box1.column(align=True) + row = col.row(align=True) + row.prop(scene, "UTLoftDrop", icon="TRIA_DOWN") + + if LOFTDROP: + # B.2 surface generation + wm = context.window_manager + scene = context.scene + layout = self.layout + layout.operator("curvetools2.create_auto_loft") + lofters = [o for o in scene.objects if "autoloft" in o.keys()] + for o in lofters: + layout.label(text=o.name) + # layout.prop(o, '["autoloft"]', toggle=True) + layout.prop(wm, "auto_loft", toggle=True) + layout.operator("curvetools2.update_auto_loft_curves") + + # Advanced options + box1 = self.layout.box() + col = box1.column(align=True) + row = col.row(align=True) + row.prop(scene, "UTAdvancedDrop", icon="TRIA_DOWN") + if ADVANCEDDROP: + # C. 3 curves + row = col.row(align=True) + row.operator("object._curve_outline", text="Curve Outline") + row = col.row(align=True) + row.operator("object.sep_outline", text="Separate Outline") + row = col.row(align=True) + row.operator("curve.remove_doubles", text="Remove Doubles") + row = col.row(align=True) + row.operator("curve.bezier_points_fillet", text='Fillet') + row = col.row(align=True) + row.operator("curve.bezier_spline_divide", text='Divide') + row = col.row(align=True) + row.operator("curve.scale_reset", text='Scale Reset') + row = col.row(align=True) + row.operator("curvetools2.operatorbirail", text="Birail") + row = col.row(align=True) + row.operator("curvetools2.convert_selected_face_to_bezier", text="Convert selected faces to Bezier") + row = col.row(align=True) + row.operator("curvetools2.convert_bezier_to_surface", text="Convert Bezier to Surface") + + # Extended options + box1 = self.layout.box() + col = box1.column(align=True) + row = col.row(align=True) + row.prop(scene, "UTExtendedDrop", icon="TRIA_DOWN") + if EXTENDEDDROP: + for operator in cad.operators: + row = col.row(align=True) + row.operator(operator.bl_idname) + + for operator in toolpath.operators: + row = col.row(align=True) + row.operator(operator.bl_idname) + + # Utils Curve options + box1 = self.layout.box() + col = box1.column(align=True) + row = col.row(align=True) + row.prop(scene, "UTUtilsDrop", icon="TRIA_DOWN") + if UTILSDROP: + # D.1 set spline resolution + row = col.row(align=True) + row.label(text="Show point Resolution:") + row = col.row(align=True) + row.operator("curvetools2.operatorsplinessetresolution", text="Set resolution") + row.prop(context.scene.curvetools, "SplineResolution", text="") + row = col.row(align=True) + row.prop(context.scene.curvetools, "curve_vertcolor", text="") + row = col.row(align=True) + row.operator("curve.show_resolution", text="Run [ESC]") + + # D.2 remove splines + row = col.row(align=True) + row.label(text="Remove splines:") + row = col.row(align=True) + row.operator("curvetools2.operatorsplinesremovezerosegment", text="Remove 0-segments splines") + row = col.row(align=True) + row.operator("curvetools2.operatorsplinesremoveshort", text="Remove short splines") + row = col.row(align=True) + row.prop(context.scene.curvetools, "SplineRemoveLength", text="Threshold remove") + + # D.3 join splines + row = col.row(align=True) + row.label(text="Join splines:") + row = col.row(align=True) + row.operator("curvetools2.operatorsplinesjoinneighbouring", text="Join neighbouring splines") + row = col.row(align=True) + row.prop(context.scene.curvetools, "SplineJoinDistance", text="Threshold join") + row = col.row(align=True) + row.prop(context.scene.curvetools, "SplineJoinStartEnd", text="Only at start & end") + row = col.row(align=True) + row.prop(context.scene.curvetools, "SplineJoinMode", text="Join mode") + + row = col.row(align=True) + row.label(text="PathFinder:") + row = col.row(align=True) + row.prop(context.scene.curvetools, "PathFinderRadius", text="PathFinder Radius") + row = col.row(align=True) + row.prop(context.scene.curvetools, "path_color", text="") + row.prop(context.scene.curvetools, "path_thickness", text="") + row = col.row(align=True) + row.operator("curvetools2.pathfinder", text="Run Path Finder [ESC]") + row = col.row(align=True) + row.label(text="ESC or TAB - exit from PathFinder") + row = col.row(align=True) + row.label(text="X or DEL - delete") + row = col.row(align=True) + row.label(text="Alt + mouse click - select spline") + row = col.row(align=True) + row.label(text="A - deselect all") + + +# Add-ons Preferences Update Panel + +# Define Panel classes for updating +panels = ( + VIEW3D_PT_CurvePanel, + ) + + +def update_panel(self, context): + message = "Curve Tools 2: Updating Panel locations has failed" + try: + for panel in panels: + if "bl_rna" in panel.__dict__: + bpy.utils.unregister_class(panel) + + for panel in panels: + panel.bl_category = context.preferences.addons[__name__].preferences.category + bpy.utils.register_class(panel) + + except Exception as e: + print("\n[{}]\n{}\n\nError:\n{}".format(__name__, message, e)) + pass + + +class CurveAddonPreferences(AddonPreferences): + # this must match the addon name, use '__package__' + # when defining this in a submodule of a python package. + bl_idname = __name__ + + category: StringProperty( + name="Tab Category", + description="Choose a name for the category of the panel", + default="Tools", + update=update_panel + ) + + def draw(self, context): + layout = self.layout + + row = layout.row() + col = row.column() + col.label(text="Tab Category:") + col.prop(self, "category", text="") + +def menu_file_export(self, context): + for operator in exports.operators: + self.layout.operator(operator.bl_idname) + +def menu_file_import(self, context): + for operator in imports.operators: + self.layout.operator(operator.bl_idname) + +# REGISTER +classes = cad.operators + toolpath.operators + exports.operators + [ + Properties.CurveTools2SelectedObject, + CurveAddonPreferences, + CurveTools2Settings, + Operators.OperatorCurveInfo, + Operators.OperatorCurveLength, + Operators.OperatorSplinesInfo, + Operators.OperatorSegmentsInfo, + Operators.OperatorOriginToSpline0Start, + Operators.OperatorIntersectCurves, + Operators.OperatorLoftCurves, + Operators.OperatorSweepCurves, + Operators.OperatorBirail, + Operators.OperatorSplinesSetResolution, + Operators.OperatorSplinesRemoveZeroSegment, + Operators.OperatorSplinesRemoveShort, + Operators.OperatorSplinesJoinNeighbouring, + VIEW3D_PT_CurvePanel, + SeparateOutline, + Operators.ConvertSelectedFacesToBezier, + Operators.ConvertBezierToSurface, + PathFinder.PathFinder, + ShowCurveResolution.ShowCurveResolution, + ] + +def register(): + bpy.types.Scene.UTSingleDrop = BoolProperty( + name="One Curve", + default=False, + description="One Curve" + ) + bpy.types.Scene.UTMOREDROP = BoolProperty( + name="Curves", + default=False, + description="Curves" + ) + bpy.types.Scene.UTLoftDrop = BoolProperty( + name="Two Curves Loft", + default=False, + description="Two Curves Loft" + ) + bpy.types.Scene.UTAdvancedDrop = BoolProperty( + name="Advanced", + default=True, + description="Advanced" + ) + bpy.types.Scene.UTExtendedDrop = BoolProperty( + name="Extended", + default=False, + description="Extended" + ) + bpy.types.Scene.UTUtilsDrop = BoolProperty( + name="Curves Utils", + default=True, + description="Curves Utils" + ) + + for cls in classes: + bpy.utils.register_class(cls) + + auto_loft.register() + + curve_outline.register() + + curve_remove_doubles.register() + + bpy.types.TOPBAR_MT_file_export.append(menu_file_export) + + bpy.types.Scene.curvetools = bpy.props.PointerProperty(type=CurveTools2Settings) + + +def unregister(): + del bpy.types.Scene.UTSingleDrop + del bpy.types.Scene.UTMOREDROP + del bpy.types.Scene.UTLoftDrop + del bpy.types.Scene.UTAdvancedDrop + del bpy.types.Scene.UTExtendedDrop + del bpy.types.Scene.UTUtilsDrop + + auto_loft.unregister() + + curve_outline.unregister() + + curve_remove_doubles.unregister() + + bpy.types.TOPBAR_MT_file_export.remove(menu_file_export) + + for cls in classes: + bpy.utils.unregister_class(cls) + + +if __name__ == "__main__": + register() |