Recipe 6.18. Solving Any Triangle


Problem

You want to solve any triangle given any three known parts. Examples might include the lengths of any two sides and the measure of the angle between them, or the measures of two angles and the length of the side between them.

Solution

Sample code folder: Chapter 06\AnyTriangle

Create a triangle class to handle the details of calculating all the remaining parts of a triangle given any combination of three of its parts. Also create a separate utility function to calculate any triangle's area given the lengths of its three sides.

Discussion

The triangle class, presented below, allows the remaining elements of any triangle to be calculated given the measures of any three of its sides and angles. The only combination that won't work, of course, is when three angles are given, as these pin down the shape of a triangle but not its size. Here is the code for the TRiangle class:

 Imports System.Math Public Class Triangle    Private StoredSideA As Double    Private StoredSideB As Double    Private StoredSideC As Double    Private StoredAngleA As Double    Private StoredAngleB As Double    Private StoredAngleC As Double    ' ----- The GivenParts variable indicates which parts    '       the user has already supplied. Uppercase letters    '       (A, B, C) indicate sides; lowercase letters    '       (a, b, c) are angles.    Private GivenParts As String = ""    Public Overrides Function ToString() As String       ' ----- Show the details of the triangle.       Return String.Format( _          "SideA={0}, SideB={1}, SideC={2}, " & _          "AngleA={3}, AngleB={4}, AngleC={5}", _          StoredSideA, StoredSideB, StoredSideC, _          StoredAngleA, StoredAngleB, StoredAngleC)    End Function    Public Property SideA() As Double       Get          If (GivenParts.Length >= 3) Then _             Return StoredSideA Else NotYet()       End Get       Set(ByVal Value As Double)          If (Value < 0) Then _             Throw New ArgumentOutOfRangeException( _             "Negative side length (A) not allowed.")          CheckIt("A")          StoredSideA = Value          Resolve()       End Set    End Property    Public Property SideB() As Double       Get          If (GivenParts.Length >= 3) Then _             Return StoredSideB Else NotYet()       End Get       Set(ByVal Value As Double)          If (Value < 0) Then _             Throw New ArgumentOutOfRangeException( _             "Negative side length (B) not allowed.")          CheckIt("B")          StoredSideB = Value          Resolve()       End Set    End Property    Public Property SideC() As Double       Get          If (GivenParts.Length >= 3) Then _             Return StoredSideC Else NotYet()       End Get       Set(ByVal Value As Double)          If (Value < 0) Then _             Throw New ArgumentOutOfRangeException( _             "Negative side length (C) not allowed.")          CheckIt("C")          StoredSideC = Value          Resolve()       End Set    End Property    Public Property AngleA() As Double       Get          If (GivenParts.Length >= 3) Then _             Return StoredAngleA Else NotYet()       End Get       Set(ByVal Value As Double)          If (Value < 0) Or (Value > Math.PI) Then _             Throw New Exception( _             "Angle (A) must range from 0 to PI.")          CheckIt("a")          StoredAngleA = Value          Resolve()       End Set    End Property    Public Property AngleB() As Double       Get          If (GivenParts.Length >= 3) Then _             Return StoredAngleB Else NotYet()       End Get       Set(ByVal Value As Double)          If (Value < 0) Or (Value > Math.PI) Then _             Throw New Exception( _             "Angle (B) must range from 0 to PI.")          CheckIt("b")          StoredAngleB = Value          Resolve()       End Set    End Property    Public Property AngleC() As Double       Get          If (GivenParts.Length >= 3) Then _             Return StoredAngleC Else NotYet()       End Get       Set(ByVal Value As Double)          If (Value < 0) Or (Value > Math.PI) Then _             Throw New Exception( _             "Angle (C) must range from 0 to PI.")          CheckIt("c")          StoredAngleC = Value          Resolve()       End Set    End Property    Private Sub CheckIt(ByVal whatToCheck As String)       ' ----- Make sure it is OK to adjust a component.       If (GivenParts.Length >= 3) Then Throw New Exception( _          "Triangle is immutable once defined by three parts.")       If (GivenParts.IndexOf(whatToCheck) >= 0) Then _          Throw New Exception( _          "Triangle component cannot be modified once set.")       ' ---- Mark this part as modified.       GivenParts &= whatToCheck    End Sub    Private Sub NotYet()       ' ----- The user tried to access components before       '       anything was calculated.       Throw New Exception( _          "Triangle has not yet been completely defined.")    End Sub    Private Sub Resolve()       ' ----- Calculate the missing angles and sides of       '       the triangle.       Dim sinRatio As Double       Dim inSort() As Char       ' ----- Wait for the triangle to be completely defined.       If (GivenParts.Length < 3) Then Return       ' ----- Sort the known parts list.       inSort = GivenParts.ToCharArray()       Array.Sort(inSort)       GivenParts = New String(inSort)       ' ----- Time to resolve. In all cases, the goal is to       '       get three known sides. Then, the ResolveABC()       '       method can work on getting the missing angles.       Select Case GivenParts          Case "ABC"             ResolveABC()          Case "ABa"             sinRatio = Sin(StoredAngleA) / StoredSideA             StoredAngleB = Asin(StoredSideB * sinRatio)             StoredAngleC = PI - StoredAngleA - StoredAngleB             StoredSideC = Sin(StoredAngleC) / sinRatio          Case "ABb"             sinRatio = Sin(StoredAngleB) / StoredSideB             StoredAngleA = Asin(StoredSideA * sinRatio)             StoredAngleC = PI - StoredAngleA - StoredAngleB             StoredSideC = Sin(StoredAngleC) / sinRatio          Case "ABc"             StoredSideC = Sqrt(StoredSideA ^ 2 + _                StoredSideB ^ 2 - 2 * StoredSideA * _                StoredSideB * Cos(StoredAngleC))          Case "ACa"             sinRatio = Sin(StoredAngleA) / StoredSideA             StoredAngleC = Asin(StoredSideC * sinRatio)             StoredAngleB = PI - StoredAngleA - StoredAngleC             StoredSideB = Sin(StoredAngleB) / sinRatio          Case "ACb"             StoredSideB = Sqrt(StoredSideA ^ 2 + _                StoredSideC ^ 2 - 2 * StoredSideA * _                StoredSideC * Cos(StoredAngleB))          Case "ACc"             sinRatio = Sin(StoredAngleC) / StoredSideC             StoredAngleA = Asin(StoredSideA * sinRatio)             StoredAngleB = PI - StoredAngleA - StoredAngleC             StoredSideB = Sin(StoredAngleB) / sinRatio          Case "Aab"             sinRatio = Sin(StoredAngleA) / StoredSideA             StoredSideB = Sin(StoredAngleB) / sinRatio             StoredAngleC = PI - StoredAngleA - StoredAngleB             StoredSideC = Sin(StoredAngleC) / sinRatio          Case "Aac"             sinRatio = Sin(StoredAngleA) / StoredSideA             StoredSideC = Sin(StoredAngleC) / sinRatio             StoredAngleB = PI - StoredAngleA - StoredAngleC             StoredSideB = Sin(StoredAngleB) / sinRatio          Case "Abc"             StoredAngleA = PI - StoredAngleB - StoredAngleC             sinRatio = Sin(StoredAngleA) / StoredSideA             StoredSideB = Sin(StoredAngleB) / sinRatio             StoredSideC = Sin(StoredAngleC) / sinRatio          Case "BCa"             StoredSideA = Sqrt(StoredSideB ^ 2 + _                StoredSideC ^ 2 - 2 * StoredSideB * _                StoredSideC * Cos(StoredAngleA))          Case "BCb"             sinRatio = Sin(StoredAngleB) / StoredSideB             StoredAngleC = Asin(StoredSideC * sinRatio)             StoredAngleA = PI - StoredAngleB - StoredAngleC             StoredSideA = Sin(StoredAngleA) / sinRatio          Case "BCc"             sinRatio = Sin(StoredAngleC) / StoredSideC             StoredAngleB = Asin(StoredSideB * sinRatio)             StoredAngleA = PI - StoredAngleB - StoredAngleC             StoredSideA = Sin(StoredAngleA) / sinRatio          Case "Bab"             StoredAngleC = PI - StoredAngleA - StoredAngleB             sinRatio = Sin(StoredAngleB) / StoredSideB             StoredSideA = Sin(StoredAngleA) / sinRatio             StoredSideC = Sin(StoredAngleC) / sinRatio          Case "Bac"             StoredAngleB = PI - StoredAngleA - StoredAngleC             sinRatio = Sin(StoredAngleB) / StoredSideB             StoredSideA = Sin(StoredAngleA) / sinRatio             StoredSideC = Sin(StoredAngleC) / sinRatio          Case "Bbc"             StoredAngleA = PI - StoredAngleB - StoredAngleC             sinRatio = Sin(StoredAngleB) / StoredSideB             StoredSideA = Sin(StoredAngleA) / sinRatio             StoredSideC = Sin(StoredAngleC) / sinRatio          Case "Cab"             StoredAngleC = PI - StoredAngleA - StoredAngleB             sinRatio = Sin(StoredAngleC) / StoredSideC             StoredSideA = Sin(StoredAngleA) / sinRatio             StoredSideB = Sin(StoredAngleB) / sinRatio          Case "Cac"             StoredAngleB = PI - StoredAngleA - StoredAngleC             sinRatio = Sin(StoredAngleC) / StoredSideC             StoredSideA = Sin(StoredAngleA) / sinRatio             StoredSideB = Sin(StoredAngleB) / sinRatio          Case "Cbc"             StoredAngleA = PI - StoredAngleB - StoredAngleC             sinRatio = Sin(StoredAngleC) / StoredSideC             StoredSideA = Sin(StoredAngleA) / sinRatio             StoredSideB = Sin(StoredAngleB) / sinRatio          Case "abc"             Throw New Exception("Cannot resolve " & _                "triangle with only angles specified.")          Case Else             Throw New Exception( _                "Undefined combination of triangle parts.")       End Select       ResolveABC()    End Sub       Private Sub ResolveABC()       ' ----- All three sides are known. Calculate the angles.       LengthCheck(StoredSideA, StoredSideB, StoredSideC)       StoredAngleC = Acos((StoredSideA ^ 2 + _          StoredSideB ^ 2 - StoredSideC ^ 2) / _          (2 * StoredSideA * StoredSideB))       StoredAngleB = Acos((StoredSideA ^ 2 + _          StoredSideC ^ 2 - StoredSideB ^ 2) / _          (2 * StoredSideA * StoredSideC))       StoredAngleA = PI - StoredAngleB - StoredAngleC    End Sub    Private Sub LengthCheck(ByVal A As Double, _          ByVal B As Double, ByVal C As Double)       ' ----- Make sure that one of the sides isn't       '       too long for the other two.       If (A >= B) AndAlso (A >= C) AndAlso _          (A <= (B + C)) Then Return       If (B >= A) AndAlso (B >= C) AndAlso _          (B <= (A + C)) Then Return       If (C >= A) AndAlso (C >= B) AndAlso _          (C <= (A + B)) Then Return       Throw New Exception( _          "One side is too long for the others.")    End Sub End Class 

