Parallel line

Parallel line

Anonymous
Not applicable
5,036 Views
15 Replies
Message 1 of 16

Parallel line

Anonymous
Not applicable

Hi,

 

I have hundreds of DetailLines.

 

How do I get the parallel line of each line

 

 

Thanks & Regards

Sanjay Pandey

0 Likes
5,037 Views
15 Replies
Replies (15)
Message 2 of 16

jeremytammik
Autodesk
Autodesk

Dear Sanjay,

 

Here is an answer to your question:

 

http://thebuildingcoder.typepad.com/blog/2013/08/generating-a-midcurve-between-two-curve-elements.ht...

 

It provides exactly what you need and much more besides.

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes
Message 3 of 16

Anonymous
Not applicable

Hi,

 

Sorry I wasn't able to express clearly my views. So I will elaborate it now. As you can see in the image below I have several lines. I want to loop each line and find the nearest parallel line.

 

parallel.png

 

Thanks & Regards

Sanjay Pandey

 

0 Likes
Message 4 of 16

jeremytammik
Autodesk
Autodesk

Yes, indeed, that sounds like a completely different question.

 

Interesting task.

 

If they were 3D objects, I would suggest that you use the reference intersector:

 

http://www.revitapidocs.com/2017/36f82b40-1065-2305-e260-18fc618e756f.htm

 

It does not work in 2D, though.

 

You can use a filtered element collector to retrieve all elements within a certain distance, and then loop over them to filter out lines, parallel, closest.

 

Note that the BoundingBoxIntersectsFilter is intended for use with a model geometry bounding box.

 

A detail item only has a view specific geometry bounding box, so it fails to filter them.

 

The workaround is to retrieve the view specific bounding box and use Outline.Intersects to perform the equivalent check as described here:

 

http://thebuildingcoder.typepad.com/blog/2016/06/filtering-for-view-specific-elements.html

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes
Message 5 of 16

jeremytammik
Autodesk
Autodesk

Please do let us know how you end up solving this. Thank you!



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes
Message 6 of 16

Anonymous
Not applicable

This is how i do to get parallel lines from cad.

Please share if there is better/simpler way to do it.

 

 

1. get lines to be in one direction (bottom to top or left to right, if not reverse the lines)

 

            List<Line> lstLine = new List<Line>();
            GeometryElement geoElement = e.get_Geometry(new Options());
            foreach (GeometryObject geoObject in geoElement) {
                GeometryInstance instance = geoObject as GeometryInstance;

                if (instance != null) {
                    foreach (GeometryObject instObj in instance.SymbolGeometry) {

                        PolyLine polyline = null;
                        Line line = null;
                        Line lineReverse = null;
                        XYZ xyzDir = null;

                        if (instObj is Line) {
                            line = instObj as Line;
                            xyzDir = line.Direction.Normalize();
                            if (((Math.Abs(xyzDir.X) >= Math.Abs(xyzDir.Y)) && xyzDir.X < 0) || ((Math.Abs(xyzDir.Y) > Math.Abs(xyzDir.X)) && xyzDir.Y < 0)) {
                                lineReverse = line.CreateReversed() as Line;
                                lstLine.Add(lineReverse);
                            }
                            else {
                                lstLine.Add(line);
                            }
                        }
                        else if (instObj is PolyLine) {
                            polyline = instObj as PolyLine;
                            for (i = 1; i < polyline.NumberOfCoordinates; i++) {
                                line = Line.CreateBound(polyline.GetCoordinate(i - 1), polyline.GetCoordinate(i));
                                xyzDir = line.Direction.Normalize();
                                if (((Math.Abs(xyzDir.X) >= Math.Abs(xyzDir.Y)) && xyzDir.X < 0) || ((Math.Abs(xyzDir.Y) > Math.Abs(xyzDir.X)) && xyzDir.Y < 0)) {
                                    lineReverse = line.CreateReversed() as Line;
                                    lstLine.Add(lineReverse);
                                }
                                else {
                                    lstLine.Add(line);
                                }
                            }
                        }
                    }
                }
            }

