Blender – Python – Sieve of Eratosthenes

Python code to create and animate the Erathostenes sieve with Blender. You choose the number of cubes


Sample from the code
20 x 20 x 20 = 8000 cubes

https://github.com/Patochun/Samples/blob/main/Sieve_of_Erathostene.py

import bpy
from bpy_extras.node_shader_utils import PrincipledBSDFWrapper
import numpy as np
import math
import random

# ********************************************************************
# Sieve_of_Erathostene
# Blender version = 2.8
# Author = Doc OuatZat (DOZ) and by the way is creator (Patrick M)
# Web Site = docouatzat.com
# Mail = docouatzat@gmail.com
#
# Licence used = Creative Commons CC BY
# Check licence here : https://creativecommons.org
#
# Generate animation of Sieve of Erathostene
# Master variables :
#   BigCubeEdge = Mean the number of cubes by edge of the big cube.
#                 4 is the default value. Feel free to choose yours.
#                 but keep in mind the consumed ressource and time.
# ********************************************************************

BigCubeEdge = 4
CubesCount = BigCubeEdge ** 3
ArrayOfCube = np.zeros((CubesCount + 2), dtype=int)


# Research about a collection
def find_collection(context, item):
    collections = item.users_collection
    if len(collections) > 0:
        return collections[0]
    return context.scene.collection


# Create collection
def create_collection(collection_name, parent_collection):
    if collection_name in bpy.data.collections:
        return bpy.data.collections[collection_name]
    else:
        new_collection = bpy.data.collections.new(collection_name)
        parent_collection.children.link(new_collection)
        return new_collection


# Create a text mesh
def Add_Text(collect, Name, x, y, z, Texte, mat1, mat2, mat3):
    bpy.ops.object.text_add()
    ot = bpy.context.active_object
    ot.name = 't_' + Name
    ot.data.body = Texte
    ot.data.extrude = 0.1
    ot.location.x = (x * 2.5) - (len(Texte)/4)
    ot.location.y = (y * 2.5) - 0.4
    ot.location.z = (z * 2.5) + 1.0

    o = bpy.context.object
    bpy.ops.object.convert(target='MESH', keep_original=False)

    # assign to material slots - Both cube and text for union later
    o.data.materials.append(mat1)
    o.data.materials.append(mat2)
    o.data.materials.append(mat3)

    collect_to_unlink = find_collection(bpy.context, o)
    collect.objects.link(o)
    collect_to_unlink.objects.unlink(o)

    return ot


# Create a Cube Mesh
def Add_Cube(collect, Name, x, y, z, mat1, mat2, mat3):
    bpy.ops.mesh.primitive_cube_add()
    oc = bpy.context.active_object
    oc.name = 'c_' + Name
    oc.location.x = x * 2.5
    oc.location.y = y * 2.5
    oc.location.z = z * 2.5

    o = bpy.context.object

    # assign to material slots - Both cube and text for union later
    o.data.materials.append(mat1)
    o.data.materials.append(mat2)
    o.data.materials.append(mat3)

    # Little bevel is always a nice idea for a cube
    modifier = oc.modifiers.new(name="Bevel", type='BEVEL')
    bpy.ops.object.modifier_apply(apply_as='DATA', modifier="Bevel")

    collect_to_unlink = find_collection(bpy.context, o)
    collect.objects.link(o)
    collect_to_unlink.objects.unlink(o)

    return oc


# Boolean operation between two objects
# Type =  [INTERSECT, UNION, DIFFERENCE]
def applyBoolean(obj_A, obj_B, Type):
    boo = obj_A.modifiers.new(type='BOOLEAN', name="booh")
    boo.object = obj_B
    boo.operation = Type
    bpy.ops.object.modifier_apply(apply_as='DATA', modifier="booh")
    bpy.ops.object.select_all(action='DESELECT')
    bpy.data.objects[obj_B.name].select_set(True)
    bpy.ops.object.delete()


# ------------------
# Change cube scale
# ------------------
def EditScale(scn, n, sc):
    Obj_Name = 'c_' + str(n)
    obj = bpy.data.objects[Obj_Name]
    a = scn.frame_current
    obj.keyframe_insert('scale')
    scn.frame_current += 10
    obj.scale = sc, sc, sc
    obj.keyframe_insert('scale')
    scn.frame_current = a


