github.com/agnivade/pgm@v0.0.0-20210528073050-e2df0d9cb72d/caliper.go (about)

     1  package pgm
     2  
     3  import (
     4  	"math"
     5  )
     6  
     7  // Ported from https://github.com/bkiers/RotatingCalipers.
     8  
     9  const sigma = 0.00000000001
    10  
    11  type caliper struct {
    12  	hull         []point
    13  	pointIndex   int
    14  	currentAngle float64
    15  }
    16  
    17  func newCaliper(hull []point, pointIndex int, currentAngle float64) caliper {
    18  	return caliper{
    19  		hull:         hull,
    20  		pointIndex:   pointIndex,
    21  		currentAngle: currentAngle,
    22  	}
    23  }
    24  
    25  func (c *caliper) getAngleNextPoint() float64 {
    26  	p1 := c.hull[c.pointIndex]
    27  	p2 := c.hull[(c.pointIndex+1)%len(c.hull)]
    28  
    29  	dx := p2.x - p1.x
    30  	dy := p2.y - p1.y
    31  
    32  	angle := math.Atan2(dy, dx) * 180 / math.Pi
    33  
    34  	if angle < 0 {
    35  		return 360 + angle
    36  	}
    37  	return angle
    38  }
    39  
    40  func (c *caliper) getConstant() float64 {
    41  	p := c.hull[c.pointIndex]
    42  	return p.y - (c.getSlope() * p.x)
    43  }
    44  
    45  func (c *caliper) getDeltaAngleNextPoint() float64 {
    46  	angle := c.getAngleNextPoint()
    47  
    48  	if angle < 0 {
    49  		angle = 360 + angle - c.currentAngle
    50  	} else {
    51  		angle = angle - c.currentAngle
    52  	}
    53  
    54  	if angle < 0 {
    55  		return 360
    56  	}
    57  	return angle
    58  }
    59  
    60  func (c *caliper) getIntersection(d caliper) point {
    61  	var p point
    62  	switch {
    63  	case c.isVertical():
    64  		p.x = c.hull[c.pointIndex].x
    65  		p.y = d.getConstant()
    66  	case c.isHorizontal():
    67  		p.x = d.hull[d.pointIndex].x
    68  		p.y = c.getConstant()
    69  	default:
    70  		p.x = (d.getConstant() - c.getConstant()) / (c.getSlope() - d.getSlope())
    71  		p.y = (c.getSlope() * p.x) + c.getConstant()
    72  	}
    73  
    74  	return p
    75  }
    76  
    77  func (c *caliper) getSlope() float64 {
    78  	return math.Tan(c.currentAngle * math.Pi / 180)
    79  }
    80  
    81  func (c *caliper) isHorizontal() bool {
    82  	return (math.Abs(c.currentAngle) < sigma) || (math.Abs(c.currentAngle-180) < sigma)
    83  }
    84  
    85  func (c *caliper) isVertical() bool {
    86  	return (math.Abs(c.currentAngle-90) < sigma) || (math.Abs(c.currentAngle-270) < sigma)
    87  }
    88  
    89  func (c *caliper) rotateBy(angle float64) {
    90  	if c.getDeltaAngleNextPoint() == angle {
    91  		c.pointIndex = (c.pointIndex + 1) % len(c.hull)
    92  	}
    93  	c.currentAngle = math.Mod(c.currentAngle+angle, 360)
    94  }
    95  
    96  func getSmallestTheta(i, j, k, l caliper) float64 {
    97  	thetaI := i.getDeltaAngleNextPoint()
    98  	thetaJ := j.getDeltaAngleNextPoint()
    99  	thetaK := k.getDeltaAngleNextPoint()
   100  	thetaL := l.getDeltaAngleNextPoint()
   101  
   102  	if thetaI <= thetaJ && thetaI <= thetaK && thetaI <= thetaL {
   103  		return thetaI
   104  	} else if thetaJ <= thetaK && thetaJ <= thetaL {
   105  		return thetaJ
   106  	} else if thetaK <= thetaL {
   107  		return thetaK
   108  	} else {
   109  		return thetaL
   110  	}
   111  }