2. Search for pair, if found create a new line between the pair

 

            List<Line> lstLineNoPair = new List<Line>();
            List<Line> lstLineBeam = new List<Line>();
            Line line1 = null;
            XYZ xyzLine1Direction = null;
            XYZ xyzLine1MidPoint = null;
            XYZ xyzLine1NormCross = null;
            XYZ xyzLine1NormCrossReverse = null;
            Line line2 = null;
            XYZ xyzLine2Direction = null;
            XYZ xyzLine2MidPoint = null;
            Line lineMiddle12 = null;
            XYZ xyzLineMiddle12Norm = null;
            Line linePair1 = null;
            Line linePair2 = null;
            XYZ xyzBeamStart = null;
            XYZ xyzBeamEnd = null;
            Line lineBeam = null;
            double dblZ = 0;

            double dblNearestDistance = 0;
            int intNearestElement = 0;
            while (lstLine.Count >= 2) {
                line1 = lstLine.ElementAt(0);
                xyzLine1Direction = line1.Direction;
                xyzLine1MidPoint = line1.Evaluate(0.5, true);
                xyzLine1NormCross = xyzLine1Direction.Normalize().CrossProduct(XYZ.BasisZ);
                xyzLine1NormCrossReverse = xyzLine1NormCross.Negate();
                intNearestElement = 0;
                dblNearestDistance = 0;
                for (i = 1; i < lstLine.Count - 1; i++) {
                    line2 = lstLine.ElementAt(i);
                    xyzLine2Direction = line2.Direction;
                    xyzLine2MidPoint = line2.Evaluate(0.5, true);
                    if (xyzLine1Direction.IsAlmostEqualTo(xyzLine2Direction, DBLPOINTSDISTANCETOLERANCE)) {
                        lineMiddle12 = Line.CreateBound(xyzLine1MidPoint, xyzLine2MidPoint);
                        if (lineMiddle12.Length < DBLMAXBEAMWIDTH) {
                            xyzLineMiddle12Norm = lineMiddle12.Direction.Normalize();
                            if (xyzLineMiddle12Norm.IsAlmostEqualTo(xyzLine1NormCross, DBLPOINTSDISTANCETOLERANCE) ||
                                xyzLineMiddle12Norm.IsAlmostEqualTo(xyzLine1NormCrossReverse, DBLPOINTSDISTANCETOLERANCE)) {
                                if (dblNearestDistance == 0 || (dblNearestDistance > 0 && lineMiddle12.Length < dblNearestDistance)) {
                                    dblNearestDistance = lineMiddle12.Length;
                                    intNearestElement = i;
                                }
                            }
                        }
                    }
                }
                if (intNearestElement == 0) {
                    lstLineNoPair.Add(lstLine.ElementAt(0));
                }
                else {
                    linePair1 = lstLine.ElementAt(0);
                    linePair2 = lstLine.ElementAt(intNearestElement);
                    xyzBeamStart = new XYZ((linePair1.GetEndPoint(0).X + linePair2.GetEndPoint(0).X) / 2, (linePair1.GetEndPoint(0).Y + linePair2.GetEndPoint(0).Y) / 2, dblZ);
                    xyzBeamEnd = new XYZ((linePair1.GetEndPoint(1).X + linePair2.GetEndPoint(1).X) / 2, (linePair1.GetEndPoint(1).Y + linePair2.GetEndPoint(1).Y) / 2, dblZ);
                    lineBeam = Line.CreateBound(xyzBeamStart, xyzBeamEnd);
                    lstLineBeam.Add(lineBeam);
                    lstLine.RemoveAt(intNearestElement);
                }
                lstLine.RemoveAt(0);
            }
            if (lstLine.Count == 1) {
                lstLineNoPair.Add(lstLine.ElementAt(0));
            }
0 Likes
Message 7 of 16

BenoitE&A
Collaborator
Collaborator

Hey,

Here is what I would do:

1) Classify your lines in different sets in which all lines are parallel. This is easy using the directing vector of each line and checking if they are parallel.

2) For each set, compute the distances of these lines by respect to the first of the set. 

3) Sort the lines using the previous parameter.

Then you can easily compute the nearest line to any given line.

Bye

Benoit


Benoit FAVRE
CEO of etudes & automates
www.etudesetautomates.com/
0 Likes
Message 8 of 16

Anonymous
Not applicable

just realized Line.Direction and Line.Direction.Normalize() are the same thing. Smiley Frustrated

 

correction:

            while (lstLine.Count >= 2) {
                line1 = lstLine.ElementAt(0);
                xyzLine1Direction = line1.GetEndPoint(1) - line1.GetEndPoint(0);
                xyzLine1MidPoint = line1.Evaluate(0.5, true);
                xyzLine1NormCross = xyzLine1Direction.Normalize().CrossProduct(XYZ.BasisZ);
                xyzLine1NormCrossReverse = xyzLine1NormCross.Negate();
                intNearestElement = 0;
                dblNearestDistance = 0;
                for (i = 1; i < lstLine.Count - 1; i++) {
                    line2 = lstLine.ElementAt(i);
                    xyzLine2Direction = line2.GetEndPoint(1) - line2.GetEndPoint(0);
                    xyzLine2MidPoint = line2.Evaluate(0.5, true);
                    if (xyzLine1Direction.IsAlmostEqualTo(xyzLine2Direction, DBLPOINTSDISTANCETOLERANCE)) {
                        lineMiddle12 = Line.CreateBound(xyzLine1MidPoint, xyzLine2MidPoint);
                        if (lineMiddle12.Length < DBLMAXBEAMWIDTH) {
                            xyzLineMiddle12Norm = lineMiddle12.Direction.Normalize();
                            if (xyzLineMiddle12Norm.IsAlmostEqualTo(xyzLine1NormCross, DBLPOINTSDISTANCETOLERANCE) ||
                                xyzLineMiddle12Norm.IsAlmostEqualTo(xyzLine1NormCrossReverse, DBLPOINTSDISTANCETOLERANCE)) {
                                if (dblNearestDistance == 0 || (dblNearestDistance > 0 && lineMiddle12.Length < dblNearestDistance)) {
                                    dblNearestDistance = lineMiddle12.Length;
                                    intNearestElement = i;
                                }
                            }
                        }
                    }
                }
                if (intNearestElement == 0) {
                    lstLineNoPair.Add(lstLine.ElementAt(0));
                }
                else {
                    linePair1 = lstLine.ElementAt(0);
                    linePair2 = lstLine.ElementAt(intNearestElement);
                    xyzBeamStart = new XYZ((linePair1.GetEndPoint(0).X + linePair2.GetEndPoint(0).X) / 2, (linePair1.GetEndPoint(0).Y + linePair2.GetEndPoint(0).Y) / 2, dblZ);
                    xyzBeamEnd = new XYZ((linePair1.GetEndPoint(1).X + linePair2.GetEndPoint(1).X) / 2, (linePair1.GetEndPoint(1).Y + linePair2.GetEndPoint(1).Y) / 2, dblZ);
                    lineBeam = Line.CreateBound(xyzBeamStart, xyzBeamEnd);
                    lstLineBeam.Add(lineBeam);
                    lstLine.RemoveAt(intNearestElement);
                }
                lstLine.RemoveAt(0);
            }
            if (lstLine.Count == 1) {
                lstLineNoPair.Add(lstLine.ElementAt(0));
            }
