Lesson 20: Libraries

A library is a collection of useful tools that allow a programmer to develop code quickly and efficiently. A library provides pre-written snippets of code that a programmer can utilize over and over instead of recreating the wheel for each and every piece of software.

Imagine that you are creating software to perform conversions between various units of measures, weights, and temperatures. In lesson 2 we created a few programs of this type, but in order to do so we had to either know the conversion formulas or research their structure beforehand. The research, coding, and subsequent testing for accuracy of each formula can be very time consuming. It would be a great benefit if another programmer has already done this formula coding and offered the code for use. This is where libraries can be very beneficial. Let's assume another programmer did the coding and you found the library they offer. The library's name is "UnitConversions.BM" and all you need to do is add the library's code to your own with a statement like this:

'$INCLUDE:'UnitConversions.BM'

You now have all the unit conversion formulas offered in the library literally at your finger tips!

A QB64 library is nothing more than a .BAS source code file containing a collection of subroutines and functions. The file's extension is simply changed to .BM (or .BI, we'll get to that later) to identify the file as a library. Most programming languages offer methods of adding libraries to code and , in fact, most commercial and open source software written today relies heavily upon numerous libraries attached to the source code by the programmer. Most programming forums also include a dedicated area for the discussion and sharing of libraries with others. You can find the QB64 Phoenix Edition library discussion area here.

Building a Library

The best way to understand how a library functions is to build one. Let's create the library mentioned above called "UnitConversions.BM" using the unit conversion formulas from lesson 2. We'll then build a small program that utilizes the library to highlight how effective a library is in the hands of a programmer.

Start the IDE and type in the following code.

( This code can be found at .\tutorial\Lesson20\UnitConversions.BM )

'Unit Conversions Library

FUNCTION UC_F2C (f AS SINGLE)
    ' Convert Fahrenheit to Celsius and return result
    UC_F2C = 5 * (f - 32) / 9
END FUNCTION

FUNCTION UC_C2F (c AS SINGLE)
    ' Convert Celsius to Fahrenheit and return result
    UC_C2F = (c * 9 / 5) + 32
END FUNCTION

FUNCTION UC_mm2i (mm AS INTEGER)
    ' Convert millimeters to inches and return result
    UC_mm2i = mm / 25.4
END FUNCTION

FUNCTION uc_i2mm (i AS SINGLE)
    ' Convert inches to millimeters and return result
    uc_i2mm = i * 25.4
END FUNCTION

We now have a library with functions that convert between Celsius to Fahrenheit and millimeters to inches. Let's add this library to a program that allows the user to choose the desired conversion. Create a new listing in your IDE and type in the following program.

( This code can be found at .\tutorial\Lesson20\UnitConversionDemo.bas )

' Unit Conversion Library Demo

DIM KeyPress AS INTEGER ' user menu value selected
DIM v AS STRING '         value to convert

DO
   CLS
   LOCATE 2, 26: PRINT "Unit Conversion Library Demo" '            display menu
   LOCATE 4, 21: PRINT "Select from the options below (1 to 5)"
   LOCATE 7, 24: PRINT "1. Convert Fahrenheit to Celsius"
   LOCATE 9, 24: PRINT "2. Convert Celsius to Fahrenheit"
   LOCATE 11, 24: PRINT "3. Convert inches to millimeters"
   LOCATE 13, 24: PRINT "4. Convert millimeters to inches"
   LOCATE 15, 24: PRINT "5. Exit to the operating system"
   DO '                                                            begin key check loop
       _LIMIT 30 '                                                 don't overheat the CPU
        KeyPress = VAL(INKEY$) '                                   get value of key press
   LOOP UNTIL KeyPress > 0 AND KeyPress < 6 '                      leave when value is 1 to 5
   CLS
   SELECT CASE KeyPress '                                          which menu option selected?
       CASE 1            LOCATE 2, 25: PRINT "Convert Fahrenheit to Celsius"
           LOCATE 4, 25: LINE INPUT "Enter Fahrenheit value> ", v
           LOCATE 6, 25: PRINT "Result>"; STR$(UC_F2C(VAL(v)))
       CASE 2
           LOCATE 2, 25: PRINT "Convert Celsius to Fahrenheit"
           LOCATE 4, 25: LINE INPUT "Enter Celsius value> ", v
           LOCATE 6, 25: PRINT "Result>"; STR$(UC_C2F(VAL(v)))
       CASE 3
           LOCATE 2, 25: PRINT "Convert inches to millimeters"
           LOCATE 4, 25: LINE INPUT "Enter inch value> ", v
           LOCATE 6, 25: PRINT "Result>"; STR$(uc_i2mm(VAL(v)))
       CASE 4
           LOCATE 2, 25: PRINT "Convert millimeters to inches"
           LOCATE 4, 25: LINE INPUT "Enter millimeter value> ", v
           LOCATE 6, 25: PRINT "Result>"; STR$(UC_mm2i(VAL(v)))
       CASE 5 '                                                    user chose to exit
           SYSTEM '                                                return to operating system
   END SELECT
   LOCATE 12, 24: PRINT "Press any key to return to menu"
   SLEEP
LOOP

'$INCLUDE:'UnitConversions.BM'

Looking at the code listing above you'll notice that the functions UC_F2C(), UC_C2F, UC_i2mm, and UC_mm2i have all been highlighted in green by the IDE identifying them as custom functions. By including the library on line 44 the IDE considers the functions contained within the library simply part of the main body code.

There are two types of library files that can be created and included within your code. The first one, .BM files, only contain QB64 functions and subroutines. Library files ending in .BM must be placed at the end of your code since the IDE expects all functions and subroutines to appear after the main body. The second type of library files end in .BI. Library files ending in .BI contain variable and type declarations. Since variables declared with DIM, constants created with CONST, and type definitions created with TYPE ... END TYPE need to be at the beginning of code, so do .BI library files.

