Author Topic: Offset polyline rectangle on each side  (Read 4834 times)

0 Members and 1 Guest are viewing this topic.

sachinpise

  • Guest
Offset polyline rectangle on each side
« on: December 17, 2010, 10:20:16 AM »
Hello Experts...

This is my first time. I usually get all my answers searching in forums thanks to all the inputs from all the users.

I am stuck with this issue for long time and I don't think I can find the solution myself.

I have a rectangle formed with Polyline. User selects the polyline object and window pops us with all the sides displayed. User can not enter the different values for each side and I want to able to offset each side with different value. It is more easy to understand my problem seeing in attached image.

I would really appreciate any help.

Regards,
Sachin

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Offset polyline rectangle on each side
« Reply #1 on: December 17, 2010, 11:17:13 AM »
First thing that comes to mind is to explode the ployline, then offset each line to what value entered, then erase original polyine and build new polyline from lines

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Offset polyline rectangle on each side
« Reply #2 on: December 17, 2010, 11:55:24 AM »
Or probably eaiser to adjust the linesegments

sachinpise

  • Guest
Re: Offset polyline rectangle on each side
« Reply #3 on: December 17, 2010, 01:15:23 PM »
Or probably eaiser to adjust the linesegments

I thought about it. But I do not know how I can move a particular linesegment in right direction. If you could give some more details it would really help. I am a quite new to AutoCAD.NET programming.

Regards,
Sachin Pise

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: Offset polyline rectangle on each side
« Reply #4 on: December 17, 2010, 05:35:24 PM »

If you post your complete code 'someone'  may be able to have a look at it.
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

Bryco

  • Water Moccasin
  • Posts: 1883
Re: Offset polyline rectangle on each side
« Reply #5 on: December 17, 2010, 07:23:36 PM »
I think the code below will give you all the info you need, it uses vectors rather than angles.

Code: [Select]
    [CommandMethod("AddRectangle")]
        public void AddRectangle()
        {
            Document doc = acadApp.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
            Database db = HostApplicationServices.WorkingDatabase;
            Matrix3d ucs = ed.CurrentUserCoordinateSystem;
            CoordinateSystem3d Cs = ucs.CoordinateSystem3d;
            Plane pn = new Plane(Point3d.Origin, Cs.Zaxis);
            Vector3d normal = Cs.Zaxis;
            //First point
            PromptPointResult Pres = ed.GetPoint("\nPick the first point:");
            if (Pres.Status != PromptStatus.OK) return;
            Point3d U1 = Pres.Value;

            //Second point
            PromptPointOptions PPO = new PromptPointOptions
                ("\nPick the second point:");
            PPO.UseBasePoint = true;
            PPO.BasePoint = U1;
            Pres = ed.GetPoint(PPO);

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

            Point3d U2 = Pres.Value;
            if (U1.Z != U2.Z) U2 = new Point3d(U2.X, U2.Y, U1.Z);

            //Add pline
            Polyline oPline = new Polyline(7);
            oPline.Normal = Cs.Zaxis;
            oPline.Elevation = -new Plane(Cs.Origin, Cs.Zaxis).Coefficients.D;
            if (U1.Z != 0) oPline.Elevation = oPline.Elevation + U1.Z;

            Point2d P1 = U1.TransformBy(ucs).Convert2d(pn);
            Point2d P2 = U2.TransformBy(ucs).Convert2d(pn);
            oPline.AddVertexAt(0, P1, 0, 0, 0);
            oPline.AddVertexAt(1, P2, 0, 0, 0);
            Vector2d v = (P2 - P1).GetNormal().RotateBy(Math.PI * 0.5);

            using (Transaction tr = doc.Database.TransactionManager.StartTransaction())
            {
                BlockTableRecord Cspace = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                Cspace.AppendEntity(oPline);
                tr.AddNewlyCreatedDBObject(oPline, true);
                tr.Commit();
            }

            double CurOffset = (double)acadApp.GetSystemVariable("OFFSETDIST");
            if (CurOffset < 0) CurOffset = CurOffset * -1;
            PromptDistanceOptions PD = new PromptDistanceOptions
                ("\nSpecify offset distance: ");
            PD.DefaultValue = CurOffset;
            Double dOffset = ed.GetDistance(PD).Value;
            if (dOffset < 0) dOffset = dOffset * -1;
            if (dOffset != CurOffset)
                ed.WriteMessage("\nOffset distance=" + dOffset);
        Retry:
            Pres = ed.GetPoint("\nSpecify point on side to offset:");
            if (Pres.Status != PromptStatus.OK) return;
            int iOffset = isLeft(P1, P2, Pres.Value.TransformBy(ucs).Convert2d(pn));
            if (iOffset == 0)  //means all points are linear
            {
                MessageBox.Show("The offset point must not be on the line:");
                goto Retry;
            }

            v = v * iOffset * dOffset;
            using (Transaction tr = doc.Database.TransactionManager.StartTransaction())
            {
                oPline = tr.GetObject(oPline.Id, OpenMode.ForWrite) as Polyline;
                oPline.AddVertexAt(2, P2.Add(v), 0, 0, 0);
                oPline.AddVertexAt(3, P1.Add(v), 0, 0, 0);
                oPline.Closed = true;
                tr.Commit();
            }

            acadApp.SetSystemVariable("OFFSETDIST", Math.Abs(dOffset));


        } //End AddRectangle



        private static int isLeftMath(Point2d Startpoint, Point2d Endpoint, Point2d P)
        {
            double Ans = ((Endpoint.X - Startpoint.X) * (P.Y - Startpoint.Y) -
              (P.X - Startpoint.X) * (Endpoint.Y - Startpoint.Y));
            if (Math.Abs(Ans) < 1.0e-8)
            { return 0; } //P is on the line
            else
            {
                if (Ans > 0)
                { return 1; } //P is left of the line (CW)
                else
                { return -1; } //P is right of the line (CCW)
            }
        }

