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  }