If you were to try and include variable declarations, type definitions, and/or constants within the same library file as the functions and subroutines the IDE would complain and generate an error. The need to create two different library files is not a common practice amongst programming languages. QB64 requires this behavior simply because the IDE does not have the capability of sorting out which statements go where. Perhaps in the future the IDE will be updated to support everything placed into one file, but for now you'll need to remember most libraries will require two separate files, a .BI that gets placed at the top of the code and a .BM file that gets placed at the bottom of the code. We'll create another library down below that uses both types of files so you can see this in action.

NOTE: Library files are not required to end in .BI and .BM. This naming convention seems to be the most common amongst most QB64 programmers. In fact, back in 2011 when I started writing QB64 libraries I named the top file .TOP and the bottom file .LIB. However, since the convention is overwhelmingly .BI and .BM this tutorial will stick with this naming convention to avoid confusion.

Unwritten Rules

Beyond the requirement for .BI and .BM files there are a few other rules that should be followed when writing libraries. Keep in mind these "rules" are merely suggestions that will help you and others when utilizing your libraries.

Collision Detection Library

Let's build a library that utilizes all of the suggested rules above and as a bonus will greatly aid in the development of your games. In lesson 15 you were introduced to four methods of collision detection; rectangular, circular, line intersection, and pixel perfect. Combining all of these detection routines into one library will save much time and effort when writing your games.

The first thing to consider is the .BI file. Which variables and type definitions do we absolutely need and how can they be designed to be used easily between the library's functions and procedures?

( This code can be found at .\tutorial\Lesson20\CollisionLibrary.BI )

' Collision Library V1.0
' Terry Ritchie 01/02/23

' CollisionLibrary.BI
' To be placed at the top of the main source code before variable and type definitions
'
TYPE CL_POINT '                    2D point definition
    x AS INTEGER '                 x coordinate
    y AS INTEGER '                 y coordinate
END TYPE

TYPE CL_CIRCLE '                   circle definition
    Center AS CL_POINT '           center of circle
    Radius AS INTEGER '            circle radius
END TYPE

TYPE CL_RECTANGLE '                2D rectangle definition
    TopLeft AS CL_POINT '          top left x,y location
    BottomRight AS CL_POINT '      bottom right x,y location
   Width AS INTEGER '              rectangle width
    Height AS INTEGER '            rectangle height
END TYPE

TYPE CL_SPRITE '                   sprite definition
    Image AS LONG '                sprite image
    Mask AS LONG '                 sprite mask image
    TopLeft AS CL_POINT '          top left x,y location
    BottomRight AS CL_POINT '      bottom right x,y location
END TYPE

TYPE CL_LINE '                     2D line segment definition
    Start AS CL_POINT '            x,y start of line
    Finish AS CL_POINT '           x,y end of line
END TYPE

TYPE CL_INTERSECT '                intersect collision points
   Line AS CL_POINT '              2D point location of line intersection
    Rectangle AS CL_RECTANGLE '    rectangular area of rectangle intersection
   Circle AS CL_POINT '            2D point location of circle intersection
    Pixel AS CL_POINT '            2D point location of pixel perfect intersection
END TYPE

DIM CL_Intersect AS CL_INTERSECT ' intersection of collisions

Now it's time for the .BM file which contains the subroutines and functions at the heart of the library.

( This code can be found at .\tutorial\Lesson20\CollisionLibrary.BM )

' Collision Library V1.0
' Terry Ritchie 01/02/23

' CollisionLibrary.BM
' To be placed at the bottom of the main source code after the subroutines and functions
'
'------------------------------------------------------------------------------------------------------------
FUNCTION CL_LineCollide (L1 AS CL_LINE, L2 AS CL_LINE)
    '--------------------------------------------------------------------------------------------------------
    '- Tests for 2 lines intersecting (in collision)                                                        -
    '- Returns: 0 if no collision, -1 if collision                                                          -
    '-       : CL_Instersect.Line.x, CL_Intersect.Line.y will contain the x,y coordinate of collision       -
    '-                                                                                                      -
    '- This function created from examples given at this discussion:                                        -
    '- https://forum.unity.com/threads/line-intersection.17384/                                             -
    '- Based on the "Faster Line Segment Intersection" code found in Graphics Gems III                      -
    '- pages 199-202 by Franklin Antonio (1992). ISBN 0-12-409672-7                                         -
    '--------------------------------------------------------------------------------------------------------
   SHARED CL_Intersect AS CL_INTERSECT ' intersection of collision
   DIM Ax AS INTEGER
   DIM Bx AS INTEGER
   DIM Cx AS INTEGER
   DIM Ay AS INTEGER
   DIM By AS INTEGER
   DIM Cy AS INTEGER
   DIM d AS INTEGER
   DIM e AS INTEGER
   DIM f AS INTEGER
   DIM x1lo AS INTEGER
   DIM x1hi AS INTEGER
   DIM y1lo AS INTEGER
   DIM y1hi AS INTEGER

    CL_Intersect.Line.x = 0 '                                                  assume no collision
    CL_Intersect.Line.y = 0
    CL_LineCollide = 0
    Ax = L1.Finish.x - L1.Start.x '                                            X bounding box test
    Bx = L2.Start.x - L2.Finish.x
   IF Ax < 0 THEN
        x1lo = L1.Finish.x
        x1hi = L1.Start.x
   ELSE
        x1hi = L1.Finish.x
        x1lo = L1.Start.x
   END IF
   IF Bx > 0 THEN
       IF x1hi < L2.Finish.x OR L2.Start.x < x1lo THEN EXIT FUNCTION
   ELSE
       IF x1hi < L2.Start.x OR L2.Finish.x < x1lo THEN EXIT FUNCTION
   END IF
    Ay = L1.Finish.y - L1.Start.y '                                            Y bounding box test
    By = L2.Start.y - L2.Finish.y

   IF Ay < 0 THEN
        y1lo = L1.Finish.y
        y1hi = L1.Start.y
   ELSE
        y1hi = L1.Finish.y
        y1lo = L1.Start.y
   END IF
   IF By > 0 THEN
       IF y1hi < L2.Finish.y OR L2.Start.y < y1lo THEN EXIT FUNCTION
   ELSE
       IF y1hi < L2.Start.y OR L2.Finish.y < y1lo THEN EXIT FUNCTION
   END IF
    Cx = L1.Start.x - L2.Start.x
    Cy = L1.Start.y - L2.Start.y
    d = By * Cx - Bx * Cy '                                                    alpha numerator
    f = Ay * Bx - Ax * By '                                                    both denominators
   IF f = 0 THEN EXIT FUNCTION '                                               parallel line check
   IF f > 0 THEN '                                                             alpha tests
       IF d < 0 OR d > f THEN EXIT FUNCTION
   ELSE
       IF d > 0 OR d < f THEN EXIT FUNCTION
   END IF
    e = Ax * Cy - Ay * Cx '                                                    beta numerator
   IF f > 0 THEN '                                                             beta tests
       IF e < 0 OR e > f THEN EXIT FUNCTION
   ELSE
       IF e > 0 OR e < f THEN EXIT FUNCTION
   END IF
    CL_Intersect.Line.x = L1.Start.x + d * Ax / f '                            calculate intersect coordinate
    CL_Intersect.Line.y = L1.Start.y + d * Ay / f
    CL_LineCollide = -1 '                                                      report that collision occurred

