Centroid for line or polyline

@Helvetosaur

Here is an improved but still simple algorithm for the basic calculation using trapezoidal rule for integration. It is applicable for both open and closed curves.

  • Divide curve into n segments

  • Average the x, y and z coordinates of the points

  • If the curve is open subtract 1/2 of the first and last point coordinates divided by n

This should converge faster for open curves than the simple average of the point coordinates.

Two different centroids related to a closed planar curve:

  1. The centroid of the curve itself. All curves have a centroid, whether open or closed, planar or not planar.

  2. The centroid of the area enclosed by the closed planar curve.

The Rhino command AreaCentroid calculates the second.

Trim if you want to discard one side. Split if you want to keep both sides.

[quote=“youngken, post:7, topic:26547, full:true”]
I think intuitionusa is talking about the equivalent function of “massprop” in Autocad.
[/quote]Rhino has mass property commands for both surfaces and volume: Area, AreaCentroid, AreaMoments, Volume, VolumeCentroid and VolumeMoments. The “Area” commands work for the area enclosed by a planar closed curve as well as both planar and non-planar surfaces.

[quote=“youngken, post:6, topic:26547, full:true”]
For a steel member section, such as a rectangular hollow section. Is it possible to create a centerline of a segment?Thanks
[/quote]Do you want the principal axis for the section area? If so those can be calculated using the results of the Area, AreaCentroid and AreaMoments commands. Someone may have a script which does the calculations.

OK, thanks (I’m not a math person). I don’t quite get that part…

Subtract 1/2 of the first and last point coordinates divided by n…

Subtract from what ? From each previous sample, then average again and loop?

Thanks, --Mitch

@Helvetosaur

• Divide curve into n segments

• Average the x, y and z coordinates of the points

• If the curve is open subtract 1/2 of the first and last point coordinates divided by n from the averages

These are the calculation steps for the centroid based on n segements.

Thanks for all of the responses! I hail from FormZ as that used to be my main modeling tool. Having Centroid for lines and curves was very useful because it gave you a handle to grab the line or curve. If I had multiple curves or parts of a curve atop of another curve it allows you to keep track of them better. Centroid is great on just a straight line because it gave me a reference that I could use when modeling. I will check out the script Helvetosaur.

The centroid of a line is just its midpoint as stated previously. If you LMB click somewhere near the midpoint of a line (with the osnap mid enabled as persistent), it will snap to the midpoint of the line (you will see the osnap light up) and you then can drag it to another snap point (commonly called snap-dragging). Otherwise with the normal move/rotate commands etc. you can always grab the midpoint via the osnap as well. No need for scripts.

Note also that the centroid (as davidcockey is describing it) will likely not lie on the curve if the curve is anything but a straight line…

–Mitch

Just getting back to this - so if i understood correctly:

let’s say my n is currently 1000.

  • I have a 1000 division points, that I average into a point X,Y,Z
    that is “close” to the centroid.

  • I get pt[0] and pt[n] and add them together, divide by n to get
    another x,y,z point

  • I subtract half of that from the average, which gets me what…
    closer?

  • I then iterate, say doubling n, do the above and compare? The
    difference should converge faster?

–Mitch

@Helvetosaur

Almost correct.

The subtraction is only done for open curves, not for closed curves. Also n is the number of segments the curve is divided into. An open curve divided into n segments will have n+1 points; a closed curve will have n points.

The subtraction improves the accuracy of the estimate with n segments. It should converge faster.

The object I want to trim is a closed extrusion. However, after using trim or split, the new object turns to a open polysurface.
Why?
How can I turn the remain part into a closed extrusion again.
Thanksslice.3dm (39.1 KB)

Use BooleanSplit or BooleanDifference instead of Split or Trim.

A solid in Rhino is a closed polysurface. Split or Trim just split or trim the polysurface and don’t take account that it is closed and considered as a surface. The Boolean commands are used when a solid needs to remain a solid.

1 Like

Please start a new topic with this stuff…

As Mitch says, please start a new topic when you have an unrelated question.

Since David has answered your question, I suppose this discussion has now ended in this thread. I would just like to add a final thing though… :stuck_out_tongue:

You’ll have to read up on nomenclature for Rhino. An extrusion is a very specific object type in Rhino that was added to Rhino 5 to allow light-weight objects to be used. An extrusion is only defined by a cross section and an extrusion length. When you trim with a surface that is at an angle to the cross section plane, you will not be able to get an extrusion. Extrusion objects are converted on-the-fly into regular Breps when needed but out-of-the-box Rhino does not convert Breps back into extrusion objects.

  • wim out…