sachinpise

  • Guest
Re: Offset polyline rectangle on each side
« Reply #6 on: December 18, 2010, 01:21:08 AM »
Hi Bryco

You have been savior. I used your code and changed according to my needs and it is working perfect. Thanks a lot.

Regards,
Sachin Pise


I think the code below will give you all the info you need, it uses vectors rather than angles.

Code: [Select]
    [CommandMethod("AddRectangle")]
        public void AddRectangle()
        {
            Document doc = acadApp.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
            Database db = HostApplicationServices.WorkingDatabase;
            Matrix3d ucs = ed.CurrentUserCoordinateSystem;
            CoordinateSystem3d Cs = ucs.CoordinateSystem3d;
            Plane pn = new Plane(Point3d.Origin, Cs.Zaxis);
            Vector3d normal = Cs.Zaxis;
            //First point
            PromptPointResult Pres = ed.GetPoint("\nPick the first point:");
            if (Pres.Status != PromptStatus.OK) return;
            Point3d U1 = Pres.Value;

            //Second point
            PromptPointOptions PPO = new PromptPointOptions
                ("\nPick the second point:");
            PPO.UseBasePoint = true;
            PPO.BasePoint = U1;
            Pres = ed.GetPoint(PPO);

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

            Point3d U2 = Pres.Value;
            if (U1.Z != U2.Z) U2 = new Point3d(U2.X, U2.Y, U1.Z);

            //Add pline
            Polyline oPline = new Polyline(7);
            oPline.Normal = Cs.Zaxis;
            oPline.Elevation = -new Plane(Cs.Origin, Cs.Zaxis).Coefficients.D;
            if (U1.Z != 0) oPline.Elevation = oPline.Elevation + U1.Z;

            Point2d P1 = U1.TransformBy(ucs).Convert2d(pn);
            Point2d P2 = U2.TransformBy(ucs).Convert2d(pn);
            oPline.AddVertexAt(0, P1, 0, 0, 0);
            oPline.AddVertexAt(1, P2, 0, 0, 0);
            Vector2d v = (P2 - P1).GetNormal().RotateBy(Math.PI * 0.5);

            using (Transaction tr = doc.Database.TransactionManager.StartTransaction())
            {
                BlockTableRecord Cspace = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                Cspace.AppendEntity(oPline);
                tr.AddNewlyCreatedDBObject(oPline, true);
                tr.Commit();
            }

            double CurOffset = (double)acadApp.GetSystemVariable("OFFSETDIST");
            if (CurOffset < 0) CurOffset = CurOffset * -1;
            PromptDistanceOptions PD = new PromptDistanceOptions
                ("\nSpecify offset distance: ");
            PD.DefaultValue = CurOffset;
            Double dOffset = ed.GetDistance(PD).Value;
            if (dOffset < 0) dOffset = dOffset * -1;
            if (dOffset != CurOffset)
                ed.WriteMessage("\nOffset distance=" + dOffset);
        Retry:
            Pres = ed.GetPoint("\nSpecify point on side to offset:");
            if (Pres.Status != PromptStatus.OK) return;
            int iOffset = isLeft(P1, P2, Pres.Value.TransformBy(ucs).Convert2d(pn));
            if (iOffset == 0)  //means all points are linear
            {
                MessageBox.Show("The offset point must not be on the line:");
                goto Retry;
            }

            v = v * iOffset * dOffset;
            using (Transaction tr = doc.Database.TransactionManager.StartTransaction())
            {
                oPline = tr.GetObject(oPline.Id, OpenMode.ForWrite) as Polyline;
                oPline.AddVertexAt(2, P2.Add(v), 0, 0, 0);
                oPline.AddVertexAt(3, P1.Add(v), 0, 0, 0);
                oPline.Closed = true;
                tr.Commit();
            }

            acadApp.SetSystemVariable("OFFSETDIST", Math.Abs(dOffset));


        } //End AddRectangle



        private static int isLeftMath(Point2d Startpoint, Point2d Endpoint, Point2d P)
        {
            double Ans = ((Endpoint.X - Startpoint.X) * (P.Y - Startpoint.Y) -
              (P.X - Startpoint.X) * (Endpoint.Y - Startpoint.Y));
            if (Math.Abs(Ans) < 1.0e-8)
            { return 0; } //P is on the line
            else
            {
                if (Ans > 0)
                { return 1; } //P is left of the line (CW)
                else
                { return -1; } //P is right of the line (CCW)
            }
        }

