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  }