08-27-2022, 12:59 AM
(This post was last modified: 08-28-2022, 04:53 AM by TerryRitchie.)
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.
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