Angle, Vector, Radian, and Distance Library
#1
While working on the tutorial site I decided the "Vectors, Angles & Rotation" lesson needed rewritten. Over the years I've had a few people point out that I was calculating these using methods much more difficult than needed. Also pointed out was some incorrect terminology I was using.

So, over the past week I went on a math quest to try and get these concepts correct. I decided to write a small library of routines to work with angles, vectors, and radians.

If you are mathematically inclined could you please take a moment to look over the code for any glaring errors I may have? Perhaps better ways of doing the math, my wording, etc.. I'm completely self-taught when it comes to higher math (Algebra II in high school) and don't always know the correct nomenclature to use. I believe I have it right this time from the week-long math brain-bender I went on.

These routines will be incorporated in the lesson and I plan to write a new lesson for creating and maintaining your own libraries using this one amongst others.

Code: (Select All)
'* Vector Demo - Tag, you're it!
'------------------------------------------------------------------------------------------------------------
' AVRDLibTop.BI
' Angle, Vector, Radian, and Distance Library
' By Terry Ritchie  quickbasic64@gmail.com
' 08/26/22
' Open source code - modify and distribute freely
' Original author's name must remain intact
'
' Declaration needed for the correct operation of the library.
'
TYPE XYPOINT '    2D point location definition
    x AS SINGLE ' x coordinate
    y AS SINGLE ' y coordinate
END TYPE
'------------------------------------------------------------------------------------------------------------

TYPE A_CIRCLE '                  circle definition
    location AS XYPOINT '        x,y coordinate
    vector AS XYPOINT '          x,y vector
    radius AS INTEGER '          radius
END TYPE

DIM RCircle AS A_CIRCLE '        a red circle
DIM GCircle AS A_CIRCLE '        a green circle
DIM Speed AS INTEGER '           speed of red circle
DIM Distance AS SINGLE '         distance between red and green circle
DIM Vector AS XYPOINT '          vector calculations
DIM Angle AS SINGLE '            angle calculations
DIM Radian AS SINGLE '           radian calculations

SCREEN _NEWIMAGE(640, 480, 32) ' graphics screen
_MOUSEHIDE '                     hide operating system mouse pointer
RCircle.location.x = 319 '       define circle properties
RCircle.location.y = 239
RCircle.radius = 30
GCircle.radius = 30
Speed = 3
DO '                                                               begin main program loop
    _LIMIT 60 '                                                    60 frames per second
    CLS
    P2PVector RCircle.location, GCircle.location, RCircle.vector ' vector from green to red circle
    Distance = P2PDistance(RCircle.location, GCircle.location) '   distance from center of circles
    Angle = P2PAngle(RCircle.location, GCircle.location) '         angle from center of circles
    Radian = Angle2Radian(Angle) '                                 radian calculated from angle
    Angle2Vector Angle, Vector
    PRINT "      Library Results     "
    PRINT " -------------------------"
    PRINT " P2PDistance   "; Distance
    PRINT " P2PAngle      "; Angle
    PRINT " Vector2Angle  "; Vector2Angle(Vector) '                angle calculated from vector
    PRINT " Radian2Angle  "; Radian2Angle(Radian) '                angle calculated from radian
    PRINT " Angle2Radian  "; Radian
    PRINT " Vector2Radian "; Vector2Radian(Vector) '               radian calculated from vector
    PRINT " Radian2Circle "; Radian2Circle(Radian) '               circle radian calculated from radian
    PRINT USING " P2PVector      Vx=##.##  Vy=##.##"; RCircle.vector.x; RCircle.vector.y
    PRINT USING " Angle2Vector   Vx=##.##  Vy=##.##"; Vector.x; Vector.y
    Radian2Vector Radian, Vector '                                 vector calculated from radian
    PRINT USING " Radian2Vector  Vx=##.##  Vy=##.##"; Vector.x, Vector.y
    LOCATE 2, 39: PRINT "TAG" '                                              print title of game
    IF Distance > GCircle.radius + RCircle.radius THEN '                     are circles colliding?
        RCircle.location.x = RCircle.location.x + RCircle.vector.x * Speed ' no, update enemy location
        RCircle.location.y = RCircle.location.y + RCircle.vector.y * Speed
    ELSE '                                                                   yes, circles colliding
        LOCATE 2, 35: PRINT "You're it!" '                                   print message to player
    END IF
    WHILE _MOUSEINPUT: WEND '                                                get latest mouse information
    GCircle.location.x = _MOUSEX '                                           update player location
    GCircle.location.y = _MOUSEY
    CIRCLE (GCircle.location.x, GCircle.location.y), GCircle.radius, _RGB32(0, 255, 0) '  draw player
    PAINT (GCircle.location.x, GCircle.location.y), _RGB32(0, 127, 0), _RGB32(0, 255, 0)
    CIRCLE (RCircle.location.x, RCircle.location.y), RCircle.radius, _RGB32(255, 0, 0) '  draw enemy
    PAINT (RCircle.location.x, RCircle.location.y), _RGB32(127, 0, 0), _RGB32(255, 0, 0)
    CIRCLE (RCircle.location.x, RCircle.location.y), 30, , -Radian2Circle(Radian), -Radian2Circle(Radian)
    _DISPLAY '                                                               update screen with changes
