From b534a0dc37c69c85f1a06f2a8663327ea603e945 Mon Sep 17 00:00:00 2001 From: meta-androcto Date: Sun, 19 Mar 2017 14:19:53 +1100 Subject: Update add_curve_extra_objects add bounce spline/sporo fit, update curves galore T50943 --- add_curve_extra_objects/__init__.py | 2 + add_curve_extra_objects/add_curve_aceous_galore.py | 256 +++++---- .../add_curve_spirofit_bouncespline.py | 630 +++++++++++++++++++++ 3 files changed, 773 insertions(+), 115 deletions(-) create mode 100644 add_curve_extra_objects/add_curve_spirofit_bouncespline.py (limited to 'add_curve_extra_objects') diff --git a/add_curve_extra_objects/__init__.py b/add_curve_extra_objects/__init__.py index 05a8e873..f328ed9d 100644 --- a/add_curve_extra_objects/__init__.py +++ b/add_curve_extra_objects/__init__.py @@ -43,6 +43,7 @@ if "bpy" in locals(): importlib.reload(add_curve_celtic_links) importlib.reload(add_curve_braid) importlib.reload(add_curve_simple) + importlib.reload(add_curve_spirofit_bouncespline) else: from . import add_curve_aceous_galore @@ -54,6 +55,7 @@ else: from . import add_curve_celtic_links from . import add_curve_braid from . import add_curve_simple + from . import add_curve_spirofit_bouncespline import bpy from bpy.types import Menu, AddonPreferences diff --git a/add_curve_extra_objects/add_curve_aceous_galore.py b/add_curve_extra_objects/add_curve_aceous_galore.py index 6a7ac654..649bf559 100644 --- a/add_curve_extra_objects/add_curve_aceous_galore.py +++ b/add_curve_extra_objects/add_curve_aceous_galore.py @@ -51,6 +51,7 @@ from math import ( ) import mathutils.noise as Noise from bpy.types import Operator + # ------------------------------------------------------------ # Some functions to use with others: # ------------------------------------------------------------ @@ -118,20 +119,6 @@ def vTurbNoise(x, y, z, iScale=0.25, Size=1.0, Depth=6, Hard=0, Basis=0, Seed=0) tz = vTurb[2]*iScale return tx, ty, tz - -#------------------------------------------------------------ -# Axis: ( used in 3DCurve Turbulence ) -def AxisFlip(x, y, z, x_axis=1, y_axis=1, z_axis=1, flip=0): - if flip != 0: - flip *= -1 - else: - flip = 1 - x *= x_axis*flip - y *= y_axis*flip - z *= z_axis*flip - return x, y, z - - # ------------------------------------------------------------------- # 2D Curve shape functions: # ------------------------------------------------------------------- @@ -193,12 +180,12 @@ def ProfileCurve(type=0, a=0.25, b=0.25): return newpoints # ------------------------------------------------------------ -# 2DCurve: Arrows +# 2DCurve: Arrow def ArrowCurve(type=1, a=1.0, b=0.5): """ ArrowCurve( type=1, a=1.0, b=0.5, c=1.0 ) - Create arrow curves + Create arrow curve Parameters: type - select type, Arrow1, Arrow2 @@ -235,7 +222,7 @@ 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 + Create square / rectangle curve Parameters: type - select type, Square, Rounded square 1, Rounded square 2 @@ -402,7 +389,7 @@ def ArcCurve(sides=6, startangle=0.0, endangle=90.0, innerradius=0.5, outerradiu newpoints.append([x1, y1, 0]) i += 1 - # if type ==0: + # if type == 1: # Arc: turn cyclic curve flag off! # Segment: @@ -538,57 +525,8 @@ def SplatCurve(sides=24, scale=1.0, seed=0, basis=0, radius=1.0): i += 1 return newpoints -# ----------------------------------------------------------- -# 3D curve shape functions: -# ----------------------------------------------------------- - -# ------------------------------------------------------------ -# 3DCurve: Helix: -def HelixCurve(number=100, height=2.0, startangle=0.0, endangle=360.0, width=1.0, a=0.0, b=0.0): - """ - HelixCurve( number=100, height=2.0, startangle=0.0, endangle=360.0, width=1.0, a=0.0, b=0.0 ) - - Create helix curve - - Parameters: - number - the number of points - (type=int) - height - height - (type=float) - startangle - startangle - (type=float) - endangle - endangle - (type=float) - width - width - (type=float) - a - a - (type=float) - b - b - (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 = [] - angle = (2.0/360.0)*(endangle-startangle) - step = angle/(number-1) - h = height/angle - start = (startangle*2.0/360.0) - a /= angle - i = 0 - while i < number: - t = (i*step+start) - x = sin((t*pi)) * (1.0 + cos(t * pi * a - (b * pi))) * (0.25 * width) - y = cos((t*pi)) * (1.0 + cos(t * pi * a - (b * pi))) * (0.25 * width) - z = (t * h) - h*start - newpoints.append([x, y, z]) - i += 1 - return newpoints - #------------------------------------------------------------ # Cycloid curve - def CycloidCurve(number=100, type=0, R=4.0, r=1.0, d=1.0): """ CycloidCurve( number=100, type=0, a=4.0, b=1.0 ) @@ -645,12 +583,58 @@ def CycloidCurve(number=100, type=0, R=4.0, r=1.0, d=1.0): i+=1 return newpoints +# ----------------------------------------------------------- +# 3D curve shape functions: +# ----------------------------------------------------------- + +# ------------------------------------------------------------ +# 3DCurve: Helix: +def HelixCurve(number=100, height=2.0, startangle=0.0, endangle=360.0, width=1.0, a=0.0, b=0.0): + """ + HelixCurve( number=100, height=2.0, startangle=0.0, endangle=360.0, width=1.0, a=0.0, b=0.0 ) + + Create helix curve + + Parameters: + number - the number of points + (type=int) + height - height + (type=float) + startangle - startangle + (type=float) + endangle - endangle + (type=float) + width - width + (type=float) + a - a + (type=float) + b - b + (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 = [] + angle = (2.0/360.0)*(endangle-startangle) + step = angle/(number-1) + h = height/angle + start = (startangle*2.0/360.0) + a /= angle + i = 0 + while i < number: + t = (i*step+start) + x = sin((t*pi)) * (1.0 + cos(t * pi * a - (b * pi))) * (0.25 * width) + y = cos((t*pi)) * (1.0 + cos(t * pi * a - (b * pi))) * (0.25 * width) + z = (t * h) - h*start + 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): +def NoiseCurve(type=0, number=100, length=2.0, size=0.5, scale=[0.5,0.5,0.5], taper=0.0, octaves=2, basis=0, seed=0): """ - NoiseCurve( number=100, length=2.0, scale=1.0, octaves=2, basis=1, seed=0, type=1 ) - Create noise curve Parameters: @@ -658,7 +642,11 @@ def NoiseCurve(number=100, length=2.0, scale=1.0, octaves=6, basis=1, seed=0, ty (type=int) length - curve length (type=float) - scale - noise scale + size - noise size + (type=float) + scale - noise intensity scale x,y,z + (type=list) + taper - taper scale (type=float) basis - noise basis (type=int) @@ -671,31 +659,39 @@ def NoiseCurve(number=100, length=2.0, scale=1.0, octaves=6, basis=1, seed=0, ty (type=list) """ - rand = randnum(-100,100,seed) newpoints = [] step = (length/number) i = 0 if type == 1: - # noise knot + # noise circle / arc while i < number: - t = ((i*step)+rand) - v = vTurbNoise(t,t,t, scale, 1.0, octaves, 0, basis, seed) + t = i*step + v = vTurbNoise(t, t, t, 1.0, size, octaves, 0, basis, seed) + x = sin(t*pi)*2.0 + (v[0] * scale[0]) + y = cos(t*pi)*2.0 + (v[1] * scale[1]) + z = v[2]*scale[2] + newpoints.append([x, y, z]) + i += 1 + elif type == 2: + # noise knot / ball + while i < number: + t = i*step + v = vTurbNoise(t,t,t, scale[2], 1.0, octaves, 0, basis, seed) newpoints.append([v[0], v[1], v[2]]) i+=1 else: # 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] + tap = length-taper*t + v = vTurbNoise(t,t,t, 1.0, size, octaves, 0, basis, seed) + x = t + v[0] * (scale[0]/length) + y = v[1] * (scale[1]/length) * tap + z = v[2] * (scale[2]/length) * tap newpoints.append([x,y,z]) i+=1 return newpoints - # ------------------------------------------------------------ # calculates the matrix for the new object # depending on user pref @@ -841,6 +837,12 @@ def main(context, self, align_matrix): self.seed, self.basis, outerRadius) + if proType == 'Cycloid': + verts = CycloidCurve(self.cycloPoints, + self.cycloType, + self.cyclo_a, + self.cyclo_b, + self.cyclo_d) if proType == 'Helix': verts = HelixCurve(self.helixPoints, self.helixHeight, @@ -849,20 +851,16 @@ def main(context, self, align_matrix): self.helixWidth, self.helix_a, self.helix_b) - if proType == 'Cycloid': - verts = CycloidCurve(self.cycloPoints, - self.cycloType, - self.cyclo_a, - self.cyclo_b, - self.cyclo_d) if proType == 'Noise': - verts = NoiseCurve(self.noisePoints, + verts = NoiseCurve(self.noiseType, + self.noisePoints, self.noiseLength, - self.noiseScale, + self.noiseSize, + [self.noiseScaleX, self.noiseScaleY, self.noiseScaleZ], + self.noiseTaper, self.noiseOctaves, self.noiseBasis, - self.noiseSeed, - self.noiseType) + self.noiseSeed) # turn verts into array vertArray = vertsToPoints(verts, splineType) @@ -928,6 +926,7 @@ class Curveaceous_galore(Operator): ('VECTOR', 'Vector', 'VECTOR'), ('AUTOMATIC', 'Auto', 'AUTOMATIC')] handleType = EnumProperty(name="Handle type", + default='AUTOMATIC', description="bezier handles type", items=bezHandles) @@ -1078,7 +1077,7 @@ class Curveaceous_galore(Operator): min=3, soft_min=3, description="Resolution") cycloType = IntProperty(name="Type", - default=0, + default=1, min=0, soft_min=0, max=2, soft_max=2, description="Type: Cycloid , Hypocycloid / Hypotrochoid , Epicycloid / Epitrochoid") @@ -1095,6 +1094,11 @@ class Curveaceous_galore(Operator): description="Cycloid: d distance") # Noise properties + noiseType = IntProperty(name="Type", + default=0, + min=0, soft_min=0, + max=2, soft_max=2, + description="Noise curve type: Linear, Circular or Knot") noisePoints = IntProperty(name="Resolution", default=100, min=3, soft_min=3, @@ -1103,29 +1107,42 @@ class Curveaceous_galore(Operator): default=2.0, min=0.01, soft_min=0.01, description="Curve Length") - noiseScale = FloatProperty(name="Noise scale", - default=1.0, + noiseSize = FloatProperty(name="Noise size", + default=0.25, + min=0.0001, soft_min=0.0001, + description="Noise size") + noiseScaleX = FloatProperty(name="Noise x", + default=0.5, min=0.0001, soft_min=0.0001, - description="Noise scale") + description="Noise x") + noiseScaleY = FloatProperty(name="Noise y", + default=0.5, + min=0.0001, soft_min=0.0001, + description="Noise y") + noiseScaleZ = FloatProperty(name="Noise z", + default=0.5, + min=0.0001, soft_min=0.0001, + description="Noise z") + noiseTaper = FloatProperty(name="Noise taper", + default=0.0, + min=0.0001, soft_min=0.0001, + max=1.0, soft_max=1.0, + description="Noise taper") noiseOctaves = IntProperty(name="Octaves", default=2, - min=0, soft_min=0, + min=1, soft_min=1, max=16, soft_max=16, description="Basis") noiseBasis = IntProperty(name="Basis", default=0, min=0, soft_min=0, - max=14, soft_max=14, + max=9, soft_max=9, 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): @@ -1193,6 +1210,14 @@ class Curveaceous_galore(Operator): box.prop(self, 'seed') box.prop(self, 'basis') + elif self.ProfileType == 'Cycloid': + box.prop(self, 'cycloPoints') + box.prop(self, 'cycloType') + box.prop(self, 'cyclo_a') + box.prop(self, 'cyclo_b') + if self.cycloType != 0: + box.prop(self, 'cyclo_d') + elif self.ProfileType == 'Helix': box.prop(self, 'helixPoints') box.prop(self, 'helixHeight') @@ -1202,18 +1227,17 @@ class Curveaceous_galore(Operator): box.prop(self, 'helix_a') box.prop(self, 'helix_b') - elif self.ProfileType == 'Cycloid': - box.prop(self, 'cycloPoints') - 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, 'noisePoints') box.prop(self, 'noiseLength') - box.prop(self, 'noiseScale') + if self.noiseType in [0,1]: + box.prop(self, 'noiseSize') + box.prop(self, 'noiseScaleX') + box.prop(self, 'noiseScaleY') + box.prop(self, 'noiseScaleZ') + if self.noiseType == 0: + box.prop(self, 'noiseTaper') box.prop(self, 'noiseOctaves') box.prop(self, 'noiseBasis') box.prop(self, 'noiseSeed') @@ -1223,11 +1247,10 @@ class Curveaceous_galore(Operator): col.row().prop(self, 'outputType', expand=True) # output options - box = layout.box() if self.outputType == 'NURBS': - box.prop(self, 'order_u') + col.prop(self, 'order_u') elif self.outputType == 'BEZIER': - box.row().prop(self, 'handleType', expand=True) + col.row().prop(self, 'handleType', expand=True) ##### POLL ##### @classmethod @@ -1254,7 +1277,10 @@ class Curveaceous_galore(Operator): else: self.use_cyclic_u = True else: - self.use_cyclic_u = True + if self.ProfileType == 'Arc' and self.arcType == 1: + self.use_cyclic_u = False + else: + self.use_cyclic_u = True # main function main(context, self, self.align_matrix or Matrix()) diff --git a/add_curve_extra_objects/add_curve_spirofit_bouncespline.py b/add_curve_extra_objects/add_curve_spirofit_bouncespline.py new file mode 100644 index 00000000..9b5d7aee --- /dev/null +++ b/add_curve_extra_objects/add_curve_spirofit_bouncespline.py @@ -0,0 +1,630 @@ +# ##### 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": "SpiroFit and BounceSpline", + "author": "Jimmy Hazevoet, Antonio Osprite, Liero, Atom", + "version": (0, 2, 0), + "blender": (2, 78, 0), + "location": "Toolshelf > Misc Tab", + "description": "SpiroFit and BounceSpline adds splines to Mesh", + "warning": "", + "wiki_url": "", + "category": "Object", +} + + +import bpy +from bpy.props import ( + BoolProperty, + EnumProperty, + FloatProperty, + IntProperty, + ) +from mathutils import ( + Matrix, + Vector, + ) +from math import ( + sin, + cos, + pi + ) +from bpy.types import Operator +import random as r + + +# ------------------------------------------------------------ +# +def use_random_seed(seed): + r.seed(seed) + return + +# ------------------------------------------------------------ +# Generate new curve object from given points +# ------------------------------------------------------------ + +def add_curve_object(verts, matrix, + x_ray=False, + spline_type='BEZIER', + spline_resolution=12, + bevel=0.0, + bevel_resolution=0, + spline_random_radius=0.0): + + curve = bpy.data.curves.new('Spline','CURVE') + curve.dimensions = '3D' + spline = curve.splines.new(spline_type) + cur = bpy.data.objects.new('Curve',curve) + + if spline_type == 'BEZIER': + spline.bezier_points.add(int(len(verts)-1)) + for i in range(len(verts)): + spline.bezier_points[i].co = verts[i] + spline.bezier_points[i].handle_right_type = 'AUTO' + spline.bezier_points[i].handle_left_type = 'AUTO' + spline.bezier_points[i].radius += r.random() * spline_random_radius + else: + spline.points.add(int(len(verts)-1)) + for i in range(len(verts)): + spline.points[i].co = verts[i][0], verts[i][1], verts[i][2], 1 + + bpy.context.scene.objects.link(cur) + cur.data.use_uv_as_generated = True + cur.data.resolution_u = spline_resolution + cur.data.fill_mode = 'FULL' + cur.data.bevel_depth = bevel + cur.data.bevel_resolution = bevel_resolution + cur.matrix_world = matrix + if x_ray: + cur.show_x_ray = x_ray + return + + +# ------------------------------------------------------------ +# Spirofit, original blender 2.45 script by: Antonio Osprite +# http://www.kino3d.com/forum/viewtopic.php?t=5374 +# ------------------------------------------------------------ + +def distance(v1, v2): + d = (Vector(v1) - Vector(v2)).length + return d + + +def spiral_point(step, radius, z_coord, spires, waves, wave_height, rndm): + x = radius * cos(spires*step) + r.random()*rndm + y = radius * sin(spires*step) + r.random()*rndm + z = z_coord + (cos(waves*step*pi)*wave_height) + r.random()*rndm + return [x, y, z] + + +def do_object_mapping(obj, vert, center, offset): + intersections = [] + ray = Vector(vert) + orig = Vector(center) + direction = ray - orig + poly = obj.data.polygons + for f in poly: + foo, hit, nor, index = obj.ray_cast(orig, direction) + if hit: + intersections.append(hit + offset * nor) + + if len(intersections) > 0: + mapped = min([(distance(i, vert), i) for i in intersections])[1] + else: + mapped = orig + + return [mapped[0], mapped[1], mapped[2]] + + +def spirofit_spline(obj, + spires=4, + spire_resolution=4, + waves=0, + wave_height=0.0, + rndm_spire=0.0, + offset=0.0): + + points = [] + bb = obj.bound_box + bb_xmin = min([ v[0] for v in bb ]) + bb_ymin = min([ v[1] for v in bb ]) + bb_zmin = min([ v[2] for v in bb ]) + bb_xmax = max([ v[0] for v in bb ]) + bb_ymax = max([ v[1] for v in bb ]) + bb_zmax = max([ v[2] for v in bb ]) + + radius = distance([bb_xmax, bb_ymax, bb_zmin], [bb_xmin, bb_ymin, bb_zmin]) / 2.0 + height = bb_zmax - bb_zmin + cx = (bb_xmax + bb_xmin) / 2.0 + cy = (bb_ymax + bb_ymin) / 2.0 + center = [cx, cy, bb_zmin] + + cp = spiral_point(bb_zmin, radius, bb_zmin, spires, waves, wave_height, 0) + cp = do_object_mapping(obj, cp, center, offset) + + steps = spires * spire_resolution + for i in range(1, steps+1): + t = bb_zmin + (2*pi / steps) * i + z = bb_zmin + (float(height) / steps) * i + center = [cx, cy, z] + + cp = spiral_point(t, radius, z, spires, waves, wave_height, rndm_spire) + cp = do_object_mapping(obj, cp, center, offset) + + points.append(cp) + return points + + +# ------------------------------------------------------------ + +class SpiroFitSpline(bpy.types.Operator): + bl_idname = "wm.add_spirofit_spline" + bl_label = "SpiroFit" + bl_description="Adds a spirofit to selected mesh" + bl_options = {'REGISTER', 'UNDO', 'PRESET'} + + spire_resolution = bpy.props.IntProperty(name="Spire Resolution", + default=4, + min=3, soft_min=3, + max=128, soft_max=128, + description="Spire Resolution") + + spires = bpy.props.IntProperty(name="Spires", + default=4, + min=1, soft_min=1, + max=512, soft_max=512, + description="Number of Spire Turns") + + waves = bpy.props.IntProperty(name="Waves Amount", + default=0, + min=0, soft_min=0, + description="Waves amount") + + wave_height = bpy.props.FloatProperty(name="Wave Intensity", + default=0.1, + min=0.0, soft_min=0.0, + description="Wave intensity scale") + + rndm_spire = bpy.props.FloatProperty(name="Randomize", + default=0.0, + min=0.0, soft_min=0.0, + description="Randomize spire") + + offset = bpy.props.FloatProperty(name="Offset", + default=0.0, + description="Use normal direction to offset spline") + + + splineTypes = [ + ('POLY', 'Poly', 'POLY'), + ('BEZIER', 'Bezier', 'BEZIER')] + spline_type = bpy.props.EnumProperty(name="Spline type", + default='BEZIER', + description="Spline type", + items=splineTypes) + + spline_resolution = bpy.props.IntProperty(name="Resolution u", + default=12, + min=0, soft_min=0, + max=64, soft_max=64, + description="Curve resolution u") + + bevel = bpy.props.FloatProperty(name="Bevel radius", + default=0.0, + min=0.0, soft_min=0.0, + description="Bevel depth") + + bevel_res = bpy.props.IntProperty(name="Bevel resolution", + default=0, + min=0, soft_min=0, + max=32, soft_max=32, + description="Bevel resolution") + + spline_random_radius = bpy.props.FloatProperty(name="Random bevel radius", + default=0.0, + min=0.0, soft_min=0.0, + description="Random radius amount") + + random_seed = bpy.props.IntProperty(name="Random seed", + default=1, + min=0, soft_min=0, + description="Random seed number") + + x_ray = bpy.props.BoolProperty(name="X-Ray", + default=True, + description = "Make the object draw in front of others") + + updateSpline = bpy.props.BoolProperty(name="Update", description="Update spline", default=False) + + + @classmethod + def poll(self, context): + ob = context.active_object + return ((ob is not None) and + (ob.type == 'MESH') and + (context.mode == 'OBJECT')) + + + def invoke(self, context, event): + self.updateSpline = True + return self.execute(context) + + + def execute(self, context): + if not self.updateSpline: + return {'PASS_THROUGH'} + + undo = context.user_preferences.edit.use_global_undo + context.user_preferences.edit.use_global_undo = False + + bpy.ops.object.mode_set(mode='EDIT', toggle=False) + bpy.ops.object.mode_set(mode='OBJECT', toggle=False) + + obj = context.active_object + matrix = obj.matrix_world + + if self.random_seed: + use_random_seed(self.random_seed) + + points = spirofit_spline(obj, + self.spires, + self.spire_resolution, + self.waves, + self.wave_height, + self.rndm_spire, + self.offset) + + add_curve_object(points, + matrix, + self.x_ray, + self.spline_type, + self.spline_resolution, + self.bevel, + self.bevel_res, + self.spline_random_radius) + + context.user_preferences.edit.use_global_undo = undo + return {'FINISHED'} + + + def draw(self, context): + layout = self.layout + + col = layout.column(align=True) + row = col.row(align=True) + + row.prop(self, 'x_ray', toggle=True) + row.separator() + + row.prop(self, 'updateSpline', toggle=True) #, icon='FILE_REFRESH') + row.separator() + + properties = row.operator('wm.add_spirofit_spline', text="Add New") + col.separator() + + properties.x_ray = self.x_ray + + properties.spire_resolution = self.spire_resolution + properties.spires = self.spires + properties.waves = self.waves + properties.wave_height = self.wave_height + properties.offset = self.offset + properties.rndm_spire = self.rndm_spire + properties.random_seed = self.random_seed + + properties.spline_type = self.spline_type + properties.spline_resolution = self.spline_resolution + properties.bevel = self.bevel + properties.bevel_res = self.bevel_res + properties.spline_random_radius = self.spline_random_radius + + col = layout.column(align=True) + col.prop(self, 'spire_resolution') + col.prop(self, 'spires') + col.prop(self, 'waves') + col.prop(self, 'wave_height') + col.prop(self, 'offset') + col.prop(self, 'rndm_spire') + col.prop(self, 'random_seed') + col.separator() + + col = layout.column(align=True) + col.prop(self, 'spline_type', text="") + col.separator() + col.prop(self, 'spline_resolution') + col.prop(self, 'bevel') + if self.spline_type == 'BEZIER': + col.prop(self, 'spline_random_radius') + col.prop(self, 'bevel_res') + + +# ------------------------------------------------------------ +# Bounce spline / Fiber mesh +# Original script by Liero and Atom +# https://blenderartists.org/forum/showthread.php?331750-Fiber-Mesh-Emulation +# ------------------------------------------------------------ + +def noise(var=1): + rand = Vector((r.gauss(0,1), r.gauss(0,1), r.gauss(0,1))) + vec = rand.normalized() * var + return vec + + +def bounce_spline(obj, + number=1000, + ang_noise=0.25, + offset=0.0, + extra=50, + active_face=False): + + dist, points = 1000, [] + poly = obj.data.polygons + + if active_face: + try: + n = poly.active + except: + print("No active face selected") + pass + else: + n = r.randint(0, len(poly)-1) + + end = poly[n].normal.copy() * -1 + start = poly[n].center + points.append(start + offset * end) + + for i in range(number): + for ray in range(extra + 1): + end += noise(ang_noise) + try: + foo, hit, nor, index = obj.ray_cast(start, end * dist) + except: + index = -1 + if index != -1: + start = hit - nor / 10000 + end = end.reflect(nor).normalized() + points.append(hit + offset * nor) + break + if index == -1: + return points + return points + +# ------------------------------------------------------------ + +class BounceSpline(bpy.types.Operator): + bl_idname = "wm.add_bounce_spline" + bl_label = "BounceSpline" + bl_description="Adds a bounce spline to selected mesh" + bl_options = {'REGISTER', 'UNDO', 'PRESET'} + + random_seed = bpy.props.IntProperty(name="Random seed", + default=0, + min=0, soft_min=0, + description="Random seed number") + + bounce_number = bpy.props.IntProperty(name="Bounces", + default=100, + min=1, soft_min=1, + max=9999, soft_max=9999, + description="Number of Bounces") + + ang_noise = bpy.props.FloatProperty(name="Angular noise", + default=0.25, + min=0.0, soft_min=0.0, + description="Add some noise to ray direction") + + offset = bpy.props.FloatProperty(name="Offset", + default=0.0, + description="Use normal direction to offset spline") + + extra = bpy.props.IntProperty(name="Extra", + default=50, + min=0, soft_min=0, + max=999, soft_max=999, + description="Number of extra tries if it fails to hit mesh") + + active_face = bpy.props.BoolProperty(name="Active face", + default=False, + description = "Starts from active face or a random one") + + splineTypes = [ + ('POLY', 'Poly', 'POLY'), + ('BEZIER', 'Bezier', 'BEZIER')] + spline_type = bpy.props.EnumProperty(name="Spline type", + default='BEZIER', + description="Spline type", + items=splineTypes) + + spline_resolution = bpy.props.IntProperty(name="Resolution u", + default=12, + min=0, soft_min=0, + max=64, soft_max=64, + description="Curve resolution u") + + bevel = bpy.props.FloatProperty(name="Bevel radius", + default=0.0, + min=0.0, soft_min=0.0, + description="Bevel depth") + + bevel_res = bpy.props.IntProperty(name="Bevel resolution", + default=0, + min=0, soft_min=0, + max=32, soft_max=32, + description="Bevel resolution") + + spline_random_radius = bpy.props.FloatProperty(name="Random bevel radius", + default=0.0, + min=0.0, soft_min=0.0, + description="Random radius amount") + + x_ray = bpy.props.BoolProperty(name="X-Ray", + default=True, + description = "Make the object draw in front of others") + + updateSpline = bpy.props.BoolProperty(name="Update", default=False) + + + @classmethod + def poll(self, context): + ob = context.active_object + return ((ob is not None) and + (ob.type == 'MESH') and + (context.mode == 'OBJECT')) + + + def invoke(self, context, event): + self.updateSpline = True + return self.execute(context) + + + def execute(self, context): + if not self.updateSpline: + return {'PASS_THROUGH'} + + undo = context.user_preferences.edit.use_global_undo + context.user_preferences.edit.use_global_undo = False + + bpy.ops.object.mode_set(mode='EDIT', toggle=False) + bpy.ops.object.mode_set(mode='OBJECT', toggle=False) + + obj = context.active_object + + if self.random_seed: + use_random_seed(self.random_seed) + + points = bounce_spline(obj, + self.bounce_number, + self.ang_noise, + self.offset, + self.extra, + self.active_face) + + add_curve_object(points, + obj.matrix_world, + self.x_ray, + self.spline_type, + self.spline_resolution, + self.bevel, + self.bevel_res, + self.spline_random_radius) + + context.user_preferences.edit.use_global_undo = undo + return {'FINISHED'} + + + def draw(self, context): + layout = self.layout + col = layout.column(align=True) + row = col.row(align=True) + + row.prop(self, 'x_ray', toggle=True) + row.separator() + + row.prop(self, 'updateSpline', toggle=True) #, icon='FILE_REFRESH') + row.separator() + + properties = row.operator('wm.add_bounce_spline', text="Add New") + col.separator() + + properties.x_ray = self.x_ray + + properties.bounce_number = self.bounce_number + properties.ang_noise = self.ang_noise + properties.offset = self.offset + properties.extra = self.extra + properties.random_seed = self.random_seed + properties.active_face = self.active_face + + properties.spline_type = self.spline_type + properties.spline_resolution = self.spline_resolution + properties.bevel = self.bevel + properties.bevel_res = self.bevel_res + properties.spline_random_radius = self.spline_random_radius + + col = layout.column(align=True) + row = col.row(align=True) + col.prop(self, 'bounce_number') + col.prop(self, 'ang_noise') + col.prop(self, 'offset') + col.prop(self, 'extra') + col.prop(self, 'random_seed') + col.separator() + col.prop(self, 'active_face', toggle=False) + + col = layout.column(align=True) + col.prop(self, 'spline_type', text="") + col.separator() + col.prop(self, 'spline_resolution') + + col.prop(self, 'bevel') + if self.spline_type == 'BEZIER': + col.prop(self, 'spline_random_radius') + col.prop(self, 'bevel_res') + + +# ------------------------------------------------------------ +# Tools Panel > Misc +# ------------------------------------------------------------ + +class SplinePanel( bpy.types.Panel ): + bl_space_type = "VIEW_3D" + bl_context = "objectmode" + bl_region_type = "TOOLS" + bl_label = "Add Spline to Mesh" + bl_category = "Create" + bl_options = {'DEFAULT_CLOSED'} + + def draw( self, context ): + scn = context.scene + layout = self.layout + col = self.layout.column() + col.operator(SpiroFitSpline.bl_idname, icon="FORCE_MAGNETIC") + col.separator() + col.operator(BounceSpline.bl_idname, icon="FORCE_HARMONIC") + +# ------------------------------------------------------------ # icon="CURVE_DATA" +# Menu: Add > Curve > +# ------------------------------------------------------------ + +''' +def menu_func(self, context): + self.layout.operator(SpiroFitSpline.bl_idname, icon="PLUGIN") + self.layout.operator(BounceSpline.bl_idname, icon="PLUGIN") +''' + +# ------------------------------------------------------------ +# Register +# ------------------------------------------------------------ + +def register(): + bpy.utils.register_class(SpiroFitSpline) + bpy.utils.register_class(BounceSpline) + bpy.utils.register_class(SplinePanel) + + #bpy.types.INFO_MT_curve_add.append(menu_func) + +def unregister(): + bpy.utils.unregister_class(SpiroFitSpline) + bpy.utils.unregister_class(BounceSpline) + bpy.utils.unregister_class(SplinePanel) + #bpy.types.INFO_MT_curve_add.remove(menu_func) + +if __name__ == "__main__": + register() -- cgit v1.2.3