gonum.org/v1/gonum@v0.14.0/spatial/r3/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 r3 6 7 import "math" 8 9 // Triangle represents a triangle in 3D space and 10 // is composed by 3 vectors corresponding to the position 11 // of each of the vertices. Ordering of these vertices 12 // decides the "normal" direction. 13 // Inverting ordering of two vertices inverts the resulting direction. 14 type Triangle [3]Vec 15 16 // Centroid returns the intersection of the three medians of the triangle 17 // as a point in space. 18 func (t Triangle) Centroid() Vec { 19 return Scale(1.0/3.0, Add(Add(t[0], t[1]), t[2])) 20 } 21 22 // Normal returns the vector with direction 23 // perpendicular to the Triangle's face and magnitude 24 // twice that of the Triangle's area. The ordering 25 // of the triangle vertices decides the normal's resulting 26 // direction. The returned vector is not normalized. 27 func (t Triangle) Normal() Vec { 28 s1, s2, _ := t.sides() 29 return Cross(s1, s2) 30 } 31 32 // IsDegenerate returns true if all of triangle's vertices are 33 // within tol distance of its longest side. 34 func (t Triangle) IsDegenerate(tol float64) bool { 35 longIdx := t.longIdx() 36 // calculate vertex distance from longest side 37 ln := line{t[longIdx], t[(longIdx+1)%3]} 38 dist := ln.distance(t[(longIdx+2)%3]) 39 return dist <= tol 40 } 41 42 // longIdx returns index of the longest side. The sides 43 // of the triangles are are as follows: 44 // - Side 0 formed by vertices 0 and 1 45 // - Side 1 formed by vertices 1 and 2 46 // - Side 2 formed by vertices 0 and 2 47 func (t Triangle) longIdx() int { 48 sides := [3]Vec{Sub(t[1], t[0]), Sub(t[2], t[1]), Sub(t[0], t[2])} 49 len2 := [3]float64{Norm2(sides[0]), Norm2(sides[1]), Norm2(sides[2])} 50 longLen := len2[0] 51 longIdx := 0 52 if len2[1] > longLen { 53 longLen = len2[1] 54 longIdx = 1 55 } 56 if len2[2] > longLen { 57 longIdx = 2 58 } 59 return longIdx 60 } 61 62 // Area returns the surface area of the triangle. 63 func (t Triangle) Area() float64 { 64 // Heron's Formula, see https://en.wikipedia.org/wiki/Heron%27s_formula. 65 // Also see William M. Kahan (24 March 2000). "Miscalculating Area and Angles of a Needle-like Triangle" 66 // for more discussion. https://people.eecs.berkeley.edu/~wkahan/Triangle.pdf. 67 a, b, c := t.orderedLengths() 68 A := (c + (b + a)) * (a - (c - b)) 69 A *= (a + (c - b)) * (c + (b - a)) 70 return math.Sqrt(A) / 4 71 } 72 73 // orderedLengths returns the lengths of the sides of the triangle such that 74 // a ≤ b ≤ c. 75 func (t Triangle) orderedLengths() (a, b, c float64) { 76 s1, s2, s3 := t.sides() 77 l1 := Norm(s1) 78 l2 := Norm(s2) 79 l3 := Norm(s3) 80 // sort-3 81 if l2 < l1 { 82 l1, l2 = l2, l1 83 } 84 if l3 < l2 { 85 l2, l3 = l3, l2 86 if l2 < l1 { 87 l1, l2 = l2, l1 88 } 89 } 90 return l1, l2, l3 91 } 92 93 // sides returns vectors for each of the sides of t. 94 func (t Triangle) sides() (Vec, Vec, Vec) { 95 return Sub(t[1], t[0]), Sub(t[2], t[1]), Sub(t[0], t[2]) 96 } 97 98 // line is an infinite 3D line 99 // defined by two points on the line. 100 type line [2]Vec 101 102 // vecOnLine takes a value between 0 and 1 to linearly 103 // interpolate a point on the line. 104 // 105 // vecOnLine(0) returns l[0] 106 // vecOnLine(1) returns l[1] 107 func (l line) vecOnLine(t float64) Vec { 108 lineDir := Sub(l[1], l[0]) 109 return Add(l[0], Scale(t, lineDir)) 110 } 111 112 // distance returns the minimum euclidean distance of point p 113 // to the line. 114 func (l line) distance(p Vec) float64 { 115 // https://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html 116 num := Norm(Cross(Sub(p, l[0]), Sub(p, l[1]))) 117 return num / Norm(Sub(l[1], l[0])) 118 }