LOOP UNTIL _KEYDOWN(27) '                                                    leave when ESC pressed
SYSTEM '                                                                     return to operating system

'------------------------------------------------------------------------------------------------------------
' AVRDLib.BI
' Angle, Vector, Radian, and Distance Library
' By Terry Ritchie  quickbasic64@gmail.com
' 08/26/22
' Open source code - modify and distribute freely
' Original author's name must remain intact
'
' All subroutines and functions treat 0 degree and 0 radian as up and rotation is clock-wise.
' Some routines have dependencies on other routines.
'
' ** Subroutines **
'
' Angle2Vector  - converts the supplied degree angle (0 to 360) to a normalized vector
' P2PVector     - calculates the normalized vector between 2 points
' Radian2Vector - converts the supplied radian (0 to 2*PI) to a normalized vector

' ** Functions **
'
' Angle2Radian  - converts the supplied angle (0 to 360) to a radian (0 to 2*PI)
' FixRange      - ensures a value stays within a 0 to maximum range of values
' P2PDistance   - calculates the distance between 2 points
' P2PAngle      - calculates the angle (0 to 360) between 2 points
' Radian2Angle  - converts the supplied radian (0 to 2*PI) to a degree angle (0 to 360)
' Radian2Circle - converts the supplied radian (0 to 2*PI) to a radian (0 to 2*PI) to be used with the
'                 CIRCLE statement. The output radian adjusts the CIRCLE statement's radian so 0 is up
'                 and increasing radian values rotate in a clock-wise fashion.
' Vector2Angle  - converts the supplied normalized vector to a degree angle (0 to 360)
' Vector2Radian - converts the supplied normalized vector to a radian (0 to 2*PI)
'
' The following statements are needed at the top of the code for this library to function properly.
' (Use INCLUDE metastatement with AVRDLibTop.BI to include automatically.)
'
'  TYPE XYPOINT '    2D point location definition
'      x AS SINGLE ' x coordinate
'      y AS SINGLE ' y coordinate
'  END TYPE
'
'                                   0 Deg
'                                  0PI Rad                                   All functions and subroutines
'                                  *******                                   designed to treat 0 degrees,
'                             *****       *****                              0 radian, and vector 0,-1
'                         ****                 ****                          as up or north with values
'             315 Deg   **         Vec 0,-1        **   45 Deg               increasing clockwise.
'                     **              |              **
'                    *   Vec -1,-1    |     Vec 1,-1   *                     Radians supplied in the range
'                   *            \    |    /            *                    of 0 to 2 * PI
'                  *               \  |  /               *
'        1.5PI Rad *                 \|/                 * 1/2PI Rad         Degrees supplied in the range
'         270 Deg  * Vec -1,0 --------+--------- Vec 1,0 *  90 Deg           of 0 to 359.99..
'                  *                 /|\                 *
'                  *               /  |  \               *                   Vectors supplied in the range
'                   *            /    |    \            *                    of -1,-1 to 1,1 normalized
'                    *   Vec -1,1     |     Vec 1,1    *
'             225 Deg **              |              ** 135 Deg
'                       **         Vec 0,1         **
'                         ****                 ****
'                             *****       *****
'                                  *******
'                                  PI Rad
'                                  180 Deg
'
'------------------------------------------------------------------------------------------------------------

