go-chart/mathutil.go

253 lines
5.5 KiB
Go
Raw Permalink Normal View History

2019-02-13 19:09:26 -05:00
package chart
2016-07-06 21:54:00 -04:00
2019-02-13 19:09:26 -05:00
import "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
2019-02-13 19:09:26 -05:00
// MinMax returns the minimum and maximum of a given set of values.
func MinMax(values ...float64) (min, max float64) {
if len(values) == 0 {
return
}
2016-07-29 21:24:25 -04:00
2019-02-13 19:09:26 -05:00
max = values[0]
min = values[0]
var value float64
for index := 1; index < len(values); index++ {
value = values[index]
if value < min {
min = value
}
if value > max {
max = value
}
}
return
}
2016-07-29 21:24:25 -04:00
2019-02-13 19:09:26 -05:00
// MinInt returns the minimum int.
func MinInt(values ...int) (min int) {
2017-02-26 04:03:47 -05:00
if len(values) == 0 {
2019-02-13 19:09:26 -05:00
return
2017-02-26 04:03:47 -05:00
}
2019-02-13 19:09:26 -05:00
min = values[0]
var value int
for index := 1; index < len(values); index++ {
value = values[index]
if value < min {
min = value
2017-02-26 04:03:47 -05:00
}
}
2019-02-13 19:09:26 -05:00
return
2017-02-26 04:03:47 -05:00
}
2019-02-13 19:09:26 -05:00
// MaxInt returns the maximum int.
func MaxInt(values ...int) (max int) {
2016-07-06 21:54:00 -04:00
if len(values) == 0 {
return
}
2019-02-13 19:09:26 -05:00
2016-07-06 21:54:00 -04:00
max = values[0]
2019-02-13 19:09:26 -05:00
var value int
for index := 1; index < len(values); index++ {
value = values[index]
if value > max {
max = value
2016-07-06 21:54:00 -04:00
}
}
return
}
2019-02-13 19:09:26 -05:00
// AbsInt returns the absolute value of an int.
func AbsInt(value int) int {
if value < 0 {
return -value
2016-07-11 21:48:51 -04:00
}
2019-02-13 19:09:26 -05:00
return value
}
2016-07-10 04:11:47 -04:00
2019-02-13 19:09:26 -05:00
// DegreesToRadians returns degrees as radians.
func DegreesToRadians(degrees float64) float64 {
return degrees * _d2r
}
// RadiansToDegrees translates a radian value to a degree value.
func RadiansToDegrees(value float64) float64 {
return math.Mod(value, _2pi) * _r2d
}
// PercentToRadians converts a normalized value (0,1) to radians.
func PercentToRadians(pct float64) float64 {
return DegreesToRadians(360.0 * pct)
}
// RadianAdd adds a delta to a base in radians.
func RadianAdd(base, delta float64) float64 {
value := base + delta
if value > _2pi {
return math.Mod(value, _2pi)
} else if value < 0 {
return math.Mod(_2pi+value, _2pi)
}
return value
}
// DegreesAdd adds a delta to a base in radians.
func DegreesAdd(baseDegrees, deltaDegrees float64) float64 {
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.
func DegreesToCompass(deg float64) float64 {
return DegreesAdd(deg, -90.0)
}
// CirclePoint returns the absolute position of a circle diameter point given
// by the radius and the theta.
func 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
}
// RotateCoordinate rotates a coordinate around a given center by a theta in radians.
func RotateCoordinate(cx, cy, x, y int, thetaRadians float64) (rx, ry int) {
tempX, tempY := float64(x-cx), float64(y-cy)
rotatedX := tempX*math.Cos(thetaRadians) - tempY*math.Sin(thetaRadians)
rotatedY := tempX*math.Sin(thetaRadians) + tempY*math.Cos(thetaRadians)
rx = int(rotatedX) + cx
ry = int(rotatedY) + cy
return
2016-07-11 21:48:51 -04:00
}
// RoundUp rounds up to a given roundTo value.
2019-02-13 19:09:26 -05:00
func 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.
2019-02-13 19:09:26 -05:00
func 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.
2019-02-13 19:09:26 -05:00
func 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 {
2019-02-13 19:09:26 -05:00
output[x] = RoundDown(v/total, 0.0001)
2016-07-28 05:34:44 -04:00
}
return output
}
2016-10-21 15:44:37 -04:00
// Mean returns the mean of a set of values
2019-02-13 19:09:26 -05:00
func Mean(values ...float64) float64 {
return Sum(values...) / float64(len(values))
2016-10-21 15:44:37 -04:00
}
// MeanInt returns the mean of a set of integer values.
2019-02-13 19:09:26 -05:00
func MeanInt(values ...int) int {
return SumInt(values...) / len(values)
2016-10-21 15:44:37 -04:00
}
2016-07-28 17:30:00 -04:00
// Sum sums a set of values.
2019-02-13 19:09:26 -05:00
func 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.
2019-02-13 19:09:26 -05:00
func 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.
2019-02-13 19:09:26 -05:00
func PercentDifference(v1, v2 float64) float64 {
2016-07-29 21:24:25 -04:00
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
2019-02-13 19:09:26 -05:00
// GetRoundToForDelta returns a `roundTo` value for a given delta.
func GetRoundToForDelta(delta float64) float64 {
startingDeltaBound := math.Pow(10.0, 10.0)
for cursor := startingDeltaBound; cursor > 0; cursor /= 10.0 {
if delta > cursor {
return cursor / 10.0
}
}
2016-07-28 16:22:18 -04:00
2019-02-13 19:09:26 -05:00
return 0.0
2016-07-28 05:34:44 -04:00
}
2019-02-13 19:09:26 -05:00
// RoundPlaces rounds an input to a given places.
func RoundPlaces(input float64, places int) (rounded float64) {
if math.IsNaN(input) {
return 0.0
2016-07-28 05:34:44 -04:00
}
2016-07-28 16:22:18 -04:00
2019-02-13 19:09:26 -05:00
sign := 1.0
if input < 0 {
sign = -1
input *= -1
2016-07-28 16:22:18 -04:00
}
2019-02-13 19:09:26 -05:00
precision := math.Pow(10, float64(places))
digit := input * precision
_, decimal := math.Modf(digit)
2016-07-28 16:22:18 -04:00
2019-02-13 19:09:26 -05:00
if decimal >= 0.5 {
rounded = math.Ceil(digit)
} else {
rounded = math.Floor(digit)
}
return rounded / precision * sign
2016-09-01 01:11:52 -04:00
}
2019-02-13 19:09:26 -05:00
func f64i(value float64) int {
r := RoundPlaces(value, 0)
return int(r)
2016-07-28 16:22:18 -04:00
}