why segment of curves created by region boundary is missing

why segment of curves created by region boundary is missing

Brainstormcn
Contributor Contributor
1,393 Views
10 Replies
Message 1 of 11

why segment of curves created by region boundary is missing

Brainstormcn
Contributor
Contributor
[CommandMethod("testregion")]
        public void RegionSubstr()
        {
            var doc = Application.DocumentManager.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;
 
            //var ent1 = ed.GetEntity("\nSelect Curve");
            //建立选择集过滤
            TypedValueList tv = new TypedValueList();
            tv.Add(0, "*line");
            SelectionFilter fil = new SelectionFilter(tv);
            PromptSelectionOptions pso = new PromptSelectionOptions();
            pso.MessageForAdding = "\n select curves:";
            PromptSelectionResult psr = ed.GetSelection(pso, fil);
 
            if (psr.Status != PromptStatus.OK)
            {
                ed.WriteMessage("\n selection is null!");
                return;
            }
            PromptEntityResult ent = ed.GetEntity("\nselect curve to be substracted:");
 
            using (var tr = db.TransactionManager.StartTransaction())
            {
                BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
                ObjectId oId = ent.ObjectId;
                Entity entity2 = tr.GetObject(oId, OpenMode.ForWrite) as Entity;
                var curveSegments = new DBObjectCollection();
                foreach (var id in psr.Value.GetObjectIds())
                {
                    curveSegments.Add(tr.GetObject(id, OpenMode.ForRead) as Entity);
                }
 
                var regions = Region.CreateFromCurves(curveSegments);
                var region1 = regions[0] as Region;
                var region2 = Region.CreateFromCurves(new DBObjectCollection { entity2 })[0] as Region;
 
                if (region1 != null)
                {
                    region1.BooleanOperation(BooleanOperationType.BoolSubtract, region2);
 
                    Brep brep = new Brep(region1);
                    BrepFaceCollection faces = brep.Faces;
                    BoundaryLoop boundaryLoop = faces.First().Loops.First();
                    Edge[] boundaryEdges = boundaryLoop.Edges.ToArray();
 
                    Curve3d[] cv3 = new Curve3d[boundaryEdges.Count()];
                    for (int i = 0; i < boundaryEdges.Count(); i++)
                    {
                        cv3[i] = (boundaryEdges[i].Curve as ExternalCurve3d).NativeCurve;
                    }
                    CompositeCurve3d cc3 = new CompositeCurve3d(cv3);
                    var newCv1 = Curve.CreateFromGeCurve(cc3);
                    newCv1.ColorIndex = 1;
                    btr.AppendEntity(newCv1);
                    tr.AddNewlyCreatedDBObject(newCv1, true);
                }
                tr.Commit();
            }
        }
0 Likes
Accepted solutions (2)
1,394 Views
10 Replies
Replies (10)
Message 2 of 11

_gile
Consultant
Consultant

Hi,

I'ts impossible to reply you without seeing a drawing (i.e. what contains the selection set and what is the selected entity).
Anyway, your code is only interested in the first loop of the face, it does not take into account a possible island in the region.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 3 of 11

Brainstormcn
Contributor
Contributor

Hi gile,thans for your reply. Here I attached the drawing with problems and the reviesed tread.Pls kindly help me to find what causing the segement missing.

 

booloperation.jpg

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

[CommandMethod("TESTREGION")]
        public void RegionSubstr()
        {
            var doc = Application.DocumentManager.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;

            //selection filter
            TypedValue[] filter = new TypedValue[] { new TypedValue(0, "*line" )};
            SelectionFilter fil = new SelectionFilter(filter);

            PromptSelectionOptions pso = new PromptSelectionOptions();
            pso.MessageForAdding = "\n select curves:";
            PromptSelectionResult psr = ed.GetSelection(pso, fil);
            if (psr.Status != PromptStatus.OK)
            {
                ed.WriteMessage("\n selection is empty!");
                return;
            }

            PromptEntityResult promptEntityResult = ed.GetEntity("\nselect curve to be substracted:");
            if (promptEntityResult.Status != PromptStatus.OK) return;
            using (var tr = db.TransactionManager.StartTransaction())
            {
                BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
                ObjectId oId = promptEntityResult.ObjectId;
                Entity ent = tr.GetObject(oId, OpenMode.ForWrite) as Entity;

                var curveSegments = new DBObjectCollection();
                foreach (var id in psr.Value.GetObjectIds())
                {
                    curveSegments.Add(tr.GetObject(id, OpenMode.ForRead) as Entity);
                }
                //make regions form curveSegments
                var regions = Region.CreateFromCurves(curveSegments);                
                //region to substrcat from
                var regionSubstrcatFrom = regions[0] as Region;
                //region to substrcat 
                var regionSubstrcat = Region.CreateFromCurves(new DBObjectCollection { ent })[0] as Region;

                if (regionSubstrcatFrom != null)
                {
                    regionSubstrcatFrom.BooleanOperation(BooleanOperationType.BoolSubtract, regionSubstrcat);
                    Brep brep = new Brep(regionSubstrcatFrom);
                    BrepFaceCollection faces = brep.Faces;
                    BoundaryLoop boundaryLoop = faces.First().Loops.First();
                    Edge[] boundaryEdges = boundaryLoop.Edges.ToArray();
                    Curve3d[] cv3 = new Curve3d[boundaryEdges.Count()];
                    for (int i = 0; i < boundaryEdges.Count(); i++)
                    {
                        cv3[i] = (boundaryEdges[i].Curve as ExternalCurve3d).NativeCurve;
                    }
                    CompositeCurve3d cc3 = new CompositeCurve3d(cv3);
                    var newCv1 = Curve.CreateFromGeCurve(cc3);
                    newCv1.ColorIndex = 1;
                    btr.AppendEntity(newCv1);
                    tr.AddNewlyCreatedDBObject(newCv1, true);
                }
                tr.Commit();
            }
        }

 

 

 

 

