Python: Centroid and Modules part 1

November 22, 2011
by admin
  • polyarea2

While I have been writing code for the DigiFab project Parallel Pleats, I started to redo my workflow in programming so it would fit better with the functionality of python. Coming from C++ I was not used to the simplicity in separating functions in different files like how python can. With this, and the fact that it never occurred to me that rhino had a built-in centroid module, I decided to attack both problems and write a post on it.  If your only interested in the linking of multiple files check out Part 2

 My first step was to figure out the math of the centroid from a polygon. While the math is pretty simple, I had a hard time finding the function written in python to easily grab. This made doing the code all that more fun (keeping in mind I had no idea about rhino internally, thanks Mark haha). The math can be broken into three main parts: find the area of the polygon, find the X centroid of the polygon, and find the Y centroid of the polygon.

To tackle the translation from math to python (within a modeling package) we first need to collect our thoughts and figure out what variables we need.  First we look at the area function, which is needed to find the centroid.

So we can see that we need:
‘i’ value equal to ‘0’ 
‘N’ value equal to the number of vertices
‘X’ value equal to the X coordinate
‘Y’ value equal to they Y coordinate
Next lets take a look at the second function, finding the x and y centroids.  
So for this we see the ‘A’ is the area and the summation looks relatively similar to the Area function, meaning we do not need any more information to find our centroid.
Now we look at how these variables can be found.Common with a loop in any programming language, we can set the summation with i=0 by just defining it before the loop.
i = 0
for i in range(our_range)
To get N, we will rely on the internal capabilities of rhino (or any modeling package that has queryable curves/lines).  We will call our variable crv_pt_count, and it will hold the number of points on our curve.  By importing the rhinoscriptsyntax (import rhinoscriptsyntax as rs) we can use the CurvePointCount() which will give us the points of the curve passed to it.  Our curve is called curve_sel (since we will select the curve).
crv_pt_count=rs.CurvePointCount(curve_sel)
The summation can now begin to translate into a for loop:
i = 0
for i in range(crv_pt_count – 1):
The next big challange is translating the points on a curve to a variable.  To do this we use the rhino module CurvePoints() which returns the points on a given curve.  We use a variable call crv_pts to store our points.
crv_pts=rs.CurvePoints(curve_sel)
 This part is a bit tricky if you do not know how rhino passes you the points.  In order to translate Xi, we need to know that the point is given as [x,y,z], making X the [0] index, always.  So what we really want to say in not crv_pts[i] but crv_pts[i][0].  This gives us the i index, say its 0, of the xyz of the first point, then gives us the 0 index of that, which is x.  Y can be described as crv_pts[i][1], as y is the index 1 of the xyz array.  Now we can begin to translate the summation of the Area into python:
Area summation:(Xi*Yi+1 – Xi+1*Yi )
Python Translation: (crv_pts[i][0] * crv_pts[i+1][1]) – (crv_pts[i+1][0] * crv_pts[i][1])
After we do the summation we need to multiply it by 1/2.  For ease of clarity, we will multiply by 0.5.  
Area = 0.5 * area_summation
To bring the math into python we get:
i=0 
for i in range(n-1): poly_area_sum=poly_area_sum + ( ( (crv_pts[i][0])*(crv_pts[i+1][1]) ) - ( (crv_pts[i+1][0])*(crv_pts[i][1]) ) ) area = ( (.5) * (poly_area_sum) )

To implement this in rhino, we use the built in functions mentioned previously. The entire function looks like this:

def Curve_Area(curve_sel):
#Description: Calculates area of a polygon
#Parameters: Takes in a curve
#Returns: Area of Polygon
#Notes: 
    poly_area_sum=0
    crv_pt_count=rs.CurvePointCount(curve_sel)
    n=crv_pt_count
    crv_pts=rs.CurvePoints(curve_sel)
    i=0
    for i in range(n-1):
        poly_area_sum=poly_area_sum + ( ( (crv_pts[i][0])*(crv_pts[i+1][1]) ) - ( (crv_pts[i+1][0])*(crv_pts[i][1]) ) )
        
    area = ( (.5) * (poly_area_sum) )
    return area

Now that we know how to find the proper index and get the variables, we can easily do the centroid function:

    #find Centroid sub X
    i=0
    for i in range(n-1):
        cntrd_x=cntrd_x + ( ( (crv_pts[i][0])+(crv_pts[i+1][0]) )*( ( (crv_pts[i][0])*(crv_pts[i+1][1]) )-( (crv_pts[i+1][0])*(crv_pts[i][1]) ) ) )
    #find Centroid sub Y, reset i to 0
    i=0
    for i in range(n-1):
        cntrd_y=cntrd_y + ( ( (crv_pts[i][1])+(crv_pts[i+1][1]) )*( ( (crv_pts[i][0])*(crv_pts[i+1][1]) )-( (crv_pts[i+1][0])*(crv_pts[i][1]) ) ) )

Like the Area function, at the end we need to do a few more manipulations to get the final result:

    cent_area_var=( ( 1 / ( 6 *(crv_area) ) ) )
    centroid_x = (cent_area_var) * (cntrd_x)
    centroid_y = (cent_area_var) * (cntrd_y)
    crv_centroid = [centroid_x,centroid_y,0]

