forked from cerc-io/ipld-eth-server
155 lines
4.0 KiB
Go
155 lines
4.0 KiB
Go
|
// CookieJar - A contestant's algorithm toolbox
|
||
|
// Copyright (c) 2013 Peter Szilagyi. All rights reserved.
|
||
|
//
|
||
|
// CookieJar is dual licensed: use of this source code is governed by a BSD
|
||
|
// license that can be found in the LICENSE file. Alternatively, the CookieJar
|
||
|
// toolbox may be used in accordance with the terms and conditions contained
|
||
|
// in a signed written agreement between you and the author(s).
|
||
|
|
||
|
package geometry
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"math"
|
||
|
)
|
||
|
|
||
|
// Two dimensional line.
|
||
|
type Line2 struct {
|
||
|
A, B, C float64
|
||
|
}
|
||
|
|
||
|
// Allocates and returns a new 2D line in canonical form.
|
||
|
func NewLine2(a, b, c float64) *Line2 {
|
||
|
if a == 0 && b == 0 {
|
||
|
panic(fmt.Sprintf("coefficients a and b simultaneously 0"))
|
||
|
}
|
||
|
return &Line2{a, b, c}
|
||
|
}
|
||
|
|
||
|
// Sets the canonical form coefficients on l (ax + by + c = 0) and returns l.
|
||
|
func (l *Line2) SetCanon(a, b, c float64) *Line2 {
|
||
|
l.A, l.B, l.C = a, b, c
|
||
|
return l
|
||
|
}
|
||
|
|
||
|
// Sets the slope form coefficients on l (y = mx + c) and returns l.
|
||
|
func (l *Line2) SetSlope(m, c float64) *Line2 {
|
||
|
l.A, l.B, l.C = -m, 1, -c
|
||
|
return l
|
||
|
}
|
||
|
|
||
|
// Sets the coordinate form coefficients on l ((y-y0)*(x1-x0) = (x-x0)*(y1-y0)) and returns l.
|
||
|
func (l *Line2) SetPoint(p0, p1 *Point2) *Line2 {
|
||
|
if p0.Equal(p1) {
|
||
|
panic(fmt.Sprintf("same point given twice: %v.", p0))
|
||
|
}
|
||
|
l.A, l.B, l.C = p0.Y-p1.Y, p1.X-p0.X, p0.X*(p1.Y-p0.Y)-p0.Y*(p1.X-p0.X)
|
||
|
return l
|
||
|
}
|
||
|
|
||
|
// Returns whether l is horizontal.
|
||
|
func (l *Line2) Horizontal() bool {
|
||
|
return l.A == 0
|
||
|
}
|
||
|
|
||
|
// Returns whether l is vertical.
|
||
|
func (l *Line2) Vertical() bool {
|
||
|
return l.B == 0
|
||
|
}
|
||
|
|
||
|
// Returns the slope/gradient m from the form y = mx + c. Returns NaN if l is vertical.
|
||
|
func (l *Line2) Slope() float64 {
|
||
|
if !l.Vertical() {
|
||
|
return -l.A / l.B
|
||
|
}
|
||
|
return math.NaN()
|
||
|
}
|
||
|
|
||
|
// Returns the x-intercept of the line (y = 0). Returns NaN if l is horizontal.
|
||
|
func (l *Line2) InterceptX() float64 {
|
||
|
if !l.Horizontal() {
|
||
|
return -l.C / l.A
|
||
|
}
|
||
|
return math.NaN()
|
||
|
}
|
||
|
|
||
|
// Returns the y-intercept of the line (x = 0). Returns NaN if l is vertical.
|
||
|
func (l *Line2) InterceptY() float64 {
|
||
|
if !l.Vertical() {
|
||
|
return -l.C / l.B
|
||
|
}
|
||
|
return math.NaN()
|
||
|
}
|
||
|
|
||
|
// Calculates the value x, the image of which is y. Returns NaN if l is horizontal.
|
||
|
func (l *Line2) X(y float64) float64 {
|
||
|
if !l.Horizontal() {
|
||
|
return -(l.C + l.B*y) / l.A
|
||
|
}
|
||
|
return math.NaN()
|
||
|
}
|
||
|
|
||
|
// Calculates the image of x. Returns NaN if l is vertical.
|
||
|
func (l *Line2) Y(x float64) float64 {
|
||
|
if !l.Vertical() {
|
||
|
return -(l.C + l.A*x) / l.B
|
||
|
}
|
||
|
return math.NaN()
|
||
|
}
|
||
|
|
||
|
// Returns whether x and y define the same line.
|
||
|
func (x *Line2) Equal(y *Line2) bool {
|
||
|
switch {
|
||
|
case x.Horizontal() != y.Horizontal() || x.Vertical() != y.Vertical():
|
||
|
return false
|
||
|
case x.Horizontal() && y.Horizontal():
|
||
|
return x.Y(0) == y.Y(0)
|
||
|
case x.Vertical() && y.Vertical():
|
||
|
return x.X(0) == y.X(0)
|
||
|
default:
|
||
|
return math.Abs(x.A/y.A-x.B/y.B) < eps && ((x.C == 0 && y.C == 0) || math.Abs(x.B/y.B-x.C/y.C) < eps)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Returns whether two lines are parallel.
|
||
|
func (x *Line2) Parallel(y *Line2) bool {
|
||
|
return math.Abs(x.A*y.B-y.A*x.B) < eps
|
||
|
}
|
||
|
|
||
|
// Returns whether two lines are perpendicular.
|
||
|
func (x *Line2) Perpendicular(y *Line2) bool {
|
||
|
if x.Parallel(y) {
|
||
|
return false
|
||
|
} else if (x.Horizontal() && y.Vertical()) || (y.Horizontal() && x.Vertical()) {
|
||
|
return true
|
||
|
}
|
||
|
return math.Abs(x.Slope()*y.Slope()+1) < eps
|
||
|
}
|
||
|
|
||
|
// Calculates the intersetion point of two lines, or returns nil if they are parallel.
|
||
|
func (x *Line2) Intersect(y *Line2) *Point2 {
|
||
|
if den := x.A*y.B - y.A*x.B; math.Abs(den) < eps {
|
||
|
return nil
|
||
|
} else {
|
||
|
return &Point2{(x.B*y.C - y.B*x.C) / den, (y.A*x.C - x.A*y.C) / den}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Calculates the delta of a point form the line (i.e. signed distance).
|
||
|
func (l *Line2) Delta(p *Point2) float64 {
|
||
|
// Get the base vector for the line
|
||
|
var base *Vec2
|
||
|
if l.Vertical() {
|
||
|
base = NewVec2(0, 1)
|
||
|
} else {
|
||
|
base = new(Vec2).Sub(&Vec2{0, l.Y(0)}, &Vec2{1, l.Y(1)})
|
||
|
}
|
||
|
// Calculate cross product and height from that
|
||
|
return base.Cross(&Vec2{p.X, p.Y}) / base.Norm()
|
||
|
}
|
||
|
|
||
|
// Calculates the distance of a point from the line.
|
||
|
func (l *Line2) Dist(p *Point2) float64 {
|
||
|
return math.Abs(l.Delta(p))
|
||
|
}
|