0 Likes
Message 4 of 11

Brainstormcn
Contributor
Contributor

Here is the problem drawing.

0 Likes
Message 5 of 11

_gile
Consultant
Consultant
Accepted solution

There's a missing segment because the generated polyline is not closed (Closed properrty).

Beside this, your code is not very safe because you do not check the number of created regions or loops and do not dispose them.

Here's a more robust implementation.

        [CommandMethod("TEST")]
        public static void Test()
        {
            var doc = Application.DocumentManager.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;

            var filter = new SelectionFilter(new[] { new TypedValue(0, "LINE,LWPOLYLINE") });
            var pso = new PromptSelectionOptions();
            pso.MessageForAdding = "\nSelect curves: ";
            var psr = ed.GetSelection(pso, filter);
            if (psr.Status != PromptStatus.OK)
                return;
            using (var tr = db.TransactionManager.StartTransaction())
            {
                if (!TryCreateRegion(psr.Value.GetObjectIds(), tr, out Region region1))
                {
                    ed.WriteMessage("\nFailed to create a region.");
                    return;
                }
                using (region1)
                {
                    var peo = new PromptEntityOptions("\nSelect a curve to be substracted: ");
                    peo.SetRejectMessage("\nMust be a curve.");
                    peo.AddAllowedClass(typeof(Curve), false);
                    var per = ed.GetEntity(peo);
                    if (per.Status != PromptStatus.OK)
                        return;
                    if (!TryCreateRegion(new[] { per.ObjectId }, tr, out Region region2))
                    {
                        ed.WriteMessage("\nFailed to create a region.");
                        return;
                    }
                    using (region2)
                    {
                        var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
                        region1.BooleanOperation(BooleanOperationType.BoolSubtract, region2);
                        if (TryCreatePolylineFromRegion(region1, out Polyline pline))
                        {
                            region1.Dispose();
                            pline.ColorIndex = 3;
                            curSpace.AppendEntity(pline);
                            tr.AddNewlyCreatedDBObject(pline, true);
                        }
                        else
                        {
                            region1.ColorIndex = 1;
                            curSpace.AppendEntity(region1);
                            tr.AddNewlyCreatedDBObject(region1, true);
                        }
                    }
                }
                tr.Commit();
            }
        }

        private static bool TryCreateRegion(ObjectId[] curveIds, Transaction tr, out Region region)
        {
            region = null;
            var curveSegments = new DBObjectCollection();
            foreach (ObjectId id in curveIds)
            {
                curveSegments.Add(tr.GetObject(id, OpenMode.ForRead));
            }
            var regions = Region.CreateFromCurves(curveSegments);
            if (regions.Count != 1)
            {
                if (1 < regions.Count)
                {
                    foreach (DBObject dBObject in regions)
                    {
                        dBObject.Dispose();
                    }
                }
                return false;
            }
            region = (Region)regions[0];
            return true;
        }

        private static bool TryCreatePolylineFromRegion(Region region, out Polyline pline)
        {
            pline = null;
            using (var brep = new Brep(region))
            {
                var face = brep.Faces.First();
                if (1 < face.Loops.Count())
                    return false;
                var loop = face.Loops.First();
                var compositeCurve = new CompositeCurve3d(
                    loop.Edges
                    .Select(e => ((ExternalCurve3d)e.Curve).NativeCurve)
                    .ToArray());
                var curve = Curve.CreateFromGeCurve(compositeCurve);
                if (curve is Polyline)
                {
                    pline = (Polyline)curve;
                    pline.Closed = true;
                    return true;
                }
                return false;
            }
        }

 

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 6 of 11

