go-hep.org/x/hep@v0.38.1/fastjet/internal/delaunay/triangle.go (about)

     1  // Copyright ©2017 The go-hep 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 delaunay
     6  
     7  import (
     8  	"fmt"
     9  	"math"
    10  
    11  	"go-hep.org/x/hep/fastjet/internal/predicates"
    12  )
    13  
    14  // Triangle is a set of three points that make up a triangle. It stores hierarchical information to find triangles.
    15  type Triangle struct {
    16  	// children are triangles that lead to the removal of this triangle.
    17  	// When a point is inserted the triangle that contains the point is found by going down the hierarchical tree.
    18  	// The tree's root is the root triangle and the children slice contains the children triangles.
    19  	children []*Triangle
    20  	// A,B,C are the CCW-oriented points that make up the triangle
    21  	A, B, C *Point
    22  	// isInTriangulation holds whether a triangle is part of the triangulation
    23  	isInTriangulation bool
    24  }
    25  
    26  // NewTriangle returns a triangle formed out of the three given points.
    27  func NewTriangle(a, b, c *Point) *Triangle {
    28  	// order the points counter clockwise
    29  	o := predicates.Orientation(a.x, a.y, b.x, b.y, c.x, c.y)
    30  	switch o {
    31  	case predicates.CW:
    32  		a, b = b, a
    33  	case predicates.Colinear:
    34  		panic(fmt.Errorf("delaunay: Can't form triangle, because Points a%v, b%v and c%v are colinear", a, b, c))
    35  	}
    36  	return &Triangle{
    37  		A: a,
    38  		B: b,
    39  		C: c,
    40  	}
    41  }
    42  
    43  // add is used when the triangle t is added to the delaunay triangulation.
    44  // It updates the information of the points in t.
    45  // It returns all points whose nearest neighbor was updated.
    46  func (t *Triangle) add() []*Point {
    47  	t.isInTriangulation = true
    48  	// update the adjacent lists
    49  	t.A.adjacentTriangles = append(t.A.adjacentTriangles, t)
    50  	t.B.adjacentTriangles = append(t.B.adjacentTriangles, t)
    51  	t.C.adjacentTriangles = append(t.C.adjacentTriangles, t)
    52  	// update nearest neighbor if one of the points is closer than current nearest.
    53  	// First find the nearest neighbor in t.
    54  	// Then check whether local min distance is smaller than current min distance.
    55  	updated := make([]*Point, 0, 3)
    56  	distAB := t.A.distance(t.B)
    57  	distBC := t.B.distance(t.C)
    58  	distCA := t.C.distance(t.A)
    59  	var localMin float64
    60  	var localNearest *Point
    61  	if distAB < distCA {
    62  		localMin = distAB
    63  		localNearest = t.B
    64  	} else {
    65  		localMin = distCA
    66  		localNearest = t.C
    67  	}
    68  	if localMin < t.A.dist2 {
    69  		t.A.dist2 = localMin
    70  		t.A.nearest = localNearest
    71  		updated = append(updated, t.A)
    72  	}
    73  	if distAB < distBC {
    74  		localMin = distAB
    75  		localNearest = t.A
    76  	} else {
    77  		localMin = distBC
    78  		localNearest = t.C
    79  	}
    80  	if localMin < t.B.dist2 {
    81  		t.B.dist2 = localMin
    82  		t.B.nearest = localNearest
    83  		updated = append(updated, t.B)
    84  	}
    85  	if distBC < distCA {
    86  		localMin = distBC
    87  		localNearest = t.B
    88  	} else {
    89  		localMin = distCA
    90  		localNearest = t.A
    91  	}
    92  	if localMin < t.C.dist2 {
    93  		t.C.dist2 = localMin
    94  		t.C.nearest = localNearest
    95  		updated = append(updated, t.C)
    96  	}
    97  	return updated
    98  }
    99  
   100  // remove is used when the triangle t is removed from the delaunay triangulation.
   101  // It updates the information of the points in t.
   102  // It returns all points whose nearest neighbor was updated.
   103  func (t *Triangle) remove() []*Point {
   104  	t.isInTriangulation = false
   105  	t.A.adjacentTriangles = t.A.adjacentTriangles.remove(t)
   106  	t.B.adjacentTriangles = t.B.adjacentTriangles.remove(t)
   107  	t.C.adjacentTriangles = t.C.adjacentTriangles.remove(t)
   108  	// update the nearest neighbor if the nearest neighbor is in t.
   109  	updated := make([]*Point, 0, 3)
   110  	if t.A.nearest.Equals(t.B) || t.A.nearest.Equals(t.C) {
   111  		t.A.findNearest()
   112  		updated = append(updated, t.A)
   113  	}
   114  	if t.B.nearest.Equals(t.A) || t.B.nearest.Equals(t.C) {
   115  		t.B.findNearest()
   116  		updated = append(updated, t.B)
   117  	}
   118  	if t.C.nearest.Equals(t.A) || t.C.nearest.Equals(t.B) {
   119  		t.C.findNearest()
   120  		updated = append(updated, t.C)
   121  	}
   122  	return updated
   123  }
   124  
   125  // circumcenter returns the x,y coordinates of the circumcenter of the triangle
   126  func (t *Triangle) circumcenter() (x, y float64) {
   127  	// TODO: there are few divisions by the same value, store the inverse of these delta values and use that instead
   128  	m1 := (t.A.x - t.B.x) / (t.B.y - t.A.y)
   129  	m2 := (t.A.x - t.C.x) / (t.C.y - t.A.y)
   130  	b1 := (t.A.y+t.B.y)*0.5 - m1*(t.A.x+t.B.x)*0.5
   131  	b2 := (t.A.y+t.C.y)*0.5 - m2*(t.A.x+t.C.x)*0.5
   132  	x = (b2 - b1) / (m1 - m2)
   133  	y = m1*x + b1
   134  	if math.IsNaN(y) {
   135  		m1 = (t.B.x - t.A.x) / (t.A.y - t.B.y)
   136  		m2 = (t.B.x - t.C.x) / (t.C.y - t.B.y)
   137  		b1 = (t.B.y+t.A.y)*0.5 - m1*(t.B.x+t.A.x)*0.5
   138  		b2 = (t.B.y+t.C.y)*0.5 - m2*(t.B.x+t.C.x)*0.5
   139  		x = (b2 - b1) / (m1 - m2)
   140  		y = m1*x + b1
   141  		if math.IsNaN(y) {
   142  			m1 = (t.C.x - t.A.x) / (t.A.y - t.C.y)
   143  			m2 = (t.C.x - t.B.x) / (t.B.y - t.C.y)
   144  			b1 = (t.C.y+t.A.y)*0.5 - m1*(t.C.x+t.A.x)*0.5
   145  			b2 = (t.C.y+t.B.y)*0.5 - m2*(t.C.x+t.B.x)*0.5
   146  			x = (b2 - b1) / (m1 - m2)
   147  			y = m1*x + b1
   148  			if math.IsNaN(y) {
   149  				// Should never reach this point. To reach this point either all three y or x coordinates
   150  				// would have to be the same, which is impossible since there are no collinear triangles.
   151  				// Or two y variables and two x variables would have to be the same. That is impossible,
   152  				// because of the way this is set up. The variables with the same x coordinates would have to
   153  				// be the same as the variables with the same y coordinates and triangles are not made up
   154  				// of duplicates.
   155  				panic(fmt.Errorf("delaunay: error calculating the circumcenter of %v", t))
   156  			}
   157  		}
   158  	}
   159  	return x, y
   160  }
   161  
   162  // Equals checks whether two triangles are the same.
   163  func (t *Triangle) Equals(s *Triangle) bool {
   164  	return t == s ||
   165  		(t.A.Equals(s.A) || t.A.Equals(s.B) || t.A.Equals(s.C)) &&
   166  			(t.B.Equals(s.A) || t.B.Equals(s.B) || t.B.Equals(s.C)) &&
   167  			(t.C.Equals(s.A) || t.C.Equals(s.B) || t.C.Equals(s.C))
   168  }
   169  
   170  func (t *Triangle) String() string {
   171  	return fmt.Sprintf("{A%s, B%s, C%s}", t.A, t.B, t.C)
   172  }
   173  
   174  type triangles []*Triangle
   175  
   176  // remove removes given triangles from a slice of triangles.
   177  //
   178  // remove may modify the content of the input slice of triangles to remove from the receiver.
   179  // remove assumes there are no duplicate triangles in the receiver list of triangles.
   180  // remove will fail to remove the duplicates if that assumption does not hold.
   181  func (ts triangles) remove(triangles ...*Triangle) triangles {
   182  	k := len(triangles) - 1
   183  	for i := len(ts) - 1; i >= 0; i-- {
   184  		if k < 0 {
   185  			break
   186  		}
   187  		for j := k; j >= 0; j-- {
   188  			if triangles[j].Equals(ts[i]) {
   189  				n := len(ts) - 1
   190  				ts[i], ts[n] = ts[n], nil
   191  				ts = ts[:n]
   192  				triangles[j] = triangles[k]
   193  				k--
   194  				break
   195  			}
   196  		}
   197  	}
   198  	return ts
   199  }
   200  
   201  // keepCurrentTriangles returns all triangles that are in the current triangulation.
   202  func (ts triangles) keepCurrentTriangles() triangles {
   203  	triangles := make(triangles, 0, len(ts))
   204  	for _, t := range ts {
   205  		if t.isInTriangulation {
   206  			triangles = append(triangles, t)
   207  		}
   208  	}
   209  	return triangles
   210  }