kaefer

  • Guest
Re: Offset polyline rectangle on each side
« Reply #7 on: December 18, 2010, 03:18:54 AM »
You have been savior. I used your code and changed according to my needs and it is working perfect. Thanks a lot.

Care to show us what you created out of Bryco's work?

Anyway, another admittedly halfbaked approach might be the oldfashioned explode/offset/intersect/recreate pattern, which I remember doing in lisp a long time ago  The idea is mapping an array of offsets onto the polyline, allowing bulges to persist if the three segments before, at and after the bulge have the same offset. Something like this, well, it's in F#.
Code: [Select]
    let offsets = [| 5.; 10.; 15.; 20. |]

    let doc = acApp.DocumentManager.MdiActiveDocument
    let db = doc.Database
    let ed = doc.Editor

    let peo =
        new PromptEntityOptions(
            "\nSelect object: ",
            AllowNone = false )
    peo.SetRejectMessage("\nNot a Polyline.")
    peo.AddAllowedClass(typeof<Polyline>, false)

    let per = ed.GetEntity peo
    if per.Status = PromptStatus.OK then
   
        use tr = db.TransactionManager.StartTransaction()
        let pl = per.ObjectId.GetObject OpenMode.ForRead :?> Polyline
        // Positive dir (inside) when polyline is CCW, for definition of getAreaDMD
        // see http://www.theswamp.org/index.php?topic=31861.msg386487#msg386487
        let dir = if getAreaDMD pl <= 0. then 1. else -1.
        // Vertices collection
        use pts = new Point3dCollection()
        for i = 0 to pl.NumberOfVertices - 1 do
            pts.Add(pl.GetPoint3dAt i) |> ignore
        // Explode the polyline
        use dbc = new DBObjectCollection()
        pl.Explode dbc
        // Try to get offset curves up to number of edges - 1
        let maxEdge = pl.NumberOfVertices - if pl.Closed then 1 else 2
        for i = 0 to min maxEdge ((Array.length offsets) - 1) do
            try
                dbc.[i] <- (dbc.[i] :?> Curve).GetOffsetCurves(offsets.[i] * dir).[0]
            with _ -> ()
        // Get intersection points
        use ptstemp = new Point3dCollection()
        for i = 0 to dbc.Count - 1 do
            if i <> 0 || pl.Closed then
                let j = if i = 0 then dbc.Count - 1 else i - 1
                (dbc.[j] :?> Entity).IntersectWith(dbc.[i] :?> Entity, Intersect.ExtendBoth, ptstemp, 1, 1)
                pts.[i] <- ptstemp.[0]
                ptstemp.Clear()
        // Create new polyline
        let npl = new Polyline()
        npl.SetDatabaseDefaults()
        for i = 0 to pts.Count - 1 do
            npl.AddVertexAt(i, new Point2d(pts.[i].X, pts.[i].Y), pl.GetBulgeAt i, pl.GetStartWidthAt i, pl.GetEndWidthAt i)
        npl.Closed <- pl.Closed
        let btr = tr.GetObject(pl.OwnerId, OpenMode.ForWrite) :?> BlockTableRecord
        btr.AppendEntity npl |> ignore
        tr.AddNewlyCreatedDBObject(npl, true)
        tr.Commit()