END FUNCTION

'------------------------------------------------------------------------------------------------------------
FUNCTION CL_RectCollide (R1 AS CL_RECTANGLE, R2 AS CL_RECTANGLE)
    '--------------------------------------------------------------------------------------------------------
    '- Checks for the collision between two rectangular areas.                                              -
    '- Returns: 0 if no collision, -1 if collision                                                          -
    '-        : CL_Intersect.Rectangle.TopLeft.x, CL_Intersect.Rectangle.TopLeft.y,                         -
    '-        : CL_Intersect.Rectangle.BottomRight.x, CL_Intersect.Rectangle.BottomRight.y will contain the -
    '-        : x,y coordinates of the collision overlapping area                                           -
    '-        : CL_Intersect.Rectangle.Width, CL_Intersect.Rectangle.Height will contain the width and      -
    '-        : height of the collision overlapping area                                                    -
    '--------------------------------------------------------------------------------------------------------
   SHARED CL_Intersect AS CL_INTERSECT ' intersection of collision

    R1.BottomRight.x = R1.TopLeft.x + R1.Width - 1
    R1.BottomRight.y = R1.TopLeft.y + R1.Height - 1
    R2.BottomRight.x = R2.TopLeft.x + R2.Width - 1
    R2.BottomRight.y = R2.TopLeft.y + R2.Height - 1
    RectCollide = 0
   IF R1.BottomRight.x >= R2.TopLeft.x THEN '                  check for overlapping coordinates
       IF R1.TopLeft.x <= R2.BottomRight.x THEN
           IF R1.BottomRight.y >= R2.TopLeft.y THEN
               IF R1.TopLeft.y <= R2.BottomRight.y THEN '      all checks passed, must be a collision

                    ' calculate collision area and return as a rectangle

                   IF R1.TopLeft.x > R2.TopLeft.x THEN
                        CL_Intersect.Rectangle.TopLeft.x = R1.TopLeft.x
                   ELSE
                        CL_Intersect.Rectangle.TopLeft.x = R2.TopLeft.x
                   END IF
                   IF R1.TopLeft.y > R2.TopLeft.y THEN
                        CL_Intersect.Rectangle.TopLeft.y = R1.TopLeft.y
                   ELSE
                        CL_Intersect.Rectangle.TopLeft.y = R2.TopLeft.y
                   END IF
                   IF R1.BottomRight.x < R2.BottomRight.x THEN
                        CL_Intersect.Rectangle.BottomRight.x = R1.BottomRight.x
                   ELSE
                        CL_Intersect.Rectangle.BottomRight.x = R2.BottomRight.x
                   END IF
                   IF R1.BottomRight.y < R2.BottomRight.y THEN
                        CL_Intersect.Rectangle.BottomRight.y = R1.BottomRight.y
                   ELSE
                        CL_Intersect.Rectangle.BottomRight.y = R2.BottomRight.y
                   END IF
                    CL_Intersect.Rectangle.Width = _
                        CL_Intersect.Rectangle.BottomRight.x - CL_Intersect.Rectangle.TopLeft.x
                    CL_Intersect.Rectangle.Height = _
                        CL_Intersect.Rectangle.BottomRight.y - CL_Intersect.Rectangle.TopLeft.y
                    CL_RectCollide = -1
               END IF
           END IF
       END IF
   END IF

END FUNCTION

