Skip to content

Commit 54e20ec

Browse files
authored
Merge pull request #1 from rena0157/develop
Adding Area for Polylines with Arcs
2 parents 6a81ba2 + bc00d7f commit 54e20ec

File tree

7 files changed

+441
-22
lines changed

7 files changed

+441
-22
lines changed

src/DxfLibrary/GeoMath/BasicGeometry.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ namespace DxfLibrary.GeoMath
1313
/// </summary>
1414
public static class BasicGeometry
1515
{
16+
/// <summary>
17+
/// The Tolerance for floating point numbers in this library.
18+
/// </summary>
1619
public static double Tolerance = 1E-10;
1720

1821
/// <summary>
@@ -49,7 +52,7 @@ public static double TrapzArea(GeoLine line)
4952
/// <param name="radius">The radius of the circle</param>
5053
/// <param name="angle">The angle that the segment occupies in Radians</param>
5154
/// <returns>The Area of the segment</returns>
52-
public static double CircleSegmentArea(double radius, double angle) => Math.Pow(radius, 2) * (angle - Math.Sin(angle));
55+
public static double CircleSegmentArea(double radius, double angle) => Math.Pow(radius, 2) * 0.5 * (angle - Math.Sin(angle));
5356

5457
#region Unit Conversion
5558

src/DxfLibrary/GeoMath/Vector.cs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,16 @@ public Vector(GeoPoint origin, GeoPoint destination)
3030
Destination = destination;
3131
}
3232

33+
/// <summary>
34+
/// Constructor that creates a vector with its tail at the origin
35+
/// and the destination given by a <see cref="GeoPoint">.
36+
/// </summary>
37+
/// <param name="destination">The GeoPoint for the destination</param>
38+
public Vector(GeoPoint destination) : this(new GeoPoint(0,0), destination)
39+
{
40+
41+
}
42+
3343
#endregion
3444

3545
#region Public Properties
@@ -94,6 +104,72 @@ public Vector Translate(GeoPoint origin)
94104
new GeoPoint(origin.X + X, origin.Y + Y, origin.Z + Z));
95105
}
96106

107+
/// <summary>
108+
/// Dot product of the vector and another vector
109+
/// </summary>
110+
/// <param name="other">The other vector to dot with</param>
111+
/// <returns>Returns: The result of the dot product</returns>
112+
public double Dot(Vector other) => X * other.X + Y * other.Y + Z * other.Z;
113+
114+
/// <summary>
115+
/// The Cross Product of this vector and another vector.
116+
/// Note that this returns a new vector where this origin is the same
117+
/// as this vector.
118+
/// </summary>
119+
/// <param name="v2">The other vector that is being crossed</param>
120+
/// <returns>Returns: A new vector</returns>
121+
public Vector Cross(Vector v2) => new Vector(Origin,
122+
new GeoPoint(Origin.X + Y*v2.Z - v2.Y*Z,
123+
Origin.Y + v2.X*Z - X*v2.Z,
124+
Origin.Z + (X*v2.Y - v2.X*Y)));
125+
126+
#endregion
127+
128+
#region Overrides
129+
130+
/// <summary>
131+
/// Overloaded operator + for the vector type
132+
/// </summary>
133+
/// <param name="a">The first vector</param>
134+
/// <param name="b">The second vector</param>
135+
/// <returns>Returns a new vector which is the addition of the two vectors</returns>
136+
public static Vector operator +(Vector a, Vector b)
137+
{
138+
return new Vector(new GeoPoint(a.Origin.X + b.Origin.X, a.Origin.Y + b.Origin.Y, a.Origin.Z + b.Origin.Z),
139+
new GeoPoint(a.Destination.X + b.Destination.X,
140+
a.Destination.Y + b.Destination.Y, a.Destination.Z + b.Destination.Z));
141+
}
142+
143+
/// <summary>
144+
/// Equals override for the vector type
145+
/// </summary>
146+
/// <param name="obj">The object that you are comparing</param>
147+
/// <returns>Returns true if the vector is equal to another vector</returns>
148+
public override bool Equals(object obj)
149+
{
150+
var vector = obj as Vector;
151+
152+
if (vector == null)
153+
return false;
154+
155+
return Origin.Equals(vector.Origin) &&
156+
Destination.Equals(vector.Destination);
157+
}
158+
159+
/// <summary>
160+
/// Override for the GetHashCode Type
161+
/// </summary>
162+
/// <returns>Returns: an int which is the hash code for the vector</returns>
163+
public override int GetHashCode()
164+
{
165+
int hash = 983251653;
166+
167+
hash = (hash * 817504243) + Origin.GetHashCode();
168+
hash = (hash * 817504243) + Destination.GetHashCode();
169+
170+
return hash;
171+
}
172+
97173
#endregion
98174

99175
#region UnitVectors

