4
4
using System . Linq ;
5
5
using Newtonsoft . Json ;
6
6
using Elements . Spatial ;
7
+ using Elements . Geometry . Interfaces ;
7
8
8
9
namespace Elements . Geometry
9
10
{
@@ -15,7 +16,7 @@ namespace Elements.Geometry
15
16
/// [!code-csharp[Main](../../Elements/test/LineTests.cs?name=example)]
16
17
/// </example>
17
18
/// TODO: Rename this class to LineSegment
18
- public class Line : TrimmedCurve < InfiniteLine > , IEquatable < Line >
19
+ public class Line : TrimmedCurve < InfiniteLine > , IEquatable < Line > , IHasArcLength
19
20
{
20
21
/// <summary>
21
22
/// The domain of the curve.
@@ -123,6 +124,48 @@ public override Vector3 PointAt(double u)
123
124
return this . BasisCurve . PointAt ( u ) ;
124
125
}
125
126
127
+ /// <summary>
128
+ /// The mid point of the curve.
129
+ /// </summary>
130
+ /// <returns>The length based midpoint.</returns>
131
+ public virtual Vector3 MidPoint ( )
132
+ {
133
+ return PointAtNormalizedLength ( 0.5 ) ;
134
+ }
135
+
136
+ /// <summary>
137
+ /// Returns the point on the line corresponding to the specified length value.
138
+ /// </summary>
139
+ /// <param name="length">The length value along the line.</param>
140
+ /// <returns>The point on the line corresponding to the specified length value.</returns>
141
+ /// <exception cref="ArgumentException">Thrown when the specified length is out of range.</exception>
142
+ public virtual Vector3 PointAtLength ( double length )
143
+ {
144
+ double totalLength = ArcLength ( this . Domain . Min , this . Domain . Max ) ; // Calculate the total length of the Line
145
+
146
+ if ( length < 0 || length > totalLength )
147
+ {
148
+ throw new ArgumentException ( "The specified length is out of range." ) ;
149
+ }
150
+ var lengthParameter = length / totalLength ;
151
+ return this . PointAtNormalized ( lengthParameter ) ;
152
+ }
153
+
154
+ /// <summary>
155
+ /// Returns the point on the line corresponding to the specified normalized length-based parameter value.
156
+ /// </summary>
157
+ /// <param name="parameter">The normalized length-based parameter value, ranging from 0 to 1.</param>
158
+ /// <returns>The point on the line corresponding to the specified normalized length-based parameter value.</returns>
159
+ /// <exception cref="ArgumentException">Thrown when the specified parameter is out of range.</exception>
160
+ public virtual Vector3 PointAtNormalizedLength ( double parameter )
161
+ {
162
+ if ( parameter < 0 || parameter > 1 )
163
+ {
164
+ throw new ArgumentException ( "The specified parameter is out of range." ) ;
165
+ }
166
+ return PointAtLength ( parameter * this . ArcLength ( this . Domain . Min , this . Domain . Max ) ) ;
167
+ }
168
+
126
169
/// <inheritdoc/>
127
170
public override Curve Transformed ( Transform transform )
128
171
{
@@ -541,38 +584,79 @@ public static bool PointOnLine(Vector3 point, Vector3 start, Vector3 end, bool i
541
584
return false ;
542
585
}
543
586
587
+ // /// <summary>
588
+ // /// Divide the line into as many segments of the provided length as possible.
589
+ // /// </summary>
590
+ // /// <param name="l">The length.</param>
591
+ // /// <param name="removeShortSegments">A flag indicating whether segments shorter than l should be removed.</param>
592
+ // public List<Line> DivideByLength(double l, bool removeShortSegments = false)
593
+ // {
594
+ // var len = this.Length();
595
+ // if (l > len)
596
+ // {
597
+ // return new List<Line>() { new Line(this.Start, this.End) };
598
+ // }
599
+
600
+ // var total = 0.0;
601
+ // var d = this.Direction();
602
+ // var lines = new List<Line>();
603
+ // while (total + l <= len)
604
+ // {
605
+ // var a = this.Start + d * total;
606
+ // var b = a + d * l;
607
+ // lines.Add(new Line(a, b));
608
+ // total += l;
609
+ // }
610
+ // if (total < len && !removeShortSegments)
611
+ // {
612
+ // var a = this.Start + d * total;
613
+ // if (!a.IsAlmostEqualTo(End))
614
+ // {
615
+ // lines.Add(new Line(a, End));
616
+ // }
617
+ // }
618
+ // return lines;
619
+ // }
620
+
544
621
/// <summary>
545
- /// Divide the line into as many segments of the provided length as possible .
622
+ /// Divides the line into segments of the specified length.
546
623
/// </summary>
547
- /// <param name="l ">The length.</param>
548
- /// <param name="removeShortSegments" >A flag indicating whether segments shorter than l should be removed .</param >
549
- public List < Line > DivideByLength ( double l , bool removeShortSegments = false )
624
+ /// <param name="divisionLength ">The desired length of each segment .</param>
625
+ /// <returns >A list of points representing the segments .</returns >
626
+ public Vector3 [ ] DivideByLength ( double divisionLength )
550
627
{
551
- var len = this . Length ( ) ;
552
- if ( l > len )
628
+ var segments = new List < Vector3 > ( ) ;
629
+
630
+ if ( this . ArcLength ( this . Domain . Min , this . Domain . Max ) < double . Epsilon )
553
631
{
554
- return new List < Line > ( ) { new Line ( this . Start , this . End ) } ;
632
+ // Handle invalid line with insufficient length
633
+ return new Vector3 [ 0 ] ;
555
634
}
556
635
557
- var total = 0.0 ;
558
- var d = this . Direction ( ) ;
559
- var lines = new List < Line > ( ) ;
560
- while ( total + l <= len )
636
+ var currentProgression = 0.0 ;
637
+ segments = new List < Vector3 > { this . Start } ;
638
+
639
+ // currentProgression from last segment before hitting end
640
+ if ( currentProgression != 0.0 )
561
641
{
562
- var a = this . Start + d * total ;
563
- var b = a + d * l ;
564
- lines . Add ( new Line ( a , b ) ) ;
565
- total += l ;
642
+ currentProgression -= divisionLength ;
566
643
}
567
- if ( total < len && ! removeShortSegments )
644
+ while ( this . ArcLength ( this . Domain . Min , this . Domain . Max ) >= currentProgression + divisionLength )
568
645
{
569
- var a = this . Start + d * total ;
570
- if ( ! a . IsAlmostEqualTo ( End ) )
571
- {
572
- lines . Add ( new Line ( a , End ) ) ;
573
- }
646
+ segments . Add ( this . PointAt ( currentProgression + divisionLength ) ) ;
647
+ currentProgression += divisionLength ;
574
648
}
575
- return lines ;
649
+ // Set currentProgression from divisionLength less distance from last segment point
650
+ currentProgression = divisionLength - segments . LastOrDefault ( ) . DistanceTo ( this . End ) ;
651
+
652
+ // Add the last vertex of the polyline as the endpoint of the last segment if it
653
+ // is not already part of the list
654
+ if ( ! segments . LastOrDefault ( ) . IsAlmostEqualTo ( this . End ) )
655
+ {
656
+ segments . Add ( this . End ) ;
657
+ }
658
+
659
+ return segments . ToArray ( ) ;
576
660
}
577
661
578
662
/// <summary>
@@ -925,7 +1009,7 @@ public double DistanceTo(Line other)
925
1009
// line vectors are not collinear, their directions share the common plane.
926
1010
else
927
1011
{
928
- // dStartStart length is distance to the common plane.
1012
+ // dStartStart length is distance to the common plane.
929
1013
dStartStart = dStartStart . ProjectOnto ( cross ) ;
930
1014
Vector3 vStartStart = other . Start + dStartStart - this . Start ;
931
1015
Vector3 vStartEnd = other . Start + dStartStart - this . End ;
@@ -1028,8 +1112,8 @@ public List<Line> Trim(Polygon polygon, out List<Line> outsideSegments, bool inc
1028
1112
var B = intersectionsOrdered [ i + 1 ] ;
1029
1113
if ( A . IsAlmostEqualTo ( B ) ) // skip duplicate points
1030
1114
{
1031
- // it's possible that A is outside, but B is at an edge, even
1032
- // if they are within tolerance of each other.
1115
+ // it's possible that A is outside, but B is at an edge, even
1116
+ // if they are within tolerance of each other.
1033
1117
// This can happen due to floating point error when the point is almost exactly
1034
1118
// epsilon distance from the edge.
1035
1119
// so if we have duplicate points, we have to update the containment value.
0 commit comments