diff --git a/add_curve_extra_objects/__init__.py b/add_curve_extra_objects/__init__.py index 65a3a41121c829b5955948fa4889b86bbac005bf..ffa049382e546c77a423a462a655f4ee8d9e7f54 100644 --- a/add_curve_extra_objects/__init__.py +++ b/add_curve_extra_objects/__init__.py @@ -17,6 +17,7 @@ # ##### END GPL LICENSE BLOCK ##### # Contributed to by # testscreenings, Alejandro Omar Chocano Vasquez, Jimmy Hazevoet, meta-androcto # +# Cmomoney, Jared Forsyth, Adam Newgas, Spivak Vladimir bl_info = { "name": "Extra Objects", @@ -37,12 +38,22 @@ if "bpy" in locals(): importlib.reload(add_curve_spirals) importlib.reload(add_curve_torus_knots) importlib.reload(add_surface_plane_cone) + importlib.reload(add_curve_curly) + importlib.reload(beveltaper_curve) + importlib.reload(add_curve_celtic_links) + importlib.reload(add_curve_braid) + importlib.reload(add_curve_simple) else: from . import add_curve_aceous_galore from . import add_curve_spirals from . import add_curve_torus_knots from . import add_surface_plane_cone + from . import add_curve_curly + from . import beveltaper_curve + from . import add_curve_celtic_links + from . import add_curve_braid + from . import add_curve_simple import bpy from bpy.types import Menu, AddonPreferences @@ -131,24 +142,22 @@ class CurveExtraObjectsAddonPreferences(AddonPreferences): else: layout.prop(self, "update_spiral_presets") - -# TODO check if this is even used. -class INFO_MT_curve_extras_add(Menu): +class INFO_MT_curve_knots_add1(bpy.types.Menu): # Define the "Extras" menu - bl_idname = "curve_extra_objects_add" - bl_label = "Extra Objects" + bl_idname = "curve_knots_add" + bl_label = "Plants" def draw(self, context): layout = self.layout layout.operator_context = 'INVOKE_REGION_WIN' - layout.operator_menu_enum("mesh.curveaceous_galore", - "ProfileType", - ) - layout.operator_menu_enum("curve.spirals", - "spiral_type", - icon='FORCE_VORTEX') layout.operator("curve.torus_knot_plus", - text="Torus Knot Plus") + text="Torus Knot Plus") + layout.operator("curve.celtic_links", + text="Celtic Links") + layout.operator("mesh.add_braid", + text="Braid Knot") + + # Define "Extras" menus def menu_func(self, context): @@ -156,14 +165,19 @@ def menu_func(self, context): # fix in D2142 will allow to work in EDIT_CURVE return None layout = self.layout - layout.separator() + layout.operator_menu_enum("mesh.curveaceous_galore", "ProfileType", - ) + icon='CURVE_DATA') layout.operator_menu_enum("curve.spirals", "spiral_type", - icon='FORCE_VORTEX') - layout.operator("curve.torus_knot_plus", text="Torus Knot Plus") + icon='CURVE_DATA') + layout.separator() + layout.menu("curve_knots_add", text="Knots", icon='CURVE_DATA') + layout.separator() + layout.operator("curve.curlycurve", text="Curly Curve", icon='CURVE_DATA') + layout.menu("OBJECT_MT_bevel_taper_curve_menu", text="Bevel/Taper", icon='CURVE_DATA') + def menu_surface(self, context): layout = self.layout @@ -177,6 +191,7 @@ def menu_surface(self, context): self.layout.operator("object.add_surface_plane", text="Plane", icon="MOD_CURVE") def register(): + add_curve_simple.register() bpy.utils.register_module(__name__) # Add "Extras" menu to the "Add Curve" menu @@ -185,12 +200,14 @@ def register(): bpy.types.INFO_MT_surface_add.append(menu_surface) def unregister(): - bpy.utils.unregister_module(__name__) + add_curve_simple.unregister() # Remove "Extras" menu from the "Add Curve" menu. bpy.types.INFO_MT_curve_add.remove(menu_func) # Remove "Extras" menu from the "Add Surface" menu. bpy.types.INFO_MT_surface_add.remove(menu_surface) + bpy.utils.unregister_module(__name__) + if __name__ == "__main__": register() diff --git a/add_curve_extra_objects/add_curve_aceous_galore.py b/add_curve_extra_objects/add_curve_aceous_galore.py index 259660fbb3de63b19a5689bd5cc1a413b17dd89a..6a7ac654d727121fcc692311f98f176555315fa2 100644 --- a/add_curve_extra_objects/add_curve_aceous_galore.py +++ b/add_curve_extra_objects/add_curve_aceous_galore.py @@ -20,7 +20,7 @@ bl_info = { "name": "Curveaceous Galore!", "author": "Jimmy Hazevoet, testscreenings", "version": (0, 2), - "blender": (2, 59, 0), + "blender": (2, 59), "location": "View3D > Add > Curve", "description": "Adds many different types of Curves", "warning": "", # used for warning icon and text in addons panel @@ -193,61 +193,81 @@ def ProfileCurve(type=0, a=0.25, b=0.25): return newpoints # ------------------------------------------------------------ -# 2DCurve: Miscellaneous.: Diamond, Arrow1, Arrow2, Square, .... -def MiscCurve(type=1, a=1.0, b=0.5, c=1.0): +# 2DCurve: Arrows +def ArrowCurve(type=1, a=1.0, b=0.5): """ - MiscCurve( type=1, a=1.0, b=0.5, c=1.0 ) + ArrowCurve( type=1, a=1.0, b=0.5, c=1.0 ) - Create miscellaneous curves + Create arrow curves Parameters: - type - select type, Diamond, Arrow1, Arrow2, Square + type - select type, Arrow1, Arrow2 (type=int) a - a scaling parameter (type=float) b - b scaling parameter (type=float) - c - c scaling parameter - (type=float) - doesn't seem to do anything Returns: a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n] (type=list) """ newpoints = [] - a *= 0.5 - b *= 0.5 - if type == 1: - # diamond: - newpoints = [[0.0, b, 0.0], [a, 0.0, 0.0], [0.0, -b, 0.0], [-a, 0.0, 0.0]] - elif type == 2: + if type == 0: # Arrow1: - newpoints = [[-a, b, 0.0], [a, 0.0, 0.0], [-a, -b, 0.0], [0.0, 0.0, 0.0]] - elif type == 3: - # Arrow2: + a *= 0.5 + b *= 0.5 newpoints = [[-1.0, b, 0.0], [-1.0+a, b, 0.0], [-1.0+a, 1.0, 0.0], [1.0, 0.0, 0.0], [-1.0+a, -1.0, 0.0], [-1.0+a, -b, 0.0], [-1.0, -b, 0.0]] - elif type == 4: - # Rounded square: + elif type == 1: + # Arrow2: + newpoints = [[-a, b, 0.0], [a, 0.0, 0.0], [-a, -b, 0.0], [0.0, 0.0, 0.0]] + else: + # diamond: + newpoints = [[0.0, b, 0.0], [a, 0.0, 0.0], [0.0, -b, 0.0], [-a, 0.0, 0.0]] + return newpoints + +# ------------------------------------------------------------ +# 2DCurve: Square / Rectangle +def RectCurve(type=1, a=1.0, b=0.5, c=1.0): + """ + RectCurve( type=1, a=1.0, b=0.5, c=1.0 ) + + Create square / rectangle curves + + Parameters: + type - select type, Square, Rounded square 1, Rounded square 2 + (type=int) + a - a scaling parameter + (type=float) + b - b scaling parameter + (type=float) + c - c scaling parameter + (type=float) + Returns: + a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n] + (type=list) + """ + + newpoints = [] + if type == 1: + # Rounded Rectangle: newpoints = [[-a, b-b*0.2, 0.0], [-a+a*0.05, b-b*0.05, 0.0], [-a+a*0.2, b, 0.0], [a-a*0.2, b, 0.0], [a-a*0.05, b-b*0.05, 0.0], [a, b-b*0.2, 0.0], [a, -b+b*0.2, 0.0], [a-a*0.05, -b+b*0.05, 0.0], [a-a*0.2, -b, 0.0], [-a+a*0.2, -b, 0.0], [-a+a*0.05, -b+b*0.05, 0.0], [-a, -b+b*0.2, 0.0]] - elif type == 5: + elif type == 2: # Rounded Rectangle II: newpoints = [] - x = a / 2 - y = b / 2 - r = c / 2 - + x = a + y = b + r = c if r > x: r = x - 0.0001 if r > y: r = y - 0.0001 - if r > 0: newpoints.append([-x+r, y, 0]) newpoints.append([x-r, y, 0]) @@ -262,9 +282,8 @@ def MiscCurve(type=1, a=1.0, b=0.5, c=1.0): newpoints.append([x, y, 0]) newpoints.append([x, -y, 0]) newpoints.append([-x, -y, 0]) - else: - # Square: + # Rectangle: newpoints = [[-a, b, 0.0], [a, b, 0.0], [a, -b, 0.0], [-a, -b, 0.0]] return newpoints @@ -567,47 +586,116 @@ def HelixCurve(number=100, height=2.0, startangle=0.0, endangle=360.0, width=1.0 i += 1 return newpoints -# ------------------------------------------------------------ ? -# 3DCurve: Cycloid: Cycloid, Epicycloid, Hypocycloid -def CycloidCurve(number=24, length=2.0, type=0, a=1.0, b=1.0, startangle=0.0, endangle=360.0): +#------------------------------------------------------------ +# Cycloid curve + +def CycloidCurve(number=100, type=0, R=4.0, r=1.0, d=1.0): """ - CycloidCurve( number=24, length=2.0, type=0, a=1.0, b=1.0, startangle=0.0, endangle=360.0 ) + CycloidCurve( number=100, type=0, a=4.0, b=1.0 ) - Create a Cycloid, Epicycloid or Hypocycloid curve + Create a Cycloid, Hypotrochoid / Hypocycloid or Epitrochoid / Epycycloid type of curve Parameters: number - the number of points (type=int) - length - length of curve - (type=float) - type - types: Cycloid, Epicycloid, Hypocycloid + type - types: Cycloid, Hypocycloid, Epicycloid (type=int) + R = Radius a scaling parameter + (type=float) + r = Radius b scaling parameter + (type=float) + d = Distance scaling parameter + (type=float) Returns: a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n] (type=list) """ + a = R + b = r newpoints = [] - angle = (2.0/360.0)*(endangle-startangle) - step = angle/(number-1) - #h = height/angle - d = length - start = (startangle*2.0/360.0) - a /= angle + step = (2.0/(number-1)) i = 0 - if type == 0: # Epitrochoid + if type == 1: + # Hypotrochoid / Hypocycloid while i < number: - t = (i*step+start) - x = ((a + b) * cos(t*pi)) - (d * cos(((a+b)/b)*t*pi)) - y = ((a + b) * sin(t*pi)) - (d * sin(((a+b)/b)*t*pi)) - z = 0 # ( t * h ) -h*start - newpoints.append([x, y, z]) - i += 1 + t = i*step + x = (((a-b)*cos(t*pi))+(d*cos(((a+b)/b)*t*pi))) + y = (((a-b)*sin(t*pi))-(d*sin(((a+b)/b)*t*pi))) + z = 0 + newpoints.append([x,y,z]) + i+=1 + elif type == 2: + # Epitrochoid / Epycycloid + while i < number: + t = i*step + x = (((a+b)*cos(t*pi))-(d*cos(((a+b)/b)*t*pi))) + y = (((a+b)*sin(t*pi))-(d*sin(((a+b)/b)*t*pi))) + z = 0 + newpoints.append([x,y,z]) + i+=1 + else: + # Cycloid + while i < number: + t = (i*step*pi)*a + x = (t-sin(t)*b) + y = (1-cos(t)*b) + z = 0 + newpoints.append([x,y,z]) + i+=1 + return newpoints + +#------------------------------------------------------------ +# 3D Noise curve +def NoiseCurve(number=100, length=2.0, scale=1.0, octaves=6, basis=1, seed=0, type=0): + """ + NoiseCurve( number=100, length=2.0, scale=1.0, octaves=2, basis=1, seed=0, type=1 ) + Create noise curve + + Parameters: + number - number of points + (type=int) + length - curve length + (type=float) + scale - noise scale + (type=float) + basis - noise basis + (type=int) + seed - noise random seed + (type=int) + type - noise curve type + (type=int) + Returns: + a list with lists of x,y,z coordinates for curve points, [[x,y,z],[x,y,z],...n] + (type=list) + """ + + rand = randnum(-100,100,seed) + newpoints = [] + step = (length/number) + i = 0 + if type == 1: + # noise knot + while i < number: + t = ((i*step)+rand) + v = vTurbNoise(t,t,t, scale, 1.0, octaves, 0, basis, seed) + newpoints.append([v[0], v[1], v[2]]) + i+=1 else: - newpoints = [[-1, -1, 0], [-1, 1, 0], [1, 1, 0], [1, -1, 0]] + # noise linear + while i < number: + t = i*step + tt = t+rand + v = vTurbNoise(t,t,t, scale, 1.0, octaves, 0, basis, seed) + x = t + y = v[1] + z = v[2] + newpoints.append([x,y,z]) + i+=1 return newpoints + # ------------------------------------------------------------ # calculates the matrix for the new object # depending on user pref @@ -712,8 +800,12 @@ def main(context, self, align_matrix): verts = ProfileCurve(self.ProfileCurveType, self.ProfileCurvevar1, self.ProfileCurvevar2) - if proType == 'Miscellaneous': - verts = MiscCurve(self.MiscCurveType, + if proType == 'Arrow': + verts = ArrowCurve(self.MiscCurveType, + self.MiscCurvevar1, + self.MiscCurvevar2) + if proType == 'Rectangle': + verts = RectCurve(self.MiscCurveType, self.MiscCurvevar1, self.MiscCurvevar2, self.MiscCurvevar3) @@ -759,12 +851,18 @@ def main(context, self, align_matrix): self.helix_b) if proType == 'Cycloid': verts = CycloidCurve(self.cycloPoints, - self.cyclo_d, self.cycloType, self.cyclo_a, self.cyclo_b, - self.cycloStart, - self.cycloEnd) + self.cyclo_d) + if proType == 'Noise': + verts = NoiseCurve(self.noisePoints, + self.noiseLength, + self.noiseScale, + self.noiseOctaves, + self.noiseBasis, + self.noiseSeed, + self.noiseType) # turn verts into array vertArray = vertsToPoints(verts, splineType) @@ -777,7 +875,7 @@ def main(context, self, align_matrix): class Curveaceous_galore(Operator): """Add many types of curves""" bl_idname = "mesh.curveaceous_galore" - bl_label = "Curve Profiles" + bl_label = "2D Profiles" bl_options = {'REGISTER', 'UNDO', 'PRESET'} # align_matrix for the invoke @@ -786,7 +884,8 @@ class Curveaceous_galore(Operator): # general properties ProfileTypes = [ ('Profile', 'Profile', 'Profile'), - ('Miscellaneous', 'Miscellaneous', 'Miscellaneous'), + ('Arrow', 'Arrow', 'Arrow'), + ('Rectangle', 'Rectangle', 'Rectangle'), ('Flower', 'Flower', 'Flower'), ('Star', 'Star', 'Star'), ('Arc', 'Arc', 'Arc'), @@ -794,7 +893,8 @@ class Curveaceous_galore(Operator): ('Nsided', 'Nsided', 'Nsided'), ('Splat', 'Splat', 'Splat'), ('Cycloid', 'Cycloid', 'Cycloid'), - ('Helix', 'Helix (3D)', 'Helix')] + ('Helix', 'Helix (3D)', 'Helix'), + ('Noise', 'Noise (3D)', 'Noise')] ProfileType = EnumProperty(name="Type", description="Form of Curve to create", items=ProfileTypes) @@ -844,22 +944,22 @@ class Curveaceous_galore(Operator): default=0.25, description="var2 of ProfileCurve") - # MiscCurve properties + # Arrow, Rectangle, MiscCurve properties MiscCurveType = IntProperty(name="Type", - min=1, soft_min=1, - max=6, soft_max=6, - default=1, - description="Type of MiscCurve") + min=0, soft_min=0, + max=3, soft_max=3, + default=0, + description="Type of Curve") MiscCurvevar1 = FloatProperty(name="var_1", default=1.0, - description="var1 of MiscCurve") + description="var1 of Curve") MiscCurvevar2 = FloatProperty(name="var_2", default=0.5, - description="var2 of MiscCurve") + description="var2 of Curve") MiscCurvevar3 = FloatProperty(name="var_3", default=0.1, min=0, soft_min=0, - description="var3 of MiscCurve") + description="var3 of Curve") # Common properties innerRadius = FloatProperty(name="Inner radius", @@ -977,28 +1077,55 @@ class Curveaceous_galore(Operator): default=100, min=3, soft_min=3, description="Resolution") - cyclo_d = FloatProperty(name="var_3", - default=1.5, - description="Cycloid var3") cycloType = IntProperty(name="Type", default=0, min=0, soft_min=0, - max=0, soft_max=0, - description="resolution") - cyclo_a = FloatProperty(name="var_1", - default=5.0, + max=2, soft_max=2, + description="Type: Cycloid , Hypocycloid / Hypotrochoid , Epicycloid / Epitrochoid") + cyclo_a = FloatProperty(name="R", + default=4.0, min=0.01, soft_min=0.01, - description="Cycloid var1") - cyclo_b = FloatProperty(name="var_2", - default=0.5, + description="Cycloid: R radius a") + cyclo_b = FloatProperty(name="r", + default=1.0, min=0.01, soft_min=0.01, - description="Cycloid var2") - cycloStart = FloatProperty(name="Start angle", - default=0.0, - description="Cycloid start angle") - cycloEnd = FloatProperty(name="End angle", - default=360.0, - description="Cycloid end angle") + description="Cycloid: r radius b") + cyclo_d = FloatProperty(name="d", + default=1.0, + description="Cycloid: d distance") + + # Noise properties + noisePoints = IntProperty(name="Resolution", + default=100, + min=3, soft_min=3, + description="Resolution") + noiseLength = FloatProperty(name="Length", + default=2.0, + min=0.01, soft_min=0.01, + description="Curve Length") + noiseScale = FloatProperty(name="Noise scale", + default=1.0, + min=0.0001, soft_min=0.0001, + description="Noise scale") + noiseOctaves = IntProperty(name="Octaves", + default=2, + min=0, soft_min=0, + max=16, soft_max=16, + description="Basis") + noiseBasis = IntProperty(name="Basis", + default=0, + min=0, soft_min=0, + max=14, soft_max=14, + description="Basis") + noiseSeed = IntProperty(name="Seed", + default=1, + min=0, soft_min=0, + description="Random Seed") + noiseType = IntProperty(name="Type", + default=0, + min=0, soft_min=0, + max=1, soft_max=1, + description="Noise curve type: Linear or Knot") ##### DRAW ##### def draw(self, context): @@ -1016,12 +1143,17 @@ class Curveaceous_galore(Operator): box.prop(self, 'ProfileCurvevar1') box.prop(self, 'ProfileCurvevar2') - elif self.ProfileType == 'Miscellaneous': + elif self.ProfileType == 'Arrow': + box.prop(self, 'MiscCurveType') + box.prop(self, 'MiscCurvevar1', text='Height') + box.prop(self, 'MiscCurvevar2', text='Width') + + elif self.ProfileType == 'Rectangle': box.prop(self, 'MiscCurveType') box.prop(self, 'MiscCurvevar1', text='Width') box.prop(self, 'MiscCurvevar2', text='Height') - if self.MiscCurveType == 5: - box.prop(self, 'MiscCurvevar3', text='Rounded') + if self.MiscCurveType == 2: + box.prop(self, 'MiscCurvevar3', text='Corners') elif self.ProfileType == 'Flower': box.prop(self, 'petals') @@ -1037,10 +1169,10 @@ class Curveaceous_galore(Operator): elif self.ProfileType == 'Arc': box.prop(self, 'arcSides') - box.prop(self, 'arcType') # has only one Type? + box.prop(self, 'arcType') box.prop(self, 'startAngle') box.prop(self, 'endAngle') - box.prop(self, 'innerRadius') # doesn't seem to do anything + box.prop(self, 'innerRadius') box.prop(self, 'outerRadius') elif self.ProfileType == 'Cogwheel': @@ -1052,7 +1184,7 @@ class Curveaceous_galore(Operator): elif self.ProfileType == 'Nsided': box.prop(self, 'Nsides') - box.prop(self, 'outerRadius', text='Radius') + box.prop(self, 'outerRadius') elif self.ProfileType == 'Splat': box.prop(self, 'splatSides') @@ -1072,34 +1204,30 @@ class Curveaceous_galore(Operator): elif self.ProfileType == 'Cycloid': box.prop(self, 'cycloPoints') - # box.prop(self, 'cycloType') # needs the other types first - box.prop(self, 'cycloStart') - box.prop(self, 'cycloEnd') + box.prop(self, 'cycloType') box.prop(self, 'cyclo_a') box.prop(self, 'cyclo_b') box.prop(self, 'cyclo_d') + elif self.ProfileType == 'Noise': + box.prop(self, 'noisePoints') + box.prop(self, 'noiseType') + box.prop(self, 'noiseLength') + box.prop(self, 'noiseScale') + box.prop(self, 'noiseOctaves') + box.prop(self, 'noiseBasis') + box.prop(self, 'noiseSeed') + col = layout.column() col.label(text="Output Curve Type:") col.row().prop(self, 'outputType', expand=True) - col.label(text="Curve Options:") # output options box = layout.box() if self.outputType == 'NURBS': - box.row().prop(self, 'shape', expand=True) - #box.prop(self, 'use_cyclic_u') - #box.prop(self, 'endp_u') box.prop(self, 'order_u') - - elif self.outputType == 'POLY': - box.row().prop(self, 'shape', expand=True) - #box.prop(self, 'use_cyclic_u') - elif self.outputType == 'BEZIER': - box.row().prop(self, 'shape', expand=True) box.row().prop(self, 'handleType', expand=True) - #box.prop(self, 'use_cyclic_u') ##### POLL ##### @classmethod @@ -1113,13 +1241,18 @@ class Curveaceous_galore(Operator): context.user_preferences.edit.use_global_undo = False # deal with 2D - 3D curve differences - if self.ProfileType in ['Helix', 'Cycloid']: + if self.ProfileType in ['Helix', 'Cycloid', 'Noise']: self.shape = '3D' - # else: - # self.shape = '2D' # someone decide if we want this + else: + self.shape = '2D' - if self.ProfileType in ['Helix']: + if self.ProfileType in ['Helix','Noise', 'Cycloid']: self.use_cyclic_u = False + if self.ProfileType in ['Cycloid']: + if self.cycloType == 0: + self.use_cyclic_u = False + else: + self.use_cyclic_u = True else: self.use_cyclic_u = True diff --git a/add_curve_extra_objects/add_curve_braid.py b/add_curve_extra_objects/add_curve_braid.py new file mode 100644 index 0000000000000000000000000000000000000000..e31033a2581f705f544b8c6b59a40ca887d824db --- /dev/null +++ b/add_curve_extra_objects/add_curve_braid.py @@ -0,0 +1,88 @@ + +import bpy +from bpy.props import (FloatProperty, + FloatVectorProperty, + IntProperty, + BoolProperty, + StringProperty) + +from .bpybraid import awesome_braid, defaultCircle +''' +bl_info = { + "name": "New Braid", + "author": "Jared Forsyth <github.com/jaredly>", + "version": (1, 0), + "blender": (2, 6, 0), + "location": "View3D > Add > Mesh > New Braid", + "description": "Adds a new Braid", + "warning": "", + "wiki_url": "", + "tracker_url": "", + "category": "Add Mesh"} +''' +from bpy.types import Operator + + +class Braid(Operator): + '''Add a Braid''' + bl_idname = 'mesh.add_braid' + bl_label = 'New Braid' + bl_description = 'Create a new braid' + bl_options = {'REGISTER', 'UNDO', 'PRESET'} + + strands = IntProperty(name='strands', min=2, max=100, default=3) + sides = IntProperty(name='sides', min=2, max=100, default=5) + radius = FloatProperty(name='radius', default=1) + thickness = FloatProperty(name='thickness', default=.3) + strandsize = FloatProperty(name='strandsize', default=.3, min=.01, max=10) + width = FloatProperty(name='width', default=.2) + resolution = IntProperty(name='resolution', min=1, default=2, max=100) + pointy = BoolProperty(name='pointy', default=False) + + def execute(self, context): + circle = defaultCircle(self.strandsize) + context.scene.objects.link(circle) + braid = awesome_braid(self.strands, self.sides, + bevel=circle.name, + pointy=self.pointy, + radius=self.radius, + mr=self.thickness, + mz=self.width, + resolution=self.resolution) + base = context.scene.objects.link(braid) + + for ob in context.scene.objects: + ob.select = False + base.select = True + context.scene.objects.active = braid + return {'FINISHED'} + + def draw(self, context): + layout = self.layout + + box = layout.box() + box.prop(self, 'strands') + box.prop(self, 'sides') + box.prop(self, 'radius') + box.prop(self, 'thickness') + box.prop(self, 'strandsize') + box.prop(self, 'width') + box.prop(self, 'resolution') + box.prop(self, 'pointy') + + +def add_object_button(self, context): + self.layout.operator(Braid.bl_idname, text="Add Braid", icon='PLUGIN') + + +def register(): + bpy.utils.register_class(Braid) + bpy.types.INFO_MT_mesh_add.append(add_object_button) + + +def unregister(): + bpy.utils.unregister_class(Braid) + bpy.types.INFO_MT_mesh_add.remove(add_object_button) + +if __name__ == "__main__": + register() diff --git a/add_curve_extra_objects/add_curve_celtic_links.py b/add_curve_extra_objects/add_curve_celtic_links.py new file mode 100644 index 0000000000000000000000000000000000000000..b1171e92967115ff23b2bd4dda537f2368b92476 --- /dev/null +++ b/add_curve_extra_objects/add_curve_celtic_links.py @@ -0,0 +1,249 @@ +# Blender plugin for generating celtic knot curves from 3d meshes +# See README for more information +# +# The MIT License (MIT) +# +# Copyright (c) 2013 Adam Newgas +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +bl_info = { + "name": "Celtic Knot", + "description": "", + "author": "Adam Newgas", + "version": (0, 1, 1), + "blender": (2, 74, 0), + "location": "View3D > Add > Curve", + "warning": "", + "wiki_url": "https://github.com/BorisTheBrave/celtic-knot/wiki", + "category": "Add Curve"} + +import bpy +import bmesh +from collections import defaultdict +from mathutils import Vector +from math import pi, sin, cos + + +class CelticKnotOperator(bpy.types.Operator): + bl_idname = "curve.celtic_links" + bl_label = "Celtic Links" + bl_description = 'Select low poly Mesh Object to cover with Knitted Links' + bl_options = {'REGISTER', 'UNDO', 'PRESET'} + + weave_up = bpy.props.FloatProperty(name="Weave Up", + description="Distance to shift curve upwards over knots", + subtype="DISTANCE", + unit="LENGTH") + weave_down = bpy.props.FloatProperty(name="Weave Down", + description="Distance to shift curve downward under knots", + subtype="DISTANCE", + unit="LENGTH") + handle_types = [("ALIGNED", "Aligned", "Points at a fixed crossing angle"), + ("AUTO", "Auto", "Automatic control points")] + handle_type = bpy.props.EnumProperty(items=handle_types, + name="Handle Type", + description="Controls what type the bezier control points use", + default="AUTO") + crossing_angle = bpy.props.FloatProperty(name="Crossing Angle", + description="Aligned only: the angle between curves in a knot", + default=pi / 4, + min=0, max=pi / 2, + subtype="ANGLE", + unit="ROTATION") + crossing_strength = bpy.props.FloatProperty(name="Crossing Strength", + description="Aligned only: strenth of bezier control points", + soft_min=0, + subtype="DISTANCE", + unit="LENGTH") + + handle_type_map = {"AUTO": "AUTOMATIC", "ALIGNED": "ALIGNED"} + geo_bDepth = bpy.props.FloatProperty( + name="Bevel Depth", + default=0.04, + min=0, soft_min=0, + description="Bevel Depth", + ) + + @classmethod + def poll(cls, context): + ob = context.active_object + # return True + return ((ob is not None) and + (ob.mode == "OBJECT") and + (ob.type == "MESH") and + (context.mode == "OBJECT")) + + def execute(self, context): + # Cache some values + s = sin(self.crossing_angle) * self.crossing_strength + c = cos(self.crossing_angle) * self.crossing_strength + handle_type = self.handle_type + weave_up = self.weave_up + weave_down = self.weave_down + # Create the new object + orig_obj = obj = context.active_object + curve = bpy.data.curves.new("Celtic", "CURVE") + curve.dimensions = "3D" + curve.twist_mode = "MINIMUM" + curve.fill_mode = "FULL" + curve.bevel_depth = 0.015 + curve.extrude = 0.003 + curve.bevel_resolution = 4 + obj = obj.data + midpoints = [] + # Compute all the midpoints of each edge + for e in obj.edges.values(): + v1 = obj.vertices[e.vertices[0]] + v2 = obj.vertices[e.vertices[1]] + m = (v1.co + v2.co) / 2.0 + midpoints.append(m) + + bm = bmesh.new() + bm.from_mesh(obj) + # Stores which loops the curve has already passed through + loops_entered = defaultdict(lambda: False) + loops_exited = defaultdict(lambda: False) + # Loops on the boundary of a surface + + def ignorable_loop(loop): + return len(loop.link_loops) == 0 + # Starting at loop, build a curve one vertex at a time + # until we start where we came from + # Forward means that for any two edges the loop crosses + # sharing a face, it is passing through in clockwise order + # else anticlockwise + + def make_loop(loop, forward): + current_spline = curve.splines.new("BEZIER") + current_spline.use_cyclic_u = True + first = True + # Data for the spline + # It's faster to store in an array and load into blender + # at once + cos = [] + handle_lefts = [] + handle_rights = [] + while True: + if forward: + if loops_exited[loop]: + break + loops_exited[loop] = True + # Follow the face around, ignoring boundary edges + while True: + loop = loop.link_loop_next + if not ignorable_loop(loop): + break + assert loops_entered[loop] == False + loops_entered[loop] = True + v = loop.vert.index + prev_loop = loop + # Find next radial loop + assert loop.link_loops[0] != loop + loop = loop.link_loops[0] + forward = loop.vert.index == v + else: + if loops_entered[loop]: + break + loops_entered[loop] = True + # Follow the face around, ignoring boundary edges + while True: + v = loop.vert.index + loop = loop.link_loop_prev + if not ignorable_loop(loop): + break + assert loops_exited[loop] == False + loops_exited[loop] = True + prev_loop = loop + # Find next radial loop + assert loop.link_loops[-1] != loop + loop = loop.link_loops[-1] + forward = loop.vert.index == v + if not first: + current_spline.bezier_points.add() + first = False + midpoint = midpoints[loop.edge.index] + normal = loop.calc_normal() + prev_loop.calc_normal() + normal.normalize() + offset = weave_up if forward else weave_down + midpoint = midpoint + offset * normal + cos.extend(midpoint) + if handle_type != "AUTO": + tangent = loop.link_loop_next.vert.co - loop.vert.co + tangent.normalize() + binormal = normal.cross(tangent).normalized() + if not forward: + tangent *= -1 + s_binormal = s * binormal + c_tangent = c * tangent + handle_left = midpoint - s_binormal - c_tangent + handle_right = midpoint + s_binormal + c_tangent + handle_lefts.extend(handle_left) + handle_rights.extend(handle_right) + points = current_spline.bezier_points + points.foreach_set("co", cos) + if handle_type != "AUTO": + points.foreach_set("handle_left", handle_lefts) + points.foreach_set("handle_right", handle_rights) + + # Attempt to start a loop at each untouched loop in the entire mesh + for face in bm.faces: + for loop in face.loops: + if ignorable_loop(loop): + continue + if not loops_exited[loop]: + make_loop(loop, True) + if not loops_entered[loop]: + make_loop(loop, False) + # Create an object from the curve + from bpy_extras import object_utils + object_utils.object_data_add(context, curve, operator=None) + # Set the handle type (this is faster than setting it pointwise) + bpy.ops.object.editmode_toggle() + bpy.ops.curve.select_all(action="SELECT") + bpy.ops.curve.handle_type_set(type=self.handle_type_map[handle_type]) + # Some blender versions lack the default + bpy.ops.curve.radius_set(radius=1.0) + bpy.ops.object.editmode_toggle() + # Restore active selection + curve_obj = context.active_object + context.scene.objects.active = orig_obj + + # If thick, then give it a bevel_object and convert to mesh + + return {'FINISHED'} + + +def menu_func(self, context): + self.layout.operator(CelticKnotOperator.bl_idname, + text="Celtic Knot From Mesh", + icon='PLUGIN') + + +def register(): + bpy.utils.register_module(__name__) + bpy.types.INFO_MT_curve_add.append(menu_func) + + +def unregister(): + bpy.types.INFO_MT_curve_add.remove(menu_func) + bpy.utils.unregister_module(__name__) + +if __name__ == "__main__": + register() diff --git a/add_curve_extra_objects/add_curve_curly.py b/add_curve_extra_objects/add_curve_curly.py new file mode 100644 index 0000000000000000000000000000000000000000..5201bf626b593312dae21ec3b2bbadb5af133b78 --- /dev/null +++ b/add_curve_extra_objects/add_curve_curly.py @@ -0,0 +1,386 @@ +bl_info = { + "name": "Curly Curves", + "author": "Cmomoney", + "version": (1, 17), + "blender": (2, 69, 0), + "location": "View3D > Add > Curve > Curly Curve", + "description": "Adds a new Curly Curve", + "warning": "", + "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Curve/Curly_Curves", + "tracker_url": "https://projects.blender.org/tracker/index.php?func=detail&aid=37299&group_id=153&atid=467", + "category": "Add Curve"} + +import bpy +from bpy.types import Operator +from bpy.props import * +from bpy_extras.object_utils import AddObjectHelper, object_data_add +from mathutils import Vector + + +def add_type6(self, context): + + scale_x = self.scale_x + scale_y = self.scale_y + verts = [[0.047131 * scale_x, 0.065832 * scale_y, + 0.0, 0.010396 * scale_x, -0.186771 * scale_y, + 0.0, 0.076107 * scale_x, 0.19414 * scale_y, + 0.0, 0.0 * scale_x, -1.0 * scale_y, 0.0], + [0.451396 * scale_x, -0.48376 * scale_y, + 0.0, 0.433623 * scale_x, -0.587557 * scale_y, + 0.0, 0.525837 * scale_x, -0.423363 * scale_y, + 0.0, 0.15115 * scale_x, -0.704345 * scale_y, 0.0]] + lhandles = [[(-0.067558 * scale_x, 0.078418 * scale_y, 0.0), + (0.168759 * scale_x, -0.154334 * scale_y, 0.0), + (-0.236823 * scale_x, 0.262436 * scale_y, 0.0), + (0.233116 * scale_x, -0.596115 * scale_y, 0.0)], + [(0.498001 * scale_x, -0.493434 * scale_y, 0.0), + (0.375618 * scale_x, -0.55465 * scale_y, 0.0), + (0.634373 * scale_x, -0.49873 * scale_y, 0.0), + (0.225277 * scale_x, -0.526814 * scale_y, 0.0)]] + rhandles = [[(0.161825 * scale_x, 0.053245 * scale_y, 0.0), + (-0.262003 * scale_x, -0.242566 * scale_y, 0.0), + (0.519691 * scale_x, 0.097329 * scale_y, 0.0), + (-0.233116 * scale_x, -1.403885 * scale_y, 0.0)], + [(0.404788 * scale_x, -0.474085 * scale_y, 0.0), + (0.533397 * scale_x, -0.644158 * scale_y, 0.0), + (0.371983 * scale_x, -0.316529 * scale_y, 0.0), + (0.077022 * scale_x, -0.881876 * scale_y, 0.0)]] + make_curve(self, context, verts, lhandles, rhandles) + + +def add_type5(self, context): + + scale_x = self.scale_x + scale_y = self.scale_y + verts = [[0.047131 * scale_x, 0.065832 * scale_y, 0.0, 0.010396 * scale_x, -0.186771 * scale_y, + 0.0, 0.076107 * scale_x, 0.19414 * scale_y, 0.0, 0.0 * scale_x, -1.0 * scale_y, 0.0], + [0.086336 * scale_x, -0.377611 * scale_y, 0.0, 0.022417 * scale_x, -0.461301 * scale_y, 0.0, + 0.079885 * scale_x, -0.281968 * scale_y, 0.0, 0.129212 * scale_x, -0.747702 * scale_y, 0.0]] + lhandles = [[(-0.067558 * scale_x, 0.078419 * scale_y, 0.0), + (0.168759 * scale_x, -0.154335 * scale_y, 0.0), + (-0.236823 * scale_x, 0.262436 * scale_y, 0.0), + (0.233116 * scale_x, -0.596115 * scale_y, 0.0)], + [(0.047518 * scale_x, -0.350065 * scale_y, 0.0), + (0.086012 * scale_x, -0.481379 * scale_y, 0.0), + (-0.049213 * scale_x, -0.253793 * scale_y, 0.0), + (0.208763 * scale_x, -0.572534 * scale_y, 0.0)]] + rhandles = [[(0.161825 * scale_x, 0.053245 * scale_y, 0.0), + (-0.262003 * scale_x, -0.242566 * scale_y, 0.0), + (0.519691 * scale_x, 0.097329 * scale_y, 0.0), + (-0.233116 * scale_x, -1.403885 * scale_y, 0.0)], + [(0.125156 * scale_x, -0.405159 * scale_y, 0.0), + (-0.086972 * scale_x, -0.426766 * scale_y, 0.0), + (0.262886 * scale_x, -0.321908 * scale_y, 0.0), + (0.049661 * scale_x, -0.92287 * scale_y, 0.0)]] + make_curve(self, context, verts, lhandles, rhandles) + + +def add_type8(self, context): + + scale_x = self.scale_x + scale_y = self.scale_y + verts = [[-0.850431 * scale_x, -0.009091 * scale_y, + 0.0, -0.818807 * scale_x, -0.130518 * scale_y, + 0.0, -0.944931 * scale_x, 0.055065 * scale_y, + 0.0, -0.393355 * scale_x, -0.035521 * scale_y, + 0.0, 0.0 * scale_x, 0.348298 * scale_y, + 0.0, 0.393355 * scale_x, -0.035521 * scale_y, + 0.0, 0.978373 * scale_x, 0.185638 * scale_y, + 0.0, 0.771617 * scale_x, 0.272819 * scale_y, + 0.0, 0.864179 * scale_x, 0.188103 * scale_y, 0.0]] + lhandles = [[(-0.90478 * scale_x, -0.025302 * scale_y, 0.0), + (-0.753279 * scale_x, -0.085571 * scale_y, 0.0), + (-1.06406 * scale_x, -0.047879 * scale_y, 0.0), + (-0.622217 * scale_x, -0.022501 * scale_y, 0.0), + (0.181 * scale_x, 0.34879 * scale_y, 0.0), + (-0.101464 * scale_x, -0.063669 * scale_y, 0.0), + (0.933064 * scale_x, 0.03001 * scale_y, 0.0), + (0.82418 * scale_x, 0.39899 * scale_y, 0.0), + (0.827377 * scale_x, 0.144945 * scale_y, 0.0)]] + rhandles = [[(-0.796079 * scale_x, 0.007121 * scale_y, 0.0), + (-0.931521 * scale_x, -0.207832 * scale_y, 0.0), + (-0.822288 * scale_x, 0.161045 * scale_y, 0.0), + (0.101464 * scale_x, -0.063671 * scale_y, 0.0), + (-0.181193 * scale_x, 0.347805 * scale_y, 0.0), + (0.622217 * scale_x, -0.022502 * scale_y, 0.0), + (1.022383 * scale_x, 0.336808 * scale_y, 0.0), + (0.741059 * scale_x, 0.199468 * scale_y, 0.0), + (0.900979 * scale_x, 0.231258 * scale_y, 0.0)]] + make_curve(self, context, verts, lhandles, rhandles) + + +def add_type3(self, context): + + scale_x = self.scale_x + scale_y = self.scale_y + verts = [[-0.78652 * scale_x, -0.070157 * scale_y, + 0.0, -0.697972 * scale_x, -0.247246 * scale_y, + 0.0, -0.953385 * scale_x, -0.002048 * scale_y, + 0.0, 0.0 * scale_x, 0.0 * scale_y, + 0.0, 0.917448 * scale_x, 0.065788 * scale_y, + 0.0, 0.448535 * scale_x, 0.515947 * scale_y, + 0.0, 0.6111 * scale_x, 0.190831 * scale_y, 0.0]] + lhandles = [[(-0.86511 * scale_x, -0.112965 * scale_y, 0.0), + (-0.61153 * scale_x, -0.156423 * scale_y, 0.0), + (-1.103589 * scale_x, -0.199934 * scale_y, 0.0), + (-0.446315 * scale_x, 0.135163 * scale_y, 0.0), + (0.669383 * scale_x, -0.254463 * scale_y, 0.0), + (0.721512 * scale_x, 0.802759 * scale_y, 0.0), + (0.466815 * scale_x, 0.112232 * scale_y, 0.0)]] + rhandles = [[(-0.707927 * scale_x, -0.027348 * scale_y, 0.0), + (-0.846662 * scale_x, -0.40347 * scale_y, 0.0), + (-0.79875 * scale_x, 0.201677 * scale_y, 0.0), + (0.446315 * scale_x, -0.135163 * scale_y, 0.0), + (1.196752 * scale_x, 0.42637 * scale_y, 0.0), + (0.289834 * scale_x, 0.349204 * scale_y, 0.0), + (0.755381 * scale_x, 0.269428 * scale_y, 0.0)]] + make_curve(self, context, verts, lhandles, rhandles) + + +def add_type2(self, context): + + scale_x = self.scale_x + scale_y = self.scale_y + verts = [[-0.719632 * scale_x, -0.08781 * scale_y, + 0.0, -0.605138 * scale_x, -0.31612 * scale_y, + 0.0, -0.935392 * scale_x, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.935392 * scale_x, 0.0, 0.0, 0.605138 * scale_x, + -0.316119 * scale_y, 0.0, 0.719632 * scale_x, -0.08781 * scale_y, 0.0]] + lhandles = [[(-0.82125 * scale_x, -0.142999 * scale_y, 0.0), + (-0.493366 * scale_x, -0.199027 * scale_y, 0.0), + (-1.129601 * scale_x, -0.25513 * scale_y, 0.0), + (-0.467584 * scale_x, 0.00044 * scale_y, 0.0), + (0.735439 * scale_x, 0.262646 * scale_y, 0.0), + (0.797395 * scale_x, -0.517531 * scale_y, 0.0), + (0.618012 * scale_x, -0.032614 * scale_y, 0.0)]] + rhandles = [[(-0.618009 * scale_x, -0.032618 * scale_y, 0.0), + (-0.797396 * scale_x, -0.517532 * scale_y, 0.0), + (-0.735445 * scale_x, 0.262669 * scale_y, 0.0), + (0.468041 * scale_x, -0.00044 * scale_y, 0.0), + (1.129616 * scale_x, -0.255119 * scale_y, 0.0), + (0.493365 * scale_x, -0.199025 * scale_y, 0.0), + (0.821249 * scale_x, -0.143004 * scale_y, 0.0)]] + make_curve(self, context, verts, lhandles, rhandles) + + +def add_type10(self, context): + + scale_x = self.scale_x + scale_y = self.scale_y + verts = [[-0.999637 * scale_x, 0.000348 * scale_y, + 0.0, 0.259532 * scale_x, -0.017841 * scale_y, + 0.0, 0.482303 * scale_x, 0.780429 * scale_y, + 0.0, 0.573183 * scale_x, 0.506898 * scale_y, 0.0], + [0.259532 * scale_x, -0.017841 * scale_y, + 0.0, 0.554919 * scale_x, -0.140918 * scale_y, + 0.0, 0.752264 * scale_x, -0.819275 * scale_y, + 0.0, 0.824152 * scale_x, -0.514881 * scale_y, 0.0]] + lhandles = [[(-1.258333 * scale_x, -0.258348 * scale_y, 0.0), + (-0.240006 * scale_x, -0.15259 * scale_y, 0.0), + (0.79037 * scale_x, 0.857575 * scale_y, 0.0), + (0.376782 * scale_x, 0.430157 * scale_y, 0.0)], + [(0.224917 * scale_x, -0.010936 * scale_y, 0.0), + (0.514858 * scale_x, -0.122809 * scale_y, 0.0), + (1.057957 * scale_x, -0.886925 * scale_y, 0.0), + (0.61945 * scale_x, -0.464285 * scale_y, 0.0)]] + rhandles = [[(-0.74094 * scale_x, 0.259045 * scale_y, 0.0), + (0.768844 * scale_x, 0.119545 * scale_y, 0.0), + (0.279083 * scale_x, 0.729538 * scale_y, 0.0), + (0.643716 * scale_x, 0.534458 * scale_y, 0.0)], + [(0.294147 * scale_x, -0.024746 * scale_y, 0.0), + (1.03646 * scale_x, -0.358598 * scale_y, 0.0), + (0.547718 * scale_x, -0.774008 * scale_y, 0.0), + (0.897665 * scale_x, -0.533051 * scale_y, 0.0)]] + make_curve(self, context, verts, lhandles, rhandles) + + +def add_type9(self, context): + + scale_x = self.scale_x + scale_y = self.scale_y + verts = [[0.260968 * scale_x, -0.668118 * scale_y, + 0.0, 0.108848 * scale_x, -0.381587 * scale_y, + 0.0, 0.537002 * scale_x, -0.77303 * scale_y, + 0.0, -0.600421 * scale_x, -0.583106 * scale_y, + 0.0, -0.600412 * scale_x, 0.583103 * scale_y, + 0.0, 0.537002 * scale_x, 0.773025 * scale_y, + 0.0, 0.108854 * scale_x, 0.381603 * scale_y, + 0.0, 0.260966 * scale_x, 0.668129 * scale_y, 0.0]] + lhandles = [[(0.387973 * scale_x, -0.594856 * scale_y, 0.0), + (-0.027835 * scale_x, -0.532386 * scale_y, 0.0), + (0.775133 * scale_x, -0.442883 * scale_y, 0.0), + (-0.291333 * scale_x, -1.064385 * scale_y, 0.0), + (-0.833382 * scale_x, 0.220321 * scale_y, 0.0), + (0.291856 * scale_x, 1.112891 * scale_y, 0.0), + (0.346161 * scale_x, 0.119777 * scale_y, 0.0), + (0.133943 * scale_x, 0.741389 * scale_y, 0.0)]] + rhandles = [[(0.133951 * scale_x, -0.741386 * scale_y, 0.0), + (0.346154 * scale_x, -0.119772 * scale_y, 0.0), + (0.291863 * scale_x, -1.112896 * scale_y, 0.0), + (-0.833407 * scale_x, -0.220324 * scale_y, 0.0), + (-0.29134 * scale_x, 1.064389 * scale_y, 0.0), + (0.775125 * scale_x, 0.442895 * scale_y, 0.0), + (-0.029107 * scale_x, 0.533819 * scale_y, 0.0), + (0.387981 * scale_x, 0.594873 * scale_y, 0.0)]] + make_curve(self, context, verts, lhandles, rhandles) + + +def add_type7(self, context): + + scale_x = self.scale_x + scale_y = self.scale_y + verts = [[-0.850431 * scale_x, -0.009091 * scale_y, + 0.0, -0.818807 * scale_x, -0.130518 * scale_y, + 0.0, -0.944931 * scale_x, 0.055065 * scale_y, 0.0, + -0.393355 * scale_x, -0.035521 * scale_y, + 0.0, 0.0 * scale_x, 0.348298 * scale_y, + 0.0, 0.393355 * scale_x, -0.035521 * scale_y, + 0.0, 0.944931 * scale_x, 0.055065 * scale_y, + 0.0, 0.818807 * scale_x, -0.130518 * scale_y, + 0.0, 0.850431 * scale_x, -0.009091 * scale_y,0.0]] + lhandles = [[(-0.90478 * scale_x, -0.025302 * scale_y, 0.0), + (-0.753279 * scale_x, -0.085571 * scale_y, 0.0), + (-1.06406 * scale_x, -0.047879 * scale_y, 0.0), + (-0.622217 * scale_x, -0.022502 * scale_y, 0.0), + (0.181 * scale_x, 0.348791 * scale_y, 0.0), + (-0.101464 * scale_x, -0.063671 * scale_y, 0.0), + (0.822288 * scale_x, 0.161045 * scale_y, 0.0), + (0.931521 * scale_x, -0.207832 * scale_y, 0.0), + (0.796079 * scale_x, 0.007121 * scale_y, 0.0)]] + rhandles = [[(-0.796079 * scale_x, 0.007121 * scale_y, 0.0), + (-0.931521 * scale_x, -0.207832 * scale_y, 0.0), + (-0.822288 * scale_x, 0.161045 * scale_y, 0.0), + (0.101464 * scale_x, -0.063671 * scale_y, 0.0), + (-0.181193 * scale_x, 0.347805 * scale_y, 0.0), + (0.622217 * scale_x, -0.022502 * scale_y, 0.0), + (1.06406 * scale_x, -0.047879 * scale_y, 0.0), + (0.753279 * scale_x, -0.085571 * scale_y, 0.0), + (0.90478 * scale_x, -0.025302 * scale_y, 0.0)]] + make_curve(self, context, verts, lhandles, rhandles) + + +def add_type4(self, context): + + scale_x = self.scale_x + scale_y = self.scale_y + verts = [[0.072838 * scale_x, -0.071461 * scale_y, + 0.0, -0.175451 * scale_x, -0.130711 * scale_y, + 0.0, 0.207269 * scale_x, 0.118064 * scale_y, + 0.0, 0 * scale_x, -1.0 * scale_y, 0.0]] + lhandles = [[(0.042135 * scale_x, 0.039756 * scale_y, 0), + (-0.086769 * scale_x, -0.265864 * scale_y, 0), + (0.002865 * scale_x, 0.364657 * scale_y, 0), + (0.233116 * scale_x, -0.596115 * scale_y, 0)]] + rhandles = [[(0.103542 * scale_x, -0.182683 * scale_y, 0), + (-0.327993 * scale_x, 0.101765 * scale_y, 0), + (0.417702 * scale_x, -0.135803 * scale_y, 0), + (-0.233116 * scale_x, -1.403885 * scale_y, 0)]] + make_curve(self, context, verts, lhandles, rhandles) + + +def add_type1(self, context): + + scale_x = self.scale_x + scale_y = self.scale_y + verts = [[-0.71753 * scale_x, -0.08781 * scale_y, + 0, -0.60337 * scale_x, -0.31612 * scale_y, 0, + -0.93266 * scale_x, 0, 0, 0, 0, 0, 0.93266 * scale_x, + 0, 0, 0.60337 * scale_x, 0.31612 * scale_y, + 0, 0.71753 * scale_x, 0.08781 * scale_y, 0]] + lhandles = [[(-0.81885 * scale_x, -0.143002 * scale_y, 0), + (-0.491926 * scale_x, -0.199026 * scale_y, 0), + (-1.126316 * scale_x, -0.255119 * scale_y, 0), + (-0.446315 * scale_x, 0.135164 * scale_y, 0), + (0.733297 * scale_x, -0.26265 * scale_y, 0), + (0.795065 * scale_x, 0.517532 * scale_y, 0), + (0.616204 * scale_x, 0.03262 * scale_y, 0)]] + rhandles = [[(-0.616204 * scale_x, -0.032618 * scale_y, 0), + (-0.795067 * scale_x, -0.517532 * scale_y, 0), + (-0.733297 * scale_x, 0.262651 * scale_y, 0), + (0.446315 * scale_x, -0.135163 * scale_y, 0), + (1.126316 * scale_x, 0.255119 * scale_y, 0), + (0.491924 * scale_x, 0.199026 * scale_y, 0), + (0.81885 * scale_x, 0.143004 * scale_y, 0)]] + make_curve(self, context, verts, lhandles, rhandles) + + +def make_curve(self, context, verts, lh, rh): + + scale_x = self.scale_x + scale_y = self.scale_y + type = self.type + curve_data = bpy.data.curves.new(name='CurlyCurve', type='CURVE') + curve_data.dimensions = '3D' + for p in range(len(verts)): + c = 0 + spline = curve_data.splines.new(type='BEZIER') + spline.bezier_points.add(len(verts[p]) / 3 - 1) + spline.bezier_points.foreach_set('co', verts[p]) + for bp in spline.bezier_points: + bp.handle_left_type = 'ALIGNED' + bp.handle_right_type = 'ALIGNED' + bp.handle_left.xyz = lh[p][c] + bp.handle_right.xyz = rh[p][c] + c += 1 + # something weird with this one + if type == 1 or type == 2 or type == 3: + spline.bezier_points[3].handle_left.xyz = lh[p][3] + object_data_add(context, curve_data, operator=self) + + +class add_curlycurve(Operator, AddObjectHelper): + """Create a Curly Curve""" + bl_idname = "curve.curlycurve" + bl_label = "Add Curly Curve" + bl_options = {'REGISTER', 'UNDO'} + + type = IntProperty(name='Type', description='Type of curly curve', default=1, min=1, max=10) + scale_x = FloatProperty(name="scale x", description="scale on x axis", default=1.0) + scale_y = FloatProperty(name="scale y", description="scale on y axis", default=1.0) + + def execute(self, context): + if self.type == 1: + add_type1(self, context) + if self.type == 2: + add_type2(self, context) + if self.type == 3: + add_type3(self, context) + if self.type == 4: + add_type4(self, context) + if self.type == 5: + add_type5(self, context) + if self.type == 6: + add_type6(self, context) + if self.type == 7: + add_type7(self, context) + if self.type == 8: + add_type8(self, context) + if self.type == 9: + add_type9(self, context) + if self.type == 10: + add_type10(self, context) + return {'FINISHED'} + +# Registration + + +def add_curlycurve_button(self, context): + self.layout.operator( + add_curlycurve.bl_idname, + text="Add Curly Curve", + icon='PLUGIN') + + +def register(): + bpy.utils.register_class(add_curlycurve) + # bpy.utils.register_manual_map(add_object_manual_map) + bpy.types.INFO_MT_curve_add.append(add_curlycurve_button) + + +def unregister(): + bpy.utils.unregister_class(add_curlycurve) + # bpy.utils.unregister_manual_map(add_object_manual_map) + bpy.types.INFO_MT_curve_add.remove(add_curlycurve_button) + +if __name__ == "__main__": + register() diff --git a/add_curve_extra_objects/add_curve_simple.py b/add_curve_extra_objects/add_curve_simple.py new file mode 100644 index 0000000000000000000000000000000000000000..111d8c08f3e2b349c0813b852357e8bc932f05e7 --- /dev/null +++ b/add_curve_extra_objects/add_curve_simple.py @@ -0,0 +1,1612 @@ +# ##### 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 ##### + +bl_info = { + 'name': 'Simple Curve', + 'author': 'Spivak Vladimir (http://cwolf3d.korostyshev.net)', + 'version': (1, 5, 2), + 'blender': (2, 6, 9), + 'location': 'View3D > Add > Curve', + 'description': 'Adds Simple Curve', + 'warning': '', # used for warning icon and text in addons panel + 'wiki_url': 'http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Curve/Simple_curves', + "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/", + 'category': 'Add Curve'} + + +# ------------------------------------------------------------ +#### import modules +import bpy +from bpy.props import * +from mathutils import * +from math import * +from bpy_extras.object_utils import * +from random import * + +# ------------------------------------------------------------ +# Point: + + +def SimplePoint(): + newpoints = [] + + newpoints.append([0.0, 0.0, 0.0]) + + return newpoints + +# ------------------------------------------------------------ +# Line: + + +def SimpleLine(c1=[0.0, 0.0, 0.0], c2=[2.0, 2.0, 2.0]): + newpoints = [] + + c3 = Vector(c2) - Vector(c1) + newpoints.append([0.0, 0.0, 0.0]) + newpoints.append([c3[0], c3[1], c3[2]]) + + return newpoints + +# ------------------------------------------------------------ +# Angle: + + +def SimpleAngle(length=1.0, angle=45.0): + newpoints = [] + + angle = radians(angle) + newpoints.append([length, 0.0, 0.0]) + newpoints.append([0.0, 0.0, 0.0]) + newpoints.append([length * cos(angle), length * sin(angle), 0.0]) + + return newpoints + +# ------------------------------------------------------------ +# Distance: + + +def SimpleDistance(length=1.0, center=True): + newpoints = [] + + if center: + newpoints.append([-length / 2, 0.0, 0.0]) + newpoints.append([length / 2, 0.0, 0.0]) + else: + newpoints.append([0.0, 0.0, 0.0]) + newpoints.append([length, 0.0, 0.0]) + + return newpoints + +# ------------------------------------------------------------ +# Circle: + + +def SimpleCircle(sides=4, radius=1.0): + newpoints = [] + + angle = radians(360) / sides + newpoints.append([radius, 0, 0]) + j = 1 + while j < sides: + t = angle * j + x = cos(t) * radius + y = sin(t) * radius + newpoints.append([x, y, 0]) + j += 1 + + return newpoints + +# ------------------------------------------------------------ +# Ellipse: + + +def SimpleEllipse(a=2.0, b=1.0): + newpoints = [] + + newpoints.append([a, 0.0, 0.0]) + newpoints.append([0.0, b, 0.0]) + newpoints.append([-a, 0.0, 0.0]) + newpoints.append([0.0, -b, 0.0]) + + return newpoints + +# ------------------------------------------------------------ +# Arc: + + +def SimpleArc(sides=0, radius=1.0, startangle=0.0, endangle=45.0): + newpoints = [] + + startangle = radians(startangle) + endangle = radians(endangle) + sides += 1 + + angle = (endangle - startangle) / sides + x = cos(startangle) * radius + y = sin(startangle) * radius + newpoints.append([x, y, 0]) + j = 1 + while j < sides: + t = angle * j + x = cos(t + startangle) * radius + y = sin(t + startangle) * radius + newpoints.append([x, y, 0]) + j += 1 + x = cos(endangle) * radius + y = sin(endangle) * radius + newpoints.append([x, y, 0]) + + return newpoints + +# ------------------------------------------------------------ +# Sector: + + +def SimpleSector(sides=0, radius=1.0, startangle=0.0, endangle=45.0): + newpoints = [] + + startangle = radians(startangle) + endangle = radians(endangle) + sides += 1 + + newpoints.append([0, 0, 0]) + angle = (endangle - startangle) / sides + x = cos(startangle) * radius + y = sin(startangle) * radius + newpoints.append([x, y, 0]) + j = 1 + while j < sides: + t = angle * j + x = cos(t + startangle) * radius + y = sin(t + startangle) * radius + newpoints.append([x, y, 0]) + j += 1 + x = cos(endangle) * radius + y = sin(endangle) * radius + newpoints.append([x, y, 0]) + + return newpoints + +# ------------------------------------------------------------ +# Segment: + + +def SimpleSegment(sides=0, a=2.0, b=1.0, startangle=0.0, endangle=45.0): + newpoints = [] + + startangle = radians(startangle) + endangle = radians(endangle) + sides += 1 + + angle = (endangle - startangle) / sides + x = cos(startangle) * a + y = sin(startangle) * a + newpoints.append([x, y, 0]) + j = 1 + while j < sides: + t = angle * j + x = cos(t + startangle) * a + y = sin(t + startangle) * a + newpoints.append([x, y, 0]) + j += 1 + x = cos(endangle) * a + y = sin(endangle) * a + newpoints.append([x, y, 0]) + + x = cos(endangle) * b + y = sin(endangle) * b + newpoints.append([x, y, 0]) + j = sides + while j > 0: + t = angle * j + x = cos(t + startangle) * b + y = sin(t + startangle) * b + newpoints.append([x, y, 0]) + j -= 1 + x = cos(startangle) * b + y = sin(startangle) * b + newpoints.append([x, y, 0]) + + return newpoints + +# ------------------------------------------------------------ +# Rectangle: + + +def SimpleRectangle(width=2.0, length=2.0, rounded=0.0, center=True): + newpoints = [] + + r = rounded / 2 + + if center: + x = width / 2 + y = length / 2 + if rounded != 0.0: + newpoints.append([-x + r, y, 0.0]) + newpoints.append([x - r, y, 0.0]) + newpoints.append([x, y - r, 0.0]) + newpoints.append([x, -y + r, 0.0]) + newpoints.append([x - r, -y, 0.0]) + newpoints.append([-x + r, -y, 0.0]) + newpoints.append([-x, -y + r, 0.0]) + newpoints.append([-x, y - r, 0.0]) + else: + newpoints.append([-x, y, 0.0]) + newpoints.append([x, y, 0.0]) + newpoints.append([x, -y, 0.0]) + newpoints.append([-x, -y, 0.0]) + + else: + x = width + y = length + if rounded != 0.0: + newpoints.append([r, y, 0.0]) + newpoints.append([x - r, y, 0.0]) + newpoints.append([x, y - r, 0.0]) + newpoints.append([x, r, 0.0]) + newpoints.append([x - r, 0.0, 0.0]) + newpoints.append([r, 0.0, 0.0]) + newpoints.append([0.0, r, 0.0]) + newpoints.append([0.0, y - r, 0.0]) + else: + newpoints.append([0.0, 0.0, 0.0]) + newpoints.append([0.0, y, 0.0]) + newpoints.append([x, y, 0.0]) + newpoints.append([x, 0.0, 0.0]) + + return newpoints + +# ------------------------------------------------------------ +# Rhomb: + + +def SimpleRhomb(width=2.0, length=2.0, center=True): + newpoints = [] + x = width / 2 + y = length / 2 + + if center: + newpoints.append([-x, 0.0, 0.0]) + newpoints.append([0.0, y, 0.0]) + newpoints.append([x, 0.0, 0.0]) + newpoints.append([0.0, -y, 0.0]) + else: + newpoints.append([x, 0.0, 0.0]) + newpoints.append([0.0, y, 0.0]) + newpoints.append([x, length, 0.0]) + newpoints.append([width, y, 0.0]) + + return newpoints + +# ------------------------------------------------------------ +# Polygon: + + +def SimplePolygon(sides=3, radius=1.0): + newpoints = [] + angle = radians(360.0) / sides + j = 0 + + while j < sides: + t = angle * j + x = sin(t) * radius + y = cos(t) * radius + newpoints.append([x, y, 0.0]) + j += 1 + + return newpoints + +# ------------------------------------------------------------ +# Polygon_ab: + + +def SimplePolygon_ab(sides=3, a=2.0, b=1.0): + newpoints = [] + angle = radians(360.0) / sides + j = 0 + + while j < sides: + t = angle * j + x = sin(t) * a + y = cos(t) * b + newpoints.append([x, y, 0.0]) + j += 1 + + return newpoints + +# ------------------------------------------------------------ +# Trapezoid: + + +def SimpleTrapezoid(a=2.0, b=1.0, h=1.0, center=True): + newpoints = [] + x = a / 2 + y = b / 2 + r = h / 2 + + if center: + newpoints.append([-x, -r, 0.0]) + newpoints.append([-y, r, 0.0]) + newpoints.append([y, r, 0.0]) + newpoints.append([x, -r, 0.0]) + + else: + newpoints.append([0.0, 0.0, 0.0]) + newpoints.append([x - y, h, 0.0]) + newpoints.append([x + y, h, 0.0]) + newpoints.append([a, 0.0, 0.0]) + + return newpoints + +# ------------------------------------------------------------ +# calculates the matrix for the new object +# depending on user pref + + +def align_matrix(context, location): + loc = Matrix.Translation(location) + obj_align = context.user_preferences.edit.object_align + if (context.space_data.type == 'VIEW_3D' + and obj_align == 'VIEW'): + rot = context.space_data.region_3d.view_matrix.to_3x3().inverted().to_4x4() + else: + rot = Matrix() + align_matrix = loc * rot + + return align_matrix + +# ------------------------------------------------------------ +# Main Function + + +def main(context, self, align_matrix): + # deselect all objects + bpy.ops.object.select_all(action='DESELECT') + + # create object + name = self.Simple_Type # Type as name + + # create curve + scene = bpy.context.scene + newCurve = bpy.data.curves.new(name, type='CURVE') # curvedatablock + newSpline = newCurve.splines.new('BEZIER') # spline + + # set curveOptions + newCurve.dimensions = self.shape + newSpline.use_endpoint_u = True + + sides = abs(int((self.Simple_endangle - self.Simple_startangle) / 90)) + + # get verts + if self.Simple_Type == 'Point': + verts = SimplePoint() + newSpline.use_cyclic_u = False + + if self.Simple_Type == 'Line': + verts = SimpleLine(self.Simple_startlocation, self.Simple_endlocation) + newSpline.use_cyclic_u = False + newCurve.dimensions = '3D' + + if self.Simple_Type == 'Distance': + verts = SimpleDistance(self.Simple_length, self.Simple_center) + newSpline.use_cyclic_u = False + + if self.Simple_Type == 'Angle': + verts = SimpleAngle(self.Simple_length, self.Simple_angle) + newSpline.use_cyclic_u = False + + if self.Simple_Type == 'Circle': + if self.Simple_sides < 4: + self.Simple_sides = 4 + verts = SimpleCircle(self.Simple_sides, self.Simple_radius) + newSpline.use_cyclic_u = True + + if self.Simple_Type == 'Ellipse': + verts = SimpleEllipse(self.Simple_a, self.Simple_b) + newSpline.use_cyclic_u = True + + if self.Simple_Type == 'Arc': + if self.Simple_sides < sides: + self.Simple_sides = sides + if self.Simple_radius == 0: + return {'FINISHED'} + verts = SimpleArc(self.Simple_sides, self.Simple_radius, self.Simple_startangle, self.Simple_endangle) + newSpline.use_cyclic_u = False + + if self.Simple_Type == 'Sector': + if self.Simple_sides < sides: + self.Simple_sides = sides + if self.Simple_radius == 0: + return {'FINISHED'} + verts = SimpleSector(self.Simple_sides, self.Simple_radius, self.Simple_startangle, self.Simple_endangle) + newSpline.use_cyclic_u = True + + if self.Simple_Type == 'Segment': + if self.Simple_sides < sides: + self.Simple_sides = sides + if self.Simple_a == 0 or self.Simple_b == 0: + return {'FINISHED'} + verts = SimpleSegment(self.Simple_sides, self.Simple_a, self.Simple_b, self.Simple_startangle, self.Simple_endangle) + newSpline.use_cyclic_u = True + + if self.Simple_Type == 'Rectangle': + verts = SimpleRectangle(self.Simple_width, self.Simple_length, self.Simple_rounded, self.Simple_center) + newSpline.use_cyclic_u = True + + if self.Simple_Type == 'Rhomb': + verts = SimpleRhomb(self.Simple_width, self.Simple_length, self.Simple_center) + newSpline.use_cyclic_u = True + + if self.Simple_Type == 'Polygon': + if self.Simple_sides < 3: + self.Simple_sides = 3 + verts = SimplePolygon(self.Simple_sides, self.Simple_radius) + newSpline.use_cyclic_u = True + + if self.Simple_Type == 'Polygon_ab': + if self.Simple_sides < 3: + self.Simple_sides = 3 + verts = SimplePolygon_ab(self.Simple_sides, self.Simple_a, self.Simple_b) + newSpline.use_cyclic_u = True + + if self.Simple_Type == 'Trapezoid': + verts = SimpleTrapezoid(self.Simple_a, self.Simple_b, self.Simple_h, self.Simple_center) + newSpline.use_cyclic_u = True + + vertArray = [] + for v in verts: + vertArray += v + + newSpline.bezier_points.add(int(len(vertArray) * 0.333333333)) + newSpline.bezier_points.foreach_set('co', vertArray) + + # create object with newCurve + SimpleCurve = bpy.data.objects.new(name, newCurve) # object + scene.objects.link(SimpleCurve) # place in active scene + SimpleCurve.select = True # set as selected + scene.objects.active = SimpleCurve # set as active + SimpleCurve.matrix_world = align_matrix # apply matrix + SimpleCurve.rotation_euler = self.Simple_rotation_euler + + all_points = [p for p in newSpline.bezier_points] + d = 2 * 0.27606262 + n = 0 + for p in all_points: + p.handle_right_type = 'VECTOR' + p.handle_left_type = 'VECTOR' + n += 1 + + if self.Simple_Type == 'Circle' or self.Simple_Type == 'Arc' or self.Simple_Type == 'Sector' or self.Simple_Type == 'Segment' or self.Simple_Type == 'Ellipse': + for p in all_points: + p.handle_right_type = 'FREE' + p.handle_left_type = 'FREE' + + if self.Simple_Type == 'Circle': + i = 0 + for p1 in all_points: + if i != n - 1: + p2 = all_points[i + 1] + u1 = asin(p1.co.y / self.Simple_radius) + u2 = asin(p2.co.y / self.Simple_radius) + if p1.co.x > 0 and p2.co.x < 0: + u1 = acos(p1.co.x / self.Simple_radius) + u2 = acos(p2.co.x / self.Simple_radius) + elif p1.co.x < 0 and p2.co.x > 0: + u1 = acos(p1.co.x / self.Simple_radius) + u2 = acos(p2.co.x / self.Simple_radius) + u = u2 - u1 + if u < 0: + u = -u + l = 4 / 3 * tan(1 / 4 * u) * self.Simple_radius + v1 = Vector((-p1.co.y, p1.co.x, 0)) + v1.normalize() + v2 = Vector((-p2.co.y, p2.co.x, 0)) + v2.normalize() + vh1 = v1 * l + vh2 = v2 * l + v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1 + v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2 + p1.handle_right = v1 + p2.handle_left = v2 + if i == n - 1: + p2 = all_points[0] + u1 = asin(p1.co.y / self.Simple_radius) + u2 = asin(p2.co.y / self.Simple_radius) + if p1.co.x > 0 and p2.co.x < 0: + u1 = acos(p1.co.x / self.Simple_radius) + u2 = acos(p2.co.x / self.Simple_radius) + elif p1.co.x < 0 and p2.co.x > 0: + u1 = acos(p1.co.x / self.Simple_radius) + u2 = acos(p2.co.x / self.Simple_radius) + u = u2 - u1 + if u < 0: + u = -u + l = 4 / 3 * tan(1 / 4 * u) * self.Simple_radius + v1 = Vector((-p1.co.y, p1.co.x, 0)) + v1.normalize() + v2 = Vector((-p2.co.y, p2.co.x, 0)) + v2.normalize() + vh1 = v1 * l + vh2 = v2 * l + v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1 + v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2 + p1.handle_right = v1 + p2.handle_left = v2 + i += 1 + + if self.Simple_Type == 'Ellipse': + all_points[0].handle_right = Vector((self.Simple_a, self.Simple_b * d, 0)) + all_points[0].handle_left = Vector((self.Simple_a, -self.Simple_b * d, 0)) + all_points[1].handle_right = Vector((-self.Simple_a * d, self.Simple_b, 0)) + all_points[1].handle_left = Vector((self.Simple_a * d, self.Simple_b, 0)) + all_points[2].handle_right = Vector((-self.Simple_a, -self.Simple_b * d, 0)) + all_points[2].handle_left = Vector((-self.Simple_a, self.Simple_b * d, 0)) + all_points[3].handle_right = Vector((self.Simple_a * d, -self.Simple_b, 0)) + all_points[3].handle_left = Vector((-self.Simple_a * d, -self.Simple_b, 0)) + + if self.Simple_Type == 'Arc': + i = 0 + for p1 in all_points: + if i != n - 1: + p2 = all_points[i + 1] + u1 = asin(p1.co.y / self.Simple_radius) + u2 = asin(p2.co.y / self.Simple_radius) + if p1.co.x > 0 and p2.co.x < 0: + u1 = acos(p1.co.x / self.Simple_radius) + u2 = acos(p2.co.x / self.Simple_radius) + elif p1.co.x < 0 and p2.co.x > 0: + u1 = acos(p1.co.x / self.Simple_radius) + u2 = acos(p2.co.x / self.Simple_radius) + u = u2 - u1 + if u < 0: + u = -u + l = 4 / 3 * tan(1 / 4 * u) * self.Simple_radius + v1 = Vector((-p1.co.y, p1.co.x, 0)) + v1.normalize() + v2 = Vector((-p2.co.y, p2.co.x, 0)) + v2.normalize() + vh1 = v1 * l + vh2 = v2 * l + if self.Simple_startangle < self.Simple_endangle: + v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1 + v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2 + p1.handle_right = v1 + p2.handle_left = v2 + else: + v1 = Vector((p1.co.x, p1.co.y, 0)) - vh1 + v2 = Vector((p2.co.x, p2.co.y, 0)) + vh2 + p1.handle_right = v1 + p2.handle_left = v2 + i += 1 + + if self.Simple_Type == 'Sector': + i = 0 + for p1 in all_points: + if i == 0: + p1.handle_right_type = 'VECTOR' + p1.handle_left_type = 'VECTOR' + elif i != n - 1: + p2 = all_points[i + 1] + u1 = asin(p1.co.y / self.Simple_radius) + u2 = asin(p2.co.y / self.Simple_radius) + if p1.co.x > 0 and p2.co.x < 0: + u1 = acos(p1.co.x / self.Simple_radius) + u2 = acos(p2.co.x / self.Simple_radius) + elif p1.co.x < 0 and p2.co.x > 0: + u1 = acos(p1.co.x / self.Simple_radius) + u2 = acos(p2.co.x / self.Simple_radius) + u = u2 - u1 + if u < 0: + u = -u + l = 4 / 3 * tan(1 / 4 * u) * self.Simple_radius + v1 = Vector((-p1.co.y, p1.co.x, 0)) + v1.normalize() + v2 = Vector((-p2.co.y, p2.co.x, 0)) + v2.normalize() + vh1 = v1 * l + vh2 = v2 * l + if self.Simple_startangle < self.Simple_endangle: + v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1 + v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2 + p1.handle_right = v1 + p2.handle_left = v2 + else: + v1 = Vector((p1.co.x, p1.co.y, 0)) - vh1 + v2 = Vector((p2.co.x, p2.co.y, 0)) + vh2 + p1.handle_right = v1 + p2.handle_left = v2 + i += 1 + + if self.Simple_Type == 'Segment': + i = 0 + for p1 in all_points: + if i < n / 2 - 1: + p2 = all_points[i + 1] + u1 = asin(p1.co.y / self.Simple_a) + u2 = asin(p2.co.y / self.Simple_a) + if p1.co.x > 0 and p2.co.x < 0: + u1 = acos(p1.co.x / self.Simple_a) + u2 = acos(p2.co.x / self.Simple_a) + elif p1.co.x < 0 and p2.co.x > 0: + u1 = acos(p1.co.x / self.Simple_a) + u2 = acos(p2.co.x / self.Simple_a) + u = u2 - u1 + if u < 0: + u = -u + l = 4 / 3 * tan(1 / 4 * u) * self.Simple_a + v1 = Vector((-p1.co.y, p1.co.x, 0)) + v1.normalize() + v2 = Vector((-p2.co.y, p2.co.x, 0)) + v2.normalize() + vh1 = v1 * l + vh2 = v2 * l + if self.Simple_startangle < self.Simple_endangle: + v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1 + v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2 + p1.handle_right = v1 + p2.handle_left = v2 + else: + v1 = Vector((p1.co.x, p1.co.y, 0)) - vh1 + v2 = Vector((p2.co.x, p2.co.y, 0)) + vh2 + p1.handle_right = v1 + p2.handle_left = v2 + elif i != n / 2 - 1 and i != n - 1: + p2 = all_points[i + 1] + u1 = asin(p1.co.y / self.Simple_b) + u2 = asin(p2.co.y / self.Simple_b) + if p1.co.x > 0 and p2.co.x < 0: + u1 = acos(p1.co.x / self.Simple_b) + u2 = acos(p2.co.x / self.Simple_b) + elif p1.co.x < 0 and p2.co.x > 0: + u1 = acos(p1.co.x / self.Simple_b) + u2 = acos(p2.co.x / self.Simple_b) + u = u2 - u1 + if u < 0: + u = -u + l = 4 / 3 * tan(1 / 4 * u) * self.Simple_b + v1 = Vector((-p1.co.y, p1.co.x, 0)) + v1.normalize() + v2 = Vector((-p2.co.y, p2.co.x, 0)) + v2.normalize() + vh1 = v1 * l + vh2 = v2 * l + if self.Simple_startangle < self.Simple_endangle: + v1 = Vector((p1.co.x, p1.co.y, 0)) - vh1 + v2 = Vector((p2.co.x, p2.co.y, 0)) + vh2 + p1.handle_right = v1 + p2.handle_left = v2 + else: + v1 = Vector((p1.co.x, p1.co.y, 0)) + vh1 + v2 = Vector((p2.co.x, p2.co.y, 0)) - vh2 + p1.handle_right = v1 + p2.handle_left = v2 + + i += 1 + all_points[0].handle_left_type = 'VECTOR' + all_points[n - 1].handle_right_type = 'VECTOR' + all_points[int(n / 2) - 1].handle_right_type = 'VECTOR' + all_points[int(n / 2)].handle_left_type = 'VECTOR' + + SimpleCurve.Simple = True + SimpleCurve.Simple_Change = False + SimpleCurve.Simple_Type = self.Simple_Type + SimpleCurve.Simple_startlocation = self.Simple_startlocation + SimpleCurve.Simple_endlocation = self.Simple_endlocation + SimpleCurve.Simple_a = self.Simple_a + SimpleCurve.Simple_b = self.Simple_b + SimpleCurve.Simple_h = self.Simple_h + SimpleCurve.Simple_angle = self.Simple_angle + SimpleCurve.Simple_startangle = self.Simple_startangle + SimpleCurve.Simple_endangle = self.Simple_endangle + SimpleCurve.Simple_rotation_euler = self.Simple_rotation_euler + SimpleCurve.Simple_sides = self.Simple_sides + SimpleCurve.Simple_radius = self.Simple_radius + SimpleCurve.Simple_center = self.Simple_center + SimpleCurve.Simple_width = self.Simple_width + SimpleCurve.Simple_length = self.Simple_length + SimpleCurve.Simple_rounded = self.Simple_rounded + + bpy.ops.object.mode_set(mode='EDIT', toggle=True) + bpy.ops.curve.select_all(action='SELECT') + bpy.ops.object.mode_set(mode='OBJECT', toggle=True) + + return + +# ------------------------------------------------------------ +# Delete simple curve + + +def SimpleDelete(name): + if bpy.ops.object.mode_set.poll(): + bpy.ops.object.mode_set(mode='OBJECT') + + bpy.context.scene.objects.active = bpy.data.objects[name] + bpy.ops.object.delete() + + return + +# ------------------------------------------------------------ +# Simple operator + + +class Simple(bpy.types.Operator): + '''''' + bl_idname = "curve.simple" + bl_label = "Simple curve" + bl_options = {'REGISTER', 'UNDO'} + bl_description = "adds simple curve" + + # align_matrix for the invoke + align_matrix = Matrix() + + # change properties + Simple = BoolProperty(name="Simple", + default=True, + description="simple curve") + + Simple_Change = BoolProperty(name="Change", + default=False, + description="change simple curve") + + Simple_Delete = StringProperty(name="Delete", + description="Delete simple curve") + + # general properties + Types = [('Point', 'Point', 'Point'), + ('Line', 'Line', 'Line'), + ('Distance', 'Distance', 'Distance'), + ('Angle', 'Angle', 'Angle'), + ('Circle', 'Circle', 'Circle'), + ('Ellipse', 'Ellipse', 'Ellipse'), + ('Arc', 'Arc', 'Arc'), + ('Sector', 'Sector', 'Sector'), + ('Segment', 'Segment', 'Segment'), + ('Rectangle', 'Rectangle', 'Rectangle'), + ('Rhomb', 'Rhomb', 'Rhomb'), + ('Polygon', 'Polygon', 'Polygon'), + ('Polygon_ab', 'Polygon_ab', 'Polygon_ab'), + ('Trapezoid', 'Trapezoid', 'Trapezoid')] + Simple_Type = EnumProperty(name="Type", + description="Form of Curve to create", + items=Types) + + # Line properties + Simple_startlocation = FloatVectorProperty(name="", + description="Start location", + default=(0.0, 0.0, 0.0), + subtype='TRANSLATION') + Simple_endlocation = FloatVectorProperty(name="", + description="End location", + default=(2.0, 2.0, 2.0), + subtype='TRANSLATION') + Simple_rotation_euler = FloatVectorProperty(name="", + description="Rotation", + default=(0.0, 0.0, 0.0), + subtype='EULER') + + # Trapezoid properties + Simple_a = FloatProperty(name="a", + default=2.0, + min=0.0, soft_min=0.0, + unit='LENGTH', + description="a") + Simple_b = FloatProperty(name="b", + default=1.0, + min=0.0, soft_min=0.0, + unit='LENGTH', + description="b") + Simple_h = FloatProperty(name="h", + default=1.0, + unit='LENGTH', + description="h") + + Simple_angle = FloatProperty(name="Angle", + default=45.0, + description="Angle") + Simple_startangle = FloatProperty(name="Start angle", + default=0.0, + min=-360.0, soft_min=-360.0, + max=360.0, soft_max=360.0, + description="Start angle") + Simple_endangle = FloatProperty(name="End angle", + default=45.0, + min=-360.0, soft_min=-360.0, + max=360.0, soft_max=360.0, + description="End angle") + + Simple_sides = IntProperty(name="sides", + default=3, + min=0, soft_min=0, + description="sides") + + Simple_radius = FloatProperty(name="radius", + default=1.0, + min=0.0, soft_min=0.0, + unit='LENGTH', + description="radius") + + Simple_center = BoolProperty(name="Length center", + default=True, + description="Length center") + + Angle_types = [('Degrees', 'Degrees', 'Degrees'), + ('Radians', 'Radians', 'Radians')] + Simple_degrees_or_radians = EnumProperty(name="Degrees or radians", + description="Degrees or radians", + items=Angle_types) + + # Rectangle properties + Simple_width = FloatProperty(name="Width", + default=2.0, + min=0.0, soft_min=0, + unit='LENGTH', + description="Width") + Simple_length = FloatProperty(name="Length", + default=2.0, + min=0.0, soft_min=0.0, + unit='LENGTH', + description="Length") + Simple_rounded = FloatProperty(name="Rounded", + default=0.0, + min=0.0, soft_min=0.0, + unit='LENGTH', + description="Rounded") + + # Curve Options + shapeItems = [ + ('2D', '2D', '2D'), + ('3D', '3D', '3D')] + shape = EnumProperty(name="2D / 3D", + items=shapeItems, + description="2D or 3D Curve") + + ##### DRAW ##### + def draw(self, context): + layout = self.layout + + # general options + col = layout.column() + col.prop(self, 'Simple_Type') + + l = 0 + s = 0 + + if self.Simple_Type == 'Line': + col.label(text=self.Simple_Type + " Options") + box = layout.box() + box.prop(self, 'Simple_endlocation') + v = Vector(self.Simple_endlocation) - Vector(self.Simple_startlocation) + l = v.length + + if self.Simple_Type == 'Distance': + col.label(text=self.Simple_Type + " Options") + box = layout.box() + box.prop(self, 'Simple_length') + box.prop(self, 'Simple_center') + l = self.Simple_length + + if self.Simple_Type == 'Angle': + col.label(text=self.Simple_Type + " Options") + box = layout.box() + box.prop(self, 'Simple_length') + box.prop(self, 'Simple_angle') + row = layout.row() + row.prop(self, 'Simple_degrees_or_radians', expand=True) + + if self.Simple_Type == 'Circle': + col.label(text=self.Simple_Type + " Options") + box = layout.box() + box.prop(self, 'Simple_sides') + box.prop(self, 'Simple_radius') + l = 2 * pi * abs(self.Simple_radius) + s = pi * self.Simple_radius * self.Simple_radius + + if self.Simple_Type == 'Ellipse': + col.label(text=self.Simple_Type + " Options") + box = layout.box() + box.prop(self, 'Simple_a') + box.prop(self, 'Simple_b') + l = pi * (3 * (self.Simple_a + self.Simple_b) - sqrt((3 * self.Simple_a + self.Simple_b) * (self.Simple_a + 3 * self.Simple_b))) + s = pi * abs(self.Simple_b) * abs(self.Simple_a) + + if self.Simple_Type == 'Arc': + col.label(text=self.Simple_Type + " Options") + box = layout.box() + box.prop(self, 'Simple_sides') + box.prop(self, 'Simple_radius') + box.prop(self, 'Simple_startangle') + box.prop(self, 'Simple_endangle') + row = layout.row() + row.prop(self, 'Simple_degrees_or_radians', expand=True) + l = abs(pi * self.Simple_radius * (self.Simple_endangle - self.Simple_startangle) / 180) + + if self.Simple_Type == 'Sector': + col.label(text=self.Simple_Type + " Options") + box = layout.box() + box.prop(self, 'Simple_sides') + box.prop(self, 'Simple_radius') + box.prop(self, 'Simple_startangle') + box.prop(self, 'Simple_endangle') + row = layout.row() + row.prop(self, 'Simple_degrees_or_radians', expand=True) + l = abs(pi * self.Simple_radius * (self.Simple_endangle - self.Simple_startangle) / 180) + self.Simple_radius * 2 + s = pi * self.Simple_radius * self.Simple_radius * abs(self.Simple_endangle - self.Simple_startangle) / 360 + + if self.Simple_Type == 'Segment': + col.label(text=self.Simple_Type + " Options") + box = layout.box() + box.prop(self, 'Simple_sides') + box.prop(self, 'Simple_a') + box.prop(self, 'Simple_b') + box.prop(self, 'Simple_startangle') + box.prop(self, 'Simple_endangle') + row = layout.row() + row.prop(self, 'Simple_degrees_or_radians', expand=True) + la = abs(pi * self.Simple_a * (self.Simple_endangle - self.Simple_startangle) / 180) + lb = abs(pi * self.Simple_b * (self.Simple_endangle - self.Simple_startangle) / 180) + l = abs(self.Simple_a - self.Simple_b) * 2 + la + lb + sa = pi * self.Simple_a * self.Simple_a * abs(self.Simple_endangle - self.Simple_startangle) / 360 + sb = pi * self.Simple_b * self.Simple_b * abs(self.Simple_endangle - self.Simple_startangle) / 360 + s = abs(sa - sb) + + if self.Simple_Type == 'Rectangle': + col.label(text=self.Simple_Type + " Options") + box = layout.box() + box.prop(self, 'Simple_width') + box.prop(self, 'Simple_length') + box.prop(self, 'Simple_rounded') + box.prop(self, 'Simple_center') + l = 2 * abs(self.Simple_width) + 2 * abs(self.Simple_length) + s = abs(self.Simple_width) * abs(self.Simple_length) + + if self.Simple_Type == 'Rhomb': + col.label(text=self.Simple_Type + " Options") + box = layout.box() + box.prop(self, 'Simple_width') + box.prop(self, 'Simple_length') + box.prop(self, 'Simple_center') + g = hypot(self.Simple_width / 2, self.Simple_length / 2) + l = 4 * g + s = self.Simple_width * self.Simple_length / 2 + + if self.Simple_Type == 'Polygon': + col.label(text=self.Simple_Type + " Options") + box = layout.box() + box.prop(self, 'Simple_sides') + box.prop(self, 'Simple_radius') + + if self.Simple_Type == 'Polygon_ab': + col.label(text=self.Simple_Type + " Options") + box = layout.box() + box.prop(self, 'Simple_sides') + box.prop(self, 'Simple_a') + box.prop(self, 'Simple_b') + + if self.Simple_Type == 'Trapezoid': + col.label(text=self.Simple_Type + " Options") + box = layout.box() + box.prop(self, 'Simple_a') + box.prop(self, 'Simple_b') + box.prop(self, 'Simple_h') + box.prop(self, 'Simple_center') + g = hypot(self.Simple_h, (self.Simple_a - self.Simple_b) / 2) + l = self.Simple_a + self.Simple_b + g * 2 + s = (abs(self.Simple_a) + abs(self.Simple_b)) / 2 * self.Simple_h + + row = layout.row() + row.prop(self, 'shape', expand=True) + box = layout.box() + box.label("Location:") + box.prop(self, 'Simple_startlocation') + box = layout.box() + box.label("Rotation:") + box.prop(self, 'Simple_rotation_euler') + if l != 0: + l_str = str(round(l, 4)) + row = layout.row() + row.label("Length: " + l_str) + if s != 0: + s_str = str(round(s, 4)) + row = layout.row() + row.label("Area: " + s_str) + + ##### POLL ##### + @classmethod + def poll(cls, context): + return context.scene != None + + ##### EXECUTE ##### + def execute(self, context): + if self.Simple_Change: + SimpleDelete(self.Simple_Delete) + + # go to object mode + if bpy.ops.object.mode_set.poll(): + bpy.ops.object.mode_set(mode='OBJECT') + + # turn off undo + undo = bpy.context.user_preferences.edit.use_global_undo + bpy.context.user_preferences.edit.use_global_undo = False + + # main function + self.align_matrix = align_matrix(context, self.Simple_startlocation) + main(context, self, self.align_matrix) + + # restore pre operator undo state + bpy.context.user_preferences.edit.use_global_undo = undo + + return {'FINISHED'} + + ##### INVOKE ##### + def invoke(self, context, event): + # store creation_matrix + if self.Simple_Change: + bpy.context.scene.cursor_location = self.Simple_startlocation + else: + self.Simple_startlocation = bpy.context.scene.cursor_location + + self.align_matrix = align_matrix(context, self.Simple_startlocation) + self.execute(context) + + return {'FINISHED'} + +# ------------------------------------------------------------ +# Fillet + + +class BezierPointsFillet(bpy.types.Operator): + '''''' + bl_idname = "curve.bezier_points_fillet" + bl_label = "Bezier points fillet" + bl_options = {'REGISTER', 'UNDO'} + bl_description = "bezier points fillet" + + Fillet_radius = FloatProperty(name="Radius", + default=0.25, + unit='LENGTH', + description="radius") + + Types = [('Round', 'Round', 'Round'), + ('Chamfer', 'Chamfer', 'Chamfer')] + Fillet_Type = EnumProperty(name="Type", + description="Fillet type", + items=Types) + + ##### DRAW ##### + def draw(self, context): + layout = self.layout + + # general options + col = layout.column() + col.prop(self, 'Fillet_radius') + col.prop(self, 'Fillet_Type', expand=True) + + ##### POLL ##### + @classmethod + def poll(cls, context): + return context.scene != None + + ##### EXECUTE ##### + def execute(self, context): + # go to object mode + if bpy.ops.object.mode_set.poll(): + bpy.ops.object.mode_set(mode='OBJECT') + bpy.ops.object.mode_set(mode='EDIT') + + # turn off undo + undo = bpy.context.user_preferences.edit.use_global_undo + bpy.context.user_preferences.edit.use_global_undo = False + + # main function + spline = bpy.context.object.data.splines.active + selected = [p for p in spline.bezier_points if p.select_control_point] + + bpy.ops.curve.handle_type_set(type='VECTOR') + n = 0 + ii = [] + for p in spline.bezier_points: + if p.select_control_point: + ii.append(n) + n += 1 + else: + n += 1 + + if n > 2: + + jn = 0 + + for j in ii: + + j += jn + + selected_all = [p for p in spline.bezier_points] + + bpy.ops.curve.select_all(action='DESELECT') + + if j != 0 and j != n - 1: + selected_all[j].select_control_point = True + selected_all[j + 1].select_control_point = True + bpy.ops.curve.subdivide() + selected_all = [p for p in spline.bezier_points] + selected4 = [selected_all[j - 1], selected_all[j], selected_all[j + 1], selected_all[j + 2]] + jn += 1 + n += 1 + + elif j == 0: + selected_all[j].select_control_point = True + selected_all[j + 1].select_control_point = True + bpy.ops.curve.subdivide() + selected_all = [p for p in spline.bezier_points] + selected4 = [selected_all[n], selected_all[0], selected_all[1], selected_all[2]] + jn += 1 + n += 1 + + elif j == n - 1: + selected_all[j].select_control_point = True + selected_all[j - 1].select_control_point = True + bpy.ops.curve.subdivide() + selected_all = [p for p in spline.bezier_points] + selected4 = [selected_all[0], selected_all[n], selected_all[n - 1], selected_all[n - 2]] + + selected4[2].co = selected4[1].co + s1 = Vector(selected4[0].co) - Vector(selected4[1].co) + s2 = Vector(selected4[3].co) - Vector(selected4[2].co) + s1.normalize() + s11 = Vector(selected4[1].co) + s1 * self.Fillet_radius + selected4[1].co = s11 + s2.normalize() + s22 = Vector(selected4[2].co) + s2 * self.Fillet_radius + selected4[2].co = s22 + + if self.Fillet_Type == 'Round': + if j != n - 1: + selected4[2].handle_right_type = 'VECTOR' + selected4[1].handle_left_type = 'VECTOR' + selected4[1].handle_right_type = 'ALIGNED' + selected4[2].handle_left_type = 'ALIGNED' + else: + selected4[1].handle_right_type = 'VECTOR' + selected4[2].handle_left_type = 'VECTOR' + selected4[2].handle_right_type = 'ALIGNED' + selected4[1].handle_left_type = 'ALIGNED' + if self.Fillet_Type == 'Chamfer': + selected4[2].handle_right_type = 'VECTOR' + selected4[1].handle_left_type = 'VECTOR' + selected4[1].handle_right_type = 'VECTOR' + selected4[2].handle_left_type = 'VECTOR' + + bpy.ops.curve.select_all(action='SELECT') + bpy.ops.curve.spline_type_set(type='BEZIER') + + # restore pre operator undo state + bpy.context.user_preferences.edit.use_global_undo = undo + + return {'FINISHED'} + + ##### INVOKE ##### + def invoke(self, context, event): + self.execute(context) + + return {'FINISHED'} + + +def subdivide_cubic_bezier(p1, p2, p3, p4, t): + p12 = (p2 - p1) * t + p1 + p23 = (p3 - p2) * t + p2 + p34 = (p4 - p3) * t + p3 + p123 = (p23 - p12) * t + p12 + p234 = (p34 - p23) * t + p23 + p1234 = (p234 - p123) * t + p123 + return [p12, p123, p1234, p234, p34] + +# ------------------------------------------------------------ +# BezierDivide Operator + + +class BezierDivide(bpy.types.Operator): + '''''' + bl_idname = "curve.bezier_spline_divide" + bl_label = "Bezier Divide (enters edit mode) for Fillet Curves" + bl_options = {'REGISTER', 'UNDO'} + bl_description = "bezier spline divide" + + # align_matrix for the invoke + align_matrix = Matrix() + + Bezier_t = FloatProperty(name="t (0% - 100%)", + default=50.0, + min=0.0, soft_min=0.0, + max=100.0, soft_max=100.0, + description="t (0% - 100%)") + + ##### POLL ##### + @classmethod + def poll(cls, context): + return context.scene != None + + ##### EXECUTE ##### + def execute(self, context): + # go to object mode + if bpy.ops.object.mode_set.poll(): + bpy.ops.object.mode_set(mode='OBJECT') + bpy.ops.object.mode_set(mode='EDIT') + + # turn off undo + undo = bpy.context.user_preferences.edit.use_global_undo + bpy.context.user_preferences.edit.use_global_undo = False + + # main function + spline = bpy.context.object.data.splines.active + vertex = [] + selected_all = [p for p in spline.bezier_points if p.select_control_point] + h = subdivide_cubic_bezier(selected_all[0].co, selected_all[0].handle_right, selected_all[1].handle_left, selected_all[1].co, self.Bezier_t / 100) + + selected_all[0].handle_right_type = 'FREE' + selected_all[0].handle_left_type = 'FREE' + selected_all[1].handle_right_type = 'FREE' + selected_all[1].handle_left_type = 'FREE' + bpy.ops.curve.subdivide(1) + selected_all = [p for p in spline.bezier_points if p.select_control_point] + + selected_all[0].handle_right = h[0] + selected_all[1].co = h[2] + selected_all[1].handle_left = h[1] + selected_all[1].handle_right = h[3] + selected_all[2].handle_left = h[4] + + # restore pre operator undo state + bpy.context.user_preferences.edit.use_global_undo = undo + + return {'FINISHED'} + + ##### INVOKE ##### + def invoke(self, context, event): + self.execute(context) + + return {'FINISHED'} + +# ------------------------------------------------------------ +# Simple change panel + + +class SimplePanel(bpy.types.Panel): + + bl_label = "Simple change" + bl_space_type = "VIEW_3D" + bl_region_type = "TOOLS" + bl_options = {'DEFAULT_CLOSED'} + bl_category = "Tools" + + ##### POLL ##### + @classmethod + def poll(cls, context): + if not context.active_object: + pass + elif context.object.Simple == True: + return (context.object) + + ##### DRAW ##### + def draw(self, context): + if context.object.Simple == True: + + layout = self.layout + + obj = context.object + row = layout.row() + simple_change = row.operator("curve.simple", text='Change') + simple_change.Simple_Change = True + simple_change.Simple_Delete = obj.name + simple_change.Simple_Type = obj.Simple_Type + simple_change.Simple_startlocation = obj.location + simple_change.Simple_endlocation = obj.Simple_endlocation + simple_change.Simple_a = obj.Simple_a + simple_change.Simple_b = obj.Simple_b + simple_change.Simple_h = obj.Simple_h + simple_change.Simple_angle = obj.Simple_angle + simple_change.Simple_startangle = obj.Simple_startangle + simple_change.Simple_endangle = obj.Simple_endangle + simple_change.Simple_rotation_euler = obj.rotation_euler + simple_change.Simple_sides = obj.Simple_sides + simple_change.Simple_radius = obj.Simple_radius + simple_change.Simple_center = obj.Simple_center + simple_change.Simple_width = obj.Simple_width + simple_change.Simple_length = obj.Simple_length + simple_change.Simple_rounded = obj.Simple_rounded + +# ------------------------------------------------------------ +# Fillet tools panel + + +class SimpleEdit(bpy.types.Operator): + + """Curve Simple""" + bl_idname = "object._simple_edit" + bl_label = "Create Curves" + bl_options = {'REGISTER', 'UNDO'} + bl_description = "Subdivide & Fillet Curves" + + ##### POLL ##### + @classmethod + def poll(cls, context): + vertex = [] + nselected = [] + n = 0 + obj = context.active_object + if obj != None: + if obj.type == 'CURVE': + for i in obj.data.splines: + for j in i.bezier_points: + n += 1 + if j.select_control_point: + nselected.append(n) + vertex.append(obj.matrix_world * j.co) + + if len(vertex) > 0 and n > 2: + return (context.active_object) + if len(vertex) == 2 and abs(nselected[0] - nselected[1]) == 1: + return (context.active_object) + + selected = 0 + for obj in context.selected_objects: + if obj.type == 'CURVE': + selected += 1 + + if selected >= 2: + return (context.selected_objects) + + ##### DRAW ##### + def draw(self, context): + vertex = [] + selected = [] + n = 0 + obj = context.active_object + if obj != None: + if obj.type == 'CURVE': + for i in obj.data.splines: + for j in i.bezier_points: + n += 1 + if j.select_control_point: + selected.append(n) + vertex.append(obj.matrix_world * j.co) + + if len(vertex) > 0 and n > 2: + layout = self.layout + row = layout.row() + simple_edit = row.operator("curve.bezier_points_fillet", text='Fillet') + if len(vertex) == 2 and abs(selected[0] - selected[1]) == 1: + layout = self.layout + row = layout.row() + simple_divide = row.operator("curve.bezier_spline_divide", text='Divide') + +# ------------------------------------------------------------ +# location update + + +def StartLocationUpdate(self, context): + + bpy.context.scene.cursor_location = self.Simple_startlocation + + return + +# ------------------------------------------------------------ +# Add properties to objects + + +def SimpleVariables(): + + bpy.types.Object.Simple = bpy.props.BoolProperty() + bpy.types.Object.Simple_Change = bpy.props.BoolProperty() + # general properties + Types = [('Point', 'Point', 'Point'), + ('Line', 'Line', 'Line'), + ('Distance', 'Distance', 'Distance'), + ('Angle', 'Angle', 'Angle'), + ('Circle', 'Circle', 'Circle'), + ('Ellipse', 'Ellipse', 'Ellipse'), + ('Arc', 'Arc', 'Arc'), + ('Sector', 'Sector', 'Sector'), + ('Segment', 'Segment', 'Segment'), + ('Rectangle', 'Rectangle', 'Rectangle'), + ('Rhomb', 'Rhomb', 'Rhomb'), + ('Polygon', 'Polygon', 'Polygon'), + ('Polygon_ab', 'Polygon_ab', 'Polygon_ab'), + ('Trapezoid', 'Trapezoid', 'Trapezoid')] + bpy.types.Object.Simple_Type = bpy.props.EnumProperty(name="Type", + description="Form of Curve to create", + items=Types) + + # Line properties + bpy.types.Object.Simple_startlocation = bpy.props.FloatVectorProperty(name="Start location", + description="Start location", + default=(0.0, 0.0, 0.0), + subtype='TRANSLATION', + update=StartLocationUpdate) + bpy.types.Object.Simple_endlocation = bpy.props.FloatVectorProperty(name="End location", + description="End location", + default=(2.0, 2.0, 2.0), + subtype='TRANSLATION') + bpy.types.Object.Simple_rotation_euler = bpy.props.FloatVectorProperty(name="Rotation", + description="Rotation", + default=(0.0, 0.0, 0.0), + subtype='EULER') + + # Trapezoid properties + bpy.types.Object.Simple_a = bpy.props.FloatProperty(name="a", + default=2.0, + min=0.0, soft_min=0.0, + unit='LENGTH', + description="a") + bpy.types.Object.Simple_b = bpy.props.FloatProperty(name="b", + default=1.0, + min=0.0, soft_min=0.0, + unit='LENGTH', + description="b") + bpy.types.Object.Simple_h = bpy.props.FloatProperty(name="h", + default=1.0, + unit='LENGTH', + description="h") + + bpy.types.Object.Simple_angle = bpy.props.FloatProperty(name="Angle", + default=45.0, + description="Angle") + bpy.types.Object.Simple_startangle = bpy.props.FloatProperty(name="Start angle", + default=0.0, + min=-360.0, soft_min=-360.0, + max=360.0, soft_max=360.0, + description="Start angle") + bpy.types.Object.Simple_endangle = bpy.props.FloatProperty(name="End angle", + default=45.0, + min=-360.0, soft_min=-360.0, + max=360.0, soft_max=360.0, + description="End angle") + + bpy.types.Object.Simple_sides = bpy.props.IntProperty(name="sides", + default=3, + min=3, soft_min=3, + description="sides") + + bpy.types.Object.Simple_radius = bpy.props.FloatProperty(name="radius", + default=1.0, + min=0.0, soft_min=0.0, + unit='LENGTH', + description="radius") + + bpy.types.Object.Simple_center = bpy.props.BoolProperty(name="Length center", + default=True, + description="Length center") + + # Rectangle properties + bpy.types.Object.Simple_width = bpy.props.FloatProperty(name="Width", + default=2.0, + min=0.0, soft_min=0.0, + unit='LENGTH', + description="Width") + bpy.types.Object.Simple_length = bpy.props.FloatProperty(name="Length", + default=2.0, + min=0.0, soft_min=0.0, + unit='LENGTH', + description="Length") + bpy.types.Object.Simple_rounded = bpy.props.FloatProperty(name="Rounded", + default=0.0, + unit='LENGTH', + description="Rounded") + +################################################################################ +##### REGISTER ##### + + +class INFO_MT_simple_menu(bpy.types.Menu): + # Define the "Extras" menu + bl_idname = "INFO_MT_simple_menu" + bl_label = "2D Objects" + + def draw(self, context): + self.layout.operator_context = 'INVOKE_REGION_WIN' + + oper2 = self.layout.operator(Simple.bl_idname, text="Point", icon="MOD_CURVE") + oper2.Simple_Change = False + oper2.Simple_Type = "Point" + + oper3 = self.layout.operator(Simple.bl_idname, text="Line", icon="MOD_CURVE") + oper3.Simple_Change = False + oper3.Simple_Type = "Line" + + oper4 = self.layout.operator(Simple.bl_idname, text="Distance", icon="MOD_CURVE") + oper4.Simple_Change = False + oper4.Simple_Type = "Distance" + + oper5 = self.layout.operator(Simple.bl_idname, text="Angle", icon="MOD_CURVE") + oper5.Simple_Change = False + oper5.Simple_Type = "Angle" + + oper6 = self.layout.operator(Simple.bl_idname, text="Circle", icon="MOD_CURVE") + oper6.Simple_Change = False + oper6.Simple_Type = "Circle" + + oper7 = self.layout.operator(Simple.bl_idname, text="Ellipse", icon="MOD_CURVE") + oper7.Simple_Change = False + oper7.Simple_Type = "Ellipse" + + oper8 = self.layout.operator(Simple.bl_idname, text="Arc", icon="MOD_CURVE") + oper8.Simple_Change = False + oper8.Simple_Type = "Arc" + + oper9 = self.layout.operator(Simple.bl_idname, text="Sector", icon="MOD_CURVE") + oper9.Simple_Change = False + oper9.Simple_Type = "Sector" + + oper10 = self.layout.operator(Simple.bl_idname, text="Segment", icon="MOD_CURVE") + oper10.Simple_Change = False + oper10.Simple_Type = "Segment" + + oper11 = self.layout.operator(Simple.bl_idname, text="Rectangle", icon="MOD_CURVE") + oper11.Simple_Change = False + oper11.Simple_Type = "Rectangle" + + oper12 = self.layout.operator(Simple.bl_idname, text="Rhomb", icon="MOD_CURVE") + oper12.Simple_Change = False + oper12.Simple_Type = "Rhomb" + + oper13 = self.layout.operator(Simple.bl_idname, text="Polygon", icon="MOD_CURVE") + oper13.Simple_Change = False + oper13.Simple_Type = "Polygon" + + oper14 = self.layout.operator(Simple.bl_idname, text="Polygon_ab", icon="MOD_CURVE") + oper14.Simple_Change = False + oper14.Simple_Type = "Polygon_ab" + + oper15 = self.layout.operator(Simple.bl_idname, text="Trapezoid", icon="MOD_CURVE") + oper15.Simple_Change = False + oper15.Simple_Type = "Trapezoid" + + +def Simple_button(self, context): + layout = self.layout + layout.separator() + oper11 = self.layout.operator(Simple.bl_idname, text="Rectangle", icon="MOD_CURVE") + oper11.Simple_Change = False + oper11.Simple_Type = "Rectangle" + + self.layout.menu("INFO_MT_simple_menu", icon="MOD_CURVE") + + +def register(): + bpy.utils.register_class(Simple) + bpy.utils.register_class(BezierPointsFillet) + bpy.utils.register_class(BezierDivide) + bpy.utils.register_class(SimplePanel) + bpy.utils.register_class(SimpleEdit) + bpy.utils.register_class(INFO_MT_simple_menu) + + bpy.types.INFO_MT_curve_add.append(Simple_button) + + SimpleVariables() + + +def unregister(): + bpy.utils.unregister_class(Simple) + bpy.utils.unregister_class(BezierPointsFillet) + bpy.utils.unregister_class(BezierDivide) + bpy.utils.unregister_class(SimplePanel) + bpy.utils.unregister_class(SimpleEdit) + bpy.utils.unregister_class(INFO_MT_simple_menu) + + bpy.types.INFO_MT_curve_add.remove(Simple_button) + +if __name__ == "__main__": + register() diff --git a/add_curve_extra_objects/beveltaper_curve.py b/add_curve_extra_objects/beveltaper_curve.py new file mode 100644 index 0000000000000000000000000000000000000000..70e910fb8a346182b8648b9e7f34550fa791659b --- /dev/null +++ b/add_curve_extra_objects/beveltaper_curve.py @@ -0,0 +1,259 @@ +bl_info = { + "name": "Bevel/Taper Curve", + "author": "Cmomoney", + "version": (1, 1), + "blender": (2, 69, 0), + "location": "View3D > Object > Bevel/Taper", + "description": "Adds bevel and/or taper curve to active curve", + "warning": "", + "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Curve/Bevel_-Taper_Curve", + "tracker_url": "https://projects.blender.org/tracker/index.php?func=detail&aid=37377&group_id=153&atid=467", + "category": "Curve"} + + # ***** 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 3 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, see <http://www.gnu.org/licenses/>. + # + # ***** END GPL LICENSE BLOCK ***** + +import bpy +from bpy.types import Operator +from bpy.props import * +from bpy_extras.object_utils import AddObjectHelper, object_data_add +from mathutils import Vector + +def add_taper(self, context): + + scale_ends1 = self.scale_ends1 + scale_ends2 = self.scale_ends2 + scale_mid = self.scale_mid + verts = [(-2.0, 1.0 * scale_ends1, 0.0, 1.0), + (-1.0, 0.75 * scale_mid, 0.0, 1.0), + (0.0, 1.5 * scale_mid, 0.0, 1.0), + (1.0, 0.75 * scale_mid, 0.0, 1.0), + (2.0, 1.0 * scale_ends2, 0.0, 1.0)] + make_path(self, context, verts) + +def add_type5(self, context): + + scale_x = self.scale_x + scale_y = self.scale_y + verts = [[0.0 * scale_x, 0.049549 * scale_y, + 0.0, 0.031603 * scale_x, 0.047013 * scale_y, + 0.0, 0.05 * scale_x, 0.0 * scale_y, 0.0, + 0.031603 * scale_x, -0.047013 * scale_y, + 0.0, 0.0 * scale_x, -0.049549 * scale_y, + 0.0, -0.031603 * scale_x, -0.047013 * scale_y, + 0.0, -0.05 * scale_x, -0.0 * scale_y, 0.0, + -0.031603 * scale_x, 0.047013 * scale_y, 0.0]] + lhandles = [[(-0.008804 * scale_x, 0.049549 * scale_y, 0.0), + (0.021304 * scale_x, 0.02119 * scale_y, 0.0), + (0.05 * scale_x, 0.051228 * scale_y, 0.0), + (0.036552 * scale_x, -0.059423 * scale_y, 0.0), + (0.008804 * scale_x, -0.049549 * scale_y, 0.0), + (-0.021304 * scale_x, -0.02119 * scale_y, 0.0), + (-0.05 * scale_x, -0.051228 * scale_y, 0.0), + (-0.036552 * scale_x, 0.059423 * scale_y, 0.0)]] + rhandles = [[(0.008803 * scale_x, 0.049549 * scale_y, 0.0), + (0.036552 * scale_x, 0.059423 * scale_y, 0.0), + (0.05 * scale_x, -0.051228 * scale_y, 0.0), + (0.021304 * scale_x, -0.02119 * scale_y, 0.0), + (-0.008803 * scale_x, -0.049549 * scale_y, 0.0), + (-0.036552 * scale_x, -0.059423 * scale_y, 0.0), + (-0.05 * scale_x, 0.051228 * scale_y, 0.0), + (-0.021304 * scale_x, 0.02119 * scale_y, 0.0)]] + make_curve(self, context, verts, lhandles, rhandles) + +def add_type4(self, context): + + scale_x = self.scale_x + scale_y = self.scale_y + verts = [[-0.0 * scale_x, 0.017183 * scale_y, + 0.0, 0.05 * scale_x, 0.0 * scale_y, + 0.0, 0.0 * scale_x, -0.017183 * scale_y, + 0.0, -0.05 * scale_x, -0.0 * scale_y, 0.0]] + lhandles = [[(-0.017607 * scale_x, 0.017183 * scale_y, 0.0), + (0.05 * scale_x, 0.102456 * scale_y, 0.0), + (0.017607 * scale_x, -0.017183 * scale_y, 0.0), + (-0.05 * scale_x, -0.102456 * scale_y, 0.0)]] + rhandles = [[(0.017607 * scale_x, 0.017183 * scale_y, 0.0), + (0.05 * scale_x, -0.102456 * scale_y, 0.0), + (-0.017607 * scale_x, -0.017183 * scale_y, 0.0), + (-0.05 * scale_x, 0.102456 * scale_y, 0.0)]] + make_curve(self, context, verts, lhandles, rhandles) + +def add_type3(self, context): + + scale_x = self.scale_x + scale_y = self.scale_y + verts = [[-0.017183 * scale_x, 0.0 * scale_y, + 0.0, 0.0 * scale_x, 0.05 * scale_y, + 0.0, 0.017183 * scale_x, 0.0 * scale_y, + 0.0, 0.0 * scale_x, -0.05 * scale_y, 0.0]] + lhandles = [[(-0.017183 * scale_x, -0.017607 * scale_y, 0.0), + (-0.102456 * scale_x, 0.05 * scale_y, 0.0), + (0.017183 * scale_x, 0.017607 * scale_y, 0.0), + (0.102456 * scale_x, -0.05 * scale_y, 0.0)]] + rhandles = [[(-0.017183 * scale_x, 0.017607 * scale_y, 0.0), + (0.102456 * scale_x, 0.05 * scale_y, 0.0), + (0.017183 * scale_x, -0.017607 * scale_y, 0.0), + (-0.102456 * scale_x, -0.05 * scale_y, 0.0)]] + make_curve(self, context, verts, lhandles, rhandles) + +def add_type2(self, context): + + scale_x = self.scale_x + scale_y = self.scale_y + verts = [[-0.05 * scale_x, 0.0 * scale_y, + 0.0, 0.0 * scale_x, 0.05 * scale_y, + 0.0, 0.05 * scale_x, 0.0 * scale_y, + 0.0, 0.0 * scale_x, -0.05 * scale_y, 0.0]] + lhandles = [[(-0.05 * scale_x, -0.047606 * scale_y, 0.0), + (-0.047606 * scale_x, 0.05 * scale_y, 0.0), + (0.05 * scale_x, 0.047607 * scale_y, 0.0), + (0.047606 * scale_x, -0.05 * scale_y, 0.0)]] + rhandles = [[(-0.05 * scale_x, 0.047607 * scale_y, 0.0), + (0.047607 * scale_x, 0.05 * scale_y, 0.0), + (0.05 * scale_x, -0.047607 * scale_y, 0.0), + (-0.047607 * scale_x, -0.05 * scale_y, 0.0)]] + make_curve(self, context, verts, lhandles, rhandles) + +def add_type1(self, context): + + scale_x = self.scale_x + scale_y = self.scale_y + verts = [[-0.05 * scale_x, 0.0 * scale_y, + 0.0, 0.0 * scale_x, 0.05 * scale_y, + 0.0, 0.05 * scale_x, 0.0 * scale_y, + 0.0, 0.0 * scale_x, -0.05 * scale_y, 0.0]] + lhandles = [[(-0.05 * scale_x, -0.027606 * scale_y, 0.0), + (-0.027606 * scale_x, 0.05 * scale_y, 0.0), + (0.05 * scale_x, 0.027606 * scale_y, 0.0), + (0.027606 * scale_x, -0.05 * scale_y, 0.0)]] + rhandles = [[(-0.05 * scale_x, 0.027607 * scale_y, 0.0), + (0.027607 * scale_x, 0.05 * scale_y, 0.0), + (0.05 * scale_x, -0.027607 * scale_y, 0.0), + (-0.027607 * scale_x, -0.05 * scale_y, 0.0)]] + make_curve(self, context, verts, lhandles, rhandles) + +def make_path(self, context, verts): + + target = bpy.context.scene.objects.active + bpy.ops.curve.primitive_nurbs_path_add(view_align=False, enter_editmode=False, location=(0, 0, 0)) + target.data.taper_object = bpy.context.scene.objects.active + taper = bpy.context.scene.objects.active + taper.name = target.name + '_Taper' + bpy.context.scene.objects.active = target + points = taper.data.splines[0].points + for i in range(len(verts)): + points[i].co = verts[i] + +def make_curve(self, context, verts, lh, rh): + + scale_x = self.scale_x + scale_y = self.scale_y + type = self.type + target = bpy.context.scene.objects.active + curve_data = bpy.data.curves.new(name=target.name +'_Bevel', type='CURVE') + curve_data.dimensions = '3D' + for p in range(len(verts)): + c = 0 + spline = curve_data.splines.new(type='BEZIER') + spline.use_cyclic_u = True + spline.bezier_points.add( len(verts[p])/3-1 ) + spline.bezier_points.foreach_set('co', verts[p]) + for bp in spline.bezier_points: + bp.handle_left_type = 'ALIGNED' + bp.handle_right_type = 'ALIGNED' + bp.handle_left.xyz = lh[p][c] + bp.handle_right.xyz = rh[p][c] + c += 1 + object_data_add(context, curve_data, operator=self) + target.data.bevel_object = bpy.context.scene.objects.active + bpy.context.scene.objects.active = target + +class add_tapercurve(Operator, AddObjectHelper): + """Add taper curve to active curve""" + bl_idname = "curve.tapercurve" + bl_label = "Add Curve as Taper" + bl_options = {'REGISTER', 'UNDO'} + + + scale_ends1 = FloatProperty(name="End Width Left", description="Adjust left end taper", default=0.0, min=0.0) + scale_ends2 = FloatProperty(name="End Width Right", description="Adjust right end taper", default=0.0, min=0.0) + scale_mid = FloatProperty(name="Center Width", description="Adjust taper at center", default=1.0, min=0.0) + link1 = BoolProperty(name='link ends', default=True) + link2 = BoolProperty(name='link ends/center', default=False) + if link2: + diff = FloatProperty(name='Difference', default=1, description='Difference between ends and center while linked') + + def execute(self, context): + if self.link1: + self.scale_ends2 = self.scale_ends1 + if self.link2: + self.scale_ends2 = self.scale_ends1 = self.scale_mid-self.diff + add_taper(self, context) + return {'FINISHED'} + +class add_bevelcurve(Operator, AddObjectHelper): + """Add bevel curve to active curve""" + bl_idname = "curve.bevelcurve" + bl_label = "Add Curve as Bevel" + bl_options = {'REGISTER', 'UNDO'} + + type = IntProperty(name='Type', description='Type of bevel curve', default=1, min=1, max=5) + scale_x = FloatProperty(name="scale x", description="scale on x axis", default=1.0) + scale_y = FloatProperty(name="scale y", description="scale on y axis", default=1.0) + link = BoolProperty(name='link xy', default=True) + + def execute(self, context): + if self.link: + self.scale_y = self.scale_x + if self.type == 1: + add_type1(self, context) + if self.type == 2: + add_type2(self, context) + if self.type == 3: + add_type3(self, context) + if self.type == 4: + add_type4(self, context) + if self.type == 5: + add_type5(self, context) + + return {'FINISHED'} + +class Bevel_Taper_Curve_Menu(bpy.types.Menu): + bl_label = "Bevel/Taper" + bl_idname = "OBJECT_MT_bevel_taper_curve_menu" + + def draw(self, context): + layout = self.layout + + layout.operator("curve.bevelcurve") + layout.operator("curve.tapercurve") + +def menu_funcs(self, context): + if bpy.context.scene.objects.active.type == "CURVE": + self.layout.menu("OBJECT_MT_bevel_taper_curve_menu") + +def register(): + bpy.utils.register_module(__name__) + bpy.types.VIEW3D_MT_object.append(menu_funcs) + +def unregister(): + bpy.utils.unregister_module(__name__) + bpy.types.VIEW3D_MT_object.remove(menu_funcs) + +if __name__ == "__main__": + register() diff --git a/add_curve_extra_objects/bpybraid.py b/add_curve_extra_objects/bpybraid.py new file mode 100644 index 0000000000000000000000000000000000000000..1046843ccf354664f81fe2c39221f1af9153c29f --- /dev/null +++ b/add_curve_extra_objects/bpybraid.py @@ -0,0 +1,84 @@ + +from math import sin, cos, pi + +from . import braid +from .braid import angle_point + +import bpy + + +def poly_line(curve, points, join=True, type='NURBS'): + polyline = curve.splines.new(type) + polyline.points.add(len(points) - 1) + for num in range(len(points)): + polyline.points[num].co = (points[num]) + (1,) + + polyline.order_u = len(polyline.points) - 1 + if join: + polyline.use_cyclic_u = True + + +def poly_lines(objname, curvename, lines, bevel=None, joins=False, ctype='NURBS'): + curve = bpy.data.curves.new(name=curvename, type='CURVE') + curve.dimensions = '3D' + + obj = bpy.data.objects.new(objname, curve) + obj.location = (0, 0, 0) # object origin + # ctx.scene.objects.link(obj) + + for i, line in enumerate(lines): + poly_line(curve, line, joins if type(joins) == bool else joins[i], type=ctype) + + if bevel: + curve.bevel_object = bpy.data.objects[bevel] + return obj + + +def nurbs_circle(name, w, h): + pts = [(-w / 2, 0, 0), (0, -h / 2, 0), (w / 2, 0, 0), (0, h / 2, 0)] + return poly_lines(name, name + '_curve', [pts], joins=True) + + +def star_pts(r=1, ir=None, points=5, center=(0, 0)): + '''Create points for a star. They are 2d - z is always zero + + r: the outer radius + ir: the inner radius + ''' + if not ir: + ir = r / 5 + pts = [] + dt = pi * 2 / points + for i in range(points): + t = i * dt + ti = (i + .5) * dt + pts.append(angle_point(center, t, r) + (0,)) + pts.append(angle_point(center, ti, ir) + (0,)) + return pts + + +def clear(): + for obj in bpy.data.objects: + if obj.type not in ('CAMERA', 'LAMP'): + obj.select = True + else: + obj.select = False + bpy.ops.object.delete() + + +def defaultCircle(w=.6): + circle = nurbs_circle('braid_circle', w, w) + circle.hide = True + return circle + + +def defaultStar(): + star = poly_lines('star', 'staz', [tuple(star_pts(points=5, r=.5, ir=.05))], type='NURBS') + star.hide = True + return star + + +def awesome_braid(strands=3, sides=5, bevel='braid_circle', pointy=False, **kwds): + lines = braid.strands(strands, sides, **kwds) + type = {True: 'POLY', False: 'NURBS'}[pointy] + return poly_lines('Braid', 'Braid_c', lines, bevel=bevel, joins=True, ctype=type) diff --git a/add_curve_extra_objects/braid.py b/add_curve_extra_objects/braid.py new file mode 100644 index 0000000000000000000000000000000000000000..cebfee179c6cb8b23821e230f93ce63c89722605 --- /dev/null +++ b/add_curve_extra_objects/braid.py @@ -0,0 +1,60 @@ + +import math +from math import sin, cos, pi + + +def angle_point(center, angle, distance): + cx, cy = center + x = cos(angle) * distance + y = sin(angle) * distance + return x + cx, y + cy + + +def flat_hump(strands, mx=1, my=1, mz=1, resolution=2): + num = 4 * resolution + dy = 2 * pi / num + dz = 2 * pi * (strands - 1) / num + for i in range(num): + x = i * mx + y = cos(i * dy) * my + z = sin(i * dz) * mz + # print(i, x, y, z) + yield x, y, z + + +def circle_hump(pos, strands, humps, radius=1, mr=1, mz=.2, resolution=2): + num = 5 * resolution + dt = 2 * pi / humps * strands / num + dr = 2 * pi * (strands - 1) / num + dz = 2 * pi / num + t0 = 2 * pi / humps * pos + # print('ds', dt, dr, dz) + for i in range(num): + # i += pos + rdi = sin(i * dr) * mr + # print('rdi', rdi, radius, i*dt, i*dz, cos(i*dz) * mz) + x, y = angle_point((0, 0), i * dt + t0, radius + sin(i * dr) * mr) + z = cos(i * dz) * mz + yield x, y, z + + +def strands(strands, humps, radius=1, mr=1, mz=.2, resolution=2): + positions = [0 for x in range(humps)] + made = 0 + last = None + lines = [] + at = 0 + while 0 in positions: + if positions[at]: + at = positions.index(0) + last = None + hump = list(circle_hump(at, strands, humps, radius, mr, mz, resolution)) + if last is None: + last = hump + lines.append(last) + else: + last.extend(hump) + positions[at] = 1 + at += strands + at %= humps + return lines