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 }