go-hep.org/x/hep@v0.38.1/fastjet/internal/delaunay/point.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 "slices" 11 12 "go-hep.org/x/hep/fastjet/internal/predicates" 13 ) 14 15 // Point in the X-Y Plane. 16 // 17 // It holds dynamic information about the 18 // adjacent triangles, the nearest neighbor and the distance to that neighbor. 19 // 20 // One should use the Equal method of Point to test whether 2 points are equal. 21 type Point struct { 22 x, y float64 // x and y are the coordinates of the point. 23 adjacentTriangles triangles // adjacentTriangles is a list of triangles containing the point. 24 nearest *Point 25 dist2 float64 // dist2 is the squared distance to the nearest neighbor. 26 // id is a unique identifier, that is assigned incrementally to a point on insertion. 27 // It is used when points are removed. Copies of the points around the point to be removed are made. 28 // The ID is set incremental in counterclockwise order. It identifies the original. It is also used 29 // to determine whether a Triangle is inside or outside the polygon formed by all those points. 30 id int 31 } 32 33 // NewPoint returns Point for the given x,y coordinates 34 func NewPoint(x, y float64) *Point { 35 return &Point{ 36 x: x, 37 y: y, 38 dist2: math.Inf(1), 39 } 40 } 41 42 // NearestNeighbor returns the nearest neighbor and the distance to that neighbor. 43 func (p *Point) NearestNeighbor() (*Point, float64) { 44 return p.nearest, math.Sqrt(p.dist2) 45 } 46 47 // Coordinates returns the x,y coordinates of a Point. 48 func (p *Point) Coordinates() (x, y float64) { 49 return p.x, p.y 50 } 51 52 // ID returns the ID of the point. It is a unique identifier that is incrementally assigned 53 // to a point on insertion. 54 func (p *Point) ID() int { 55 return p.id 56 } 57 58 func (p *Point) String() string { 59 return fmt.Sprintf("(%f, %f)", p.x, p.y) 60 } 61 62 // Equals checks whether two points are the same. 63 func (p *Point) Equals(v *Point) bool { 64 return p == v || (p.x == v.x && p.y == v.y) 65 } 66 67 // SecondNearestNeighbor looks at all adjacent points of p and returns the second nearest one 68 // and the distance to that point. 69 func (p *Point) SecondNearestNeighbor() (*Point, float64) { 70 var nearest, secondNearest *Point 71 min, secondMin := math.Inf(1), math.Inf(1) 72 if p.dist2 == 0 { 73 // p has a duplicate 74 nearest = p.nearest 75 min = 0 76 } 77 for _, t := range p.adjacentTriangles { 78 var p2, p3 *Point 79 // find the point in t that is not p 80 switch { 81 case p.Equals(t.A): 82 p2 = t.B 83 p3 = t.C 84 case p.Equals(t.B): 85 p2 = t.A 86 p3 = t.C 87 case p.Equals(t.C): 88 p2 = t.A 89 p3 = t.B 90 default: 91 panic(fmt.Errorf("delaunay: point %v not found in %v", p, t)) 92 } 93 dist := p.distance(p2) 94 switch { 95 case dist < min: 96 min, secondMin = dist, min 97 nearest, secondNearest = p2, nearest 98 if p2.dist2 == 0 { 99 // p2 has a duplicate 100 secondNearest = p2.nearest 101 secondMin = dist 102 } 103 case dist < secondMin: 104 secondMin = dist 105 secondNearest = p2 106 } 107 dist = p.distance(p3) 108 switch { 109 case dist < min: 110 min, secondMin = dist, min 111 nearest, secondNearest = p3, nearest 112 if p3.dist2 == 0 { 113 // p3 has a duplicate 114 secondNearest = p3.nearest 115 secondMin = dist 116 } 117 case dist < secondMin: 118 secondMin = dist 119 secondNearest = p3 120 } 121 } 122 return secondNearest, math.Sqrt(secondMin) 123 } 124 125 // distance returns the squared distance between two points. 126 func (p *Point) distance(v *Point) float64 { 127 dx := p.x - v.x 128 dy := p.y - v.y 129 return dx*dx + dy*dy 130 } 131 132 // findNearest looks at all adjacent points of p and finds the nearest one. 133 // p's nearest neighbor will be updated. 134 func (p *Point) findNearest() { 135 var newNearest *Point 136 min := math.Inf(1) 137 for _, t := range p.adjacentTriangles { 138 var dist float64 139 var np *Point 140 // find the point in t that is closest to p, but that is not p. 141 switch { 142 case p.Equals(t.A): 143 distB := p.distance(t.B) 144 distC := p.distance(t.C) 145 if distB <= distC { 146 dist = distB 147 np = t.B 148 } else { 149 dist = distC 150 np = t.C 151 } 152 case p.Equals(t.B): 153 distA := p.distance(t.A) 154 distC := p.distance(t.C) 155 if distA <= distC { 156 dist = distA 157 np = t.A 158 } else { 159 dist = distC 160 np = t.C 161 } 162 case p.Equals(t.C): 163 distA := p.distance(t.A) 164 distB := p.distance(t.B) 165 if distA <= distB { 166 dist = distA 167 np = t.A 168 } else { 169 dist = distB 170 np = t.B 171 } 172 default: 173 panic(fmt.Errorf("delaunay: point P%s not found in T%s", p, t)) 174 } 175 // check whether the distance found is smaller than the previous smallest distance. 176 if dist < min { 177 min = dist 178 newNearest = np 179 } 180 } 181 // update p's nearest Neighbor 182 p.dist2 = min 183 p.nearest = newNearest 184 } 185 186 // surroundingPoints returns the points that surround p in counterclockwise order. 187 func (p *Point) surroundingPoints() []*Point { 188 points := make([]*Point, len(p.adjacentTriangles)) 189 t := p.adjacentTriangles[0] 190 // j is the index of the previous point 191 j := 1 192 // k is the index of the previous triangle 193 k := 0 194 switch { 195 case p.Equals(t.A): 196 points[0] = t.B 197 points[1] = t.C 198 case p.Equals(t.B): 199 points[0] = t.C 200 points[1] = t.A 201 case p.Equals(t.C): 202 points[0] = t.A 203 points[1] = t.B 204 default: 205 panic(fmt.Errorf("delaunay: point %v not in adjacent triangle %v", p, t)) 206 } 207 for i := 0; j < len(points)-1; { 208 if i >= len(p.adjacentTriangles) { 209 panic(fmt.Errorf("delaunay: internal error with adjacent triangles for %v. Can't find counterclockwise neighbor of %v", p, points[j])) 210 } 211 // it needs to find the triangle next to k and not k again 212 if p.adjacentTriangles[i].Equals(p.adjacentTriangles[k]) { 213 i++ 214 continue 215 } 216 t = p.adjacentTriangles[i] 217 switch { 218 case points[j].Equals(t.A): 219 j++ 220 points[j] = t.B 221 k = i 222 // start the loop over 223 i = 0 224 continue 225 case points[j].Equals(t.B): 226 j++ 227 points[j] = t.C 228 k = i 229 // start the loop over 230 i = 0 231 continue 232 case points[j].Equals(t.C): 233 j++ 234 points[j] = t.A 235 k = i 236 // start the loop over 237 i = 0 238 continue 239 } 240 i++ 241 } 242 return points 243 } 244 245 // findClockwiseTriangle finds the next triangle in clockwise order. 246 func (p *Point) findClockwiseTriangle(t *Triangle) *Triangle { 247 // points in a triangle are ordered counter clockwise 248 var p2 *Point 249 // find point counterclockwise of p 250 switch { 251 case p.Equals(t.A): 252 p2 = t.B 253 case p.Equals(t.B): 254 p2 = t.C 255 case p.Equals(t.C): 256 p2 = t.A 257 default: 258 panic(fmt.Errorf("delaunay: can't find Point %v in Triangle %v", p, t)) 259 } 260 for _, t1 := range p.adjacentTriangles { 261 for _, t2 := range p2.adjacentTriangles { 262 if !t1.Equals(t) && t1.Equals(t2) { 263 return t1 264 } 265 } 266 } 267 panic(fmt.Errorf("delaunay: no clockwise neighbor of Triangle %v around Point %v", t, p)) 268 } 269 270 // inTriangle checks whether the point is in the triangle and whether it is on an edge. 271 func (p *Point) inTriangle(t *Triangle) location { 272 o1 := predicates.Orientation(t.A.x, t.A.y, t.B.x, t.B.y, p.x, p.y) 273 o2 := predicates.Orientation(t.B.x, t.B.y, t.C.x, t.C.y, p.x, p.y) 274 o3 := predicates.Orientation(t.C.x, t.C.y, t.A.x, t.A.y, p.x, p.y) 275 if o1 == predicates.CCW && o2 == predicates.CCW && o3 == predicates.CCW { 276 return inside 277 } 278 if o1 == predicates.CW || o2 == predicates.CW || o3 == predicates.CW { 279 return outside 280 } 281 return onEdge 282 } 283 284 // location is the position of a point relative to a triangle 285 type location int 286 287 const ( 288 inside location = iota 289 onEdge 290 outside 291 ) 292 293 func (l location) String() string { 294 switch l { 295 case inside: 296 return "Inside Triangle" 297 case onEdge: 298 return "On Edge of Triangle" 299 case outside: 300 return "Outside Triangle" 301 default: 302 panic(fmt.Errorf("delaunay: unknown location %d", int(l))) 303 } 304 } 305 306 type points []*Point 307 308 // remove removes given points from a slice of points. 309 // 310 // remove will remove all occurrences of the points. 311 func (ps points) remove(pts ...*Point) points { 312 out := make(points, 0, len(ps)) 313 for _, p := range ps { 314 keep := true 315 if slices.ContainsFunc(pts, p.Equals) { 316 keep = false 317 } 318 if keep { 319 out = append(out, p) 320 } 321 322 } 323 return out 324 } 325 326 // polyArea finds the area of an irregular polygon. 327 // The points need to be in clockwise order. 328 func (points points) polyArea() float64 { 329 var area float64 330 j := len(points) - 1 331 for i := range points { 332 area += (points[j].x + points[i].x) * (points[j].y - points[i].y) 333 j = i 334 } 335 return area * 0.5 336 }