[Python] Centroid of Polyline RhinoCommon

Hi guys,

I want to select all polylines on a layer, and then get the the center point of each one to then move the camera’s target to those centroids.

But I am not sure about the syntax of using RhinoCommon classes…

I am getting this error.

import Rhino
import scriptcontext
import System.Guid, System.Drawing.Color

# Prompt for a layer name
layername = scriptcontext.doc.Layers.CurrentLayer.Name
rc, layername = Rhino.Input.RhinoGet.GetString("Name of layer to select objects", True, layername)

Curves = scriptcontext.doc.Objects.FindByLayer(layername)
for obj in Curves: obj.Select(True)

Rhino.Geometry.Polyline.CenterPoint(Curves)

Message: expected Polyline, got Array[RhinoObject]

Traceback:
line 12,

I tried multiple commands such as the AreaMassProperties.Centroid Property with no luck either.

Hoping someone could shed some light for me.
Shynn

Hi @ShynnSup, if you find multiple polylines, you would get multiple centroids but can only set the camera target to one of it or the average of all centroids. Below are two simplified examples the first zooms to all polylines found on a specific layer.

import Rhino
import scriptcontext
import rhinoscriptsyntax as rs

def DoSomething():
    # get the layer
    layer_name = rs.GetLayer("Select Layer", None, False)
    if not layer_name: return

    # get all objects on the layer
    obj_ids = rs.ObjectsByLayer(layer_name)
    if not obj_ids: return

    # filter out polylines
    polyline_ids = [obj_id for obj_id in obj_ids if rs.IsPolyline(obj_id)]
    if not polyline_ids: return

    # zoom to polylines bounding box
    bounding_box = rs.BoundingBox(polyline_ids)
    rs.ZoomBoundingBox(bounding_box)
    
DoSomething()

The second example sets the current views camera target to the average polyline’s center point without changing the camera location:

def DoSomething():
    layer_name = rs.GetLayer("Select Layer", None, False)
    if not layer_name: return
    
    idx = scriptcontext.doc.Layers.FindByFullPath(layer_name, True)
    oes = Rhino.DocObjects.ObjectEnumeratorSettings()
    oes.ObjectTypeFilter = Rhino.DocObjects.ObjectType.Curve
    oes.LayerIndexFilter = idx
    
    rh_objs = scriptcontext.doc.Objects.FindByFilter(oes)
    if not rh_objs: return
    
    point, count = Rhino.Geometry.Point3d.Origin, 0

    for rh_obj in rh_objs:
        rc, polyline = rh_obj.Geometry.TryGetPolyline()
        if rc: 
            point += polyline.CenterPoint()
            count += 1
    
    center = Rhino.Geometry.Point3d.Divide(point, count)
    rs.ViewCameraTarget(None, None, center)
    
DoSomething()

_
c.

Hi @clement, thanks for examples.

Actually what I am trying to do is generate a layout with a detail zooming in to the bounding curve, for each curve on the selected layer.

This is what I have so far… mainly from RhinoDeveloper examples:
https://developer.rhino3d.com/api/RhinoCommon/html/M_Rhino_Display_RhinoViewport_ZoomExtents.htm

import Rhino
import scriptcontext
import rhinoscriptsyntax as rs

# Generate a layout with a single detail view that zooms
# to a list of objects
def AddLayout():
    scriptcontext.doc.PageUnitSystem = Rhino.UnitSystem.Millimeters
    page_views = scriptcontext.doc.Views.GetPageViews()
    page_number = 1
    if page_views: page_number = len(page_views) + 1
    pageview = scriptcontext.doc.Views.AddPageView("A0_{0}".format(page_number), 1189, 841)
    if pageview:
        top_left = Rhino.Geometry.Point2d(20,821)
        bottom_right = Rhino.Geometry.Point2d(1169, 20)
        detail = pageview.AddDetailView("ModelView", top_left, bottom_right, Rhino.Display.DefinedViewportProjection.Top)
        if detail:
            pageview.SetActiveDetail(detail.Id)
            detail.Viewport.ZoomBoundingBox(bounding_box)
            detail.DetailGeometry.IsProjectionLocked = True
            detail.DetailGeometry.SetScale(1, scriptcontext.doc.ModelUnitSystem, 1, scriptcontext.doc.PageUnitSystem)
            # Commit changes tells the document to replace the document's detail object
            # with the modified one that we just adjusted
            detail.CommitChanges()
        pageview.SetPageAsActive()
        scriptcontext.doc.Views.ActiveView = pageview
        scriptcontext.doc.Views.Redraw()