src/DxfLibrary/Geometry/Bulge.cs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public Bulge(double bulgeValue)
4141
/// <summary>
4242
/// The Angle of the Bulge in Degrees
4343
/// </summary>
44-
public double AngleDeg => Rad2Deg(Angle);
44+
public double AngleDeg => BasicGeometry.Rad2Deg(Angle);
4545

4646
/// <summary>
4747
/// Distance from the Chord line to the highest point of the arc,
@@ -61,12 +61,5 @@ public double Sagitta(GeoPoint p0, GeoPoint p1)
6161
/// <returns>The Radius of the Arc</returns>
6262
public double Radius(GeoPoint p0, GeoPoint p1)
6363
=> BasicGeometry.Distance(p0, p1) * 0.5 * (Math.Pow(Value, 2) + 1) / (2 * Value);
64-
65-
/// <summary>
66-
/// Converter for rads to degs
67-
/// </summary>
68-
/// <param name="val">Val in radians</param>
69-
/// <returns>The value in degrees</returns>
70-
private double Rad2Deg(double val) => val * (180 / Math.PI);
7164
}
7265
}

src/DxfLibrary/Geometry/GeoLine.cs

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ public GeoLine(GeoPoint p0, GeoPoint p1, Bulge bulge)
6969
/// </summary>
7070
public Bulge Bulge {get;}
7171

72+
/// <summary>
73+
/// Returns True if the line has a bulge, and false
74+
/// if the file does not have a bulge
75+
/// </summary>
76+
public bool HasBulge => !(BasicGeometry.DoubleCompare(Bulge.Value, 0));
77+
7278
/// <summary>
7379
/// The Length of the Line.
7480
/// </summary>
@@ -85,7 +91,8 @@ public GeoLine(GeoPoint p0, GeoPoint p1, Bulge bulge)
8591
/// <summary>
8692
/// Area of the Segment. The Area of the segment
8793
/// Is the area of a trapizoid that is bounded by the two points of the
88-
/// line.
94+
/// line. Note that if the segment is an arc then the area is the area
95+
/// bounded by that arc.
8996
/// </summary>
9097
public double Area => CalcArea();
9198

@@ -101,8 +108,13 @@ public double Radius
101108
/// If the bulge is zero then the angle will return
102109
/// positive infinity
103110
/// </summary>
111+
/// <remarks>
112+
/// If there is no arc then the segment is a line and
113+
/// will then have an angle of PI (180 degrees). This angle will be returned
114+
/// in radians.
115+
/// </remarks>
104116
public double Angle
105-
=> Bulge.Value != 0 ? Bulge.Angle : double.PositiveInfinity;
117+
=> Bulge.Value != 0 ? Bulge.Angle : Math.PI;
106118

107119
#endregion
108120

@@ -111,7 +123,7 @@ public double Angle
111123
/// <summary>
112124
/// Override of the ToString Method
113125
/// </summary>
114-
/// <returns></returns>
126+
/// <returns>Returns: The Lines points and coordinates</returns>
115127
public override string ToString()
116128
{
117129
return $"P0({Point0.X}, {Point0.Y}, {Point0.Z}), P1({Point1.X}, {Point1.Y}, {Point1.Z})";
@@ -120,7 +132,10 @@ public override string ToString()
120132
/// <summary>
121133
/// Convert this entity to a vector
122134
/// </summary>
123-
/// <returns>A new vector</returns>
135+
/// <returns>
136+
/// Returns: A new vector that has the origin and destination
137+
/// the same as this lines point 0 and point 1
138+
/// </returns>
124139
public Vector ToVector() => new Vector(Point0, Point1);
125140

126141
#endregion
@@ -130,7 +145,12 @@ public override string ToString()
130145
/// <summary>
131146
/// Calculate the length of the Segment.
132147
/// </summary>
133-
/// <returns>The length of the line</returns>
148+
/// <remarks>
149+
/// If the bulge value is 0: Returns the length of the line segment
150+
///
151+
/// If the bulge value is not 0: Returns the arc length of the arc segment
152+
/// </remarks>
153+
/// <returns>The length of the segment</returns>
134154
private double CalcLength()
135155
{
136156
// If the line has no bulge then calculate the straight length
@@ -155,11 +175,9 @@ private double CalcArea()
155175

156176
// If the bulge value is not equal to 0 then return the
157177
// Area of the circle segment
158-
return BasicGeometry.CircleSegmentArea(Bulge.Radius(Point0, Point1), Bulge.Angle);
178+
return Math.Abs(BasicGeometry.CircleSegmentArea(Bulge.Radius(Point0, Point1), Bulge.Angle));
159179
}
160180

161-
162-
163181
#endregion
164182
}
165183
}

src/DxfLibrary/Geometry/GeoPolyline.cs

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,30 @@
33
// Created on: 2019-01-13
44