'------------------------------------------------------------------------------------------------------------
FUNCTION CL_CircCollide (C1 AS CL_CIRCLE, C2 AS CL_CIRCLE)
    '--------------------------------------------------------------------------------------------------------
    '- Checks for the collision between two circular areas.                                                 -
    '- Returns: 0 if no collision, -1 if collision                                                          -
    '-        : CL_Intersect.Circle.x, CL_Intersect.Circle.y will contain the x,y coordinate of the         -
    '-        : collision                                                                                   -
    '- Function optimized with removal of SQR() by Brandon Ritchie                                          -
    '--------------------------------------------------------------------------------------------------------
   SHARED CL_Intersect AS CL_INTERSECT ' intersection of collision
   DIM d AS INTEGER '                    distance between circle centers

    CL_CircCollide = 0
    CL_Intersect.Circle.x = 0
    CL_Intersect.Circle.y = 0
   IF C1.Center.x + C1.Radius + C2.Radius > C2.Center.x THEN
       IF C1.Center.x < C2.Center.x + C1.Radius + C2.Radius THEN
           IF C1.Center.y + C1.Radius + C2.Radius > C2.Center.y THEN
               IF C1.Center.y < C2.Center.y + C1.Radius + C2.Radius THEN
                    d = (C1.Center.x - C2.Center.x) * (C1.Center.x - C2.Center.x) + _
                        (C1.Center.y - C2.Center.y) * (C1.Center.y - C2.Center.y)
                   IF d <= (C1.Radius + C2.Radius) * (C1.Radius + C2.Radius) THEN
                        CL_Intersect.Circle.x = ((C1.Center.x * C2.Radius) + (C2.Center.x * _
                            C1.Radius)) / (C1.Radius + C2.Radius)
                        CL_Intersect.Circle.y = ((C1.Center.y * C2.Radius) + (C2.Center.y * _
                            C1.Radius)) / (C1.Radius + C2.Radius)
                        CL_CircCollide = -1
                   END IF
               END IF
           END IF
       END IF
   END IF

END FUNCTION

'------------------------------------------------------------------------------------------------------------
SUB CL_MakeMask (Obj AS CL_SPRITE)
    '--------------------------------------------------------------------------------------------------------
    '- Creates a negative mask of image for pixel collision detection.                                      -
    '-                                                                                                      -
    '- Obj - object containing an image and mask image holder                                               -
    '-                                                                                                      -
    '- This subroutine for internal library use only                                                        -
    '--------------------------------------------------------------------------------------------------------

   DIM x AS INTEGER '         image column and row counters
   DIM y AS INTEGER
   DIM cc AS _UNSIGNED LONG ' clear transparent color
   DIM Osource AS LONG '      original source image
   DIM Odest AS LONG '        original destination image

    Obj.Mask = _NEWIMAGE(_WIDTH(Obj.Image), _HEIGHT(Obj.Image), 32) ' create mask image
    Osource = _SOURCE '                                               save source image
    Odest = _DEST '                                                   save destination image
   _SOURCE Obj.Image '                                                make object image the source
   _DEST Obj.Mask '                                                   make object mask image the destination
    cc = _RGB32(255, 0, 255) '                                        set the color to be used as transparent
   FOR y = 0 TO _HEIGHT(Obj.Image) - 1 '                              cycle through image rows
       FOR x = 0 TO _WIDTH(Obj.Image) - 1 '                           cycle through image columns
           IF POINT(x, y) = cc THEN '                                 is image pixel the transparent color?
               PSET (x, y), _RGB32(0, 0, 0, 255) '                    yes, set mask image to solid black
           ELSE '                                                     no, pixel is part of actual image
               PSET (x, y), cc '                                      set mask image to transparent color
           END IF
       NEXT x
   NEXT y
   _DEST Odest '                                                      restore original destination image
   _SOURCE Osource '                                                  restore original source image
   _SETALPHA 0, cc, Obj.Image '                                       set image transparent color
   _SETALPHA 0, cc, Obj.Mask '                                        set mask transparent color

END SUB

'------------------------------------------------------------------------------------------------------------
FUNCTION CL_PixelCollide (Obj1 AS CL_SPRITE, Obj2 AS CL_SPRITE)
    '--------------------------------------------------------------------------------------------------------
    '- Checks for pixel perfect collision between two rectangular areas.                                    -
    '- Returns: 0 if no collision, -1 if in collision                                                       -
    '-        : CL_Intersect.Pixel.x, cl_intersect.Pixel.y will contain the x,y coordinate of the collision -
    '--------------------------------------------------------------------------------------------------------

   SHARED CL_Intersect AS CL_INTERSECT ' intersection of collision
   DIM x1 AS INTEGER '                   upper left x,y coordinate of rectangular collision area
   DIM y1 AS INTEGER
   DIM x2 AS INTEGER '                   lower right x,y coordinate of rectangular collision area
   DIM y2 AS INTEGER
   DIM Test AS LONG '                    overlap image to test for collision
   DIM Hit AS INTEGER '                  -1 (TRUE) if a collision occurs, 0 (FALSE) otherwise
   DIM Osource AS LONG '                 original source image handle
   DIM p AS _UNSIGNED LONG '             pixel color being tested in overlap image

   IF Obj1.Mask = 0 THEN CL_MakeMask Obj1 '                         create image masks if not present
   IF Obj2.Mask = 0 THEN CL_MakeMask Obj2
    Obj1.BottomRight.x = Obj1.TopLeft.x + _WIDTH(Obj1.Image) - 1 '  calculate lower right x,y coordinates
    Obj1.BottomRight.y = Obj1.TopLeft.y + _HEIGHT(Obj1.Image) - 1 ' of both objects
    Obj2.BottomRight.x = Obj2.TopLeft.x + _WIDTH(Obj2.Image) - 1
    Obj2.BottomRight.y = Obj2.TopLeft.y + _HEIGHT(Obj2.Image) - 1
    Hit = 0 '                                                       assume no collision
    CL_Intersect.Pixel.x = 0
    CL_Intersect.Pixel.y = 0

    '** perform rectangular collision check (proximity)

   IF Obj1.BottomRight.x >= Obj2.TopLeft.x THEN '             rect 1 lower right X >= rect 2 upper left  X ?
       IF Obj1.TopLeft.x <= Obj2.BottomRight.x THEN '         rect 1 upper left  X <= rect 2 lower right X ?
           IF Obj1.BottomRight.y >= Obj2.TopLeft.y THEN '     rect 1 lower right Y >= rect 2 upper left  Y ?
               IF Obj1.TopLeft.y <= Obj2.BottomRight.y THEN ' rect 1 upper left  Y <= rect 2 lower right Y ?

                    '** rectangular collision detected, perform pixel perfect collision check

                   IF Obj2.TopLeft.x <= Obj1.TopLeft.x THEN ' calculate overlapping
                        x1% = Obj1.TopLeft.x
                   ELSE
                        x1% = Obj2.TopLeft.x
                   END IF
                   IF Obj2.TopLeft.y <= Obj1.TopLeft.y THEN ' rectangle coordinates
                        y1 = Obj1.TopLeft.y
                   ELSE
                        y1 = Obj2.TopLeft.y
                   END IF
                   IF Obj2.BottomRight.x <= Obj1.BottomRight.x THEN
                        x2 = Obj2.BottomRight.x
                   ELSE
                        x2 = Obj1.BottomRight.x
                   END IF
                   IF Obj2.BottomRight.y <= Obj1.BottomRight.y THEN
                        y2 = Obj2.BottomRight.y
                   ELSE
                        y2 = Obj1.BottomRight.y
                   END IF
                    Test = _NEWIMAGE(x2 - x1 + 1, y2 - y1 + 1, 32) '                          make overlap image
                   _PUTIMAGE (-(x1 - Obj1.TopLeft.x), -(y1 - Obj1.TopLeft.y)), Obj1.Image, Test ' image 1
                   _PUTIMAGE (-(x1 - Obj2.TopLeft.x), -(y1 - Obj2.TopLeft.y)), Obj2.Mask, Test '  image mask 2

                    '** enable the line below to see a visual representation of mask on image
                    '_PUTIMAGE (x1%, y1%), Test

                    x2 = x1
                    y2 = y1

                    y1 = 0 '                                     reset row counter
                    Osource = _SOURCE '                          record current source image
                   _SOURCE Test '                                make test image the source
                   DO '                                          begin row (y) loop
                        x1 = 0 '                                 reset column counter
                       DO '                                      begin column (x) loop
                            p = POINT(x1, y1) '                  get color at current coordinate

                            '** if a color from object 1 found then a collision has occurred

                           IF p <> _RGB32(0, 0, 0, 255) AND p <> _RGB32(0, 0, 0, 0) THEN
                                Hit = -1
                                CL_Intersect.Pixel.x = x1 + x2 ' return collision coordinates
                                CL_Intersect.Pixel.y = y1 + y2
                           END IF
                            x1 = x1 + 1 '                        increment to next column
                       LOOP UNTIL x1 = _WIDTH(Test) OR Hit '     leave when column checked or collision
                        y1 = y1 + 1 '                            increment to next row
                   LOOP UNTIL y1 = _HEIGHT(Test) OR Hit '        leave when all rows checked or collision
                   _SOURCE Osource '                             restore original destination
                   _FREEIMAGE Test '                             test image no longer needed (free RAM)
               END IF
           END IF
       END IF
   END IF
    CL_PixelCollide = Hit '                                      return result of collision check

