Scatter Multiple Objects Across Surface Of Another Object?

Hi All,

I know you can do this using dupliverts, but I want to scatter a series of font mesh objects across the surface of a sphere and duplivert only works with a single object and is static.

Does anyone know of a script that can scatter an object across the surface of another object?

The main goal here is to be able to get the position and orientation of a face in order to apply that to the object that will occupy that face.

Thanks!

Tightening up thread, no delete available on forum.

I’ve gotten a little closer with this version, but I just don’t know the math to apply to the rotation.

To use this code, create a sphere, a cone and a cube. Put the cube in edit mode and scale it down to 0.1 size. do the same thing for the cone.

As you can see, the rotation is off.


# Custom Scatter
import Blender
from Blender import *
from Blender import Mathutils
from Blender.Mathutils import *
import math
 
def fetchIfObject (passedObjectName):
 # NOTE: Returns the object even if it is unlinked to the scene.
 try:
  tempObj = Blender.Object.Get(passedObjectName)
  return tempObj
 except:
  return None
 
def instanceObject (passedNewName, passedSourceName, passedScene):
 sourceObj = fetchIfObject (passedSourceName)
 if sourceObj != None:
  # See if this new name is in use.
  tempObj = fetchIfObject(passedNewName)
  if tempObj != None:
   # Fetch the mesh name of this object.
   newMeshName =tempObj.getData(name_only=True)
   sourceMeshName =sourceObj.getData(name_only=True)
   if newMeshName == sourceMeshName:
    # This object already exists and is linked to the mesh that is requested.
    # This is ideal, no more work to do, simply return tempObj.
    return tempObj
   else:
    # This object needs it's mesh linked to the source object's mesh.
    passedScene.objects.unlink(tempObj)
    sourceMeshName =sourceObj.getData(name_only=True)
    me = Mesh.Get(sourceMeshName)
    tempObj.link(me)
    passedScene.objects.link(tempObj)
    return tempObj
  else:
   # Looks like the new name is available, 
   # This case is only executed on initial creation, increase on sample rate or file was just loaded and unlinked units were lost.
   print ("Creating new object [" + passedNewName + "].")
   newObj = Object.New('Mesh', passedNewName)
   newObj.properties['isParented'] = 0   # This object is not yet parented.
   sourceMeshName =sourceObj.getData(name_only=True)
   me = Mesh.Get(sourceMeshName)
   newObj.link(me)
   passedScene.objects.link(newObj)
   return newObj
 else:
  # Return nothing, the source object does not exist.
  return None
 
sourceName = "Cube"   # Start off with the cube.
distributeName = "Sphere"
tempObj = fetchIfObject (distributeName)
if tempObj != None:
 me = Mesh.Get('Sphere')
 localScene = Scene.GetCurrent()
 c = 0
 newSizeX = 1.0
 newSizeY = 1.0
 newSizeZ = 1.0
 for f in me.faces:
  no = f.no #f.cent
  #Create a name for the instance object.
  newName = "distributed_" + str(c)
  tempUnit = instanceObject(newName,sourceName,localScene)
  if tempUnit != None:
   #Rotate the unit based upon the angle of the face?
   angle1 = math.atan2(no.x, no.y);
   angle2 = math.atan2(no.y, no.z);
   angle3 = math.atan2(no.z, no.x);
   tempUnit.RotX = math.radians(-90.0) - angle1
   tempUnit.RotY = math.radians(90.0) - angle2
   tempUnit.RotZ = math.radians(90.0) - angle3
   #Lets scale the new unit based upon the area of the face.   
   ar = f.area
   newSizeX = newSizeX * ar
   newSizeY = newSizeY * ar
   newSizeZ = newSizeZ * ar
   #tempUnit.setSize (newSizeX, newSizeY, newSizeZ)
   #Lets position this new object along the normal of the face.
   tempUnit.setLocation (no.x, no.y, no.z)
 
   c = c + 1
   if sourceName == "Cube":
    sourceName = "Cone"
   else:
    sourceName = "Cube"
Window.RedrawAll()

This would be really handy!
I cannot help, I’m not a coder but hope you’ll find a fix.

