Sampling points uniformly on meshes in Blender for custom addon.
$$ \vec{s}=u\vec{A}+v\vec{B}+w\vec{C}=u\vec{A}+v\vec{B}+(1-(u+v))\vec{C} $$
$$ \vec{A} + (u \vec{B} + v \vec{C}) $$
Simulating a coin toss:
import random
random = random.random()
probability = 0.5
if probability =< random:
print('HEADS!')
else:
print('TAILS!')
No matter how many times the coin is tossed, it will always result in a ratio that half the total number of times it is heads, the other half it's tails.
def random_point_no_uv(polypicklist):
randompoly = []
polychoice = randomizer.choice(polypicklist)
randompoly.append(polychoice)
A = randompoly[0][0]
B = randompoly[0][1]
C = randompoly[0][2]
s1 = randomizer.uniform(0.0, 1.0)
s2 = randomizer.uniform(0.0, 1.0)
if (s1 + s2) <= 1:
random_point_no_uv.result = ((((B - A) * s1) + ((C - A) * s2)) + A)
elif (s1 + s2) > 1:
random_point_no_uv(polypicklist)
return
def random_point(polypicklist, dim_x, dim_y, image_array):
randompoly = []
polychoice = randomizer.choice(polypicklist)
randompoly.append(polychoice)
A = randompoly[0][0]
B = randompoly[0][1]
C = randompoly[0][2]
A_uv = randompoly[0][3][0]
B_uv = randompoly[0][3][1]
C_uv = randompoly[0][3][2]
s1 = randomizer.uniform(0.0, 1.0)
s2 = randomizer.uniform(0.0, 1.0)
if (s1 + s2) <= 1:
chance = randomizer.uniform(0.0, 1.0)
random_uv_point = ((((B_uv - A_uv) * s1) + ((C_uv - A_uv) * s2)) + A_uv)
pixel_x = math.floor(dim_x * random_uv_point.x)
pixel_y = math.floor(dim_y * random_uv_point.y)
if image_array[pixel_x, pixel_y] > chance:
random_point.result = ((((B - A) * s1) + ((C - A) * s2)) + A)
else:
random_point(polypicklist, dim_x, dim_y, image_array)
elif (s1 + s2) > 1:
random_point(polypicklist, dim_x, dim_y, image_array)
return
Create image to sample pixels from
if (active_canvas != None) and (active_instance != None):
bpy.ops.object.select_all(action='DESELECT')
""" Choose image in UV editor """
try:
if pixelimage != (pixelimage == pixelimage):
# set selected image mask from pulldown menu as active image in UV editor
for area in bpy.context.screen.areas:
if area.type == 'IMAGE_EDITOR':
# loop through the index of names of objects
for imagename in range(len(bpy.data.images)):
if bpy.data.images[imagename].name == scene.Image:
area.spaces.active.image = bpy.data.images[imagename]
pixelimage = bpy.data.images[imagename]
""" get pixels of image """
# get pixels of image
image_width = pixelimage.size[0]
image_height = pixelimage.size[1]
rgba = list(pixelimage.pixels)
# store pixels in a list of lists
parted_rgba = [rgba[i:i+4] for i in range(0, len(rgba), 4)]
image_list = []
for floats in range(len(parted_rgba)):
image_list.append(parted_rgba[floats][0])
img_array = numpy.array(image_list)
reshape_array = img_array.reshape(image_height, image_width)
""" fix this so it's rotated properly """
rot_array = numpy.rot90(reshape_array, 3)
image_array = numpy.fliplr(rot_array)
#flip array as well
""" UV coordinates"""
if uv_layer:
uvlist = []
for v in active_canvas.data.loops:
uvlist.append(uv_layer[v.index].uv)
# use a variable for number of verts in poly later
parted_uvs = [uvlist[i:i+3] for i in range(0, len(uvlist), 3)]
except:
pass
Create cumulative density function.
# get all polygons, their areas, vertices
polylist = [] #get all polys and put them in a list
arealist = [] # list to store area of polys
for poly in active_canvas.data.polygons:
polyarea = poly.area # get area of polygon
arealist.append(polyarea)
vertlist = []
for loop_index in poly.vertices: # iterate through all vertices on every face index
# iterate through all vertices, multiply it to world space
k = (active_canvas.matrix_world * active_canvas.data.vertices[loop_index].co)
# append vertices to list
vertlist.append(k)
polylist.append(vertlist)
try:
for y in range(len(polylist)):
polylist[y].append(parted_uvs[y])
except:
pass
""" cumulative distribution: """
# sort arealist
sortedareas = arealist[:]
sortedareas.sort()
smallest_area = sortedareas[0]
cumul_distribution = []
# step through all area values in arealist and divide with the smallest area
for o in range(len(arealist)):
divide_by_smallest= math.ceil(arealist[o] / smallest_area)
cumul_distribution.append(divide_by_smallest)
polypicklist = []
for u in range(len(cumul_distribution)):
list_step = cumul_distribution[u]
for g in range(list_step):
distrib_polylist = polylist[u]
polypicklist.append(distrib_polylist)
Error that appears if UV set used extends beyond 0-1 UV space bounds.
Distribution error that was resolved by changing the order of evaluation.
# resulting random points on canvas
pointlist = []
if uv_layer != None:
# loop number of times population slider is set to
for t in range(bpy.context.scene.population):
try:
random_point(polypicklist, image_width, image_height, image_array)
pointlist.append(random_point.result)
except:
random_point_no_uv(polypicklist)
pointlist.append(random_point_no_uv.result)
# for every point
for l in pointlist:
if (bpy.context.scene.lodactive == True) and (active_camera != None):
cameradistance = ((l) - active_camera.location)
vecmagnitude((cameradistance.x), (cameradistance.y), (cameradistance.z))
# change to built in vector magnitude
if vecmagnitude.vector_magnitude <= (bpy.context.scene.camdistance):
bpy.ops.object.select_all(action='DESELECT')
#select object from pulldown menu and create instances
active_instance.select = True
bpy.ops.object.duplicate(linked=True)
# randomize scale
randomscale(scene.minscale, scene.maxscale)
# randomize rotation
randomrotation(scene.rotx, scene.roty, scene.rotz)
# pick randoms from list "facepoints" with choice, use that instead of
# random_point.result
# remove used list item after use!
bpy.ops.transform.translate(value=(l - (active_instance.location)))
bpy.ops.object.select_all(action='DESELECT')
elif vecmagnitude.vector_magnitude > (bpy.context.scene.camdistance):
#select object from pulldown menu and create instances
LODlevel_one_instance.select = True
bpy.ops.object.duplicate(linked=True)
# randomize scale
randomscale(scene.minscale, scene.maxscale)
# randomize rotation
randomrotation(scene.rotx, scene.roty, scene.rotz)
# pick randoms from list "facepoints" with choice, use that instead of
# random_point.result
# remove used list item after use!
bpy.ops.transform.translate(value=(l - (LODlevel_one_instance.location)))
bpy.ops.object.select_all(action='DESELECT')
elif (bpy.context.scene.lodactive == False):
bpy.ops.object.select_all(action='DESELECT')
#select object from pulldown menu and create instances
active_instance.select = True
bpy.ops.object.duplicate(linked=True)
# randomize scale
randomscale(scene.minscale, scene.maxscale)
# randomize rotation
randomrotation(scene.rotx, scene.roty, scene.rotz)
# pick randoms from list "facepoints" with choice, use that instead of
# random_point.result
# remove used list item after use!
bpy.ops.transform.translate(value=(l - (active_instance.location)))
bpy.ops.object.select_all(action='DESELECT')
elif (bpy.context.scene.lodactive == True) and (active_camera == None):
raise Exception("No camera selected")
else:
# print error message
raise Exception("No objects selected")
return {'FINISHED'}
Greyscale texture decides probability of sample points across mesh.