_gile
Consultant
Consultant

Here's another implementation which makes an union of the regions created from the first selection.

        private static bool TryCreateRegion(ObjectId[] curveIds, Transaction tr, out Region region)
        {
            region = null;
            var curveSegments = new DBObjectCollection();
            foreach (ObjectId id in curveIds)
            {
                curveSegments.Add(tr.GetObject(id, OpenMode.ForRead));
            }
            var regions = Region.CreateFromCurves(curveSegments);
            if (regions.Count == 0)
                return false;
            region = (Region)regions[0];
            for (int i = 1; i < regions.Count; i++)
            {
                region.BooleanOperation(BooleanOperationType.BoolUnite, (Region)regions[i]);
                regions[i].Dispose();
            }
            return true;
        }

        private static bool TryCreatePolylineFromRegion(Region region, out Polyline pline)
        {
            pline = null;
            using (var brep = new Brep(region))
            {
                if (1 < brep.Complexes.Count())
                    return false;
                var face = brep.Faces.First();
                if (1 < face.Loops.Count())
                    return false;
                var loop = face.Loops.First();
                var compositeCurve = new CompositeCurve3d(
                    loop.Edges
                    .Select(e => ((ExternalCurve3d)e.Curve).NativeCurve)
                    .ToArray());
                var curve = Curve.CreateFromGeCurve(compositeCurve);
                if (curve is Polyline)
                {
                    pline = (Polyline)curve;
                    pline.Closed = true;
                    return true;
                }
                return false;
            }
        }

 

_gile_0-1637141826220.png

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 7 of 11

_gile
Consultant
Consultant

I'd try to do something more generic to get the curves making up the boundaries of a region, including any islands and separate boundary. If the boundary allows it, the returned curve is a closed polyline.

using Autodesk.AutoCAD.BoundaryRepresentation;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

using System.Collections.Generic;
using System.Linq;

namespace Gile.AutoCAD.Geometry
{
    public static class Extension
    {
        /// <summary>
        /// Gets the curves constituting the boundaries of the region.
        /// </summary>
        /// <param name="region">The region this method applies to.</param>
        /// <returns>Curve collection.</returns>
        public static IEnumerable<Curve> GetCurves(this Region region)
        {
            using (var brep = new Brep(region))
            {
                var loops = brep.Complexes
                    .SelectMany(complex => complex.Shells)
                    .SelectMany(shell => shell.Faces)
                    .SelectMany(face => face.Loops);
                foreach (var loop in loops)
                {
                    var curves3d = loop.Edges
                        .Select(edge => ((ExternalCurve3d)edge.Curve).NativeCurve);
                    Curve curve;
                    if (1 < curves3d.Count())
                    {
                        if (curves3d.All(curve3d => curve3d is CircularArc3d || curve3d is LineSegment3d))
                        {
                            if (!curves3d.First().EndPoint.IsEqualTo(curves3d.Skip(1).First().StartPoint))
                            {
                                curves3d = curves3d
                                    .Reverse()
                                    .Select(curve3d => 
                                        curve3d is CircularArc3d ? 
                                        curve3d.GetReverseParameterCurve() : curve3d);
                            }
                            curve = Curve.CreateFromGeCurve(new CompositeCurve3d(curves3d.ToArray()));
                            ((Polyline)curve).Closed = true;
                            yield return curve;
                        }
                        else
                        {
                            foreach (Curve3d curve3d in curves3d)
                            {
                                yield return Curve.CreateFromGeCurve(curve3d);
                            }
                        }
                    }
                    else
                    {
                        yield return Curve.CreateFromGeCurve(curves3d.First());
                    }
                }
            }
        }
    }
}


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 8 of 11

Brainstormcn
Contributor
Contributor

Thanks _gile. Learn a lot from this thread and your other posts.like your programming characteristic of using Ling programma.

And thanks for pointing out the defects of my codes,Indeed there are many things that I dont handle.

As for this post ,I found a new problem : the  bool result is not as expected if the polyline has circular segments.

see picture below and attached the drawing .

 

regtiontest.jpg

0 Likes
Message 9 of 11

_gile
Consultant
Consultant
Accepted solution

OK, I think I fixed it.

We have order and/or reverse the curves of the loop before passing them the CompositeCurve3d constructor.

Here're the new extension methods.

using Autodesk.AutoCAD.BoundaryRepresentation;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

using System;
using System.Collections.Generic;
using System.Linq;