END FUNCTION

Now a program is needed to make use of the collision library and test its features.

NOTE: You'll also need the files greenoval.PNG and redoval.PNG to execute this code. Both of these files are also included in your .\tutorial\Lesson20 directory.

( This code can be found at .\tutorial\Lesson20\CollisionLibraryDemo.bas )

' Collision Library V1.0 Demo
' Terry Ritchie 01/02/23
' quickbasic64@gmail.com

' See CollisionLibrary.DOC for documentation on the use of the collision library.
'
'$INCLUDE:'CollisionLibrary.BI'

CONST GREEN = _RGB32(0, 255, 0) '     color green
CONST RED = _RGB32(255, 0, 0) '       color red
CONST YELLOW = _RGB32(255, 255, 0) '  color yellow
DIM KeyPress AS STRING '              key user pressed
DIM Selection AS INTEGER '            selection user chose from menu (1 to 5)
DIM Greenbox AS CL_RECTANGLE '        green rectangle
DIM RedBox AS CL_RECTANGLE '          red rectangle
DIM GreenCircle AS CL_CIRCLE '        green circle
DIM RedCircle AS CL_CIRCLE '          red circle
DIM GreenLine AS CL_LINE '            green line
DIM RedLine AS CL_LINE '              red line
DIM GreenOval AS CL_SPRITE '          green oval image
DIM RedOval AS CL_SPRITE '            red oval image
DIM ObjectColor AS _UNSIGNED LONG '   color of colliding object
DIM Collision AS INTEGER '            true (-1) if objects in collision
DIM Plot(359) AS CL_POINT '           x,y points for rotating line
DIM c AS SINGLE '                     0 to 2*PI radian counter
DIM c1 AS INTEGER '                   plot location
DIM c2 AS INTEGER '                   plot location

'** Line collision demo setup **

FOR c1 = 0 TO 359 '                                 cycle through 360 radian points
    Plot(c1).x = 319 + 100 * COS(c) '               calculate x,y point on a circle with 100 radius
    Plot(c1).y = 239 + 100 * -SIN(c)
    c = c + .0174533 '                              increase 1/360th (2 * PI / 360)
NEXT c1
c1 = 0 '                                            starting x,y point of rotating line
c2 = 179 '                                          ending x,y point of rotating line

'** Rectangular collision demo setup **

Greenbox.TopLeft.x = 294 '                          set variables for rectangular collision demo
Greenbox.TopLeft.y = 214
Greenbox.Width = 50
Greenbox.Height = 50
RedBox.Width = 25
RedBox.Height = 25

'** Circular collision demo setup **

GreenCircle.Center.x = 319 '                        set variables for circular collision demo
GreenCircle.Center.y = 239
GreenCircle.Radius = 50
RedCircle.Radius = 25

'** Pixel perfect collision demo setup **

RedOval.Image = _LOADIMAGE("redoval.png", 32) '     set variables for pixel perfect collision demo
GreenOval.Image = _LOADIMAGE("greenoval.png", 32)
GreenOval.TopLeft.x = 294
GreenOval.TopLeft.y = 165

'** Begin main program **