# ------------------
# Highlight cube text
# ------------------
def Highlight_Cube(scn, n):
    Obj_Name = 'c_' + str(n)
    obj = bpy.data.objects[Obj_Name]

    a = scn.frame_current
    # Mem state at pos-1 to avoid changing in middle latter
    scn.frame_current -= 1
    mesh = obj.data
    for f in mesh.polygons:  # iterate over faces
        f.keyframe_insert('material_index')

    # Change lighting of text by changing material_index
    scn.frame_current += 1
    for f in mesh.polygons:  # iterate over faces
        if (f.material_index == 2):
            f.material_index = 1
            f.keyframe_insert('material_index')
    scn.frame_current = a

# ------------------
# MAIN
# ------------------

bpy.ops.transform.translate(value=(1, 1, 1))

# Create a new collection
nCol = create_collection("Big_Cube", bpy.context.scene.collection)

# Create materials for cubes
# Cube body
mat_c = bpy.data.materials.new(name="Material_Cube")
mat_c.use_nodes = True
principled = PrincipledBSDFWrapper(mat_c, is_readonly=False)
principled.base_color = (0.8, 0.4, 0.2)

# Highlight Text
mat_ch = bpy.data.materials.new(name="Material_Highlight")
mat_ch.use_nodes = True
principled = PrincipledBSDFWrapper(mat_ch, is_readonly=False)
principled.base_color = (0.4, 1.0, 0.2)

# Normal Text
mat_t = bpy.data.materials.new(name="Material_Highlight")
mat_t.use_nodes = True
principled = PrincipledBSDFWrapper(mat_t, is_readonly=False)
principled.base_color = (1.0, 0.0, 0.0)

# Create the 3D array of cube
k = 0
for z in range(BigCubeEdge, 0, -1):
    for y in range(BigCubeEdge, 0, -1):
        for x in range(0, BigCubeEdge):
            k += 1
            ot = Add_Text(nCol, str(k), x, y, z, str(k), mat_t, mat_c, mat_ch)
            oc = Add_Cube(nCol, str(k), x, y, z, mat_c, mat_ch, mat_t)
            applyBoolean(oc, ot, "UNION")

# Set animation start
scn = bpy.context.scene
scn.frame_current = 1

# Freeze all objects in start state
for num in range(1, CubesCount):
    Obj_Name = 'c_' + str(num)
    obj = bpy.data.objects[Obj_Name]
    obj.scale = 1.0, 1.0, 1.0
    obj.keyframe_insert('scale')

# Leave 4 second before start
    scn.frame_current = 100

# Freeze all objects in start state (again)
for num in range(1, CubesCount):
    Obj_Name = 'c_' + str(num)
    obj = bpy.data.objects[Obj_Name]
    obj.scale = 1.0, 1.0, 1.0
    obj.keyframe_insert('scale')

# Play the Sieve of Erathostene
ArrayOfCube.fill(1)
ArrayOfCube[0] = 0  # Do not use the 0
ArrayOfCube[CubesCount+1] = 0  # Mark the last+1

# Main loop of Sieve
for i in range(1, CubesCount, 1):

    # Do not treat the one
    if (i == 1):
        ArrayOfCube[i] = 0
        EditScale(scn, i, 0.0)
        continue

    # Do not treat if already treated
    if (ArrayOfCube[i] == 0):
        continue

    # Highlight running number
    scn.frame_current += 2
    Highlight_Cube(scn, i)
    EditScale(scn, i, 2.0)

    # Sieve all N since 2xN
    for j in range(i*2, CubesCount+1, i):
        # Do not treat if already treated (also here)
        if (ArrayOfCube[j] == 0):
            continue
        ArrayOfCube[j] = 0
        scn.frame_current += 2
        EditScale(scn, j, 0.0)

    # Downlight running number
    scn.frame_current += 2
    EditScale(scn, i, 1.0)

# Leave 4 seconds after Sieve
scn.frame_end = scn.frame_current + 100

# Return to frame 1
scn.frame_current = 1

# End of script - Enjoy


To learn how using Python code into Blender click here


One thought on “Blender – Python – Sieve of Eratosthenes

Leave a Reply to handjob Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.