Still closer with this version, but I don’t know how to invert the rotation? The objects should be facing out.

I found some alignment code here:
http://jmsoler.free.fr/didacticiel/blender/tutor/cpl_b223new.htm#rampofspotscript
But that link is designed to make lamps face inward. In need the reverse.

To use this code, create a sphere, a cone and a cube. Put the cube in edit mode and scale it down to 0.1 size. do the same thing for the cone.


# Scatter objects across the surface of another object.
import Blender
from Blender import *
from Blender import Mathutils
from Blender.Mathutils import *
import math
from math import *
 
def multmat(M,x,y,z): 
 x1 = (x * M[0][0]) + (y * M[1][0]) + (z * M[2][0]) + M[3][0] 
 y1 = (x * M[0][1]) + (y * M[1][1]) + (z * M[2][1]) + M[3][1] 
 z1 = (x * M[0][2]) + (y * M[1][2]) + (z * M[2][2]) + M[3][2] 
 return x1,y1,z1 
 
def unLinkObjects (passedObjectName):
 print "Unlinking objects named like [" + passedObjectName + "]." 
 l = len(passedObjectName)
 localScene = Scene.GetCurrent()
 for ob in localScene.objects:
  candidate = ob.name[0:l]
  if passedObjectName == candidate:
   tempUnit = Blender.Object.Get(ob.name)
   if tempUnit != None:
    localScene.objects.unlink(tempUnit)
    tempUnit.properties['isLinked'] = 0
 
def fetchIfObject (passedObjectName):
 # NOTE: Returns the object even if it is unlinked to the scene.
 try:
  tempObj = Blender.Object.Get(passedObjectName)
  return tempObj
 except:
  return None
 
def instanceObject (passedNewName, passedSourceName, passedScene):
 sourceObj = fetchIfObject (passedSourceName)
 if sourceObj != None:
  # See if this new name is in use.
  tempObj = fetchIfObject(passedNewName)
  if tempObj != None:
   # Fetch the mesh name of this object.
   newMeshName =tempObj.getData(name_only=True)
   sourceMeshName =sourceObj.getData(name_only=True)
   if newMeshName == sourceMeshName:
    # This object already exists and is linked to the mesh that is requested.
    # This is ideal, no more work to do, simply return tempObj.
    return tempObj
   else:
    # This object needs it's mesh linked to the source object's mesh.
    passedScene.objects.unlink(tempObj)
    sourceMeshName =sourceObj.getData(name_only=True)
    me = Mesh.Get(sourceMeshName)
    tempObj.link(me)
    passedScene.objects.link(tempObj)
    return tempObj
  else:
   # Looks like the new name is available, 
   # This case is only executed on initial creation, increase on sample rate or file was just loaded and unlinked units were lost.
   print ("Creating new object [" + passedNewName + "].")
   newObj = Object.New('Mesh', passedNewName)
   newObj.properties['isParented'] = 0   # This object is not yet parented.
   sourceMeshName =sourceObj.getData(name_only=True)
   me = Mesh.Get(sourceMeshName)
   newObj.link(me)
   passedScene.objects.link(newObj)
   return newObj
 else:
  # Return nothing, the source object does not exist.
  return None
 
