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  }