Really interesting reading @davidcockey posts in this thread and then got to @wim post ……so had to search for what a Brep was and LOL found a thread from July 13 with @davidcockey asking the same question.

1 Like

for clarity (just in case ; ) )

that’s july **'**13 …as in a couple years ago… instead of july 13 as in a few months ago.

Thanks @jeff_hammond. Yes July 2013….and Pascals explanation of what a Brep is in that thread is excellent.

Also just to clarify in case it came across the wrong way…I found it amusing because it reminded me we all learn from asking questions….even someone who is obviously as intelligent and educated as @davidcockey is.

1 Like

Hi all

I have tried to test the two algorithms explained here, plus a third algorithm that, after dividing the curve, discards even points and only uses odd points …

It’s only a quick test, but I get pretty different iteration count ( and the points seem a little apart from each other )
( I tried it onto a half circle )

… But I suspect that the script be somewhat buggy … particularly on the second algorithm, maybe it’s not written correctly …

First algorithm is the one from Mitch’s script
Second is the one explained by David
Third is the one that uses odd points only

Should someone happen to see some bug here, please tell me about it, thanks … :smiley:

BTW Thanks Mitch for teaching me about reduce() ! :smiley:

import Rhino
import scriptcontext
import System

def cent1( cur, cnt ):
  '''
  algorithm 1
  '''
  pars = cur.DivideByCount( cnt, True )
  pts = [ cur.PointAt( par ) for par in pars ]
  cen = reduce( lambda a, b : a + b, pts ) / len( pts )
  return cen

def cent2( cur, cnt ):
  '''
  algorithm 2
  '''
  pars = cur.DivideByCount( cnt, True )
  pts = [ cur.PointAt( par ) for par in pars ]
  pts[ 0 ] /= 2
  pts[ -1 ] /= 2
  cen = reduce( lambda a, b : a + b, pts ) / len( pts )
  return cen

def cent3( cur, cnt ):
  '''
  algorithm 3
  '''
  pars = cur.DivideByCount( cnt, True )
  pars = pars[ 1 : : 2 ]
  pts = [ cur.PointAt( par ) for par in pars ]
  cen = reduce( lambda a, b : a + b, pts ) / len( pts )
  return cen

def main():
  gob = Rhino.Input.Custom.GetObject()
  gob.AcceptNothing( True )
  gob.SetCommandPrompt( 'Curve for centroid ?' )
  gob.GeometryFilter = Rhino.DocObjects.ObjectType.Curve
  gob.Get()
  res = gob.Result()
  if res == Rhino.Input.GetResult.Cancel:
    return
  elif res == Rhino.Input.GetResult.Object:
    obref = gob.Object( 0 )
    cur = obref.Curve()
  else:
    return
  tolr = 0.001
 
  cnt = 4
  prev = Rhino.Geometry.Point3d( 1.0e99, 0, 0 )
  itr = 0
  att = Rhino.DocObjects.ObjectAttributes()
  att.ObjectColor = System.Drawing.Color.Red
  att.ColorSource = Rhino.DocObjects.ObjectColorSource.ColorFromObject
  while True:
    scriptcontext.escape_test()
    itr += 1
    cen = cent1( cur, cnt )
    if cen.DistanceTo( prev ) < tolr:
      print 'Algorithm 1: %d iterations' % itr
      Rhino.RhinoDoc.ActiveDoc.Objects.AddPoint( cen, att )
      break
    prev = cen
    cnt += 2
 
  cnt = 4
  prev = Rhino.Geometry.Point3d( 1.0e99, 0, 0 )
  itr = 0
  att = Rhino.DocObjects.ObjectAttributes()
  att.ObjectColor = System.Drawing.Color.Green
  att.ColorSource = Rhino.DocObjects.ObjectColorSource.ColorFromObject
  while True:
    scriptcontext.escape_test()
    itr += 1
    cen = cent2( cur, cnt )
    if cen.DistanceTo( prev ) < tolr:
      print 'Algorithm 2: %d iterations' % itr
      Rhino.RhinoDoc.ActiveDoc.Objects.AddPoint( cen, att )
      break
    prev = cen
    cnt += 2
 
  cnt = 4
  prev = Rhino.Geometry.Point3d( 1.0e99, 0, 0 )
  itr = 0
  att = Rhino.DocObjects.ObjectAttributes()
  att.ObjectColor = System.Drawing.Color.Blue
  att.ColorSource = Rhino.DocObjects.ObjectColorSource.ColorFromObject
  while True:
    scriptcontext.escape_test()
    itr += 1
    cen = cent3( cur, cnt )
    if cen.DistanceTo( prev ) < tolr:
      print 'Algorithm 3: %d iterations' % itr
      Rhino.RhinoDoc.ActiveDoc.Objects.AddPoint( cen, att )
      break
    prev = cen
    cnt += 2
      
