Everything in Degrees instead of Radians
#21
This problem was started by James in Help Me Board and is quite a challenge and has inspired previous 2 posts by me here and now a 3rd post.

Containing a bouncing circle inside a raggedy frame of random line segments, the problem arises, for me, of overcoming "stuck" points where the circle runs into a segment corner (called peninsulas by me) here the circle center may be clear of corner but it's lower body gets stuck pressing into corner. 

First how to detect stick points in code?
2nd how to escape them?

To detect a stick, I just check the new coordinates of circle x, y if neither of them has changed, it's stuck.
If I find that condition then I change the ball angle to the perpendicular of the line segment (blue color code) it's mostly stuck at instead of using it's natural reflective angle (white color code).  This seems to get the ball clear most every time.

There are times when ball gets wedged into a "bay" and this method of getting unstuck doesn't look like it would help much, the white arrow looks like the best way out but using the perpendicular doesn't seem to get it stuck worse...

See for yourself, let this demo run in delay mode (press d to toggle on/off) when you here a beep it will stop show arrows and attempt to use the blue direction angle (perpendicular to the line segment it's hitting) instead of the normal white reflective angle. (In delay mode, it will stop and show color coded arrows at every hit with boundary.)

When delay mode is not toggled, you will hear a BEEP and the beep will be counted but the circle just keeps moving the color coded arrows don't show up at that speed.

Code: (Select All)
Option _Explicit
_Title "James Random Container 4" ' b+ 2022-10-19
' Modify the lineSegIntersectCircle function to count number of points intersecting
' if more than one line segment do the one with the most points   Yes! works better.
' Still can get a point stuck but it doesn't fly out of bounds.
' 2022-10-19 Aha! a simple solution to being stuck on peninsulas, just back out 1 more time
' nope, nor 2 more, nor until both x or y change more than a pixel
' Now we are getting stuck in bays as well as peninsulas!   and it seems bad choice to use segment
' normal to get out of a bay, usually just follow white arrow for bays...
' Move Container making code into a sub and make doubly raggety changing min 80 to min 40
' Added delaid mode toggle with d keypresses listen for BEEP in this mode it goes into sleep mode.
' Have to press a key to continue...

Screen _NewImage(800, 680, 32)
_ScreenMove 250, 50
Randomize Timer
_PrintMode _KeepBackground
Type lineSegment
    As Single x1, y1, x2, y2, dN ' 2 end points and an angle pointing towards center (I think)
End Type
Dim Shared As Long Container, Nlines, L
ReDim Shared Boundaries(1 To 100) As lineSegment
MakeContainer ' for background ball boundaries

Dim bx, by, ba, br, bspeed, diff, test, saveL, hits ' now for bouncing circles around
Dim saveBx, saveBy, Beeps, delaid ' more variables to handle getting stuck
bx = _Width / 2: by = _Height / 2: bspeed = 5
br = 15 ' make ball radius (br) at least 2* speed
ba = Rnd * 360 ' setup up ball in middle of screen/container random heading = ba (ball angle)

' ok just bounce ball around the polygon container
Do
    _PutImage , Container, 0
    Locate 1, 1: Print "  Number of times line segment perpendicular used to prevent getting stuck"; Beeps
    Circle (bx, by), br ' draw ball then calc next loaction
    bx = bx + bspeed * CosD(ba) ' test x, y is new ball position if dont run into wall
    by = by + bspeed * SinD(ba)
    hits = 0: saveL = 0
    For L = 1 To Nlines ' get line segment with highest hit count if any
        test = lineSegIntersectCircle(Boundaries(L).x1, Boundaries(L).y1, Boundaries(L).x2, Boundaries(L).y2, bx, by, br)
        If test > hits Then saveL = L: hits = test
    Next
    ' probably should back it up before processing bounce
    If hits Then ' backup circle


        While lineSegIntersectCircle(Boundaries(saveL).x1, Boundaries(saveL).y1, Boundaries(saveL).x2, Boundaries(saveL).y2, bx, by, br) ' back up circle
            bx = bx + CosD(ba - 180)
            by = by + SinD(ba - 180)
        Wend

        '' getting over the peninsula's mod 2022-10-19  one more backout?  this did not help much
        'For i = 1 To 2
        'bx = bx + CosD(ba - 180)
        'by = by + SinD(ba - 180)
        'Next
        ' =========================== fix penisula stick problem ???????????????????????????????????????????

        _PutImage , Container, 0 ' show circle hit on boundary
        Locate 1, 1: Print "  Number of times line segment perpendicular used to prevent getting stuck"; Beeps
        Circle (bx, by), br

        ' Yellow arrow for incoming towards boundary (I reversed the head of arrow to compare to reflection angle)
        ArrowTo bx + 3 * br * CosD(ba + 180), by + 3 * br * SinD(ba + 180), ba, 3 * br, &HFFFFFF00

        ' Blue Vector Perpendicular to plane
        ArrowTo bx, by, Boundaries(saveL).dN, 5 * br, &HFF0000FF

        ' Reflected ball off line  are we stuck???
        If Abs(bx - saveBx) > 1 Or Abs(by - saveBy) > 1 Then ' NO ball is moving right along
            diff = Boundaries(saveL).dN - ba + 180
            ba = Boundaries(saveL).dN + diff ' >>>> new direction
        Else '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>          Quite likely stuck so use the normal angle for ba
            If delaid Then
                ' show what I would normally do with ball
                diff = Boundaries(saveL).dN - ba + 180
                ba = Boundaries(saveL).dN + diff ' >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> new direction
                ArrowTo bx, by, ba, 3 * br, &HFFFFFFFF
                _Display
            End If
            Beep ' indicate by sound that the alternate angle for ball was used
            Beeps = Beeps + 1
            If delaid Then Sleep

            ' now fix angle to normal instead of regular method
            ba = Boundaries(saveL).dN ' >>>>>>>>>>>>>>>>>>>>>>> new direction from line segment perpendicular angle
        End If
        ArrowTo bx, by, ba, 3 * br, &HFFFFFFFF
        saveBx = bx: saveBy = by
        _Display
        If delaid Then _Delay 1 'comment out to find stucks faster
    End If
    If InKey$ = "d" Then delaid = 1 - delaid ' toggle dlaid mode
    _Display

    _Limit 300
Loop Until _KeyDown(27)

Sub MakeContainer
    Dim cx, cy, x1, y1, flag, x2, y2 ' building container
    Dim As _Unsigned Long c1

    cx = _Width / 2: cy = _Height / 2 + 40
    c1 = _RGB32(0, 150, 85) ' minty green background out of bounds
    Cls
    x1 = 50
    y1 = 50
    flag = 0
    While flag = 0
        x2 = (Rnd * 80) + 40 + x1
        If x2 > 750 Then
            x2 = 750
            flag = 1
        End If
        y2 = Rnd * 60 + 20
        Line (x1, y1)-(x2, y2), c1
        Nlines = Nlines + 1
        Boundaries(Nlines).x1 = x1: Boundaries(Nlines).y1 = y1
        Boundaries(Nlines).x2 = x2: Boundaries(Nlines).y2 = y2
        Boundaries(Nlines).dN = DAtan2(x1, y1, x2, y2) + 90 ' the angle x2, y2 is to x1, y1 + 90 points inward
        x1 = x2
        y1 = y2
    Wend

    flag = 0
    While flag = 0
        y2 = (Rnd * 80) + 40 + y1
        If y2 > 550 Then
            y2 = 550
            flag = 1
        End If
        x2 = 750 - (Rnd * 60 + 20)
        Line (x1, y1)-(x2, y2), c1
        Nlines = Nlines + 1
        Boundaries(Nlines).x1 = x1: Boundaries(Nlines).y1 = y1
        Boundaries(Nlines).x2 = x2: Boundaries(Nlines).y2 = y2
        Boundaries(Nlines).dN = DAtan2(x1, y1, x2, y2) + 90 ' the angle x2, y2 is to x1, y1 + 90 points inward
        x1 = x2
        y1 = y2
    Wend

    flag = 0
    While flag = 0
        x2 = x1 - ((Rnd * 80) + 40)
        If x2 < 50 Then
            x2 = 50
            flag = 1
        End If
        y2 = 550 - (Rnd * 60 + 20)
        Line (x1, y1)-(x2, y2), c1
        Nlines = Nlines + 1
        Boundaries(Nlines).x1 = x1: Boundaries(Nlines).y1 = y1
        Boundaries(Nlines).x2 = x2: Boundaries(Nlines).y2 = y2
        Boundaries(Nlines).dN = DAtan2(x1, y1, x2, y2) + 90 ' the angle x2, y2 is to x1, y1 + 90 points inward
        x1 = x2
        y1 = y2
    Wend

    flag = 0
    While flag = 0
        y2 = y1 - ((Rnd * 80) + 40)
        If y2 < 50 Then
            y2 = 50
            flag = 1
        End If
        x2 = Rnd * 60 + 20
        If flag = 1 Then x2 = 50
        Line (x1, y1)-(x2, y2), c1
        Nlines = Nlines + 1
        Boundaries(Nlines).x1 = x1: Boundaries(Nlines).y1 = y1
        Boundaries(Nlines).x2 = x2: Boundaries(Nlines).y2 = y2
        Boundaries(Nlines).dN = DAtan2(x1, y1, x2, y2) + 90 ' the angle x2, y2 is to x1, y1 + 90 points inward
        x1 = x2
        y1 = y2
    Wend
    Paint (1, 1), c1, c1
    Locate 37, 1
    Print "  Press d for delay mode, if you hear a beep then in sleep mode showing potential stuck point,"
    Print "      press any to continue...    Yellow = the angle of ball heading towards line,"
    Print "   Blue = angle perpendicular (normal) to boundary line. White = angle of reflection off line."
    Container = _NewImage(_Width, _Height, 32)
    _PutImage , 0, Container
End Sub

' return count of how many points overlap segment
Function lineSegIntersectCircle (x1, y1, x2, y2, cx, cy, r)
    ' x1, y1 and x2, y2  are end points of line segment
    ' cx, cy are circle center with radius r
    Dim rtn, d, dx, dy, i, x, y
    d = Sqr((x1 - x2) ^ 2 + (y1 - y2) ^ 2)
    dx = (x2 - x1) / d
    dy = (y2 - y1) / d
    For i = 0 To d
        x = x1 + dx * i
        y = y1 + dy * i
        If Sqr((x - cx) ^ 2 + (y - cy) ^ 2) <= r Then rtn = rtn + 1
    Next
    lineSegIntersectCircle = rtn
End Function

' use angles in degrees units instead of radians (converted inside sub)
Function CosD (degrees)
    ' Note this function uses whatever the default type is, better not be some Integer Type.
    CosD = Cos(_D2R(degrees))
End Function

' use angles in degrees units instead of radians (converted inside sub)
Function SinD (degrees)
    ' Note this function uses whatever the default type is, better not be some Integer Type.
    SinD = Sin(_D2R(degrees))
End Function

' use angles in degrees units instead of radians (converted inside sub)
Function DAtan2 (x1, y1, x2, y2) ' The angle in degrees a 2nd point (x2, y2)  makes to a first point (x1, y1)
    ' Note this function uses whatever the default type is, better not be some Integer Type.
    ' Delta means change between 1 measure and another for example x2 - x1
    Dim deltaX, deltaY, rtn
    deltaX = x2 - x1
    deltaY = y2 - y1
    '  To find the angle point(x2, y2) makes to (x1, y1) in Degrees
    ' Take DegreeAngle = DAtan2(y2 - y1, x2 - x1)
    rtn = _R2D(_Atan2(deltaY, deltaX))
    If rtn < 0 Then DAtan2 = rtn + 360 Else DAtan2 = rtn
End Function

' use angles in degrees units instead of radians (converted inside sub)
Sub ArrowTo (BaseX As Long, BaseY As Long, dAngle As Double, lngth As Long, colr As _Unsigned Long)
    Dim As Long x1, y1, x2, y2, x3, y3
    Dim As Double rAngle
    rAngle = _D2R(dAngle)
    x1 = BaseX + lngth * Cos(rAngle)
    y1 = BaseY + lngth * Sin(rAngle)
    x2 = BaseX + .8 * lngth * Cos(rAngle - _Pi(.05))
    y2 = BaseY + .8 * lngth * Sin(rAngle - _Pi(.05))
    x3 = BaseX + .8 * lngth * Cos(rAngle + _Pi(.05))
    y3 = BaseY + .8 * lngth * Sin(rAngle + _Pi(.05))
    Line (BaseX, BaseY)-(x1, y1), colr
    Line (x1, y1)-(x2, y2), colr
    Line (x1, y1)-(x3, y3), colr
End Sub

   
The circle does not get stuck at this peninsula, you don't see the white arrow pushing into the corner.

Here is a common example of when a circle does get stuck because the white arrow is headed straight into the point of peninsula and the lower body is going to collide with it. Here you can see why the blue arrow direction is the way out of being stuck.


Attached Files Image(s)
   
b = b + ...
Reply
#22
Oh I forgot to post my solution to James Container #5, where the ball no longer gets stuck on peninsulas. @TempodiBasic comment reminded me of it.

Here is solution, I added a message box to stop in the places I was getting hung at. Part of the solution was to use average of normal directions when ball is in 2 lines ie peninsula but also bays too if I recall. Sometimes on rare occasions the calculation is wrong usually in upper left corner, that's where message box shows the average angle that looks wrong and the corrected direction.
Code: (Select All)
Option _Explicit
_Title "James Random Container 5" ' b+ 2022-10-21
' Modify the lineSegIntersectCircle function to count number of points intersecting
' if more than one line segment do the one with the most points   Yes! works better.
' Still can get a point stuck but it doesn't fly out of bounds.
' 2022-10-19 Aha! a simple solution to being stuck on peninsulas, just back out 1 more time
' nope, nor 2 more, nor until both x or y change more than a pixel
' Now we are getting stuck in bays as well as peninsulas!   and it seems bad choice to use segment
' normal to get out of a bay, usually just follow white arrow for bays...
' Move Container making code into a sub and make doubly raggety changing min 80 to min 40
' Added delaid mode toggle with d keypresses listen for BEEP in this mode it goes into sleep mode.
' Have to press a key to continue...

' 2022-10-21 Container #5 OK this time around let's average the normals (hopefully only 2) I think
' for both innies and outies the aveage of 2 line segments will be ideal path out of either.
' Yes that works fine until the average is screwed up in top left corner mostly, very rare!
' I am tracking normal reflections, reflections by average of perpediculars when more than one segment
' and finally fixed average by simply using the angle to screen center!
' I've added my message box code for showing the wrongness of the avaerage and the fixed angle.

Screen _NewImage(800, 680, 32)
_ScreenMove 250, 10
Randomize Timer
Const x0 = 400, y0 = 300, bspeed = 5, br = 15 ' make ball radius (br) at least 2 * speed
_PrintMode _KeepBackground
Type lineSegment
    As Single x1, y1, x2, y2, dN ' 2 end points and an Normal angle pointing towards center
End Type
Dim Shared As Long Container, Nlines, L '  building a Random Container with NLines random lines
Dim bx, by, ba '                           changing ball location and angle
Dim test, saveL, totN, hits, totLinesHit ' now for bouncing circles around, finding the best reflection
Dim saveBx, saveBy, diff '                 check for stuckness
Dim delaid, delayT, k$ '                   delay mode and time, key check for reset, escape delay mode toggle
Dim As Long nr, anr, fixr '                track types of bounces
'  nr = normal reflection, anr = average of 2 normals, fixr is fixed average

restart:
Nlines = 0: nr = 0: anr = 0: fixr = 0 'reset report data
ReDim Shared Boundaries(1 To 100) As lineSegment
MakeContainer ' for background ball boundaries
bx = x0: by = y0: ba = Rnd * 360 ' setup up ball in middle of screen/container random heading = ba (ball angle)

' ok just bounce ball around the polygon container
Do
    _PutImage , Container, 0
    Locate 1, 1: Print "  Normal Reflection:"; nr; ", (beep) average normals:"; anr; ", (mbox) fixed average:"; fixr
    Circle (bx, by), br ' draw ball then calc next loaction
    bx = bx + bspeed * CosD(ba) ' test x, y is new ball position if dont run into wall
    by = by + bspeed * SinD(ba)
    hits = 0: saveL = 0: totN = 0: totLinesHit = 0
    For L = 1 To Nlines ' get line segment with highest hit count if any
        test = lineSegIntersectCircle(Boundaries(L).x1, Boundaries(L).y1, Boundaries(L).x2, Boundaries(L).y2, bx, by, br)
        If test Then
            If test > hits Then saveL = L: hits = test ' save the line number with greatest anount of hits to backup from
            totN = totN + Boundaries(L).dN: totLinesHit = totLinesHit + 1 ' save data to get an average N
        End If
    Next
    delayT = 0
    If hits Then ' back circle out of most hit line should show up right next to line
        While lineSegIntersectCircle(Boundaries(saveL).x1, Boundaries(saveL).y1, Boundaries(saveL).x2, Boundaries(saveL).y2, bx, by, br) ' back up circle
            bx = bx + CosD(ba - 180)
            by = by + SinD(ba - 180)
        Wend
        _PutImage , Container, 0 ' show circle hit on boundary
        Locate 1, 1: Print "  Normal Reflection:"; nr; ", (beep) average normals:"; anr; ", (mbox) fixed average:"; fixr
        Circle (bx, by), br

        ' Yellow arrow for incoming towards boundary (I reversed the head of arrow to compare to reflection angle)
        ArrowTo bx + 3 * br * CosD(ba + 180), by + 3 * br * SinD(ba + 180), ba, 3 * br, &HFFFFFF00

        ' Blue Vector Perpendicular to plane
        ArrowTo bx, by, Boundaries(saveL).dN, 5 * br, &HFF0000FF

        If Abs(bx - saveBx) > 1 Or Abs(by - saveBy) > 1 Then ' Ball is moving right along
            diff = Boundaries(saveL).dN - ba + 180
            ba = Boundaries(saveL).dN + diff ' >>>> new direction
            delayT = 1: nr = nr + 1
        Else ' could be stuck
            If totLinesHit = 1 Then
                diff = Boundaries(saveL).dN - ba + 180
                ba = Boundaries(saveL).dN + diff ' >>>> new direction
                delayT = 1: nr = nr + 1
            ElseIf totLinesHit > 1 Then
                'If totLinesHit > 1 Then
                ' new 2022-10-21 fix angle to average of normals hit totN is total or all Normals / total lines hit
                ' new ball direction is average of normals
                ba = totN / totLinesHit ' >>>>>>>>>>>>>>>>>>>>>>> new direction from line segment perpendicular angle
                delayT = 3: Beep: anr = anr + 1
                If Abs(ba - DAtan2(bx, by, x0, y0)) > 90 And Abs(ba - 360 - DAtan2(bx, by, x0, y0)) > 90 Then
                    Beep ' indicate by sound that the alternate angle for ball was used
                    mBox "Multiple Line hits", "Ave of norms looks wrong:" + Str$(totN / totLinesHit) + ", fixed using:" + Str$(DAtan2(bx, by, x0, y0))
                    ba = DAtan2(bx, by, x0, y0)
                    fixr = fixr + 1
                End If
            End If
        End If
        If delaid Then
            ArrowTo bx, by, ba, 3 * br, &HFFFFFFFF
            _Display
            _Delay delayT
        End If
    End If
    ArrowTo bx, by, ba, 3 * br, &HFFFFFFFF
    saveBx = bx: saveBy = by
    _Display

    k$ = InKey$
    If k$ = "d" Then
        delaid = 1 - delaid ' toggle dlaid mode
    ElseIf k$ = "r" Then
        GoTo restart
    End If
    _Limit 300
Loop Until _KeyDown(27)

Sub MakeContainer
    Dim cx, cy, x1, y1, flag, x2, y2 ' building container
    Dim As _Unsigned Long c1

    cx = _Width / 2: cy = _Height / 2 + 40
    c1 = _RGB32(0, 150, 85) ' minty green background out of bounds
    Cls
    x1 = 50
    y1 = 50
    flag = 0
    While flag = 0
        x2 = (Rnd * 80) + 40 + x1
        If x2 > 750 Then
            x2 = 750
            flag = 1
        End If
        y2 = Rnd * 60 + 20
        Line (x1, y1)-(x2, y2), c1
        Nlines = Nlines + 1
        Boundaries(Nlines).x1 = x1: Boundaries(Nlines).y1 = y1
        Boundaries(Nlines).x2 = x2: Boundaries(Nlines).y2 = y2
        Boundaries(Nlines).dN = DAtan2(x1, y1, x2, y2) + 90 ' the angle x2, y2 is to x1, y1 + 90 points inward
        If Boundaries(Nlines).dN > 359.99999 Then Boundaries(Nlines).dN = Boundaries(Nlines).dN - 360
        x1 = x2
        y1 = y2
    Wend

    flag = 0
    While flag = 0
        y2 = (Rnd * 80) + 40 + y1
        If y2 > 550 Then
            y2 = 550
            flag = 1
        End If
        x2 = 750 - (Rnd * 60 + 20)
        Line (x1, y1)-(x2, y2), c1
        Nlines = Nlines + 1
        Boundaries(Nlines).x1 = x1: Boundaries(Nlines).y1 = y1
        Boundaries(Nlines).x2 = x2: Boundaries(Nlines).y2 = y2
        Boundaries(Nlines).dN = DAtan2(x1, y1, x2, y2) + 90 ' the angle x2, y2 is to x1, y1 + 90 points inward
        x1 = x2
        y1 = y2
    Wend

    flag = 0
    While flag = 0
        x2 = x1 - ((Rnd * 80) + 40)
        If x2 < 50 Then
            x2 = 50
            flag = 1
        End If
        y2 = 550 - (Rnd * 60 + 20)
        Line (x1, y1)-(x2, y2), c1
        Nlines = Nlines + 1
        Boundaries(Nlines).x1 = x1: Boundaries(Nlines).y1 = y1
        Boundaries(Nlines).x2 = x2: Boundaries(Nlines).y2 = y2
        Boundaries(Nlines).dN = DAtan2(x1, y1, x2, y2) + 90 ' the angle x2, y2 is to x1, y1 + 90 points inward
        x1 = x2
        y1 = y2
    Wend

    flag = 0
    While flag = 0
        y2 = y1 - ((Rnd * 80) + 40)
        If y2 < 50 Then
            y2 = 50
            flag = 1
        End If
        x2 = Rnd * 60 + 20
        If flag = 1 Then x2 = 50
        Line (x1, y1)-(x2, y2), c1
        Nlines = Nlines + 1
        Boundaries(Nlines).x1 = x1: Boundaries(Nlines).y1 = y1
        Boundaries(Nlines).x2 = x2: Boundaries(Nlines).y2 = y2
        Boundaries(Nlines).dN = DAtan2(x1, y1, x2, y2) + 90 ' the angle x2, y2 is to x1, y1 + 90 points inward
        x1 = x2
        y1 = y2
    Wend
    Paint (1, 1), c1, c1
    Locate 37, 1
    Print " Press d for delay mode, stop at every intersect. Yellow = the angle of ball heading towards line,"
    Print "    Blue = angle perpendicular (normal) to boundary line. White = angle of reflection off line."
    Print "                        A longer pause and beep is an average of 2 normals."
    Print "                         Press r to reset boundary lines,  escape to quite."
    Container = _NewImage(_Width, _Height, 32)
    _PutImage , 0, Container
End Sub

' return count of how many points overlap segment
Function lineSegIntersectCircle (x1, y1, x2, y2, cx, cy, r)
    ' x1, y1 and x2, y2  are end points of line segment
    ' cx, cy are circle center with radius r
    Dim rtn, d, dx, dy, i, x, y
    d = Sqr((x1 - x2) ^ 2 + (y1 - y2) ^ 2)
    dx = (x2 - x1) / d
    dy = (y2 - y1) / d
    For i = 0 To d
        x = x1 + dx * i
        y = y1 + dy * i
        If Sqr((x - cx) ^ 2 + (y - cy) ^ 2) <= r Then rtn = rtn + 1
    Next
    lineSegIntersectCircle = rtn
End Function

' use angles in degrees units instead of radians (converted inside sub)
Function CosD (degrees)
    ' Note this function uses whatever the default type is, better not be some Integer Type.
    CosD = Cos(_D2R(degrees))
End Function

' use angles in degrees units instead of radians (converted inside sub)
Function SinD (degrees)
    ' Note this function uses whatever the default type is, better not be some Integer Type.
    SinD = Sin(_D2R(degrees))
End Function

' use angles in degrees units instead of radians (converted inside sub)
Function DAtan2 (x1, y1, x2, y2) ' The angle in degrees a 2nd point (x2, y2)  makes to a first point (x1, y1)
    ' Note this function uses whatever the default type is, better not be some Integer Type.
    ' Delta means change between 1 measure and another for example x2 - x1
    Dim deltaX, deltaY, rtn
    deltaX = x2 - x1
    deltaY = y2 - y1
    '  To find the angle point(x2, y2) makes to (x1, y1) in Degrees
    ' Take DegreeAngle = DAtan2(y2 - y1, x2 - x1)
    rtn = _R2D(_Atan2(deltaY, deltaX))
    If rtn < 0 Then DAtan2 = rtn + 360 Else DAtan2 = rtn
End Function

' use angles in degrees units instead of radians (converted inside sub)
Sub ArrowTo (BaseX As Long, BaseY As Long, dAngle As Double, lngth As Long, colr As _Unsigned Long)
    Dim As Long x1, y1, x2, y2, x3, y3
    Dim As Double rAngle
    rAngle = _D2R(dAngle)
    x1 = BaseX + lngth * Cos(rAngle)
    y1 = BaseY + lngth * Sin(rAngle)
    x2 = BaseX + .8 * lngth * Cos(rAngle - _Pi(.05))
    y2 = BaseY + .8 * lngth * Sin(rAngle - _Pi(.05))
    x3 = BaseX + .8 * lngth * Cos(rAngle + _Pi(.05))
    y3 = BaseY + .8 * lngth * Sin(rAngle + _Pi(.05))
    Line (BaseX, BaseY)-(x1, y1), colr
    Line (x1, y1)-(x2, y2), colr
    Line (x1, y1)-(x3, y3), colr
End Sub

Sub mBox (title As String, m As String)

    Dim bg As _Unsigned Long, fg As _Unsigned Long
    bg = &H33404040
    fg = &HFF33AAFF

    'first screen dimensions and items to restore at exit
    Dim sw As Long, sh As Long
    Dim curScrn As Long, backScrn As Long, mbx As Long 'some handles
    Dim ti As Long, limit As Long 'ti = text index for t$(), limit is number of chars per line
    Dim i As Long, j As Long, ff As _Bit, addb As _Byte 'index, flag and
    Dim bxH As Long, bxW As Long 'first as cells then as pixels
    Dim mb As Long, mx As Long, my As Long, mi As Long, grabx As Long, graby As Long
    Dim tlx As Long, tly As Long 'top left corner of message box
    Dim lastx As Long, lasty As Long, t As String, b As String, c As String, tail As String
    Dim d As String, r As Single, kh As Long

    'screen and current settings to restore at end ofsub
    ScnState 0
    sw = _Width: sh = _Height

    _KeyClear '<<<<<<<<<<<<<<<<<<<< do i still need this?   YES! 2019-08-06 update!

    'screen snapshot
    curScrn = _Dest
    backScrn = _NewImage(sw, sh, 32)
    _PutImage , curScrn, backScrn

    'setup t() to store strings with ti as index, linit 58 chars per line max, b is for build
    ReDim t(0) As String: ti = 0: limit = 58: b = ""
    For i = 1 To Len(m)
        c = Mid$(m, i, 1)
        'are there any new line signals, CR, LF or both? take CRLF or LFCR as one break but dbl LF or CR means blank line
        Select Case c
            Case Chr$(13) 'load line
                If Mid$(m, i + 1, 1) = Chr$(10) Then i = i + 1
                t(ti) = b: b = "": ti = ti + 1: ReDim _Preserve t(ti) As String
            Case Chr$(10)
                If Mid$(m, i + 1, 1) = Chr$(13) Then i = i + 1
                t(ti) = b: b = "": ti = ti + 1: ReDim _Preserve t(ti)
            Case Else
                If c = Chr$(9) Then c = Space$(4): addb = 4 Else addb = 1
                If Len(b) + addb > limit Then
                    tail = "": ff = 0
                    For j = Len(b) To 1 Step -1 'backup until find a space, save the tail end for next line
                        d = Mid$(b, j, 1)
                        If d = " " Then
                            t(ti) = Mid$(b, 1, j - 1): b = tail + c: ti = ti + 1: ReDim _Preserve t(ti)
                            ff = 1 'found space flag
                            Exit For
                        Else
                            tail = d + tail 'the tail grows!
                        End If
                    Next
                    If ff = 0 Then 'no break? OK
                        t(ti) = b: b = c: ti = ti + 1: ReDim _Preserve t(ti)
                    End If
                Else
                    b = b + c 'just keep building the line
                End If
        End Select
    Next
    t(ti) = b
    bxH = ti + 3: bxW = limit + 2

    'draw message box
    mbx = _NewImage(60 * 8, (bxH + 1) * 16, 32)
    _Dest mbx
    Color _RGB32(128, 0, 0), _RGB32(225, 225, 255)
    Locate 1, 1: Print Left$(Space$((bxW - Len(title) - 3) / 2) + title + Space$(bxW), bxW)
    Color _RGB32(225, 225, 255), _RGB32(200, 0, 0)
    Locate 1, bxW - 2: Print " X "
    Color fg, bg
    Locate 2, 1: Print Space$(bxW);
    For r = 0 To ti
        Locate 1 + r + 2, 1: Print Left$(" " + t(r) + Space$(bxW), bxW);
    Next
    Locate 1 + bxH, 1: Print Space$(limit + 2);

    'now for the action
    _Dest curScrn

    'convert to pixels the top left corner of box at moment
    bxW = bxW * 8: bxH = bxH * 16
    tlx = (sw - bxW) / 2: tly = (sh - bxH) / 2
    lastx = tlx: lasty = tly
    'now allow user to move it around or just read it
    While 1
        Cls
        _PutImage , backScrn
        _PutImage (tlx, tly), mbx, curScrn
        _Display
        While _MouseInput: Wend
        mx = _MouseX: my = _MouseY: mb = _MouseButton(1)
        If mb Then
            If mx >= tlx And mx <= tlx + bxW And my >= tly And my <= tly + 16 Then 'mouse down on title bar
                If mx >= tlx + bxW - 24 Then Exit While
                grabx = mx - tlx: graby = my - tly
                Do While mb 'wait for release
                    mi = _MouseInput: mb = _MouseButton(1)
                    mx = _MouseX: my = _MouseY
                    If mx - grabx >= 0 And mx - grabx <= sw - bxW And my - graby >= 0 And my - graby <= sh - bxH Then
                        'attempt to speed up with less updates
                        If ((lastx - (mx - grabx)) ^ 2 + (lasty - (my - graby)) ^ 2) ^ .5 > 10 Then
                            tlx = mx - grabx: tly = my - graby
                            Cls
                            _PutImage , backScrn
                            _PutImage (tlx, tly), mbx, curScrn
                            lastx = tlx: lasty = tly
                            _Display
                        End If
                    End If
                    _Limit 400
                Loop
            End If
        End If
        kh = _KeyHit
        If kh = 27 Or kh = 13 Or kh = 32 Then Exit While
        _Limit 400
    Wend

    'put things back
    Color _RGB32(255, 255, 255), _RGB32(0, 0, 0): Cls '
    _PutImage , backScrn
    _Display
    _FreeImage backScrn
    _FreeImage mbx
    ScnState 1 'Thanks Steve McNeill
End Sub

'  ======================= This is old version dev for mBox or InputBox and new version dev with new GetArrayItem$
' for saving and restoring screen settins
Sub ScnState (restoreTF As Long) 'Thanks Steve McNeill
    Static defaultColor~&, backGroundColor~&
    Static font&, dest&, source&, row&, col&, autodisplay&, mb&
    If restoreTF Then
        _Font font&
        Color defaultColor~&, backGroundColor~&
        _Dest dest&
        _Source source&
        Locate row&, col&
        If autodisplay& Then _AutoDisplay Else _Display
        _KeyClear
        While _MouseInput: Wend 'clear mouse clicks
        mb& = _MouseButton(1)
        If mb& Then
            Do
                While _MouseInput: Wend
                mb& = _MouseButton(1)
                _Limit 100
            Loop Until mb& = 0
        End If
    Else
        font& = _Font: defaultColor~& = _DefaultColor: backGroundColor~& = _BackgroundColor
        dest& = _Dest: source& = _Source
        row& = CsrLin: col& = Pos(0): autodisplay& = _AutoDisplay
        _KeyClear
    End If
End Sub

   
   
b = b + ...
Reply
#23
That's a blast! +1

Pete
Reply
#24
This is main difference in code between Container #4 and Container #5

Container #5 has these fixes:
Code: (Select All)
        If Abs(bx - saveBx) > 1 Or Abs(by - saveBy) > 1 Then ' Ball is moving right along
            diff = Boundaries(saveL).dN - ba + 180
            ba = Boundaries(saveL).dN + diff ' >>>> new direction
            delayT = 1: nr = nr + 1
        Else ' could be stuck
            If totLinesHit = 1 Then
                diff = Boundaries(saveL).dN - ba + 180
                ba = Boundaries(saveL).dN + diff ' >>>> new direction
                delayT = 1: nr = nr + 1
            ElseIf totLinesHit > 1 Then
                'If totLinesHit > 1 Then
                ' new 2022-10-21 fix angle to average of normals hit totN is total or all Normals / total lines hit
                ' new ball direction is average of normals
                ba = totN / totLinesHit ' >>>>>>>>>>>>>>>>>>>>>>> new direction from line segment perpendicular angle
                delayT = 3: Beep: anr = anr + 1
                If Abs(ba - DAtan2(bx, by, x0, y0)) > 90 And Abs(ba - 360 - DAtan2(bx, by, x0, y0)) > 90 Then
                    Beep ' indicate by sound that the alternate angle for ball was used
                    mBox "Multiple Line hits", "Ave of norms looks wrong:" + Str$(totN / totLinesHit) + ", fixed using:" + Str$(DAtan2(bx, by, x0, y0))
                    ba = DAtan2(bx, by, x0, y0)
                    fixr = fixr + 1
                End If
            End If
        End If

First IF detects if ball is stuck, same place it was in last loop.
Then it detects if ball is collided with 1 line or more than one.
 
If one line, stick with the plan it will work it's way out, notice same code is used as if it weren't stuck.
If more than one line we have the problem of hitting 2 lines simultaneously or nearly so, so I took the average of the normals of the 2 lines. This calc is not perfect so I compare it to the angle of the ball to the center of the container if the calc is off by more than 90 degrees I head the ball to the center of the container and issue a message to that effect with the message box.

With the message box, I could see that the calc ave normal is wrong and choosing to head ball to center is smart way out of getting stuck.

Looking at screen shot, I see I was counting normal reflections off wall, the times where I had to use an average of normal angles because more than one line was intersected and the number of times I had to fix the average because it was pointed out not in.
b = b + ...
Reply




Users browsing this thread: 9 Guest(s)