0 Likes
Message 9 of 16

Anonymous
Not applicable

i dont understand your solution. thanks anyway.

0 Likes
Message 10 of 16

jeremytammik
Autodesk
Autodesk

I believe this comparison operator can be used to create an extremely efficient implementation of Benoit's suggestion:

 

    /// <summary>
    /// Implement a comparison operator for lines 
    /// in the XY plane useful for sorting into 
    /// groups of parallel lines.
    /// </summary>
    public static int Compare( Line a, Line b )
    {
      XYZ pa = a.GetEndPoint( 0 );
      XYZ qa = a.GetEndPoint( 1 );
      XYZ pb = b.GetEndPoint( 0 );
      XYZ qb = b.GetEndPoint( 1 );
      XYZ va = qa - pa;
      XYZ vb = qb - pb;

      // Compare angle in the XY plane

      double ang_a = Math.Atan2( va.Y, va.X );
      double ang_b = Math.Atan2( vb.Y, vb.X );

      int d = Compare( ang_a, ang_b );

      if( 0 == d )
      {
        // Compare distance of unbounded line to origin

        double da = ( qa.X * pa.Y - qa.Y * pa.Y )
          / va.GetLength();

        double db = ( qb.X * pb.Y - qb.Y * pb.Y )
          / vb.GetLength();

        d = Compare( da, db );

        if( 0 == d )
        {
          // Compare distance of start point to origin

          d = Compare( pa.GetLength(), pb.GetLength() );

          if( 0 == d )
          {
            // Compare distance of end point to origin

            d = Compare( qa.GetLength(), qb.GetLength() );
          }
        }
      }
      return d;
    }

 

Added to The Building Coder samples in Util.cs:

 

https://github.com/jeremytammik/the_building_coder_samples/commit/07da23feb41faba168523e3d22f3d34a09...

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes
Message 11 of 16

jeremytammik
Autodesk
Autodesk

Do you want to share a sample model to test this?



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes
Message 12 of 16

RPTHOMAS108
Mentor
Mentor

Nearest by which criteria?

 

NearestHow.png

 

Red line is nearest to black in terms of endpoints

Orange line is nearest to black in terms of midpoints

Green line is nearest to black in terms of perpendicular distance between lines

 

Sometimes the above criteria will coincide but I believe that is likely unknown until they are assessed in turn in the order of preference.

 

My approach which may be a bit long winded:

Create transform with basis X aligned with normalised Subject line vector and with one of the Subject line endpoints as Transform origin

Use .OfPoint of an inverse transform to record adjusted end point values for all lines (adjusted Subject line end points should now have Y cords equal to 0).

Parallel lines are the ones with adjusted Y ords that are similar at both ends (need to do a rounded comparison)

Alternatively you can compare and group by a line vector direction but I believe you have to allow for inverted lines (parallel but opposite ends). You are also in effect comparing two doubles rather than one to compare equality.

 

After getting the groups of parallel lines:

Green: Nearest is the line with an adjusted unsigned Y ord  that is the minimum (based on transform method above).

Orange: Nearest is minimum distance between the average end points of the lines (distance between midpoints)

Red: Nearest is probably the sum of the minimum two distances between endpoints (not sure).

 

Based on your image it looks like you care most about parallel lines with closest matching end points.

 

 

Message 13 of 16

Anonymous
Not applicable

Attached file that contains lines (horizontal and vertical only).

 

Below are the lines.

Lines on the left are the original lines.

Middle lines are original lines with parallel lines filled in with beams.

Right lines are lines that parallel but not connected. i still need make them in pair programmatically to fill in beams in the middle.

I am working on it. (the balance - parallel, but the other pair is not connected lines)

Any idea is appreciated.

 

 

Beam Lines.png

 

 

 

 

0 Likes
Message 14 of 16

RPTHOMAS108
Mentor
Mentor

This is a pretty unique problem to solve but based on my approach noted above I've devised something that works for all of the lines in your sample file. Luckily your lines are split at junctions otherwise it would not work.

 

I think there is room for improvement in terms of the order of execution and better filtering order to limit number of comparisons required (for speed) but it works. There are certain situations where it would not work:

  1. Closely spaced beams where gap between is smaller than beam width
  2. Line where both ends are not within the limits of the other line i.e. staggered, one starts before the start of the other but stops before the end (as shown below).

There may be a quicker direct approach but to be honest there is a lot to consider in terms of the different forms of nearby parallel line ends. So I don't believe there is a perfect mathematical approach just an object orientated approach where you pick different braches of a decision tree based upon the situation for each set of related end points you find.

 

There are some constants to be aware of at the top of each class, these relate to tolerance of comparison.

 

