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  }