Finding the two Intersection Points Between Two Endpoints of a Polyline and a different Polyline

Finding the two Intersection Points Between Two Endpoints of a Polyline and a different Polyline

tristan.jonas8XAAW
Advocate Advocate
1,191 Views
8 Replies
Message 1 of 9

Finding the two Intersection Points Between Two Endpoints of a Polyline and a different Polyline

tristan.jonas8XAAW
Advocate
Advocate
Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.EditorInput
Imports Autodesk.AutoCAD.Geometry
Imports Autodesk.AutoCAD.Runtime

Public Class IntersectionHelper

    <CommandMethod("GetIntersections")>
    Public Sub GetIntersections()
        Dim doc As Document = Application.DocumentManager.MdiActiveDocument
        Dim db As Database = doc.Database
        Dim ed As Editor = doc.Editor

        ' Select first entity
        Dim peo1 As New PromptEntityOptions("Select first polyline")
        peo1.SetRejectMessage("Entity must be a polyline.")
        peo1.AddAllowedClass(GetType(Polyline), False)
        Dim per1 As PromptEntityResult = ed.GetEntity(peo1)

        If per1.Status <> PromptStatus.OK Then
            Return
        End If

        ' Select second entity
        Dim peo2 As New PromptEntityOptions("Select second polyline")
        peo2.SetRejectMessage("Entity must be a polyline.")
        peo2.AddAllowedClass(GetType(Polyline), False)
        Dim per2 As PromptEntityResult = ed.GetEntity(peo2)

        If per2.Status <> PromptStatus.OK Then
            Return
        End If

        Dim ent1 As Entity = Nothing
        Dim ent2 As Entity = Nothing

        Using tr As Transaction = db.TransactionManager.StartTransaction()
            ent1 = tr.GetObject(per1.ObjectId, OpenMode.ForRead)
            ent2 = tr.GetObject(per2.ObjectId, OpenMode.ForRead)

            Dim intersectionPoints As Point3dCollection = GetIntersectionPoints(ent1, ent2)

            ed.WriteMessage(Environment.NewLine & "Intersection Points: ")
            For Each pt As Point3d In intersectionPoints
                ed.WriteMessage(Environment.NewLine & pt.ToString())
            Next

            tr.Commit()
        End Using
    End Sub

    Public Function GetIntersectionPoints(ent1 As Entity, ent2 As Entity) As Point3dCollection
        Dim intersectionPoints As New Point3dCollection()

        If Not TypeOf ent1 Is Curve OrElse Not TypeOf ent2 Is Curve Then
            Return intersectionPoints
        End If

        Dim curve1 As Curve = CType(ent1, Curve)
        Dim curve2 As Curve = CType(ent2, Curve)

        curve1.IntersectWith(curve2, Intersect.OnBothOperands, intersectionPoints, IntPtr.Zero, IntPtr.Zero)

        Return intersectionPoints
    End Function

    Public Function GetLineSegmentsFromEntity(ent As Entity) As List(Of LineSegment3d)
        Dim segments As New List(Of LineSegment3d)

        If TypeOf ent Is Polyline Then
            Dim pl As Polyline = CType(ent, Polyline)

            For i As Integer = 0 To pl.NumberOfVertices - 2
                Dim startPoint As Point3d = pl.GetPoint3dAt(i)
                Dim endPoint As Point3d = pl.GetPoint3dAt(i + 1)
                segments.Add(New LineSegment3d(startPoint, endPoint))
            Next

            If pl.Closed Then
                segments.Add(New LineSegment3d(pl.GetPoint3dAt(pl.NumberOfVertices - 1), pl.GetPoint3dAt(0)))
            End If
        End If

        Return segments
    End Function

End Class


I'm at my wits end with this script, I can't for the life of me figure out a way to click a polyline and then have it calculate the slope and intersection point with the nearest relevant polyline on a specific layer. I can't find a good reference on the methods that might make something like this easier, would anybody be able to tell me what's wrong with the code or at least more info on methods resources?

0 Likes
Accepted solutions (2)
1,192 Views
8 Replies
Replies (8)
Message 2 of 9

_gile
Consultant
Consultant

Hi,

You should clearly explain what you want to achieve (providing a drawing or a picture) and what's "wrong" with the code you posted.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 3 of 9

tristan.jonas8XAAW
Advocate
Advocate

Ok, here are two photos, before and after:

tristanjonas8XAAW_0-1678910656986.png

and this is what it's supposed to look like when it's done:

tristanjonas8XAAW_1-1678910719513.png


instead my script stalls because it's not detecting a theoretical intersection point. But they're all on the same z axis so they aren't flying right past each other but for some reason it's just not working.

0 Likes
Message 4 of 9

_gile
Consultant
Consultant

IntersectWith does not find intersection because you're using Intersect.OnBothOperands argument and there's none intersection point which is on both polylines.

If the red polyline is curve1, you have to use Intersect.ExtendArgument to extend the yellow polyline.

If the yellow polyline is curve1, you have to use Intersect.ExtendThis to extend it.

