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 }