|
import bpy |
|
import itertools |
|
import mathutils |
|
|
|
from mathutils import Vector |
|
from sklearn.cluster import KMeans |
|
|
|
CLUSTER_COUNT = 4 |
|
SEED_POINT_COUNT = 1024 * 4 |
|
ITERATION_COUNT = 3 |
|
|
|
def lerp(a, b, t): |
|
return t * (b - a) + a |
|
|
|
|
|
def is_point_in_mesh(point, object): |
|
axes = [Vector((1, 0, 0)), Vector((0, 1, 0)), Vector((0, 0, 1))] |
|
|
|
is_outside = False |
|
|
|
for axis in axes: |
|
count = 0 |
|
orig = point |
|
|
|
while True: |
|
result, location, normal, index = object.ray_cast(orig, orig + axis * 10000.0) |
|
|
|
if index == -1: break |
|
|
|
count += 1 |
|
orig = location + axis * 0.00001 |
|
if count % 2 == 0: |
|
is_outside = True |
|
break |
|
|
|
return not is_outside |
|
|
|
|
|
def get_bounding_box_component(bounding_box, component_index): |
|
return list(map(lambda v: v[component_index], bounding_box)) |
|
|
|
|
|
def unselect_all(): |
|
for obj in bpy.context.selected_objects: |
|
obj.select = False |
|
|
|
|
|
def get_bounding_box_min_max(bounding_box): |
|
x = get_bounding_box_component(bounding_box, 0) |
|
y = get_bounding_box_component(bounding_box, 1) |
|
z = get_bounding_box_component(bounding_box, 2) |
|
|
|
vmin = Vector((min(x), min(y), min(z))) |
|
vmax = Vector((max(x), max(y), max(z))) |
|
return (vmin, vmax) |
|
|
|
|
|
def generate_points(point_count, object): |
|
min, max = get_bounding_box_min_max(object.bound_box) |
|
points = [] |
|
|
|
for x in range(0, point_count): |
|
points.append(Vector(( |
|
lerp(min.x, max.x, mathutils.noise.random()), |
|
lerp(min.y, max.y, mathutils.noise.random()), |
|
lerp(min.z, max.z, mathutils.noise.random()) |
|
))) |
|
|
|
return list(filter(lambda v: is_point_in_mesh(v, object), points)) |
|
|
|
|
|
def get_clusters(points, n_clusters=2): |
|
clusters = list(map(lambda c: [], range(0, n_clusters))) |
|
cluster_indexes = KMeans(n_clusters=n_clusters).fit(points).predict(points) |
|
|
|
for i in range(0, len(points)): |
|
point = points[i] |
|
cluster_index = cluster_indexes[i] |
|
clusters[cluster_index].append(point) |
|
|
|
return clusters |
|
|
|
|
|
def replace_object(object): |
|
objects = [] |
|
points = generate_points(SEED_POINT_COUNT, object) |
|
clusters = get_clusters(points, CLUSTER_COUNT) |
|
|
|
for cluster in clusters: |
|
unselect_all() |
|
|
|
mesh = bpy.data.meshes.new('fractal') |
|
mesh.from_pydata(cluster, [], []) |
|
|
|
fractal = bpy.data.objects.new('fractal', mesh) |
|
fractal.location = bpy.context.scene.cursor_location |
|
fractal.active_material = object.active_material |
|
|
|
bpy.context.scene.objects.link(fractal) |
|
bpy.context.scene.update() |
|
|
|
bpy.context.scene.objects.active = fractal |
|
fractal.select = True |
|
|
|
bpy.ops.object.mode_set(mode='EDIT') |
|
bpy.ops.mesh.convex_hull() |
|
bpy.ops.object.mode_set(mode='OBJECT') |
|
|
|
objects.append(fractal) |
|
|
|
bpy.context.scene.objects.unlink(object) |
|
|
|
return objects |
|
|
|
|
|
def main(): |
|
if len(bpy.context.selected_objects) == 0: |
|
console.error('No object selected') |
|
return |
|
|
|
objects = [bpy.context.selected_objects[0]] |
|
|
|
for i in range(0, ITERATION_COUNT): |
|
newObjects = list(map(lambda o: replace_object(o), objects)) |
|
objects = list(itertools.chain(*newObjects)) |
|
|
|
|
|
main() |