But in that algorithm a, b, c are the side lengths, and assumes they are given and exact, and it is compared to Heron's formula. I guess I could calculate the side lengths using simd_precise_distance, but I'm not sure how this method would compare to the one I gave above (where a b c are vertices), repeated here:
func area() -> Float {
return ((b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y)) / 2
}
EDIT: Here is a program with an example triangle for which the two methods show quite different areas:
import simd
struct Triangle2D {
let a, b, c: float2
init(_ a: float2, _ b: float2, _ c: float2) {
(self.a, self.b, self.c) = (a, b, c)
}
/// Returns a positive value if the triangle is positively oriented, a
/// negative value if it is negatively oriented and zero if it is
/// degenerate (the three points are colinear).
func orientation() -> Float {
return simd_orient(a, b, c)
}
/// Returns the area of the triangle. The area is positive if the triangle
/// is positively oriented, negative if it's negatively oriented and zero
/// if it's degenerate.
func areaFast() -> Float {
// See: https://forums.swift.org/t/15725/2
//return orientation() / 2 // <-- Not correct according to above link.
//return simd_length(simd_cross(b - a, c - a)) / 2 // <- not signed
return ((b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y)) / 2
}
/// Returns the area of the triangle, no matter the orientation, or -1 if
/// the computed side length of the triangle cannot be a real triangle.
func areaPrecise() -> Float {
// https://people.eecs.berkeley.edu/~wkahan/Triangle.pdf
// First sort sa, sb, sc such that sa >= sb >= sc:
var sa = simd_precise_distance(a, b)
var sb = simd_precise_distance(b, c)
var sc = simd_precise_distance(c, a)
if (sa < sc) { swap(&sa, &sc) }
if (sa < sb) {
swap(&sa, &sb)
} else if (sb < sc) {
swap(&sb, &sc)
}
// If sc-(sa-sb) < 0, the data are not side-lengths of a real triangle:
if sc - (sa - sb) < 0 { return -1 }
// Area is:
// (1/4)√( (sa+(sb+sc))*(sc-(sa-sb))*(sc+(sa-sb))*(sa+(sb-sc)) )
return sqrt( (sa+(sb+sc))*(sc-(sa-sb))*(sc+(sa-sb))*(sa+(sb-sc)) ) / 4
}
}
let triangle = Triangle2D(float2(0,0), float2(15_000, 0), float2(5_000.001, 0.001))
print("areaFast: ", triangle.areaFast())
print("areaPrecise:", triangle.areaPrecise())
It will print:
areaFast: 7.5000005
areaPrecise: 0.0
I tried to ask Wolfram Alpha for the true area but for some reason it doesn't think those vertices form a possible triangle, but AFAICS it's no less possible than eg this:
let triangle = Triangle2D(float2(0, 0), float2(15_000, 0), float2(5_000, 10))
print("areaFast: ", triangle.areaFast())
print("areaPrecise:", triangle.areaPrecise())
for which Wolfram Alpha agrees with areaFast:
areaFast: 75000.0
areaPrecise: 74115.914
And here is another example for which areaFast is clearly right and areaPrecise is wrong:
let triangle = Triangle2D(float2(1, 0), float2(0, 1000000000), float2(0, -1000000000))
print("areaFast: ", triangle.areaFast()) // 1e+09
print("areaPrecise:", triangle.areaPrecise()) // 0.0
(Again, sorry if this is considered off-topic, I'll trust a moderator to intervene if that's the case.)