Code Red > .NET

centroid of closed polyline

(1/6) > >>

Chumplybum:
hi all, i'm trying to find the centroid of a closed polyline.

i've read a few lisp examples that can do this (including one on the swamp http://www.theswamp.org/index.php?topic=18725.0) but haven't been able to transform this code to .net. From the lisp examples it seems that the polyline is converted to a region and then the centriod is found from the region, however from what i can see there is no centroid property (although there is a centroid property under solid3dmassproperties).
So i couldn't convert lisp to .net and went off and found the formula for the centroid of a polyline (http://en.wikipedia.org/wiki/Centroid) as well as a vb.net sample (http://www.vb-helper.com/howto_net_polygon_centroid.html) after slightly modifying the code i've come up with the following...


--- Code: ---        ' For polylines
        Dim pl As AADS.Polyline = TryCast(context.PickedObject, AADS.Polyline)

        If pl Is Nothing OrElse pl.Closed = False Then Return

        Dim RunningX As Long
        Dim RunningY As Long
        Dim second_factor As Long

        For Counter As Integer = 0 To pl.NumberOfVertices - 2
            second_factor = pl.GetPoint2dAt(Counter).X * pl.GetPoint2dAt(Counter + 1).Y - pl.GetPoint2dAt(Counter + 1).X * pl.GetPoint2dAt(Counter).Y

            RunningX += (pl.GetPoint2dAt(Counter).X + pl.GetPoint2dAt(Counter + 1).X) * second_factor
            RunningY += (pl.GetPoint2dAt(Counter).Y + pl.GetPoint2dAt(Counter + 1).Y) * second_factor
        Next


        Dim X, Y As Long

        ' Divide by 6 times the polygon's area.
        Dim polygon_area As Single = pl.Area
        X = RunningX / (6 * pl.Area)
        Y = RunningY / (6 * pl.Area)

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

        Dim CentroidPoint As AAG.Point3d = New AAG.Point3d(X, Y, pl.Elevation)


--- End code ---

the code works for a rectangle that is drawn at 0,0... however, when the polyline moves away from 0,0 the code produces some crazy results and if the polyline is in a different quadrant about the usc it produces some more crazy results

does anyone know of a better / easier way to find the centroid, or a problem with my code???

any help would be great


cheers, Mark

pkohut:
You have to do the calculations relative to the object you're trying to measure.  The code would
look something like this (sorry I don't have VB skills)

--- Code: ---dim ptRef as Point2d
dim pt1 as Point2d
dim pt2 as Point2d
dim second_factor as double
dim drX as double
dim drY as double

For Counter as Integer = 0 To pl.NumberOfVertices - 2
  pt1 = pl.GetPoint2dAt(Counter)
  Pt2 = pl.GetPoint2dAt(counter + 1)
  if i == 0 then
      ptRef = pt1
  endif
  second_factor = (pt1.x - ptRef.x) * (pt2.y - ptRef.y) - (pt2.x - ptRef.x) * (pt1.y - ptRef.y);
  drX += ((pt1.x - ptRef.x) + (pt2.x - ptRef.x)) * b0;
  drY += ((pt1.y - ptRef.y) + (pt2.y - ptRef.y)) * b0;
Next

x = drX / (6 * pl.area) + ptRef.x
y = drY / (6 * pl.area) + ptRef.y


--- End code ---

With this there is no need to reverse sign at the end.  Your example code is declaring some variables
as Long when they should be doubles (or whatever VB calls them).  Was your code returning
a float or double for those computations?  If they are then I would turn on the Option strict to get a
little tighter control over the VB.

Paul

Cathy:
It looks to me like you are missing one side of the polygon -- the one from the last point to the first point.  I note that the sample code at http://www.vb-helper.com/howto_net_polygon_centroid.html makes a copy of the first point at the end, but I don't see where you do that.

SomeCallMeDave:
You could also used TF.net  http://code.google.com/p/tf-net/ and let it do the work for you.


C# code, but I'm sure you can convert it

--- Code: ---
using System;
using System.Linq;
using System.Text;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Topology.Geometries;
using Topology.IO.Dwg;

namespace TF_Test
{

    public class TF_Test
    {
        [CommandMethod("TFCentroid")]
        public void FindCentroid()
        {
            {
                Document doc = Application.DocumentManager.MdiActiveDocument;
                Database db = doc.Database;
                Editor ed = doc.Editor;

                PromptEntityOptions opts = new PromptEntityOptions("\nSelect Entity to Query: ");
                opts.AllowNone = true;
                opts.AllowObjectOnLockedLayer = true;
                opts.SetRejectMessage("\nDon't pick that kind ");
                opts.AddAllowedClass(typeof(Polyline), false);


                //Select the object on-screen
                PromptEntityResult res = ed.GetEntity(opts);
                if (res.Status == PromptStatus.OK)
                {
                    ObjectId ObjID = res.ObjectId;
                    try
                    {
                        using (Transaction trans = db.TransactionManager.StartTransaction())
                        {
                            Entity ent = (Entity)trans.GetObject(ObjID, OpenMode.ForRead, false);
                            DwgReader reader = new DwgReader();
                            DwgWriter writer = new DwgWriter();
                            Geometry geometry = (Geometry)reader.ReadGeometry(ent);
                            IPoint centroid = geometry.Centroid;
                            ed.WriteMessage("\nCentroid is " + centroid.ToString());




                        }//using
                    }//end try
                    catch (System.Exception ex)
                    {
                        ed.WriteMessage("Error Reading Centroid: " + ex.Message);

                    }
                }// if

            }
        }


--- End code ---

Chumplybum:
thanks pkohut for your help / code...

i've amended my code to suit your changes (see below), however, the code only seems to produce an accurate result if the polyline is drawn counterclockwise (see attached images, the polyline has been mirrod to get counterclockwise vs clockwise)



--- Code: ---       

imports AADS = AutoDesk.AutoCAD.DatabaseServices
imports AAG = Autodesk.AutoCAD.Geometry

Public Shared Function GetCentroid(ByVal polyline As AADS.Polyline) As AAG.Point3d

            Dim ReferencePoint As AAG.Point2d
            Dim RunningX As Double
            Dim RunningY As Double
            Dim second_factor As Double
            Dim Point1 As AAG.Point2d
            Dim Point2 As AAG.Point2d

            For Counter As Integer = 0 To polyline.NumberOfVertices - 2
                Point1 = polyline.GetPoint2dAt(Counter)
                Point2 = polyline.GetPoint2dAt(Counter + 1)

                If Counter = 0 Then ReferencePoint = Point1

                second_factor = (Point1.X - ReferencePoint.X) * (Point2.Y - ReferencePoint.Y) - (Point2.X - ReferencePoint.X) * (Point1.Y - ReferencePoint.Y)

                RunningX += ((Point1.X - ReferencePoint.X) + (Point2.X - ReferencePoint.X)) * second_factor
                RunningY += ((Point1.Y - ReferencePoint.Y) + (Point2.Y - ReferencePoint.Y)) * second_factor
            Next

            Dim X, Y As Double

            X = RunningX / (6 * polyline.Area) + ReferencePoint.X
            Y = RunningY / (6 * polyline.Area) + ReferencePoint.Y


            Return New AAG.Point3d(X, Y, polyline.Elevation)

        End Function

--- End code ---

Navigation

[0] Message Index

[#] Next page

Go to full version