go-hep.org/x/hep@v0.38.1/fastjet/internal/delaunay/delaunay.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  	"math/rand"
    11  
    12  	"go-hep.org/x/hep/fastjet/internal/predicates"
    13  )
    14  
    15  // Delaunay holds necessary information for the delaunay triangulation.
    16  type Delaunay struct {
    17  	// triangles is a slice of all triangles that have been created. It is used to get the final
    18  	// list of triangles in the delaunay triangulation.
    19  	triangles triangles
    20  	// root is a triangle that contains all points. It is used as the starting point in the hierarchy
    21  	// to locate a point. The variable root's nil-ness also indicates which method to use to locate a point.
    22  	root *Triangle
    23  	n    int // n is the number of points inserted. It is used to assign the id to points.
    24  }
    25  
    26  // HierarchicalDelaunay creates a Delaunay Triangulation using the delaunay hierarchy.
    27  //
    28  // The worst case time complexity is O(n*log(n)).
    29  //
    30  // The three root points (-2^30,-2^30), (2^30,-2^30) and (0,2^30) can't be in the circumcircle of any three non-collinear points in the
    31  // triangulation. Additionally all points have to be inside the Triangle formed by these three points.
    32  // If any of these conditions doesn't apply use the WalkDelaunay function instead.
    33  // 2^30 = 1,073,741,824
    34  //
    35  // To locate a point this algorithm uses a Directed Acyclic Graph with a single root.
    36  // All triangles in the current triangulation are leaf triangles.
    37  // To find the triangle which contains the point, the algorithm follows the graph until
    38  // a leaf is reached.
    39  //
    40  // Duplicate points don't get inserted, but the nearest neighbor is set to the corresponding point.
    41  // When a duplicate point is removed nothing happens.
    42  func HierarchicalDelaunay() *Delaunay {
    43  	a := NewPoint(-1<<30, -1<<30)
    44  	b := NewPoint(1<<30, -1<<30)
    45  	c := NewPoint(0, 1<<30)
    46  	root := NewTriangle(a, b, c)
    47  	return &Delaunay{
    48  		root: root,
    49  		n:    0,
    50  	}
    51  }
    52  
    53  func WalkDelaunay(points []*Point, r *rand.Rand) *Delaunay {
    54  	panic(fmt.Errorf("delaunay: WalkDelaunay not implemented"))
    55  }
    56  
    57  // Triangles returns the triangles that form the delaunay triangulation.
    58  func (d *Delaunay) Triangles() []*Triangle {
    59  	// rt are triangles that contain the root points
    60  	rt := make(triangles, len(d.root.A.adjacentTriangles)+len(d.root.B.adjacentTriangles)+len(d.root.C.adjacentTriangles))
    61  	n := copy(rt, d.root.A.adjacentTriangles)
    62  	n += copy(rt[n:], d.root.B.adjacentTriangles)
    63  	copy(rt[n:], d.root.C.adjacentTriangles)
    64  	// make a copy so that the original slice is not modified
    65  	triangles := make(triangles, len(d.triangles))
    66  	copy(triangles, d.triangles)
    67  	// keep only the triangles that are in the current triangulation
    68  	triangles = triangles.keepCurrentTriangles()
    69  	// remove all triangles that contain the root points
    70  	return triangles.remove(rt...)
    71  }
    72  
    73  // Insert inserts the point into the triangulation. It returns the points
    74  // whose nearest neighbor changed due to the insertion. The slice may contain
    75  // duplicates.
    76  func (d *Delaunay) Insert(p *Point) (updatedNearestNeighbor []*Point) {
    77  	p.id = d.n
    78  	d.n++
    79  	if len(p.adjacentTriangles) == 0 {
    80  		p.adjacentTriangles = make(triangles, 0)
    81  	}
    82  	p.adjacentTriangles = p.adjacentTriangles[:0]
    83  	t, l := d.locatePointHierarchy(p, d.root)
    84  	var updated points
    85  	switch l {
    86  	case inside:
    87  		updated = d.insertInside(p, t)
    88  	case onEdge:
    89  		updated = d.insertOnEdge(p, t)
    90  	default:
    91  		panic(fmt.Errorf("delaunay: no triangle containing point %v", p))
    92  	}
    93  	return updated.remove(d.root.A, d.root.B, d.root.C)
    94  }
    95  
    96  // Remove removes the point from the triangulation. It returns the points
    97  // whose nearest neighbor changed due to the removal. The slice may contain
    98  // duplicates.
    99  func (d *Delaunay) Remove(p *Point) (updatedNearestNeighbor []*Point) {
   100  	if len(p.adjacentTriangles) < 3 {
   101  		if p.dist2 == 0 {
   102  			// must be a duplicate point, therefore don't panic
   103  			return updatedNearestNeighbor
   104  		}
   105  		panic(fmt.Errorf("delaunay: can't remove point %v, not enough adjacent triangles", p))
   106  	}
   107  	var updated points
   108  	pts := p.surroundingPoints()
   109  	ts := make([]*Triangle, len(p.adjacentTriangles))
   110  	copy(ts, p.adjacentTriangles)
   111  	for _, t := range ts {
   112  		updtemp := t.remove()
   113  		updated = append(updated, updtemp...)
   114  	}
   115  	updtemp := d.retriangulateAndSew(pts, ts)
   116  	return append(updated, updtemp...).remove(d.root.A, d.root.B, d.root.C)
   117  }
   118  
   119  // locatePointHierarchy locates the point using the delaunay hierarchy.
   120  //
   121  // It returns the triangle that contains the point and the location
   122  // indicates whether it is on an edge or not. The worst case time complexity
   123  // for locating a point is O(log(n)).
   124  func (d *Delaunay) locatePointHierarchy(p *Point, t *Triangle) (*Triangle, location) {
   125  	l := p.inTriangle(t)
   126  	if l == outside {
   127  		return nil, l
   128  	}
   129  	if len(t.children) == 0 {
   130  		// leaf triangle
   131  		return t, l
   132  	}
   133  	// go down the children to eventually reach a leaf triangle
   134  	for _, child := range t.children {
   135  		t, l = d.locatePointHierarchy(p, child)
   136  		if l != outside {
   137  			return t, l
   138  		}
   139  	}
   140  	panic(fmt.Errorf("delaunay: error locating Point %v in Triangle %v", p, t))
   141  }
   142  
   143  // insertInside inserts a point inside a triangle. It returns the points whose nearest
   144  // neighbor changed during the process.
   145  func (d *Delaunay) insertInside(p *Point, t *Triangle) []*Point {
   146  	// form three new triangles
   147  	t1 := NewTriangle(t.A, t.B, p)
   148  	t2 := NewTriangle(t.B, t.C, p)
   149  	t3 := NewTriangle(t.C, t.A, p)
   150  	updated := t1.add()
   151  	updtemp := t2.add()
   152  	updated = append(updated, updtemp...)
   153  	updtemp = t3.add()
   154  	updated = append(updated, updtemp...)
   155  	updtemp = t.remove()
   156  	updated = append(updated, updtemp...)
   157  	t.children = append(t.children, t1, t2, t3)
   158  	d.triangles = append(d.triangles, t1, t2, t3)
   159  	// change the edges so it is a valid delaunay triangulation
   160  	updtemp = d.swapDelaunay(t1, p)
   161  	updated = append(updated, updtemp...)
   162  	updtemp = d.swapDelaunay(t2, p)
   163  	updated = append(updated, updtemp...)
   164  	updtemp = d.swapDelaunay(t3, p)
   165  	updated = append(updated, updtemp...)
   166  	return updated
   167  }
   168  
   169  // insertOnEdge inserts a point on an Edge between two triangles. It returns the points
   170  // whose nearest neighbor changed.
   171  func (d *Delaunay) insertOnEdge(p *Point, t *Triangle) []*Point {
   172  	// Check if p is a duplicate
   173  	switch {
   174  	case p.Equals(t.A):
   175  		if p.id == t.A.id {
   176  			panic(fmt.Errorf("delaunay: Point %v was previously inserted", p))
   177  		}
   178  		p.nearest = t.A
   179  		p.dist2 = 0
   180  		t.A.nearest = p
   181  		t.A.dist2 = 0
   182  		return []*Point{p, t.A}
   183  	case p.Equals(t.B):
   184  		if p.id == t.B.id {
   185  			panic(fmt.Errorf("delaunay: Point %v was previously inserted", p))
   186  		}
   187  		p.nearest = t.B
   188  		p.dist2 = 0
   189  		t.B.nearest = p
   190  		t.B.dist2 = 0
   191  		return []*Point{p, t.B}
   192  	case p.Equals(t.C):
   193  		if p.id == t.C.id {
   194  			panic(fmt.Errorf("delaunay: Point %v was previously inserted", p))
   195  		}
   196  		p.nearest = t.C
   197  		p.dist2 = 0
   198  		t.C.nearest = p
   199  		t.C.dist2 = 0
   200  		return []*Point{p, t.C}
   201  	}
   202  	// To increase performance find the points in t, where p1 has the least adjacent triangles and
   203  	// p2 the second least.
   204  	var p1, p2, p3 *Point
   205  	if len(t.A.adjacentTriangles) < len(t.B.adjacentTriangles) {
   206  		if len(t.C.adjacentTriangles) < len(t.A.adjacentTriangles) {
   207  			p1 = t.C
   208  			p2 = t.A
   209  			p3 = t.B
   210  		} else {
   211  			p1 = t.A
   212  			if len(t.C.adjacentTriangles) < len(t.B.adjacentTriangles) {
   213  				p2 = t.C
   214  				p3 = t.B
   215  			} else {
   216  				p2 = t.B
   217  				p3 = t.C
   218  			}
   219  		}
   220  	} else {
   221  		if len(t.C.adjacentTriangles) < len(t.B.adjacentTriangles) {
   222  			p1 = t.C
   223  			p2 = t.B
   224  			p3 = t.A
   225  		} else {
   226  			p1 = t.B
   227  			if len(t.A.adjacentTriangles) < len(t.C.adjacentTriangles) {
   228  				p2 = t.A
   229  				p3 = t.C
   230  			} else {
   231  				p2 = t.C
   232  				p3 = t.A
   233  			}
   234  		}
   235  	}
   236  	// pA1 and pA2 will be the points adjacent to the edge. pO1 and pO2 will be the points opposite to the edge
   237  	var pA1, pA2, pO1, pO2 *Point
   238  	// find second triangle adjacent to edge
   239  	var t2 *Triangle
   240  	// exactly two points in each adjacent triangle to the edge have to be adjacent to that edge.
   241  	found := false
   242  	for _, ta := range p1.adjacentTriangles {
   243  		if !ta.Equals(t) && p.inTriangle(ta) == onEdge {
   244  			found = true
   245  			t2 = ta
   246  			break
   247  		}
   248  	}
   249  	if found {
   250  		pA1 = p1
   251  		found = false
   252  		for _, ta := range p2.adjacentTriangles {
   253  			if ta.Equals(t2) {
   254  				found = true
   255  				break
   256  			}
   257  		}
   258  		if found {
   259  			pA2 = p2
   260  			pO1 = p3
   261  		} else {
   262  			pA2 = p3
   263  			pO1 = p2
   264  		}
   265  	} else {
   266  		pO1 = p1
   267  		for _, ta := range p2.adjacentTriangles {
   268  			if !ta.Equals(t) && p.inTriangle(ta) == onEdge {
   269  				found = true
   270  				t2 = ta
   271  				break
   272  			}
   273  		}
   274  		if !found {
   275  			panic(fmt.Errorf("delaunay: can't find second triangle with edge to %v and %v on edge", t, p))
   276  		}
   277  		pA1 = p2
   278  		pA2 = p3
   279  	}
   280  	switch {
   281  	case !t2.A.Equals(pA1) && !t2.A.Equals(pA2):
   282  		pO2 = t2.A
   283  	case !t2.B.Equals(pA1) && !t2.B.Equals(pA2):
   284  		pO2 = t2.B
   285  	case !t2.C.Equals(pA1) && !t2.C.Equals(pA2):
   286  		pO2 = t2.C
   287  	default:
   288  		panic(fmt.Errorf("delaunay: no point in %v that is not adjacent to the edge of %v", t2, p))
   289  	}
   290  	// form four new triangles
   291  	nt1 := NewTriangle(pA1, p, pO1)
   292  	nt2 := NewTriangle(pA1, p, pO2)
   293  	nt3 := NewTriangle(pA2, p, pO1)
   294  	nt4 := NewTriangle(pA2, p, pO2)
   295  	updated := nt1.add()
   296  	updtemp := nt2.add()
   297  	updated = append(updated, updtemp...)
   298  	updtemp = nt3.add()
   299  	updated = append(updated, updtemp...)
   300  	updtemp = nt4.add()
   301  	updated = append(updated, updtemp...)
   302  	updtemp = t.remove()
   303  	updated = append(updated, updtemp...)
   304  	updtemp = t2.remove()
   305  	updated = append(updated, updtemp...)
   306  	t.children = append(t.children, nt1, nt3)
   307  	t2.children = append(t2.children, nt2, nt4)
   308  	d.triangles = append(d.triangles, nt1, nt2, nt3, nt4)
   309  	// change the edges so it is a valid delaunay triangulation
   310  	updtemp = d.swapDelaunay(nt1, p)
   311  	updated = append(updated, updtemp...)
   312  	updtemp = d.swapDelaunay(nt2, p)
   313  	updated = append(updated, updtemp...)
   314  	updtemp = d.swapDelaunay(nt3, p)
   315  	updated = append(updated, updtemp...)
   316  	updtemp = d.swapDelaunay(nt4, p)
   317  	updated = append(updated, updtemp...)
   318  	return updated
   319  }
   320  
   321  // swapDelaunay finds the triangle adjacent to t and opposite to p.
   322  // Then it checks whether p is in the circumcircle. If p is in the circumcircle
   323  // that means that the triangle is not a valid delaunay triangle.
   324  // Therefore the edge in between the two triangles is swapped, creating
   325  // two new triangles that need to be checked.
   326  // It returns the points whose nearest neighbors changed during the
   327  // process.
   328  func (d *Delaunay) swapDelaunay(t *Triangle, p *Point) []*Point {
   329  	// find points in the triangle that are not p
   330  	var p2, p3 *Point
   331  	switch {
   332  	case p.Equals(t.A):
   333  		p2 = t.B
   334  		p3 = t.C
   335  	case p.Equals(t.B):
   336  		p2 = t.C
   337  		p3 = t.A
   338  	case p.Equals(t.C):
   339  		p2 = t.A
   340  		p3 = t.B
   341  	default:
   342  		panic(fmt.Errorf("delaunay: can't find point %v in Triangle %v", p, t))
   343  	}
   344  	// find triangle opposite to p
   345  	var ta *Triangle
   346  loop:
   347  	for _, t1 := range p2.adjacentTriangles {
   348  		for _, t2 := range p3.adjacentTriangles {
   349  			if !t1.Equals(t) && t1.Equals(t2) {
   350  				ta = t1
   351  				break loop
   352  			}
   353  		}
   354  
   355  	}
   356  	var updated []*Point
   357  	if ta == nil {
   358  		return updated
   359  	}
   360  	pos := predicates.Incircle(ta.A.x, ta.A.y, ta.B.x, ta.B.y, ta.C.x, ta.C.y, p.x, p.y)
   361  	// swap edges if p is inside the circumcircle of ta
   362  	if pos == predicates.Inside {
   363  		var nt1, nt2 *Triangle
   364  		nt1, nt2, updated = d.swapEdge(t, ta)
   365  		updtemp := d.swapDelaunay(nt1, p)
   366  		updated = append(updated, updtemp...)
   367  		updtemp = d.swapDelaunay(nt2, p)
   368  		updated = append(updated, updtemp...)
   369  	}
   370  	return updated
   371  }
   372  
   373  // swapEdge swaps edge between two triangles.
   374  // The edge in the middle of the two triangles is removed and
   375  // an edge between the two opposite points is added.
   376  func (d *Delaunay) swapEdge(t1, t2 *Triangle) (nt1, nt2 *Triangle, updated []*Point) {
   377  	// find points adjacent and opposite to edge
   378  	var adj1, adj2, opp1, opp2 *Point
   379  	switch {
   380  	case !t1.A.Equals(t2.A) && !t1.A.Equals(t2.B) && !t1.A.Equals(t2.C):
   381  		adj1 = t1.A
   382  		opp1 = t1.B
   383  		opp2 = t1.C
   384  	case !t1.B.Equals(t2.A) && !t1.B.Equals(t2.B) && !t1.B.Equals(t2.C):
   385  		adj1 = t1.B
   386  		opp1 = t1.A
   387  		opp2 = t1.C
   388  	case !t1.C.Equals(t2.A) && !t1.C.Equals(t2.B) && !t1.C.Equals(t2.C):
   389  		adj1 = t1.C
   390  		opp1 = t1.B
   391  		opp2 = t1.A
   392  	default:
   393  		panic(fmt.Errorf("delaunay: triangle T1%v is equal to T2%v", t1, t2))
   394  	}
   395  	switch {
   396  	case !t2.A.Equals(t1.A) && !t2.A.Equals(t1.B) && !t2.A.Equals(t1.C):
   397  		adj2 = t2.A
   398  	case !t2.B.Equals(t1.A) && !t2.B.Equals(t1.B) && !t2.B.Equals(t1.C):
   399  		adj2 = t2.B
   400  	case !t2.C.Equals(t1.A) && !t2.C.Equals(t1.B) && !t2.C.Equals(t1.C):
   401  		adj2 = t2.C
   402  	default:
   403  		panic(fmt.Errorf("delaunay: triangle T2%v is equal to T1%v", t2, t1))
   404  	}
   405  	// create two new triangles
   406  	nt1 = NewTriangle(adj1, adj2, opp1)
   407  	nt2 = NewTriangle(adj1, adj2, opp2)
   408  	updated = nt1.add()
   409  	updtemp := nt2.add()
   410  	updated = append(updated, updtemp...)
   411  	updtemp = t1.remove()
   412  	updated = append(updated, updtemp...)
   413  	updtemp = t2.remove()
   414  	updated = append(updated, updtemp...)
   415  	t1.children = append(t1.children, nt1, nt2)
   416  	t2.children = append(t2.children, nt1, nt2)
   417  	d.triangles = append(d.triangles, nt1, nt2)
   418  	return nt1, nt2, updated
   419  }
   420  
   421  // retriangulateAndSew uses the re-triangulate and sew method to find the delaunay triangles
   422  // inside the polygon formed by the CCW-ordered points. If k = len(points) then it has a
   423  // worst-time complexity of O(k*log(k)).
   424  func (d *Delaunay) retriangulateAndSew(points []*Point, parents []*Triangle) (updated []*Point) {
   425  	nd := HierarchicalDelaunay()
   426  	// change limits to create a root triangle that's far outside of the original root triangle
   427  	nd.root.A.x = -1 << 35
   428  	nd.root.A.y = -1 << 35
   429  	nd.root.B.x = 1 << 35
   430  	nd.root.B.y = -1 << 35
   431  	nd.root.C.y = 1 << 35
   432  	// make copies of points on polygon and run a delaunay triangulation with them
   433  	// indices of copies are in counter clockwise order, so that with the help of
   434  	// areCounterclockwise it can be determined if a point is inside or outside the polygon.
   435  	// A,B,C are ordered counterclockwise, so if the numbers in A,B,C are counterclockwise it is
   436  	// inside the polygon.
   437  	copies := make([]*Point, len(points))
   438  	for i, p := range points {
   439  		copies[i] = NewPoint(p.x, p.y)
   440  		nd.Insert(copies[i])
   441  	}
   442  	ts := nd.Triangles()
   443  	triangles := make([]*Triangle, 0, len(ts))
   444  	for _, t := range ts {
   445  		a := t.A.id
   446  		b := t.B.id
   447  		c := t.C.id
   448  		// only keep triangles that are inside the polygon
   449  		// points are inside the polygon if the order of the indices inside the triangle
   450  		// is counterclockwise
   451  		if areCounterclockwise(a, b, c) {
   452  			tr := NewTriangle(points[a], points[b], points[c])
   453  			updtemp := tr.add()
   454  			updated = append(updated, updtemp...)
   455  			triangles = append(triangles, tr)
   456  		}
   457  	}
   458  	d.triangles = append(d.triangles, triangles...)
   459  	for i := range parents {
   460  		parents[i].children = append(parents[i].children, triangles...)
   461  	}
   462  	return updated
   463  }
   464  
   465  // areCounterclockwise is a helper function for retriangulateAndSew. It returns
   466  // whether three points are in counterclockwise order.
   467  // Since the points in triangle are ordered counterclockwise and the indices around
   468  // the polygon are ordered counterclockwise checking if the indices of A,B,C
   469  // are counter clockwise is enough.
   470  func areCounterclockwise(a, b, c int) bool {
   471  	if b < c {
   472  		return a < b || c < a
   473  	}
   474  	return a < b && c < a
   475  }
   476  
   477  // VoronoiCell returns the Vornoi points of a point in clockwise order
   478  // and the area those points enclose.
   479  //
   480  // If a point is on the border of the Delaunay triangulation the area will be Infinity
   481  // and the first and last point of the cell will be part of a root triangle.
   482  // The function will panic if the number of adjacent triangles is < 3.
   483  func (d *Delaunay) VoronoiCell(p *Point) ([]*Point, float64) {
   484  	if len(p.adjacentTriangles) < 3 {
   485  		panic(fmt.Errorf("delaunay: point %v doesn't have enough adjacent triangles", p))
   486  	}
   487  	// border1 is set to the index of the first voronoi point that is part of a root triangle
   488  	border1 := -1
   489  	// border2 is set to the index of the first voronoi point that is part of a root triangle
   490  	border2 := -1
   491  	voronoi := make(points, len(p.adjacentTriangles))
   492  	t := p.adjacentTriangles[0]
   493  	// check whether the triangle contains any root points
   494  	if t.A.Equals(d.root.A) || t.A.Equals(d.root.B) || t.A.Equals(d.root.C) ||
   495  		t.B.Equals(d.root.A) || t.B.Equals(d.root.B) || t.B.Equals(d.root.C) ||
   496  		t.C.Equals(d.root.A) || t.C.Equals(d.root.B) || t.C.Equals(d.root.C) {
   497  		border1 = 0
   498  	}
   499  	x, y := t.circumcenter()
   500  	voronoi[0] = NewPoint(x, y)
   501  	for i := 1; i < len(p.adjacentTriangles); i++ {
   502  		t = p.findClockwiseTriangle(t)
   503  		// check whether the triangle contains any root points
   504  		if t.A.Equals(d.root.A) || t.A.Equals(d.root.B) || t.A.Equals(d.root.C) ||
   505  			t.B.Equals(d.root.A) || t.B.Equals(d.root.B) || t.B.Equals(d.root.C) ||
   506  			t.C.Equals(d.root.A) || t.C.Equals(d.root.B) || t.C.Equals(d.root.C) {
   507  			if border1 == -1 {
   508  				border1 = i
   509  			} else {
   510  				border2 = i
   511  			}
   512  		}
   513  		x, y = t.circumcenter()
   514  		voronoi[i] = NewPoint(x, y)
   515  	}
   516  	if border1 == -1 {
   517  		area := voronoi.polyArea()
   518  		return voronoi, area
   519  	}
   520  	if border2 == -1 {
   521  		panic(fmt.Errorf("delaunay: point %v has exactly one adjacent root triangle", p))
   522  	}
   523  	// at this point border1 is either 0 or >= 1.
   524  	// If necessary reposition the points, so that the border points are the first and last points
   525  	if border1 != 0 || border2 != len(voronoi)-1 {
   526  		if border2 != border1+1 {
   527  			panic(fmt.Errorf("delaunay: point %v has adjacent root triangles at index %d and %d in the voronoi slice", p, border1, border2))
   528  		}
   529  		voronoi = append(voronoi[border2:], voronoi[:border2]...)
   530  	}
   531  	return voronoi, math.Inf(1)
   532  }