github.com/pavlo67/common@v0.5.3/common/mathlib/plane/points.go (about)

     1  package plane
     2  
     3  import (
     4  	"image"
     5  	"math"
     6  )
     7  
     8  type Point2 struct {
     9  	X, Y float64
    10  }
    11  
    12  func (p Point2) ImagePoint() image.Point {
    13  	return image.Point{int(math.Round(p.X)), int(math.Round(p.Y))}
    14  }
    15  
    16  // TODO!!!! be careful: all single point angles are calculated in range -pi < angle <= pi
    17  
    18  func (p Point2) VectorTo(p1 Point2) Point2 {
    19  	return Point2{X: p1.X - p.X, Y: p1.Y - p.Y}
    20  }
    21  
    22  func (p Point2) Sub(p1 Point2) Point2 {
    23  	return Point2{p.X - p1.X, p.Y - p1.Y}
    24  }
    25  
    26  func (p Point2) Add(p1 Point2) Point2 {
    27  	return Point2{p.X + p1.X, p.Y + p1.Y}
    28  }
    29  
    30  func (p Point2) Radius() float64 {
    31  	return math.Sqrt(p.X*p.X + p.Y*p.Y)
    32  }
    33  
    34  // XToYAngle lies in the range: -math.Pi < p.XToYAngleFromOy() <= math.Pi
    35  func (p Point2) XToYAngleFromOx() XToYAngle {
    36  	if p.X == 0 {
    37  		if p.Y > 0 {
    38  			return math.Pi / 2
    39  		} else if p.Y < 0 {
    40  			return -math.Pi / 2
    41  		} else {
    42  			return XToYAngle(math.NaN())
    43  		}
    44  	} else if p.X >= 0 {
    45  		return XToYAngle(math.Atan(p.Y / p.X))
    46  	} else if p.Y >= 0 {
    47  		return XToYAngle(math.Atan(p.Y/p.X) + math.Pi)
    48  	} else {
    49  		return XToYAngle(math.Atan(p.Y/p.X) - math.Pi)
    50  	}
    51  }
    52  
    53  func (p Point2) AnglesDelta(p1 Point2) float64 {
    54  	angle := p1.XToYAngleFromOx() - p.XToYAngleFromOx()
    55  	if angle > math.Pi {
    56  		return float64(angle - 2*math.Pi)
    57  	} else if angle <= -math.Pi {
    58  		return float64(angle + 2*math.Pi)
    59  	}
    60  	return float64(angle)
    61  }
    62  
    63  func (p Point2) DistanceTo(p1 Point2) float64 {
    64  	return math.Sqrt((p.X-p1.X)*(p.X-p1.X) + (p.Y-p1.Y)*(p.Y-p1.Y))
    65  }
    66  
    67  func (p Point2) DistanceSquare(p1 Point2) float64 {
    68  	return (p.X-p1.X)*(p.X-p1.X) + (p.Y-p1.Y)*(p.Y-p1.Y)
    69  }
    70  
    71  func (p Point2) DistanceToSegment(s Segment) (distance, projectionPosition float64) {
    72  	d0, d1, d := p.DistanceSquare(s[0]), p.DistanceSquare(s[1]), s[0].DistanceSquare(s[1])
    73  	var reversed bool
    74  	if d1 < d0 {
    75  		d0, d1 = d1, d0
    76  		reversed = true
    77  	}
    78  	if d0+d <= d1 {
    79  		if reversed {
    80  			return math.Sqrt(d0), math.Sqrt(d)
    81  		} else {
    82  			return math.Sqrt(d0), 0
    83  		}
    84  	}
    85  
    86  	c0 := (d0 + d - d1) / (2 * math.Sqrt(d))
    87  
    88  	if reversed {
    89  		distance, projectionPosition = math.Sqrt(d0-c0*c0), max(0, math.Sqrt(d)-c0)
    90  	} else {
    91  		distance, projectionPosition = math.Sqrt(d0-c0*c0), max(0, c0)
    92  	}
    93  
    94  	if math.IsNaN(distance) {
    95  		return 0, projectionPosition
    96  	}
    97  
    98  	return distance, projectionPosition
    99  }
   100  
   101  func (p Point2) RotateAround(center Point2, angle XToYAngle) Point2 {
   102  	pToCenter := Point2{p.X - center.X, p.Y - center.Y}
   103  	pToCenterRotated := pToCenter.RotateByAngle(angle)
   104  
   105  	return Point2{pToCenterRotated.X + center.X, pToCenterRotated.Y + center.Y}
   106  }
   107  
   108  func (p Point2) RotateByAngle(addAngle XToYAngle) Point2 {
   109  	angle := p.XToYAngleFromOx()
   110  	r := math.Sqrt(p.X*p.X + p.Y*p.Y)
   111  
   112  	return Point2{r * math.Cos(float64(angle+addAngle)), r * math.Sin(float64(angle+addAngle))}
   113  }
   114  
   115  func (p Point2) RotateWithRatio(ratio float64) Point2 {
   116  	angle := p.XToYAngleFromOx()
   117  	r := math.Sqrt(p.X*p.X + p.Y*p.Y)
   118  
   119  	return Point2{r * math.Cos(float64(angle)*ratio), r * math.Sin(float64(angle)*ratio)}
   120  }
   121  
   122  // TODO!!!! be careful: axis angles are calculated in range -pi/2 < angle <= pi/2
   123  // TODO!!!! be careful:	if math.Abs(angle - math.Pi / 2) <= Eps then deltaFromCenter == DX, else == DY
   124  
   125  //func CenterAxis(axis Segment) (angle, deltaFromCenter float64) {
   126  //	axisDX, axisDY := axis.End.Position-axis.Begin.Position, axis.End.Y-axis.Begin.Y
   127  //
   128  //	if axisDX == 0 {
   129  //		if axisDY == 0 {
   130  //			// TODO!!! be careful: it's a convention only
   131  //			return 0, axis.Begin.Y
   132  //		}
   133  //		return math.Pi / 2, axis.Begin.Position
   134  //	}
   135  //
   136  //	axisDerivative := axisDY / axisDX
   137  //	if math.IsInf(axisDerivative, 0) {
   138  //		return math.Pi / 2, axis.Begin.Position
   139  //	}
   140  //
   141  //	angle = math.Atan(axisDerivative)
   142  //	if math.Abs(angle-math.Pi/2) <= Eps {
   143  //		return angle, axis.Begin.Position - axis.Begin.Y/axisDerivative
   144  //	}
   145  //
   146  //	return angle, axis.Begin.Y - axis.Begin.Position*axisDerivative
   147  //}
   148  //
   149  //func SetAlongOx(points []Point2, axis Segment) ([]Point2, image.Rect) {
   150  //	angle, deltaFromCenter := CenterAxis(axis)
   151  //	pointsMoved := make([]Point2, len(points))
   152  //
   153  //	if math.Abs(angle-math.Pi/2) <= Eps {
   154  //		for i, p := range points {
   155  //			pointsMoved[i] = RotateByAngle(Point2{p.Position - deltaFromCenter, p.Y}, -angle)
   156  //		}
   157  //	} else {
   158  //		for i, p := range points {
   159  //			pointsMoved[i] = RotateByAngle(Point2{p.Position, p.Y - deltaFromCenter}, -angle)
   160  //		}
   161  //	}
   162  //
   163  //	return pointsMoved
   164  //}
   165  
   166  func (p Point2) DistanceToLine(line Segment) float64 {
   167  	p2 := line.Vector()
   168  	pIntersect := line.LinesIntersection(Segment{p, Point2{p.X - p2.Y, p.Y + p2.X}})
   169  	if pIntersect == nil {
   170  		// TODO!!! be careful: it's impossible
   171  		return math.NaN()
   172  	}
   173  
   174  	return math.Sqrt((pIntersect.X-p.X)*(pIntersect.X-p.X) + (pIntersect.Y-p.Y)*(pIntersect.Y-p.Y))
   175  }
   176  
   177  func Diameter(pts []Point2) float64 {
   178  	distMax := 0.
   179  	for i, p0 := range pts {
   180  		for _, p1 := range pts[i+1:] {
   181  			if dist := p0.DistanceTo(p1); dist > distMax {
   182  				distMax = dist
   183  			}
   184  		}
   185  	}
   186  
   187  	return distMax
   188  }