namespace Gile.AutoCAD.Geometry
{
    public static class Extension
    {
        /// <summary>
        /// Gets the curves constituting the boundaries of the region.
        /// </summary>
        /// <param name="region">The region this method applies to.</param>
        /// <returns>Curve collection.</returns>
        public static IEnumerable<Curve> GetCurves(this Region region)
        {
            using (var brep = new Brep(region))
            {
                var loops = brep.Complexes
                    .SelectMany(complex => complex.Shells)
                    .SelectMany(shell => shell.Faces)
                    .SelectMany(face => face.Loops);
                foreach (var loop in loops)
                {
                    var curves3d = loop.Edges.Select(edge => ((ExternalCurve3d)edge.Curve).NativeCurve);
                    if (1 < curves3d.Count())
                    {
                        if (curves3d.All(curve3d => curve3d is CircularArc3d || curve3d is LineSegment3d))
                        {
                            var pline = (Polyline)Curve.CreateFromGeCurve(new CompositeCurve3d(curves3d.ToOrderedArray()));
                            pline.Closed = true;
                            yield return pline;
                        }
                        else
                        {
                            foreach (Curve3d curve3d in curves3d)
                            {
                                yield return Curve.CreateFromGeCurve(curve3d);
                            }
                        }
                    }
                    else
                    {
                        yield return Curve.CreateFromGeCurve(curves3d.First());
                    }
                }
            }
        }

        /// <summary>
        /// Order the collection by contiguous curves ([n].EndPoint equals to [n+1].StartPoint)
        /// </summary>
        /// <param name="source">Collection this method applies to.</param>
        /// <returns>Ordered array of Curve3d.</returns>
        public static Curve3d[] ToOrderedArray(this IEnumerable<Curve3d> source)
        {
            var list = source.ToList();
            int count = list.Count;
            var array = new Curve3d[count];
            int i = 0;
            array[0] = list[0];
            list.RemoveAt(0);
            int index;
            while (i < count - 1)
            {
                var pt = array[i++].EndPoint;
                if ((index = list.FindIndex(c => c.StartPoint.IsEqualTo(pt))) != -1)
                    array[i] = list[index];
                else if ((index = list.FindIndex(c => c.EndPoint.IsEqualTo(pt))) != -1)
                    array[i] = list[index].GetReverseParameterCurve();
                else
                    throw new ArgumentException("Not contiguous curves.");
                list.RemoveAt(index);
            }
            return array;
        }
    }
}


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 10 of 11

Brainstormcn
Contributor
Contributor
Thanks _gile, it works. Method of ToOrderedArray is helpfull for me .It can be used to reorder lines / plines that are connected end-to-end.
Thansk again.
0 Likes
Message 11 of 11

Civil3DReminders_com
Mentor
Mentor

The code works great.

 

There is an issue I ran into where the GetCurves method is returning objects with an ECS that is not aligned with WCS. I had to change the ECS to WCS to be able to use the polylines and get the expected results.

 

My previous post didn't work. Here is a revised method using the GeometryExtensions from GileCAD

http://www.theswamp.org/index.php?PHPSESSID=b5a2ca0a05e981d0178d243f06127a41&topic=31865.0
http://gilecad.azurewebsites.net/Default.aspx

var mWPlane = Matrix3d.WorldToPlane(poly.Normal);

var polySegments = new PolylineSegmentCollection(poly, mWPlane);

                    var transformedPolySegments = polySegments.Join();

                    foreach (var transformedPolySegmentColl in transformedPolySegments)
                    {
                        var transformedPoly = transformedPolySegmentColl.ToPolyline();

                        joinedAreas.Add(new SubassemblyArea(transformedPoly, "Joined", "Joined"));
                    }

        /// <summary>
        /// Creates a new instance of PolylineSegmentCollection from a Polyline and converts
        /// the OCS to WCS.
        /// </summary>
        /// <param name="pline">A Polyline instance.</param>
        public PolylineSegmentCollection(Polyline pline, Matrix3d ocsToWcs)
        {
            int n = pline.NumberOfVertices - 1;
            for (int i = 0; i < n; i++)
            {
                _contents.Add(new PolylineSegment(
                    pline.GetPoint2dAt(i).ToPoint3d().TransformBy(ocsToWcs).ToPoint2d(),
                    pline.GetPoint2dAt(i + 1).ToPoint3d().TransformBy(ocsToWcs).ToPoint2d(),
                    pline.GetBulgeAt(i),
                    pline.GetStartWidthAt(i),
                    pline.GetEndWidthAt(i)));
            }
            if (pline.Closed == true)
            {
                _contents.Add(new PolylineSegment(
                    pline.GetPoint2dAt(n).ToPoint3d().TransformBy(ocsToWcs).ToPoint2d(),
                    pline.GetPoint2dAt(0).ToPoint3d().TransformBy(ocsToWcs).ToPoint2d(),
                    pline.GetBulgeAt(n),
                    pline.GetStartWidthAt(n),
                    pline.GetEndWidthAt(n)));
            }
        }

 

Civil Reminders
http://blog.civil3dreminders.com/
http://www.CivilReminders.com/
Alumni
0 Likes