# get the layer
layer_name = rs.GetLayer("Select Layer", None, False)

# get all objects on the layer
obj_ids = rs.ObjectsByLayer(layer_name)

# zoom to polylines bounding box
bounding_box = rs.BoundingBox(obj_ids) 

The error that I am getting right now is: Message: expected BoundingBox, got list. Line 19.
Basically I am inputting all bounding boxes into

detail.Viewport.ZoomBoundingBox(bounding_box)

When I need to loop through the list, so that it cycles through each Bounding Box each time the function AddLayout runs…

Any ideas on how to solve this?
Thanks in advance,
Shynn

rs.BoundingBox returns a list of eight points, but RhinoViewport.ZoomBoundingBox needs a box so you need to use rs.coerceboundingbox.

Before this line you need to change the ActiveView to pageview.
Here is the final code:

import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino

def AddLayout():
    sc.doc.PageUnitSystem = Rhino.UnitSystem.Millimeters
    page_views = sc.doc.Views.GetPageViews()
    page_number = 1
    if page_views: page_number = len(page_views) + 1
    pageview = sc.doc.Views.AddPageView("A0_{0}".format(page_number), 34, 20)
    if pageview:
        top_left = Rhino.Geometry.Point2d(0,0)
        bottom_right = Rhino.Geometry.Point2d(34, 20)
        detail = pageview.AddDetailView("ModelView", top_left, bottom_right, Rhino.Display.DefinedViewportProjection.Top)
        pageview.SetPageAsActive()
        sc.doc.Views.ActiveView = pageview
        if detail:
            pageview.SetActiveDetail(detail.Id)
            detail.Viewport.ZoomBoundingBox(bounding_box)
            detail.DetailGeometry.SetScale(1, sc.doc.ModelUnitSystem, 1, sc.doc.PageUnitSystem)
            detail.DetailGeometry.IsProjectionLocked = True
            detail.CommitChanges()
        sc.doc.Views.Redraw()


layer = rs.GetLayer("Select Layer", None, False)
ids = rs.ObjectsByLayer(layer)
for id in ids:
    bounding_box = rs.coerceboundingbox(rs.BoundingBox(id))
    AddLayout()

ShynnSup.py (1.2 KB)

Here is a Test:

Thanks @Mahdiyar!

I read that it is advised to work with RhinoCommon to avoid those kind of things, thats why I wanted to get the bounding box with RhinnoCommon syntax. But this works too!

Any idea why the first Layout is wrong? (Not zooming to the bounding box).

Guys any idea why the latest script fails for the first layout created? The first layout is always out of focus. You can test for just one curve and it will become more evident.

import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino

def AddLayout():
    sc.doc.PageUnitSystem = Rhino.UnitSystem.Millimeters
    page_views = sc.doc.Views.GetPageViews()
    page_number = 1
    if page_views: page_number = len(page_views) + 1
    pageview = sc.doc.Views.AddPageView("A0_{0}".format(page_number), 34, 20)
    if pageview:
        top_left = Rhino.Geometry.Point2d(0,0)
        bottom_right = Rhino.Geometry.Point2d(34, 20)
        detail = pageview.AddDetailView("ModelView", top_left, bottom_right, Rhino.Display.DefinedViewportProjection.Top)
        pageview.SetPageAsActive()
        sc.doc.Views.ActiveView = pageview
        if detail:
            pageview.SetActiveDetail(detail.Id)
            detail.Viewport.ZoomBoundingBox(bounding_box)
            detail.DetailGeometry.SetScale(1, sc.doc.ModelUnitSystem, 1, sc.doc.PageUnitSystem)
            detail.DetailGeometry.IsProjectionLocked = True
            detail.CommitChanges()
        sc.doc.Views.Redraw()


layer = rs.GetLayer("Select Layer", None, False)
ids = rs.ObjectsByLayer(layer)
for id in ids:
    bounding_box = rs.coerceboundingbox(rs.BoundingBox(id))
    AddLayout()