Author Topic: Offset curve toward centroid  (Read 1260 times)

0 Members and 1 Guest are viewing this topic.

Offline fixo

  • Full Member
  • ***
  • Posts: 135
  • Karma: +4/-0
  • Gender: Male
    • prefered language: C
    • Prog expertise: Good
    • View Profile
Offset curve toward centroid
« on: February 26, 2013, 07:13:16 PM »
// Offset curve toward centroid

Code: [Select]
        [CommandMethod("OFT")]
        public static void OffsetTowardCentroid()
        {
            Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;

            Editor ed = doc.Editor;

            Database db = doc.Database;

            Transaction tr = doc.TransactionManager.StartTransaction();

                using (tr)
                {
                    try
                    {
                        BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);

                        PromptEntityOptions peo = new PromptEntityOptions("\nSelect curve to Offset Toward: ");

                        peo.SetRejectMessage("\nSelect curve only: ");

                        peo.AddAllowedClass(typeof(Line), true);

                        peo.AddAllowedClass(typeof(Arc), true);

                        peo.AddAllowedClass(typeof(Circle), true);

                        peo.AddAllowedClass(typeof(Ellipse), true);

                        peo.AddAllowedClass(typeof(Polyline), true);

                        peo.AddAllowedClass(typeof(Spline), true);

                        PromptEntityResult pres = ed.GetEntity(peo);

                        if ((pres.Status != PromptStatus.OK)) return;

                        Entity ent = (Entity)tr.GetObject(pres.ObjectId, OpenMode.ForRead);

                        // add the polylines to the array
                        DBObjectCollection dbobjs = new DBObjectCollection();
                        dbobjs.Add(ent);
                        // create the region
                        Region objreg = new Region();

                        DBObjectCollection objRegions = new DBObjectCollection();

                        objRegions = Region.CreateFromCurves(dbobjs);

                        objreg = objRegions[0] as Region;
                       
                        Curve curv = ent as Curve;

                        PromptDoubleResult dres = ed.GetDouble("\nDistance for Offset: ");

                        double dist = dres.Value;

                        DBObjectCollection ids = curv.GetOffsetCurves(dist);
                        DBObjectCollection ids2 = curv.GetOffsetCurves(-1 * dist);
                       double leg= ((ids[0]) as Curve).Area;
                       double leg2 = ((ids2[0]) as Curve).Area;
                       if (leg - leg2 < 0)
                       {
                           foreach (Entity oEnt in ids)
                           {
                               btr.AppendEntity(oEnt);
                               tr.AddNewlyCreatedDBObject(oEnt, true);
                           }
                       }
                       else
                       {

                           foreach (Entity oEnt in ids2)
                           {
                               btr.AppendEntity(oEnt);
                               tr.AddNewlyCreatedDBObject(oEnt, true);
                           }
                       }
                       
                       objRegions.Dispose();
                       ids.Dispose();
                       ids2.Dispose();
                       doc.TransactionManager.FlushGraphics();
                        tr.Commit();
                        ed.UpdateScreen();
                    }

                    catch (Autodesk.AutoCAD.Runtime.Exception ex)
                    {
                        ed.WriteMessage("\nError: {0}\nTrace: {1}", ex.Message, ex.StackTrace);
                    }
                }
            }

Offline bcinnv

  • Newbie
  • *
  • Posts: 9
  • Karma: +0/-0
    • prefered language: VB
    • Prog expertise: Good
    • View Profile
Re: Offset curve toward centroid
« Reply #1 on: March 17, 2013, 12:37:31 AM »
Thanks! But not always centroid I think, it's different for "I" and "moon" shapes.  Should just say Offset Polyline to Inside :). Works wonderfully though!

I do think I found a method to find the actual centroid... perhaps there's a way to find the offset closest to it and keep it?  I'm new at programming but here's the code I came up with to get the centroid for you to "fix" as you see fit :)

