views:

141

answers:

6

I'm trying to calculate triangles base on the Area and the angles. If Angle-B is 90° then the formula works, but in my case, the angle can be from 0.1° to 179.8°. The formula assumes that the angle is 90, so I was thinking that there might be something that is hidden that could work for very angle. Here is the formula:

alt text alt text

The formula in code would be:

Height = sqrt((2 * Area) / (tan(Angle-A)));

I'm looking for the second half of the formula. Would the next part of the formula be something like this:

cos(sin(AngleB))
A: 

If I made no mistake the length of a side x between two angles a and b should be the following.

         ________________
        /  2A       2A
x =    / ------ + ------
     \/  tan(a)   tan(b)

So to calculate side-C you but in angle-A and angle-B.

(Now double checked it - seems to be correct.)

Daniel Brückner
A: 

How about calculating the lengths of two of the sides of a triangle where the third size is assigned a length of 1 (using the law of sines on the angles, with that side assigned length 1), then scale the triangle until its area matches the area you have? Then you can calculate the height fairly easily. http://en.wikipedia.org/wiki/Triangle_area#Using_trigonometry

JAB
+5  A: 

Okay, new try: If my calculations are correct, side B equals sqrt(2*area*sin(angle-B)/(sin(angle-A)*sin(angle-C))

Since Area = 1/2 * A * B * sin(c) = 1/2 * C * B * sin(a) = 1/2 * A * C * sin(b) we get:

A = 2 * area / (B * sin(c)) and using this we get:

C = sin(c) * B / sin(b) and when we place that back into the equation of area, we get:

B = sqrt(2*area*sin(angle-B)/(sin(angle-A)*sin(angle-C))

When you know one side and all the angles, calculating the other sides should be easy using normal trigonometry.

tsiki
Sorry, I kinda wrote the same thing you did at about the same time. If you want me to delete mine (because yours was first), just let me know.
Welbog
Oh no need, better two explanations than one
tsiki
Does this formula work for all cases? Ex. Angle-B < 90,>90 and =90?
NSArray
Yes it should work for all cases (when all the angles are > 0 obviously)
tsiki
I make sure that the angles are between 0.1 and 179.8, and that the three angles equal to 180. So there should be a problem.
NSArray
Just tested your formula on all cases, and they work! Thanks~
NSArray
+6  A: 

tziki's answer is correct, but I'd like to elaborate on how it's derived.

We start with angles and area as knowns. I'm going to use the labels in the OP's diagram for this explanation.

First we have the basic truth that the area of a triangle is half the product of its base and height: Area = base * height / 2. We want to be able to determine the relationship between base and height so that we can reduce this equation to one unknown and solve for base.

Another important thing to know is that the height of the triangle is proportional to Side-A: height = Side-A * sin(Angle-B). So knowing Side-A will give us the height.

Now we need to establish a relationship between Side-A and Side-C (the base). The most appropriate rule here is the sine law: Side-A/sin(A) = Side-C/sin(C). We re-arrange this equation to find Side-A in terms of Side-C: Side-A = Side-C * sin(A)/sin(C).

We can now insert this result into the height equation to get the formula for height in terms of Side-C only: height = Side-C * sin(A) * sin(B) / sin(C)

Using Side-C as the base in the area equation, we can now find the area in terms of Side-C only: Area = Side-C^2 * sin(A) * sin(B) / 2sin(C)

Then re-arrange this equation to find Side-C in terms of Area:

Side-C = SQRT(2 * Area * sin(C) / (sin(B) * (sin(A)))

And that gives you one side. This can be repeated to find the other sides, or you can use a different approach to find the other sides knowing this one.

Welbog
A: 

The formula you give for Side-A seems to be correct IF the triangle is isosceles, i.e., Angle-B = Angle-C (you get this using the law of sines and the sine formula for the area). If it is not isosceles, you seem to need to know the other angles; the general formula is:

Side-A = sqrt(2*Area*sin(Angle-A)/(sin(Angle-B)*sin(Angle-C)))

Of course, I did this in my head, so check the math ;)

Edit: Ok, fixed the formula after checkin on paper.

Juupeli
+1  A: 

You already have your answer, but I had to solve this kind of exercise for a job interview some time ago. It's not hard, and it didn't took me much time to arrive to the following solution.

Read it through and it should be self explanatory.

Create a Python module that solves triangles by aplying the sin and cosin theorems.

The module receives as parameters some of a triangle's values and, if possible, returns the values of all it's angles and side lengths.

The parameters are received as a dict and it should be able to be called stand-alone from the command line.

from __future__ import division
import sys, logging
from math import radians, degrees, acos, cos, sin, sqrt, asin

class InconsistentDataError(TypeError):
    pass


class InsufficientDataError(TypeError):
    pass


class NonUpdatable(dict):
    """Dictionary whose items can be set only once."""
    def __setitem__(self, i, y):
        if self.get(i, None):
            raise InconsistentDataError()
        super(NonUpdatable, self).__setitem__(i, y)


def get_known_sides(**kwarg):
    """Filter from the input elements the Side elements."""
    return dict([i for i in kwarg.iteritems() if i[0].isupper()])

def get_known_angles(**kwarg):
    """Filter from the input elements the Angle elements."""
    return dict([i for i in kwarg.iteritems() if i[0].islower()])

def get_opposite_angle(C, B, A):
    """
    Get the angle corresponding to C.

    Keyword arguments:
    A -- right side of the angle (real number > 0)
    B -- left side of the angle (real number > 0)
    C -- side opposite to the angle (real number > 0)

    Returns:
    angle opposite to C
    """
    return degrees(acos((A**2 + B**2 - C**2) / (2 * A * B)))

def get_side(A, B, c):
    """
    Calculate the Side corresponding to the Angle c.

    Keyword arguments:
    A -- left side of C (real number > 0)
    B -- right side of C (real number > 0)
    c -- angle opposite to side C (real number)

    Returns:
    side C, opposite to c
    """
    return sqrt(A**2 + B**2 - 2*A*B*cos(radians(c)))

def get_overlapping_angle(known_angles, known_sides):
    """
    Calculate the Angle of a known side, knowing the angle to another known side.

    Keyword arguments:
    known_angles -- (dict of angles)
    known_sides -- (dict of sides)

    Returns:
    angle of the known side, to which there is no known angle
    """
    a = (set([i.lower() for i in known_sides.iterkeys()]) - 
            set([i.lower() for i in known_angles.iterkeys()])).pop()

    b = (set([i.lower() for i in known_sides.iterkeys()]) & 
            set([i.lower() for i in known_angles.iterkeys()])).pop()

    y = (known_sides[a.upper()]/known_sides[b.upper()]) * sin(radians(known_angles[b.lower()]))
    if y > 1: y = 1 #Rounding error fix --- y = 1.000000000001; asin(y) -> Exception
    return {a.lower(): degrees(asin(y))}

def get_angles(A, B, C):
    """
    Calculate all the angles, given the length of all the sides.

    Keyword arguments:
    A -- side A (real number > 0)
    B -- side B (real number > 0)
    C -- side C (real number > 0)

    Returns:
    dict of angles
    """
    sides = {"A":A,"B":B,"C":C}
    _sides = sides.keys()
    angles = {}

    for side in sides.keys():
        angles[side.lower()] = get_opposite_angle(
                                    sides[_sides[0]], 
                                    sides[_sides[1]], 
                                    sides[_sides[2]])
        _sides.append(_sides.pop(0))

    return angles

def get_triangle_values(**kwargs):
    """Calculate the missing values of a triangle based on the known values."""
    known_params = kwargs
    angles = NonUpdatable({
        "a":0, 
        "b":0, 
        "c":0,
    })
    sides = NonUpdatable({
        "A":0, 
        "B":0, 
        "C":0,
    })

    if len(known_params) < 3:
        raise InsufficientDataError("Three parameters are needed to calculate triangle's values.")

    if str(known_params.keys()).islower():
        raise TypeError("At least one length needed.")

    known_sides = NonUpdatable(get_known_sides(**known_params))
    sides.update(known_sides)
    known_angles = NonUpdatable(get_known_angles(**known_params))
    angles.update(known_angles)

    if len(known_angles) == 3 and sum(known_angles.itervalues()) != 180:
        raise InconsistentDataError("One of the sides is too long.")

    if len(known_sides) == 3:
        x=[side for side in known_sides.itervalues() if (sum(known_sides.itervalues()) - side) < side]
        if len(x):
            raise InconsistentDataError("One of the sides is too long.")

        for angle, value in get_angles(**known_sides).iteritems(): 
            # Done this way to force exception when overwriting a 
            # user input angle, otherwise it would be a simple assignment.
            # >>> angles = get_angles(**known_sides)
            # This means inconsistent input data.
            angles[angle] = value

    else: # There are angles given and not enough sides.
        if len(known_angles) > 1:
            #2 angles given. Get last angle and calculate missing sides
            for angle, val in angles.iteritems():
                if val == 0:
                    angles[angle] = 180. - sum(angles.itervalues())

            known_sides = known_sides.items()
            for side, length in sides.iteritems():
                if length == 0:
                    sides[side] = known_sides[0][1] / \
                        sin(radians(angles[known_sides[0][0].lower()])) * \
                        sin(radians(angles[side.lower()]))

        else:
            unknown_side = (set(sides.keys()) - set(known_sides.keys())).pop()

            chars = [ord(i.lower()) for i in known_params.iterkeys()]
            chars.sort()

            if chars[0] < chars[1] < chars[2]:
                sides[unknown_side] = get_side(known_sides.values()[0], known_sides.values()[1], known_angles[unknown_side.lower()])
                angles = get_angles(**sides)

            else:
                known_angles.update(get_overlapping_angle(known_angles, known_sides))
                angles.update(known_angles)

                for angle, val in angles.iteritems():
                    if val == 0:
                        angles[angle] = 180. - sum(angles.itervalues())

                sides[unknown_side] = get_side(known_sides.values()[0], known_sides.values()[1], angles[unknown_side.lower()])

    angles.update(sides)
    return angles

if __name__ == "__main__":
    try:
        values = get_triangle_values( **eval(sys.argv[1], {}, {}) )
    except IndexError, e:
        values = get_triangle_values(A=10,B=10,C=10)
    except InsufficientDataError, e:
        print "Not enough data!"
        exit(1)
    except InconsistentDataError, e:
        print "Data is inconsistent!"
        exit(1)

    print values

A, B and C are the side lengths and a, b and c are the angles, so c is the angle opposite to the side C.

Tests

voyager