If you do not know which is the yellow or the red, use Intersect.ExtendBoth.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 5 of 9

tristan.jonas8XAAW
Advocate
Advocate

I'm sorry, I'm doing a very poor job of explaining, let me try a second time:

I've taken a different approach that has allowed me to get closer to the solution but not quite, the way the code works is I click a polyline, and the polyline is always only made up of arcs and line segments, all connected. Then I click a random other point and it finds the closest point on that polyline and draws a line between the points. My problem is that it seems to work with straight lines within polylines but it just ignores the arc. In working with it I have developed a suspicion for why it's not working (arc vs circulararc3d entity types) but I don't know how to fix it. Furthermore, I have other functions that work for arcs that are not a part of any polyline, so I know it's all supposed to work but I don't understand why it isn't. Anywhere here's the code:

 

Function GetClosestPointOnPolyline(point As Point3d, polyline As Polyline) As Point3d
    ' Initialize minimum distance and closest point variables
    Dim minDistance As Double = Double.MaxValue
    Dim closestPoint As Point3d = New Point3d()

    ' Loop through polyline vertices (excluding the last one)
    For i As Integer = 0 To polyline.NumberOfVertices - 2
        ' Get start and end points of the current polyline segment
        Dim startPoint As Point3d = polyline.GetPoint3dAt(i)
        Dim endPoint As Point3d = polyline.GetPoint3dAt(i + 1)
        Dim segmentClosestPoint As Point3d

        ' Check if the current segment is a line segment
        If polyline.GetSegmentType(i) = SegmentType.Line Then
            ' Create a line object and calculate the closest point on the line
            Dim line As New Line(startPoint, endPoint)
            segmentClosestPoint = GetClosestPointOnLine(point, line)
        ' Check if the current segment is an arc segment
        ElseIf polyline.GetSegmentType(i) = SegmentType.Arc Then
            ' Create a temporary Arc object using polyline segment properties
            Dim bulge As Double = polyline.GetBulgeAt(i)
            Dim arc As Arc = CreateArcFromPolylineSegment(startPoint, endPoint, bulge)
            ' Make sure the arc is valid before processing it
            If arc IsNot Nothing Then
                ' Calculate the closest point on the arc
                segmentClosestPoint = GetClosestPointOnArc(point, arc)
            Else
                ' If the arc is not valid, skip to the next polyline segment
                Continue For
            End If
        Else
            ' If the segment type is neither Line nor Arc, skip to the next polyline segment
            Continue For
        End If

        ' Calculate the distance from the input point to the current closest point on the polyline segment
        Dim distance As Double = point.DistanceTo(segmentClosestPoint)

        ' If this distance is smaller than the current minimum distance, update the closest point
        If distance < minDistance Then
            minDistance = distance
            closestPoint = segmentClosestPoint
        End If
    Next

    ' Return the closest point on the polyline
    Return closestPoint
End Function

 


And here's the function i'm using for the Arc:

 

        Function CreateArcFromPolylineSegment(startPoint As Point3d, endPoint As Point3d, bulge As Double) As Arc
            Dim arc As Arc = Nothing

            If bulge <> 0.0 Then
                ' Calculate the middle point of the chord
                Dim midPoint As Point3d = startPoint + (endPoint - startPoint) / 2.0

                ' Calculate the distance between startPoint and endPoint
                Dim chordLength As Double = startPoint.DistanceTo(endPoint)

                ' Calculate the sagitta length
                Dim sagittaLength As Double = bulge * chordLength / 2.0

                ' Calculate the radius of the circle
                Dim radius As Double = (chordLength / 2.0) / Math.Sin(bulge * Math.PI / 2.0)

                ' Calculate the circle's center
                Dim center As Point3d = midPoint + ((endPoint - startPoint).RotateBy(-Math.PI / 2.0, Vector3d.ZAxis) * sagittaLength).GetNormal() * (radius - sagittaLength)

                ' Calculate the start and end angles for the arc
                Dim startVector As Vector3d = startPoint - center
                Dim endVector As Vector3d = endPoint - center



                Dim startAngle As Double = Math.Atan2(startVector.Y, startVector.X) * (180 / Math.PI)
                Dim endAngle As Double = Math.Atan2(endVector.Y, endVector.X) * (180 / Math.PI)
                startAngle = (startAngle + 360) Mod 360
                endAngle = (endAngle + 360) Mod 360

                ' If bulge is negative, swap the start and end angles
                If bulge < 0 Then
                    Dim temp As Double = startAngle
                    startAngle = endAngle
                    endAngle = temp
                End If

                ' Create the Arc object using the calculated values
                arc = New Arc(center, radius, startAngle, endAngle)
            End If

            Return arc
        End Function

 

 

 

