Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'curve_simplify.py')
-rw-r--r--curve_simplify.py597
1 files changed, 0 insertions, 597 deletions
diff --git a/curve_simplify.py b/curve_simplify.py
deleted file mode 100644
index f723b99d..00000000
--- a/curve_simplify.py
+++ /dev/null
@@ -1,597 +0,0 @@
-# ##### 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_addon_info = {
- "name": "Simplify curves",
- "author": "testscreenings",
- "version": (1,),
- "blender": (2, 5, 3),
- "api": 31667,
- "location": "Toolshelf > search > simplify curves",
- "description": "This script simplifies 3D curves and fcurves",
- "warning": "",
- "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.5/Py/"\
- "Scripts/Curve/Curve_Simplify",
- "tracker_url": "https://projects.blender.org/tracker/index.php?"\
- "func=detail&aid=22327&group_id=153&atid=468",
- "category": "Add Curve"}
-
-"""
-This script simplifies Curves.
-"""
-
-####################################################
-import bpy
-from bpy.props import *
-import mathutils
-import math
-
-##############################
-#### simplipoly algorithm ####
-##############################
-# get SplineVertIndicies to keep
-def simplypoly(splineVerts, options):
- # main vars
- newVerts = [] # list of vertindices to keep
- points = splineVerts # list of 3dVectors
- pointCurva = [] # table with curvatures
- curvatures = [] # averaged curvatures per vert
- for p in points:
- pointCurva.append([])
- order = options[3] # order of sliding beziercurves
- k_thresh = options[2] # curvature threshold
- dis_error = options[6] # additional distance error
-
- # get curvatures per vert
- for i, point in enumerate(points[:-(order-1)]):
- BVerts = points[i:i+order]
- for b, BVert in enumerate(BVerts[1:-1]):
- deriv1 = getDerivative(BVerts, 1/(order-1), order-1)
- deriv2 = getDerivative(BVerts, 1/(order-1), order-2)
- curva = getCurvature(deriv1, deriv2)
- pointCurva[i+b+1].append(curva)
-
- # average the curvatures
- for i in range(len(points)):
- avgCurva = sum(pointCurva[i]) / (order-1)
- curvatures.append(avgCurva)
-
- # get distancevalues per vert - same as Ramer-Douglas-Peucker
- # but for every vert
- distances = [0.0] #first vert is always kept
- for i, point in enumerate(points[1:-1]):
- dist = altitude(points[i], points[i+2], points[i+1])
- distances.append(dist)
- distances.append(0.0) # last vert is always kept
-
- # generate list of vertindicies to keep
- # tested against averaged curvatures and distances of neighbour verts
- newVerts.append(0) # first vert is always kept
- for i, curv in enumerate(curvatures):
- if (curv >= k_thresh*0.01
- or distances[i] >= dis_error*0.1):
- newVerts.append(i)
- newVerts.append(len(curvatures)-1) # last vert is always kept
-
- return newVerts
-
-# get binomial coefficient
-def binom(n, m):
- b = [0] * (n+1)
- b[0] = 1
- for i in range(1, n+1):
- b[i] = 1
- j = i-1
- while j > 0:
- b[j] += b[j-1]
- j-= 1
- return b[m]
-
-# get nth derivative of order(len(verts)) bezier curve
-def getDerivative(verts, t, nth):
- order = len(verts) - 1 - nth
- QVerts = []
-
- if nth:
- for i in range(nth):
- if QVerts:
- verts = QVerts
- derivVerts = []
- for i in range(len(verts)-1):
- derivVerts.append(verts[i+1] - verts[i])
- QVerts = derivVerts
- else:
- QVerts = verts
-
- if len(verts[0]) == 3:
- point = mathutils.Vector((0, 0, 0))
- if len(verts[0]) == 2:
- point = mathutils.Vector((0, 0))
-
- for i, vert in enumerate(QVerts):
- point += binom(order, i) * math.pow(t, i) * math.pow(1-t, order-i) * vert
- deriv = point
-
- return deriv
-
-# get curvature from first, second derivative
-def getCurvature(deriv1, deriv2):
- if deriv1.length == 0: # in case of points in straight line
- curvature = 0
- return curvature
- curvature = (deriv1.cross(deriv2)).length / math.pow(deriv1.length, 3)
- return curvature
-
-#########################################
-#### Ramer-Douglas-Peucker algorithm ####
-#########################################
-# get altitude of vert
-def altitude(point1, point2, pointn):
- edge1 = point2 - point1
- edge2 = pointn - point1
- if edge2.length == 0:
- altitude = 0
- return altitude
- if edge1.length == 0:
- altitude = edge2.length
- return altitude
- alpha = edge1.angle(edge2)
- altitude = math.sin(alpha) * edge2.length
- return altitude
-
-# iterate through verts
-def iterate(points, newVerts, error):
- new = []
- for newIndex in range(len(newVerts)-1):
- bigVert = 0
- alti_store = 0
- for i, point in enumerate(points[newVerts[newIndex]+1:newVerts[newIndex+1]]):
- alti = altitude(points[newVerts[newIndex]], points[newVerts[newIndex+1]], point)
- if alti > alti_store:
- alti_store = alti
- if alti_store >= error:
- bigVert = i+1+newVerts[newIndex]
- if bigVert:
- new.append(bigVert)
- if new == []:
- return False
- return new
-
-#### get SplineVertIndicies to keep
-def simplify_RDP(splineVerts, options):
- #main vars
- error = options[4]
-
- # set first and last vert
- newVerts = [0, len(splineVerts)-1]
-
- # iterate through the points
- new = 1
- while new != False:
- new = iterate(splineVerts, newVerts, error)
- if new:
- newVerts += new
- newVerts.sort()
- return newVerts
-
-##########################
-#### CURVE GENERATION ####
-##########################
-# set bezierhandles to auto
-def setBezierHandles(newCurve):
- scene = bpy.context.scene
- bpy.ops.object.mode_set(mode='EDIT', toggle=True)
- bpy.ops.curve.select_all(action='SELECT')
- bpy.ops.curve.handle_type_set(type='AUTOMATIC')
- bpy.ops.object.mode_set(mode='OBJECT', toggle=True)
-
-# get array of new coords for new spline from vertindices
-def vertsToPoints(newVerts, splineVerts, splineType):
- # main vars
- newPoints = []
-
- # array for BEZIER spline output
- if splineType == 'BEZIER':
- for v in newVerts:
- newPoints += splineVerts[v].to_tuple()
-
- # array for nonBEZIER output
- else:
- for v in newVerts:
- newPoints += (splineVerts[v].to_tuple())
- if splineType == 'NURBS':
- newPoints.append(1) #for nurbs w=1
- else: #for poly w=0
- newPoints.append(0)
- return newPoints
-
-#########################
-#### MAIN OPERATIONS ####
-#########################
-
-def main(context, obj, options):
- #print("\n_______START_______")
- # main vars
- mode = options[0]
- output = options[1]
- degreeOut = options[5]
- keepShort = options[7]
- bpy.ops.object.select_all(action='DESELECT')
- scene = context.scene
- splines = obj.data.splines.values()
-
- # create curvedatablock
- curve = bpy.data.curves.new("simple_"+obj.name, type = 'CURVE')
-
- # go through splines
- for spline_i, spline in enumerate(splines):
- # test if spline is a long enough
- if len(spline.points) >= 7 or keepShort:
- #check what type of spline to create
- if output == 'INPUT':
- splineType = spline.type
- else:
- splineType = output
-
- # get vec3 list to simplify
- if spline.type == 'BEZIER': # get bezierverts
- splineVerts = [splineVert.co.copy()
- for splineVert in spline.bezier_points.values()]
-
- else: # verts from all other types of curves
- splineVerts = [splineVert.co.copy().resize3D()
- for splineVert in spline.points.values()]
-
- # simplify spline according to mode
- if mode == 'distance':
- newVerts = simplify_RDP(splineVerts, options)
-
- if mode == 'curvature':
- newVerts = simplypoly(splineVerts, options)
-
- # convert indicies into vectors3D
- newPoints = vertsToPoints(newVerts, splineVerts, splineType)
-
- # create new spline
- newSpline = curve.splines.new(type = splineType)
-
- # put newPoints into spline according to type
- if splineType == 'BEZIER':
- newSpline.bezier_points.add(int(len(newPoints)*0.33))
- newSpline.bezier_points.foreach_set('co', newPoints)
- else:
- newSpline.points.add(int(len(newPoints)*0.25 - 1))
- newSpline.points.foreach_set('co', newPoints)
-
- # set degree of outputNurbsCurve
- if output == 'NURBS':
- newSpline.order_u = degreeOut
-
- # splineoptions
- newSpline.use_endpoint_u = spline.use_endpoint_u
-
- # create ne object and put into scene
- newCurve = bpy.data.objects.new("simple_"+obj.name, curve)
- scene.objects.link(newCurve)
- newCurve.select = True
- scene.objects.active = newCurve
- newCurve.matrix_world = obj.matrix_world
-
- # set bezierhandles to auto
- setBezierHandles(newCurve)
-
- #print("________END________\n")
- return
-
-##################
-## get preoperator fcurves
-def getFcurveData(obj):
- fcurves = []
- for fc in obj.animation_data.action.fcurves:
- if fc.select:
- fcVerts = [vcVert.co.copy().resize3D()
- for vcVert in fc.keyframe_points.values()]
- fcurves.append(fcVerts)
- return fcurves
-
-def selectedfcurves(obj):
- fcurves_sel = []
- for i, fc in enumerate(obj.animation_data.action.fcurves):
- if fc.select:
- fcurves_sel.append(fc)
- return fcurves_sel
-
-###########################################################
-## fCurves Main
-def fcurves_simplify(context, obj, options, fcurves):
- # main vars
- mode = options[0]
- scene = context.scene
- fcurves_obj = obj.animation_data.action.fcurves
-
- #get indicies of selected fcurves
- fcurve_sel = selectedfcurves(obj)
-
- # go through fcurves
- for fcurve_i, fcurve in enumerate(fcurves):
- # test if fcurve is long enough
- if len(fcurve) >= 7:
-
- # simplify spline according to mode
- if mode == 'distance':
- newVerts = simplify_RDP(fcurve, options)
-
- if mode == 'curvature':
- newVerts = simplypoly(fcurve, options)
-
- # convert indicies into vectors3D
- newPoints = []
-
- #this is different from the main() function for normal curves, different api...
- for v in newVerts:
- newPoints.append(fcurve[v])
-
- #remove all points from curve first
- for i in range(len(fcurve)-1,0,-1):
- fcurve_sel[fcurve_i].keyframe_points.remove(fcurve_sel[fcurve_i].keyframe_points[i])
- # put newPoints into fcurve
- for v in newPoints:
- fcurve_sel[fcurve_i].keyframe_points.add(frame=v[0],value=v[1])
- #fcurve.points.foreach_set('co', newPoints)
- return
-
-#################################################
-#### ANIMATION CURVES OPERATOR ##################
-#################################################
-class GRAPH_OT_simplify(bpy.types.Operator):
- ''''''
- bl_idname = "graph.simplify"
- bl_label = "simplifiy f-curves"
- bl_description = "simplify selected f-curves"
- bl_options = {'REGISTER', 'UNDO'}
-
- ## Properties
- opModes = [
- ('distance', 'distance', 'distance'),
- ('curvature', 'curvature', 'curvature')]
- mode = EnumProperty(name="Mode",
- description="choose algorithm to use",
- items=opModes)
- k_thresh = FloatProperty(name="k",
- min=0, soft_min=0,
- default=0, precision=3,
- description="threshold")
- pointsNr = IntProperty(name="n",
- min=5, soft_min=5,
- max=16, soft_max=9,
- default=5,
- description="degree of curve to get averaged curvatures")
- error = FloatProperty(name="error",
- description="maximum error to allow - distance",
- min=0.0, soft_min=0.0,
- default=0, precision=3)
- degreeOut = IntProperty(name="degree",
- min=3, soft_min=3,
- max=7, soft_max=7,
- default=5,
- description="degree of new curve")
- dis_error = FloatProperty(name="distance error",
- description="maximum error in Blenderunits to allow - distance",
- min=0, soft_min=0,
- default=0.0, precision=3)
- fcurves = []
-
- ''' Remove curvature mode as long as it isnn't significantly improved
-
- def draw(self, context):
- props = self.properties
- layout = self.layout
- col = layout.column()
- col.label('Mode:')
- col.prop(props, 'mode', expand=True)
- if self.properties.mode == 'distance':
- box = layout.box()
- box.label(props.mode, icon='ARROW_LEFTRIGHT')
- box.prop(props, 'error', expand=True)
- if self.properties.mode == 'curvature':
- box = layout.box()
- box.label('degree', icon='SMOOTHCURVE')
- box.prop(props, 'pointsNr', expand=True)
- box.label('threshold', icon='PARTICLE_PATH')
- box.prop(props, 'k_thresh', expand=True)
- box.label('distance', icon='ARROW_LEFTRIGHT')
- box.prop(props, 'dis_error', expand=True)
- col = layout.column()
- '''
-
- def draw(self, context):
- props = self.properties
- layout = self.layout
- col = layout.column()
- col.prop(props, 'error', expand=True)
-
- ## Check for animdata
- @classmethod
- def poll(cls, context):
- obj = context.active_object
- fcurves = False
- if obj:
- animdata = obj.animation_data
- if animdata:
- act = animdata.action
- if act:
- fcurves = act.fcurves
- return (obj and fcurves)
-
- ## execute
- def execute(self, context):
- #print("------START------")
-
- options = [
- self.properties.mode, #0
- self.properties.mode, #1
- self.properties.k_thresh, #2
- self.properties.pointsNr, #3
- self.properties.error, #4
- self.properties.degreeOut, #6
- self.properties.dis_error] #7
-
- obj = context.active_object
-
- if not self.fcurves:
- self.fcurves = getFcurveData(obj)
-
- fcurves_simplify(context, obj, options, self.fcurves)
-
- #print("-------END-------")
- return {'FINISHED'}
-
-###########################
-##### Curves OPERATOR #####
-###########################
-class CURVE_OT_simplify(bpy.types.Operator):
- ''''''
- bl_idname = "curve.simplify"
- bl_label = "simplifiy curves"
- bl_description = "simplify curves"
- bl_options = {'REGISTER', 'UNDO'}
-
- ## Properties
- opModes = [
- ('distance', 'distance', 'distance'),
- ('curvature', 'curvature', 'curvature')]
- mode = EnumProperty(name="Mode",
- description="choose algorithm to use",
- items=opModes)
- SplineTypes = [
- ('INPUT', 'Input', 'same type as input spline'),
- ('NURBS', 'Nurbs', 'NURBS'),
- ('BEZIER', 'Bezier', 'BEZIER'),
- ('POLY', 'Poly', 'POLY')]
- output = EnumProperty(name="Output splines",
- description="Type of splines to output",
- items=SplineTypes)
- k_thresh = FloatProperty(name="k",
- min=0, soft_min=0,
- default=0, precision=3,
- description="threshold")
- pointsNr = IntProperty(name="n",
- min=5, soft_min=5,
- max=9, soft_max=9,
- default=5,
- description="degree of curve to get averaged curvatures")
- error = FloatProperty(name="error in Bu",
- description="maximum error in Blenderunits to allow - distance",
- min=0, soft_min=0,
- default=0.0, precision=3)
- degreeOut = IntProperty(name="degree",
- min=3, soft_min=3,
- max=7, soft_max=7,
- default=5,
- description="degree of new curve")
- dis_error = FloatProperty(name="distance error",
- description="maximum error in Blenderunits to allow - distance",
- min=0, soft_min=0,
- default=0.0)
- keepShort = BoolProperty(name="keep short Splines",
- description="keep short splines (less then 7 points)",
- default=True)
-
- ''' Remove curvature mode as long as it isnn't significantly improved
-
- def draw(self, context):
- props = self.properties
- layout = self.layout
- col = layout.column()
- col.label('Mode:')
- col.prop(props, 'mode', expand=True)
- if self.properties.mode == 'distance':
- box = layout.box()
- box.label(props.mode, icon='ARROW_LEFTRIGHT')
- box.prop(props, 'error', expand=True)
- if self.properties.mode == 'curvature':
- box = layout.box()
- box.label('degree', icon='SMOOTHCURVE')
- box.prop(props, 'pointsNr', expand=True)
- box.label('threshold', icon='PARTICLE_PATH')
- box.prop(props, 'k_thresh', expand=True)
- box.label('distance', icon='ARROW_LEFTRIGHT')
- box.prop(props, 'dis_error', expand=True)
- col = layout.column()
- col.separator()
- col.prop(props, 'output', text='Output', icon='OUTLINER_OB_CURVE')
- if props.output == 'NURBS':
- col.prop(props, 'degreeOut', expand=True)
- col.prop(props, 'keepShort', expand=True)
- '''
-
- def draw(self, context):
- props = self.properties
- layout = self.layout
- col = layout.column()
- col.prop(props, 'error', expand=True)
- col.prop(props, 'output', text='Output', icon='OUTLINER_OB_CURVE')
- if props.output == 'NURBS':
- col.prop(props, 'degreeOut', expand=True)
- col.prop(props, 'keepShort', expand=True)
-
-
- ## Check for curve
- @classmethod
- def poll(cls, context):
- obj = context.active_object
- return (obj and obj.type == 'CURVE')
-
- ## execute
- def execute(self, context):
- #print("------START------")
-
- options = [
- self.properties.mode, #0
- self.properties.output, #1
- self.properties.k_thresh, #2
- self.properties.pointsNr, #3
- self.properties.error, #4
- self.properties.degreeOut, #5
- self.properties.dis_error, #6
- self.properties.keepShort] #7
-
-
- bpy.context.user_preferences.edit.use_global_undo = False
-
- bpy.ops.object.mode_set(mode='OBJECT', toggle=True)
- obj = context.active_object
-
- main(context, obj, options)
-
- bpy.context.user_preferences.edit.use_global_undo = True
-
- #print("-------END-------")
- return {'FINISHED'}
-
-#################################################
-#### REGISTER ###################################
-#################################################
-def register():
- pass
-
-def unregister():
- pass
-
-if __name__ == "__main__":
- register()