Code: [Select]
// get centroid of polygon


namespace BCCGETCENTROIDCOMMAND
{
    public class BCCGETCENTROIDCOM
    {

        [CommandMethod("BCC:CENTROID")]

        public static void BCCGETCENTROID()
        {

            // Get the current document and database

            Document acDoc = Application.DocumentManager.MdiActiveDocument;
            Database acCurDb = acDoc.Database;
            Editor ed = acDoc.Editor;
            Transaction tr = acCurDb.TransactionManager.StartTransaction();

            // Start a transaction

            using (tr)
            {

            SelectionFilter sf = new SelectionFilter(new TypedValue[1] { new TypedValue((int)DxfCode.Start, "LWPolyline") });//<<<--- a selection filter to get text only
            PromptSelectionResult objects = ed.GetSelection(sf);
            if (objects.Status != PromptStatus.OK) return;
            List<Polyline> polylines = new List<Polyline>();

            foreach (ObjectId oid in objects.Value.GetObjectIds())
            {
                DBObject ent = tr.GetObject(oid, OpenMode.ForWrite);
                Polyline p = ent as Polyline;

                // add the polylines to the array
                DBObjectCollection dbobjs = new DBObjectCollection();
                dbobjs.Add(ent);
                // create solid to get region and centroid
                Solid3d Solid = new Solid3d();
                Solid.Extrude(((Region)Region.CreateFromCurves(dbobjs)[0]), 1, 0);
                Point2d centroid = new Point2d(Solid.MassProperties.Centroid.X, Solid.MassProperties.Centroid.Y);
                Solid.Dispose();

                BlockTable acBlkTbl = tr.GetObject(acCurDb.BlockTableId, OpenMode.ForRead) as BlockTable;
                BlockTableRecord acBlkTblRec; acBlkTblRec = tr.GetObject(acBlkTbl[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
                DBPoint acPoint = new DBPoint(new Point3d(centroid.X, centroid.Y,0));
                acBlkTblRec.AppendEntity(acPoint);
                tr.AddNewlyCreatedDBObject(acPoint, true);
                acCurDb.Pdmode = 99;
                acCurDb.Pdsize = 1;                     
            }

                // Save the new objects to the database
                tr.Commit();
            }

        }
    }
}

« Last Edit: March 17, 2013, 04:51:30 AM by bcinnv »

Offline (gile)

  • C#
  • *
  • Posts: 87
  • Karma: +8/-0
  • Gender: Male
    • prefered language: F
    • Prog expertise: Good
    • View Profile
Re: Offset curve toward centroid
« Reply #2 on: March 17, 2013, 09:28:39 AM »
Hi,

To get a Polyline centroid, an easier (and faster) way is to add to your project the GeometryExtensions.dll and simply call the Polyline.Centroid() extension method.

To insure offseting inside a (closed) polyline, you can offset it on both sides and compare the resulting polylines areas.
Here's a little sample which uses this way.

In the PolylineExtension static class, the Polyline.Offset() extension method is defined. This method can be called as an instance method and requires an argument to specify the offset side. This argument is a member of the OffsetSide enum defined in the PolylineExtension class too.

The PolylineExtension class:
Code: [Select]
using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.AutoCAD.DatabaseServices;

namespace OffsetPolylineSample
{
    /// <summary>
    /// Provides a Offset() extension methode for the Polyline type
    /// </summary>
    public static class PolylineExtension
    {
        /// <summary>
        /// Enumeration of offset side options
        /// </summary>
        public enum OffsetSide { In, Out, Left, Right, Both }