Capture0114.PNG

 

 

 

 Public Function NearestParallelV1(ByVal commandData As Autodesk.Revit.UI.ExternalCommandData, _
                     ByRef message As String, ByVal elements As Autodesk.Revit.DB.ElementSet)

        If commandData.Application.ActiveUIDocument Is Nothing Then Return Result.Succeeded Else 
        Dim IntUIDoc As UIDocument = commandData.Application.ActiveUIDocument
        Dim IntDoc As Document = IntUIDoc.Document

        Dim LinesAlreadyPaired As New List(Of AdjustedLineAndID)
        Dim NewCentreLines As New List(Of AdjustedLineAndID)

        Dim FEC As New FilteredElementCollector(IntDoc, IntDoc.ActiveView.Id)
        Dim ECF As New ElementCategoryFilter(BuiltInCategory.OST_Lines)
        Dim Lines As ModelLine() = FEC.WherePasses(ECF).WhereElementIsNotElementType.WhereElementIsViewIndependent. _
            ToElements.Cast(Of ModelLine).ToArray

        For Each item As ModelLine In Lines
            Dim LinesToProcess As New List(Of AdjustedLineAndID)
            If item.GeometryCurve.GetType <> GetType(Line) Then
                Continue For
            End If

            Dim EP0 As XYZ = item.GeometryCurve.GetEndPoint(0)
            Dim EP1 As XYZ = item.GeometryCurve.GetEndPoint(1)
            Dim NV As XYZ = (EP1 - EP0).Normalize
            Dim T As Transform = Transform.Identity
            T.BasisX = NV
            T.BasisY = New XYZ(-NV.Y, NV.X, 0)
            T.BasisZ = New XYZ(0, 0, 1)
            T.Origin = EP0

            Dim MainLine As New AdjustedLineAndID(item, T)
            'Re-creating set below for each iteration is time consuming
            'If we group lines into direction groups first we can iterate those directions instead
            'and deduct 'item' co-ords from 'other.item' co-ords.
            'i.e. no need to compare each line against each line but instead compare each line against only the lines it is parallel to
            For Each Otheritem As ModelLine In Lines
                LinesToProcess.Add(New AdjustedLineAndID(Otheritem, T))
            Next
            Dim LineSet As New AdjustedLineAndIDSet(MainLine, LinesToProcess, LinesAlreadyPaired)
            If LineSet.IsEmpty Then Continue For Else 

            LinesAlreadyPaired.AddRange(LineSet.OriginalLinesInSet)
            NewCentreLines.AddRange(LineSet.NewCentreLines)
        Next

        If NewCentreLines.Count = 0 Then Return Result.Cancelled Else 
        Using Tx As New Transaction(IntDoc, "Add centre lines")
            If Tx.Start = TransactionStatus.Started Then
                For Each item As AdjustedLineAndID In NewCentreLines
                    Dim NL As Line = Line.CreateBound(item.NewEnds(0), item.NewEnds(1))
                    IntDoc.Create.NewDetailCurve(IntDoc.ActiveView, NL)
                Next
                Tx.Commit()
            End If
        End Using
        Return Result.Succeeded
    End Function

    Private Class AdjustedLineAndIDSet
        Const mm_to_ft As Double = 1 / 304.8
        'Max parallel distance to search for nearest neighbour e.g. max beam width
        Private Shared MaxWidth As Double = 400 * mm_to_ft
        Private IntMainLine As AdjustedLineAndID
        Private IntLinesWithinLimits As New List(Of AdjustedLineAndID)
        Public ReadOnly Property IsEmpty As Boolean
            Get
                Return IntLinesWithinLimits.Count = 0
            End Get
        End Property
        Public ReadOnly Property OriginalLinesInSet As List(Of AdjustedLineAndID)
            Get
                If IsEmpty Then Return New List(Of AdjustedLineAndID) Else 
                Dim Out As New List(Of AdjustedLineAndID)
                Out.Add(IntMainLine)
                Out.AddRange(IntLinesWithinLimits)
                Return Out
            End Get
        End Property
        Public ReadOnly Property NewCentreLines As List(Of AdjustedLineAndID)
            Get
                Return GetNewCentreLines()
            End Get
        End Property
        Public Sub New(MainLine As AdjustedLineAndID, OtherLines As List(Of AdjustedLineAndID), _
                     Optional Excluded As List(Of AdjustedLineAndID) = Nothing)
            If Excluded Is Nothing Then Excluded = New List(Of AdjustedLineAndID)
            If MainLine Is Nothing Then Exit Sub Else 
            IntMainLine = MainLine

            If OtherLines.Contains(MainLine) Then
                OtherLines.Remove(MainLine)
            End If

            Dim Filtered As List(Of AdjustedLineAndID) = OtherLines.FindAll(Function(j) Excluded.Contains(j) = False AndAlso _
                                                                               IntMainLine.GetEndsRelationToOther(j) = _
                                                   AdjustedLineAndID.EndsRelationToOther.ParallelAndBothEndsWithin)

            Dim dist_ft As Double = 0
            Dim NPL As AdjustedLineAndID = MainLine.NearestParallelLine(Filtered, dist_ft)
            If NPL Is Nothing Then Exit Sub Else 
            dist_ft += AdjustedLineAndID.DistanceTolerance

            If dist_ft > MaxWidth Then Exit Sub Else 
            Dim Filtered1 As List(Of AdjustedLineAndID) = Filtered.FindAll(Function(j) IntMainLine.IsNearToOther(j, dist_ft))
            IntLinesWithinLimits = Filtered1
        End Sub
        Private Function GetNewCentreLines() As List(Of AdjustedLineAndID)
            Dim Out As New List(Of AdjustedLineAndID)
            For Each item As AdjustedLineAndID In IntLinesWithinLimits
                Dim Y As Double = item.AverageLocalY / 2
                Dim E1 As New XYZ(item.NewEnds(0).X, Y, 0)
                Dim E2 As New XYZ(item.NewEnds(1).X, Y, 0)
                Out.Add(New AdjustedLineAndID(New XYZ() {E1, E2}, IntMainLine.TransformUsed.Inverse))
            Next
            Return Out
        End Function
    End Class

    Private Class AdjustedLineAndID
        Implements IEquatable(Of AdjustedLineAndID)

        Const mm_to_ft As Double = 1 / 304.8
        'When comparing position of lines what tolerance to use 
        Public Shared Property DistanceTolerance As Double = 5 * mm_to_ft
        'When comparing end extents of parallel lines how much beyond strict limit to still count as within limits
        Public Shared Property EndExtentsTolerance As Double = 150 * mm_to_ft
        'General tolerance used for rounding comparisons of doubles
        Public Shared Property DecimalPlaces As Integer = 8
        Private IntNewEnds As XYZ()
        Private IntOriginalEnds As XYZ()
        Private IntLocalAvY As Double
        Private IntTransform As Transform
       
        Public ReadOnly Property TransformUsed() As Transform
            Get
                Return IntTransform
            End Get
        End Property
        Public ReadOnly Property AverageLocalY() As Double
            Get
                Return IntLocalAvY
            End Get
        End Property
        Public ReadOnly Property NewEnds([End] As Integer) As XYZ
            Get
                Return IntNewEnds([End])
            End Get
        End Property
        Public ReadOnly Property OriginalEnds([End] As Integer) As XYZ
            Get
                Return IntOriginalEnds([End])
            End Get
        End Property
        Public Sub New(Line As ModelLine, T As Transform)
            MyClass.New(Line.GeometryCurve, T)
        End Sub
        Public Sub New(Line As DetailLine, T As Transform)
            MyClass.New(Line.GeometryCurve, T)
        End Sub
        Public Sub New(Line As Line, T As Transform)
            MyClass.New(New XYZ() {Line.GetEndPoint(0), Line.GetEndPoint(1)}, T)
        End Sub
        Public Sub New(EndPoints As XYZ(), T As Transform)
            If EndPoints.Length <> 2 Then Throw New ArgumentException("EndPoints must be an array containing 2 elements.")
            IntOriginalEnds = EndPoints
            IntNewEnds = New XYZ() {T.Inverse.OfPoint(EndPoints(0)), _
                                   T.Inverse.OfPoint(EndPoints(1))}

            IntLocalAvY = (IntNewEnds(0).Y + IntNewEnds(1).Y) / 2
            IntTransform = T
        End Sub

        Public Function IsParallelToOther(other As AdjustedLineAndID) As Boolean
            Dim XYZ1adj As XYZ = IntTransform.Inverse.OfPoint(other.IntOriginalEnds(0))
            Dim XYZ2adj As XYZ = IntTransform.Inverse.OfPoint(other.IntOriginalEnds(1))
            Dim Y1 As Double = TruncateDbl(XYZ1adj.Y)
            Dim Y2 As Double = TruncateDbl(XYZ2adj.Y)
            Return Y1 = Y2
        End Function

        Private Shared Function TruncateDbl(V As Double) As Double
            Return CDbl(V.ToString("F" & CStr(DecimalPlaces)))
        End Function

        Public Function NearestParallelLineOLD(others As List(Of AdjustedLineAndID), ByRef ParallelDistance As Double) As AdjustedLineAndID
            Dim Jx As List(Of AdjustedLineAndID) = others.FindAll(Function(x) IsParallelToOther(x) = True _
                                                                      AndAlso Math.Abs(x.IntLocalAvY) > 0)
            If Jx.Count = 0 Then Return Nothing Else 
            Dim ParallelDist = Jx.Min(Function(x) Math.Abs(x.IntLocalAvY))
            ParallelDistance = ParallelDist
            Dim LN As AdjustedLineAndID = Jx.Find(Function(x) Math.Abs(x.IntLocalAvY) = ParallelDist)
            Return LN
        End Function
        'Same as above but assumes input set are already parallel to this line
        Public Function NearestParallelLine(ParallelLines As List(Of AdjustedLineAndID), ByRef ParallelDistance As Double) As AdjustedLineAndID
            Dim Jx As List(Of AdjustedLineAndID) = ParallelLines.FindAll(Function(x) Math.Abs(x.IntLocalAvY) > 0)
            If Jx.Count = 0 Then Return Nothing Else 
            Dim ParallelDist = Jx.Min(Function(x) Math.Abs(x.IntLocalAvY))
            ParallelDistance = ParallelDist
            Dim LN As AdjustedLineAndID = Jx.Find(Function(x) Math.Abs(x.IntLocalAvY) = ParallelDist)
            Return LN
        End Function

        Private Function NumberOfEndsOfOtherWithinMyLimits(other As AdjustedLineAndID, ByRef BothToOneSide As Boolean) As Byte 'number of ends
            Dim MinX As Double
            Dim MaxX As Double
            If Me.IntNewEnds(0).X < Me.IntNewEnds(1).X Then
                MinX = Me.IntNewEnds(0).X
                MaxX = Me.IntNewEnds(1).X
            Else
                MinX = Me.IntNewEnds(1).X
                MaxX = Me.IntNewEnds(0).X
            End If
            Dim Lim0 As Double = MinX - EndExtentsTolerance
            Dim Lim1 As Double = MaxX + EndExtentsTolerance
            Dim Out As Byte = 0
            If other.IntNewEnds(0).X >= Lim0 AndAlso other.IntNewEnds(0).X <= Lim1 Then Out += 1
            If other.IntNewEnds(1).X >= Lim0 AndAlso other.IntNewEnds(1).X <= Lim1 Then Out += 1
            If Out = 0 Then
                If other.IntNewEnds(0).X < Lim0 AndAlso other.IntNewEnds(1).X < Lim0 Then BothToOneSide = True Else 
                If other.IntNewEnds(0).X > Lim1 AndAlso other.IntNewEnds(1).X > Lim1 Then BothToOneSide = True Else 
            End If
            Return Out
        End Function
       
        Public Function IsNearToOther(other As AdjustedLineAndID, DistanceTolerance As Double)
            Dim Y1 As Double = TruncateDbl(Math.Abs(Me.IntLocalAvY))
            Dim Y2 As Double = TruncateDbl(Math.Abs(other.IntLocalAvY))
            If Y1 > Y2 Then
                Return Y1 - Y2 <= TruncateDbl(DistanceTolerance)
            Else
                Return Y2 - Y1 <= TruncateDbl(DistanceTolerance)
            End If
        End Function

        Public Enum EndsRelationToOther As Byte
            None = 0
            ParallelAndBothEndsWithin = 1
            ParallelAndOneEndWithin = 2
            ParallelAndBothEndsToOneSide = 3
            ParallelAndEndsEitherSideSide = 4
            NotParallelTo = 5
        End Enum
        Public Function GetEndsRelationToOther(other As AdjustedLineAndID) As EndsRelationToOther
            If IsParallelToOther(other) Then
                Dim BothToOneSide As Boolean = False
                Dim E As Byte = NumberOfEndsOfOtherWithinMyLimits(other, BothToOneSide)
                Select Case E
                    Case Is = 0
                        If BothToOneSide = True Then
                            Return EndsRelationToOther.ParallelAndBothEndsToOneSide
                        Else
                            Return EndsRelationToOther.ParallelAndEndsEitherSideSide
                        End If
                    Case Is = 1
                        Return EndsRelationToOther.ParallelAndOneEndWithin
                    Case Is = 2
                        Return EndsRelationToOther.ParallelAndBothEndsWithin
                End Select
            Else
                Return EndsRelationToOther.NotParallelTo
            End If
        End Function

        Public Overloads Function Equals(other As AdjustedLineAndID) As Boolean Implements IEquatable(Of AdjustedLineAndID).Equals
            Dim a1 As XYZ = other.IntOriginalEnds(0)
            Dim a2 As XYZ = other.IntOriginalEnds(1)
            Dim b1 As XYZ = Me.IntOriginalEnds(0)
            Dim b2 As XYZ = Me.IntOriginalEnds(1)
            If a1.DistanceTo(b1) <= DistanceTolerance Then
                Return a2.DistanceTo(b2) <= DistanceTolerance
            Else
                Return a1.DistanceTo(b2) <= DistanceTolerance AndAlso a2.DistanceTo(b1) <= DistanceTolerance
            End If
        End Function
    End Class
