github.com/pavlo67/common@v0.5.3/common/mathlib/plane/chain_projections.go (about) 1 package plane 2 3 import ( 4 "math" 5 6 "github.com/pavlo67/common/common/mathlib" 7 ) 8 9 type ProjectionOnPolyChain struct { 10 N int 11 Position float64 12 Point2 13 } 14 15 func (p Point2) DistanceToPolyChain(pCh PolyChain) (float64, ProjectionOnPolyChain) { 16 17 if len(pCh) < 1 { 18 return math.NaN(), ProjectionOnPolyChain{N: -1, Position: math.NaN(), Point2: Point2{math.NaN(), math.NaN()}} 19 } else if len(pCh) == 1 { 20 return p.DistanceTo(pCh[0]), ProjectionOnPolyChain{Point2: pCh[0]} 21 } 22 23 minDist := math.Inf(1) 24 var pr ProjectionOnPolyChain 25 var n int 26 var pPr Point2 27 28 // POINTS: 29 for i, pI := range pCh[:len(pCh)-1] { 30 dist, position := p.DistanceToSegment(Segment{pI, pCh[i+1]}) 31 32 // fmt.Printf("{%g %g} {%g %g} {%g %g} --> %g\n", p.X, p.Y, pI.X, pI.Y, pCh[i+1].X, pCh[i+1].Y, dist) 33 34 if dist >= minDist { 35 continue 36 } 37 38 if segmentLength := pI.DistanceTo(pCh[i+1]); segmentLength <= 0 { 39 pPr, n, position = pI, i, 0 40 } else if position >= segmentLength { 41 pPr, n, position = pCh[i+1], i+1, 0 42 } else { 43 dx, dy := pCh[i+1].X-pI.X, pCh[i+1].Y-pI.Y 44 pPr, n = Point2{pI.X + dx*position/segmentLength, pI.Y + dy*position/segmentLength}, i 45 } 46 47 minDist, pr = dist, ProjectionOnPolyChain{n, position, pPr} 48 49 //if dist < minDist { 50 // minDist, projections = dist, []ProjectionOnPolyChain{{n, position, pPr}} 51 //} else { 52 // for _, pr := range projections { 53 // if n == pr.n && position == pr.pos { 54 // continue POINTS 55 // } 56 // } 57 // 58 // projections = append(projections, ProjectionOnPolyChain{n, position, pPr}) 59 //} 60 } 61 62 return minDist, pr 63 } 64 65 func AddProjectionPoint(pCh PolyChain, pr ProjectionOnPolyChain) (PolyChain, ProjectionOnPolyChain, bool) { 66 if pr.N < 0 { 67 return append(PolyChain{pr.Point2}, pCh...), ProjectionOnPolyChain{Point2: pr.Point2}, true 68 } else if pr.N >= len(pCh) || (pr.N == len(pCh)-1 && pr.Position > 0) { 69 return append(pCh, pr.Point2), ProjectionOnPolyChain{N: len(pCh), Point2: pr.Point2}, true 70 } else if pr.Position == 0 { 71 // TODO??? check if pr.Point2 == pCh[pr.N] 72 return pCh, pr, false 73 } 74 75 return append(pCh[:pr.N+1], append(PolyChain{pr.Point2}, pCh[pr.N+1:]...)...), ProjectionOnPolyChain{N: pr.N + 1, Point2: pr.Point2}, true 76 } 77 78 type ProjectionOnPolyChainDirected struct { 79 Distance float64 80 Angle float64 81 ProjectionOnPolyChain 82 } 83 84 func ProjectionsOnPolyChain(polyChain PolyChain, p Point2, distanceMax float64) []ProjectionOnPolyChainDirected { 85 if len(polyChain) < 1 { 86 return nil 87 88 } else if len(polyChain) == 1 { 89 if distance := p.DistanceTo(polyChain[0]); distance <= distanceMax { 90 return []ProjectionOnPolyChainDirected{{ 91 Distance: distance, 92 ProjectionOnPolyChain: ProjectionOnPolyChain{Point2: polyChain[0]}, 93 }} 94 } 95 return nil 96 97 } 98 99 var projections []ProjectionOnPolyChainDirected 100 101 for n := 0; n < len(polyChain)-1; n++ { 102 pn := polyChain[n] 103 dist, position := p.DistanceToSegment(Segment{pn, polyChain[n+1]}) 104 105 // log.Print(dist, distanceMax) 106 107 if dist > distanceMax { 108 continue 109 } 110 111 segmLen := pn.DistanceTo(polyChain[n+1]) 112 if position < segmLen { 113 projections = append(projections, ProjectionOnPolyChainDirected{dist, 0, ProjectionOnPolyChain{ 114 n, position, Point2{pn.X + (polyChain[n+1].X-pn.X)*position/segmLen, pn.Y + (polyChain[n+1].Y-pn.Y)*position/segmLen}}}) 115 } else if n == len(polyChain)-2 { 116 projections = append(projections, ProjectionOnPolyChainDirected{dist, 0, ProjectionOnPolyChain{ 117 n + 1, 0, polyChain[n+1]}}) 118 } 119 } 120 121 return projections 122 } 123 124 // we check the ray directed from ls[1] to infinity 125 func SegmentProjectionOnPolyChain(polyChain PolyChain, ls Segment, distanceMax float64) *ProjectionOnPolyChainDirected { 126 //log.Printf("%v + %v", polyChain, ls) 127 128 if len(polyChain) < 1 { 129 return nil 130 131 } 132 133 segmLen := ls[0].DistanceTo(ls[1]) 134 135 if len(polyChain) == 1 { 136 distance0, distance1 := polyChain[0].DistanceTo(ls[0]), polyChain[0].DistanceTo(ls[1]) 137 if math.Abs(segmLen+distance1-distance0) <= mathlib.Eps { 138 return &ProjectionOnPolyChainDirected{Distance: distance1, ProjectionOnPolyChain: ProjectionOnPolyChain{Point2: polyChain[0]}} 139 } 140 return nil 141 } 142 143 var pr *ProjectionOnPolyChainDirected 144 145 for n, pn := range polyChain[:len(polyChain)-1] { 146 if p := ls.DivideByLine(pn, polyChain[n+1]); p != nil { 147 148 //log.Print(*p) 149 150 if distance1 := p.DistanceTo(ls[1]); distance1 <= distanceMax && (pr == nil || distance1 < pr.Distance) { 151 152 distance0 := p.DistanceTo(ls[0]) 153 154 //log.Print(distance0, distance1, segmLen) 155 156 if math.Abs(segmLen+distance1-distance0) <= mathlib.Eps { 157 position := p.DistanceTo(pn) 158 if position >= pn.DistanceTo(polyChain[n+1]) { 159 pr = &ProjectionOnPolyChainDirected{ 160 Distance: distance1, 161 ProjectionOnPolyChain: ProjectionOnPolyChain{N: n + 1, Point2: *p}} 162 } else { 163 pr = &ProjectionOnPolyChainDirected{ 164 Distance: distance1, 165 ProjectionOnPolyChain: ProjectionOnPolyChain{N: n, Position: position, Point2: *p}} 166 } 167 } 168 } 169 } 170 } 171 172 return pr 173 } 174 175 func EndProjection(pCh PolyChain, start bool) ProjectionOnPolyChain { 176 var pr ProjectionOnPolyChain 177 if start { 178 pr.Point2 = pCh[0] 179 } else { 180 pr.N, pr.Point2 = len(pCh)-1, pCh[len(pCh)-1] 181 } 182 183 return pr 184 } 185 186 func ProjectionBetween(pr0, pr1, pr ProjectionOnPolyChain) bool { 187 if pr0.N > pr1.N || (pr0.N == pr1.N && pr0.Position > pr1.Position) { 188 pr0, pr1 = pr1, pr0 189 } 190 191 return (pr.N > pr0.N || (pr.N == pr0.N && pr.Position >= pr0.Position)) && 192 (pr.N < pr1.N || (pr.N == pr1.N && pr.Position <= pr1.Position)) 193 } 194 195 func CutWithProjection(pCh PolyChain, pr ProjectionOnPolyChain) (head, tail PolyChain) { 196 if pr.N < 0 { 197 return nil, pCh 198 } else if pr.N >= len(pCh) { 199 return pCh, nil 200 } 201 202 if pr.Position == 0 { 203 return pCh[:pr.N], pCh[pr.N:] 204 } 205 206 return pCh[:pr.N+1], append(PolyChain{pr.Point2}, pCh[pr.N+1:]...) 207 } 208 209 //func CutWithProjection(pCh PolyChain, pr ProjectionOnPolyChain, fromStart bool) PolyChain { 210 // if pr.N < 0 || pr.N >= len(pCh) { 211 // return nil 212 // } 213 // 214 // if fromStart { 215 // if pr.Position == 0 { 216 // return append(PolyChain{}, pCh[:pr.N+1]...) 217 // } 218 // return append(append(PolyChain{}, pCh[:pr.N+1]...), pr.Point2) 219 // } 220 // if pr.Position == 0 { 221 // return append(PolyChain{}, pCh[pr.N:]...) 222 // } 223 // return append(PolyChain{pr.Point2}, pCh[pr.N+1:]...) 224 //} 225 226 func CutWithProjections(pCh PolyChain, pr0, pr1 ProjectionOnPolyChain) PolyChain { 227 if pr0.N < 0 || pr0.N >= len(pCh) || pr1.N < 0 || pr1.N >= len(pCh) { 228 return nil 229 } 230 231 var reversed bool 232 if pr0.N > pr1.N || (pr0.N == pr1.N && pr0.Position > pr1.Position) { 233 reversed, pr0, pr1 = true, pr1, pr0 234 235 } 236 237 if pr1.Position == 0 { 238 pCh = append(PolyChain{}, pCh[:pr1.N+1]...) 239 } else { 240 pCh = append(append(PolyChain{}, pCh[:pr1.N+1]...), pr1.Point2) 241 } 242 243 if pr0.Position == 0 { 244 pCh = append(PolyChain{}, pCh[pr0.N:]...) 245 } else { 246 pCh = append(PolyChain{pr0.Point2}, pCh[pr0.N+1:]...) 247 } 248 if reversed { 249 return pCh.Reversed() 250 } 251 252 return pCh 253 } 254 255 //func DivideByProjection(pCh plane.PolyChain, pr plane.ProjectionOnPolyChain) []plane.PolyChain { 256 // if pr.n < 0 || pr.n >= len(pCh) { 257 // return nil 258 // } 259 // 260 // if pr.pos > 0 { 261 // return []plane.PolyChain{ 262 // append(pCh[:pr.n+1], pr.Point2).Reversed(), 263 // append(plane.PolyChain{pr.Point2}, pCh[pr.n+1:]...), 264 // } 265 // } 266 // 267 // pChs := []plane.PolyChain{pCh[pr.n:]} 268 // if pr.n > 0 { 269 // pChs = append(pChs, pCh[:pr.n+1].Reversed()) 270 // } 271 // return pChs 272 //}