package gg import ( "math" "fmt" ) // The type represents mathematical equation of line and line itself. type Line struct { K, C Float } type Liner interface { Line() Line } type LinerPointContainer interface { Liner PointContainer } // The type represents a line segment. type LineSegment [2]Point // The type represents multiple line segments. type LineSegments []LineSegment type Edge = LineSegment type Edges = LineSegments // Check if two LinerPointContainers do cross and return the // crossing point. func LinersCross(lp1, lp2 LinerPointContainer) (Point, bool) { l1 := lp1.Line() l2 := lp2.Line() p, crosses := l1.crossesLine(l2) if !crosses || !lp1.ContainsPoint(p) || !lp2.ContainsPoint(p) { return Point{}, false } return p, true } // Check whether the liner is parallel to the other liner. func LinersParallel(first, second Liner) bool { l1 := first.Line() l2 := second.Line() return l1.K == l2.K } // Returns angle between liners in radians. // The value fits the -Pi < Value < Pi condition. func LinersAngle(first, second Liner) Float { l1 := first.Line() l2 := second.Line() if l1.K == l2.K { return 0 } return math.Atan(l1.K/l2.K) } // Returns the line itself. Made to implement the Liner interface. func (l Line) Line() Line { return l } // Returns corresponding to the segment line line. func (l LineSegment) Line() Line { p0 := l[0] p1 := l[1] k := (p0.Y - p1.Y) / (p0.X - p1.X) c := p0.Y - p0.X*k return Line{k, c} } func (l Line) ContainsPoint(p Point) bool { buf := Line{0, p.Y} pc, ok := l.crossesLine(buf) if !ok { return false } return pc == p } func (l LineSegment) ContainsPoint(p Point) bool { line := l.Line() if !line.ContainsPoint(p) { return false } xMax := Max(l[0].X, l[1].X) xMin := Min(l[0].X, l[1].X) yMax := Max(l[0].Y, l[1].Y) yMin := Min(l[0].Y, l[1].Y) if !(xMin < p.X && p.X < xMax) || !(yMin < p.Y && p.Y < yMax) { return false } return true } func (l1 Line) crossesLine(l2 Line) (Point, bool) { if LinersParallel(l1, l2) { return Point{}, false } x := (l1.C - l2.C) / (l2.K - l1.K) y := l1.K*x + l1.C return Point{x, y}, true } // Get square of length of line segment. func (ls LineSegment) LenSqr() Float { return Sqr(ls[0].X - ls[1].X) + Sqr(ls[0].Y - ls[1].Y) } // Get length of the line segment. func (ls LineSegment) Len() Float { return math.Sqrt(ls.LenSqr()) } func (what LineSegments) Cross(with LineSegments) ([][2]int, Points) { indexes := [][2]int{} points := Points{} for i := range what { for j := range with { p, cross := LinersCross(what[i], with[j]) if cross { points = append(points, p) indexes = append(indexes, [2]int{i, j}) } } } return indexes, points }