# Before we begin, let's unlink objects.
unLinkObjects("distributed")
#n=n/0.0
sourceName = "Cube"   # Start off with the cube.
distributeName = "Sphere"
tempObj = fetchIfObject (distributeName)
if tempObj != None:
 me=NMesh.GetRaw(tempObj.data.name) 
 #me = Mesh.Get('Sphere')
 localScene = Scene.GetCurrent()
 c = 0
 newSizeX = 1.0
 newSizeY = 1.0
 newSizeZ = 1.0
 #for f in me.faces:
 for f in me.verts:
  no = f.no #f.cent
 
  #Create a name for the instance object.
  newName = "distributed_" + str(c)
  tempUnit = instanceObject(newName,sourceName,localScene)
  if tempUnit != None:
 
   #Link this object into the scene.
   try:
    localScene.objects.link(tempUnit)
   except:
    pass
 
   orientStyle = 1
   if orientStyle == 1:
    # to put in perspective the source position
    x,y,z=multmat(tempObj.mat,f.co[0],f.co[1],f.co[2]) 
    # position the unit.  
    tempUnit.LocX=x 
    tempUnit.LocY=y 
    tempUnit.LocZ=z 
 
    # to position the normal on the top 
    xa=f.no[0]+f.co[0] 
    da=f.no[1]+f.co[1] 
    wi=f.no[2]+f.co[2] 
 
    # to recompute the position of the normal compared to source space scene.
    al,be,ga=multmat(tempObj.mat,xa,da,wi) 
 
    # to return the normal towards source origin 
    f.no[0]=al-x 
    f.no[1]=be-y 
    f.no[2]=ga-z 
 
    # calculate the length of the vector  
    norm=sqrt(f.no[0]**2+f.no[1]**2+f.no[2]**2) 
 
    # standardize the normal.
    f.no[0]=f.no[0]/norm 
    f.no[1]=f.no[1]/norm 
    f.no[2]=f.no[2]/norm 
 
    # print f.no[0], f.no[1], f.no[2] 
 
    if abs(f.no[1])<0.0001: 
     f.no[1]=0.0 
 
    tempUnit.RotX=0 
    teta=-acos(f.no[2]) 
    tempUnit.RotY=-pi+teta 
 
    if f.no[1]!=0: 
     tempUnit.RotZ=-acos(f.no[0]/sin(teta))*f.no[1]/abs(f.no[1]) 
    else: 
     tempUnit.RotZ=0 
     if f.no[0]>0: 
      tempUnit.RotY=-tempUnit.RotY
 
    #tempUnit.RotY = math.radians(180-math.degrees(tempUnit.RotY))
    #tempUnit.RotZ = math.radians(180-math.degrees(tempUnit.RotZ))
    #print tempUnit.RotX, tempUnit.RotY, tempUnit.RotZ
 
   c = c + 1
   if sourceName == "Cube":
    sourceName = "Cone"
   else:
    sourceName = "Cube"
Window.RedrawAll()

Ok, I figured out the inversion problem. Duh, just invert the normal before processing.

To use this code, create a sphere, a cone and a cube. Put the cube in edit mode and scale it down to 0.1 size. do the same thing for the cone.

Here is the final code: (for now)


# Scatter objects across the surface of another object.
import Blender
from Blender import *
from Blender import Mathutils
from Blender.Mathutils import *
import math
from math import *
 
def multmat(M,x,y,z): 
 x1 = (x * M[0][0]) + (y * M[1][0]) + (z * M[2][0]) + M[3][0] 
 y1 = (x * M[0][1]) + (y * M[1][1]) + (z * M[2][1]) + M[3][1] 
 z1 = (x * M[0][2]) + (y * M[1][2]) + (z * M[2][2]) + M[3][2] 
 return x1,y1,z1 
 
def unLinkObjects (passedObjectName):
 print "Unlinking objects named like [" + passedObjectName + "]." 
 l = len(passedObjectName)
 localScene = Scene.GetCurrent()
 for ob in localScene.objects:
  candidate = ob.name[0:l]
  if passedObjectName == candidate:
   tempUnit = Blender.Object.Get(ob.name)
   if tempUnit != None:
    localScene.objects.unlink(tempUnit)
    tempUnit.properties['isLinked'] = 0
 
def fetchIfObject (passedObjectName):
 # NOTE: Returns the object even if it is unlinked to the scene.
 try:
  tempObj = Blender.Object.Get(passedObjectName)
  return tempObj
 except:
  return None
 