        /// <summary>
        /// Offset the source polyline to specified side(s).
        /// </summary>
        /// <param name="source">The polyline to be offseted.</param>
        /// <param name="offsetDist">The offset distance.</param>
        /// <param name="side">The offset side(s).</param>
        /// <returns>A polyline sequence resulting from the offset of the source polyline.</returns>
        public static IEnumerable<Polyline> Offset(this Polyline source, double offsetDist, OffsetSide side)
        {
            offsetDist = Math.Abs(offsetDist);
            IEnumerable<Polyline> offsetRight = source.GetOffsetCurves(offsetDist).Cast<Polyline>();
            double areaRight = offsetRight.Select(pline => pline.Area).Sum();
            IEnumerable<Polyline> offsetLeft = source.GetOffsetCurves(-offsetDist).Cast<Polyline>();
            double areaLeft = offsetLeft.Select(pline => pline.Area).Sum();
            switch (side)
            {
                case OffsetSide.In:
                    if (areaRight < areaLeft)
                    {
                        offsetLeft.Dispose();
                        return offsetRight;
                    }
                    else
                    {
                        offsetRight.Dispose();
                        return offsetLeft;
                    }
                case OffsetSide.Out:
                    if (areaRight < areaLeft)
                    {
                        offsetRight.Dispose();
                        return offsetLeft;
                    }
                    else
                    {
                        offsetLeft.Dispose();
                        return offsetRight;
                    }
                case OffsetSide.Left:
                    offsetRight.Dispose();
                    return offsetLeft;
                case OffsetSide.Right:
                    offsetLeft.Dispose();
                    return offsetRight;
                case OffsetSide.Both:
                    return offsetRight.Concat(offsetLeft);
                default:
                    return null;
            }
        }

        private static void Dispose(this IEnumerable<Polyline> plines)
        {
            foreach (Polyline pline in plines)
            {
                pline.Dispose();
            }
        }
    }
}

A testing command:
Code: [Select]
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;

namespace OffsetPolylineSample
{
    public class CommandMethods
    {
        [CommandMethod("Test", CommandFlags.Modal)]
        public void Test()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;

            PromptDistanceOptions pdo =
                new PromptDistanceOptions("\nSpecify the offset distance: ");
            pdo.AllowZero = false;
            PromptDoubleResult pdr = ed.GetDistance(pdo);
            if (pdr.Status != PromptStatus.OK) return;
            double offsetDist = pdr.Value;

            PromptKeywordOptions pko =
                new PromptKeywordOptions("\nEnter the offset side [In/Out/Left/Right/Both]", "In Out Left Right Both");
            PromptResult pr = ed.GetKeywords(pko);
            if (pr.Status != PromptStatus.OK) return;
            PolylineExtension.OffsetSide side;
            switch (pr.StringResult)
            {
                case "In": side = PolylineExtension.OffsetSide.In; break;
                case "Out": side = PolylineExtension.OffsetSide.Out; break;
                case "Left": side = PolylineExtension.OffsetSide.Left; break;
                case "Right": side = PolylineExtension.OffsetSide.Right; break;
                default: side = PolylineExtension.OffsetSide.Both; break;
            }

            PromptEntityOptions peo = new PromptEntityOptions("\nSelect a polyline: ");
            peo.SetRejectMessage("Only a polyline !");
            peo.AddAllowedClass(typeof(Polyline), true);

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
                while (true)
                {
                    PromptEntityResult per = ed.GetEntity(peo);
                    if (per.Status != PromptStatus.OK) break;

                    Polyline pline = (Polyline)tr.GetObject(per.ObjectId, OpenMode.ForRead);
                    foreach (Polyline pl in pline.Offset(offsetDist, side))
                    {
                        btr.AppendEntity(pl);
                        tr.AddNewlyCreatedDBObject(pl, true);
                    }
                    db.TransactionManager.QueueForGraphicsFlush();
                }
                tr.Commit();
            }
        }
    }
}
« Last Edit: March 17, 2013, 10:24:47 AM by (gile) »

Offline bcinnv

  • Newbie
  • *
  • Posts: 9
  • Karma: +0/-0
    • prefered language: VB
    • Prog expertise: Good
    • View Profile