SUB P2PVector (P1 AS XYPOINT, P2 AS XYPOINT, V AS XYPOINT)

    '** NOTE: V passed by reference is altered

    '** Point to Point Vector Calculator
    '** Returns the x,y normalized vectors from P1 to P2

    ' P1.x, P1.y = FROM coordinate            (INPUT )
    ' P2.x, P2.y = TO coordinate              (INPUT )
    ' V.x, V.y   = normalized vectors to P2   (OUTPUT)

    DIM D AS SINGLE ' distance between points

    V.x = P2.x - P1.x '      horizontal distance (  side A  )
    V.y = P2.y - P1.y '      vertical distance   (  side B  )
    D = _HYPOT(V.x, V.y) '   direct distance (hypotenuse)
    IF D = 0 THEN EXIT SUB ' can't divide by 0
    V.x = V.x / D '          normalized x vector (  -1 to 1 )
    V.y = V.y / D '          normalized y vector (  -1 to 1 )

END SUB

'------------------------------------------------------------------------------------------------------------

FUNCTION P2PDistance (P1 AS XYPOINT, P2 AS XYPOINT)

    '** Point to Point Distance Calculator
    '** Returns the distance between P1 and P2

    ' P1.x, P1.y - FROM coordinate (INPUT)
    ' P2.x, P2.y - TO   coordinate (INPUT)
    ' returns SQR((P2.x - P1.x)^2 + (P2.y - P1.y)^2) using QB64 _HYPOT() function

    P2PDistance = _HYPOT(P2.x - P1.x, P2.y - P1.y) ' return direct distance (hypotenuse)

END FUNCTION

'------------------------------------------------------------------------------------------------------------

FUNCTION P2PAngle (P1 AS XYPOINT, P2 AS XYPOINT)

    '** Point to Point Angle Calculator
    '** Returns the degree angle from point 1 to point 2 with 0 degrees being up

    ' P1.x, P1.y - FROM coordinate (INPUT)
    ' P2.x, P2.y - TO   coordinate (INPUT)

    ' 57.29578 = 180 / PI

    DIM Theta AS SINGLE ' the returned degree angle

    IF P1.y = P2.y THEN '                                 do both points have same y value?
        IF P1.x = P2.x THEN '                             yes, do both points have same x value?
            EXIT FUNCTION '                               yes, identical points, no angle (0)
        END IF
        IF P2.x > P1.x THEN '                             is second point to the right of first point?
            P2PAngle = 90 '                               yes, the angle must be 90
        ELSE '                                            no, second point is to the left of first point
            P2PAngle = 270 '                              the amgle must be 270
        END IF
        EXIT FUNCTION '                                   leave function, angle calculated
    END IF
    IF P1.x = P2.x THEN '                                 do both points have the same x value?
        IF P2.y > P1.y THEN '                             yes, is second point below first point?
            P2PAngle = 180 '                              yes, the angle must be 180
        END IF
        EXIT FUNCTION '                                   leave function, angle calculated
    END IF
    Theta = _ATAN2(P2.y - P1.y, P2.x - P1.x) * 57.29578 ' calculate +/-180 degree angle
    IF Theta < 0 THEN Theta = 360 + Theta '               convert to 360 degree
    Theta = Theta + 90 '                                  set 0 degrees as up
    IF Theta > 360 THEN Theta = Theta - 360 '             adjust accordingly if needed
    P2PAngle = Theta '                                    return degree angle (0 to 359.99..)

END FUNCTION

'------------------------------------------------------------------------------------------------------------

FUNCTION Vector2Angle (V AS XYPOINT)

    '** Vector to Angle Calculator
    '** Converts the supplied normalized vector to a degree angle with 0 degrees facing up

    ' V.x, V.y - normalized vector (INPUT)

    ' 57.29578 = 180 / PI

    DIM Degrees AS SINGLE ' the returned degree angle

    Degrees = _ATAN2(V.y, V.x) * 57.29578 '         get angle from vector (-180 to 180)
    IF Degrees < 0 THEN Degrees = 360 + Degrees '   convert to 360
    Degrees = Degrees + 90 '                        set 0 degrees as up
    IF Degrees > 360 THEN Degrees = Degrees - 360 ' adjust if necessary
    Vector2Angle = Degrees '                        return degree angle (0 to 359.99..)

END FUNCTION

'------------------------------------------------------------------------------------------------------------

SUB Angle2Vector (A AS SINGLE, V AS XYPOINT)

    '** NOTE: V passed by reference is altered

    '** Angle to Vector Calculator
    '** Converts the supplied degree angle to a normalized vector

    ' A        - degree angle      (INPUT )
    ' V.x, V.y - normalized vector (OUTPUT)

    ' .017453292 = PI / 180

    DIM Angle AS SINGLE ' the angle value passed in

    Angle = A '                         don't alter passed in value
    IF Angle < 0 OR Angle >= 360 THEN ' angle outside limits?
        Angle = FixRange(Angle, 360) '  yes, correct angle
    END IF
    V.x = SIN(Angle * .017453292) '     return x vector
    V.y = -COS(Angle * .017453292) '    return y vector

