web-dev-qa-db-fra.com

Centre de gravité d'un polygone

J'essaie d'écrire une fonction PHP qui calculera le centre de gravité d'un polygone.

J'ai regardé les autres questions similaires mais je n'arrive pas à trouver de solution à cela.

Mon problème est que je dois pouvoir calculer le centre de gravité des polygones réguliers et irréguliers et même des polygones à intersection automatique. 

Est-ce possible? 

J'ai aussi lu que: http://paulbourke.net/geometry/polyarea/ Mais ceci est limité aux polygones non auto-intersectants.

Comment puis-je faire ceci? Pouvez-vous me diriger dans la bonne direction?

17
mixkat

Le centre de gravité (également appelé "centre de gravité" ou "centroïde" peut être calculé à l'aide de la formule suivante:

X = SUM[(Xi + Xi+1) * (Xi * Yi+1 - Xi+1 * Yi)] / 6 / A
Y = SUM[(Yi + Yi+1) * (Xi * Yi+1 - Xi+1 * Yi)] / 6 / A

Extrait de Wikipedia : Centroïde d’un polygone fermé qui ne se croise pas lui-même et défini par n sommets (x0, y0), (x1, y1), ..., (xn − 1, yn −1) est le point (Cx, Cy), où
X coordinate of the center
Y coordinate of the center
et où A est la surface signée du polygone,
Area formula

Exemple utilisant VBasic:

' Find the polygon's centroid.
Public Sub FindCentroid(ByRef X As Single, ByRef Y As _
    Single)
Dim pt As Integer
Dim second_factor As Single
Dim polygon_area As Single

    ' Add the first point at the end of the array.
    ReDim Preserve m_Points(1 To m_NumPoints + 1)
    m_Points(m_NumPoints + 1) = m_Points(1)

    ' Find the centroid.
    X = 0
    Y = 0
    For pt = 1 To m_NumPoints
        second_factor = _
            m_Points(pt).X * m_Points(pt + 1).Y - _
            m_Points(pt + 1).X * m_Points(pt).Y
        X = X + (m_Points(pt).X + m_Points(pt + 1).X) * _
            second_factor
        Y = Y + (m_Points(pt).Y + m_Points(pt + 1).Y) * _
            second_factor
    Next pt

    ' Divide by 6 times the polygon's area.
    polygon_area = PolygonArea
    X = X / 6 / polygon_area
    Y = Y / 6 / polygon_area

    ' If the values are negative, the polygon is
    ' oriented counterclockwise. Reverse the signs.
    If X < 0 Then
        X = -X
        Y = -Y
    End If
End Sub

Pour plus d'informations, consultez ce website ou Wikipedia .

J'espère que ça aide.

Cordialement!

27
redent84

dans cold c ++ et en supposant que vous avez une structure Vec2 avec les propriétés x et y:

const Vec2 findCentroid(Vec2* pts, size_t nPts){
    Vec2 off = pts[0];
    float twicearea = 0;
    float x = 0;
    float y = 0;
    Vec2 p1, p2;
    float f;
    for (int i = 0, j = nPts - 1; i < nPts; j = i++) {
        p1 = pts[i];
        p2 = pts[j];
        f = (p1.x - off.x) * (p2.y - off.y) - (p2.x - off.x) * (p1.y - off.y);
        twicearea += f;
        x += (p1.x + p2.x - 2 * off.x) * f;
        y += (p1.y + p2.y - 2 * off.y) * f;
    }

    f = twicearea * 3;

    return Vec2(x / f + off.x, y / f + off.y);
}

et en javascript:

function findCentroid(pts, nPts) {
    var off = pts[0];
    var twicearea = 0;
    var x = 0;
    var y = 0;
    var p1,p2;
    var f;
    for (var i = 0, j = nPts - 1; i < nPts; j = i++) {
        p1 = pts[i];
        p2 = pts[j];
        f = (p1.lat - off.lat) * (p2.lng - off.lng) - (p2.lat - off.lat) * (p1.lng - off.lng);
        twicearea += f;
        x += (p1.lat + p2.lat - 2 * off.lat) * f;
        y += (p1.lng + p2.lng - 2 * off.lng) * f;
    }
    f = twicearea * 3;
    return {
    X: x / f + off.lat,
    Y: y / f + off.lng
    };
}

ou dans le bon vieux c et en supposant que vous avez une structure Point avec les propriétés x et y:

const Point centroidForPoly(const int numVerts, const Point* verts)
{
    float sum = 0.0f;
    Point vsum = 0;

    for (int i = 0; i<numVerts; i++){
        Point v1 = verts[i];
        Point v2 = verts[(i + 1) % numVerts];
        float cross = v1.x*v2.y - v1.y*v2.x;
        sum += cross;
        vsum = Point(((v1.x + v2.x) * cross) + vsum.x, ((v1.y + v2.y) * cross) + vsum.y);
    }

    float z = 1.0f / (3.0f * sum);
    return Point(vsum.x * z, vsum.y * z);
}
7
Joseph

Swift 4, basé sur la réponse c donnée ci-dessus

/// Given an array of points, find the "center of gravity" of the points
/// - Parameters:
///     - points: Array of points
/// - Returns:
///     - Point or nil if input points count < 3
static func centerOfPoints(points: [CGPoint]) -> CGPoint? {
    if points.count < 3 {
        return nil
    }

    var sum: CGFloat = 0
    var pSum: CGPoint = .zero

    for i in 0..<points.count {
        let p1 = points[i]
        let p2 = points[(i+1) % points.count]
        let cross = p1.x * p2.y - p1.y * p2.x
        sum += cross
        pSum = CGPoint(x:((p1.x + p2.x) * cross) + pSum.x,
                       y:((p1.y + p2.y) * cross) + pSum.y)
    }

    let z = 1 / (3 * sum)
    return CGPoint(x:pSum.x * z,
                   y:pSum.y * z)
}
1
n_b

C’était mon implémentation en Java de la solution acceptée, j’avais ajouté une vérification conditionnelle supplémentaire car certains de mes polygones étaient plats et n’avaient pas d’aire, et plutôt que de me donner le point médian, elle revenait (0,0). Ainsi, dans ce cas, je fais référence à une méthode différente qui consiste simplement à faire la moyenne des sommets. L'arrondi à la fin est parce que je voulais conserver mon objet de sortie sous forme d'entiers même s'il est imprécis, mais je vous invite à supprimer ce bit. De plus, comme tous mes points étaient des entiers positifs, la vérification était logique pour moi, mais pour vous, ajouter une vérification de la surface == 0 aurait également un sens.

private Vertex getCentroid() {

        double xsum = 0, ysum = 0, A = 0;
        for (int i = 0; i < corners.size() ; i++) {

            int iPlusOne = (i==corners.size()-1)?0:i+1;

            xsum += (corners.get(i).getX() + corners.get(iPlusOne).getX()) * (corners.get(i).getX() * corners.get(iPlusOne).getY() - corners.get(iPlusOne).getX() * corners.get(i).getY());
            ysum += (corners.get(i).getY() + corners.get(iPlusOne).getY()) * (corners.get(i).getX() * corners.get(iPlusOne).getY() - corners.get(iPlusOne).getX() * corners.get(i).getY());
            A += (corners.get(i).getX() * corners.get(iPlusOne).getY() - corners.get(iPlusOne).getX() * corners.get(i).getY());
        }
        A = A / 2;
        if(xsum==0 &&ysum==0)
        {
            area = averageHeight/2;
            return getMidpointCenter();
        }
        double x = xsum / (6 * A);
        double y = ysum / (6 * A);
        area = A;


        return new Vertex((int) Math.round(x), (int) Math.round(y));
    }
0
joseph

En php:

// Find the polygon's centroid.
function getCenter($polygon)
{ 
    $NumPoints = count($polygon);

    if($polygon[$NumPoints-1] == $polygon[0]){
        $NumPoints--;
    }else{
        //Add the first point at the end of the array.
        $polygon[$NumPoints] = $polygon[0];
    }

    // Find the centroid.
    $X = 0;
    $Y = 0;
    For ($pt = 0 ;$pt<= $NumPoints-1;$pt++){
        $factor = $polygon[$pt][0] * $polygon[$pt + 1][1] - $polygon[$pt + 1][0] * $polygon[$pt][1];
        $X += ($polygon[$pt][0] + $polygon[$pt + 1][0]) * $factor;
        $Y += ($polygon[$pt][1] + $polygon[$pt + 1][1]) * $factor;
    }

    // Divide by 6 times the polygon's area.
    $polygon_area = ComputeArea($polygon);
    $X = $X / 6 / $polygon_area;
    $Y = $Y / 6 / $polygon_area;

    return array($X, $Y);
}


function ComputeArea($polygon)
{ 
    $NumPoints = count($polygon);

    if($polygon[$NumPoints-1] == $polygon[0]){
        $NumPoints--;
    }else{
        //Add the first point at the end of the array.
        $polygon[$NumPoints] = $polygon[0];
    }

    $area = 0;

    for ($i = 0; $i < $NumPoints; $i++) {
      $i1 = ($i + 1) % $NumPoints;
      $area += ($polygon[$i][1] + $polygon[$i1][1]) * ($polygon[$i1][0] - $polygon[$i][0]);
    }

    $area /= 2;
    return $area;
}

Lire la suite sur:

PHP: Comment déterminer le centre d'un polygone

0
Adrian Cid Almaguer

Puisque nous avons tous beaucoup de plaisir à implémenter cet algorithme dans différentes langues, voici ma version que j'ai mixée pour Python:

def polygon_centre_area(vertices: Sequence[Sequence[float]]) -> Tuple[Sequence[float], float]:
    x_cent = y_cent = area = 0
    v_local = vertices + [vertices[0]]

    for i in range(len(v_local) - 1):
        factor = v_local[i][0] * v_local[i+1][1] - v_local[i+1][0] * v_local[i][1]
        area += factor
        x_cent += (v_local[i][0] + v_local[i+1][0]) * factor
        y_cent += (v_local[i][1] + v_local[i+1][1]) * factor

    area /= 2.0
    x_cent /= (6 * area)
    y_cent /= (6 * area)

    area = math.fabs(area)

    return ([x_cent, y_cent], area)
0
Steztric