def instanceObject (passedNewName, passedSourceName, passedScene):
 sourceObj = fetchIfObject (passedSourceName)
 if sourceObj != None:
  # See if this new name is in use.
  tempObj = fetchIfObject(passedNewName)
  if tempObj != None:
   # Fetch the mesh name of this object.
   newMeshName =tempObj.getData(name_only=True)
   sourceMeshName =sourceObj.getData(name_only=True)
   if newMeshName == sourceMeshName:
    # This object already exists and is linked to the mesh that is requested.
    # This is ideal, no more work to do, simply return tempObj.
    return tempObj
   else:
    # This object needs it's mesh linked to the source object's mesh.
    passedScene.objects.unlink(tempObj)
    sourceMeshName =sourceObj.getData(name_only=True)
    me = Mesh.Get(sourceMeshName)
    tempObj.link(me)
    passedScene.objects.link(tempObj)
    return tempObj
  else:
   # Looks like the new name is available, 
   # This case is only executed on initial creation, increase on sample rate or file was just loaded and unlinked units were lost.
   print ("Creating new object [" + passedNewName + "].")
   newObj = Object.New('Mesh', passedNewName)
   newObj.properties['isParented'] = 0   # This object is not yet parented.
   sourceMeshName =sourceObj.getData(name_only=True)
   me = Mesh.Get(sourceMeshName)
   newObj.link(me)
   passedScene.objects.link(newObj)
   return newObj
 else:
  # Return nothing, the source object does not exist.
  return None
 
# Before we begin, let's unlink any like named objects.
unLinkObjects("distributed")
#n=n/0.0
sourceName = "Cube"   # Start off with the cube.
distributeName = "Sphere"
tempObj = fetchIfObject (distributeName)
if tempObj != None:
 me=NMesh.GetRaw(tempObj.data.name) 
 #me = Mesh.Get('Sphere')
 localScene = Scene.GetCurrent()
 c = 0
 newSizeX = 1.0
 newSizeY = 1.0
 newSizeZ = 1.0
 #for f in me.faces:
 for f in me.verts:
  #Invert or uninvert to flip the orientation.
  no = -f.no #f.cent
  #Create a name for the instance object.
  newName = "distributed_" + str(c)
  tempUnit = instanceObject(newName,sourceName,localScene)
  if tempUnit != None:
   #Link this object into the scene.
   try:
    localScene.objects.link(tempUnit)
   except:
    pass
 
   orientStyle = 1
   if orientStyle ==1:
    # to put in perspective the source position
    x,y,z=multmat(tempObj.mat,f.co[0],f.co[1],f.co[2]) 
    # position the unit.  
    tempUnit.LocX=x 
    tempUnit.LocY=y 
    tempUnit.LocZ=z 
 
    # to position the normal on the top 
    xa=no[0]+f.co[0] 
    da=no[1]+f.co[1] 
    wi=no[2]+f.co[2] 
 
    # to recompute the position of the normal compared to source space scene.
    al,be,ga=multmat(tempObj.mat,xa,da,wi) 
 
    # to return the normal towards source origin 
    no[0]=al-x 
    no[1]=be-y 
    no[2]=ga-z 
 
    # calculate the length of the vector  
    norm=sqrt(no[0]**2+no[1]**2+no[2]**2) 
 
    # standardize the normal.
    no[0]=no[0]/norm 
    no[1]=no[1]/norm 
    no[2]=no[2]/norm 
 
    # print no[0], no[1], no[2] 
 
    if abs(no[1])<0.0001: 
     no[1]=0.0 
 
    tempUnit.RotX=0 
    teta=-acos(no[2]) 
    tempUnit.RotY=-pi+teta 
 
    if no[1]!=0: 
     tempUnit.RotZ=-acos(no[0]/sin(teta))*no[1]/abs(no[1]) 
    else: 
     tempUnit.RotZ=0 
     if no[0]>0: 
      tempUnit.RotY=-tempUnit.RotY
 
   c = c + 1
   if sourceName == "Cube":
    sourceName = "Cone"
   else:
    sourceName = "Cube"
Window.RedrawAll()

Good one.
there’s a similar feature in the Geodesic Domes script.
Struts:selected object along edges.
Hubs: selected objects at vertices.
The Geodesic Domes script lacks your Rotation function.
You are more than welcome to add this function to Geodesic if you like.
Or use any functions from it you may need for your script.
thanks.

Hey this is nice ! I was looking for something similar too!
Is there a way to change the number of refeence elements? Like only the cube over the sphere, or a cube, a cone and a cylinder?

well just to say you can use particles with duplis for that. just use dupligroup instead of dulivert and use the random choice option. make a group of objects you want to scatter, link it an thee you go…

Meta Androcto: Thanks for the link to the geodesic dome, the re-sizable GUI is a nice feature. I’m supprised your script is not integrated into the Blender distribution itself?

