Public Const Pi = 3.14159265358979
'http://www.vbaexpress.com/forum/showthread.php?56547-Calculate-distance-between-point-and-rectangle
Sub Test_MinDistToSegment()
MsgBox MinDistToSegment(500, 800, 1000, 1750, 6000, 3300, 0) '5257.44
MsgBox MinDistToSegment(500, 800, 1000, 1750, 4415.316, 3572.7878, 30) '4277.16, not 4127.16
MsgBox MinDistToSegment(500, 800, 1000, 1750, 1472.149, 2793.8736, 30) '2317.46
End Sub
Function MinDistToSegment(Xc As Double, Yc As Double, w As Double, _
h As Double, Xp As Double, Yp As Double, Optional ccwAngle As Double = 0) As Double
Dim a() As Variant, d(1 To 5) As Variant, i As Integer
a() = CoordRect(Xc, Yc, w, h, ccwAngle)
For i = LBound(a) + 1 To UBound(a)
d(i) = DistToSegment(Xp, Yp, a(i - 1, 1), a(i - 1, 2), a(i, 1), a(i, 2))
Next i
MinDistToSegment = WorksheetFunction.Min(d)
End Function
'Rotation adjustments:
'https://www.siggraph.org/education/materials/HyperGraph/modeling/mod_tran/2drota.htm
'Created by Kenneth Hobson, July 11, 2016, final July 26, 2016
'Degrees=Radians*180/Pi, Radians=Degrees*pi/180
Function CoordRect(Xc As Double, Yc As Double, w As Double, _
h As Double, Optional ccwAngle As Double = 0) As Variant
Dim a(1 To 5, 1 To 2) As Variant
a(1, 1) = Xc - w / 2
a(1, 2) = Yc - h / 2
a(2, 1) = Xc - w / 2
a(2, 2) = Yc + h / 2
a(3, 1) = Xc + w / 2
a(3, 2) = Yc + h / 2
a(4, 1) = Xc + w / 2
a(4, 2) = Yc - h / 2
a(5, 1) = a(1, 1) 'Close loop for chart purposes
a(5, 2) = a(1, 2)
If ccwAngle = 0 Then
CoordRect = a()
Exit Function
End If
'Translation needed if ccwAngle<>0
'Fill a same size array with coordinates for the translation adjustments.
Dim aOrg() As Variant, aRot(1 To 5, 1 To 2) As Variant
Dim aOff(1 To 5, 1 To 2) As Variant, aFin(1 To 5, 1 To 2) As Variant
Dim phi As Double, i As Integer
'Make phi (radians) = ccwAngle (degrees)
phi = ccwAngle * Pi / 180
'Translate coordinates to origin
aOrg() = CoordRect(0, 0, w, h)
'Rotate coordinates about origin by phi (ccwAngle)
For i = 1 To 5
aRot(i, 1) = aOrg(i, 1) * Cos(phi) - aOrg(i, 2) * Sin(phi) 'xPrime
aRot(i, 2) = aOrg(i, 1) * Sin(phi) + aOrg(i, 2) * Cos(phi) 'yPrime
Next i
'Determine offsets (dx, dy) - changes from aRot() to aOrg()
For i = 1 To 5
aOff(i, 1) = aRot(i, 1) - aOrg(i, 1) 'dx
aOff(i, 2) = aRot(i, 2) - aOrg(i, 2) 'dy
Next i
'Translate rotation back to final coordinates - a() + aOff()
For i = 1 To 5
aFin(i, 1) = a(i, 1) + aOff(i, 1) 'Final X
aFin(i, 2) = a(i, 2) + aOff(i, 2) 'Final Y
Next i
CoordRect = aFin()
End Function
'http://vb-helper.com/howto_distance_point_to_line.html
' Calculate the distance between the point and the segment.
' Modified by Kenneth Hobson, July 13, 2016, moved near_x and near_y from inputs.
Function DistToSegment(ByVal px As Double, ByVal py _
As Double, ByVal x1 As Double, ByVal y1 As Double, _
ByVal x2 As Double, ByVal y2 As Double) As Double
Dim dx As Double, dy As Double, t As Double
Dim near_x As Double, near_y As Double
dx = x2 - x1
dy = y2 - y1
If dx = 0 And dy = 0 Then
' It's a point not a line segment.
dx = px - x1
dy = py - y1
near_x = x1
near_y = y1
DistToSegment = Sqr(dx * dx + dy * dy)
Exit Function
End If
' Calculate the t that minimizes the distance.
t = ((px - x1) * dx + (py - y1) * dy) / (dx * dx + dy * dy)
' See if this represents one of the segment's
' end points or a point in the middle.
If t < 0 Then
dx = px - x1
dy = py - y1
near_x = x1
near_y = y1
ElseIf t > 1 Then
dx = px - x2
dy = py - y2
near_x = x2
near_y = y2
Else
near_x = x1 + t * dx
near_y = y1 + t * dy
dx = px - near_x
dy = py - near_y
End If
DistToSegment = Sqr(dx * dx + dy * dy)
End Function
Data check:
Bookmarks