With the beginning variable declarations the entire function looks like this:

def Curve_Centroid(crv_area, curve_sel):
#Description: Calculates Centroid of Polygon
#Parameters: Takes in the area of curve and the curve
#Returns: x,y,z of curve centroid
#Notes:
    cntrd_x=0
    cntrd_y=0
    crv_pt_count=rs.CurvePointCount(curve_sel)
    crv_pts=rs.CurvePoints(curve_sel)
    n=crv_pt_count
    #find Centroid sub X
    i=0
    for i in range(n-1):
        cntrd_x=cntrd_x + ( ( (crv_pts[i][0])+(crv_pts[i+1][0]) )*( ( (crv_pts[i][0])*(crv_pts[i+1][1]) )-( (crv_pts[i+1][0])*(crv_pts[i][1]) ) ) )
    #find Centroid sub Y, reset i to 0
    i=0
    for i in range(n-1):
        cntrd_y=cntrd_y + ( ( (crv_pts[i][1])+(crv_pts[i+1][1]) )*( ( (crv_pts[i][0])*(crv_pts[i+1][1]) )-( (crv_pts[i+1][0])*(crv_pts[i][1]) ) ) )
    #Create centroid coordinate
    cent_area_var=( ( 1 / ( 6 *(crv_area) ) ) )
    centroid_x = (cent_area_var) * (cntrd_x)
    centroid_y = (cent_area_var) * (cntrd_y)
    crv_centroid = [centroid_x,centroid_y,0]

So we have these two working functions, although if you observe carefully, we never call either function. So how do we use them? Well, thats part 2. Since the programming I have been doing for our team project involves me testing functions manually but will eventually be automated, I wanted to start creating scripts that are independent. This way I can pass a curve to a function and not worry about how that curve was found, ie. i can manually select the curve and pass it into my function, or I can have a different function get a curve, either way, I never need to touch my centroid.py file again.

#Centroid Module
#Notes: x[i] can translate to x being crv_pts[i][0] while y is crv_pts[i][1]
import rhinoscriptsyntax as rs

def Centroid(curve_sel):
#Description: Calculates centroid of polygon
#Parameters: Takes in a Curve
#Returns: Nothing
#Notes:
    crv_area=Curve_Area(curve_sel)
    Curve_Centroid(crv_area, curve_sel)

def Curve_Area(curve_sel):
#Description: Calculates area of a polygon
#Parameters: Takes in a curve
#Returns: Area of Polygon
#Notes: 
    poly_area_sum=0
    crv_pt_count=rs.CurvePointCount(curve_sel)
    n=crv_pt_count
    crv_pts=rs.CurvePoints(curve_sel)
    i=0
    for i in range(n-1):
        poly_area_sum=poly_area_sum + ( ( (crv_pts[i][0])*(crv_pts[i+1][1]) ) - ( (crv_pts[i+1][0])*(crv_pts[i][1]) ) )
        
    area = ( (.5) * (poly_area_sum) )
    return area
    
def Curve_Centroid(crv_area, curve_sel):
#Description: Calculates Centroid of Polygon
#Parameters: Takes in the area of curve and the curve
#Returns: x,y,z of curve centroid
#Notes:
    cntrd_x=0
    cntrd_y=0
    crv_pt_count=rs.CurvePointCount(curve_sel)
    crv_pts=rs.CurvePoints(curve_sel)
    print"Curve point count",crv_pt_count
    n=crv_pt_count
    #find Centroid sub X
    i=0
    for i in range(n-1):
        cntrd_x=cntrd_x + ( ( (crv_pts[i][0])+(crv_pts[i+1][0]) )*( ( (crv_pts[i][0])*(crv_pts[i+1][1]) )-( (crv_pts[i+1][0])*(crv_pts[i][1]) ) ) )
    #find Centroid sub Y, reset i to 0
    i=0
    for i in range(n-1):
        cntrd_y=cntrd_y + ( ( (crv_pts[i][1])+(crv_pts[i+1][1]) )*( ( (crv_pts[i][0])*(crv_pts[i+1][1]) )-( (crv_pts[i+1][0])*(crv_pts[i][1]) ) ) )
    #Create centroid coordinate
    cent_area_var=( ( 1 / ( 6 *(crv_area) ) ) )
    centroid_x = (cent_area_var) * (cntrd_x)
    centroid_y = (cent_area_var) * (cntrd_y)
    crv_centroid = [centroid_x,centroid_y,0]
    
    #Check if Rhino Is same, doesnt matter though, we will trust our math
    #Rhino gives different answer always but close enough
    rhino_centroid=rs.CurveAreaCentroid(curve_sel)
    print(rhino_centroid[0])
    print(crv_centroid)
    #if rhino_centroid[0] != crv_centroid:
    #    print"Centroid does not match with Rhino Internal Centroid"
    #rs.AddPoint(crv_centroid)
    #return crv_centroid

Check out the next part discussing separate files: Part 2

Leave a Comment