55
using System;
6+
using System.Collections;
67
using System.Collections.Generic;
78
using System.Linq;
89

10+
using DxfLibrary.GeoMath;
11+
912
namespace DxfLibrary.Geometry
1013
{
11-
public class GeoPolyline : GeoBase, IGeoLength, IGeoArea
14+
/// <summary>
15+
/// Class that represents a collection of Geolines
16+
/// </summary>
17+
public class GeoPolyline : GeoBase, IGeoLength, IGeoArea, IEnumerable<GeoLine>
1218
{
19+
#region Private Members
20+
1321
/// <summary>
1422
/// Private backing field for lines
1523
/// </summary>
1624
private List<GeoLine> _lines;
1725

26+
#endregion
27+
28+
#region Constructors
29+
1830
/// <summary>
1931
/// Constructor for the GeoPolyline that takes in Lists of
2032
/// X, y and z coodinates
@@ -27,8 +39,12 @@ public GeoPolyline(List<double> x, List<double> y, List<double> bulges, bool isC
2739
{
2840
// If the numbers do not match then throw an error
2941
if (x.Count != y.Count || x.Count != bulges.Count)
30-
throw new IndexOutOfRangeException();
42+
throw new IndexOutOfRangeException("All coordinates must be the same size");
3143

44+
// If there are less than 2 points then the polyline cannot be defined
45+
if (x.Count < 2)
46+
throw new ArgumentException("Need more than two points to define a polyline");
47+
3248
_lines = new List<GeoLine>();
3349

3450
// Iterate through and create new lines
@@ -50,6 +66,12 @@ public GeoPolyline(List<double> x, List<double> y, List<double> bulges, bool isC
5066
var bulge = new Bulge(bulges.Last());
5167
_lines.Add(new GeoLine(point0, point1, bulge));
5268
}
69+
70+
// Determine the draw direction using the first and second
71+
// line by converting them to vectors and crossing them.
72+
if (_lines.Count > 1)
73+
DrawDirection = _lines[0].ToVector()
74+
.Cross( _lines[1].ToVector() ).Z;
5375
}
5476

5577
/// <summary>
@@ -62,6 +84,10 @@ public GeoPolyline(List<double> x, List<double> y, bool isClosed)
6284
{
6385
}
6486

87+
#endregion
88+
89+
#region Public Properties
90+
6591
/// <summary>
6692
/// Get the total length of all the lines
6793
/// </summary>
@@ -70,6 +96,76 @@ public GeoPolyline(List<double> x, List<double> y, bool isClosed)
7096
/// <summary>
7197
/// Get the total area of all the lines
7298
/// </summary>
73-
public double Area => _lines.Select(l => l.Area).Sum();
99+
public double Area => CalcArea();
100+
101+
/// <summary>
102+
/// The Draw direction of the polyline.
103+
/// If the value is >0 then the draw direction is counterclockwise
104+
/// If the value is less than 0 then the direction is clockwise
105+
/// </summary>
106+
public double DrawDirection {get;}
107+
108+
#endregion
109+
110+
#region Public Methods
111+
112+
/// <summary>
113+
/// Get the enumerator for the lines in the polyline
114+
/// </summary>
115+
/// <returns>Returns the Enumberator for the lines in the polyline</returns>
116+
public IEnumerator<GeoLine> GetEnumerator()
117+
{
118+
return _lines.GetEnumerator();
119+
}
120+
121+
/// <summary>
122+
/// Gets the enumerator for the polyline class
123+
/// </summary>
124+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
125+
126+
#endregion
127+
128+
#region Private Methods
129+
130+
private double CalcArea()
131+
{
132+
double sum = 0.0d;
133+
134+
// Need to iterate through the lines to caluclate the
135+
// Total area
136+
foreach(var segment in _lines)
137+
{
138+
// If the segment does not have a bulge then
139+
// Add the area from the object to the sum
140+
if (!segment.HasBulge)
141+
{
142+
sum += segment.Area;
143+
continue;
144+
}
145+
146+
// First add the area of the Trapizoid
147+
if (DrawDirection > 0)
148+
sum += (new GeoLine(segment.Point0, segment.Point1).Area) * -1.0;
149+
150+
else if (DrawDirection < 0)
151+
sum += new GeoLine(segment.Point0, segment.Point1).Area;
152+
153+
// This value will determine if we are to add or subtract the segment area
154+
var segmentAreaSwtich = DrawDirection * segment.Bulge.Value;
155+
156+
if (segmentAreaSwtich > 0)
157+
sum += segment.Area;
158+
159+
else if (segmentAreaSwtich < 0)
160+
sum -= segment.Area;
161+
162+
}
163+
164+
return Math.Abs(sum);
165+
}
166+
167+
168+
169+
#endregion
74170
}
75171
}

0 commit comments

Comments
 (0)