J'ai un polygone convexe de 4 latéral défini par 4 points en 2D et je souhaite pouvoir générer des points aléatoires à l'intérieur.
Si cela simplifie vraiment le problème, je peux limiter le polygone à un parallélogramme, mais une réponse plus générale est préférée.
Générer des points aléatoires jusqu'à ce que l'on entre à l'intérieur du polygone ne fonctionne pas car il est vraiment imprévisible le temps qu'il faut.
R. Si vous pouvez restreindre votre contribution au parallélogramme, c'est vraiment simple:
u
et v
.Si votre parallélogramme est défini par les points ABCD tels que AB, BC, CD et DA sont les côtés, puis prenez votre point comme étant:
p = A + (u * AB) + (v * AD)
Où AB
est le vecteur de A à B et AD
le vecteur de A à D.
B. Maintenant, si vous ne pouvez pas, vous pouvez toujours utiliser les coordonnées barycentriques. Les coordonnées barycentiriques correspondent, pour une quad à 4 coordonnées (a,b,c,d)
tel que a+b+c+d=1
. Ensuite, tout point P
à l'intérieur du quad peut être décrit par une 4 étaine telle que:
P = a A + b B + c C + d D
Dans votre cas, vous pouvez dessiner 4 nombres aléatoires et les normaliser afin d'ajouter jusqu'à 1. Cela vous donnera un point. Notez que la distribution des points ne sera pas uniforme dans ce cas.
C. Vous pouvez également, comme proposé ailleurs, décomposer le quad à deux triangles et utiliser la méthode de demi-parallélogramme (c'est-à-dire comme parallélogramme, mais vous ajoutez la condition u+v=1
) ou les coordonnées barycentiriques pour les triangles. Toutefois, si vous souhaitez une distribution uniforme, la probabilité d'avoir un point dans l'un des triangle doit être égale à la zone du triangle divisée par la zone du quad.
La question de l'OP est un peu ambiguë donc la question que je répondrai est la suivante: Comment générer un point d'une distribution uniforme dans un quadrilatère arbitraire , qui est en fait une généralisation de Comment générer un point d'une distribution uniforme dans un polygone arbitraire (convexe) . La réponse est basée sur le cas de générer un échantillon d'une distribution uniforme dans un triangle (voir http://mathworld.wolfram.com/triangglepointpicking.html , qui a une très belle explication).
Pour accomplir cela, nous:
Trianguler le polygone (c'est-à-dire générer une collection de régions triangulaires non chevauchantes qui couvrent le polygone). Pour le cas d'un quadrilatère, créez un bord sur deux sommets non adjacents. Pour d'autres polygones, voir http://fr.wikipedia.org/wiki/polygon_trangulant pour un point de départ ou http://www.cgal.org/ Si vous juste besoin d'une bibliothèque.
Pour choisir l'un des triangles au hasard, laissez-nous attribuer un index à chaque triangle (c'est-à-dire 0,1,2, ...). Pour le quadrilatère, ils seront 0,1. Pour chaque triangle, nous affectons un poids égal comme suit:
Générez ensuite un index aléatoire I de la distribution finie sur les index compte tenu de leurs poids. Pour le quadrilatère, il s'agit d'une distribution de Bernoulli:
Soit V0, V1, V2 Soyez des sommets du triangle (représenté par leurs emplacements de points, de sorte que v0 = (x0, y0), etc., nous génèverons deux nombres aléatoires A0 et A1, tous deux dessinés de manière uniforme de l'intervalle [0,1 ]. Ensuite, nous calculons le point aléatoire X par X = A0 (V1-V0) + A1 (V2-V0).
Notez qu'avec la probabilité de 0,5, x se situe à l'extérieur de l'extérieur du triangle, cependant, si elle le fait, il se situe à l'intérieur du parallélogramme composé de l'union du triangle avec son image après une rotation de PI autour du point central de (v1, v2) (lignes pointillées dans l'image). Dans ce cas, nous pouvons générer un nouveau point X '= V0 + R (PI) (X-V3), où R(pi) est une rotation par PI (180 degrés). Le point X 'sera à l'intérieur du triangle.
Notez plus loin que, si le quadrilatère était déjà un parallélogramme, nous n'avons pas besoin de choisir un triangle au hasard, nous pouvons choisir l'un ou l'autre de manière déterministe, puis choisissez le point X sans tester qu'il est à l'intérieur du triangle source.
En supposant que vous voulez une distribution uniforme: formez deux triangles de votre polygone. Choisissez quel triangle générer le point dans leur rapport de zone.
Appelez les coins du triangle A, B, C, des vecteurs latéraux AB, BC, AC et génèrent deux nombres aléatoires dans [0,1] appelé u et v. Laisser p = u * ab + v * ac.
Si A + P est à l'intérieur du triangle, retournez A + P
Si A + P est en dehors du triangle, retournez A + AB + AC - P
(Il s'agit essentiellement de la formule de Pierrebdr à l'exception du prétraitement et de la dernière étape qui replique le point dans un triangle, il peut donc gérer d'autres formes que les parallélogrammes).
Votre polygone est deux triangles, alors pourquoi ne pas en choisir de manière aléatoire, puis trouver un point aléatoire dans le triangle.
Probablement pas la meilleure solution, mais cela fonctionnerait.
Par "Général", vous voulez dire tous les polygones de 4-parallélogrammes en général ou tous les polygones possibles?
Que diriez-vous de dessiner une ligne aléatoire reliant les 4 côtés par ex. Si vous avez ceci:
.BBBB.
A C
A C
.DDDD.
Générez ensuite un point aléatoire sur un carré de l'unité, puis marquez le point sur la ligne B et D au pourcentage de distance sur l'axe X. Faites la même chose sur la ligne A et C à l'aide de la valeur de l'axe Y.
Connectez ensuite le point sur la ligne A à la ligne C et la ligne B de la ligne D, le point d'intersection est ensuite utilisé comme point aléatoire.
Ce n'est pas uniforme car les erreurs d'arrondies aideront certains points, mais il devrait être proche si vous travaillez avec des valeurs de points flottantes.
La mise en œuvre devrait également être assez facile, car vous travaillez déjà avec des polygones. Vous devriez déjà avoir du code qui fait ces tâches simples.
Voici un pseudocode rapide:
void GetRandomPoint(Polygon p, ref float x, ref float y) {
float xrand = random();
float yrand = random();
float h0 = p.Vertices[0] + xrand * p.Vertices[1];
float h1 = p.Vertices[2] + yrand * p.Vertices[3];
float v0 = p.Vertices[0] + xrand * p.Vertices[2];
float v1 = p.Vertices[1] + yrand * p.Vertices[3];
GetLineIntersection(h0, h1, v0, v1, x, y);
}
Cela fonctionne pour les quadrilatères généraux, convexes:
Vous pouvez emprunter des concepts à partir de la méthode des éléments finis, en particulier pour les éléments quadrilatères (4 faces) ( ((, reportez-vous à la section 16.5 ici =). Fondamentalement, il existe un paramétrage bilinéaire qui mappe un carré dans un espace UV (pour U, V\dans [-1, 1] dans ce cas) à votre quadrilatère composé de points P_I (pour i = 1,2,3,4 ). Notez que dans la référence fournie, les paramètres sont appelés\eta et\XI.
Recette de base:
Le seul problème est que des points uniformément distribués dans l'espace U-V ne produiront pas de points de manière uniformément distribué dans votre quad (au sens euclidien). Si cela est important, vous pouvez travailler directement en 2D dans la zone de sélection du quad et écrire un point in-quad (peut-être en divisant le problème en deux points de TRIS) sur des points aléatoires de Cull qui sont à l'extérieur.
Une approche quelque peu moins " naïfe " serait d'utiliser un algorithme de remplissage de polygone , puis sélectionner des points à partir des lignes de remplissage au hasard.
// public-domain code by Darel Rex Finley, 2007
int nodes, nodeX[MAX_POLY_CORNERS], pixelX, pixelY, i, j, swap ;
// Loop through the rows of the image.
for (pixelY=IMAGE_TOP; pixelY<IMAGE_BOT; pixelY++) {
// Build a list of nodes.
nodes=0; j=polyCorners-1;
for (i=0; i<polyCorners; i++) {
if (polyY[i]<(double) pixelY && polyY[j]>=(double) pixelY
|| polyY[j]<(double) pixelY && polyY[i]>=(double) pixelY) {
nodeX[nodes++]=(int) (polyX[i]+(pixelY-polyY[i])/(polyY[j]-polyY[i])
*(polyX[j]-polyX[i])); }
j=i; }
// Sort the nodes, via a simple “Bubble” sort.
i=0;
while (i<nodes-1) {
if (nodeX[i]>nodeX[i+1]) {
swap=nodeX[i]; nodeX[i]=nodeX[i+1]; nodeX[i+1]=swap; if (i) i--; }
else {
i++; }}
// Fill the pixels between node pairs.
// Code modified by SoloBold 27 Oct 2008
// The flagPixel method below will flag a pixel as a possible choice.
for (i=0; i<nodes; i+=2) {
if (nodeX[i ]>=IMAGE_RIGHT) break;
if (nodeX[i+1]> IMAGE_LEFT ) {
if (nodeX[i ]< IMAGE_LEFT ) nodeX[i ]=IMAGE_LEFT ;
if (nodeX[i+1]> IMAGE_RIGHT) nodeX[i+1]=IMAGE_RIGHT;
for (j=nodeX[i]; j<nodeX[i+1]; j++) flagPixel(j,pixelY); }}}
// TODO pick a flagged pixel randomly and fill it, then remove it from the list.
// Repeat until no flagged pixels remain.
La fonction MATLAB CPRND génère des points de la distribution uniforme sur un polytope convexe général. Pour votre question, un algorithme plus spécialisé basé sur la décomposition du quadrilatère en triangles est plus efficace.
Les points doivent-ils être distribués uniformément, ou une distribution est-elle correcte?
Le polygone peut-il être concave, ou est-ce que cela garantit d'être convexe?
Si la réponse à la fois ci-dessus est non, choisissez deux sommets et choisissez un point aléatoire sur le segment de ligne entre eux. Ceci est limité aux segments de ligne reliant les sommet (c'est-à-dire très non uniformes); Vous pouvez faire un peu mieux en cueillant un troisième sommet puis cueillir un point entre cela et le premier point - toujours non uniforme, mais au moins tout point du polygone est possible.
Choisir un point aléatoire sur une ligne entre deux points est facile, il suffit d'A + P (B-A), où A et B sont les points et P est un nombre aléatoire compris entre 0,0 et 1,0
Quel type de distribution voulez-vous que les points aient? Si vous ne vous souciez pas, les méthodes ci-dessus vont bien fonctionner. Si vous souhaitez une distribution uniforme, la procédure suivante fonctionnera: divisez le polygone en deux triangles, A et B. Soit A(a) et A(b) Soyez leurs zones. Échantillon d'un point P de la distribution uniforme de l'intervalle entre 0 et A (A) + A (b). Si p <a (a), choisissez Triangle A. Sinon, choisissez Triangle B. Choisissez un Vertex V du triangle choisi et que vous laissez C et D être les vecteurs correspondant aux côtés du triangle. Échantillon Deux nombres X et Y de la distribution exponentielle avec une moyenne unitaire. Ensuite, le point (xc + yd)/(x + y) est un échantillon de la distribution uniforme sur le polygone.
Pour Postgis, c'est ce que j'utilise (vous voudrez peut-être une salle pour des boucles infinies possibles). Vous pouvez exporter l'algorithme vers votre langage de programmation:
CREATE or replace FUNCTION random_point(geometry)
RETURNS geometry
AS $$
DECLARE
env geometry;
corner1 geometry;
corner2 geometry;
minx real;
miny real;
maxx real;
maxy real;
x real;
y real;
ret geometry;
begin
select ST_Envelope($1) into env;
select ST_PointN(ST_ExteriorRing(env),1) into corner1;
select ST_PointN(ST_ExteriorRing(env),3) into corner2;
select st_x(corner1) into minx;
select st_x(corner2) into maxx;
select st_y(corner1) into miny;
select st_y(corner2) into maxy;
loop
select minx+random()*(maxx-minx) into x;
select miny+random()*(maxy-miny) into y;
select ST_SetSRID(st_point(x,y), st_srid($1)) into ret;
if ST_Contains($1,ret) then
return ret ;
end if;
end loop;
end;
$$
LANGUAGE plpgsql
volatile
RETURNS NULL ON NULL INPUT;