END SUB

'------------------------------------------------------------------------------------------------------------

FUNCTION Vector2Radian (V AS XYPOINT)

    '** Vector to Radian Calculator
    '** Converts the supplied vector to a radian (0 to 2*PI)

    ' V.x, V.y - the supplied vector (INPUT)

    Vector2Radian = Angle2Radian(Vector2Angle(V)) ' return radian (0 to 2*PI)

END FUNCTION

'------------------------------------------------------------------------------------------------------------

SUB Radian2Vector (R AS SINGLE, V AS XYPOINT)

    '** NOTE: V passed by reference is altered

    '** Radian to Vector Calculator
    '** Converts the supplied radian to a normalized vector

    ' R        - supplied radian                (INPUT )
    ' V.x, V.y - the returned normalized vector (OUTPUT)

    ' 6.2831852 = 2 * PI

    DIM Radian AS SINGLE ' the radian value passed in

    Radian = R '                                don't alter passed in value
    IF Radian < 0 OR Radian >= 6.2831852 THEN ' radian outside limits?
        Radian = FixRange(Radian, 6.2831852) '  yes, correct radian
    END IF
    Angle2Vector Radian2Angle(Radian), V '      return normalized vector

END SUB

'------------------------------------------------------------------------------------------------------------

FUNCTION Radian2Angle (R AS SINGLE)

    '** Radian to Degree Angle Calculator
    '** Converts the supplied radian to an angle in degrees

    ' R - the supplied radian (INPUT)
    ' returns R * 180 / PI using the QB64 _R2D() function

    ' 6.2831852 = 2 * PI

    DIM Radian AS SINGLE ' the radian value passed in

    Radian = R '                                don't alter passed in value
    IF Radian < 0 OR Radian >= 6.2831852 THEN ' radian outside limits?
        Radian = FixRange(Radian, 6.2831852) '  yes, correct radian
    END IF
    Radian2Angle = _R2D(Radian) '               return angle (0 to 359.99..)

END FUNCTION

'------------------------------------------------------------------------------------------------------------

FUNCTION Angle2Radian (A AS SINGLE)

    '** Degree Angle to Radian Calculator
    '** Converts the supplied degree angle to radian

    ' A - the supplied degree angle (INPUT)
    ' returns A * PI / 180 using QB64 _D2R() function

    DIM Angle AS SINGLE ' the angle value passed in

    Angle = A '                         don't alter passed in value
    IF Angle < 0 OR Angle >= 360 THEN ' angle outside limits?
        Angle = FixRange(Angle, 360) '  yes, correct angle
    END IF
    Angle2Radian = _D2R(Angle) '        return radian (0 to 2*PI)

END FUNCTION

'------------------------------------------------------------------------------------------------------------

FUNCTION Radian2Circle (R AS SINGLE)

    '** Radian to CIRCLE Statement Radian Calculator
    '** Produces an output radian to be used with the CIRCLE statement that makes 0 up and
    '** the radian value increase clockwise instead of the CIRCLE statement's counter-clockwise default.

    ' R - the supplied radian (INPUT)

    ' 7.8539815 = 2.5 * PI
    ' 6.2831852 =   2 * PI

    DIM Radian AS SINGLE ' the radian value passed in

    Radian = R '                                             don't alter passed in value
    IF Radian < 0 OR Radian >= 6.2831852 THEN '              radian outside of limits?
        Radian = FixRange(Radian, 6.2831852) '               yes, correct radian
    END IF
    Radian = 7.8539815 - Radian '                            radian location on circle
    IF Radian > 6.2831852 THEN Radian = Radian - 6.2831852 ' adjust if necessary
    Radian2Circle = Radian '                                 return CIRCLE statement radian value

END FUNCTION

'------------------------------------------------------------------------------------------------------------

FUNCTION FixRange (N AS SINGLE, M AS SINGLE)

    '** Returns N within a fixed 0 to M range

    ' N - value to check
    ' M - maximim value N must be less than

    '** Code for this function provided by dcromley
    '** https://staging.qb64phoenix.com/showthread.php?tid=817
    '** 08/27/22

    FixRange = N - INT(N / M) * M ' rotate value as needed

END FUNCTION
Reply


Messages In This Thread
Angle, Vector, Radian, and Distance Library - by TerryRitchie - 08-27-2022, 12:59 AM



Users browsing this thread: 3 Guest(s)