xy area coordinate

hi all, it is possible to get y and x coordinate of a plane area?
i mean, if i have a plane for example, i want to add something in random x and y coordinate but only in the plane area, i can do that if i have a primitive plane or rectangular one (and only if they are orienented same as world axis) but if i have an octagon or more complex plane like star one ecc?
i hope i was clear about what i need
thx

Easiest would be to try to place something randomly around(+ some extra vertical distance) the world position of the object.
Then check with rayCast downwards whether it is above the surface of the object and place it on the hit position.

Otherwise use math.

Check out this page’s vertex enumeration example:
https://www.blender.org/api/blender_python_api_2_67_1/bge.types.KX_MeshProxy.html

You can enumerate through the vertices of that object, all the while storing each vertex in a list.

Then, don’t forget to import random and use random.choice(someList) to get a random vertex. Then get the vertex’s position with vertex.XYZ (it will return a list with [x, y, z] coordinates).

i kenw something about that but i need random point inside the area of the plane, something like this:
http://i1286.photobucket.com/albums/a616/giobbolo/areapoint_zpsucvixlwk.png

You can do that with raycast. That is the simplest method.
This is what I did a while back within 1 cider session.
http://i.imgur.com/66UwFVX.gif

Otherwise, you would need to get the faces of the mesh, then from which you get the triangles, then (again) from which you can use math:


https://www.google.ee/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=random%20point%20on%20triangle

I believe Raycasting, as Vegetable mentioned, is the simplest method.

However, there still needs to be a good randomization algorithm - otherwise, raycasting can be inefficient if a really skinny planar object is being used, and the raycasting constantly misses the plane. This is how the vertex/face/edge enumeration with random choice can come in handy - you “get an idea of where points of the plane are”, then offset the vertex positions you collect, and with raycasting, determine if that point is within the plane. If it’s not, even smaller random distances can be offset, repeat.

What about specifying bounds of the object to raycast to? This could work, but again, for a really skinny or irregular planar object, the raycast could constantly miss the plane.

What if you have a fat planar object? This is where specifying bounds can really help.

Raycasting is inefficient.

Think about it:

You calculate a position. Then you measure if it is a valid position. What happens when it is not valid? How many times do you need to calculate to get a valid result? Think about worst case!

Better create valid results in the first place.

There are many different options. You could do it in Python. I guess when you are able to do that you would not ask.

You can do it in logic bricks.

Let me explain a method:

A) create a random number.
The random actuator can do that for you.

This number is just random and it is no position (not a valid position, nor an invalid position - it is a number).

what you now need is a way to

B) map the number to a (valid) position

Again, there are many ways to do that. I’m sure whatever you are thinking right now, is simply too complicated ;).

How about that:
Create an action that forms a shape within the valid area. Lets say it reaches from 1 to 100 frames. It can be any shape as long it never exceeds the given limits of the area. Obviously you can’t cover the whole area. But is it really necessary?

How to map?

Pretty simple: Play the action in property mode. The property is the random number from A).

This way you can guaranty the object is at a valid position. The position is random (as the property is random).

Benefits:

  • You can form any shape you like.
  • You can make certain areas more likely as others (by having condensing the action at that areas).
  • You can use float values to get a huge amount of possible values.

Extra: dense area

I guess you notified the action is still a curve and most of the area will never be possible to get a position. There is a solution:
Multidimensional animation!

You combine two random animations to one.

The rectangle area is a good example, but curved areas would work the same way. One random animation moves a parent object along X. A second animation moves a child object along Y.

Here you calculate two random numbers. One for the parent object, one for the child object. Then you play the actions (parent object the X-animation and child object the Y-animation).

The combined animation covers more valid positions than the single animation does.

For true random sampling, you need to select a polygon (triangle) weighted by area (so larger triangles are more likely to be chosen), and then use barycentric coordinates to randomly sample a centerpoint.

from collections import namedtuplefrom random import random
from math import sqrt
from mathutils.geometry import area_tri




Triangle = namedtuple('Triangle', 'p1 p2 p3 area')








def create_tri(p1, p2, p3):
    return Triangle(p1, p2, p3, area_tri(p1, p2, p3))