SCREEN _NEWIMAGE(640, 480, 32)
_MOUSEHIDE
DO
   CLS
   LOCATE 2, 29: PRINT "COLLISION LIBRARY DEMO" '                          display menu
   LOCATE 4, 26: PRINT "Make Your Selection (1 to 5)"
   LOCATE 7, 27: PRINT "1. Rectangle Collision"
   LOCATE 9, 27: PRINT "2. Circle Collision"
   LOCATE 11, 27: PRINT "3. Line Intersect Collision"
   LOCATE 13, 27: PRINT "4. Pixel Perfect Collision"
   LOCATE 15, 27: PRINT "5. Exit back to Windows"
   DO '                                                                    wait for key press
       _LIMIT 30
        KeyPress = INKEY$
   LOOP UNTIL KeyPress <> ""
    Selection = VAL(KeyPress)
   IF Selection > 0 AND Selection < 5 THEN '                               continue if valid key press
       DO
           _LIMIT 30
           CLS
           LOCATE 28, 21: PRINT "Use mouse to move object, ESC to exit." ' print instructions
           WHILE _MOUSEINPUT: WEND
           SELECT CASE Selection
               CASE 1 '                                                    user chose rectangular collision demo
                    RedBox.TopLeft.x = _MOUSEX
                    RedBox.TopLeft.y = _MOUSEY
                    Collision = CL_RectCollide(Greenbox, RedBox)
                   LINE (Greenbox.TopLeft.x, Greenbox.TopLeft.y)-_
                    (Greenbox.BottomRight.x, Greenbox.BottomRight.y), ObjectColor, BF
                   LINE (RedBox.TopLeft.x, RedBox.TopLeft.y)-(RedBox.BottomRight.x, RedBox.BottomRight.y), RED, BF
                   IF Collision THEN
                       LINE (CL_Intersect.Rectangle.TopLeft.x, CL_Intersect.Rectangle.TopLeft.y)-_
                        (CL_Intersect.Rectangle.BottomRight.x, CL_Intersect.Rectangle.BottomRight.y), GREEN, BF
                   ELSE
                        ObjectColor = GREEN
                   END IF
               CASE 2 '                                                    user chose circular collision demo
                    RedCircle.Center.x = _MOUSEX
                    RedCircle.Center.y = _MOUSEY
                   CIRCLE (GreenCircle.Center.x, GreenCircle.Center.y), GreenCircle.Radius, ObjectColor
                   PAINT (GreenCircle.Center.x, GreenCircle.Center.y), ObjectColor, ObjectColor
                   CIRCLE (RedCircle.Center.x, RedCircle.Center.y), RedCircle.Radius, RED
                   PAINT (RedCircle.Center.x, RedCircle.Center.y), RED, RED
                    Collision = CL_CircCollide(RedCircle, GreenCircle)
                   IF Collision THEN
                       CIRCLE (CL_Intersect.Circle.x, CL_Intersect.Circle.y), 5, GREEN
                       PAINT (CL_Intersect.Circle.x, CL_Intersect.Circle.y), GREEN, GREEN
                   ELSE
                        ObjectColor = GREEN
                   END IF
               CASE 3 '                                                 user chose line intersection collision demo
                   LINE (GreenLine.Start.x, GreenLine.Start.y)-(GreenLine.Finish.x, GreenLine.Finish.y), ObjectColor
                   LINE (RedLine.Start.x, RedLine.Start.y)-(RedLine.Finish.x, RedLine.Finish.y), RED
                    RedLine.Start.x = _MOUSEX - 50
                    RedLine.Finish.x = RedLine.Start.x + 100
                    RedLine.Start.y = _MOUSEY
                    RedLine.Finish.y = RedLine.Start.y
                    GreenLine.Start = Plot(c1)
                    GreenLine.Finish = Plot(c2)
                    Collision = CL_LineCollide(RedLine, GreenLine)
                   IF Collision THEN
                       CIRCLE (CL_Intersect.Line.x, CL_Intersect.Line.y), 4, GREEN
                       PAINT (CL_Intersect.Line.x, CL_Intersect.Line.y), GREEN, GREEN
                   ELSE
                        ObjectColor = GREEN
                   END IF
                    c1 = c1 + 1
                   IF c1 = 360 THEN c1 = 0
                    c2 = c2 + 1
                   IF c2 = 360 THEN c2 = 0
               CASE 4 '                                                    user chose pixel perfect collision demo
                   _PUTIMAGE (GreenOval.TopLeft.x, GreenOval.TopLeft.y), GreenOval.Image
                   _PUTIMAGE (RedOval.TopLeft.x, RedOval.TopLeft.y), RedOval.Image
                    RedOval.TopLeft.x = _MOUSEX
                    RedOval.TopLeft.y = _MOUSEY
                    Collision = CL_PixelCollide(GreenOval, RedOval)
                   IF Collision THEN
                       CIRCLE (CL_Intersect.Pixel.x, CL_Intersect.Pixel.y), 4, YELLOW
                       PAINT (CL_Intersect.Pixel.x, CL_Intersect.Pixel.y), YELLOW, YELLOW
                   END IF
           END SELECT
           IF Collision THEN
               LOCATE 2, 36: PRINT "COLLISION!" '                          inform user of collision if occurring
                ObjectColor = YELLOW
           END IF
           _DISPLAY
       LOOP UNTIL _KEYDOWN(27) '                                           leave demo when ESC key pressed
       _AUTODISPLAY
   END IF
LOOP UNTIL Selection = 5 '                                                 leave menu when 5 selected
_FREEIMAGE RedOval.Image '                                                 RAM cleanup
_FREEIMAGE RedOval.Mask
_FREEIMAGE GreenOval.Image
_FREEIMAGE GreenOval.Mask
SYSTEM '                                                                   return to operating system

'$INCLUDE:'CollisionLibrary.BM'