Exceptions are thrown if the triangle "doesn't make sense." For example, if the sum of two sides is less than the length of the third, or if three angles are given, the triangle is impossible, or at least the data is insufficient to completely define the triangle.

To find the area of any triangle, you could include a shared function within the TRiangle class, but for the sake of demonstration (and because it can be useful in a wider variety of computational situations) we've chosen to create a triangleArea() function separate from the class. This makes it easy to find the area of any triangle given the lengths of its three sides, whether or not you're solving triangles using the triangle class:

 Public Function TriangleArea(ByVal sideA As Double, _       ByVal sideB As Double, _       ByVal sideC As Double) As Double    ' ----- Calculate the area of a triangle.    Dim sumHalfSides As Double    Dim deltaA As Double    Dim deltaB As Double    Dim deltaC As Double    sumHalfSides = (sideA + sideB + sideC) / 2    deltaA = sumHalfSides - sideA    deltaB = sumHalfSides - sideB    deltaC = sumHalfSides - sideC    Return Math.Sqrt(sumHalfSides * deltaA * deltaB * deltaC) End Function 

The following code demonstrates the use of the TRiangle class by solving for a triangle that has two sides of length 4 and 5, with a 75°; angle between the two sides. The RadPerDeg constant (see Recipe 6.10) converts 75°to radians at compile time rather than at runtime (to be consistent with all other angular measurements in Visual Basic 2005, radians are always assumed in all the procedures in this book that involve angles):

 Const RadPerDeg As Double = Math.PI / 180 Dim testTriangle As New Triangle Dim area As Double ' ----- Build a triangle with sides of 4 and 5, and an '       angle between them of 75 degrees. testTriangle.SideA = 4 testTriangle.SideB = 5 testTriangle.AngleC = 75 * RadPerDeg ' ----- The triangle is already resolved. Calculate area. area = TriangleArea(testTriangle.SideA, _    testTriangle.SideB, testTriangle.SideC) MsgBox(testTriangle.ToString & vbNewLine & _    "Area = " & area.ToString) 

A ToString() function is included in the TRiangle class to provide a default format for presenting the triangle's parts in a single string. The solved triangle for our example is shown in Figure 6-18.

Figure 6-18. Solving a triangle with the Triangle class





Visual Basic 2005 Cookbook(c) Solutions for VB 2005 Programmers
Visual Basic 2005 Cookbook: Solutions for VB 2005 Programmers (Cookbooks (OReilly))
ISBN: 0596101775
EAN: 2147483647
Year: 2006
Pages: 400

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net