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