Also when posting large code blocks pls use the </> button and paste your code so that indentation is maintained.
bl_info = {
"name": "Harmonograph",
"description": "Generates a path object representing a harmonograph based on 3 dual axis pendulums.",
"author": "Silan Laurent",
"version": (0,1),
"category": "Object",
}
import bpy
import random
import math
class cHarmonograph(bpy.types.Operator):
'''Harmonograph''' #tooltip
bl_idname = "object.harmonograph" # unique identifier for buttons and menu items to reference.
bl_label = "Harmonograph" #display name in the interface
bl_options = {'REGISTER', 'UNDO'}
PathType: bpy.props.BoolProperty(name="Path Type Poly (default Bezier)")
Detail: bpy.props.FloatProperty(name="Detail", default=0.15, min=0.0, max=1)
Steps: bpy.props.IntProperty(name="Steps", default=500, min=1, max=50000)
Dampening: bpy.props.FloatProperty(name="Dampening", default=0.02, min=0.0, max=1)
Frequency_X: bpy.props.FloatVectorProperty(name="Pendulum X: Frequency", default=(2.0,2.0),min=0.0, max=25, soft_min=0.0, soft_max=25,subtype='XYZ',size=2)
Amplitude_X: bpy.props.FloatVectorProperty(name="Pendulum X: Amplitude", default=(1.0,1.0),min=0.0, max=25, soft_min=0.0, soft_max=25,subtype='XYZ',size=2)
Phase_X: bpy.props.FloatVectorProperty(name="Pendulum X: Rotation", default=(180,180),min=0.0, max=360, soft_min=0.0, soft_max=10,subtype='XYZ',size=2)
Frequency_Y: bpy.props.FloatVectorProperty(name="Pendulum Y: Frequency", default=(1.94,1.94),min=0.0, max=25, soft_min=0.0, soft_max=25,subtype='XYZ',size=2)
Amplitude_Y: bpy.props.FloatVectorProperty(name="Pendulum Y: Amplitude", default=(1.0,1.0),min=0.0, max=25, soft_min=0.0, soft_max=25,subtype='XYZ',size=2)
Phase_Y: bpy.props.FloatVectorProperty(name="Pendulum Y: Rotation", default=(90.0,90.0),min=0.0, max=360, soft_min=0.0, soft_max=10,subtype='XYZ',size=2)
Frequency_Z: bpy.props.FloatVectorProperty(name="Pendulum Z: Frequency", default=(1.9,1.9),min=0.0, max=25, soft_min=0.0, soft_max=25,subtype='XYZ',size=2)
Amplitude_Z: bpy.props.FloatVectorProperty(name="Pendulum Z: Amplitude", default=(0.5,0.5),min=0.0, max=25, soft_min=0.0, soft_max=25,subtype='XYZ',size=2)
Phase_Z: bpy.props.FloatVectorProperty(name="Pendulum Z: Rotation", default=(0.0,0.0),min=0.0, max=360, soft_min=0.0, soft_max=10,subtype='XYZ',size=2)
def execute(self, context):
e = math.e
piRad = math.pi/180.0
#print(e)
#Amplitudes
Ax1 = self.Amplitude_X.x
Ax2 = self.Amplitude_X.y
Ay1 = self.Amplitude_Y.x
Ay2 = self.Amplitude_Y.y
Az1 = self.Amplitude_Z.x
Az2 = self.Amplitude_Z.y
#Phase
Px1 = self.Phase_X.x*piRad
Px2 = self.Phase_X.y*piRad
Py1 = self.Phase_Y.x*piRad
Py2 = self.Phase_Y.y*piRad
Pz1 = self.Phase_Z.x*piRad
Pz2 = self.Phase_Z.y*piRad
#Frequency
Fx1 = self.Frequency_X.x
Fx2 = self.Frequency_X.y
Fy1 = self.Frequency_Y.x
Fy2 = self.Frequency_Y.y
Fz1 = self.Frequency_Z.x
Fz2 = self.Frequency_Z.y
#dampening
d = self.Dampening # the dampening factor
dmp = 0.1 # use as help
#clear scene of all objects
#bpy.ops.object.select_all(action='SELECT') # delete all selected
bpy.ops.curve.primitive_nurbs_path_add() # create default path
bpy.ops.object.editmode_toggle() # switch to edit mode
bpy.ops.curve.select_all(action='SELECT') # select the entire path
bpy.ops.curve.delete() # delete the path
x = 0.0
y = 0.0
z = 0.0
t = 0.0 # the decimal makes it a floating point number
cnt = 0
while cnt < self.Steps:
# e^(-d*t)
dmp = pow(e,-d*t)
# x(t) = Ax1*sin(t*Fx1 + Px1)*e^(-d*t) + Ax2*sin(t*Fx2 + Px2)*e^(-d*t)
x = Ax1*math.sin(t*Fx1 + Px1)*dmp + Ax2*math.sin(t*Fx2 + Px2)*dmp
y = Ay1*math.sin(t*Fy1 + Py1)*dmp + Ay2*math.sin(t*Fy2 + Py2)*dmp
z = Az1*math.sin(t*Fz1 + Pz1)*dmp + Az2*math.sin(t*Fz2 + Pz2)*dmp
# pt =[random.randint(-10,10),random.randint(-10,10),random.randint(-10,10)]
pt =[x,y,z]
bpy.ops.curve.vertex_add(location = pt)
t+=self.Detail
cnt+=1
bpy.ops.curve.select_all(action='SELECT') # select all points
if self.PathType:
bpy.ops.curve.spline_type_set(type='POLY')
# convert path to polyline from bezier curve. (SUMO paths are poly lines)
bpy.ops.object.editmode_toggle()
# return the scene to object mode
return {'FINISHED'}
def menu_func(self, context):
self.layout.operator(cHarmonograph.bl_idname)
# store keymaps here to access after registration
addon_keymaps = []
def register():
bpy.utils.register_class(cHarmonograph)
bpy.types.VIEW3D_MT_object.append(menu_func)
def unregister():
bpy.utils.unregister_class(cHarmonograph)
bpy.types.VIEW3D_MT_object.remove(menu_func)
if __name__ == "__main__":
register()