Calculating **centroid of multiple polygons**, or compound polygons, first we need the centroid of each individual polygons. One we have that, we simply calculate the **average of each centroid** weighted by the area of each individual polygon. **Centroid calculation for a single polygon** will be covered in details, but area calculation falls out of the scope of this article.

# TL;DR

Centroid of multiple polygons, or compound polygons, is **the average of each composing polygon’s centroid weighted by their area**. Holes should be weighted negative.

An intuitive mental model for this can be if you **consider the polygons as actual bodies with mass**. To balance these bodies on a single point, the compound center has to be shifted towards heavier objects. In case there is a hole in a body, the greater the hole, the less the weight, the further the centerpoint should be.

# Calculate centroid of polygon

To see the algorithm in action, I recommend to take look on the coordinates step by step. Having this example, the triangle is made of points (2, 3), (3, 7) and (6, 2).

Simply adding them up as (2 + 3 + 6, 3 + 7 + 2) results in (11, 12). Having three points, get the average dividing by 3 resulting (3.6, 4), the **coordinates of the centroid of this triangle**.

It gives the very **same result like we’ve constructed the intersection of bisectors** of each angle (left), or median of each side can say. Or just **have a single median and get the third** (right), it is all the same.

A simple C# implementation is something like this one below, an actual code piece from the **geometry library I maintain at GitHub**. I really enjoyed when I realized that Σ character compiles fine :), so code readability stays high.

private void CalculateCentroid() { // Enumerate points. float Σx = 0.0f; float Σy = 0.0f; EnumeratePoints((Vector2 eachPoint) => { Σx += eachPoint.x; Σy += eachPoint.y; }); // Average. float x = Σx / pointCount; float y = Σy / pointCount; // Assign. _centroid = new Vector2(x, y); }

# Algorithm for centroid of multiple polygons

When you work with multiple polygons, or can say working with a compound polygon, a convenient way to **calculate centroid is using geometric decomposition**. This method basically divides a compound polygon into simpler figures, then cummulate the centroid of each, weighted by their area.

This figure above is **dissected to three distinct polygon**. A red rectangle (with area of 32), an orange triangle (with area of 12), and a smaller blue rectangle (with area of 12). As the latter is a hole on the compound figure, its area is count negative as -12.

You can **calculate the corresponding weight for each polygon**, by simply divide each area by the cummulative area (32 + 12 – 12 = 32). This gives 100% (32/32) for red rectangle, 37.5% for the orange triangle and -37.5% for the blue rectangle.

After that simply add up each centroid, but apply the corresponding weight percentages before. The resulting centroid of multiple polygons goes like 5 * 1.0 + 5 * 0.375 + 3.5 * -0.375 for x and 3 * 1.0 + 6 * 0.375 + 3 * -0.375 for y. Gives (5.5625, 4.125).

If you move the hole to the right, its individual centroid also goes to the right, so the weighted (thus inverted) centroid part goes left. The compound centroid x coordinate is now 5*1.0 + 5 * 0.375 + 6.5 * -0.375 resulting 4.4375, actually 0.5625 away from 5 again, yet to the other direction (y is unchanged).

In **eppz! Geometry**, the implementation is straightforward, as it uses the **winding direction** (to determine positive / negative polygons), **area** and **centroid** features of the library.

public static Vector2 CentroidOfPolygons(Polygon[] polygons, Polygon.WindingDirection windingDirection) { float ΣxA = 0.0f; float ΣyA = 0.0f; float ΣA = 0.0f; foreach (Polygon eachPolygon in polygons) { // Add or subtract area. float sign = (eachPolygon.windingDirection == windingDirection) ? 1.0f : -1.0f; float eachSignedArea = eachPolygon.area * sign; // Get centroid. Vector2 eachCentroid = eachPolygon.centroid; // Sum weighted. ΣxA += eachCentroid.x * eachSignedArea; ΣyA += eachCentroid.y * eachSignedArea; ΣA += eachSignedArea; } // "Remove" area. float x = ΣxA / ΣA; float y = ΣyA / ΣA; return new Vector2(x, y); }

The area weighting here happens in two stage. First simply multiple each centroid with each area, and store the sums of the areas as well. Then **normalize the resulting cummulative centroid with the sums of the areas** in a single step. Gives the same result as working with percentages directly, but with **less calculation** (as it spares the calculation of each percentage).

**DISCLAIMER.** THE INFORMATION ON THIS BLOG (INCLUDING BUT NOT LIMITED TO ARTICLES, IMAGES, CODE SNIPPETS, SOURCE CODES, EXAMPLE PROJECTS, ETC.) IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE INFORMATION ON THIS BLOG (INCLUDING BUT NOT LIMITED TO ARTICLES, IMAGES, CODE SNIPPETS, SOURCE CODES, EXAMPLE PROJECTS, ETC.).