def random_point_in_mesh(triangles):
    p1, p2, p3, area = random_tri_from_triangles(triangles)
    
    t = sqrt(random())
    s = random()
    
    a = 1 - t
    b = (1 - s) * t
    c = s * t
        
    return a*p1 + b*p2 + c*p3
    
    
def random_tri_from_triangles(triangles):
    area_sum = 0.0
    random_tri = None
    
    for tri in triangles:
        area = tri.area
        area_sum += area
        
        prob = area / area_sum
        j = random()
        
        if j <= prob:
            random_tri = tri




    return random_tri




def vertex_id_to_pos(poly, i):
    mesh = poly.getMesh()
    vertex = mesh.getVertex(poly.getMaterialIndex(), i)
    return vertex.XYZ


def get_triangles(mesh):
    for p_index in range(mesh.numPolygons):
        poly = mesh.getPolygon(p_index)
        yield create_tri(vertex_id_to_pos(poly, poly.v1), vertex_id_to_pos(poly, poly.v2), 
                       vertex_id_to_pos(poly, poly.v3))
    
        if poly.getNumVertex() == 4:
            yield create_tri(vertex_id_to_pos(poly, poly.v1), vertex_id_to_pos(poly, poly.v3), 
                           vertex_id_to_pos(poly, poly.v4))


def example(cont):
    own = cont.owner
    obj_name = 'Sphere'


    triangles = list(get_triangles(own.meshes[0]))
    for i in range(1000):
        pt = random_point_in_mesh(triangles) # In local space
        world_pt = own.worldTransform * pt


        own.scene.addObject(obj_name, own).worldPosition = world_pt

i was doing something like this ^^

thx all for support

here it is my code, i tried to write one by my own even because your code agoose77 gave me an error after adding the first object (something about he couldn’t find the area)
anyway thx i think without your support i couldn’t write it, what i needed was the area_tri module and that strange thing namedtuple that i used without understand so much ><


import bge
from collections import namedtuple
from mathutils.geometry import area_tri
from mathutils import Vector
from math import sqrt
import random


def area_point():
    
    scene=bge.logic.getCurrentScene()
    cont=bge.logic.getCurrentController()
    own=cont.owner
    
    Triangle = namedtuple('Triangle', 'p1 p2 p3 area')
    sp=scene.objectsInactive["Sphere"]
    verts_position = []
    
    for mesh in own.meshes:
        for poly_index in range(mesh.numPolygons):
            polygon = mesh.getPolygon(poly_index)
            mat_index = polygon.getMaterialIndex()
            for poly_vert_index in range(polygon.getNumVertex()):
                mesh_vert_index = polygon.getVertexIndex(poly_vert_index)
                vertex = mesh.getVertex(mat_index, mesh_vert_index)           
                vertex_position = list(vertex.XYZ)
                verts_position.append(vertex_position)
                
            p1=Vector(verts_position[0])
            p2=Vector(verts_position[1])
            p3=Vector(verts_position[2])
            p4=None


            try:
                p4=Vector(verts_position[3])            
            except:
                pass
            
            if p4:
                v4=verts_position[3]
                ar=area_tri(p1,p3,p4)
                tri=Triangle(p1,p3,p4,ar)
                verts_position=[]
                for m in range(int(ar)*3):
                    t = sqrt(random.uniform(0,1))
                    s = random.uniform(0,1)            
                    a = 1 - t
                    b = (1 - s) * t
                    c = s * t            
                    point= a*p1 + b*p3 + c*p4
                    world_pt = own.worldTransform * point
                    scene.addObject(sp,sp).worldPosition=world_pt
                    
            ar=area_tri(p1,p2,p3)
            tri=Triangle(p1,p2,p3, ar)
            verts_position=[]
            for m in range(int(ar)*3):
                t = sqrt(random.uniform(0,1))
                s = random.uniform(0,1)        
                a = 1 - t
                b = (1 - s) * t
                c = s * t            
                point= a*p1 + b*p2 + c*p3
                world_pt = own.worldTransform * point
                scene.addObject(sp,sp).worldPosition=world_pt

Good job trying it yourself. However, be careful: this won’t give you a statistically random distribution of points across all polygons, and will misbehave with small areas (< 1.0).

I fixed my script. It was an error with reusing a consumable generator.

the random distribution seems to be nice but you are right i have to fix area<1.0 problem
edit: i tried with a circle and you are right, bad random distribution…