Function GetClosestPointOnArc(point As Point3d, arc As Arc) As Point3d
            ' Calculate the circle's equation from the arc
            Dim h As Double = arc.Center.X
            Dim k As Double = arc.Center.Y
            Dim r As Double = arc.Radius

            ' Find intersection points
            Dim intersections As List(Of Point3d) = GetIntersectionPoints(point, h, k, r)

            ' Filter intersection points that lie within the arc's start and end angles
            Dim validIntersections As List(Of Point3d) = GetValidIntersections(intersections, arc)

            ' Find the closest point on the arc to the point
            Return GetClosestPoint(point, validIntersections)
        End Function

 

0 Likes
Message 6 of 9

tristan.jonas8XAAW
Advocate
Advocate

Here is also a better screenshot, on the left is the polyline, with the arc near the bottom. The red arrow indicates where I clicked and the green dotted line shows what the line should look like when successfully recognizing the arc segments of the polyline. But the image on the right is where the line created by the script linked to on the polyline. I've also attached the full script here if you're interested in the whole thing, the command i'm testing  the function with is TestClosestPointOnPolyline.

tristanjonas8XAAW_0-1678925934724.png

 

0 Likes
Message 7 of 9

_gile
Consultant
Consultant
Accepted solution

It seems that what you want to do is not very clear in your mind.

For your last question, why not simply using the Curve.GetClosestPointTo method?

Here's a little example.

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

            var entityOptions = new PromptEntityOptions("\nSelect polyline: ");
            entityOptions.SetRejectMessage("\nSelected object is not a Polyline.");
            entityOptions.AddAllowedClass(typeof(Polyline), true);
            var entityResult = ed.GetEntity(entityOptions);
            if (entityResult.Status != PromptStatus.OK)
                return;



            using (var tr = db.TransactionManager.StartTransaction())
            {
                var pline = (Polyline)tr.GetObject(entityResult.ObjectId, OpenMode.ForRead);
                var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);

                var pointOptions = new PromptPointOptions("\nSpecify a point (or Enter to quit): ");
                pointOptions.AllowNone = true;
                while (true)
                {
                    var pointResult = ed.GetPoint(pointOptions);
                    if (pointResult.Status == PromptStatus.None)
                        break;
                    if (pointResult.Status != PromptStatus.OK)
                        return;
                    var point = pointResult.Value.TransformBy(ed.CurrentUserCoordinateSystem);
                    var line = new Line(point, pline.GetClosestPointTo(point, false));
                    curSpace.AppendEntity(line);
                    tr.AddNewlyCreatedDBObject(line, true);
                    db.TransactionManager.QueueForGraphicsFlush();
                }
                tr.Commit();
            }
        }

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 8 of 9

_gile
Consultant
Consultant

If you absolutely want to reinvent the wheel, here's a way to implement your GetClosestPointonPolyline method:

        private static Point3d GetClosestPointonPolyline(Point3d point, Polyline pline)
        {
            var composite = (CompositeCurve3d)pline.GetGeCurve();
            var segments = composite.GetCurves();
            var closestPoint = segments[0].StartPoint;
            double distance = point.DistanceTo(closestPoint);
            foreach (Curve3d curve in segments)
            {
                var pt = curve.GetClosestPointTo(point).Point;
                double d = point.DistanceTo(pt);
                if (d < distance)
                {
                    distance = d;
                    closestPoint = pt;
                }
            }
            return closestPoint;
        }

 

Or, in a more concise way with IEnumerable<T> extension methods:

        private static Point3d GetClosestPointonPolyline(Point3d point, Polyline pline) =>
            ((CompositeCurve3d)pline.GetGeCurve()).GetCurves()
            .Select(s => new { Segment = s, ClosestPoint = s.GetClosestPointTo(point).Point })
            .Aggregate((x1, x2) => point.DistanceTo(x1.ClosestPoint) < point.DistanceTo(x2.ClosestPoint) ? x1 : x2)
            .ClosestPoint;


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 9 of 9

cuongtk2
Advocate
Advocate
Accepted solution

cuong3n_0-1678960770840.png

private static Point3d PerpendicularToPline (Polyline pline , Point3d point)
        {
            Point3d result = new Point3d(double.NaN, double.NaN, double.NaN);

            Point3d pv = pline.GetClosestPointTo(point, true);

            double n = pline.GetParameterAtPoint(pv);
            int i = (int)n;

            if (pv == pline.EndPoint)
            {
                i = i - 1;
            }

            Point3d sp = pline.GetPoint3dAt(i);
            Point3d ep = pline.GetPoint3dAt(i + 1);

            if (pline.GetBulgeAt(i) != 0)
            {
                CircularArc3d ac = pline.GetArcSegmentAt(i);
                Point3d cent = ac.Center;
                double radius = ac.Radius;

                double ang1 = cent.GetVectorTo(sp).AngleOnPlane(new Plane());
                double ang2 = cent.GetVectorTo(ep).AngleOnPlane(new Plane());



                Arc arc = new Arc(cent, Vector3d.ZAxis, radius, ang1, ang2 );
                result = arc.GetClosestPointTo(point, true);

                arc.Dispose();

            }
            else
            {
                Line l1 = new Line(sp, ep);
                result = l1.GetClosestPointTo(point, true);
                l1.Dispose();

            }
            return result;

        }
0 Likes