Cloud_GL: The distribution method is crude, at this point. This is mainly a test for proof of concept, but if you want to add more mesh items you would customize the script here.


   if sourceName == "Cube":
    sourceName = "Cone"
   else:
    sourceName = "Cube"

Simply come up with a method to cycle through various object names (this will only work with mesh objects) and you can put as many as you like.

pildanovak: For me, particles were out from the begining. You can’t use particles to distribute multiple mesh objects across another object over time in a sequential fashion. I plan on integrating this funtionality into the Blendgraph script I have been working on.

You can find the Blendrgaph script here:
http://blenderartists.org/forum/showthread.php?t=141637

I second the Cloud_GL request for further developments.
Also, what about a randomize and a gradient map distributing function?
I know, it would be pretty like MoGraph Cloner, anyhow it would be nice :wink:

@pildanovak: I tried your method of using a particle system with dupligroups and found it incredibly flawed. It works ok on a simple plane, but change the plane to a Suzanne object and flaws are revealed. Once again the particle system seems to be missing the orientation code neccessary to align particles along the trajectory they are traveling. In this case, it should be the orientation of the face I was hoping for an instant WOW, but all I got was an oh it doesn’t really work.

Hi blenderartists, just registered here to post a script I wrote quite some time ago, when I started fiddling around with blender. I used to fill an objects surface with spheres, and it is probably not the best approach ever done, it worked for me.

This works currently only with spheres, but maybe someone will find it usefull anyways and it might be not too hard to modify the script to create any kind of object.


#!BPY

"""
Name: 'Object Filler'
Blender: 236
Group: 'Object'
Tooltips: 'fill an objects surface with other objects'
"""

# to use this script:
# 1. create a Material named "Particle" in your scene
# 2. select the object which you want to fill with spheres
# 3. open this script in the blender text editor
# 4. optional: tweak the globals which control the particle to your liking
# 5. execute the script
#
# be carefull with very complex objects or very small particle sizes, the script may
# take a very long time to finish! you have been warned
#
# author: [email protected]

import Blender;
from Blender import Mathutils, NMesh, Object, Scene, Window, Material;
from Blender.Mathutils import MidpointVecs;

import math;
from math import *;

import random;

# Define some things, like exceptions we need later.
selection_except = 'selection_except'
type_except = 'type_except'
cantfill_except = 'cantfill_except'

# these globals can be used to control the size and the variation of the spheres that will be created as particles
particle_radius = .3;
particle_radius_diff = 0;
particle_resolution = 12;

# one big dict for every vertex we have already touched (iirc)
# i didn't rc, it is one big dict for every _position_ ever touched, so this grows by one every time
# a particle gets created and certainly contributes alot to the horrible performance of this script...
processed = {};

#
# these tricky_func thingies are little helpers to find the correct neighbouring vertices for a given vertex
# on a quad or a triangle
#

def tricky_func_quad(x):
    if x == 0:
        return (1,2,3);
    elif x == 1:
        return (2,3,0);
    elif x == 2:
        return (3,0,1);
    elif x == 3:
        return (0,1,2);
    else:
        raise ValueError;

def tricky_func_triangle(x):
    if x == 0:
        return (1,2);
    elif x == 1:
        return (2,0);
    elif x == 2:
        return (0,1);
    else:
        raise ValueError;

def dot_product(v,w):
    dp = 0;
    if len(v) != len(w):
        return false;

    for i in range(0,len(v)):
        dp=dp+round(v[i],4)*round(w[i],4);

    return dp;

def edge_length(v,w):
    if len(v) != len(w) or v == w:
        return 0;

    length_v = sqrt(dot_product(v,v));
    length_w = sqrt(dot_product(w,w));

    if length_v == 0 or length_w == 0:
        return 0;

    cosinus_vw = dot_product(v,w)/(length_v*length_w);

    length_squared = pow(length_v,2) + pow(length_w,2) - 2*length_v*length_w*cosinus_vw;

    if length_squared < 0:
            return 0;
    else:
        return sqrt(length_squared);

