Untitled

 avatar
unknown
python
4 years ago
4.9 kB
4
Indexable
import bpy

def print(data):
    for window in bpy.context.window_manager.windows:
        screen = window.screen
        for area in screen.areas:
            if area.type == 'CONSOLE':
                override = {'window': window, 'screen': screen, 'area': area}
                bpy.ops.console.scrollback_append(override, text=str(data), type="OUTPUT")  

# load image
bpy.ops.image.open(filepath='/Users/stas/Downloads/concentric_circles.png')
img = bpy.data.images['concentric_circles.png']

width, height = img.size

def set_pix_col(x, y, col):
    global img
    base_i = (width * y + x) * 4
    for i in range(base_i, min(base_i + len(col), len(img.pixels))):
        img.pixels[i] = col[i - base_i]
        
def get_pix_col(x, y):
    global img
    base_i = (width * y + x) * 4
    return img.pixels[base_i:base_i+4]

def is_black(x, y):
    col = get_pix_col(x, y)
    return sum(col[0:3]) == 0 and col[3] == 1 # values are 0-1 instead of 0-255
        
def in_bounds(x, y):
    global width, height
    return 0 <= x < width and 0 <= y < height

# process pixels to remove extra corners 
dirs = [[[1, 0], [1, 1]], [[1, 0], [1, -1]], [[0, 1], [1, 1]], [[0, 1], [-1, 1]], [[-1, 0], [-1, 1]], [[-1, 0], [-1, -1]], [[0, -1], [1, -1]], [[0, -1], [-1, -1]]]

for y in range(height):
    for x in range(width):
        if is_black(x, y):       
            for d in dirs:
                nx1, ny1 = [x + d[0][0], y + d[0][1]]
                nx2, ny2 = [x + d[1][0], y + d[1][1]]
                
                if in_bounds(nx1, ny1) and in_bounds(nx2, ny2):
                    if is_black(nx1, ny1) and is_black(nx2, ny2):
                        set_pix_col(nx1, ny1, [1, 1, 1, 0])
                    

# get rings
checked = []
for y in range(height):
    checked.append([False] * width)
    
dirs = [[1, 0], [0, 1], [-1, 0], [0, -1], [1, 1], [1, -1], [-1, 1], [-1, -1]]

rings = []
    
for y in range(height):
    for x in range(width):
        if is_black(x, y):
            if not checked[y][x]:
                ring = []
                buffer = [[x, y]]
                while len(buffer) > 0:
                    cx, cy = buffer[0]
                    ring.append([cx, cy])
                    for d in dirs:
                        nx, ny = cx + d[0], cy + d[1]
                        if in_bounds(nx, ny):
                            if is_black(nx, ny):
                                if not checked[ny][nx]:
                                    checked[ny][nx] = True
                                    buffer.append([nx, ny])
                                    break
                    buffer.pop(0)
                rings.append(ring)
                
rings.sort(key=len)
rings.reverse()

from math import sqrt, inf

def mag(v1, v2):
    return sqrt((v1[0] - v2[0]) ** 2 + (v1[1] - v2[1]) ** 2)

print("begun calculating connections")
# calculate connections
con = []
for i in range(len(rings) - 1):
    cring, nring = rings[i], rings[i + 1]
    con.append([-1] * len(cring))
    
    # greedy algo for finding nearest point:
    # we can guess that the closest point to ring[i] is min(con[ring[i - 1]], con[ring[i -  1]])
    # since points on circles are consecutive
    # first one needs to be calculated honestly
    closest_i = -1
    min_dst = inf
    for k in range(len(nring)):
        dst = mag(cring[0], nring[k])
        if dst < min_dst:
            min_dst = dst
            closest_i = k
    con[i][0] = closest_i
    
    print("calculated closest")
        
    for j in range(1, len(cring)):
        cdst = mag(nring[con[i][j - 1]], cring[k])
        ndst = mag(nring[(con[i][j - 1] + 1) % len(con[i])], cring[k])
        if ndst < cdst:
            con[i][k] = (con[i][j - 1] + 1) % len(con[i])
        else:
            con[i][k] = con[i][j - 1]

print("calculating verts")

# put vertices in bucket, layered
verts = []
for i in range(len(rings)):
    for part in rings[i]:
        verts.append([part[0], part[1], i])
        
print("calculating ssum")
        
# get size sum to use vertice bucket
ssum = []
s = 0
for ring in rings:
    s += len(ring)
    ssum.append(s)


# if same point is closest, we can keep connecting to that, after that need to do downwards triangle
faces = []
print("calculating faces")
for i in range(len(con)):
    ccon = con[i]
    for j in range(len(con[i])):
        base_off = ssum[i - 1] if i != 0 else 0
        if ccon[j] == ccon[j - 1]:
            faces.append([(j - 1) % len(con[i]) + base_off, ccon[j] + ssum[i], j + base_off])
        else:
            faces.append([j + base_off, ccon[j - 1] + ssum[i], ccon[j]])

print("creating mesh")
mesh = bpy.data.meshes.new("myBeautifulMesh")  # add the new mesh
obj = bpy.data.objects.new(mesh.name, mesh)
col = bpy.data.collections.get("Collection")
col.objects.link(obj)
bpy.context.view_layer.objects.active = obj
mesh.from_pydata(verts, [], faces)
Editor is loading...