2019-04-10 00:35:44 +00:00
|
|
|
package render
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Point holds an X,Y coordinate value.
|
|
|
|
type Point struct {
|
2019-12-28 01:35:42 +00:00
|
|
|
X int
|
|
|
|
Y int
|
2019-04-10 00:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Common points.
|
|
|
|
var (
|
|
|
|
Origin Point
|
|
|
|
)
|
|
|
|
|
|
|
|
// NewPoint makes a new Point at an X,Y coordinate.
|
2019-12-28 01:35:42 +00:00
|
|
|
func NewPoint(x, y int) Point {
|
2019-04-10 00:35:44 +00:00
|
|
|
return Point{
|
|
|
|
X: x,
|
|
|
|
Y: y,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p Point) String() string {
|
|
|
|
return fmt.Sprintf("%d,%d", p.X, p.Y)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ParsePoint to parse a point from its string representation.
|
|
|
|
func ParsePoint(v string) (Point, error) {
|
|
|
|
halves := strings.Split(v, ",")
|
|
|
|
if len(halves) != 2 {
|
|
|
|
return Point{}, fmt.Errorf("'%s': not a valid coordinate string", v)
|
|
|
|
}
|
|
|
|
x, errX := strconv.Atoi(halves[0])
|
|
|
|
y, errY := strconv.Atoi(halves[1])
|
|
|
|
if errX != nil || errY != nil {
|
|
|
|
return Point{}, fmt.Errorf("invalid coordinate string (X: %v; Y: %v)",
|
|
|
|
errX,
|
|
|
|
errY,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
return Point{
|
2019-12-28 01:35:42 +00:00
|
|
|
X: int(x),
|
|
|
|
Y: int(y),
|
2019-04-10 00:35:44 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsZero returns if the point is the zero value.
|
|
|
|
func (p Point) IsZero() bool {
|
|
|
|
return p.X == 0 && p.Y == 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// Inside returns whether the Point falls inside the rect.
|
|
|
|
//
|
|
|
|
// NOTICE: the W and H are zero-relative, so a 100x100 box at coordinate
|
|
|
|
// X,Y would still have W,H of 100.
|
|
|
|
func (p Point) Inside(r Rect) bool {
|
|
|
|
var (
|
|
|
|
x1 = r.X
|
|
|
|
y1 = r.Y
|
|
|
|
x2 = r.X + r.W
|
|
|
|
y2 = r.Y + r.H
|
|
|
|
)
|
|
|
|
return ((p.X >= x1 && p.X <= x2) &&
|
|
|
|
(p.Y >= y1 && p.Y <= y2))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add (or subtract) the other point to your current point.
|
|
|
|
func (p *Point) Add(other Point) {
|
|
|
|
p.X += other.X
|
|
|
|
p.Y += other.Y
|
|
|
|
}
|
|
|
|
|
2019-04-14 22:25:03 +00:00
|
|
|
// Subtract the other point from your current point.
|
|
|
|
func (p *Point) Subtract(other Point) {
|
|
|
|
p.X -= other.X
|
|
|
|
p.Y -= other.Y
|
|
|
|
}
|
|
|
|
|
2020-04-07 05:59:40 +00:00
|
|
|
// Compare the point to another, returning a delta coordinate.
|
|
|
|
//
|
|
|
|
// If the two points are equal the result has X=0 Y=0. Otherwise the X and Y
|
|
|
|
// return values will be positive or negative numbers of how you could modify
|
|
|
|
// the current Point to be equal to the other.
|
|
|
|
func (p *Point) Compare(other Point) Point {
|
|
|
|
return Point{
|
|
|
|
X: other.X - p.X,
|
|
|
|
Y: other.Y - p.Y,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-10 00:35:44 +00:00
|
|
|
// MarshalText to convert the point into text so that a render.Point may be used
|
|
|
|
// as a map key and serialized to JSON.
|
|
|
|
func (p *Point) MarshalText() ([]byte, error) {
|
|
|
|
return []byte(fmt.Sprintf("%d,%d", p.X, p.Y)), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalText to restore it from text.
|
|
|
|
func (p *Point) UnmarshalText(b []byte) error {
|
|
|
|
halves := strings.Split(strings.Trim(string(b), `"`), ",")
|
|
|
|
if len(halves) != 2 {
|
|
|
|
return fmt.Errorf("'%s': not a valid coordinate string", b)
|
|
|
|
}
|
|
|
|
|
|
|
|
x, errX := strconv.Atoi(halves[0])
|
|
|
|
y, errY := strconv.Atoi(halves[1])
|
|
|
|
if errX != nil || errY != nil {
|
|
|
|
return fmt.Errorf("Point.UnmarshalJSON: Atoi errors (X=%s Y=%s)",
|
|
|
|
errX,
|
|
|
|
errY,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-12-28 01:35:42 +00:00
|
|
|
p.X = int(x)
|
|
|
|
p.Y = int(y)
|
2019-04-10 00:35:44 +00:00
|
|
|
return nil
|
|
|
|
}
|