#
# function to create an UVsphere, stolen from jmsoler.free.fr
#
# takes radius, resolution, and a translation vector
#

def create_sphere(radius,res,tx,ty,tz):
    sphere=NMesh.New();

    maxpoints=res*res;
    n=math.sqrt(maxpoints);
    n=int(n);

    for i in range(0, n):
        for j in range(0, n):
            x = (sin(j*pi*2/(n-1))*cos(-pi/2+i*pi/(n-1))*radius)+tx;
            y = (cos(j*pi*2/(n-1))*cos(-pi/2+i*pi/(n-1))*radius)+ty;
            z = (sin(-pi/2+i*pi/(n-1))*radius)+tz;
            v=NMesh.Vert( x , y , z );
            sphere.verts.append(v);

    n0=len(range(0,n));

    for i in range(0,n-1):
        for j in range(0,n-1):
            f=NMesh.Face();
            f.v.append(sphere.verts[i*n0+j]);
            f.v.append(sphere.verts[i*n0+j+1]);
            f.v.append(sphere.verts[(i+1)*n0+j+1]);
            f.v.append(sphere.verts[(i+1)*n0+j]);
            sphere.faces.append(f);

    blubb = Object.New("Mesh","blubb");
    blubb.setMatrix(current_matrix);
    blubb.link(sphere);
    sphere.update(1);
    scene.link(blubb);

    blubb2 = Object.Get(blubb.getName());
    blubb2.setMaterials(current_materials);
    blubb2.colbits = 1;

    Window.Redraw();

def create_pseudo_face(face,i,midpoint):
    pseudo_face = NMesh.Face();

    if len(face.v) == 4:
        foo = MidpointVecs(face.v[i].co,face.v[tricky_func_quad(i)[0]].co);
        pseudo_face.append(NMesh.Vert(foo[0],foo[1],foo[2]));

        foo = midpoint;
        pseudo_face.append(NMesh.Vert(foo[0],foo[1],foo[2]));

        foo = MidpointVecs(face.v[tricky_func_quad(i)[2]].co,face.v[i].co)
        pseudo_face.append(NMesh.Vert(foo[0],foo[1],foo[2]));

        pseudo_face.append(face.v[i]);
    else:
        foo = face.v[i].co;
        pseudo_face.append(NMesh.Vert(foo[0],foo[1],foo[2]));

        foo = face.v[tricky_func_triangle(i)[0]].co;
        pseudo_face.append(NMesh.Vert(foo[0],foo[1],foo[2]));

        foo = midpoint;
        pseudo_face.append(NMesh.Vert(foo[0],foo[1],foo[2]));

        pseudo_face.append(face.v[i]);

    return pseudo_face;

def process_vertex(v):
    vertex = (v.x,v.y,v.z);
    found_long_edge = 0;
    if not processed.has_key(vertex):
        for i in processed.keys():
            if edge_length(vertex, processed[i]) < particle_radius:
                found_long_edge = 1;

        if found_long_edge == 0:
            processed[vertex] = vertex;
            diff = random.uniform(-1*particle_radius_diff,particle_radius_diff);
            create_sphere(particle_radius+diff,particle_resolution,vertex[0],vertex[1],vertex[2]);

def process_edge(edge):
        midpoint = Mathutils.MidpointVecs(edge[0],edge[1]);
        process_vertex(midpoint);