sachinpise

  • Guest
Re: Offset polyline rectangle on each side
« Reply #8 on: December 18, 2010, 04:49:16 AM »
Hi kaefer

Actually this tool is for distrubuting Hollowcore slabs in the selected room. Now in room for each column user can specify custom offset. So using Bryco's suggestions. I show user form once the column polyline is selected and user can input each side offset (usually it is only 2 sides since column is at any corner of room) and also user defines the point of direction and once pressed ok I use the following code to create new polyline uniting regions from all the polylines that are created for each side.
Code: [Select]
var ed = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor;
            var db = HostApplicationServices.WorkingDatabase;
            var ucs = ed.CurrentUserCoordinateSystem;
            var cs = ucs.CoordinateSystem3d;
            var pn = new Plane(Point3d.Origin, cs.Zaxis);
            var customColumn = this.rhCustomColumn.Element.AsObject as CustomColumn;
            var vertices = customColumn.Vertices.Where(x => x.Offset > 0 && x.DPX != null && x.DPY != null);
            using (var transaction = db.TransactionManager.StartTransaction())
            {
                var polyline =
                        (Polyline)
                        transaction.GetObject(ArxUtility.GetObjectId(customColumn.Handle2), OpenMode.ForWrite);
                var regionList = new List<Autodesk.AutoCAD.DatabaseServices.Region>();
                foreach (var cv in vertices)
                {
                    var u1 = new Point3d(cv.SP2X == null ? cv.StartPointX : cv.SP2X.Value,
                                         cv.SP2Y == null ? cv.StartPointY : cv.SP2Y.Value, 0.0);
                    var u2 = new Point3d(cv.EP2X == null ? cv.EndPointX : cv.EP2X.Value,
                                         cv.EP2Y == null ? cv.EndPointY : cv.EP2Y.Value, 0.0);
                    if (u1.Z != u2.Z)
                        u2 = new Point3d(u2.X, u2.Y, u1.Z);
                    //Add pline
                    var nPolyline = new Polyline(7) { Normal = cs.Zaxis, Elevation = -new Plane(cs.Origin, cs.Zaxis).Coefficients.D };
                    if (u1.Z != 0)
                        nPolyline.Elevation = nPolyline.Elevation + u1.Z;
                    var p1 = u1.TransformBy(ucs).Convert2d(pn);
                    var p2 = u2.TransformBy(ucs).Convert2d(pn);
                    nPolyline.AddVertexAt(0, p1, 0, 0, 0);
                    nPolyline.AddVertexAt(1, p2, 0, 0, 0);
                    var v = (p2 - p1).GetNormal().RotateBy(Math.PI * 0.5);
                    var dOffset = cv.Offset;
                    if (dOffset < 0)
                        dOffset = dOffset * -1;
                    var sidePoint = new Point3d(cv.DPX.Value, cv.DPY.Value, 0.0);
                    var iOffset = this.isLeftMath(p1, p2, sidePoint.TransformBy(ucs).Convert2d(pn));
                    v = v * iOffset * dOffset;
                    var pt1 = p1.Add(v);
                    var pt2 = p2.Add(v);
                    nPolyline.AddVertexAt(2, p2.Add(v), 0, 0, 0);
                    nPolyline.AddVertexAt(3, p1.Add(v), 0, 0, 0);
                    nPolyline.Closed = true;
                    var nRegion = RegionManager.Convert2Region(nPolyline as Curve);
                    regionList.Add(nRegion);
                    this.ChangePoints(customColumn, cv, pt1, pt2);
                }
                var oRegion = RegionManager.Convert2Region(polyline as Curve);
                foreach (var region in regionList)
                    oRegion.BooleanOperation(BooleanOperationType.BoolUnite, region);
                var finalPolyline = EntityManager.GeneratePolyline(oRegion, true)[0];
                finalPolyline.Layer = "_Obstructions2";
                var id = BlockManager.AddEntity(finalPolyline);
                EntityManager.SendEntitiesBackWard(transaction, new ObjectIdCollection(new ObjectId[] { id }));
                EntityManager.DeleteEntity(ArxUtility.GetObjectId(customColumn.Handle2), transaction);
                customColumn.Handle2 = id.Handle.ToString();
                transaction.Commit();
            }
            ed.Regen();


