I'm trying to re-create and animate a "Whispy Clifford Attractors" this http://paulbourke.net/fractals/clifford/5.jpg from this site http://paulbourke.net/fractals/clifford/
I can create the equations and animate the a
variable in Octave / Matlab (but as you can see it's not a great image see animated gif below).
I'm having trouble re-creating and animating the image in Bender 2.8
I've adapted some python code but I'm having trouble with the math syntax to get it to work in Blender 2.8
Clifford formula
xn+1 = sin(a*yn) + c cos(a*xn)
yn+1 = sin(b*xn) + d cos(b*yn)
where a, b, c, d are variables that define each attractor.
When I run my script and click on -Add- -Curve- -Test Attactors- -Lorenz- This one works
When I click on -Add- -Curve- -Test Attactors- -Clifford- I get a single curve line.
I believe the issue is in these lines but I'm not sure how to fix it. I tried different for
and while
loops but no luck.
def iterate(self, x, y, z):
#while itr < 100:
#for i in range (10):
#add for loop find out npoints in while loop
dx = math.sin(self.a*y) + self.c*math.cos(self.a*x)
dy = math.sin(self.b*x) + self.d*math.cos(self.b*y)
dz = math.sin(self.e*x) + self.f*math.cos(self.e*z)
return (dx, dy, dz)
Here's the entire python code:
import bpy
import math
from bpy.props import BoolProperty
from bpy.props import FloatProperty
from bpy.props import FloatVectorProperty
from bpy.props import IntProperty
#print(IndentationError().msg)
bl_info = {
"author": "rt_and_mike.tyka@gmail.com",
"blender": (2, 80, 0),
"category": "Add Curve",
"description": "Creates a strange attractor curve",
"location": "View3D > Add > Curve > Attractors",
"name": "Test Attractors",
"version": (0, 1),
"warning": "",
"wiki_url": ""
}
def get_prop(cls, name, default):
return bpy.props.FloatProperty(attr=cls + "_" + name,
name=name,
description="",
default=default)
def get_int_prop(cls, name, default):
return bpy.props.IntProperty(attr=cls + "_" + name,
name=name,
description="",
default=default)
def get_npoints(default=10000):
return IntProperty(attr="npoints",
name="Vertices",
description="",
min=1, soft_min=1, default=default)
class Object_OT_Attractor(bpy.types.Operator):
bl_idname= "object.attractor"
bl_label= "Label Attractor"
view_align= BoolProperty(name="Align to View",default=False)
location= FloatVectorProperty(name="Location",subtype="TRANSLATION")
rotation= FloatVectorProperty(name="Rotation",subtype="EULER")
def draw(self, context):
layout = self.layout
row = layout.row()
row.prop(self, "npoints", text="Vertices")
col = layout.column(align=True)
for param in self.params:
col.prop(self, param, text=param)
col2 = layout.column(align=True)
col2.prop(self, "x", text="x")
col2.prop(self, "y", text="y")
col2.prop(self, "z", text="z")
col2.prop(self, "dt", text="dt")
def execute(self, context):
curvedata = bpy.data.curves.new(name="Curve", type="CURVE")
curvedata.dimensions = "3D"
objectdata = bpy.data.objects.new("ObjCurve", curvedata)
objectdata.location = (0,0,0)
#bpy.context.scene.objects.link(objectdata)
bpy.context.scene.collection.objects.link(objectdata)
polyline = curvedata.splines.new("POLY")
polyline.points.add(self.npoints-1)
x = self.x
y = self.y
z = self.z
n = 0
while n < self.npoints:
dx, dy, dz = self.iterate(x, y, z)
x = x + self.dt * dx
y = y + self.dt * dy
z = z + self.dt * dz
polyline.points[n].co = (x,y,z,math.sqrt(dx*dx+dy*dy+dz*dz))
n += 1
return {"FINISHED"}
class CliffordAttractor(Object_OT_Attractor):
bl_idname = "curve.clifford_attractor_add"
bl_label = "Clifford Attractor"
bl_options = {"REGISTER", "UNDO"}
npoints = get_npoints()
params = ["a", "b", "c", "d", "e", "f"]
a= get_prop("clifford", "a", -1.24458)
b= get_prop("clifford", "b", -1.25191)
c= get_prop("clifford", "c", -1.815908)
d= get_prop("clifford", "d", -1.90866)
e= get_prop("clifford", "e", -0.20)
f= get_prop("clifford", "f", 0.50)
x= get_prop("clifford", "x", 0.10)
y= get_prop("clifford", "y", 0.00)
z= get_prop("clifford", "z", 0.00)
dt= get_prop("clifford", "dt", 0.01)
def iterate(self, x, y, z):
#global itr
#while itr < 100:
#for i in range (10):
#add for loop find out npoints in while loop
dx = math.sin(self.a*y) + self.c*math.cos(self.a*x)
dy = math.sin(self.b*x) + self.d*math.cos(self.b*y)
dz = math.sin(self.e*x) + self.f*math.cos(self.e*z)
return (dx, dy, dz)
class LorenzAttractor(Object_OT_Attractor):
bl_idname = "curve.lorenz_attractor_add"
bl_label = "Lorenz Attractor"
bl_options = {"REGISTER", "UNDO"}
npoints = get_npoints()
params = ["a", "b", "c"]
a = get_prop("lorenz", "a", 10.00)
b = get_prop("lorenz", "b", 28.00)
c = get_prop("lorenz", "c", (8/3))
x = get_prop("lorenz", "x", 0.10)
y = get_prop("lorenz", "y", 0.00)
z = get_prop("lorenz", "z", 0.00)
dt = get_prop("lorenz", "dt", 0.01)
def iterate(self, x, y, z):
dx = self.a * (y - x)
dy = (x * (self.b - z) - y)
dz = (x * y - self.c * z)
return (dx, dy, dz)
class Menu_MT_AttractorMenu(bpy.types.Menu):
#bl_idname = "INFO_MT_curve_attractor_add"
bl_idname= "menu.attractormenu"
bl_label= "Test Attractors"
def draw(self, context):
layout = self.layout
layout.operator_context = "INVOKE_REGION_WIN"
#layout.label(text="Hello First Menu!", icon='WORLD_DATA')
layout.operator("curve.clifford_attractor_add", text="Clifford")
layout.operator("curve.lorenz_attractor_add",text="Lorenz")
def menu_func(self, context):
#self.layout.menu("INFO_MT_curve_attractor_add", icon="PLUGIN")
self.layout.menu(Menu_MT_AttractorMenu.bl_idname, icon="PLUGIN")
#self.layout.operator(AttractorMenu.bl_idname)
#needed as workaround
bpy.types.VIEW3D_MT_curve_add.append(menu_func)
#All classes used
classes = (
Object_OT_Attractor,
Menu_MT_AttractorMenu,
LorenzAttractor,
CliffordAttractor,
)
#Register and unregister all classes
register, unregister = bpy.utils.register_classes_factory(classes)
# This allows you to run the script directly from Blender's Text editor
# to test the add-on without having to install it.
if __name__ == "__main__":
register()