Finally, no library is complete without documentation explaining how to utilize the library. For personal libraries I create my library documentation within the IDE using REM statements. This way I can simply copy the documentation directly into my main code body at the bottom for easy referral. When I'm finished writing my code I simply delete the documentation lines.

( This code can be found at .\tutorial\Lesson20\CollisionLibrary.DOC )

'******************  Collision Library V1.0
' * DOCUMENTATION *  Terry Ritchie 01/02/23
'******************  quickbasic64@gmail.com

' CollisionLibrary.BI and CollisionLibrary.BM is a set of files to include in your source code that adds
' collision detection routines to your program. Open and execute the included CollisionLibraryDemo.BAS
' to see the concepts outlined in this documentation in action.
'
' Four collision detection routines are included in this library:
'
' ********************************************************
' * 1. Rectangle collision between two rectangular areas *
' ********************************************************
'
'    collision% = CL_RectCollide(Rectangle1, Rectangle2)
'
'    collision% will contain 0 (zero) if the two supplied rectangular areas are not in collision or -1
'    if the areas are in collision (0 for false and -1 for true).
'
'    The rectangular areas are defined using the CL_RECTANGLE type definition:
'
'    DIM Rectangle1 AS CL_RECTANGLE ' rectangular areas
'    DIM Rectangle2 AS CL_RECTANGLE
'
'    The CL_RECTANGLE type definition contains the following subvariables that can be set:
'
'       .TopLeft.x     (integer) - the top left X coordinate of the rectangular area      *
'       .TopLeft.y     (integer) - the top left Y coordinate pf the rectangular area      *
'       .BottomRight.x (integer) - the bottom right X coordinate of the rectangular area
'       .BottomRight.y (integer) - the bottom right Y coordinate of the rectangular area
'       .Width         (integer) - the width of the rectangular area                      *
'       .Height        (integer) - the height of the rectangular area                     *
'
'       * = these subvariables must be set for the CL_RectCollide() function to return useful results.
'
'                   RecTangle1.TopLeft.x,
'                   Rectangle1.TopLeft.y
'                            +-------------------------------------------------+
'                            |              ^                                  |
'                            |<-------------|----- Rectangle1.Width ---------->|
'                            |              |                                  |
'                            |              |                                  |
'                            |     Rectangle1.Height                           |
'                            |              |                                  |
'                            |              |                                  |
'                            |              V                                  |
'                            +-------------------------------------------------+
'                                                                  Rectangle1.BottomRight.x,
'                                                                  Rectangle1.BottomRight.y
'
'    When the CL_RectCollide() function is called .BottomRight.x and .BottomRight.y are automatically
'    calculated from the supplied .Topleft.x, .TopLeft.y, .Width, and .Height values supplied.
'
'    If a collision is detected the variable CL_Intersect will contain the overlapping coordinates of the
'    collision:
'
'       CL_Intersect.Rectangle.TopLeft.x     (integer) - the top left X coordinate of the overlapping area
'       CL_Intersect.Rectangle.TopLeft.y     (integer) - the top left Y coordinate of the overlapping area
'       CL_Intersect.Rectangle.BottomRight.x (integer) - the bottom right X coordinate of the overlapping area
'       CL_Intersect.Rectangle.BottomRight.y (integer) - the bottom right Y coordinate of the overlapping area
'       CL_Intersect.Rectangle.Width         (integer) - the width of the overlapping area
'       CL_Intersect.Rectangle.Height        (integer) - the height of the overlapping area
'
'       +----------------------------------------------------------------------------+
'       | Rectangle1                                                                 |
'       |                                                                            |  * = overlapping area
'       |   CL_Intersect.Rectangle.TopLeft.x,                                        |
'       |   CL_Intersect.Rectangle.TopLeft.y                                         |
'       |                  +*********************************************************+----------------------+
'       |                  *       ^                                                 *                      |
'       |                  *<------|---- CL_Intersect.Rectangle.Width -------------->*                      |
'       |                  *       |                                                 *                      |
'       |                  *   CL_Intersect.Rectangle.Height                         *                      |
'       |                  *       |                                                 *                      |
'       |                  *       V                                                 *                      |
'       +------------------+*********************************************************+                      |
'                          |                                       CL_Intersect.Rectangle.BottomRight.x,    |
'                          |                                       CL_Intersect.Rectangle.BottomRight.y     |
'                          |                                                                                |
'                          |                                                                     Rectangle2 |
'                          +--------------------------------------------------------------------------------+
'
' **************************************************
' * 2. Circle collision between two circular areas *
' **************************************************
'
'    collision% = CL_CircCollide(Circle1, Circle2)
'
'    collision% will contain 0 (zero) if the two supplied circular areas are not in collision or -1
'    if the areas are in collision (0 for false and -1 for true).
'
'    The circular areas are defined using the CL_CIRCLE type definition:
'
'    DIM Circle1 as CL_CIRCLE ' circular areas
'    DIM Circle2 as CL_CIRCLE
'
'    The CL_CIRCLE type definition contains the following subvariables that can be set:
'
'        .Center.x (integer) - the center X coordinate of the circular area
'        .Center.y (integer) - the center Y coordinate of the circular area
'        .Radius   (integer) - the radius from the center x,y coordinate of the circular area
'
'
'             Circle1.Radius
'              __---|---__
'           _--     |     --_
'          /        |        \
'         |         |         |
'        |          V          |
'        |          +          |
'        |  Circle1.Center.x,  |
'         | Circle1.Center.y  |
'          \_               _/
'            --__       __--
'                -------
'
'    If a collision is detected the variable CL_Intersect will contain the overlapping coordinate of the
'    collision:
'
'       .Circle.x (integer) - the X coordinate of the circle collision
'       .Circle.y (integer) - the Y coordinate of the circle collision
'
'              __-------__           __-------__
'           _--           --_     _--           --_
'          /                 \   /                 \
'         |                   | |                   |
'        |                     |                     |
'        |CL_Intersect.Circle.x+CL_Intersect.Circle.y|
'        |                     |                     |
'         |                   | |                   |
'          \_               _/   \_               _/
'            --__       __--       --__       __--
'                -------               -------
'
' ***********************************************
' * 3. Line collision between two line segments *
' ***********************************************
'
'    collision% = CL_LineCollide(Line1, Line2)
'
'    collision% will contain 0 (zero) if the two supplied line segments are not in collision or -1
'    if the line segments are in collision (0 for false and -1 for true).
'
'    The line segments are defined using the CL_LINE type definition:
'
'    DIM Line1 AS CL_LINE ' line segments
'    DIM Line2 AS CL_LINE
'
'    The CL_LINE type definition contains the following subvariables that can be set:
'
'        .Start.x  (integer) - the x coordinate of the start of the line segment
'        .Start.y  (integer) - the y coordinate of the start of the line segment
'        .Finish.x (integer) - the x coordinate of the end of the line segment
'        .Finish.y (integer) - the y coordinate of the end of the line segment
'
'        Line1.Start.x,                  Line1.Finish.x,
'        Line1.Start.y                   Line1.Finish.y
'             +--------------------------------+
'
'    If a collision is detected the variable CL_Intersect will contain the line intersection coordinate of the
'    collision:
'
'        .Circle.x (integer) ' the x coordinate of line intersection
'        .Circle.y (integer) ' the y coordinate of line intersection
'
'                             +
'           * = intersection  |
'                             |
'                             |
'             +---------------*----------------+
'                             |CL_Intersect.Circle.x,
'                             |CL_Intersect.Circle.y
'                             |
'                             +
'
' ********************************************************
' * 4. Pixel perfect collision between two sprite images *
' ********************************************************
'
'    collision% = CL_PixelCollide(Sprite1, Sprite2)
'
'    collision% will contain 0 (zero) if the two supplied sprites are not in collision or -1
'    if the sprites are in collision (0 for false, -1 for true).
'
'    The sprites are defined using the CL_SPRITE type definition:
'
'    DIM Sprite1 AS CL_SPRITE ' sprite images
'    DIM Sprite2 AS CL_SPRITE
'
'    The CL_SPRITE type definition contains the following subvariables that can be set:
'
'        .Image    (long integer) - the handle to an image file loaded with _LOADIMAGE() *
'        .Mask     (long integer) - the negative mask of the image (see below)
'        .TopLeft.x     (integer) - top left X coordinate of sprite image                *
'        .TopLeft.y     (integer) - top left Y coordinate of sprite image                *
'        .BottomRight.x (integer) - bottom right X coordinate of sprite image
'        .BottomRight.y (integer) - bottom right Y coordinate of sprite image
'
'       * = these subvariables must be set for the CL_PixelCollide() function to return useful results.
'
'    Sprite1.Image = _LOADIMAGE("Sprite1.png", 32)
'    Sprite2.Image = _LOADIMAGE("Sprite2.png", 32)
'
'    When the CL_SpriteCollide() function is called .BottomRight.x and .BottomRight.y are automatically
'    calculated from the supplied .Topleft.x, .TopLeft.y, and the _WIDTH() and _HEIGHT() of the image.
'    Also, the .Mask negative image is automatically created the first time CL_SpriteCollide() is called.
'    You'll need to free this image manually along with the original image before your source code exits.
'
'    _FREEIMAGE Sprite1.Image
'    _FREEIMAGE Sprite1.Mask '  auto generated by CL_SpriteCollide()
'    _FREEIMAGE Sprite2.Image
'    _FREEIMAGE Sprite2.Mask '  auto generated by CL_SpriteCollide()
'