def process_face(face):
    dont_place_midpoint = 0;
    face3 = 0;

    if len(face.v) == 4:
        edges = [[face.v[0].co, face.v[1].co], [face.v[1].co, face.v[2].co], [face.v[2].co, face.v[3].co], [face.v[3].co, face.v[0].co]];
        edges_length = [edge_length(face.v[0], face.v[1]), edge_length(face.v[1], face.v[2]), edge_length(face.v[2], face.v[3]), edge_length(face.v[3], face.v[0])];

        midpoint = MidpointVecs(MidpointVecs(face.v[0].co, face.v[1].co),MidpointVecs(face.v[2].co, face.v[3].co));
    else:
        face3 = 1;

        edges = [[face.v[0].co, face.v[1].co], [face.v[1].co, face.v[2].co], [face.v[2].co, face.v[0].co]];
        edges_length = [edge_length(face.v[0], face.v[1]), edge_length(face.v[1], face.v[2]), edge_length(face.v[2], face.v[0])];

        if edges_length[0] > edges_length[1] and edges_length[2]:
            midpoint = MidpointVecs(MidpointVecs(edges[0][0], edges[0][1]), edges[2][0]);
        elif edges_length[1] > edges_length[0] and edges_length[2]:
            midpoint = MidpointVecs(MidpointVecs(edges[1][0], edges[1][1]), edges[0][0]);
        else:
            midpoint = MidpointVecs(MidpointVecs(edges[2][0], edges[2][1]), edges[1][0]);

    if edges_length[0] >= particle_radius:
        process_vertex(edges[0][0]);
        if edges_length[0] > 2*particle_radius:
            process_edge(edges[0]);

    if edges_length[1] >= particle_radius:
        process_vertex(edges[1][0]);
        if edges_length[1] > 2*particle_radius:
            process_edge(edges[1]);

    if edges_length[2] >= particle_radius:
        process_vertex(edges[2][0]);
        if edges_length[2] > 2*particle_radius:
            process_edge(edges[2]);

    if face3 == 0:
        if edges_length[3] >= particle_radius:
            process_vertex(edges[3][0]);
            if edges_length[3] > 2*particle_radius:
                process_edge(edges[3]);

    for i in range(0,len(face.v)):
        if edges_length[i] >= 3*particle_radius:
            process_face(create_pseudo_face(face,i,midpoint));
            dont_place_midpoint = 1;

    if dont_place_midpoint == 0:
        process_vertex(midpoint);
        dont_place_midpoint = 1;

def transform_vertex(v,matrix):
    new_v = [0, 0, 0, 0];
    for i in range(0,2):
        for j in range(0,3):
            new_v[i] = new_v[i]+v[i]*matrix[j][i];
            print new_v;
    return new_v;

def transform_face(face,matrix):
    new_face = NMesh.Face();

    for i in range(0,len(face.v)):
        foo = transform_vertex(face.v[i].co, matrix);
        new_face.append(NMesh.Vert(foo[0],foo[1],foo[2]));
    return new_face;

#
# Get the Scene + Selected Objects, if more then
# one selected -> throw exception(for now)
#

scene = Scene.getCurrent();

objects = Object.GetSelected();

if len(objects) != 1:
    print "You must select at least 1 Object, nor should you select more!";
    raise selection_except;

if objects[0].getType() != "Mesh":
        print "ALARM! Object isn't a Mesh! It is : " +  objects[0].getType();
        raise type_except;

current_object = objects[0];
current_matrix = current_object.getMatrix();    
current_data = current_object.getData();
current_faces = current_data.faces;

current_materials = [];
current_materials.append(Material.Get("Particle"));

print "STARTING";

for i in range(0,len(current_faces)):
    process_face(current_faces[i]);

print "FINISHED";

The good thing about this script and why I wrote it in the first place is that it manages to fill any arbitrary object evenly with spheres, because it does not only rely on the vertices.

The bad things are that it can be really slow when used with more complex objects, and that as a whole, it feels a little bit like an ugly hack.

Anyways, hope it might be of some kind of use.

PS: Sorry for not documenting the ‘algorithm’. I am being a bit lazy. But if anyone would like some more information, i’ll explain.

At the bottom of same page, there is the script that makes what you want to do.
http://jmsoler.free.fr/didacticiel/blender/tutor/cpl_b223new.htm#duplicopy232

Yeah,

That one only does a single object, which can be done with dupligroups or particles anyway. I was looking for a way to do a series of objects across a mesh.

Right now, I am looking for a way to extend the code to be more usefull. I don’t know a lot about math, but I do know logic. What I would like to be able to do is to offset the scattered objects from the mesh surface along the face normal trajectory by a specific value. Basically cause the scattered object to lift off the surface, over time, then drop back down.

My script uses a list of objects. The firts one is the target and all the others are displayed on its surface.

is it possible to do it wiht one object
Ex: a quares or cube uniformely distriubted around a sphere
you know like a dicso ball with small rectangular mirrors!

Very intersting

Salutations