2017-05-12 20:12:23 -04:00
|
|
|
package util
|
2016-07-06 21:54:00 -04:00
|
|
|
|
|
|
|
import (
|
2016-07-11 21:48:51 -04:00
|
|
|
"math"
|
2016-07-06 21:54:00 -04:00
|
|
|
)
|
|
|
|
|
2016-07-29 21:24:25 -04:00
|
|
|
const (
|
|
|
|
_pi = math.Pi
|
|
|
|
_2pi = 2 * math.Pi
|
|
|
|
_3pi4 = (3 * math.Pi) / 4.0
|
|
|
|
_4pi3 = (4 * math.Pi) / 3.0
|
|
|
|
_3pi2 = (3 * math.Pi) / 2.0
|
|
|
|
_5pi4 = (5 * math.Pi) / 4.0
|
|
|
|
_7pi4 = (7 * math.Pi) / 4.0
|
|
|
|
_pi2 = math.Pi / 2.0
|
|
|
|
_pi4 = math.Pi / 4.0
|
|
|
|
_d2r = (math.Pi / 180.0)
|
|
|
|
_r2d = (180.0 / math.Pi)
|
|
|
|
)
|
2016-07-11 21:48:51 -04:00
|
|
|
|
2016-07-29 21:24:25 -04:00
|
|
|
var (
|
|
|
|
// Math contains helper methods for common math operations.
|
|
|
|
Math = &mathUtil{}
|
|
|
|
)
|
|
|
|
|
|
|
|
type mathUtil struct{}
|
|
|
|
|
2017-02-26 04:03:47 -05:00
|
|
|
// Max returns the maximum value of a group of floats.
|
|
|
|
func (m mathUtil) Max(values ...float64) float64 {
|
|
|
|
if len(values) == 0 {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
max := values[0]
|
|
|
|
for _, v := range values {
|
|
|
|
if max < v {
|
|
|
|
max = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return max
|
|
|
|
}
|
|
|
|
|
2016-07-06 21:54:00 -04:00
|
|
|
// MinAndMax returns both the min and max in one pass.
|
2016-07-29 21:24:25 -04:00
|
|
|
func (m mathUtil) MinAndMax(values ...float64) (min float64, max float64) {
|
2016-07-06 21:54:00 -04:00
|
|
|
if len(values) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
min = values[0]
|
|
|
|
max = values[0]
|
2017-05-12 20:12:23 -04:00
|
|
|
for _, v := range values[1:] {
|
2016-07-06 21:54:00 -04:00
|
|
|
if max < v {
|
|
|
|
max = v
|
|
|
|
}
|
|
|
|
if min > v {
|
|
|
|
min = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-07-11 21:48:51 -04:00
|
|
|
// GetRoundToForDelta returns a `roundTo` value for a given delta.
|
2016-07-29 21:24:25 -04:00
|
|
|
func (m mathUtil) GetRoundToForDelta(delta float64) float64 {
|
2016-07-11 21:48:51 -04:00
|
|
|
startingDeltaBound := math.Pow(10.0, 10.0)
|
|
|
|
for cursor := startingDeltaBound; cursor > 0; cursor /= 10.0 {
|
|
|
|
if delta > cursor {
|
|
|
|
return cursor / 10.0
|
|
|
|
}
|
|
|
|
}
|
2016-07-10 04:11:47 -04:00
|
|
|
|
2016-07-11 21:48:51 -04:00
|
|
|
return 0.0
|
|
|
|
}
|
|
|
|
|
|
|
|
// RoundUp rounds up to a given roundTo value.
|
2016-07-29 21:24:25 -04:00
|
|
|
func (m mathUtil) RoundUp(value, roundTo float64) float64 {
|
2018-04-15 15:35:39 -04:00
|
|
|
if roundTo < 0.000000000000001 {
|
|
|
|
return value
|
|
|
|
}
|
2016-07-11 21:48:51 -04:00
|
|
|
d1 := math.Ceil(value / roundTo)
|
|
|
|
return d1 * roundTo
|
|
|
|
}
|
|
|
|
|
|
|
|
// RoundDown rounds down to a given roundTo value.
|
2016-07-29 21:24:25 -04:00
|
|
|
func (m mathUtil) RoundDown(value, roundTo float64) float64 {
|
2018-04-15 15:35:39 -04:00
|
|
|
if roundTo < 0.000000000000001 {
|
|
|
|
return value
|
|
|
|
}
|
2016-07-11 21:48:51 -04:00
|
|
|
d1 := math.Floor(value / roundTo)
|
|
|
|
return d1 * roundTo
|
|
|
|
}
|
|
|
|
|
2016-07-28 05:34:44 -04:00
|
|
|
// Normalize returns a set of numbers on the interval [0,1] for a given set of inputs.
|
|
|
|
// An example: 4,3,2,1 => 0.4, 0.3, 0.2, 0.1
|
|
|
|
// Caveat; the total may be < 1.0; there are going to be issues with irrational numbers etc.
|
2016-07-29 21:24:25 -04:00
|
|
|
func (m mathUtil) Normalize(values ...float64) []float64 {
|
2016-07-28 05:34:44 -04:00
|
|
|
var total float64
|
|
|
|
for _, v := range values {
|
|
|
|
total += v
|
|
|
|
}
|
|
|
|
output := make([]float64, len(values))
|
|
|
|
for x, v := range values {
|
2016-07-29 21:24:25 -04:00
|
|
|
output[x] = m.RoundDown(v/total, 0.0001)
|
2016-07-28 05:34:44 -04:00
|
|
|
}
|
|
|
|
return output
|
|
|
|
}
|
|
|
|
|
2016-07-11 21:48:51 -04:00
|
|
|
// MinInt returns the minimum of a set of integers.
|
2016-07-29 21:24:25 -04:00
|
|
|
func (m mathUtil) MinInt(values ...int) int {
|
2016-07-11 21:48:51 -04:00
|
|
|
min := math.MaxInt32
|
|
|
|
for _, v := range values {
|
|
|
|
if v < min {
|
|
|
|
min = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return min
|
|
|
|
}
|
|
|
|
|
|
|
|
// MaxInt returns the maximum of a set of integers.
|
2016-07-29 21:24:25 -04:00
|
|
|
func (m mathUtil) MaxInt(values ...int) int {
|
2016-07-11 21:48:51 -04:00
|
|
|
max := math.MinInt32
|
|
|
|
for _, v := range values {
|
|
|
|
if v > max {
|
|
|
|
max = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return max
|
2016-07-08 20:57:14 -04:00
|
|
|
}
|
2016-07-12 19:47:52 -04:00
|
|
|
|
|
|
|
// AbsInt returns the absolute value of an integer.
|
2016-07-29 21:24:25 -04:00
|
|
|
func (m mathUtil) AbsInt(value int) int {
|
2016-07-12 19:47:52 -04:00
|
|
|
if value < 0 {
|
|
|
|
return -value
|
|
|
|
}
|
|
|
|
return value
|
|
|
|
}
|
2016-07-13 14:50:22 -04:00
|
|
|
|
2017-02-28 20:55:48 -05:00
|
|
|
// AbsInt64 returns the absolute value of a long.
|
|
|
|
func (m mathUtil) AbsInt64(value int64) int64 {
|
|
|
|
if value < 0 {
|
|
|
|
return -value
|
|
|
|
}
|
|
|
|
return value
|
|
|
|
}
|
|
|
|
|
2016-10-21 15:44:37 -04:00
|
|
|
// Mean returns the mean of a set of values
|
|
|
|
func (m mathUtil) Mean(values ...float64) float64 {
|
|
|
|
return m.Sum(values...) / float64(len(values))
|
|
|
|
}
|
|
|
|
|
|
|
|
// MeanInt returns the mean of a set of integer values.
|
|
|
|
func (m mathUtil) MeanInt(values ...int) int {
|
|
|
|
return m.SumInt(values...) / len(values)
|
|
|
|
}
|
|
|
|
|
2016-07-28 17:30:00 -04:00
|
|
|
// Sum sums a set of values.
|
2016-07-29 21:24:25 -04:00
|
|
|
func (m mathUtil) Sum(values ...float64) float64 {
|
2016-07-28 17:30:00 -04:00
|
|
|
var total float64
|
|
|
|
for _, v := range values {
|
|
|
|
total += v
|
|
|
|
}
|
|
|
|
return total
|
|
|
|
}
|
|
|
|
|
|
|
|
// SumInt sums a set of values.
|
2016-07-29 21:24:25 -04:00
|
|
|
func (m mathUtil) SumInt(values ...int) int {
|
2016-07-28 17:30:00 -04:00
|
|
|
var total int
|
|
|
|
for _, v := range values {
|
|
|
|
total += v
|
|
|
|
}
|
|
|
|
return total
|
|
|
|
}
|
|
|
|
|
2016-07-13 19:11:31 -04:00
|
|
|
// PercentDifference computes the percentage difference between two values.
|
2016-07-13 18:46:51 -04:00
|
|
|
// The formula is (v2-v1)/v1.
|
2016-07-29 21:24:25 -04:00
|
|
|
func (m mathUtil) PercentDifference(v1, v2 float64) float64 {
|
|
|
|
if v1 == 0 {
|
|
|
|
return 0
|
|
|
|
}
|
2016-07-13 18:46:51 -04:00
|
|
|
return (v2 - v1) / v1
|
|
|
|
}
|
2016-07-28 05:34:44 -04:00
|
|
|
|
2016-07-28 16:22:18 -04:00
|
|
|
// DegreesToRadians returns degrees as radians.
|
2016-07-29 21:24:25 -04:00
|
|
|
func (m mathUtil) DegreesToRadians(degrees float64) float64 {
|
2016-07-28 16:22:18 -04:00
|
|
|
return degrees * _d2r
|
|
|
|
}
|
|
|
|
|
|
|
|
// RadiansToDegrees translates a radian value to a degree value.
|
2016-07-29 21:24:25 -04:00
|
|
|
func (m mathUtil) RadiansToDegrees(value float64) float64 {
|
2016-07-28 16:22:18 -04:00
|
|
|
return math.Mod(value, _2pi) * _r2d
|
|
|
|
}
|
|
|
|
|
2016-07-28 05:34:44 -04:00
|
|
|
// PercentToRadians converts a normalized value (0,1) to radians.
|
2016-07-29 21:24:25 -04:00
|
|
|
func (m mathUtil) PercentToRadians(pct float64) float64 {
|
|
|
|
return m.DegreesToRadians(360.0 * pct)
|
2016-07-28 05:34:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// RadianAdd adds a delta to a base in radians.
|
2016-07-29 21:24:25 -04:00
|
|
|
func (m mathUtil) RadianAdd(base, delta float64) float64 {
|
2016-07-28 05:34:44 -04:00
|
|
|
value := base + delta
|
|
|
|
if value > _2pi {
|
|
|
|
return math.Mod(value, _2pi)
|
|
|
|
} else if value < 0 {
|
2016-07-28 16:22:18 -04:00
|
|
|
return math.Mod(_2pi+value, _2pi)
|
2016-07-28 05:34:44 -04:00
|
|
|
}
|
|
|
|
return value
|
|
|
|
}
|
2016-07-28 16:22:18 -04:00
|
|
|
|
|
|
|
// DegreesAdd adds a delta to a base in radians.
|
2016-07-29 21:24:25 -04:00
|
|
|
func (m mathUtil) DegreesAdd(baseDegrees, deltaDegrees float64) float64 {
|
2016-07-28 16:22:18 -04:00
|
|
|
value := baseDegrees + deltaDegrees
|
|
|
|
if value > _2pi {
|
|
|
|
return math.Mod(value, 360.0)
|
|
|
|
} else if value < 0 {
|
|
|
|
return math.Mod(360.0+value, 360.0)
|
|
|
|
}
|
|
|
|
return value
|
|
|
|
}
|
|
|
|
|
|
|
|
// DegreesToCompass returns the degree value in compass / clock orientation.
|
2016-07-29 21:24:25 -04:00
|
|
|
func (m mathUtil) DegreesToCompass(deg float64) float64 {
|
|
|
|
return m.DegreesAdd(deg, -90.0)
|
2016-07-28 16:22:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// CirclePoint returns the absolute position of a circle diameter point given
|
2016-09-01 01:11:52 -04:00
|
|
|
// by the radius and the theta.
|
|
|
|
func (m mathUtil) CirclePoint(cx, cy int, radius, thetaRadians float64) (x, y int) {
|
|
|
|
x = cx + int(radius*math.Sin(thetaRadians))
|
|
|
|
y = cy - int(radius*math.Cos(thetaRadians))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m mathUtil) RotateCoordinate(cx, cy, x, y int, thetaRadians float64) (rx, ry int) {
|
|
|
|
tempX, tempY := float64(x-cx), float64(y-cy)
|
2016-09-05 16:26:12 -04:00
|
|
|
rotatedX := tempX*math.Cos(thetaRadians) - tempY*math.Sin(thetaRadians)
|
|
|
|
rotatedY := tempX*math.Sin(thetaRadians) + tempY*math.Cos(thetaRadians)
|
2016-09-05 17:03:20 -04:00
|
|
|
rx = int(rotatedX) + cx
|
2016-09-05 16:26:12 -04:00
|
|
|
ry = int(rotatedY) + cy
|
2016-07-28 16:22:18 -04:00
|
|
|
return
|
|
|
|
}
|