go-chart/util/math.go

268 lines
6.1 KiB
Go
Raw Normal View History

package util
2016-07-07 03:54:00 +02:00
import (
2016-07-12 03:48:51 +02:00
"math"
2016-07-07 03:54:00 +02:00
"time"
)
2016-07-30 03:24:25 +02: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-12 03:48:51 +02:00
2016-07-30 03:24:25 +02:00
var (
// Math contains helper methods for common math operations.
Math = &mathUtil{}
)
type mathUtil struct{}
2017-02-26 10:03:47 +01: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-07 03:54:00 +02:00
// MinAndMax returns both the min and max in one pass.
2016-07-30 03:24:25 +02:00
func (m mathUtil) MinAndMax(values ...float64) (min float64, max float64) {
2016-07-07 03:54:00 +02:00
if len(values) == 0 {
return
}
min = values[0]
max = values[0]
for _, v := range values[1:] {
2016-07-07 03:54:00 +02:00
if max < v {
max = v
}
if min > v {
min = v
}
}
return
}
// MinAndMaxOfTime returns the min and max of a given set of times
// in one pass.
2016-07-30 03:24:25 +02:00
func (m mathUtil) MinAndMaxOfTime(values ...time.Time) (min time.Time, max time.Time) {
2016-07-07 03:54:00 +02:00
if len(values) == 0 {
return
}
min = values[0]
max = values[0]
for _, v := range values[1:] {
2016-07-07 03:54:00 +02:00
if max.Before(v) {
max = v
}
if min.After(v) {
min = v
}
}
return
}
2016-07-12 03:48:51 +02:00
// GetRoundToForDelta returns a `roundTo` value for a given delta.
2016-07-30 03:24:25 +02:00
func (m mathUtil) GetRoundToForDelta(delta float64) float64 {
2016-07-12 03:48:51 +02: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 10:11:47 +02:00
2016-07-12 03:48:51 +02:00
return 0.0
}
// RoundUp rounds up to a given roundTo value.
2016-07-30 03:24:25 +02:00
func (m mathUtil) RoundUp(value, roundTo float64) float64 {
2016-07-12 03:48:51 +02:00
d1 := math.Ceil(value / roundTo)
return d1 * roundTo
}
// RoundDown rounds down to a given roundTo value.
2016-07-30 03:24:25 +02:00
func (m mathUtil) RoundDown(value, roundTo float64) float64 {
2016-07-12 03:48:51 +02:00
d1 := math.Floor(value / roundTo)
return d1 * roundTo
}
2016-07-28 11:34:44 +02: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-30 03:24:25 +02:00
func (m mathUtil) Normalize(values ...float64) []float64 {
2016-07-28 11:34:44 +02:00
var total float64
for _, v := range values {
total += v
}
output := make([]float64, len(values))
for x, v := range values {
2016-07-30 03:24:25 +02:00
output[x] = m.RoundDown(v/total, 0.0001)
2016-07-28 11:34:44 +02:00
}
return output
}
2016-07-12 03:48:51 +02:00
// MinInt returns the minimum of a set of integers.
2016-07-30 03:24:25 +02:00
func (m mathUtil) MinInt(values ...int) int {
2016-07-12 03:48:51 +02: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-30 03:24:25 +02:00
func (m mathUtil) MaxInt(values ...int) int {
2016-07-12 03:48:51 +02:00
max := math.MinInt32
for _, v := range values {
if v > max {
max = v
}
}
return max
2016-07-09 02:57:14 +02:00
}
// AbsInt returns the absolute value of an integer.
2016-07-30 03:24:25 +02:00
func (m mathUtil) AbsInt(value int) int {
if value < 0 {
return -value
}
return value
}
2016-07-13 20:50:22 +02:00
2017-03-01 02:55:48 +01: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 21:44:37 +02: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 23:30:00 +02:00
// Sum sums a set of values.
2016-07-30 03:24:25 +02:00
func (m mathUtil) Sum(values ...float64) float64 {
2016-07-28 23:30:00 +02:00
var total float64
for _, v := range values {
total += v
}
return total
}
// SumInt sums a set of values.
2016-07-30 03:24:25 +02:00
func (m mathUtil) SumInt(values ...int) int {
2016-07-28 23:30:00 +02:00
var total int
for _, v := range values {
total += v
}
return total
}
2016-07-14 01:11:31 +02:00
// PercentDifference computes the percentage difference between two values.
2016-07-14 00:46:51 +02:00
// The formula is (v2-v1)/v1.
2016-07-30 03:24:25 +02:00
func (m mathUtil) PercentDifference(v1, v2 float64) float64 {
if v1 == 0 {
return 0
}
2016-07-14 00:46:51 +02:00
return (v2 - v1) / v1
}
2016-07-28 11:34:44 +02:00
2016-07-28 22:22:18 +02:00
// DegreesToRadians returns degrees as radians.
2016-07-30 03:24:25 +02:00
func (m mathUtil) DegreesToRadians(degrees float64) float64 {
2016-07-28 22:22:18 +02:00
return degrees * _d2r
}
// RadiansToDegrees translates a radian value to a degree value.
2016-07-30 03:24:25 +02:00
func (m mathUtil) RadiansToDegrees(value float64) float64 {
2016-07-28 22:22:18 +02:00
return math.Mod(value, _2pi) * _r2d
}
2016-07-28 11:34:44 +02:00
// PercentToRadians converts a normalized value (0,1) to radians.
2016-07-30 03:24:25 +02:00
func (m mathUtil) PercentToRadians(pct float64) float64 {
return m.DegreesToRadians(360.0 * pct)
2016-07-28 11:34:44 +02:00
}
// RadianAdd adds a delta to a base in radians.
2016-07-30 03:24:25 +02:00
func (m mathUtil) RadianAdd(base, delta float64) float64 {
2016-07-28 11:34:44 +02:00
value := base + delta
if value > _2pi {
return math.Mod(value, _2pi)
} else if value < 0 {
2016-07-28 22:22:18 +02:00
return math.Mod(_2pi+value, _2pi)
2016-07-28 11:34:44 +02:00
}
return value
}
2016-07-28 22:22:18 +02:00
// DegreesAdd adds a delta to a base in radians.
2016-07-30 03:24:25 +02:00
func (m mathUtil) DegreesAdd(baseDegrees, deltaDegrees float64) float64 {
2016-07-28 22:22:18 +02: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-30 03:24:25 +02:00
func (m mathUtil) DegreesToCompass(deg float64) float64 {
return m.DegreesAdd(deg, -90.0)
2016-07-28 22:22:18 +02:00
}
// CirclePoint returns the absolute position of a circle diameter point given
2016-09-01 07:11:52 +02: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 22:26:12 +02:00
rotatedX := tempX*math.Cos(thetaRadians) - tempY*math.Sin(thetaRadians)
rotatedY := tempX*math.Sin(thetaRadians) + tempY*math.Cos(thetaRadians)
2016-09-05 23:03:20 +02:00
rx = int(rotatedX) + cx
2016-09-05 22:26:12 +02:00
ry = int(rotatedY) + cy
2016-07-28 22:22:18 +02:00
return
}
2017-05-17 02:50:17 +02:00
func (m mathUtil) LinesIntersect(l0x0, l0y0, l0x1, l0y1, l1x0, l1y0, l1x1, l1y1 float64) bool {
var s0x, s0y, s1x, s1y float64
s0x = l0x1 - l0x0
s0y = l0y1 - l0y0
s1x = l1x1 - l1x0
s1y = l1y1 - l1y0
var s, t float64
s = (-s0y*(l0x0-l1x0) + s0x*(l0y0-l1y0)) / (-s1x*s0y + s0x*s1y)
t = (s1x*(l0y0-l1y0) - s1y*(l0x0-l1x0)) / (-s1x*s0y + s0x*s1y)
return s >= 0 && s <= 1 && t >= 0 && t <= 1
}