github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/avatar/identicon/polygon.go (about)

     1  // Copyright 2023 The GitBundle Inc. All rights reserved.
     2  // Copyright 2017 The Gitea Authors. All rights reserved.
     3  // Use of this source code is governed by a MIT-style
     4  // license that can be found in the LICENSE file.
     5  
     6  // Copied and modified from https://github.com/issue9/identicon/ (MIT License)
     7  
     8  package identicon
     9  
    10  var (
    11  	// cos(0),cos(90),cos(180),cos(270)
    12  	cos = []int{1, 0, -1, 0}
    13  
    14  	// sin(0),sin(90),sin(180),sin(270)
    15  	sin = []int{0, 1, 0, -1}
    16  )
    17  
    18  // rotate the points by center point (x,y)
    19  // angle: [0,1,2,3] means [0,90,180,270] degree
    20  func rotate(points []int, x, y, angle int) {
    21  	// the angle is only used internally, and it has been guaranteed to be 0/1/2/3, so we do not check it again
    22  	for i := 0; i < len(points); i += 2 {
    23  		px, py := points[i]-x, points[i+1]-y
    24  		points[i] = px*cos[angle] - py*sin[angle] + x
    25  		points[i+1] = px*sin[angle] + py*cos[angle] + y
    26  	}
    27  }
    28  
    29  // check whether the point is inside the polygon (defined by the points)
    30  // the first and the last point must be the same
    31  func pointInPolygon(x, y int, polygonPoints []int) bool {
    32  	if len(polygonPoints) < 8 { // a valid polygon must have more than 2 points
    33  		return false
    34  	}
    35  
    36  	// reference: nonzero winding rule, https://en.wikipedia.org/wiki/Nonzero-rule
    37  	// split the plane into two by the check point horizontally:
    38  	//   y>0,includes (x>0 && y==0)
    39  	//   y<0,includes (x<0 && y==0)
    40  	//
    41  	// then scan every point in the polygon.
    42  	//
    43  	// if current point and previous point are in different planes (eg: curY>0 && prevY<0),
    44  	// check the clock-direction from previous point to current point (use check point as origin).
    45  	// if the direction is clockwise, then r++, otherwise then r--
    46  	// finally, if 2==abs(r), then the check point is inside the polygon
    47  
    48  	r := 0
    49  	prevX, prevY := polygonPoints[0], polygonPoints[1]
    50  	prev := (prevY > y) || ((prevX > x) && (prevY == y))
    51  	for i := 2; i < len(polygonPoints); i += 2 {
    52  		currX, currY := polygonPoints[i], polygonPoints[i+1]
    53  		curr := (currY > y) || ((currX > x) && (currY == y))
    54  
    55  		if curr == prev {
    56  			prevX, prevY = currX, currY
    57  			continue
    58  		}
    59  
    60  		if mul := (prevX-x)*(currY-y) - (currX-x)*(prevY-y); mul >= 0 {
    61  			r++
    62  		} else { // mul < 0
    63  			r--
    64  		}
    65  		prevX, prevY = currX, currY
    66  		prev = curr
    67  	}
    68  
    69  	return r == 2 || r == -2
    70  }