But now I will look at what you have shown.
Somehow I don't want to take the direction from the user. (i.e. I want to avoid following code from Bryco's suggestions)
Code: [Select]
Pres = ed.GetPoint("\nSpecify point on side to offset:");
            if (Pres.Status != PromptStatus.OK) return;
            int iOffset = isLeft(P1, P2, Pres.Value.TransformBy(ucs).Convert2d(pn));
            if (iOffset == 0)  //means all points are linear
            {
                MessageBox.Show("The offset point must not be on the line:");
                goto Retry;
            }


« Last Edit: December 18, 2010, 04:52:39 AM by Sachin Pise »

sachinpise

  • Guest
Re: Offset polyline rectangle on each side
« Reply #9 on: December 18, 2010, 05:54:36 AM »
Hi Kaefer

I just converted your F# code to C#. (It was more like Da Vinci Code). And I exactly got my answer. Now I don't need to take Direction point from the user. And it is more easy and perfect solution.

Thanks a millions.

Sachin Pise



You have been savior. I used your code and changed according to my needs and it is working perfect. Thanks a lot.

Care to show us what you created out of Bryco's work?

Anyway, another admittedly halfbaked approach might be the oldfashioned explode/offset/intersect/recreate pattern, which I remember doing in lisp a long time ago  The idea is mapping an array of offsets onto the polyline, allowing bulges to persist if the three segments before, at and after the bulge have the same offset. Something like this, well, it's in F#.
Code: [Select]
    let offsets = [| 5.; 10.; 15.; 20. |]

    let doc = acApp.DocumentManager.MdiActiveDocument
    let db = doc.Database
    let ed = doc.Editor

    let peo =
        new PromptEntityOptions(
            "\nSelect object: ",
            AllowNone = false )
    peo.SetRejectMessage("\nNot a Polyline.")
    peo.AddAllowedClass(typeof<Polyline>, false)

    let per = ed.GetEntity peo
    if per.Status = PromptStatus.OK then
   
        use tr = db.TransactionManager.StartTransaction()
        let pl = per.ObjectId.GetObject OpenMode.ForRead :?> Polyline
        // Positive dir (inside) when polyline is CCW, for definition of getAreaDMD
        // see http://www.theswamp.org/index.php?topic=31861.msg386487#msg386487
        let dir = if getAreaDMD pl <= 0. then 1. else -1.
        // Vertices collection
        use pts = new Point3dCollection()
        for i = 0 to pl.NumberOfVertices - 1 do
            pts.Add(pl.GetPoint3dAt i) |> ignore
        // Explode the polyline
        use dbc = new DBObjectCollection()
        pl.Explode dbc
        // Try to get offset curves up to number of edges - 1
        let maxEdge = pl.NumberOfVertices - if pl.Closed then 1 else 2
        for i = 0 to min maxEdge ((Array.length offsets) - 1) do
            try
                dbc.[i] <- (dbc.[i] :?> Curve).GetOffsetCurves(offsets.[i] * dir).[0]
            with _ -> ()
        // Get intersection points
        use ptstemp = new Point3dCollection()
        for i = 0 to dbc.Count - 1 do
            if i <> 0 || pl.Closed then
                let j = if i = 0 then dbc.Count - 1 else i - 1
                (dbc.[j] :?> Entity).IntersectWith(dbc.[i] :?> Entity, Intersect.ExtendBoth, ptstemp, 1, 1)
                pts.[i] <- ptstemp.[0]
                ptstemp.Clear()
        // Create new polyline
        let npl = new Polyline()
        npl.SetDatabaseDefaults()
        for i = 0 to pts.Count - 1 do
            npl.AddVertexAt(i, new Point2d(pts.[i].X, pts.[i].Y), pl.GetBulgeAt i, pl.GetStartWidthAt i, pl.GetEndWidthAt i)
        npl.Closed <- pl.Closed
        let btr = tr.GetObject(pl.OwnerId, OpenMode.ForWrite) :?> BlockTableRecord
        btr.AppendEntity npl |> ignore
        tr.AddNewlyCreatedDBObject(npl, true)
        tr.Commit()