Art Spotlight: attractor/8 Wang-Sun

Back to overview

こんにちは,

I’m n- also known as A-na5.
I’ve been using Blender for several years. recent works are making 3D models based on mathematical expressions. However, I’ve just started studying Blender – Python API (https://docs.blender.org/api/) since last month. Please let me know if there are any mistakes.

Inspiration and References

A new 3-d four-wing smooth autonomous chaotic systems

Modeling
How To Make “A 3-D Four-Wing Attractor” with Blender in Python

import bpy
import platform
print("Blender " + bpy.app.version_string)
print("Python " + platform.python_version())
# Blender 2.77 (sub 3)
# Python 3.5.1

Setting

Choose Screen Layout > Scripting

Window > Toggle System Console

Create a new text data block

Creating curve with chaotic attractors

import bpy

def wang_sun(a, b, c, d, e, f):
return lambda x, y, z: (
a*x + c*y*z,
b*x + d*y - x*z,
e*z + f*x*y
)

def make_components(func, max_iteration, x=0, y=0, z=0, dt=0.1):
verts = []
edges = []
faces = []
i = 0
while i < max_iteration:
xn, yn, zn = func(x, y, z)
x = x + xn*dt
y = y + yn*dt
z = z + zn*dt
verts.append((x, y, z))
i = i + 1
return verts, edges, faces

def generate_curve(func, max_iteration, curve_name, *start_xyz):
verts, edges, faces = make_components(func, max_iteration, *start_xyz)
w = 1

curvedata = bpy.data.curves.new(name=curve_name, type='CURVE')
curvedata.dimensions = '3D'

obj = bpy.data.objects.new(curve_name, curvedata)
obj.location = (0, 0, 0)
bpy.context.scene.objects.link(obj)

polyline = curvedata.splines.new('POLY')
polyline.points.add(len(verts)-1)
for num in range(len(verts)):
polyline.points[num].co = (verts[num])+(w,)

obj.select = True
obj.show_bounds = True

wang_sun_params = (("a", 0.2), ("b", -0.01), ("c", 1.0), ("d", -0.4), ("e", -1.0), ("f", -1.0))
wang_sun_start = (0.15, 0.1 , 0.2, 0.05)

n = 20000
name = "wang-sun"
param = (v for k,v in wang_sun_params)

attract = wang_sun(*param)
generate_curve(attract, n, name, *wang_sun_start)

Run

Resizing Objects

import bpy

def resize_objects(objects_name, *, axis="z", size=10):
    obj = bpy.data.objects[objects_name]
    bpy.context.scene.update()
    obj.select = True
    bpy.context.scene.objects.active = obj
    bo = bpy.context.object
    dimX = bo.dimensions[0]
    dimY = bo.dimensions[1]
    dimZ = bo.dimensions[2]
    if axis == "x":
        s = size/dimX
    elif axis == "y":
        s = size/dimY
    else:
        s = size/dimZ
    bo.scale[0] = 1*s
    bo.scale[1] = 1*s
    bo.scale[2] = 1*s
    bpy.ops.object.transform_apply(
                                  location=False, 
                                  rotation=False, 
                                  scale=True
                                  )

name = "wang-sun"
s = 10
resize_objects(name, axis="z", size=s)

Run

Curve to mesh

import bpy

def mesh_from_curve(curve_name, *, fill_mode='BACK', bevel_depth=0.05):
        obj = bpy.data.objects[curve_name]
        bpy.ops.object.select_all(action='DESELECT')
        obj.select = True
        bpy.context.scene.objects.active = obj
        
        bo = bpy.context.object

        bo.data.fill_mode = fill_mode
        bo.data.bevel_depth = bevel_depth

        bpy.ops.object.shade_flat()
        bpy.ops.object.convert(target='MESH')
        obj.data.name = curve_name

name = "wang-sun"
mesh_from_curve(name, fill_mode='BACK', bevel_depth=0.05)

Run

Duplicate

import bpy
import bmesh

def duplicate_checker_select(mesh_name, *, nth=3):
    for offset in range(nth):
        bpy.ops.object.select_all(action='DESELECT')
        obj = bpy.data.objects[mesh_name]
        scn = bpy.context.scene
        obj.select = True
        scn.objects.active = obj
        bpy.ops.object.mode_set(mode='EDIT')
        bpy.ops.mesh.select_mode(type="FACE")
        bpy.ops.mesh.select_all(action='DESELECT')
        me = obj.data

        bm = bmesh.from_edit_mesh(me)
        bm.faces.ensure_lookup_table()
        index_offset = 0
        for faces in bm.faces:
            index_offset = faces.index + offset
            if faces.index % nth == 0:
                index_select = index_offset if index_offset < len(me.polygons) else index_offset - len(me.polygons)
                bm.faces[index_select].select = True
                bmesh.update_edit_mesh(me, True)

        bpy.ops.mesh.duplicate()
        bpy.ops.mesh.separate(type='SELECTED')
        bpy.ops.object.mode_set(mode='OBJECT')
        bpy.ops.object.select_all(action='DESELECT')
    obj.select = True
    bpy.context.scene.objects.active = obj
    bpy.ops.object.delete(use_global=False)

name = "wang-sun"
duplicate_checker_select(name, nth=10)

Run

Export to sketchfab.timeframe

import bpy
import os

def export_txt_sketchfab_timeframe(path, sec):
    if os.path.isdir(path) is False:
#        os.makedirs(path)
        print("'path' is missing")
        return 0
    bpy.ops.object.select_all(action='DESELECT')
    bpy.ops.object.select_by_type(type='MESH')
    objList = [obj.name for obj in bpy.context.selected_objects]
    objList = sorted(objList)
    timeframe = open(path + 'sketchfab.timeframe', 'w')
    with timeframe as text:
        for objects in objList:
            text.write(sec + " " + objects + ".ply" + "\n")
    bpy.ops.object.select_all(action='DESELECT')
    print("writing " + path + "sketchfab.timeframe done")

# "~/Desktop/attractor/Wang-Sun/"
# "/Users/your-name/Desktop/attractor/Wang-Sun/"
# "C:/Users/your-name/Desktop/attractor/Wang-Sun/"
sec = "0.1"
path = "/path/to/"
export_txt_sketchfab_timeframe(path, sec)

# print(os.makedirs.__doc__)
"""
Recursive directory creation function. Like mkdir(), 
but makes all intermediate-level directories needed to contain the leaf directory. 

Run

Creating Wire-Frame Models

# bevel = 0.05
bevel = 0.01
n = 20000
name = "wang-sun"
param = (v for k,v in wang_sun_params)
# 2) Run
attract = wang_sun(*param)
generate_curve(attract, n, name, *wang_sun_start)
# 3) Run
resize_objects(name)
# 4) Run
mesh_from_curve(name, bevel_depth=bevel)

Run 

Adding objects to sketchfab.timeframe

import bpy
import os

def append_objects_to_sketchfab_timeframe(path, objects_name):
    if os.path.isdir(path) is False:
#        os.makedirs(path)
        print("'path' is missing")
        return 0

    obj = bpy.data.objects[objects_name]
    bpy.ops.object.select_all(action='DESELECT')
    obj.select = True

    timeframe = open(path + 'sketchfab.timeframe', 'r')
    lines = timeframe.readlines()
    timeframe.close()

    timeframe = open(path + 'sketchfab.timeframe', 'w')
    with timeframe as text:
        for line in lines:
            text.write(line.strip() + "+" + obj.name + ".ply" + "\n")
            print("writing " + line.strip() + "+" + obj.name + ".ply")

# "~/Desktop/attractor/Wang-Sun/"
# "/Users/your-name/Desktop/attractor/Wang-Sun/"
# "C:/Users/your-name/Desktop/attractor/Wang-Sun/"
path = "/path/to/"
name = "wang-sun"

append_objects_to_sketchfab_timeframe(path, name)

Run 

Export to .ply

import bpy
import os

def export_ply_all(path):
    if os.path.isdir(path) is False:
#        os.makedirs(path)
        print("'path' is missing")
        return 0

    bpy.ops.object.select_all(action='DESELECT')
    bpy.ops.object.select_by_type(type='MESH')
    objList = [obj.name for obj in bpy.context.selected_objects]
    objList = sorted(objList)

    for objects in objList:
        obj = bpy.data.objects[objects]
        _objects = objects.replace('.', '_')
        bpy.ops.object.select_all(action='DESELECT')
        obj.select=True
        bpy.context.scene.objects.active = obj
        obj.data.name = _objects
        bpy.context.active_object.name = _objects

        bpy.ops.export_mesh.ply(
            filepath=path+_objects+".ply", 
            check_existing=True, 
            axis_forward='Y', 
            axis_up='Z', 
            filter_glob="*.ply", 
            use_mesh_modifiers=True, 
            use_normals=True, 
            use_uv_coords=True, 
            use_colors=True, 
            global_scale=1.0
            )

# "~/Desktop/attractor/test/"
# "/Users/your-name/Desktop/attractor/test/"
# "C:/Users/your-name/Desktop/attractor/test/"
path = "/path/to/"
export_ply_all(path)

Run

Code

Rendering of the Sketchfab is beautiful than my own rendering image…, that is easy to use for beginner 3D artist. It is the most attractive thing to me. Thank you for give me a place to publish my work.

I’d like to thank the Sketchfab Team & Community!

Find me on:

Sketchfab

Tumblr

Twitter

About the author

A-na5

複雑な


Leave a Reply

Your email address will not be published. Required fields are marked *

Related articles