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  //}