This library follows the .BI and .BM file requirements and all of the suggested rules when writing a set of library functions and procedures:

A good example of the use of libraries can be found in the Minesweeper game I wrote. It utilizes three libraries that I wrote and brought together to create the game; A button library to create Windows style clickable buttons on screen, a menu library to create Windows style drop down menus, and graphics Line Input library to create interactive Line Input data fields.

Windows API Calls

QB64 also allows libraries within Windows (.DLL files) to be used as subroutines and functions. This functionality is beyond the scope of this tutorial but you need to be aware this exists. This is an advanced topic but opens up a whole new world of possibilities to the QB64 programmer. The following code uses a subroutine within the Windows kernel to report the physically installed amount of RAM within your computer.

NOTE: This code was written by Spriggsy and shared on the QB64 forum here.

( This code can be found at .\tutorial\Lesson20\PhysicalRAM.bas)

OPTION EXPLICIT
$NOPREFIX
$CONSOLE:ONLY

CONST KILOBYTE = 1024
CONST MEGABYTE = 1024 ^ 2
CONST GIGABYTE = 1024 ^ 3
CONST TERABYTE = 1024 ^ 4

DECLARE DYNAMIC LIBRARY "Kernel32"
   SUB GetPhysicallyInstalledSystemMemory (BYVAL TotalMemoryInKilobytes AS OFFSET)
END DECLARE

DIM AS UNSIGNED INTEGER64 memory
GetPhysicallyInstalledSystemMemory OFFSET(memory)

memory = memory * KILOBYTE

SELECT CASE memory
   CASE IS < KILOBYTE
       PRINT USING "   ####  B"; memory
   CASE IS < (MEGABYTE) AND memory >= KILOBYTE
       PRINT USING "####.## KB"; (memory / KILOBYTE)
   CASE IS < (GIGABYTE) AND memory >= (MEGABYTE)
       PRINT USING "####.## MB"; (memory / (MEGABYTE))
   CASE IS < (TERABYTE) AND memory >= (GIGABYTE)
       PRINT USING "####.## GB"; (memory / (GIGABYTE))
END SELECT

If you wish to explore using Windows API calls the best place to start investigating and asking questions is the QB64 Phoenix Edition forum.