import bpy import math import bmesh import random # This makes it easy to keep revising and running the script bpy.ops.object.select_all(action='SELECT') bpy.ops.object.delete(use_global=False) # ccmp = Color Component def random_ccmp(): return random.randrange(0, 1000) / 1000.0 def random_color(): return (random_ccmp(), random_ccmp(), random_ccmp(), 1.0) mat = bpy.data.materials.new(name="VertexColorMaterial") mat.use_nodes = True bsdf = mat.node_tree.nodes.get("Principled BSDF") color_node = mat.node_tree.nodes.new(type='ShaderNodeVertexColor') color_node.layer_name = "Col" mat.node_tree.links.new(color_node.outputs['Color'], bsdf.inputs['Base Color']) def point_average(A, B): return [(A[0] + B[0]) / 2, ((A[1] + B[1]) / 2), ((A[2] + B[2]) / 2)] def line_length(A, B): # Works ONLY for axis-aligned lines with only one component different return sum([math.fabs(i - j) for i,j in zip(A, B)]) def drawline(A, B): M = point_average(A, B) length = line_length(A, B) if math.fabs(A[0] - B[0]) < math.fabs(A[1] - B[1]): new_cylinder = bpy.ops.mesh.primitive_cylinder_add(vertices=4, location=M, scale=(.1, .1, length/2), rotation=(math.radians(90), 0, 0)) else: new_cylinder = bpy.ops.mesh.primitive_cylinder_add(vertices=4, location=M, scale=(.1, .1, length/2), rotation=(0, math.radians(90), 0)) def replace_line(A, B, subdivisions_left=1): I = point_average(A, B) H = point_average(A, I) J = point_average(I, B) direction = 0 if math.fabs(A[0] - B[0]) > math.fabs(A[1] - B[1]): direction = 1 length = line_length(A, H) K = H.copy() K[direction] -= length L = I.copy() L[direction] -= length M = I.copy() M[direction] += length N = J.copy() N[direction] += length if subdivisions_left > 0: replace_line(A, H, subdivisions_left - 1) replace_line(H, K, subdivisions_left - 1) replace_line(K, L, subdivisions_left - 1) replace_line(I, L, subdivisions_left - 1) replace_line(M, I, subdivisions_left - 1) replace_line(M, N, subdivisions_left - 1) replace_line(N, J, subdivisions_left - 1) replace_line(J, B, subdivisions_left - 1) else: drawline(A, H) drawline(H, K) drawline(K, L) drawline(L, I) drawline(I, M) drawline(M, N) drawline(N, J) drawline(J, B) # This runs in the background even when this script has reach the end def timer_function(): if bpy.context.mode != "OBJECT": return None replace_line((-10.0, 0.0, 0.0), (10.0, 0.0, 0.0), 2) bpy.app.timers.register(timer_function)