1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
|
// Copyright ©2022 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package r2
import "math"
// Triangle represents a triangle in 2D space and
// is composed by 3 vectors corresponding to the position
// of each of the vertices.
type Triangle [3]Vec
// Centroid returns the intersection of the three medians of the triangle
// as a point in space.
func (t Triangle) Centroid() Vec {
return Scale(1.0/3.0, Add(Add(t[0], t[1]), t[2]))
}
// Area returns the surface area of the triangle.
func (t Triangle) Area() float64 {
// Heron's Formula, see https://en.wikipedia.org/wiki/Heron%27s_formula.
// Also see William M. Kahan (24 March 2000). "Miscalculating Area and Angles of a Needle-like Triangle"
// for more discussion. https://people.eecs.berkeley.edu/~wkahan/Triangle.pdf.
a, b, c := t.orderedLengths()
A := (c + (b + a)) * (a - (c - b))
A *= (a + (c - b)) * (c + (b - a))
return math.Sqrt(A) / 4
}
// orderedLengths returns the lengths of the sides of the triangle such that
// a ≤ b ≤ c.
func (t Triangle) orderedLengths() (a, b, c float64) {
s1, s2, s3 := t.sides()
l1 := Norm(s1)
l2 := Norm(s2)
l3 := Norm(s3)
// sort-3
if l2 < l1 {
l1, l2 = l2, l1
}
if l3 < l2 {
l2, l3 = l3, l2
if l2 < l1 {
l1, l2 = l2, l1
}
}
return l1, l2, l3
}
// sides returns vectors for each of the sides of t.
func (t Triangle) sides() (Vec, Vec, Vec) {
return Sub(t[1], t[0]), Sub(t[2], t[1]), Sub(t[0], t[2])
}
// IsDegenerate returns true if all of triangle's vertices are
// within tol distance of its longest side.
func (t Triangle) IsDegenerate(tol float64) bool {
sides := [3]Vec{Sub(t[1], t[0]), Sub(t[2], t[1]), Sub(t[0], t[2])}
len2 := [3]float64{Norm2(sides[0]), Norm2(sides[1]), Norm2(sides[2])}
longLen := len2[0]
longIdx := 0
if len2[1] > longLen {
longLen = len2[1]
longIdx = 1
}
if len2[2] > longLen {
longIdx = 2
}
// calculate vertex distance from longest side
ln := line{t[longIdx], t[(longIdx+1)%3]}
dist := ln.distance(t[(longIdx+2)%3])
return dist <= tol
}
// line is an infinite 3D line
// defined by two points on the line.
type line [2]Vec
// vecOnLine takes a value between 0 and 1 to linearly
// interpolate a point on the line.
//
// vecOnLine(0) returns l[0]
// vecOnLine(1) returns l[1]
func (l line) vecOnLine(t float64) Vec {
lineDir := Sub(l[1], l[0])
return Add(l[0], Scale(t, lineDir))
}
// distance returns the minimum euclidean distance of point p to the line.
func (l line) distance(p Vec) float64 {
// https://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html
p1 := l[0]
p2 := l[1]
num := math.Abs((p2.X-p1.X)*(p1.Y-p.Y) - (p1.X-p.X)*(p2.Y-p1.Y))
return num / math.Hypot(p2.X-p1.X, p2.Y-p1.Y)
}
|