Message 15 of 16

Anonymous
Not applicable

Hi Thomas,

 

I've tried to convert your code to c#.

It is too tedious (difficult for me to convert).

Give up for now.

I will study the code again later.

 

Thanks again.

0 Likes
Message 16 of 16

RPTHOMAS108
Mentor
Mentor

C#

 

public Result NearestParallelVx(Autodesk.Revit.UI.ExternalCommandData commandData, ref string message, Autodesk.Revit.DB.ElementSet elements)
        {

            if (commandData.Application.ActiveUIDocument == null)
                return Result.Succeeded;
            UIDocument IntUIDoc = commandData.Application.ActiveUIDocument;
            Document IntDoc = IntUIDoc.Document;

            List<AdjustedLineAndID> LinesAlreadyPaired = new List<AdjustedLineAndID>();
            List<AdjustedLineAndID> NewCentreLines = new List<AdjustedLineAndID>();

            FilteredElementCollector FEC = new FilteredElementCollector(IntDoc, IntDoc.ActiveView.Id);
            ElementCategoryFilter ECF = new ElementCategoryFilter(BuiltInCategory.OST_Lines);
            ModelLine[] Lines = FEC.WherePasses(ECF).WhereElementIsNotElementType().WhereElementIsViewIndependent().ToElements().Cast<ModelLine>().ToArray();

            foreach (ModelLine item in Lines)
            {
                List<AdjustedLineAndID> LinesToProcess = new List<AdjustedLineAndID>();
                if (item.GeometryCurve.GetType() != typeof(Line))
                {
                    continue;
                }

                XYZ EP0 = item.GeometryCurve.GetEndPoint(0);
                XYZ EP1 = item.GeometryCurve.GetEndPoint(1);
                XYZ NV = (EP1 - EP0).Normalize();
                Transform T = Transform.Identity;
                T.BasisX = NV;
                T.BasisY = new XYZ(-NV.Y, NV.X, 0);
                T.BasisZ = new XYZ(0, 0, 1);
                T.Origin = EP0;

                AdjustedLineAndID MainLine = new AdjustedLineAndID(item, T);
                //Re-creating set below for each iteration is time consuming
                //If we group lines into direction groups first we can iterate those directions instead
                //and deduct 'item' co-ords from 'other.item' co-ords.
                //i.e. no need to compare each line against each line but instead compare each line against only the lines it is parallel to
                foreach (ModelLine Otheritem in Lines)
                {
                    LinesToProcess.Add(new AdjustedLineAndID(Otheritem, T));
                }
                AdjustedLineAndIDSet LineSet = new AdjustedLineAndIDSet(MainLine, LinesToProcess, LinesAlreadyPaired);
                if (LineSet.IsEmpty)
                    continue;

                LinesAlreadyPaired.AddRange(LineSet.OriginalLinesInSet);
                NewCentreLines.AddRange(LineSet.NewCentreLines);
            }

            if (NewCentreLines.Count == 0)
                return Result.Cancelled;
            using (Transaction Tx = new Transaction(IntDoc, "Add centre lines"))
            {
                if (Tx.Start() == TransactionStatus.Started)
                {
                    foreach (AdjustedLineAndID item in NewCentreLines)
                    {
                        double dx = item.NewEnds[0].DistanceTo(item.NewEnds[1]);
                        

                        if (dx > commandData.Application.Application.ShortCurveTolerance)
                        {
                            Line NL = Line.CreateBound(item.NewEnds[0], item.NewEnds[1]);
                            IntDoc.Create.NewDetailCurve(IntDoc.ActiveView, NL);
                        }

                     
                    }
                    Tx.Commit();
                }
            }
            return Result.Succeeded;
        }

        private class AdjustedLineAndIDSet
        {
            const double mm_to_ft = 1 / 304.8;
            //Max parallel distance to search for nearest neighbour e.g. max beam width
            private static double MaxWidth = 400 * mm_to_ft;
            private AdjustedLineAndID IntMainLine;
            private List<AdjustedLineAndID> IntLinesWithinLimits = new List<AdjustedLineAndID>();
            public bool IsEmpty
            {
                get { return IntLinesWithinLimits.Count == 0; }
            }
            public List<AdjustedLineAndID> OriginalLinesInSet
            {
                get
                {
                    if (IsEmpty)
                        return new List<AdjustedLineAndID>();
                    List<AdjustedLineAndID> Out = new List<AdjustedLineAndID>();
                    Out.Add(IntMainLine);
                    Out.AddRange(IntLinesWithinLimits);
                    return Out;
                }
            }
            public List<AdjustedLineAndID> NewCentreLines
            {
                get { return GetNewCentreLines(); }
            }
            public AdjustedLineAndIDSet(AdjustedLineAndID MainLine, List<AdjustedLineAndID> OtherLines, List<AdjustedLineAndID> Excluded = null)
            {
                if (Excluded == null)
                    Excluded = new List<AdjustedLineAndID>();
                if (MainLine == null)
                    return;
                IntMainLine = MainLine;

                if (OtherLines.Contains(MainLine))
                {
                    OtherLines.Remove(MainLine);
                }

                List<AdjustedLineAndID> Filtered = OtherLines.FindAll(j => Excluded.Contains(j) == false && IntMainLine.GetEndsRelationToOther(j) == AdjustedLineAndID.EndsRelationToOther.ParallelAndBothEndsWithin);

                double dist_ft = 0;
                AdjustedLineAndID NPL = MainLine.NearestParallelLine(Filtered, ref dist_ft);
                if (NPL == null)
                    return;
                dist_ft += AdjustedLineAndID.DistanceTolerance;

                if (dist_ft > MaxWidth)
                    return;
                List<AdjustedLineAndID> Filtered1 = Filtered.FindAll(j => IntMainLine.IsNearToOther(j, dist_ft));
                IntLinesWithinLimits = Filtered1;
            }
            private List<AdjustedLineAndID> GetNewCentreLines()
            {
                List<AdjustedLineAndID> Out = new List<AdjustedLineAndID>();
                foreach (AdjustedLineAndID item in IntLinesWithinLimits)
                {
                    double Y = item.AverageLocalY / 2;
                    XYZ E1 = new XYZ(item.NewEnds[0].X, Y, 0);
                    XYZ E2 = new XYZ(item.NewEnds[1].X, Y, 0);
                    Out.Add(new AdjustedLineAndID(new XYZ[] {
					E1,
					E2
				}, IntMainLine.TransformUsed.Inverse));
                }
                return Out;
            }
        }

        private class AdjustedLineAndID : IEquatable<AdjustedLineAndID>
        {

            const double mm_to_ft = 1 / 304.8;
            //When comparing position of lines what tolerance to use 
            public static double DistanceTolerance { get; set; }
            //When comparing end extents of parallel lines how much beyond strict limit to still count as within limits
            public static double EndExtentsTolerance { get; set; }
            //General tolerance used for rounding comparisons of doubles
            public static int DecimalPlaces { get; set; }
            public XYZ[] NewEnds;
            public XYZ[] OriginalEnds;
            private double IntLocalAvY;
            private Transform IntTransform;

            public Transform TransformUsed
            {
                get { return IntTransform; }
            }
            public double AverageLocalY
            {
                get { return IntLocalAvY; }
            }
          
           
            public AdjustedLineAndID(ModelLine Line, Transform T)
                : this(Line.GeometryCurve as Line, T)
            {
            }
            public AdjustedLineAndID(DetailLine Line, Transform T)
                : this(Line.GeometryCurve as Line, T)
            {
            }
            public AdjustedLineAndID(Line Line, Transform T)
                : this(new XYZ[] {
			Line.GetEndPoint(0),
			Line.GetEndPoint(1)
		}, T)
            {
            }
            public AdjustedLineAndID(XYZ[] EndPoints, Transform T)
            {
                if (EndPoints.Length != 2)
                    throw new ArgumentException("EndPoints must be an array containing 2 elements.");
                OriginalEnds = EndPoints;
                NewEnds = new XYZ[] {
				T.Inverse.OfPoint(EndPoints[0]),
				T.Inverse.OfPoint(EndPoints[1])
			};

                DistanceTolerance = 5 * mm_to_ft;
                EndExtentsTolerance = 150 * mm_to_ft;
                DecimalPlaces = 8;

                IntLocalAvY = (NewEnds[0].Y + NewEnds[1].Y) / 2;
                IntTransform = T;
            }

            public bool IsParallelToOther(AdjustedLineAndID other)
            {
                XYZ XYZ1adj = IntTransform.Inverse.OfPoint(other.OriginalEnds[0]);
                XYZ XYZ2adj = IntTransform.Inverse.OfPoint(other.OriginalEnds[1]);
                double Y1 = TruncateDbl(XYZ1adj.Y);
                double Y2 = TruncateDbl(XYZ2adj.Y);
                return Y1 == Y2;
            }

            private static double TruncateDbl(double V)
            {
                return Convert.ToDouble(V.ToString("F" + Convert.ToString(DecimalPlaces)));
            }

            public AdjustedLineAndID NearestParallelLineOLD(List<AdjustedLineAndID> others, ref double ParallelDistance)
            {
                List<AdjustedLineAndID> Jx = others.FindAll(x => IsParallelToOther(x) == true && Math.Abs(x.IntLocalAvY) > 0);
                if (Jx.Count == 0)
                    return null;
                dynamic ParallelDist = Jx.Min(x => Math.Abs(x.IntLocalAvY));
                ParallelDistance = ParallelDist;
                AdjustedLineAndID LN = Jx.Find(x => Math.Abs(x.IntLocalAvY) == ParallelDist);
                return LN;
            }
            //Same as above but assumes input set are already parallel to this line
            public AdjustedLineAndID NearestParallelLine(List<AdjustedLineAndID> ParallelLines, ref double ParallelDistance)
            {
                List<AdjustedLineAndID> Jx = ParallelLines.FindAll(x => Math.Abs(x.IntLocalAvY) > 0);
                if (Jx.Count == 0)
                    return null;
                dynamic ParallelDist = Jx.Min(x => Math.Abs(x.IntLocalAvY));
                ParallelDistance = ParallelDist;
                AdjustedLineAndID LN = Jx.Find(x => Math.Abs(x.IntLocalAvY) == ParallelDist);
                return LN;
            }

            private byte NumberOfEndsOfOtherWithinMyLimits(AdjustedLineAndID other, out bool BothToOneSide)
            {
                BothToOneSide = false;
                //number of ends
                double MinX = 0;
                double MaxX = 0;
                if (this.NewEnds[0].X < this.NewEnds[1].X)
                {
                    MinX = this.NewEnds[0].X;
                    MaxX = this.NewEnds[1].X;
                }
                else
                {
                    MinX = this.NewEnds[1].X;
                    MaxX = this.NewEnds[0].X;
                }
                double Lim0 = MinX - EndExtentsTolerance;
                double Lim1 = MaxX + EndExtentsTolerance;
                byte Out = 0;
                if (other.NewEnds[0].X >= Lim0 && other.NewEnds[0].X <= Lim1)
                    Out += 1;
                if (other.NewEnds[1].X >= Lim0 && other.NewEnds[1].X <= Lim1)
                    Out += 1;
                if (Out == 0)
                {
                    if (other.NewEnds[0].X < Lim0 && other.NewEnds[1].X < Lim0)
                        BothToOneSide = true;
                    if (other.NewEnds[0].X > Lim1 && other.NewEnds[1].X > Lim1)
                        BothToOneSide = true;
                }
                return Out;
            }

            public bool IsNearToOther(AdjustedLineAndID other, double DistanceTolerance)
            {
                double Y1 = TruncateDbl(Math.Abs(this.IntLocalAvY));
                double Y2 = TruncateDbl(Math.Abs(other.IntLocalAvY));
                if (Y1 > Y2)
                {
                    return Y1 - Y2 <= TruncateDbl(DistanceTolerance);
                }
                else
                {
                    return Y2 - Y1 <= TruncateDbl(DistanceTolerance);
                }
            }

            public enum EndsRelationToOther : byte
            {
                None = 0,
                ParallelAndBothEndsWithin = 1,
                ParallelAndOneEndWithin = 2,
                ParallelAndBothEndsToOneSide = 3,
                ParallelAndEndsEitherSideSide = 4,
                NotParallelTo = 5
            }

            public EndsRelationToOther GetEndsRelationToOther(AdjustedLineAndID other)
            {
                if (IsParallelToOther(other))
                {
                    bool BothToOneSide = false;
                    byte E = NumberOfEndsOfOtherWithinMyLimits(other, out BothToOneSide);
                    if (E == 0)
                    {
                        if (BothToOneSide == true)
                        {
                            return EndsRelationToOther.ParallelAndBothEndsToOneSide;
                        }
                        else
                        {
                            return EndsRelationToOther.ParallelAndEndsEitherSideSide;
                        }
                    }
                    else if (E == 1)
                    {
                        return EndsRelationToOther.ParallelAndOneEndWithin;
                    }
                    else ///if (E == 2)
                    {
                        return EndsRelationToOther.ParallelAndBothEndsWithin;
                    }
                   
                }
                else
                {
                    return EndsRelationToOther.NotParallelTo;
                }
            }
            public bool Equals(AdjustedLineAndID other)
            {
                XYZ a1 = other.OriginalEnds[0];
                XYZ a2 = other.OriginalEnds[1];
                XYZ b1 = this.OriginalEnds[0];
                XYZ b2 = this.OriginalEnds[1];
                if (a1.DistanceTo(b1) <= DistanceTolerance)
                {
                    return a2.DistanceTo(b2) <= DistanceTolerance;
                }
                else
                {
                    return a1.DistanceTo(b2) <= DistanceTolerance && a2.DistanceTo(b1) <= DistanceTolerance;
                }
            }
        }