amazing .i had write a “traslator” (from jurassic to post industrial :D) class to make easy get from the mesh what you want .(the code original with all these indexes is pretty hard to use)at the bottom there a pseudo example (is not keeped in count the area as instead should)

import mathutilsfrom mathutils import Vectorimport bgedef mean(*values):    sum_value = values[0] * 0    for v in values:        sum_value += v    return sum_value / len(values)class Vertex(Vector):    passclass Edge:    def __init__(self, a,b):        self.vertexes = [Vertex(a),Vertex(b)]        self.position = mean(*self.vertexes)class Triangle:    def __init__(self, a,b,c):        self.edges = [Edge(a,b), Edge(b,c), Edge(c,a)]        self.vertexes = []        for e in self.edges:            self.vertexes.append(e.vertexes[0])                self.position = mean(*[e.position for e in self.edges])#is right?        self.normal = mathutils.geometry.normal(*self.vertexes)        self.area = mathutils.geometry.area_tri(*self.vertexes)class Polygon:    def __init__(self, p):        #print(dir(p))        #print([vn for vn in range(p.getNumVertex())])        verts = [p.getMesh().getVertex(p.getMaterialIndex(),p.getVertexIndex(vn))                  for vn in range(p.getNumVertex())]        positions = [v.XYZ.copy() for v in verts]        a,b,c = positions[:3]        self.triangles = [Triangle(a,b,c),]        if len(verts)==4:            a,b,c,d = positions[:4]            self.triangles.append(Triangle(a,c,d))                    self.edges = []        self.vertexes = []        for t in self.triangles:            self.edges.extend(t.edges)            self.vertexes.extend(t.vertexes)                    self.material_name = p.material_name        self.material_index = p.getMaterialIndex()                    for t in self.triangles:            t.material_name = self.material_name            t.material_index = self.material_index            for e in t.edges:                e.material_name = self.material_name                e.material_index = self.material_index                for v in e.vertexes:                    v.material_name = self.material_name                    v.material_index = self.material_index                            self.normal = mean(*[t.normal for t in self.triangles]).normalized()        self.area = sum([t.area for t in self.triangles])class MeshRead:    """    the instance grab all(?) info about the mesh in the moment that the class    do the instance, (deformations of armatures are not valid)    all reference to real object are missed, you get a copy of that hopefully     (was the goal) easy to read.        the class is structured in part as a tree:        MeshRead            polys                triangles                    edges                        vertexes                            anyway the meshread object has access direct to all its "sub objects".    so you can do both:            for poly in mr.polys:        for t in poly.triangles:            for e in t.edges:                for v in e.vertexes    or:            triangles = mr.triangles    edges = mr.edges        what fit better.        note: all info (meanly vectors) are in "mesh space"    to trasform it in global space use the object owner of the mesh.    ex:    local_position = mr.polygons[2].position    global_position = obj.worldTransform * local_position    no objects contain functions .    each "result" is calculated-baked once at instantiation.    """        def __init__(self, mesh):        self.polygons = []        for p in [mesh.getPolygon(index) for index in range(mesh.numPolygons)]:            self.polygons.append(Polygon(p))        self.triangles = []        self.edges = []        self.vertexes = []        for p in self.polygons:            self.triangles.extend(p.triangles)            self.edges.extend(p.edges)            self.vertexes.extend(p.vertexes)        ##################################################################################################### example on this case.. (is not "wheighted" the area, not clear how make :))import randomdef lerp(a,b,fac):    return a + (b-a) * facdef get_random_point_in_triangle(triangle):    a,b,c = triangle.vertexes    return lerp(lerp(a,b,random.random()),c,random.random())def m(c):    own = c.owner        if not "mr" in own:        mesh = own.meshes[0]        mr = own["mr"] = MeshRead(mesh)        mr = own["mr"]    for t in mr.triangles:        p = get_random_point_in_triangle(t)        p += t.normal*0.2        added = own.scene.addObject("Icosphere", own,50)        added.worldPosition = own.worldTransform * p                    #own.applyRotation((0,0,0.01),1)    #own.applyRotation((0,0.05,0),0)    

amazing .:yes:

i had write a “traslator” (from jurassik to post-industrial :D) class to make easy get from the mesh what you want .(the code original with all these indexes is too hard to use)

