github.com/pavlo67/common@v0.5.3/common/mathlib/plane/segments.go (about) 1 package plane 2 3 import ( 4 "log" 5 "math" 6 7 "github.com/pavlo67/common/common/mathlib/algebra" 8 9 "github.com/pavlo67/common/common/mathlib" 10 ) 11 12 type Segment [2]Point2 13 14 func (s *Segment) Vector() Point2 { 15 if s == nil { 16 return Point2{} 17 } 18 return Point2{s[1].X - s[0].X, s[1].Y - s[0].Y} 19 } 20 21 func (s Segment) Middle() Point2 { 22 return Point2{(s[1].X + s[0].X) / 2, (s[1].Y + s[0].Y) / 2} 23 } 24 25 func (segment Segment) Paired(distanceToRight float64) Segment { 26 if segment[0] == segment[1] || distanceToRight == 0 { 27 return segment 28 } 29 30 direction := segment[1].Sub(segment[0]) 31 dirRadius := direction.Radius() 32 dirDistance := Point2{direction.X * distanceToRight / dirRadius, direction.Y * distanceToRight / dirRadius} 33 34 dirToTheSide := dirDistance.RotateByAngle(XToYAngle(math.Pi * 0.5)) 35 return Segment{segment[0].Add(dirToTheSide), segment[1].Add(dirToTheSide)} 36 } 37 38 //func (segment Segment) Paired(distanceToRight float64) Segment { 39 // if segment[0] == segment[1] || distanceToRight == 0 { 40 // return segment 41 // } 42 // 43 // direction := segment[1].Sub(segment[0]) 44 // dirRadius := direction.Radius() 45 // dirDistance := Point2{direction.X * distanceToRight / dirRadius, direction.Y * distanceToRight / dirRadius} 46 // angle := XToYAngleFromOy(math.Pi * 0.5) 47 // if distanceToRight < 0 { 48 // angle = -angle 49 // } 50 // 51 // dirToTheSide := dirDistance.RotateByAngle(angle) 52 // return Segment{segment[0].Add(dirToTheSide), segment[1].Add(dirToTheSide)} 53 //} 54 55 func SegmentGoOutCircle(s Segment, p Point2, r float64) *Point2 { 56 if s[0].DistanceTo(p) > r || s[1].DistanceTo(p) <= r { 57 return nil 58 } 59 60 x0, y0, dx, dy := s[0].X-p.X, s[0].Y-p.Y, s[1].X-s[0].X, s[1].Y-s[0].Y 61 a, b, c := dx*dx+dy*dy, 2*(dx*x0+dy*y0), x0*x0+y0*y0-r*r 62 63 roots := algebra.QuadraticEquation(a, b, c) 64 if roots == nil { 65 return nil 66 } else if roots[0] >= 0 && roots[0] <= 1 { 67 return &Point2{s[0].X + dx*roots[0], s[0].Y + dy*roots[0]} 68 } else if roots[1] >= 0 && roots[1] <= 1 { 69 return &Point2{s[0].X + dx*roots[1], s[0].Y + dy*roots[1]} 70 } 71 72 log.Printf("wrong roots: %v for s = %v, p = %v, r = %g / on SegmentGoOutCircle()", *roots, s, p, r) 73 74 return nil 75 } 76 77 func SegmentsIntersection(s, s1 Segment) (pCross *Point2) { 78 if s[1].X < s[0].X { 79 s[0], s[1] = s[1], s[0] 80 } 81 if s1[1].X < s1[0].X { 82 s1[0], s1[1] = s1[1], s1[0] 83 } 84 if s1[0].X < s[0].X { 85 s, s1 = s1, s 86 } 87 if s1[0].X-s[1].X >= mathlib.Eps { 88 return nil 89 } 90 91 r := Point2{s[1].X - s[0].X, s[1].Y - s[0].Y} 92 l := Point2{s1[1].X - s1[0].X, s1[1].Y - s1[0].Y} 93 94 cr := Cross(r, l) 95 96 if cr > -mathlib.Eps && cr < mathlib.Eps { 97 // vertical segments 98 if math.Abs(Cross(r, Point2{0, 1})) < mathlib.Eps { 99 if s[1].X-s1[0].X >= mathlib.Eps { 100 return nil 101 } 102 103 if s[1].Y < s[0].Y { 104 s = Segment{s[1], s[0]} 105 } 106 if s1[1].Y < s1[0].Y { 107 s1 = Segment{s1[1], s1[0]} 108 } 109 110 if s[0].Y < s1[0].Y { 111 if s[1].Y >= s1[0].Y { 112 return &s1[0] 113 } 114 } else if s[0].Y > s1[0].Y { 115 if s1[1].Y >= s[0].Y { 116 return &s[0] 117 } 118 } else { 119 return &s[0] 120 } 121 return nil 122 } 123 124 // compare s1[0].Y and corresponding point on s 125 k := r.Y / r.X 126 s01Y := s[0].Y + k*(s1[0].X-s[0].X) 127 if math.Abs(s01Y/math.Sqrt(1+k*k)) >= mathlib.Eps { 128 return nil 129 } 130 131 return &s1[0] 132 //} else if math.Abs(s[0].X-s[1].X) < mathlib.Eps { 133 // 134 // s, s1 = s1, s 135 // r = Point2{s[1].X - s[0].X, s[1].Y - s[0].Y} 136 // l = Point2{s1[1].X - s1[0].X, s1[1].Y - s1[0].Y} 137 // cr = Cross(r, l) 138 139 } 140 141 q := Point2{s1[0].X - s[0].X, s1[0].Y - s[0].Y} 142 t := Cross(q, l) / cr 143 xIntersect := s[0].X + t*r.X 144 yIntersect := s[0].Y + t*r.Y 145 146 if math.Abs(s[0].X-s[1].X) < mathlib.Eps { 147 s0Y, s1Y := s[0].Y, s[1].Y 148 if s0Y > s1Y { 149 s0Y, s1Y = s1Y, s0Y 150 } 151 152 if yIntersect < s0Y || yIntersect > s1Y || xIntersect < s1[0].X || xIntersect > s1[1].X { 153 return nil 154 } 155 156 } else { 157 s10Y, s11Y := s1[0].Y, s1[1].Y 158 if s10Y > s11Y { 159 s10Y, s11Y = s11Y, s10Y 160 } 161 162 if xIntersect < s[0].X || xIntersect > s[1].X || yIntersect < s10Y || yIntersect > s11Y { 163 return nil 164 } 165 166 } 167 168 return &Point2{xIntersect, yIntersect} 169 } 170 171 func (s Segment) DistanceTo(s1 Segment) float64 { 172 if pCross := SegmentsIntersection(s, s1); pCross != nil { 173 return 0 174 } 175 176 d00, _ := s[0].DistanceToSegment(s1) 177 d01, _ := s[1].DistanceToSegment(s1) 178 d10, _ := s1[0].DistanceToSegment(s) 179 d11, _ := s1[1].DistanceToSegment(s) 180 181 return min(d00, d01, d10, d11) 182 } 183 184 func (s Segment) AngleAbs(s1 Segment) float64 { 185 angle := math.Abs(s.Vector().AnglesDelta(s1.Vector())) 186 for angle > math.Pi { 187 angle -= math.Pi 188 } 189 190 return angle 191 } 192 193 func (s Segment) AngleLinesAbs(s1 Segment) float64 { 194 angle := math.Abs(s.Vector().AnglesDelta(s1.Vector())) 195 for angle > math.Pi/2 { 196 angle -= math.Pi / 2 197 } 198 199 return angle 200 }