Re: Offset curve toward centroid
« Reply #3 on: March 18, 2013, 02:23:53 AM »
YOU GUYS ROCK!

Offline (gile)

  • C#
  • *
  • Posts: 87
  • Karma: +8/-0
  • Gender: Male
    • prefered language: F
    • Prog expertise: Good
    • View Profile
Re: Offset curve toward centroid
« Reply #4 on: March 20, 2013, 07:11:04 PM »
According to Tony Tanzillo's advices at TheSwamp, here's a safer way because of the using of DisposableSet class to insure the newly created polylines to be diposed in case an exception is thrown.

Code: [Select]
using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.AutoCAD.DatabaseServices;

// With the help of Tony Tanzillo's advices
// http://www.theswamp.org/index.php?topic=31862.msg494503#msg494503

namespace OffsetPolylineSample
{
    /// <summary>
    /// Provides the Offset() extension method for the Polyline type
    /// </summary>
    public static class PolylineExtension
    {
        /// <summary>
        /// Enumeration of offset side options
        /// </summary>
        public enum OffsetSide
        {
            In, Out, Left, Right, Both
        }

        /// <summary>
        /// Offset the source polyline to specified side(s).
        /// </summary>
        /// <param name="source">The polyline to be offseted.</param>
        /// <param name="offsetDist">The offset distance.</param>
        /// <param name="side">The offset side(s).</param>
        /// <returns>A polyline sequence resulting from the offset of the source polyline.</returns>
        public static IEnumerable<Polyline> Offset(this Polyline source, double offsetDist, OffsetSide side)
        {
            offsetDist = Math.Abs(offsetDist);
            using (var plines = new DisposableSet<Polyline>())
            {
                IEnumerable<Polyline> offsetRight = source.GetOffsetCurves(offsetDist).Cast<Polyline>();
                plines.UnionWith(offsetRight);
                IEnumerable<Polyline> offsetLeft = source.GetOffsetCurves(-offsetDist).Cast<Polyline>();
                plines.UnionWith(offsetLeft);
                double areaRight = offsetRight.Select(pline => pline.Area).Sum();
                double areaLeft = offsetLeft.Select(pline => pline.Area).Sum();
                switch (side)
                {
                    case OffsetSide.In:
                        return plines.RemoveRange(
                           areaRight < areaLeft ? offsetRight : offsetLeft);
                    case OffsetSide.Out:
                        return plines.RemoveRange(
                           areaRight < areaLeft ? offsetLeft : offsetRight);
                    case OffsetSide.Left:
                        return plines.RemoveRange(offsetLeft);
                    case OffsetSide.Right:
                        return plines.RemoveRange(offsetRight);
                    case OffsetSide.Both:
                        plines.Clear();
                        return offsetRight.Concat(offsetLeft);
                    default:
                        return null;
                }
            }
        }
    }

    /// <summary>
    /// Represents a set of disposable values.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class DisposableSet<T> : HashSet<T>, IDisposable
        where T : IDisposable
    {
        /// <summary>
        /// Disposes all items of the current DisposableSet object.
        /// </summary>
        public void Dispose()
        {
            if (base.Count > 0)
            {
                System.Exception last = null;
                foreach (T item in this)
                {
                    if (item != null)
                    {
                        try
                        {
                            item.Dispose();
                        }
                        catch (System.Exception ex)
                        {
                            last = last ?? ex;
                        }
                    }
                }
                this.Clear();
                if (last != null)
                    throw last;
            }
        }

        /// <summary>
        /// Removes all elements in the specified collection from the current DisposableSet object.
        /// </summary>
        /// <param name="items">The collection of items to remove from the current DisposableSet object.</param>
        /// <returns>The collection of items to remove.</returns>
        public IEnumerable<T> RemoveRange(IEnumerable<T> items)
        {
            base.ExceptWith(items);
            return items;
        }
    }
}