# Rhino.RhinoDoc.ActiveDoc.Views.Redraw()

main()


I got that one from Steve a long time ago… :smile:

About the centroid point differences, I only compared mine to the area centroid of a tiny diameter pipe as suggested in one of the posts - it ended up pretty close to that in my quick tests…

–Mitch

OK, I’ve found my error. :blush:
The results look somewhat OK now and David’s algorithm should be the fastest. :smiley:
( But I think that my exit test comparing the result against the previous one makes no sense … :frowning: )
Anyway … interesting stuff.

import Rhino
import scriptcontext
import System

def cent1( cur, cnt ):
  '''
  algorithm 1
  '''
  pars = cur.DivideByCount( cnt, True )
  pts = [ cur.PointAt( par ) for par in pars ]
  cen = reduce( lambda a, b : a + b, pts ) / len( pts )
  return cen

def cent2( cur, cnt ):
  '''
  algorithm 2
  '''
  pars = cur.DivideByCount( cnt, True )
  pts = [ cur.PointAt( par ) for par in pars ]
  pts[ 0 ] = ( pts[ 0 ] + pts[ -1 ] ) / 2.0
  pts.pop()
  cen = reduce( lambda a, b : a + b, pts ) / len( pts )
  return cen

def cent3( cur, cnt ):
  '''
  algorithm 3
  '''
  pars = cur.DivideByCount( cnt, True )
  pars = pars[ 1 : : 2 ]
  pts = [ cur.PointAt( par ) for par in pars ]
  cen = reduce( lambda a, b : a + b, pts ) / len( pts )
  return cen

def main():
  gob = Rhino.Input.Custom.GetObject()
  gob.AcceptNothing( True )
  gob.SetCommandPrompt( 'Curve for centroid ?' )
  gob.GeometryFilter = Rhino.DocObjects.ObjectType.Curve
  gob.Get()
  res = gob.Result()
  if res == Rhino.Input.GetResult.Cancel:
    return
  elif res == Rhino.Input.GetResult.Object:
    obref = gob.Object( 0 )
    cur = obref.Curve()
  else:
    return
  tolr = 0.001
 
  cnt = 4
  prev = Rhino.Geometry.Point3d( 1.0e99, 0, 0 )
  itr = 0
  att = Rhino.DocObjects.ObjectAttributes()
  att.ObjectColor = System.Drawing.Color.Red
  att.ColorSource = Rhino.DocObjects.ObjectColorSource.ColorFromObject
  while True:
    scriptcontext.escape_test()
    itr += 1
    cen = cent1( cur, cnt )
    if cen.DistanceTo( prev ) < tolr:
      print 'Algorithm 1: %d iterations' % itr
      Rhino.RhinoDoc.ActiveDoc.Objects.AddPoint( cen, att )
      break
    prev = cen
    cnt += 2
 
  cnt = 4
  prev = Rhino.Geometry.Point3d( 1.0e99, 0, 0 )
  itr = 0
  att = Rhino.DocObjects.ObjectAttributes()
  att.ObjectColor = System.Drawing.Color.Green
  att.ColorSource = Rhino.DocObjects.ObjectColorSource.ColorFromObject
  while True:
    scriptcontext.escape_test()
    itr += 1
    cen = cent2( cur, cnt )
    if cen.DistanceTo( prev ) < tolr:
      print 'Algorithm 2: %d iterations' % itr
      Rhino.RhinoDoc.ActiveDoc.Objects.AddPoint( cen, att )
      break
    prev = cen
    cnt += 2
 
  cnt = 4
  prev = Rhino.Geometry.Point3d( 1.0e99, 0, 0 )
  itr = 0
  att = Rhino.DocObjects.ObjectAttributes()
  att.ObjectColor = System.Drawing.Color.Blue
  att.ColorSource = Rhino.DocObjects.ObjectColorSource.ColorFromObject
  while True:
    scriptcontext.escape_test()
    itr += 1
    cen = cent3( cur, cnt )
    if cen.DistanceTo( prev ) < tolr:
      print 'Algorithm 3: %d iterations' % itr
      Rhino.RhinoDoc.ActiveDoc.Objects.AddPoint( cen, att )
      break
    prev = cen
    cnt += 2
      
# Rhino.RhinoDoc.ActiveDoc.Views.Redraw()

main()