at the bottom there a pseudo example (is not keeped in count the area as instead should)


import mathutils
from mathutils import Vector
import bge






def mean(*values):
    sum_value = values[0] * 0
    for v in values:
        sum_value += v
    return sum_value / len(values)






class Vertex(Vector):
    pass






class Edge:
    def __init__(self, a,b):
        self.vertexes = [Vertex(a),Vertex(b)]
        self.position = mean(*self.vertexes)






class Triangle:
    def __init__(self, a,b,c):
        self.edges = [Edge(a,b), Edge(b,c), Edge(c,a)]
        self.vertexes = []
        for e in self.edges:
            self.vertexes.append(e.vertexes[0])
        
        self.position = mean(*[e.position for e in self.edges])#is right?
        self.normal = mathutils.geometry.normal(*self.vertexes)
        self.area = mathutils.geometry.area_tri(*self.vertexes)








class Polygon:
    def __init__(self, p):
        #print(dir(p))
        #print([vn for vn in range(p.getNumVertex())])
        verts = [p.getMesh().getVertex(p.getMaterialIndex(),p.getVertexIndex(vn)) 
                 for vn in range(p.getNumVertex())]
        positions = [v.XYZ.copy() for v in verts]
        a,b,c = positions[:3]
        self.triangles = [Triangle(a,b,c),]
        if len(verts)==4:
            a,b,c,d = positions[:4]
            self.triangles.append(Triangle(a,c,d))
            
        self.edges = []
        self.vertexes = []
        for t in self.triangles:
            self.edges.extend(t.edges)
            self.vertexes.extend(t.vertexes)
            
        self.material_name = p.material_name
        self.material_index = p.getMaterialIndex()
            
        for t in self.triangles:
            t.material_name = self.material_name
            t.material_index = self.material_index
        for e in t.edges:
            e.material_name = self.material_name
            e.material_index = self.material_index
        for v in e.vertexes:
            v.material_name = self.material_name
            v.material_index = self.material_index
                    
        self.normal = mean(*[t.normal for t in self.triangles]).normalized()
        self.area = sum([t.area for t in self.triangles])








class MeshRead:
    """
    the instance grab all(?) info about the mesh in the moment that the class
    do the instance, (deformations of armatures are not valid)
    all reference to real object are missed, you get a copy of that hopefully 
    (was the goal) easy to read.
    
    the class is structured in part as a tree:
        MeshRead
            polys
                triangles
                    edges
                        vertexes
                        
    anyway the meshread object has access direct to all its "sub objects".
    so you can do both:
        
    for poly in mr.polys:
        for t in poly.triangles:
            for e in t.edges:
                for v in e.vertexes
    or:
        
    triangles = mr.triangles
    edges = mr.edges
    
    what fit better.
    
    note: all info (meanly vectors) are in "mesh space"
    to trasform it in global space use the object owner of the mesh.
    ex:
    local_position = mr.polygons[2].position
    global_position = obj.worldTransform * local_position
    no objects contain functions .
    each "result" is calculated-baked once at instantiation.
    """
    
    def __init__(self, mesh):
        self.polygons = []
        for p in [mesh.getPolygon(index) for index in range(mesh.numPolygons)]:
            self.polygons.append(Polygon(p))
        self.triangles = []
        self.edges = []
        self.vertexes = []


        for p in self.polygons:
            self.triangles.extend(p.triangles)
            self.edges.extend(p.edges)
            self.vertexes.extend(p.vertexes)
        








##################################################
##################################################
# example on this case.. (is not "wheighted" the area, not clear how make :))




import random




def lerp(a,b,fac):
    return a + (b-a) * fac


def get_random_point_in_triangle(triangle):
    a,b,c = triangle.vertexes
    return lerp(lerp(a,b,random.random()),c,random.random())








def m(c):
    own = c.owner
    
    if not "mr" in own:
        mesh = own.meshes[0]
        mr = own["mr"] = MeshRead(mesh)
    
    mr = own["mr"]
    for t in mr.triangles:
        p = get_random_point_in_triangle(t)
        p += t.normal*0.2
        added = own.scene.addObject("Icosphere", own,50)
        added.worldPosition = own.worldTransform * p
        
        
    #own.applyRotation((0,0,0.01),1)
    #own.applyRotation((0,0.05,0),0)