183 lines
4.9 KiB
Go
183 lines
4.9 KiB
Go
package chart
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
|
|
util "github.com/wcharczuk/go-chart/util"
|
|
)
|
|
|
|
// Box2d is a box with (4) independent corners.
|
|
// It is used when dealing with ~rotated~ boxes.
|
|
type Box2d struct {
|
|
TopLeft, TopRight, BottomRight, BottomLeft Point
|
|
}
|
|
|
|
// Points returns the constituent points of the box.
|
|
func (bc Box2d) Points() []Point {
|
|
return []Point{
|
|
bc.TopRight,
|
|
bc.BottomRight,
|
|
bc.BottomLeft,
|
|
bc.TopLeft,
|
|
}
|
|
}
|
|
|
|
// Box return the Box2d as a regular box.
|
|
func (bc Box2d) Box() Box {
|
|
return Box{
|
|
Top: int(bc.Top()),
|
|
Left: int(bc.Left()),
|
|
Right: int(bc.Right()),
|
|
Bottom: int(bc.Bottom()),
|
|
}
|
|
}
|
|
|
|
// Top returns the top-most corner y value.
|
|
func (bc Box2d) Top() float64 {
|
|
return math.Min(bc.TopLeft.Y, bc.TopRight.Y)
|
|
}
|
|
|
|
// Left returns the left-most corner x value.
|
|
func (bc Box2d) Left() float64 {
|
|
return math.Min(bc.TopLeft.X, bc.BottomLeft.X)
|
|
}
|
|
|
|
// Right returns the right-most corner x value.
|
|
func (bc Box2d) Right() float64 {
|
|
return math.Max(bc.TopRight.X, bc.BottomRight.X)
|
|
}
|
|
|
|
// Bottom returns the bottom-most corner y value.
|
|
func (bc Box2d) Bottom() float64 {
|
|
return math.Max(bc.BottomLeft.Y, bc.BottomLeft.Y)
|
|
}
|
|
|
|
// Width returns the width
|
|
func (bc Box2d) Width() float64 {
|
|
minLeft := math.Min(bc.TopLeft.X, bc.BottomLeft.X)
|
|
maxRight := math.Max(bc.TopRight.X, bc.BottomRight.X)
|
|
return maxRight - minLeft
|
|
}
|
|
|
|
// Height returns the height
|
|
func (bc Box2d) Height() float64 {
|
|
minTop := math.Min(bc.TopLeft.Y, bc.TopRight.Y)
|
|
maxBottom := math.Max(bc.BottomLeft.Y, bc.BottomRight.Y)
|
|
return maxBottom - minTop
|
|
}
|
|
|
|
// Center returns the center of the box
|
|
func (bc Box2d) Center() (x, y float64) {
|
|
left := util.Math.Mean(bc.TopLeft.X, bc.BottomLeft.X)
|
|
right := util.Math.Mean(bc.TopRight.X, bc.BottomRight.X)
|
|
x = ((right - left) / 2.0) + left
|
|
|
|
top := util.Math.Mean(bc.TopLeft.Y, bc.TopRight.Y)
|
|
bottom := util.Math.Mean(bc.BottomLeft.Y, bc.BottomRight.Y)
|
|
y = ((bottom - top) / 2.0) + top
|
|
|
|
return
|
|
}
|
|
|
|
// Rotate rotates the box.
|
|
func (bc Box2d) Rotate(thetaDegrees float64) Box2d {
|
|
cx, cy := bc.Center()
|
|
|
|
thetaRadians := util.Math.DegreesToRadians(thetaDegrees)
|
|
|
|
tlx, tly := util.Math.RotateCoordinate(int(cx), int(cy), int(bc.TopLeft.X), int(bc.TopLeft.Y), thetaRadians)
|
|
trx, try := util.Math.RotateCoordinate(int(cx), int(cy), int(bc.TopRight.X), int(bc.TopRight.Y), thetaRadians)
|
|
brx, bry := util.Math.RotateCoordinate(int(cx), int(cy), int(bc.BottomRight.X), int(bc.BottomRight.Y), thetaRadians)
|
|
blx, bly := util.Math.RotateCoordinate(int(cx), int(cy), int(bc.BottomLeft.X), int(bc.BottomLeft.Y), thetaRadians)
|
|
|
|
return Box2d{
|
|
TopLeft: Point{float64(tlx), float64(tly)},
|
|
TopRight: Point{float64(trx), float64(try)},
|
|
BottomRight: Point{float64(brx), float64(bry)},
|
|
BottomLeft: Point{float64(blx), float64(bly)},
|
|
}
|
|
}
|
|
|
|
// Shift shifts a box by a given x and y value.
|
|
func (bc Box2d) Shift(x, y float64) Box2d {
|
|
return Box2d{
|
|
TopLeft: bc.TopLeft.Shift(x, y),
|
|
TopRight: bc.TopRight.Shift(x, y),
|
|
BottomRight: bc.BottomRight.Shift(x, y),
|
|
BottomLeft: bc.BottomLeft.Shift(x, y),
|
|
}
|
|
}
|
|
|
|
// Equals returns if the box equals another box.
|
|
func (bc Box2d) Equals(other Box2d) bool {
|
|
return bc.TopLeft.Equals(other.TopLeft) &&
|
|
bc.TopRight.Equals(other.TopRight) &&
|
|
bc.BottomRight.Equals(other.BottomRight) &&
|
|
bc.BottomLeft.Equals(other.BottomLeft)
|
|
}
|
|
|
|
// Overlaps returns if two boxes overlap.
|
|
func (bc Box2d) Overlaps(other Box2d) bool {
|
|
pa := bc.Points()
|
|
pb := other.Points()
|
|
for i := 0; i < 4; i++ {
|
|
for j := 0; j < 4; j++ {
|
|
pa0 := pa[i]
|
|
pa1 := pa[(i+1)%4]
|
|
|
|
pb0 := pb[j]
|
|
pb1 := pb[(j+1)%4]
|
|
|
|
if util.Math.LinesIntersect(pa0.X, pa0.Y, pa1.X, pa1.Y, pb0.X, pb0.Y, pb1.X, pb1.Y) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Grow grows a box by a given set of dimensions.
|
|
func (bc Box2d) Grow(by Box) Box2d {
|
|
top, left, right, bottom := float64(by.Top), float64(by.Left), float64(by.Right), float64(by.Bottom)
|
|
return Box2d{
|
|
TopLeft: Point{X: bc.TopLeft.X - left, Y: bc.TopLeft.Y - top},
|
|
TopRight: Point{X: bc.TopRight.X + right, Y: bc.TopRight.Y - top},
|
|
BottomRight: Point{X: bc.BottomRight.X + right, Y: bc.BottomRight.Y + bottom},
|
|
BottomLeft: Point{X: bc.BottomLeft.X - left, Y: bc.BottomLeft.Y + bottom},
|
|
}
|
|
}
|
|
|
|
func (bc Box2d) String() string {
|
|
return fmt.Sprintf("Box2d{%s,%s,%s,%s}", bc.TopLeft.String(), bc.TopRight.String(), bc.BottomRight.String(), bc.BottomLeft.String())
|
|
}
|
|
|
|
// Point is an X,Y pair
|
|
type Point struct {
|
|
X, Y float64
|
|
}
|
|
|
|
// Shift shifts a point.
|
|
func (p Point) Shift(x, y float64) Point {
|
|
return Point{
|
|
X: p.X + x,
|
|
Y: p.Y + y,
|
|
}
|
|
}
|
|
|
|
// DistanceTo calculates the distance to another point.
|
|
func (p Point) DistanceTo(other Point) float64 {
|
|
dx := math.Pow(p.X-other.X, 2)
|
|
dy := math.Pow(p.Y-other.Y, 2)
|
|
return math.Pow(dx+dy, 0.5)
|
|
}
|
|
|
|
// Equals returns if a point equals another point.
|
|
func (p Point) Equals(other Point) bool {
|
|
return p.X == other.X && p.Y == other.Y
|
|
}
|
|
|
|
// String returns a string representation of the point.
|
|
func (p Point) String() string {
|
|
return fmt.Sprintf("(%.2f,%.2f)", p.X, p.Y)
|
|
}
|