Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
The Van Halenizer 2.5 update
#1
This was a side project that got out of hand. Generates striped patterns like Edward Van Halen's famous guitar and animations & lets you save the images to use as desktop wallpaper or whatever. Enjoy.

v2.5 update: Added flying VH with more config options, fixed file save & it will beep when image is saved.

Download the attached file or see below for the complete code listing.



[Image: Van-Halenizer-screenshot-1.png]

[Image: vanhalen-20251108-014147.png]

[Image: Van-Halenizer-screenshot-8.png]

[Image: vanhalen-20251108-014222.png]

[Image: vanhalen-20251108-014503.png]

[Image: Van-Halenizer-screenshot-10.png]

[Image: Van-Halenizer-screenshot-7.png]

[Image: Van-Halenizer-screenshot-6.png]

[Image: Van-Halenizer-screenshot-5.png]


Main program "VanHalenizer2-05.bas":

Code: (Select All)
' Van Halenizer v2.05 by Softintheheadware, October 2025.
' This silly little program simulates the striped pattern
' on Edward Van Halen's famous "Frankenstrat" guitars.

' -----------------------------------------------------------------------------
' FEATURES
' -----------------------------------------------------------------------------
' * Draws the Frankenstrat to look how he painted it,
'   simulates "taping" on the original black/white design
'   which is revealed when the tape is removed.
' * Draws basic stripes for the '78 guitar and Bumblebee colors.
' * Simulates the different sized rolls of tape Ed used for
'   different stripes (1/8", 1/4", 1/2", 3/4").
' * Option to save image to JPEG and PNG format.
' * Option for trippy animation.

' -----------------------------------------------------------------------------
' CHANGE LOG
' -----------------------------------------------------------------------------
' Version   Date         Who                 What
' -------   ----------   -----------------   ----------------------------------
' v1.0      10/22/2025   Softintheheadware   Initial version
' v1.01     10/23/2025   Softintheheadware   Minor update, tweaked save image filename
' v2.0      10/25/2025   Softintheheadware   Added animation mode + 5 new color schemes
' v2.01     10/26/2025   Softintheheadware   Auto-detects screen resolution to size window
' v2.02     10/27/2025   Softintheheadware   Always fullscreen at maximum resolution.
'                                            Added auto-cycling options for repaint + color schemes.
'                                            Saves settings to config (.cfg) file + loads at startup.
' v2.03     10/28/2025   Softintheheadware   Added config options for screen resolution, delay MS,
'                                            filename template, popup image filename.
'                                            Updated config value names + variable names to match.
'                                            Tweaked to fit menu/title on 640x480 and 800x600 resolutions.
' v2.05     11/01/2025   Softintheheadware   Added flying VH! Fixed broken file save. Beeps when saved.

' -----------------------------------------------------------------------------
' CREDITS
' -----------------------------------------------------------------------------
' * VH logo based on the one by ryan.keefer@psybbs.durham.nc
' * Various coding routines by James D. Jarvis, BPlus, Luke,
'   Dustinian Camburides, and the folks at qb64phoenix.com
'   and QB64.com without whom this would have been impossible!

' -----------------------------------------------------------------------------
' TO DO
' -----------------------------------------------------------------------------
' Things to do that maybe you, the intrepid BASIC programmer,
' can possibly add in a future version:
' * Pull color schemes from config file to allow editing + adding new ones
'   without having to touch the code.
' * Option to display the Van Halen logo + band name.
' * Better simulate certain stripe patterns the '78 and Bumblebee had
'   (stripes fan out in rays or stripe with parallel pin stripe, etc.)
' * Display info (arrMoreInfo) on each guitar in a sidebar on menu screen.
' * Add options for custom colors, onscreen color picker.
' * Add options for ratio or how many of each size stripe / color.
'   (Get stripe name with function StripeName$.)
' * Add the virtual "f-holes" or "mustache" designs, the quarter under the bridge,
'   5-way switch in the middle PU cavity, etc.
'   OK we're getting carried away here!
' * Allow multiple layers of paint + tape (currently max is 2).
'   e.g., create layer 1: paint coat #1, then add stripes
'         create layer 2: Paint coat #2, then add stripes (erase)
'         overlay layer 2 over layer 1
'         create layer 3: Paint coat #3, then add stripes (erase)
'         overlay layer 3 over layer 1
'         etc.
' * Visual tool to see stripe size as scale is adjusted.
' * Other designs (Rasta guitar, etc.),
'   see https://vctrgtr.com/evh.html
'   https://www.angelfire.com/ca2/smithtone/vh_gtrs.html
'   https://vintagefloydrose.com/evh-tremolos/

' -----------------------------------------------------------------------------
' MORE INFO
' -----------------------------------------------------------------------------
' About the paint job:
' How did he do it? Edward Van Halen created his iconic "Frankenstrat"
' paint job doing something like this:
'   1. Paint it all black
'   2. Add cris-crossed tape to "mask" the black
'   3. Paint it all white
'   4. Pull off the tape to reveal the black stripes
'   5. Add cris-cross tape again
'   6. Paint it all red
'   7. Pull off the tape to reveal the black/white stripes
' -----------------------------------------------------------------------------
' About Van Halen (if you don't know):
' For more information on Edward Lodewijk Van Halen and his band,
' read "Brothers" by Alex Van Halen,"Van Halen Rising" by Greg Renoff,
' "Running With the Devil" by Noel Monk, visit the Van Halen News Desk
' at www.vhnd.com and listen to some Van Halen records to find out.
' -----------------------------------------------------------------------------
' Stripe info from various sources:
' * The exact widths of the stripes on Eddie Van Halen's Frankenstrat are not
'   definitively recorded, as they were applied with tape and applied by hand.
'   However, a common approach for replicating the look is to use a combination
'   of 1/8", 1/4", 1/2", and 3/4" wide tape, with the final widths up to the
'   individual replicating it.
' * Frankenstrat: The original guitar's stripes were applied by hand using tape,
'   resulting in a slightly irregular and unique look.
' * DIY Replication: To replicate the striped effect, a combination of
'   1/8", 1/4", 1/2", and 3/4" wide tape can be used. The specific widths can
'   be adjusted to achieve a desired final look.
' * Original Material: The original stripes were created with gaffer's tape,
'   which allowed for a rough-edge look when applied.
' * Modern Alternatives: While gaffer's tape was used originally, modern options
'   like masking tape or specialized "Stripers Tape" are also viable for creating
'   the stripes.
' * 3/4 1/2 and 1/8th is all I ever used on the dozens I ever painted.
'   And never did the same striping twice.
' * You need 3/4” tape for the black and white taping.
'   For the red phase you need 1", 1/2", and 1/4"
' * Instead of obsessing about what stripes the guy put on his guitar,
'   try picking up an instrument or a paintbrush and find your own original idea
'   that some geek in 20 years will make a computer program about :-)

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

$Color:32
' 32-bit color names  = Black Blue Green Cyan Red Magenta Brown White Gray LightBlue LightGreen LightCyan LightRed LightMagenta Yellow BrightWhite

'$Dynamic

Option _Explicit

_Title "Van Halenizer v2.05"

' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' BEGIN CONSTANTS #CONST
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' -----------------------------------------------------------------------------
' HARDCODED DEFAULT VALUES
' -----------------------------------------------------------------------------
Const cScreenWidth = 0 ' 0 means _DesktopWidth
Const cScreenHeight = 0 ' 0 means _DesktopHeight
Const cEnableAnimation = _TRUE
Const cDelayMS = 0
Const cMiniMenuIsVisible = _FALSE
Const cFilenameTemplate = "VanHalen_{yyyy}{mm}{dd}_{hh}{nn}{ss}.{ext}"
Const cEnableSaveResultsPopup = _FALSE
Const cCycleEverySeconds = 20
Const cRepaintEverySeconds = 8
Const cColorSchemeIndex = 0
Const cBaseStripeCount = 25
Const cStripeVariance = 5
' -----------------------------------------------------------------------------
Const cEnableFlyingVH = _TRUE
Const cNumVectors = 7 ' 25 100
Const cVectorEveryMS = 3000 ' 100 ' 50 ' 250 ' 2000
Const cMinDX = 150 ' 50 1
Const cMaxDX = 250 ' 100 5
Const cMaxScale = 250 ' 300
Const cScaleIncrement = 2500

' -----------------------------------------------------------------------------
' COLOR SCHEME DEFINITONS
' -----------------------------------------------------------------------------
' TODO: MOVE DEFINTIONS INTO CFG
Const cFrankenstrat = 0
Const c78 = 1
Const cBumbleebee = 2
Const cBlueFrankenstrat = 3
Const cInverse78 = 4
Const cInverseBumbleebee = 5
Const cRastaGuitar1 = 6
Const cRastaGuitar2 = 7

' -----------------------------------------------------------------------------
' FOR STRIPE WIDTH
' -----------------------------------------------------------------------------
Const cStripe_1_16 = 0 ' only used for parallel pinstripes
Const cStripe_1_8 = 1
Const cStripe_1_4 = 2
Const cStripe_1_2 = 3
Const cStripe_3_4 = 4
Const cStripe_1_0 = 5 ' 1" = largest, not used vurrently

' -----------------------------------------------------------------------------
' FOR DATA TYPE IN DATA STATEMENTS
' -----------------------------------------------------------------------------
Const cDrawStart = 1 ' starting point of next shape
Const cFillPoint = 2 ' point to do a flood fill from if shape is closed and can be filled
Const cDrawLine = 3 ' line to delta x, delta y
Const cLastLine = 4 ' last point in shape
Const cEndAll = 5 ' end of data

' MAX / MIN VALUES
Const cIntegerMin = -32768
Const cIntegerMax = 32767
Const cSingleMin = -2.802597E-45
Const cSingleMax = 3.402823E+38

' -----------------------------------------------------------------------------
' _KeyDown key codes
' -----------------------------------------------------------------------------
Const cLeftKD = 19200
Const cRightKD = 19712
Const cUpKD = 18432
Const cDownKD = 20480
Const cPageUp = 18688
Const cPageDown = 20736
Const cEsc = 27
Const cEnter = 13
Const cHome = 18176
Const cEnd = 20224
Const cSpace = 32

Const cKey0 = 48
Const cKey1 = 49
Const cKey2 = 50
Const cKey3 = 51
Const cKey4 = 52
Const cKey5 = 53
Const cKey6 = 54
Const cKey7 = 55
Const cKey8 = 56
Const cKey9 = 57

Const cUpperA = 65
Const cUpperB = 66
Const cUpperC = 67
Const cUpperD = 68
Const cUpperE = 69
Const cUpperF = 70
Const cUpperG = 71
Const cUpperH = 72
Const cUpperI = 73
Const cUpperJ = 74
Const cUpperK = 75
Const cUpperL = 76
Const cUpperM = 77
Const cUpperN = 78
Const cUpperO = 79
Const cUpperP = 80
Const cUpperQ = 81
Const cUpperR = 82
Const cUpperS = 83
Const cUpperT = 84
Const cUpperU = 85
Const cUpperV = 86
Const cUpperW = 87
Const cUpperX = 88
Const cUpperY = 89
Const cUpperZ = 90

Const cLowerA = 97
Const cLowerB = 98
Const cLowerC = 99
Const cLowerD = 100
Const cLowerE = 101
Const cLowerF = 102
Const cLowerG = 103
Const cLowerH = 104
Const cLowerI = 105
Const cLowerJ = 106
Const cLowerK = 107
Const cLowerL = 108
Const cLowerM = 109
Const cLowerN = 110
Const cLowerO = 111
Const cLowerP = 112
Const cLowerQ = 113
Const cLowerR = 114
Const cLowerS = 115
Const cLowerT = 116
Const cLowerU = 117
Const cLowerV = 118
Const cLowerW = 119
Const cLowerX = 120
Const cLowerY = 121
Const cLowerZ = 122

' -----------------------------------------------------------------------------
' _Button key codes (just used to track key state)
' -----------------------------------------------------------------------------
Const KeyCode_Left = 332
Const KeyCode_Right = 334
Const KeyCode_Up = 329
Const KeyCode_Down = 337
Const KeyCode_PgUp = 330
Const KeyCode_PgDn = 338
Const KeyCode_Escape = 2
Const KeyCode_Enter = 29
Const KeyCode_Home = 328
Const KeyCode_End = 336
Const KeyCode_Spacebar = 58
Const KeyCode_0 = 12
Const KeyCode_1 = 3
Const KeyCode_2 = 4
Const KeyCode_3 = 5
Const KeyCode_4 = 6
Const KeyCode_5 = 7
Const KeyCode_6 = 8
Const KeyCode_7 = 9
Const KeyCode_8 = 10
Const KeyCode_9 = 11
Const KeyCode_A = 31
Const KeyCode_B = 49
Const KeyCode_C = 47
Const KeyCode_D = 33
Const KeyCode_E = 19
Const KeyCode_F = 34
Const KeyCode_G = 35
Const KeyCode_H = 36
Const KeyCode_I = 24
Const KeyCode_J = 37
Const KeyCode_K = 38
Const KeyCode_L = 39
Const KeyCode_M = 51
Const KeyCode_N = 50
Const KeyCode_O = 25
Const KeyCode_P = 26
Const KeyCode_Q = 17
Const KeyCode_R = 20
Const KeyCode_S = 32
Const KeyCode_T = 21
Const KeyCode_U = 23
Const KeyCode_V = 48
Const KeyCode_W = 18
Const KeyCode_X = 46
Const KeyCode_Y = 22
Const KeyCode_Z = 45
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' END CONSTANTS @CONST
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' BEGIN USER-DEFINED DATA TYPES (UDTs) #UDT
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Type StripeType
    ' stripe properties
    layer As Integer
    weight As Single

    ' TODO: individual stripe colors
    fg As _Unsigned Long
    ' TODO: or even multicolors?
    'bg as _UnsignedLong

    ' starting point location + movement
    x1 As Single
    y1 As Single
    speed1 As Single
    'direction1 As Integer ' pointer to arrDirection
    wise1 As Integer ' 1 = clockwise, -1 = counterclockwise
    mx1 As Integer
    my1 As Integer
    dx1 As Single
    dy1 As Single
    side1 As Integer

    ' ending point location + movement
    x2 As Single
    y2 As Single
    speed2 As Single
    'direction2 As Integer ' pointer to arrDirection
    wise2 As Integer ' 1 = clockwise, -1 = counterclockwise
    mx2 As Integer
    my2 As Integer
    dx2 As Single
    dy2 As Single
    side2 As Integer
End Type

Type MotionType
    dx As Single
    dy As Single
End Type

Type LayerType
    Count As Integer
    fg As _Unsigned Long
    bg As _Unsigned Long
    'image1 as long
    'image2 as long
End Type

' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' END USER-DEFINED DATA TYPES (UDTs) @UDT
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' BEGIN GLOBAL VARIABLES #GLOBAL
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

' Program name / path
Dim Shared m_ProgramPath As String: m_ProgramPath = Left$(Command$(0), _InStrRev(Command$(0), "\")) ' executable path
Dim Shared m_ProgramName As String: m_ProgramName = Mid$(Command$(0), _InStrRev(Command$(0), "\") + 1) ' executable filename
Dim Shared m_sDebugFile As String: m_sDebugFile = m_ProgramPath$ + m_ProgramName$ + ".txt"

' For tracking keyboard
ReDim Shared arrKeyState(340) As _Byte

' Used by RotoLine
Dim Shared dot&

' How many text rows+columns fit on screen in current resolution
Dim Shared CurrentWidth As Long
Dim Shared CurrentHeight As Long
Dim Shared MaxRow As Long
Dim Shared MaxCol As Long

' Variables loaded from config file
Dim Shared iScreenWidth As Long
Dim Shared iScreenHeight As Long
Dim Shared iScaleToPercent As Integer
Dim Shared bEnableAnimation As _Byte
Dim Shared iDelayMS As Long
Dim Shared bMiniMenuIsVisible As _Byte
Dim Shared sFilenameTemplate As String
Dim Shared bEnableSaveResultsPopup As _Byte
Dim Shared iCycleEverySeconds As Long
Dim Shared iRepaintEverySeconds As Long
Dim Shared iColorSchemeIndex As Integer
Dim Shared iBaseStripeCount As Integer
Dim Shared iStripeVariance As Integer
' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
Dim Shared bEnableFlyingVH As _Byte
Dim Shared iNumVectors As Integer
Dim Shared iVectorEveryMS As Long
Dim Shared iMinDX As Integer
Dim Shared iMaxDX As Integer
Dim Shared iMaxScale As Integer
Dim Shared iScaleIncrement As Long

' GENERAL COLORS
ReDim Shared m_SpectrumColorArray(-1) As _Unsigned Long ' grayscale colors

' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' END GLOBAL VARIABLES @GLOBAL
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' EXECUTION BEGINS HERE #EXE
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

' Local vars
Dim sError As String

' Init local
sError = ""

' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
' BEGIN GET COMMAND LINE ARGS #ARGS
' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
' Let's have the screen saver check for command line arguments,
' to see if that's how Windows tells it what "mode" to open in,
' i.e., normal screensaver mode, or settings mode

' We observed the following 3 variations in command line arguments:

' When running normally or in preview mode, we get:
' Command$(1) = "/s"

' When [Settings] button is clicked, we get:
' Command$(1) = "/c:13504396"

' When scr is first installed and runs, we get:
' Command$(1) = "/p"
' Command$(2) = "4591630"

If Len(sError) = 0 Then
    Dim iLoop As Integer
    Dim sNextArgument As String
    Dim message As String
    Dim NumRows As Integer
    Dim NumCols As Integer
    NumRows = 0: NumCols = 0
    message = "_CommandCount = " + _Trim$(Str$(_CommandCount)) + Chr$(13)
    NumRows = 1: NumCols = Len(message)
    If _CommandCount > 0 Then
        For iLoop = 1 To _CommandCount
            message = message + Chr$(13): NumRows = NumRows + 1
            message = message + "Command$(" + _Trim$(Str$(iLoop)) + ") = " + Chr$(34) + Command$(iLoop) + Chr$(34)
            sNextArgument = LCase$(Left$(Command$(iLoop), 2)) ' look at first 2 chars
            If sNextArgument = "/s" Then
                message = message + ", /s received when running normally or in preview mode"
            ElseIf sNextArgument = "/c" Then
                message = message + ", /c received when Settings is opened"
                ' If we detect this, instead of running the screensaver
                ' we can display a Settings dialog
                ' load the values from the Registry
                ' let the user change values
                ' and save changes back to the Registry
                ' for how to do that, see:
                ' https://qb64phoenix.com/qb64wiki/index.php/Windows_Environment
            ElseIf sNextArgument = "/p" Then
                message = message + ", /p received when installing or wait time changed"
            End If
            If Len(message) > NumCols Then NumCols = Len(message)
        Next iLoop
    End If
End If
' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
' END GET COMMAND LINE ARGS @ARGS
' ----------------------------------------------------------------------------------------------------------------------------------------------------------------

' Initialize global vars
If Len(sError) = 0 Then
    sError = InitVars$
End If

' Start main
If Len(sError) = 0 Then
    VanHalenizer
End If

' Handle errors
If Len(sError) > 0 Then
    Screen 0
    _AutoDisplay
    Print sError
End If

' Cleanup & exit
FreeImage dot&
End

' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' EXECUTION ENDS HERE @EXT
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' BEGIN VECTOR COORDINATE DATA #DATA
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' Each line:
'     Data {id}, {type}, {x}, {y}
' where
'     {id} = unique ID for that line
'     {type} can be:
'         1 = start shape
'         2 = fill point for shape (if shape is closed and can be filled)
'         3 = line from previous point to x, y
'         4 = line from previous point to x, y (last point in shape)
'         5 = end of data marker
'        -n = (negative numbers denote a deleted node, which can be ignored)
'    {x} and {y} = absolute coordinates at scaling factor of 1 (100%)

DataStart:

' first value in each line is type:
' 1 = cDrawStart = first point, don't draw line
' 2 = cFillPoint = to fill this shape flood fill should start at these coordinates
' 3 = cDrawLine = draw line from last point
' 4 = cLastLine = draw line from last point (final point in this shape)
' 5 = cEndAll = last line of data, quit after this

' V:
Data 1,1,1,1
Data 2,2,100,100
Data 3,3,393,1
Data 3,4,498,276
Data 3,5,587,41
Data 3,6,483,41
Data 3,7,468,1
Data 3,8,648,1
Data 3,9,497,398
Data 3,10,420,191
Data 3,11,106,191
Data 3,12,85,152
Data 3,13,404,152
Data 3,14,391,116
Data 3,15,64,116
Data 3,16,42,76
Data 3,17,376,76
Data 3,18,363,41
Data 3,19,23,41
Data 4,20,1,1

' H:
Data 1,21,837,1
Data 2,22,900,20
Data 3,23,1237,1
Data 3,24,1215,41
Data 3,25,867,41
Data 3,26,854,76
Data 3,27,1195,76
Data 3,28,1173,116
Data 3,29,838,116
Data 3,30,826,151
Data 3,31,1153,151
Data 3,32,1131,191
Data 3,33,810,191
Data 3,34,614,707
Data 3,35,592,649
Data 3,36,732,277
Data 3,37,633,277
Data 3,38,542,515
Data 3,39,519,458
Data 3,40,693,1
Data 3,41,772,1
Data 3,42,757,41
Data 3,43,722,41
Data 3,44,649,237
Data 3,45,748,237
Data 4,46,837,1

' End
Data 5,45,-1,-1

' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' END VECTOR COORDINATE DATA @DATA
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

' /////////////////////////////////////////////////////////////////////////////

Function InitVars$
    Dim sError As String: sError = ""
   
    ' Init other global vars
    dot& = _NewImage(1, 1, 32) ' ALL rotoline routines all need this defiend as so
    AddSpectrumColors m_SpectrumColorArray() ' PALETTE FOR GENERAL GRAYSCALE COLORS
   
    ' Initialize global vars to default values from constants
    iScreenWidth = cScreenWidth
    iScreenHeight = cScreenHeight
    iScaleToPercent = GetDefaultScale%
    bEnableAnimation = cEnableAnimation
    iDelayMS = cDelayMS
    bMiniMenuIsVisible = cMiniMenuIsVisible
    sFilenameTemplate = cFilenameTemplate
    bEnableSaveResultsPopup = cEnableSaveResultsPopup
    iCycleEverySeconds = cCycleEverySeconds
    iRepaintEverySeconds = cRepaintEverySeconds
    iColorSchemeIndex = cColorSchemeIndex
    iBaseStripeCount = cBaseStripeCount
    iStripeVariance = cStripeVariance
    bEnableFlyingVH = cEnableFlyingVH
    iNumVectors = cNumVectors
    iVectorEveryMS = cVectorEveryMS
    iMinDX = cMinDX
    iMaxDX = cMaxDX
    iMaxScale = cMaxScale
    iScaleIncrement = cScaleIncrement
   
    ' LOAD SETTINGS FROM CONFING FILE
    sError = LoadConfig$
   
    ' RETURN RESULT
    InitVars$ = sError
End Function ' InitVars$

' /////////////////////////////////////////////////////////////////////////////

Sub VanHalenizer
    ' Local vars
    Dim RoutineName As String:: RoutineName = "VanHalenizer"
    ReDim arrColorScheme(-1) As String
    ReDim arrMoreInfo(-1) As String
    Dim arrWeight(1 To 4) As Single ' cStripe_1_8 = 1/8", cStripe_1_4 = 1/4", cStripe_1_2 = 1/2" and cStripe_3_4 = 3/4" (cStripe_1_16 = 1/16" and cStripe_1_0 = 1" is defined but not yet used)
    ReDim arrLayer(-1) As LayerType
    ReDim arrStripe(-1) As StripeType
    ReDim arrLines(-1) As String
    Dim in$
    Dim message As String
    Dim fg As _Unsigned Long
    Dim t1 As Single
    Dim t2 As Single
    Dim n As Long
    Dim c As Integer
    Dim iValue1 As Integer
    Dim iValue2 As Integer
    Dim iScreenSize As Integer ' screen height or width whichever is smaller
    Dim x As Long
    Dim y As Long
    Dim bRefresh As _Byte
    Dim iOldColor As Integer
    Dim bgColor As _Unsigned Long
    Dim sngScale As Single ' always set to iScaleToPercent * .01
    Dim iPixelsPerInch As Integer
    Dim iTapeSize As Integer
    Dim index As Long
    Dim bSavedJPG As _Byte
    Dim bSavedPNG As _Byte
    Dim sPrompt As String
    Dim fgPromptColor As _Unsigned Long
    Dim bgPromptColor As _Unsigned Long
    Dim sIndent As String
    Dim bTurn As _Byte
    Dim iLayer As Integer
    Dim r, g, b As Integer
    Dim iMaxColorSchemeNameLen As Integer
    Dim x1, y1, x2, y2 As Single
    Dim iLoop As Integer
    Dim iLen As Integer
    Dim iMaxLen As Integer
    Dim sLine As String
    Dim NextTime As _Float ' used by ExtendedTimer
    Dim bMove As _Byte
    Dim NextColor As _Float ' used by ExtendedTimer
    Dim bCycleColor As _Byte
    Dim NextRepaint As _Float ' used by ExtendedTimer
    Dim bRepaint As _Byte
    Dim sNextError As String
    Dim iMin As Integer
    Dim iMax As Integer
    Dim iNumStripes As Integer ' calculated from iBaseStripeCount and percentage depending on the color scheme
    Dim sngNumStripes As Single
    Dim sngStripeDensity As Single
   
    ' IMAGES / LAYERS
    Dim image1 As Long ' the cumulative layer
    Dim image2 As Long ' copied over image1 (with transparency)
    Dim VectorImage As Long ' for vh logo
    Dim NextImage As Long ' for vh logo
   
    ' For vector graphics
    Dim iShapeCount As Integer
    Dim iPointCount As Integer
    Dim id As Long
    Dim iType As Long
    Dim iPoint As Long
   
    ' Store the vector data
    ReDim arrID(-1) As Long
    ReDim arrType(-1) As Long
    ReDim arrScreenX1(-1) As Long
    ReDim arrScreenY1(-1) As Long
    ReDim arrScreenX2(-1) As Long
    ReDim arrScreenY2(-1) As Long
   
    ' Store the fill point for each shape
    ReDim arrFillX(-1) As Long
    ReDim arrFillY(-1) As Long
   
    ' For scaling to screen
    'Dim sngScalingFactor As Single ' NOW HELD IN arrScalingFactor
    Dim xMax As Long ' rightmost point in vector logo
    Dim yMax As Long ' bottommost point in vector logo
   
    ' For autoscaling screen size to shape
    Dim xDiff As Single
    Dim yDiff As Single
   
    ' Variables to track the current absolute position
    Dim nextX As Long
    Dim nextY As Long
    Dim startX As Long
    Dim startY As Long
    Dim iX1 As Long
    Dim iY1 As Long
    Dim iX2 As Long
    Dim iY2 As Long
   
    ' Variables for multiple vectors
    Dim arrScalingFactor(0 To iNumVectors) As Single
    Dim arrScaleIncrement(0 To iNumVectors) As Single
    Dim arrOffsetX(0 To iNumVectors) As Long
    Dim arrOffsetY(0 To iNumVectors) As Long
    Dim arrOffsetDX(0 To iNumVectors) As Long
    Dim arrOffsetDY(0 To iNumVectors) As Long
    'Dim arrVectorWeight(0 To iNumVectors) As Single ' used when shapes were just outlines drawn with RotoLine
    Dim arrColor(0 To iNumVectors) As _Unsigned Long
    Dim arrZOrder(0 To iNumVectors) As Integer ' for displaying closer shapes in front of farther shapes
    Dim arrShapeSort(0 To iNumVectors) As Integer ' shape IDs in order of zOrder
    Dim bSpawn As _Byte
   
    ' Variables for displaying vector shapes
    Dim iWhichVector As Integer
    Dim iWhichColor As Integer
    Dim CurrentColor As _Unsigned Long
    Dim NextVector As _Float ' used by ExtendedTimer
    Dim VectorX As Long
    Dim VectorY As Long
    Dim VectorDX As Long
    Dim VectorDY As Long
    Dim xFill As Long ' calculate from arrFillX
    Dim yFill As Long ' calculate from arrFillY
   
    ' Variables derived from shared variables loaded from configL
    Dim sngMaxScale As Single ' = derived from: iMaxScale / 100
    Dim sngScaleIncrement As Single ' = derived from: iScaleIncrement / 1000000
   
    ' Variables used to sort zOrder of shapes (flying vh symbols)
    Dim BiggestShapeScale As Single
    Dim BiggestShapeIndex As Integer
    Dim iOrder As Integer
    ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    ' BEGIN Initialize
    ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    ' CONVERT SOME VALUES FROM CONFIG SETTINGS
    sngMaxScale = iMaxScale / 100
    sngScaleIncrement = iScaleIncrement / 1000000
   
    '' BEGIN DEBUG
    'message = _tostr$(sngScaleIncrement) + " = " + _tostr$(iScaleIncrement) + " / 1000000"
    'in$ = _InputBox$(m_ProgramName, "sngScaleIncrement = iScaleIncrement / 1000000", message)
    '' END DEBUG
   
    ' ADJUST SCREEN SIZE BASED ON SETTINGS
    If iScreenWidth <= 0 Then CurrentWidth = _DesktopWidth Else CurrentWidth = iScreenWidth
    If iScreenHeight <= 0 Then CurrentHeight = _DesktopHeight Else CurrentHeight = iScreenHeight
    iScreenSize = _Min(CurrentWidth, CurrentHeight)

    ' SETUP SCREEN
    Screen _NewImage(CurrentWidth, CurrentHeight, 32)

    '_ScreenMove 0, 0 ' USING THIS COMMAND BREAKS _FullScreen IF DONE RIGHT BEFORE _FullScreen
    _FullScreen _SquarePixels

    ' CALCULATE AVAILABLE TEXT ROWS/COLUMNS
    MaxRow = _Height / _FontHeight
    MaxCol = _Width / _FontWidth

    ' INIT IMAGES
    InitImage image1, CurrentWidth, CurrentHeight, _RGBA(0, 0, 0, 0)
    InitImage image2, CurrentWidth, CurrentHeight, _RGBA(0, 0, 0, 0)
    InitImage VectorImage, CurrentWidth, CurrentHeight, _RGBA(0, 0, 0, 0)
    InitImage NextImage, CurrentWidth, CurrentHeight, _RGBA(0, 0, 0, 0)
   
    ' SET RANDOM SEED
    Randomize Timer

    ' ================================================================================================================================================================
    ' OTHER INITIALIZATION
    ' ================================================================================================================================================================
    bgColor = _RGB32(3, 20, 72)
    fgPromptColor = _RGB32(0, 0, 0)
    bgPromptColor = _RGB32(0, 255, 0)
    iPixelsPerInch = 64
    sngScale = iScaleToPercent * 0.01
    'NOW IN arrScalingFactor: sngScalingFactor = 1 ' Scales the logo up (166 * 2.2 ~= 365 px high)
   
    For index = LBound(arrScalingFactor) To UBound(arrScalingFactor): arrScalingFactor(index) = 0: Next index
    bSpawn = _TRUE
    iWhichColor = LBound(arrColor) - 1
    'AddColor _rgb32(255, 255, 255), arrIgnoreColors()

    ' LINE WEIGHTS = STRIPE SIZES (DIFFERENT SIZES OF TAPE)
    'arrWeight(cStripe_1_16) = 1 / 16
    arrWeight(cStripe_1_8) = 1 / 8
    arrWeight(cStripe_1_4) = 1 / 4
    arrWeight(cStripe_1_2) = 1 / 2
    arrWeight(cStripe_3_4) = 3 / 4
    'arrWeight(cStripe_1_0) = 1

    ' RESET KEY STATES
    For index = LBound(arrKeyState) To UBound(arrKeyState): arrKeyState(index) = _FALSE: Next index

    ' -----------------------------------------------------------------------------
    ' <COLOR SCHEME DEFINITONS>
    ' TODO: MOVE THESE INTO CONFIG FILE
    ' TODO: MOVE THE ReDim _Preserve + UBOUND INTO AN "AppendOption" SUB
    ' -----------------------------------------------------------------------------
    ReDim _Preserve arrColorScheme(0 To UBound(arrColorScheme) + 1) As String
    ReDim _Preserve arrMoreInfo(0 To UBound(arrMoreInfo) + 1) As String
    index = UBound(arrColorScheme): arrColorScheme(index) = "Frankenstrat (Black/White on Red)      "
    index = UBound(arrMoreInfo): arrMoreInfo(index) = "The 'Frankenstrat' guitar repainted with the iconic red, black, and white stripes, the full-sized pickguard removed and a mysterious electical doodad in the middle pickup hole. Ed added the red paint on top of the existing black and white stripes by mid 1979."

    ReDim _Preserve arrColorScheme(0 To UBound(arrColorScheme) + 1) As String
    ReDim _Preserve arrMoreInfo(0 To UBound(arrMoreInfo) + 1) As String
    index = UBound(arrColorScheme): arrColorScheme(index) = "78 Eruption  (Black on White)          "
    index = UBound(arrMoreInfo): arrMoreInfo(index) = "The original 'Frankenstrat' guitar that Edward Van Halen built, as it looked in 1978 with a black and white striped pattern and black pickguard, used on the first album and tour."

    ReDim _Preserve arrColorScheme(0 To UBound(arrColorScheme) + 1) As String
    ReDim _Preserve arrMoreInfo(0 To UBound(arrMoreInfo) + 1) As String
    index = UBound(arrColorScheme): arrColorScheme(index) = "79 Bumblebee (Yellow on Black)         "
    index = UBound(arrMoreInfo): arrMoreInfo(index) = "The guitar pictured on the Van Halen II album, with a black body with yellow  stripes, used on the 1979 tour, and now lies buried with Pantera guitarist Dimebag Darrell (RIP)."

    ReDim _Preserve arrColorScheme(0 To UBound(arrColorScheme) + 1) As String
    ReDim _Preserve arrMoreInfo(0 To UBound(arrMoreInfo) + 1) As String
    index = UBound(arrColorScheme): arrColorScheme(index) = "blue Frankenstrat (Black/White on Blue)"
    index = UBound(arrMoreInfo): arrMoreInfo(index) = "(Frankenstrat blue)"

    ReDim _Preserve arrColorScheme(0 To UBound(arrColorScheme) + 1) As String
    ReDim _Preserve arrMoreInfo(0 To UBound(arrMoreInfo) + 1) As String
    index = UBound(arrColorScheme): arrColorScheme(index) = "inverse 78 Eruption  (White on Black)  "
    index = UBound(arrMoreInfo): arrMoreInfo(index) = "('78 guitar reversed colors)"

    ReDim _Preserve arrColorScheme(0 To UBound(arrColorScheme) + 1) As String
    ReDim _Preserve arrMoreInfo(0 To UBound(arrMoreInfo) + 1) As String
    index = UBound(arrColorScheme): arrColorScheme(index) = "inverse 79 Bumblebee (Yellow on Black) "
    index = UBound(arrMoreInfo): arrMoreInfo(index) = "(Bumblebee reversed colors)"

    ReDim _Preserve arrColorScheme(0 To UBound(arrColorScheme) + 1) As String
    ReDim _Preserve arrMoreInfo(0 To UBound(arrMoreInfo) + 1) As String
    index = UBound(arrColorScheme): arrColorScheme(index) = "attempt #1 at Rasta guitar             " ' black, white, red, green, yellow
    index = UBound(arrMoreInfo): arrMoreInfo(index) = "(Sort of 'the rasta guitar' that Ed gave to Dweezil)"

    ReDim _Preserve arrColorScheme(0 To UBound(arrColorScheme) + 1) As String
    ReDim _Preserve arrMoreInfo(0 To UBound(arrMoreInfo) + 1) As String
    index = UBound(arrColorScheme): arrColorScheme(index) = "attempt #2 at Rasta guitar             " ' black, white, red, green, yellow
    index = UBound(arrMoreInfo): arrMoreInfo(index) = "(Sort of 'the rasta guitar' that Ed gave to Dweezil)"

    ' Set index
    If iColorSchemeIndex < LBound(arrColorScheme) Then
        iColorSchemeIndex = LBound(arrColorScheme)
    ElseIf iColorSchemeIndex > UBound(arrColorScheme) Then
        iColorSchemeIndex = UBound(arrColorScheme)
    End If
    iOldColor = iColorSchemeIndex - 1 ' set value to something different to trigger initial refresh

    ' Get length of longest color scheme name
    iMaxColorSchemeNameLen = Len(arrColorScheme(LBound(arrColorScheme)))
    For index = LBound(arrColorScheme) + 1 To UBound(arrColorScheme)
        If Len(arrColorScheme(index)) > iMaxColorSchemeNameLen Then
            iMaxColorSchemeNameLen = Len(arrColorScheme(index))
        End If
    Next index
    ' -----------------------------------------------------------------------------
    ' </COLOR SCHEME DEFINITONS>
    ' -----------------------------------------------------------------------------


    ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    ' BEGIN READ VECTOR DATA
    ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    ' Init vars
    xMax = 0
    yMax = 0

    ' Read vector data into array
    Restore DataStart
   
   
    iShapeCount = LBound(arrFillX) - 1
    iPointCount = 0
    Do
        sNextError = ""

        ' READ NEXT DATA ROW
        If Len(sNextError) = 0 Then
            Read iType
            Read id
            Read nextX
            Read nextY
        End If

        ' IS NEXT DATA TYPE VALID?
        If Len(sNextError) = 0 Then
            If iType < 1 Then
                sNextError = "DELETED"
            End If
        End If

        ' PROCESS NEXT DATA ROW
        ' first value in each line is type:
        ' 1 = cDrawStart = first point, don't draw line
        ' 2 = cDrawLine = draw line from last point
        ' NOT USED: 3 = cRelativeStart
        ' NOT USED: 4 = cBacktoStart
        ' 5 = cLastLine = draw line like cDrawLine
        ' 6 = cEndAll = draw line like cDrawLine; this is the final data, quit after this
        If Len(sNextError) = 0 Then
            If iType = cDrawStart Then
                ' New shape
                iShapeCount = iShapeCount + 1
                iPointCount = 0
               
                ' Increment arrays for each new shape
                ReDim _Preserve arrFillX(0 To UBound(arrFillX) + 1)
                ReDim _Preserve arrFillY(0 To UBound(arrFillY) + 1)
               
                ' Start point
                startX = nextX
                startY = nextY
                iX1 = nextX
                iY1 = nextY
                iX2 = -1
                iY2 = -1
                If iX1 > xMax Then xMax = iX1
                If iY1 > yMax Then yMax = iY1
               
            ElseIf iType = cFillPoint Then
                ' Save fill point coords for this shape
                arrFillX(iShapeCount) = nextX
                arrFillY(iShapeCount) = nextY
               
            ElseIf iType = cDrawLine Then
                ' For drawing line
                iX2 = iX1
                iY2 = iY1
                iX1 = nextX
                iY1 = nextY
                If iX1 > xMax Then xMax = iX1
                If iY1 > yMax Then yMax = iY1
               
            ElseIf iType = cLastLine Then
                ' For drawing line
                iX2 = iX1
                iY2 = iY1
                iX1 = nextX
                iY1 = nextY
                If iX1 > xMax Then xMax = iX1
                If iY1 > yMax Then yMax = iY1
            ElseIf iType = cEndAll Then
                Exit Do
            Else
                sNextError = "TYPE UNKNOWN: " + _ToStr$(iType)
            End If
        End If

        ' SAVE VALUES TO ARRAY TO RENDER + EDIT LATER
        If Len(sNextError) = 0 Then
            ' SAVE NEXT POINT
            ReDim _Preserve arrID(0 To UBound(arrID) + 1): arrID(UBound(arrID)) = id
            ReDim _Preserve arrType(0 To UBound(arrType) + 1): arrType(UBound(arrType)) = iType
            ReDim _Preserve arrScreenX1(0 To UBound(arrScreenX1) + 1): arrScreenX1(UBound(arrScreenX1)) = iX1
            ReDim _Preserve arrScreenY1(0 To UBound(arrScreenY1) + 1): arrScreenY1(UBound(arrScreenY1)) = iY1
            ReDim _Preserve arrScreenX2(0 To UBound(arrScreenX2) + 1): arrScreenX2(UBound(arrScreenX2)) = iX2
            ReDim _Preserve arrScreenY2(0 To UBound(arrScreenY2) + 1): arrScreenY2(UBound(arrScreenY2)) = iY2
        End If
    Loop
    ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    ' END READ VECTOR DATA
    ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
   
    '' ================================================================================================================================================================
    '' SET SCALE TO FIT SCREEN
    '' ================================================================================================================================================================
    '' Figure out the math:
    ''     xMax = 720, _width  = 640, xDiff = 500 / 400 = 1.125
    ''     yMax = 800, _height = 480, yDiff = 600 / 300 = 1.666
    ''     If Y1 = 720, fit to screen height 480
    ''
    ''     1.666   Y1=720
    ''     ----- = ------
    ''     1       Y
    ''
    ''     1.666 * Y = 1 * 720
    ''
    ''     Y * 1.666 = 720
    ''     Y = 720 / 1.666 = 432
    '
    ''     so
    ''
    ''     If Y1 = 1, fit to screen 480
    ''
    ''     1.666 = Y1=1
    ''     -----   ---
    ''     1       Y=1
    ''
    ''     1.666 * Y = 1 * Y1
    ''
    ''     Y * 1.666 = Y1
    ''     Y = Y1 / 1.666
    '
    'If xMax > _Width _OrElse yMax > _Height Then
    '   xDiff = xMax / _Width
    '   yDiff = yMax / _Height
    '   If xDiff > yDiff Then
    '       sngScalingFactor = 1 / xDiff
    '   Else
    '       sngScalingFactor = 1 / yDiff
    '   End If
    'End If
   
    ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    ' END Initialize
    ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
   
    ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    ' BEGIN MAIN LOOP
    ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    Do
        ' Clear screen
        _Dest 0
        Cls , bgColor
        x = 0: y = 0
        PrintLogo x, y ' PrintLogo returns x, y position byref to below logo
        bRefresh = _TRUE

        ' ================================================================================================================================================================
        ' BEGIN CHOOSE SETTINGS
        ' ================================================================================================================================================================
        Do
            ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
            ' BEGIN REDRAW MENU
            ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
            If bRefresh = _TRUE Then
                _KeyClear: '_Delay .5
                _Title "Van Halenizer v2.02"

                ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                ' Did color scheme change?
                ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                ChangeColorScheme:
                If iColorSchemeIndex <> iOldColor Then
                    ' Select appropriate optimal default values
                    iOldColor = iColorSchemeIndex
                    Select Case iColorSchemeIndex
                        Case cFrankenstrat:
                            ' red=190,2,13 white=248,247,242, black=16,20,20
                            fgPromptColor = _RGB32(0, 0, 0)
                            bgPromptColor = _RGB32(0, 255, 0)
                            sngStripeDensity = 1
                            ReDim arrLayer(0 To 1) As LayerType

                            ' black:
                            'arrLayer(0).fg = _RGB32(0, 0, 0)
                            arrLayer(0).fg = _RGB32(0, 0, 0)

                            ' white:
                            'arrLayer(0).bg = _RGB32(255, 255, 255)
                            arrLayer(0).bg = _RGB32(248, 247, 242)

                            ' transparent
                            arrLayer(1).fg = _RGBA32(0, 0, 0, 0)

                            ' red:
                            'arrLayer(1).bg = _RGB32(255, 0, 0)
                            arrLayer(1).bg = _RGB32(190, 2, 13)

                        Case cBlueFrankenstrat:
                            'blue=13,118,184 white=233,232,238 black=16,18,17
                            'blue=62,118,193 white=252,250,237 black=38,37,42
                            fgPromptColor = _RGB32(0, 0, 0)
                            bgPromptColor = _RGB32(255, 255, 255)
                            sngStripeDensity = 1
                            ReDim arrLayer(0 To 1) As LayerType

                            ' black:
                            'arrLayer(0).fg = _RGB32(0, 0, 0)
                            arrLayer(0).fg = _RGB32(16, 18, 17)

                            ' white:
                            'arrLayer(0).bg = _RGB32(255, 255, 255)
                            arrLayer(0).bg = _RGB32(233, 232, 238)

                            ' transparent
                            arrLayer(1).fg = _RGBA32(0, 0, 0, 0)

                            ' blue:
                            'arrLayer(1).bg = _RGB32(0, 0, 255)
                            arrLayer(1).bg = _RGB32(13, 118, 184)

                        Case c78:
                            fgPromptColor = _RGB32(0, 0, 0)
                            bgPromptColor = _RGB32(0, 255, 0)

                            sngStripeDensity = .75
                            ReDim arrLayer(0 To 0) As LayerType

                            'black:
                            arrLayer(0).fg = _RGB32(0, 0, 0)

                            'white:
                            'arrLayer(0).bg = _RGB32(255, 255, 255)
                            arrLayer(0).bg = _RGB32(246, 244, 240)

                        Case cInverse78:
                            fgPromptColor = _RGB32(0, 0, 0)
                            bgPromptColor = _RGB32(0, 255, 0)
                            sngStripeDensity = .75
                            ReDim arrLayer(0 To 0) As LayerType

                            'white:
                            arrLayer(0).fg = _RGB32(255, 255, 255)

                            'black:
                            arrLayer(0).bg = _RGB32(0, 0, 0)

                        Case cBumbleebee:
                            'yellow = 236,196,0
                            fgPromptColor = _RGB32(255, 255, 255)
                            bgPromptColor = _RGB32(0, 0, 255)
                            sngStripeDensity = .65
                            ReDim arrLayer(0 To 0) As LayerType

                            'yellow:
                            arrLayer(0).fg = _RGB32(255, 255, 0)

                            'black:
                            arrLayer(0).bg = _RGB32(0, 0, 0)

                        Case cInverseBumbleebee:
                            fgPromptColor = _RGB32(255, 255, 255)
                            bgPromptColor = _RGB32(0, 0, 255)
                            sngStripeDensity = .65
                            ReDim arrLayer(0 To 0) As LayerType

                            'black:
                            arrLayer(0).fg = _RGB32(0, 0, 0)

                            'yellow:
                            arrLayer(0).bg = _RGB32(255, 255, 0)

                        Case cRastaGuitar1:
                            ' This version tapes the red/white/black, sprays green, removes paint, adds yellow stripes on top
                            ' Not that great a recreation, needs more work
                            ' but demonstrates that >2 layers work!

                            'black=17,17,18, white=232,239,241, red=197,0,24, green=13,126,58, yellow = 250,209,84
                            fgPromptColor = _RGB32(0, 0, 0)
                            bgPromptColor = _RGB32(255, 255, 255)
                            sngStripeDensity = 2.5
                            ReDim arrLayer(0 To 3) As LayerType

                            'black:
                            arrLayer(0).fg = _RGB32(17, 17, 18)

                            'white:
                            arrLayer(0).bg = _RGB32(232, 239, 241)

                            'red
                            arrLayer(1).bg = _RGB32(197, 0, 24)
                            arrLayer(1).fg = _RGBA32(0, 0, 0, 0) ' transparent bg

                            'green:
                            arrLayer(2).bg = _RGB32(13, 126, 58)
                            arrLayer(2).fg = _RGBA32(0, 0, 0, 0) ' transparent bg

                            'yellow:
                            arrLayer(3).fg = _RGB32(250, 209, 84)
                            arrLayer(3).bg = _RGBA32(0, 0, 0, 0) ' transparent bg

                        Case cRastaGuitar2:
                            ' This version tapes the red/white/black, then adds green stripes on top, then yellow stripes on top (layer 2 fg/bg are swapped)
                            ' Not that great a recreation, needs more work
                            ' but demonstrates that >2 layers work!

                            'black=17,17,18, white=232,239,241, red=197,0,24, green=13,126,58, yellow = 250,209,84
                            fgPromptColor = _RGB32(0, 0, 0)
                            bgPromptColor = _RGB32(255, 255, 255)
                            sngStripeDensity = 2
                            ReDim arrLayer(0 To 3) As LayerType

                            'black:
                            arrLayer(0).fg = _RGB32(17, 17, 18)

                            'white:
                            arrLayer(0).bg = _RGB32(232, 239, 241)

                            'red
                            arrLayer(1).bg = _RGB32(197, 0, 24)
                            arrLayer(1).fg = _RGBA32(0, 0, 0, 0) ' transparent bg

                            'green:
                            arrLayer(2).fg = _RGB32(13, 126, 58)
                            arrLayer(2).bg = _RGBA32(0, 0, 0, 0) ' transparent bg

                            'yellow:
                            arrLayer(3).fg = _RGB32(250, 209, 84)
                            arrLayer(3).bg = _RGBA32(0, 0, 0, 0) ' transparent bg
                    End Select
                    If bCycleColor = _TRUE Then bCycleColor = _FALSE: GoTo PaintGuitar
                    If bRepaint = _TRUE Then bRepaint = _FALSE: GoTo PaintGuitar
                End If

                ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                ' SHOW OPTIONS BELOW PrintLogo
                ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                ' Build the menu text with the current choices
                in$ = ""

                If Len(sNextError) > 0 Then
                    in$ = in$ + "*****************************************************************************" + Chr$(13)
                    in$ = in$ + "*** " + sNextError + Chr$(13)
                    in$ = in$ + "*****************************************************************************" + Chr$(13)
                    sNextError = ""
                End If

                If iScreenHeight >= 768 Then in$ = in$ + Chr$(13)

                in$ = in$ + "Page Up/Down..........Base stripe count  : " + PadLeft$(_ToStr$(iBaseStripeCount), 4) + "     " + Chr$(13)
                in$ = in$ + "Crsr Up/Down..........Scale size         : " + PadLeft$(_ToStr$(iScaleToPercent), 4) + "%     " + Chr$(13)
                in$ = in$ + "Crsr Left/Right.......Change color scheme: " + arrColorScheme(iColorSchemeIndex) + Space$(iMaxColorSchemeNameLen) + Chr$(13)
                in$ = in$ + "S: +1  X: -1..........Cycle scheme  every: " + PadLeft$(_ToStr$(iCycleEverySeconds), 5) + " seconds (0 = stay put)" + Chr$(13)
                in$ = in$ + "D: +1  C: -1..........Repaint       every: " + PadLeft$(_ToStr$(iRepaintEverySeconds), 5) + " seconds (0 = stay put)" + Chr$(13)
                in$ = in$ + "A: enable Z:disable...Toggle animation   : " + "animation " + _IIf(bEnableAnimation, "enabled ", "disabled") + Chr$(13)
                in$ = in$ + "F: enable V:disable...Toggle flying VH   : " + "flying VH " + _IIf(bEnableFlyingVH, "enabled ", "disabled") + Chr$(13)
                in$ = in$ + "M: enable N:disable...Toggle mini menu   : " + "menu " + _IIf(bMiniMenuIsVisible, "enabled ", "disabled") + Chr$(13)
                in$ = in$ + "Enter.................Van Halenize!" + Chr$(13)
                in$ = in$ + "Esc...................Quit <- get off the computer and go play guitar!" + Chr$(13)

                ' We need to center it, but how?
                ' Let's think about a concrete example
                ' to sort out the math:
                '
                '         11111111112
                '12345678901234567890
                'abba
                'dabba
                'doo
                '12345678901234567890
                '       abba
                '      dabba
                '      doo
                'MaxCol=20
                'iLen = 5
                '(MaxCol-iLen) / 2 = ?
                '(20    - 5  ) / 2 = 8

                ' Find the average line length
                split in$, Chr$(13), arrLines()
                iLen = 0: iMaxLen = 0
                For iLoop = LBound(arrLines) To UBound(arrLines)
                    iLen = iLen + Len(arrLines(iLoop))
                    If Len(arrLines(iLoop)) > iMaxLen Then iMaxLen = Len(arrLines(iLoop))
                Next iLoop
                If iMaxLen <= MaxCol Then
                    ' Calculate the indent
                    iLen = iLen / ((UBound(arrLines) - LBound(arrLines)) + 1)
                    iLen = (MaxCol - iLen) / 2
                    sIndent = Space$(iLen)
                Else
                    ' Not enough room for indent
                    sIndent = ""
                End If
               
                ' Start beneath PrintLogo
                If (y <= MaxRow) Then
                    Locate y + 1, 1
                Else
                    Cls , bgColor
                    Print "The Van Halenizer by Softintheheadware"
                End If
               
                ' 32-bit basic colors = Black, Blue, Cyan / Aqua, Lime, Magenta, Red, White
                Color White, bgColor

                ' Print menu roughly centered
                For iLoop = LBound(arrLines) To UBound(arrLines)
                    If Left$(arrLines(iLoop), 1) <> "*" Then
                        If IsOdd%(iLoop) Then Color White, bgColor Else Color Cyan, bgColor
                    Else
                        Color White, Red
                    End If
                   
                    sLine = sIndent + arrLines(iLoop)
                    If Len(sLine) <= MaxCol Then
                        Print sLine
                    Else
                        Print Left$(sLine, MaxCol - 1)
                    End If
                Next iLoop
                'Print "iMaxLen=" + _ToStr$(iMaxLen)
                'Print "iLen   =" + _ToStr$(iLen)
                'Print "sIndent=" + Chr$(34) + sIndent + Chr$(34)

                '_PrintString (0, MaxRow * _FontHeight), "THIS IS THE LAST ROW"
                _Display: bRefresh = _FALSE
            End If
            ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
            ' END REDRAW MENU
            ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------

            ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
            ' BEGIN PROCESS INPUT     <----  *** TODO: MOVE THIS INTO ITS OWN ROUTINE ***
            ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
            ' PAGE DOWN = STRIPE COUNT -1   PAGE UP = STRIPE COUNT +1
            If _KeyDown(cPageDown) Then
                iBaseStripeCount = iBaseStripeCount - 1
                If iBaseStripeCount <= 1 Then iBaseStripeCount = 1
                bRefresh = _TRUE
            ElseIf _KeyDown(cPageUp) Then
                iBaseStripeCount = iBaseStripeCount + 1
                If iBaseStripeCount > 500 Then iBaseStripeCount = 500
                bRefresh = _TRUE
            End If

            ' DOWN = SCALE -1   UP = SCALE +1
            If _KeyDown(cDownKD) Then
                iScaleToPercent = iScaleToPercent - 1
                If iScaleToPercent < 1 Then iScaleToPercent = 1
                sngScale = iScaleToPercent * 0.01
                bRefresh = _TRUE
            ElseIf _KeyDown(cUpKD) Then
                iScaleToPercent = iScaleToPercent + 1
                If iScaleToPercent > 1000 Then iScaleToPercent = 1000
                sngScale = iScaleToPercent * 0.01
                bRefresh = _TRUE
            End If

            ' RIGHT = COLOR SCHEME -1
            If _KeyDown(cLeftKD) Then
                If arrKeyState(KeyCode_Left) = _FALSE Then
                    arrKeyState(KeyCode_Left) = _TRUE
                    iColorSchemeIndex = iColorSchemeIndex - 1
                    If iColorSchemeIndex < LBound(arrColorScheme) Then iColorSchemeIndex = LBound(arrColorScheme)
                    bRefresh = _TRUE
                End If
            Else
                arrKeyState(KeyCode_Left) = _FALSE
            End If

            ' RIGHT = COLOR SCHEME +1
            If _KeyDown(cRightKD) Then
                If arrKeyState(KeyCode_Right) = _FALSE Then
                    arrKeyState(KeyCode_Right) = _TRUE
                    iColorSchemeIndex = iColorSchemeIndex + 1
                    If iColorSchemeIndex > UBound(arrColorScheme) Then iColorSchemeIndex = UBound(arrColorScheme)
                    bRefresh = _TRUE
                End If
            Else
                arrKeyState(KeyCode_Right) = _FALSE
            End If

            ' S = INCREASE TIME, X = DECREASE TIME
            If _KeyDown(cUpperS) Or _KeyDown(cLowerS) Then
                iCycleEverySeconds = iCycleEverySeconds + 1
                If iCycleEverySeconds > 86400 Then iCycleEverySeconds = 86400 ' Max time = 1 day
                bRefresh = _TRUE
            ElseIf _KeyDown(cUpperX) Or _KeyDown(cLowerX) Then
                iCycleEverySeconds = iCycleEverySeconds - 1
                If iCycleEverySeconds < 0 Then iCycleEverySeconds = 0 ' 0 means color scheme doesn't cycle
                bRefresh = _TRUE
            End If

            ' D = INCREASE TIME, C = DECREASE TIME
            If _KeyDown(cUpperD) Or _KeyDown(cLowerD) Then
                iRepaintEverySeconds = iRepaintEverySeconds + 1
                If iRepaintEverySeconds > 3600 Then iRepaintEverySeconds = 3600 ' Max time = 1 hour
                bRefresh = _TRUE
            ElseIf _KeyDown(cUpperC) Or _KeyDown(cLowerC) Then
                iRepaintEverySeconds = iRepaintEverySeconds - 1
                If iRepaintEverySeconds < 0 Then iRepaintEverySeconds = 0 ' 0 means picture doesn't repaint automatically (user must press Enter)
                bRefresh = _TRUE
            End If

            ' A = ENABLE, Z=DISABLE ANIMATE
            If _KeyDown(cUpperA) Or _KeyDown(cLowerA) Then
                bEnableAnimation = _TRUE
                bRefresh = _TRUE
            ElseIf _KeyDown(cUpperZ) Or _KeyDown(cLowerZ) Then
                bEnableAnimation = _FALSE
                bRefresh = _TRUE
            End If

            ' F = ENABLE, V=DISABLE FLYING VH
            If _KeyDown(cUpperF) Or _KeyDown(cLowerF) Then
                bEnableFlyingVH = _TRUE
                bRefresh = _TRUE
            ElseIf _KeyDown(cUpperV) Or _KeyDown(cLowerV) Then
                bEnableFlyingVH = _FALSE
                bRefresh = _TRUE
            End If

            ' M = ENABLE, N=DISABLE MENU
            If _KeyDown(cUpperM) Or _KeyDown(cLowerM) Then
                bMiniMenuIsVisible = _TRUE
                bRefresh = _TRUE
            ElseIf _KeyDown(cUpperN) Or _KeyDown(cLowerN) Then
                bMiniMenuIsVisible = _FALSE
                bRefresh = _TRUE
            End If

            ' ENTER = START PAINTING!
            If _KeyDown(cEnter) Then
                Exit Do
            End If
           
            ' ESC = EXIT
            If _KeyDown(cEsc) Then
                GoTo CleanupAndExit
            End If

            ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
            ' END PROCESS INPUT
            ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------

            _Limit 60
        Loop
       
        ' Initialize vector spawn point
        VectorX = _Width / 2
        VectorY = _Height / 2
        'VectorX = RandomNumber%(_Width/4, _Width - (_Width/4) )
        'VectorY = RandomNumber%(_Height/4, _Height - (_Height/4) )
       
        VectorDX = RandomNumber%(iMinDX, iMaxDX)
        If RandomNumber%(0, 1) = 0 Then VectorDX = 0 - VectorDX
       
        VectorDY = RandomNumber%(iMinDX, iMaxDX)
        If RandomNumber%(0, 1) = 0 Then VectorDY = 0 - VectorDY
        For iWhichVector = LBound(arrScalingFactor) To UBound(arrScalingFactor): arrScalingFactor(iWhichVector) = 0: Next iWhichVector
       
        ResetTimer:
        ' Set timer to cycle color scheme
        If iCycleEverySeconds > 0 Then
            NextColor = ExtendedTimer + iCycleEverySeconds ' SCHEDULE COLOR SCHEME TO CHANGE IN iCycleEverySeconds SECONDS
        End If

        ' Set timer to cycle repaint
        If iRepaintEverySeconds > 0 Then
            NextRepaint = ExtendedTimer + iRepaintEverySeconds ' SCHEDULE COLOR SCHEME TO CHANGE IN iCycleEverySeconds SECONDS
        End If
       
        ' Set timer to spawn new vector
        If iVectorEveryMS > 0 Then
            NextVector = ExtendedTimer + iVectorEveryMS * 0.001 ' SCHEDULE NEW VECTOR SPAWNED IN iVectorEveryMS MILLISECONDS
        End If
       
        ' ================================================================================================================================================================
        ' END CHOOSE SETTINGS
        ' ================================================================================================================================================================

        ' ================================================================================================================================================================
        ' BEGIN INITIALIZE STRIPES
        ' ================================================================================================================================================================
        PaintGuitar:
        ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
        ' PREPARE TO PAINT
        ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
        '' Time it
        't1 = Timer

        ' Reset stripes
        ReDim arrStripe(-1) As StripeType

        ' How many stripes?
        ' Adjust depending on the pattern and add a little randomness
        sngNumStripes = sngStripeDensity * iBaseStripeCount
        iMin = _Cast(Integer, sngNumStripes) - iStripeVariance
        If iMin < 1 Then iMin = 1
        iMax = _Cast(Integer, sngNumStripes) + iStripeVariance
        If iMax > 500 Then iMax = 500
        iNumStripes = RandomNumber%(iMin, iMax)

        ' For now evenly divide them among layers
        iValue1 = UBound(arrLayer) + 1
        For index = LBound(arrLayer) To UBound(arrLayer)
            arrLayer(index).Count = iNumStripes / iValue1
        Next index

        ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
        ' BEGIN CREATE STRIPES
        ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
        For iLayer = LBound(arrLayer) To UBound(arrLayer)
            For n = 1 To arrLayer(iLayer).Count
                ' Add stripe
                ReDim _Preserve arrStripe(0 To UBound(arrStripe) + 1) As StripeType

                ' Get stripe's index
                index = UBound(arrStripe)

                ' Save layer #
                arrStripe(index).layer = iLayer

                ' Random stripe width
                iTapeSize = RandomNumber%(LBound(arrWeight), UBound(arrWeight))
                arrStripe(index).weight = iPixelsPerInch * arrWeight(iTapeSize) * sngScale
               
                ' Stripe color
                arrStripe(index).fg = arrLayer(iLayer).fg
               
                ' Pick random start/end points
                arrStripe(index).x1 = RandomNumber%(0, _Width)
                arrStripe(index).y1 = RandomNumber%(0, _Height)
                arrStripe(index).x2 = RandomNumber%(0, _Width)
                arrStripe(index).y2 = RandomNumber%(0, _Height)

                ' Pick random movement for each end (clockwise or counter-clockwise)
                arrStripe(index).wise1 = _IIf(RandomNumber%(0, 1), 1, -1)
                arrStripe(index).wise2 = _IIf(RandomNumber%(0, 1), 1, -1)
                'arrStripe(index).wise1 = 1
                'arrStripe(index).wise2 = 1

                ' Pick which side of the screen the stripe begins at
                iValue1 = RandomNumber%(1, 4) ' (1=left, 2=right, 3=top, 4=bottom)

                ' Pick which side of the screen the stripe ends at (depends on where it starts)
                iValue2 = RandomNumber%(1, 3)

                ' Adjust begin & end points and movement direction of stripe depending on which side of screen
                If iValue1 = 1 Then
                    ' start off screen from left
                    arrStripe(index).x1 = 0 - arrStripe(index).x1
                    arrStripe(index).mx1 = 0
                    arrStripe(index).my1 = -1

                    If iValue2 = 1 Then
                        ' To right
                        arrStripe(index).x2 = arrStripe(index).x2 + _Width
                        arrStripe(index).mx2 = 0
                        arrStripe(index).my2 = 1

                    ElseIf iValue2 = 2 Then
                        ' To top
                        arrStripe(index).y2 = 0 - arrStripe(index).y2
                        arrStripe(index).mx2 = 1
                        arrStripe(index).my2 = 0

                    Else
                        ' To bottom
                        arrStripe(index).y2 = arrStripe(index).y2 + _Height
                        arrStripe(index).mx2 = -1
                        arrStripe(index).my2 = 0

                    End If
                ElseIf iValue1 = 2 Then
                    ' start off screen from right
                    arrStripe(index).x1 = arrStripe(index).x1 + _Width
                    arrStripe(index).mx1 = 0
                    arrStripe(index).my1 = 1

                    If iValue2 = 1 Then
                        ' To left
                        arrStripe(index).x2 = 0 - arrStripe(index).x2
                        arrStripe(index).mx2 = 0
                        arrStripe(index).my2 = -1

                    ElseIf iValue2 = 2 Then
                        ' To top
                        arrStripe(index).y2 = 0 - arrStripe(index).y2
                        arrStripe(index).mx2 = 1
                        arrStripe(index).my2 = 0

                    Else
                        ' To bottom
                        arrStripe(index).y2 = arrStripe(index).y2 + _Height
                        arrStripe(index).mx2 = -1
                        arrStripe(index).my2 = 0

                    End If
                ElseIf iValue1 = 3 Then
                    ' start off screen from top
                    arrStripe(index).y1 = 0 - arrStripe(index).y1
                    arrStripe(index).mx1 = 1
                    arrStripe(index).my1 = 0

                    If iValue2 = 1 Then
                        ' To bottom
                        arrStripe(index).y2 = arrStripe(index).y2 + _Height
                        arrStripe(index).mx2 = -1
                        arrStripe(index).my2 = 0

                    ElseIf iValue2 = 2 Then
                        ' To left
                        arrStripe(index).x2 = 0 - arrStripe(index).x2
                        arrStripe(index).mx2 = 0
                        arrStripe(index).my2 = -1

                    Else
                        ' To right
                        arrStripe(index).x2 = arrStripe(index).x2 + _Width
                        arrStripe(index).mx2 = 0
                        arrStripe(index).my2 = 1

                    End If
                Else
                    ' start off screen from bottom
                    arrStripe(index).y1 = arrStripe(index).y1 + _Height
                    arrStripe(index).mx1 = -1
                    arrStripe(index).my1 = 0

                    If iValue2 = 1 Then
                        ' To top
                        arrStripe(index).y2 = 0 - arrStripe(index).y2
                        arrStripe(index).mx2 = 1
                        arrStripe(index).my2 = 0

                    ElseIf iValue2 = 2 Then
                        ' To left
                        arrStripe(index).x2 = 0 - arrStripe(index).x2
                        arrStripe(index).mx2 = 0
                        arrStripe(index).my2 = -1

                    Else
                        ' To right
                        arrStripe(index).x2 = arrStripe(index).x2 + _Width
                        arrStripe(index).mx2 = 0
                        arrStripe(index).my2 = 1

                    End If
                End If

                ' SAVE INITIAL MOTION
                arrStripe(index).speed1 = 1 ' RandomNumber%(1, 5)
                arrStripe(index).dx1 = arrStripe(index).mx1 * arrStripe(index).speed1 * arrStripe(index).wise1
                arrStripe(index).dy1 = arrStripe(index).my1 * arrStripe(index).speed1 * arrStripe(index).wise1

                arrStripe(index).speed2 = arrStripe(index).speed1 ' RandomNumber%(1, 15)
                arrStripe(index).dx2 = arrStripe(index).mx2 * arrStripe(index).speed2 * arrStripe(index).wise2
                arrStripe(index).dy2 = arrStripe(index).my2 * arrStripe(index).speed2 * arrStripe(index).wise2

            Next n
        Next iLayer
        ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
        ' END CREATE STRIPES
        ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------

        ' ================================================================================================================================================================
        ' END INITIALIZE STRIPES
        ' ================================================================================================================================================================

        ' ================================================================================================================================================================
        ' BEGIN DRAW
        ' ================================================================================================================================================================
        ' CLEAR FLAGS FOR TIMED EVENTS
        bCycleColor = _FALSE
        bRepaint = _FALSE
        bMove = _TRUE
       
        ' CLEAR THE MASTER IMAGE
        Do

            ' $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
            ' TIMED EVENTS
            ' $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
            If iDelayMS > 0 Then
                If ExtendedTimer > NextTime Then
                    bMove = _TRUE
                    NextTime = ExtendedTimer + (iDelayMS * .001) ' SCHEDULE IT AGAIN IN 5 SECONDS
                End If
            End If

            If bMove = _TRUE Then

                _Dest image1
                Cls , _RGBA32(0, 0, 0, 0)

                ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
                ' BEGIN PAINT LAYERS
                ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
                ' START AT BOTTOM LAYER MOVING UP TO THE TOP
                For iLayer = LBound(arrLayer) To UBound(arrLayer)
                    ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                    ' BEGIN DRAW LAYER
                    ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                    ' CLEAR THE NEXT LAYER'S IMAGE
                    _Dest image2
                    Cls , arrLayer(iLayer).bg ' Lay down next layer's bg paint (or none if transparent)

                    ' Set next layer's stripe color (handle differently for transparent)
                    If arrLayer(iLayer).fg <> _RGBA32(0, 0, 0, 0) Then
                        ' Not transparent: just use the layer's fg color
                        fg = arrLayer(iLayer).fg
                    Else
                        ' Stripe color is transparent, use inverse color of layer's bg color
                        r = _Red32(arrLayer(iLayer).bg): g = _Green32(arrLayer(iLayer).bg): b = _Blue32(arrLayer(iLayer).bg)
                        fg = _RGB32(255 - r, 255 - g, 255 - b)
                    End If

                    ' DRAW NEXT LAYER'S STRIPES
                    'For index = LBound(arrStripe) To LBound(arrStripe)
                    For index = LBound(arrStripe) To UBound(arrStripe)

                        ' Ignore stripes from other layers
                        If arrStripe(index).layer = iLayer Then

                            ' FOR ANIMATION DON'T DO DIFFERENT COLOR STRIPES ON A LAYER
                            ' TODO: IF NOT ANIMATING ENABLE THIS
                            '' If transparent, use inverse color of bg
                            'if arrStripe(index).fg = _RGBA32(0, 0, 0, 0) then
                            '   r = _Red32(arrLayer(iLayer).bg): g = _Green32(arrLayer(iLayer).bg): b = _Blue32(arrLayer(iLayer).bg)
                            '   fg = _RGB32(255-r, 255-g, 255-b)
                            'end if

                            ' Draw the stripe
                            If arrStripe(index).x1 <> arrStripe(index).x2 And arrStripe(index).y1 <> arrStripe(index).y2 Then
                                RotoLine arrStripe(index).x1, arrStripe(index).y1, arrStripe(index).x2, arrStripe(index).y2, arrStripe(index).weight, fg
                                'ThickLine arrStripe(index).x1, arrStripe(index).y1, arrStripe(index).x2, arrStripe(index).y2, arrStripe(index).weight, fg
                            End If

                            ' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                            ' BEGIN MOVE IT!
                            ' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                            ' Move starting point
                            arrStripe(index).x1 = arrStripe(index).x1 + arrStripe(index).dx1
                            arrStripe(index).y1 = arrStripe(index).y1 + arrStripe(index).dy1

                            ' Check boundaries and if reached a corner then turn
                            bTurn = _FALSE
                            If arrStripe(index).x1 > _Width Then
                                arrStripe(index).x1 = _Width
                                arrStripe(index).mx1 = 0
                                arrStripe(index).my1 = 1
                                bTurn = _TRUE
                            End If
                            If arrStripe(index).y1 > _Height Then
                                arrStripe(index).y1 = _Height
                                arrStripe(index).mx1 = -1
                                arrStripe(index).my1 = 0
                                bTurn = _TRUE
                            End If
                            If arrStripe(index).x1 < 0 Then
                                arrStripe(index).x1 = 0
                                arrStripe(index).mx1 = 0
                                arrStripe(index).my1 = -1
                                bTurn = _TRUE
                            End If
                            If arrStripe(index).y1 < 0 Then
                                arrStripe(index).y1 = 0
                                arrStripe(index).mx1 = 1
                                arrStripe(index).my1 = 0
                                bTurn = _TRUE
                            End If

                            If arrStripe(index).x1 > _Width Then arrStripe(index).x1 = _Width
                            If arrStripe(index).x1 < 0 Then arrStripe(index).x1 = 0
                            If arrStripe(index).y1 > _Height Then arrStripe(index).y1 = _Height
                            If arrStripe(index).y1 < 0 Then arrStripe(index).y1 = 0

                            If bTurn = _TRUE Then
                                arrStripe(index).dx1 = arrStripe(index).mx1 * arrStripe(index).speed1 * arrStripe(index).wise1
                                arrStripe(index).dy1 = arrStripe(index).my1 * arrStripe(index).speed1 * arrStripe(index).wise1
                            End If

                            ' Move end point
                            arrStripe(index).x2 = arrStripe(index).x2 + arrStripe(index).dx2
                            arrStripe(index).y2 = arrStripe(index).y2 + arrStripe(index).dy2

                            ' Check boundaries and if reached a corner then turn
                            bTurn = _FALSE
                            If arrStripe(index).x2 > _Width Then
                                arrStripe(index).x2 = _Width
                                arrStripe(index).mx2 = 0
                                arrStripe(index).my2 = 1
                                bTurn = _TRUE
                            End If
                            If arrStripe(index).y2 > _Height Then
                                arrStripe(index).y2 = _Height
                                arrStripe(index).mx2 = -1
                                arrStripe(index).my2 = 0
                                bTurn = _TRUE
                            End If
                            If arrStripe(index).x2 < 0 Then
                                arrStripe(index).x2 = 0
                                arrStripe(index).mx2 = 0
                                arrStripe(index).my2 = -1
                                bTurn = _TRUE
                            End If
                            If arrStripe(index).y2 < 0 Then
                                arrStripe(index).y2 = 0
                                arrStripe(index).mx2 = 1
                                arrStripe(index).my2 = 0
                                bTurn = _TRUE
                            End If

                            If arrStripe(index).x2 > _Width Then arrStripe(index).x2 = _Width
                            If arrStripe(index).x2 < 0 Then arrStripe(index).x2 = 0
                            If arrStripe(index).y2 > _Height Then arrStripe(index).y2 = _Height
                            If arrStripe(index).y2 < 0 Then arrStripe(index).y2 = 0

                            If bTurn = _TRUE Then
                                arrStripe(index).dx2 = arrStripe(index).mx2 * arrStripe(index).speed2 * arrStripe(index).wise2
                                arrStripe(index).dy2 = arrStripe(index).my2 * arrStripe(index).speed2 * arrStripe(index).wise2
                            End If

                            ' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                            ' END MOVE IT!
                            ' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                        End If
                    Next index
                    ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                    ' END DRAW LAYER
                    ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                    ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                    ' ADD LAYER TO FINAL
                    ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                    _Dest image1

                    ' If transparent, use inverse color of bg
                    If arrLayer(iLayer).fg = _RGBA32(0, 0, 0, 0) Then
                        '_CLEARCOLOR {color&|_NONE}[, Dest_Handle&]
                        _ClearColor fg, image2
                    Else
                        _ClearColor _None, image2
                    End If

                    ' OVERLAY LAYER #2 OVER LAYER #1
                    _Dest image1
                    _PutImage (0, 0), image2, image1

                Next iLayer
               
                ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
                ' END PAINT LAYERS
                ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
               
               
                ' ================================================================================================================================================================
                ' BEGIN DRAW + MOVE VECTOR
                ' ================================================================================================================================================================
                ' INIT IMAGE ALL LOGOS WILL GET STACKED ON
                'TODO: TRACK Z-ORDER AND STACK IN ORDER OF FARTHEST TO NEAREST
                _Dest VectorImage: Cls , _RGBA32(0, 0, 0, 0)
               
                ' ADD EACH LOGO IN ORDER OF Z ORDER
                For index = iNumVectors To 0 Step -1
                    iWhichVector = arrShapeSort(index)
                    If iWhichVector > -1 Then
                        If arrScalingFactor(iWhichVector) > 0 Then
                            _Dest NextImage: Cls , _RGBA32(0, 0, 0, 0)
                           
                            ' GET NEXT LOGO'S COLOR
                            CurrentColor = arrColor(iWhichVector)
               
                            ' DRAW IT
                            iShapeCount = LBound(arrFillX) - 1
                            For iPoint = LBound(arrID) To UBound(arrID)
                                id = arrID(iPoint)
                                iType = arrType(iPoint)
                                x1 = (arrScreenX1(iPoint) * arrScalingFactor(iWhichVector)) '+ arrOffsetX(iWhichVector)
                                y1 = (arrScreenY1(iPoint) * arrScalingFactor(iWhichVector)) '+ arrOffsetY(iWhichVector)
                                x2 = (arrScreenX2(iPoint) * arrScalingFactor(iWhichVector)) '+ arrOffsetX(iWhichVector)
                                y2 = (arrScreenY2(iPoint) * arrScalingFactor(iWhichVector)) '+ arrOffsetY(iWhichVector)
                               
                                Select Case iType
                                    Case cDrawStart:
                                        'PSet (x1, y1), CurrentColor
                                        iShapeCount = iShapeCount + 1
                                    Case cDrawLine:
                                        Line (x1, y1)-(x2, y2), CurrentColor
                                        'RotoLine x1, y1, x2, y2, arrVectorWeight(iWhichVector), CurrentColor
                                    Case cLastLine:
                                        Line (x1, y1)-(x2, y2), CurrentColor
                                        'RotoLine x1, y1, x2, y2, arrVectorWeight(iWhichVector), CurrentColor
                                    Case cEndAll:
                                        ' EXIT LOOP
                                        Exit For
                                    Case Else:
                                        ' {DO NOTHING)
                                End Select
                            Next iPoint
                           
                            ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
                            ' NOW THAT THE SHAPE OUTLINE IS DRAWN,
                            ' USE FLOOD FILL (e.g., Paint) TO MAKE IT SOLID IN COLOR CurrentColor
                            ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
                            For iShapeCount = LBound(arrFillX) To UBound(arrFillX)
                                ' Get coords for fill point
                                xFill = arrFillX(iShapeCount) * arrScalingFactor(iWhichVector)
                                yFill = arrFillY(iShapeCount) * arrScalingFactor(iWhichVector)
                               
                                'PAINT [STEP] (column%, row%), fillColor[, borderColor%]
                                Paint (xFill, yFill), CurrentColor, CurrentColor
                               
                                ' OTHER FILL METHODS WHICH WEREN'T AS FAST:
                                'Paint2 xFill, yFill, CurrentColor
                                'PaintMask2 xFill, yFill, CurrentColor, _RGB32(255, 255, 255)
                            Next iShapeCount
                           
                            ' COPY SHAPE TO VECTOR LAYER
                            _PutImage (arrOffsetX(iWhichVector), arrOffsetY(iWhichVector)), NextImage, VectorImage
                           
                            ' TODO: instead of a solid color, use logo as a mask for the inverse color of the background
                           
                            ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
                            ' NOW MOVE THE SHAPE AND INCREASE THE SIZE
                            ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
                            ' MOVE IT X / Y (UP / DOWN / RIGHT / LEFT)
                            arrOffsetX(iWhichVector) = arrOffsetX(iWhichVector) + arrOffsetDX(iWhichVector)
                            arrOffsetY(iWhichVector) = arrOffsetY(iWhichVector) + arrOffsetDY(iWhichVector)
                           
                            '' ACCELERATE THE X / Y MOVEMENT AS THE SHAPE GETS CLOSER
                            'arrOffsetDX(iWhichVector) = arrOffsetDX(iWhichVector) * 2
                            'arrOffsetDY(iWhichVector) = arrOffsetDY(iWhichVector) * 2
                           
                            ' MOVE IT ALONG Z AXIS (FORWARD / BACK)
                            'arrScalingFactor(iWhichVector) = arrScalingFactor(iWhichVector) + 0.05
                            arrScalingFactor(iWhichVector) = arrScalingFactor(iWhichVector) + arrScaleIncrement(iWhichVector)
                           
                            'This was when we used the RotoLine method:
                            '' LINE GETS THICKER AS IT GETS CLOSER
                            ''arrVectorWeight(iWhichVector) = iPixelsPerInch * (1/8) * sngScale
                            'arrVectorWeight(iWhichVector) = arrVectorWeight(iWhichVector) * 1.01
                           
                            ' ACCELERATE SCALING FACTOR INCREMENT
                            arrScaleIncrement(iWhichVector) = arrScaleIncrement(iWhichVector) * 1.01
                            'arrScaleIncrement(iWhichVector) = arrScaleIncrement(iWhichVector) * 1.1
                           
                            ' HAS IT MOVED PAST US & TIME TO CREATE A NEW ONE?
                            'if arrScalingFactor(iWhichVector) >= sngMaxScale _orelse arrOffsetX(iWhichVector) > _width _orelse arrOffsetY(iWhichVector) > _height _orelse arrOffsetX(iWhichVector) < 0 _orelse arrOffsetY(iWhichVector) < 0 then
                            If arrScalingFactor(iWhichVector) >= sngMaxScale Then
                                ' KILL IT, MAKE AVAILABLE FOR RESPAWN
                                arrScalingFactor(iWhichVector) = 0
                            End If
                           
                        End If
                    End If
                Next index
                ' ================================================================================================================================================================
                ' END DRAW + MOVE VECTOR
                ' ================================================================================================================================================================
               
               
               
               
               
               
                ' ================================================================================================================================================================
                ' BEGIN SPAWN VECTOR
                ' ================================================================================================================================================================
                If bSpawn = _TRUE And bEnableFlyingVH = _TRUE Then
                    ' FIND AN UNUSED ONE
                    iWhichVector = LBound(arrScalingFactor) - 1
                    For index = LBound(arrScalingFactor) To UBound(arrScalingFactor)
                        If arrScalingFactor(index) = 0 Then
                            iWhichVector = index
                            Exit For
                        End If
                    Next index
                   
                    ' IF ONE IS AVAIALBLE THEN INITIALIZE
                    If iWhichVector >= LBound(arrScalingFactor) Then
                        ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
                        ' MOVE SPAWN POINT
                        VectorX = VectorX + VectorDX
                        If VectorX > _Width Then VectorX = 0 Else If VectorX < 0 Then VectorX = _Width
                        VectorY = VectorY + VectorDY
                        If VectorY > _Height Then VectorY = 0 Else If VectorY < 0 Then VectorY = _Height
                        ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
                        ' GET NEXT COLOR
                        iWhichColor = iWhichColor + 1
                        If iWhichColor > UBound(m_SpectrumColorArray) Then iWhichColor = LBound(m_SpectrumColorArray)
                        arrColor(iWhichVector) = m_SpectrumColorArray(iWhichColor)
                        ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
                        ' START LOGO SMALL
                        arrScalingFactor(iWhichVector) = .01
                        'sngScalingFactor = .01
                        ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
                        ' SCALE INCREMENT = how fast it moves closer
                        ' halfway = 500, to get to 0.05, multiply by what? 500x = .05, x = .05/500 = .0001
                        arrScaleIncrement(iWhichVector) = sngScaleIncrement
                        '' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
                        '' START LINE WEIGHT THIN
                        ''arrVectorWeight(iWhichVector) = (iPixelsPerInch * (1/8) * sngScale) * .25
                        'arrVectorWeight(iWhichVector) = 1
                        ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
                        ' X POSITION
                        arrOffsetX(iWhichVector) = VectorX
                        ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
                        ' X MOVEMENT
                        'if RandomNumber%(0, 1) = 0 then
                        If VectorDX < 0 Then
                            arrOffsetDX(iWhichVector) = 1
                        Else
                            arrOffsetDX(iWhichVector) = -1
                        End If
                        ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
                        ' Y POSITION
                        arrOffsetY(iWhichVector) = VectorY
                        ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
                        ' Y MOVEMENT
                        'if RandomNumber%(0, 1) = 0 then
                        If VectorDY < 0 Then
                            arrOffsetDY(iWhichVector) = 1
                        Else
                            arrOffsetDY(iWhichVector) = -1
                        End If
                        ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
                        ' RE-CALCULATE Z-ORDER FOR SHAPES
                        For iWhichVector = 0 To iNumVectors
                            arrZOrder(iWhichVector) = 0
                            arrShapeSort(iWhichVector) = -1
                        Next iWhichVector
                        iOrder = 0
                        Do
                            BiggestShapeScale = 0
                            BiggestShapeIndex = -1
                            For iWhichVector = 0 To iNumVectors
                                ' Only enabled shapes
                                If arrScalingFactor(iWhichVector) > 0 Then
                                    ' Only unsorted shapes
                                    If arrZOrder(iWhichVector) = 0 Then
                                        If arrScalingFactor(iWhichVector) > BiggestShapeScale Then
                                            BiggestShapeScale = arrScalingFactor(iWhichVector)
                                            BiggestShapeIndex = iWhichVector
                                        End If
                                    End If
                                End If
                            Next iWhichVector
                            If BiggestShapeIndex >= 0 Then
                                iOrder = iOrder + 1
                                arrZOrder(BiggestShapeIndex) = iOrder
                                arrShapeSort(iOrder - 1) = BiggestShapeIndex
                            Else
                                Exit Do
                            End If
                        Loop
                       
                        ' ****************************************************************************************************************************************************************
                        ' BEGIN DEBUG
                        ' ****************************************************************************************************************************************************************
                        'PrintDebugFile "--------------------------------------------------------------------------------"
                        'PrintDebugFile _
                        '    PadLeft$( "Z-Order" + " " + _
                        '    PadLeft$( "Index") + " " + _
                        '    PadLeft$( "ScalingFactor" + " " + _
                        '    ""
                        'For iWhichVector = 0 To iNumVectors
                        '    PrintDebugFile _
                        '        PadLeft$( _ToStr$(arrZOrder(iWhichVector)), 7) + " " + _
                        '        PadLeft$( _ToStr$(iWhichVector) , 5) + " " + _
                        '        PadLeft$( SngRoundedToStr$(arrScalingFactor(iWhichVector), 3), 13) + " " + _
                        '        ""
                        'Next iWhichVector
                        ' ****************************************************************************************************************************************************************
                        ' END DEBUG
                        ' ****************************************************************************************************************************************************************
                       
                        ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
                        ' UNTIL WE MEET AGAIN!
                        bSpawn = _FALSE
                    End If
                End If
                ' ================================================================================================================================================================
                ' END SPAWN VECTOR
                ' ================================================================================================================================================================

                ' COPY FINAL TO SCREEN
                _Dest 0
                _PutImage (0, 0), image1, 0
                _PutImage (0, 0), VectorImage, 0

                ' ****************************************************************************************************************************************************************
                ' BEGIN DEBUGGING CODE
                ' ****************************************************************************************************************************************************************
                '_Dest 0
                'Color White, Blue
                'For iShapeCount = LBound(arrFillX) To UBound(arrFillX)
                '    in$ = in$ + "arrFillX(" + _ToStr$(iShapeCount) + ")=" + _ToStr$(arrFillX(iShapeCount)) + " "
                '    in$ = in$ + "arrFillY(" + _ToStr$(iShapeCount) + ")=" + _ToStr$(arrFillY(iShapeCount)) + " "
                'Next iShapeCount
                'in$ = "LBound(arrFillX=" + _ToStr$(LBound(arrFillX)) + " UBound(arrFillX)=" + _ToStr$(UBound(arrFillX))
                ''in$ = "VectorX=" + _ToStr$(VectorX) + " VectorY=" + _ToStr$(VectorY)
                '_PrintString (100, 100), "HELLO: " + in$
                ' ****************************************************************************************************************************************************************
                ' END DEBUGGING CODE
                ' ****************************************************************************************************************************************************************
                ' ****************************************************************************************************************************************************************
                ' BEGIN DEBUGGING CODE
                ' ****************************************************************************************************************************************************************
                '' SEE LINE POSITION:
                'Color fgPromptColor, bgPromptColor
                'index = 0
                'Locate 10, 1
                'Print "x1=" + _ToStr$(arrStripe(index).x1)
                'Print "y1=" + _ToStr$(arrStripe(index).y1)
                'Print "x2=" + _ToStr$(arrStripe(index).x2)
                'Print "y2=" + _ToStr$(arrStripe(index).y2)
                'Locate 15, 1
                'Print "dx1=" + _ToStr$(arrStripe(index).dx1)
                'Print "dy1=" + _ToStr$(arrStripe(index).dy1)
                'Print "dx2=" + _ToStr$(arrStripe(index).dx2)
                'Print "dy2=" + _ToStr$(arrStripe(index).dy2)
                '
                '' SEE SPEED DELAY VALUE + ANIMATION FLAGS
                'Color fgPromptColor, bgPromptColor
                'Locate 20, 1
                'Print "iDelayMS=" + _ToStr$(iDelayMS)
                'Print "bMove   =" + TrueFalse$(bMove)
                'Print "bEnableAnimation=" + TrueFalse$(bEnableAnimation)
                ' ****************************************************************************************************************************************************************
                ' END DEBUGGING CODE
                ' ****************************************************************************************************************************************************************
               
                ' REFRESH DISPLAY
                _Display

                ' WAIT FOR NEXT TIMER EVENT
                If iDelayMS > 0 Then bMove = _FALSE
            End If

            ' ================================================================================================================================================================
            ' END DRAW
            ' ================================================================================================================================================================

            ' ================================================================================================================================================================
            ' BEGIN WHAT NEXT
            ' ================================================================================================================================================================

            ' =============================================================================
            ' SHOW RESULTS + WAIT FOR USER
            ' =============================================================================
            If bMiniMenuIsVisible = _TRUE Then
                't2 = Timer
                'Locate MaxRow - 2, 1: Print _ToStr$(t2 - t1) + " seconds to draw " + _ToStr$(iStripeCount) + " randomly generated lines.";
                bRefresh = _TRUE
                bSavedJPG = _FALSE
                bSavedPNG = _FALSE

                sPrompt = "PRESS " + _
                    _IIF(bSavedJPG=_FALSE, "J TO SAVE JPG, ", Space$(15)) + _
                    _IIF(bSavedPNG=_FALSE, "P TO SAVE PNG, ", Space$(15)) + _
                    "N/M TO HIDE/SHOW THIS MENU, ENTER TO REPAINT, SPACE FOR OPTIONS, ESC TO QUIT" + _
                    ""
                Color fgPromptColor, bgPromptColor
                Locate MaxRow - 2, (MaxCol - Len(sPrompt)) / 2: Print sPrompt;
                Color _RGBA32(0, 0, 0, 0), _RGBA32(0, 0, 0, 0)

                _Display
            Else
                ' SHOW KEYS TO EXIT IN TITLE BAR
                _Title "Van Halenizer v2.02 - SPACE=MAIN MENU, UP/DOWN=SPEED, HOME/END=TOGGLE FULLSCREEN, ESC=QUIT"
            End If

            ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
            ' BEGIN GET USER INPUT
            ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
            If bEnableAnimation = _TRUE Then
                ' WHILE ANIMATION IS ENABLED
                ' CHECK FOR INPUT SO USER CAN RETURN TO MAIN MENU, ETC.

                ' M = ENABLE, N=DISABLE MENU
                If _KeyDown(cUpperM) Or _KeyDown(cLowerM) Then
                    bMiniMenuIsVisible = _TRUE
                    bRefresh = _TRUE
                ElseIf _KeyDown(cUpperN) Or _KeyDown(cLowerN) Then
                    bMiniMenuIsVisible = _FALSE
                    bRefresh = _TRUE
                End If

                ' UP/DOWN = SLOW DOWN/SPEED UP
                If _KeyDown(cDownKD) Then
                    iDelayMS = iDelayMS + 1
                    If iDelayMS > 60000 Then iDelayMS = 60000
                    'bMove = _TRUE
                    NextTime = ExtendedTimer + (iDelayMS * .001) ' SCHEDULE IT AGAIN IN 5 SECONDS
                ElseIf _KeyDown(cUpKD) Then
                    iDelayMS = iDelayMS - 1
                    If iDelayMS < 0 Then iDelayMS = 0
                    bMove = _TRUE
                    NextTime = ExtendedTimer + (iDelayMS * .001) ' SCHEDULE IT AGAIN IN 5 SECONDS
                End If

                ' HOME = DECREASE TIME, END = INCREASE TIME
                If _KeyDown(cHome) Then
                    iCycleEverySeconds = iCycleEverySeconds + 1
                    If iCycleEverySeconds > 86400 Then iCycleEverySeconds = 86400 ' Max time = 1 day
                    bRefresh = _TRUE
                ElseIf _KeyDown(cEnd) Then
                    iCycleEverySeconds = iCycleEverySeconds - 1
                    If iCycleEverySeconds < 0 Then iCycleEverySeconds = 0 ' 0 means color scheme doesn't cycle
                    bRefresh = _TRUE
                End If

                ' for some reason enable/disable animation while graphics are displayed doesn't work right
                ' * if we disable animate, it doesn't display the Save menu, have to press space to go back to the main menu
                ' * if we enable animate, everything freezes and won't unfreeze unless we goto PaintGuitar
                '' A/Z = ENABLE / DISABLE ANIMATE
                'If _KeyDown(cUpperA) Or _KeyDown(cLowerA) Then
                '    bEnableAnimation = _TRUE
                '   bRefresh = _TRUE : sPrompt = ""
                '   GoTo PaintGuitar
                'ElseIf _KeyDown(cUpperZ) Or _KeyDown(cLowerZ) Then
                '    bEnableAnimation = _FALSE
                '   bRefresh = _TRUE : sPrompt = "" : _Title "Van Halenizer v2.02"
                '   GoTo PaintGuitar
                'End If

                ' ENTER = REPAINT
                ' Did they press Enter (makes sure Enter key was released first)
                If _KeyDown(cEnter) Then
                    If arrKeyState(KeyCode_Enter) = _FALSE Then
                        arrKeyState(KeyCode_Enter) = _TRUE
                        GoTo ResetTimer
                    End If
                Else
                    arrKeyState(KeyCode_Enter) = _FALSE
                End If

                ' SPACE = RETURN TO MENU
                If _KeyDown(cSpace) Then GoTo LoopBackToMainMenu ' Exit Do

                ' ESC = QUIT
                If _KeyDown(cEsc) Then GoTo CleanupAndExit

                ' $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
                ' TIMED EVENT
                ' $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
                If iCycleEverySeconds > 0 Then
                    If ExtendedTimer > NextColor Then
                        ' CYCLE COLOR SCHEME
                        iColorSchemeIndex = iColorSchemeIndex + 1
                        If iColorSchemeIndex > UBound(arrColorScheme) Then iColorSchemeIndex = LBound(arrColorScheme)
                        NextColor = ExtendedTimer + iCycleEverySeconds ' SCHEDULE COLOR SCHEME TO CHANGE IN iCycleEverySeconds SECONDS
                        NextRepaint = ExtendedTimer + iRepaintEverySeconds ' SCHEDULE REPAINT IN iRepaintEverySeconds SECONDS
                        bCycleColor = _TRUE
                        GoTo ChangeColorScheme
                    End If
                End If
               
                If iRepaintEverySeconds > 0 Then
                    If ExtendedTimer > NextRepaint Then
                        ' PAINT A NEW ONE
                        NextRepaint = ExtendedTimer + iRepaintEverySeconds ' SCHEDULE REPAINT IN iRepaintEverySeconds SECONDS
                        bRepaint = _TRUE
                        GoTo PaintGuitar
                        'GoTo ChangeColorScheme
                    End If
                End If
               
                If iVectorEveryMS > 0 Then
                    If ExtendedTimer > NextVector Then
                        ' SPAWN A NEW VECTOR
                        NextVector = ExtendedTimer + iVectorEveryMS * 0.001 ' SCHEDULE NEW VECTOR SPAWNED IN iVectorEveryMS MILLISECONDS
                        bSpawn = _TRUE
                    End If
                End If
               
            Else
                Do
                    ' M = ENABLE, N=DISABLE MENU
                    If _KeyDown(cUpperM) Or _KeyDown(cLowerM) Then
                        bMiniMenuIsVisible = _TRUE
                        bRefresh = _TRUE
                    ElseIf _KeyDown(cUpperN) Or _KeyDown(cLowerN) Then
                        bMiniMenuIsVisible = _FALSE
                        bRefresh = _TRUE
                    End If

                    ' J = SAVE TO JPEG (IF NOT ALREADY SAVED TO JPEG)
                    If _KeyDown(cUpperJ) Or _KeyDown(cLowerJ) Then
                        If bSavedJPG = _FALSE Then
                            bSavedJPG = SaveImage%%(image1, "JPG")
                            If bSavedJPG Then sPrompt = "" : beep
                        End If
                    End If

                    ' P = SAVE TO PNG (IF NOT ALREADY SAVED TO PNG)
                    If _KeyDown(cUpperP) Or _KeyDown(cLowerP) Then
                        If bSavedPNG = _FALSE Then
                            bSavedPNG = SaveImage%%(image1, "PNG")
                            If bSavedPNG Then sPrompt = "" : beep
                        End If
                    End If

                    ' HOME = DECREASE TIME, END = INCREASE TIME
                    If _KeyDown(cHome) Then
                        iCycleEverySeconds = iCycleEverySeconds + 1
                        If iCycleEverySeconds > 86400 Then iCycleEverySeconds = 86400 ' Max time = 1 day
                        bRefresh = _TRUE
                    ElseIf _KeyDown(cEnd) Then
                        iCycleEverySeconds = iCycleEverySeconds - 1
                        If iCycleEverySeconds < 0 Then iCycleEverySeconds = 0 ' 0 means color scheme doesn't cycle
                        bRefresh = _TRUE
                    End If

                    '' for some reason if we disable animate, it doesn't display the Save menu
                    '' A = (RE)ENABLE ANIMATE
                    'If _KeyDown(cUpperA) Or _KeyDown(cLowerA) Then
                    '    'Sound 800, .15 ' iPitch% = 800
                    '    bEnableAnimation = _TRUE
                    '   bRefresh = _TRUE : sPrompt = "" : _Title "Van Halenizer v2.02"
                    '    'NextTime = ExtendedTimer + 1 ' SCHEDULE IT AGAIN IN 1 SECOND
                    '    'iDelayMS = 0 ' reset delay
                    '    'sPrompt = ""
                    '    ' for some reason everything freezes and won't unfreeze unless we goto PaintGuitar
                    '    GoTo PaintGuitar
                    'End If

                    ' ENTER = REPAINT
                    If _KeyDown(cEnter) Then
                        If arrKeyState(KeyCode_Enter) = _FALSE Then
                            arrKeyState(KeyCode_Enter) = _TRUE
                            bSavedJPG = _FALSE
                            bSavedPNG = _FALSE
                            GoTo ResetTimer
                        End If
                    Else
                        arrKeyState(KeyCode_Enter) = _FALSE
                    End If

                    ' SPACE = RETURN TO MENU
                    If _KeyDown(cSpace) Then GoTo LoopBackToMainMenu ' Exit Do

                    ' ESC = QUIT
                    If _KeyDown(cEsc) Then GoTo CleanupAndExit

                    ' Redraw prompt?
                    If Len(sPrompt) = 0 Then
                        sPrompt = "PRESS " + _
                            _IIF(bSavedJPG=_FALSE, "J TO SAVE JPG, ", Space$(15)) + _
                            _IIF(bSavedPNG=_FALSE, "P TO SAVE PNG, ", Space$(15)) + _
                            "ENTER TO REPAINT, SPACE FOR OPTIONS, ESC TO QUIT" + _
                            ""
                        Color fgPromptColor, bgPromptColor
                        Locate MaxRow - 2, (MaxCol - Len(sPrompt)) / 2: Print sPrompt
                    End If

                    ' $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
                    ' TIMED EVENT
                    ' $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
                    If iCycleEverySeconds > 0 Then
                        If ExtendedTimer > NextColor Then
                            ' CYCLE COLOR SCHEME
                            iColorSchemeIndex = iColorSchemeIndex + 1
                            If iColorSchemeIndex > UBound(arrColorScheme) Then iColorSchemeIndex = LBound(arrColorScheme)
                            NextColor = ExtendedTimer + iCycleEverySeconds ' SCHEDULE COLOR SCHEME TO CHANGE IN iCycleEverySeconds SECONDS
                            NextRepaint = ExtendedTimer + iRepaintEverySeconds ' SCHEDULE REPAINT IN iRepaintEverySeconds SECONDS
                            bCycleColor = _TRUE
                            GoTo ChangeColorScheme
                        End If
                    End If
                    If iRepaintEverySeconds > 0 Then
                        If ExtendedTimer > NextRepaint Then
                            ' PAINT A NEW ONE
                            NextRepaint = ExtendedTimer + iRepaintEverySeconds ' SCHEDULE REPAINT IN iRepaintEverySeconds SECONDS
                            bRepaint = _TRUE
                            GoTo PaintGuitar
                            'GoTo ChangeColorScheme
                        End If
                    End If

                Loop
            End If
            ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
            ' END GET USER INPUT
            ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------

            ' UPDATE SCREEN
            _Display
            _Limit 60
        Loop
        ' ================================================================================================================================================================
        ' END WHAT NEXT
        ' ================================================================================================================================================================


        ' START OVER AT MAIN MENU
        LoopBackToMainMenu:

    Loop
    ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    ' END MAIN LOOP
    ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    CleanupAndExit:

    ' SAVE SETTINGS TO CONFIG FILE
    sNextError = SaveConfig$
    If Len(sNextError) > 0 Then
        Cls , Black
        Color White, Red
        Print sNextError
    End If

    ' RESET DISPLAY AND CLEAR IMAGES
    _AutoDisplay
    FreeImage image1
    FreeImage image2
    FreeImage VectorImage
    FreeImage NextImage

    If _FullScreen Then _FullScreen _Off
   
    ' BEGIN DEBUG
    'in$ = _InputBox$(m_ProgramName, "debug file", m_ProgramPath$ + m_ProgramName$ + ".txt")
    ' END DEBUG
   
End Sub ' VanHalenizer





' /////////////////////////////////////////////////////////////////////////////
' based on
'
' bplus: The Gold Standard is even better than THE QB64 CIRCLE sub in this respect!
' https://forum.qb64.org/index.php?topic=1044.135

' from Steve Gold standard
' Renamed fcirc to DrawCircleSolid

' Not as fast as DrawCircleTopLeft but pretty fast.

' Example:
' Screen _NewImage(800, 600, 32)
' _ScreenMove 250, 60
' For r = 250 To 0 Step -60
'     DrawCircleSolid 400, 300, r, _RGBA(255, 255, 255, 100)
' Next r

'DrawSolidCircle 0, iX, iY, iRadius, fgColor
Sub DrawSolidCircle (TargetImage As Long, iX As Long, iY As Long, iRadius As Integer, fgColor As _Unsigned Long)
    Dim iNextRadius As Integer
    Dim iRadiusError As Integer
    Dim iNextX As Integer
    Dim iNextY As Integer

    'If TargetImage < -1 _orelse TargetImage > 0 then
    If (TargetImage <= 0) Then
        ' Select target image
        _Dest TargetImage ': Cls , cEmpty

        ' Draw circle fill
        iNextRadius = Abs(iRadius)
        iRadiusError = -iNextRadius
        iNextX = iNextRadius
        iNextY = 0
        If iNextRadius = 0 Then
            PSet (iX, iY), fgColor
        Else
            ' Draw the middle span here so we don't draw it twice in the main loop,
            ' which would be a problem with blending turned on.
            Line (iX - iNextX, iY)-(iX + iNextX, iY), fgColor, BF
            While iNextX > iNextY
                iRadiusError = iRadiusError + iNextY * 2 + 1
                If iRadiusError >= 0 Then
                    If iNextX <> iNextY + 1 Then
                        Line (iX - iNextY, iY - iNextX)-(iX + iNextY, iY - iNextX), fgColor, BF
                        Line (iX - iNextY, iY + iNextX)-(iX + iNextY, iY + iNextX), fgColor, BF
                    End If
                    iNextX = iNextX - 1
                    iRadiusError = iRadiusError - iNextX * 2
                End If
                iNextY = iNextY + 1
                Line (iX - iNextX, iY - iNextY)-(iX + iNextX, iY - iNextY), fgColor, BF
                Line (iX - iNextX, iY + iNextY)-(iX + iNextX, iY + iNextY), fgColor, BF
            Wend
        End If
    End If
End Sub ' DrawSolidCircle

' /////////////////////////////////////////////////////////////////////////////

Function DataTypeToString$ (MyValue As Integer)
    Dim MyString As String
    Select Case MyValue
        Case cDrawStart:
            MyString = "cDrawStart"
        Case cDrawLine:
            MyString = "cDrawLine"
        Case cLastLine:
            MyString = "cLastLine"
        Case cEndAll:
            MyString = "cEndAll"
        Case Else:
            MyString = _ToStr$(MyValue)
    End Select
    DataTypeToString$ = MyString
End Function ' DataTypeToString$

' /////////////////////////////////////////////////////////////////////////////

Function GetDefaultScale%
    'iScaleToPercent = cScaleToPercent
    If _DesktopWidth >= 3840 Then
        GetDefaultScale% = 125
    ElseIf _DesktopWidth >= 1920 Then
        GetDefaultScale% = 110
    ElseIf _DesktopWidth >= 1280 Then
        GetDefaultScale% = 100
    ElseIf _DesktopWidth >= 1024 Then
        GetDefaultScale% = 80
    ElseIf _DesktopWidth >= 800 Then
        GetDefaultScale% = 60
    Else
        GetDefaultScale% = 50
    End If
End Function ' GetDefaultScale%

' /////////////////////////////////////////////////////////////////////////////

Function LoadConfig$
    Dim sError As String
    Dim sNextError As String
    Dim iLoop1 As Integer
    Dim iLoop2 As Integer
    ReDim NameArray$(-1 To -1)
    ReDim ValuArray$(-1 To -1)
    Dim sCompareName As String
    Dim sCompareValu As String
    Dim MyInteger As Integer
    Dim MyLong As Long
    Dim MyByte As _Byte
   
    ' READ IN NAMES + VALUES AS TEXT FROM CONFIG FILE
    sError = ReadConfig$(NameArray$(), ValuArray$())
   
    ' DID VALUES LOAD?
    If Len(sError) = 0 Then
        ' POPULATE VARIABLES WITH CONVERTED VALUES
       
        ' NAME                      VARIABLE                  VALUE RANGE                 TYPE
        ' -----------------------   ---------------           ------------                -------
        ' ScreenWidth               iScreenWidth              0 or 640 - _DesktopWidth    Long
        ' ScreenHeight              iScreenHeight             0 or 480 - _DesktopHeight   Long
        ' ScaleToPercent            iScaleToPercent           1-1000 (percent)            Integer
        ' EnableAnimation           bEnableAnimation          True / False                _Byte
        ' DelayMS                   iDelayMS                  0-60000 (ms)                Long
        ' MiniMenuIsVisible         bMiniMenuIsVisible        True / False                _Byte
        ' FilenameTemplate          sFilenameTemplate         (any valid filename)        String
        ' EnableSaveResultsPopup    bEnableSaveResultsPopup   True / False                _Byte
        ' CycleEverySeconds         iCycleEverySeconds        0-86400 (seconds)           Long
        ' RepaintEverySeconds       iRepaintEverySeconds      0-3600 (seconds)            Long
        ' ColorSchemeIndex          iColorSchemeIndex         0-7                         Integer
        ' BaseStripeCount           iBaseStripeCount          1-500                       Integer
        ' StripeVariance            iStripeVariance           1-250                       Integer
        ' -----------------------   ---------------           ------------                -------
        ' EnableFlyingVH            bEnableFlyingVH           True / False                _Byte
        ' NumVectors                iNumVectors               1-100                       Integer
        ' VectorEveryMS             iVectorEveryMS            50-9999 (ms)                Integer
        ' MinDX                     iMinDX                    1-8192 (px)                 Integer
        ' MaxDX                     iMaxDX                    1-8192 (px)                 Integer
        ' MaxScale                  iMaxScale                 1-1000 (percent)            Integer
        ' ScaleIncrement            iScaleIncrement           1000-5000 (micro %)         Long
       
        ' TODO: turn all this validation into a reusable function(sCompareValu, bStripMinus, iMinLen, iMaxLen, iMinValue, iMaxValue)
        For iLoop1 = LBound(NameArray$) To UBound(NameArray$)
            sCompareName = LCase$(NameArray$(iLoop1))
            sCompareValu = LCase$(_Trim$(ValuArray$(iLoop1)))
            sCompareValu = Replace$(sCompareValu, "-", "")
            sCompareValu = Replace$(sCompareValu, " ", "")
            Select Case sCompareName
                Case LCase$("ScreenWidth"):
                    If IsNumber%(sCompareValu) Then
                        If Len(sCompareValu) <= 4 Then
                            iScreenWidth = _Cast(Long, Val(sCompareValu))
                            If iScreenWidth < 640 Then iScreenWidth = _DesktopWidth Else If iScreenWidth > _DesktopWidth Then iScreenWidth = _DesktopWidth
                        Else
                            iScreenWidth = _DesktopWidth
                        End If
                    Else
                        iScreenWidth = _DesktopWidth
                    End If
                Case LCase$("ScreenHeight"):
                    If IsNumber%(sCompareValu) Then
                        If Len(sCompareValu) <= 4 Then
                            iScreenHeight = _Cast(Long, Val(sCompareValu))
                            If iScreenHeight < 480 Then iScreenHeight = _DesktopHeight Else If iScreenHeight > _DesktopHeight Then iScreenHeight = _DesktopHeight
                        Else
                            iScreenHeight = _DesktopHeight
                        End If
                    Else
                        iScreenHeight = _DesktopHeight
                    End If

                Case LCase$("ScaleToPercent"):
                    If IsNumber%(sCompareValu) Then
                        If Len(sCompareValu) <= 4 Then
                            iScaleToPercent = _Cast(Integer, Val(sCompareValu))
                            If iScaleToPercent < 1 Then iScaleToPercent = GetDefaultScale% Else If iScaleToPercent > 1000 Then iScaleToPercent = GetDefaultScale%
                        Else
                            iScaleToPercent = GetDefaultScale%
                        End If
                    Else
                        iScaleToPercent = GetDefaultScale%
                    End If

                Case LCase$("EnableAnimation"):
                    bEnableAnimation = (sCompareValu = "true")

                Case LCase$("DelayMS"):
                    If IsNumber%(sCompareValu) Then
                        If Len(sCompareValu) <= 5 Then
                            iDelayMS = _Cast(Long, Val(sCompareValu))
                            If iDelayMS < 0 Then iDelayMS = cDelayMS Else If iDelayMS > 86400 Then iDelayMS = cDelayMS
                        Else
                            iDelayMS = cDelayMS
                        End If
                    Else
                        iDelayMS = cDelayMS
                    End If

                Case LCase$("MiniMenuIsVisible"):
                    bMiniMenuIsVisible = (sCompareValu = "true")

                Case LCase$("FilenameTemplate"):
                    sFilenameTemplate = _IIf(Len(sCompareValu) > 0, sCompareValu, cFilenameTemplate)
               
                Case LCase$("EnableSaveResultsPopup"):
                    bEnableSaveResultsPopup = (sCompareValu = "true")
               
                Case LCase$("CycleEverySeconds"):
                    If IsNumber%(sCompareValu) Then
                        If Len(sCompareValu) <= 5 Then
                            iCycleEverySeconds = _Cast(Long, Val(sCompareValu))
                            If iCycleEverySeconds < 0 Then iCycleEverySeconds = cCycleEverySeconds Else If iCycleEverySeconds > 86400 Then iCycleEverySeconds = cCycleEverySeconds
                        Else
                            iCycleEverySeconds = cCycleEverySeconds
                        End If
                    Else
                        iCycleEverySeconds = cCycleEverySeconds
                    End If
               
                Case LCase$("RepaintEverySeconds"):
                    If IsNumber%(sCompareValu) Then
                        If Len(sCompareValu) <= 5 Then
                            iRepaintEverySeconds = _Cast(Integer, Val(sCompareValu))
                            If iRepaintEverySeconds < 0 Then iRepaintEverySeconds = cRepaintEverySeconds Else If iRepaintEverySeconds > 3000 Then iRepaintEverySeconds = cRepaintEverySeconds
                        Else
                            iRepaintEverySeconds = cRepaintEverySeconds
                        End If
                    Else
                        iRepaintEverySeconds = cRepaintEverySeconds
                    End If
               
                Case LCase$("ColorSchemeIndex"):
                    If IsNumber%(sCompareValu) Then
                        If Len(sCompareValu) <= 4 Then
                            iColorSchemeIndex = _Cast(Integer, Val(sCompareValu))
                            'TODO: test iColorSchemeIndex against boundaries of array
                        Else
                            iColorSchemeIndex = cColorSchemeIndex
                        End If
                    Else
                        iColorSchemeIndex = cColorSchemeIndex
                    End If
               
                Case LCase$("BaseStripeCount"):
                    If IsNumber%(sCompareValu) Then
                        If Len(sCompareValu) <= 4 Then
                            iBaseStripeCount = _Cast(Long, Val(sCompareValu))
                            If iBaseStripeCount < 1 Then iBaseStripeCount = cBaseStripeCount Else If iBaseStripeCount > 500 Then iBaseStripeCount = cBaseStripeCount
                        Else
                            iBaseStripeCount = cBaseStripeCount
                        End If
                    Else
                        iBaseStripeCount = cBaseStripeCount
                    End If
                   
                Case LCase$("StripeVariance"):
                    If IsNumber%(sCompareValu) Then
                        If Len(sCompareValu) <= 4 Then
                            iStripeVariance = _Cast(Long, Val(sCompareValu))
                            If iStripeVariance < 1 Then iStripeVariance = cStripeVariance Else If iStripeVariance > 250 Then iStripeVariance = cStripeVariance
                        Else
                            iStripeVariance = cStripeVariance
                        End If
                    Else
                        iStripeVariance = cStripeVariance
                    End If
                   
                    ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
               
                Case LCase$("EnableFlyingVH"):
                    bEnableFlyingVH = (sCompareValu = "true")
               
                Case LCase$("NumVectors"):
                    If IsNumber%(sCompareValu) Then
                        If Len(sCompareValu) <= 4 Then
                            iNumVectors = _Cast(Integer, Val(sCompareValu))
                        Else
                            iNumVectors = cNumVectors
                        End If
                    Else
                        iNumVectors = cNumVectors
                    End If

                Case LCase$("VectorEveryMS"):
                    If IsNumber%(sCompareValu) Then
                        If Len(sCompareValu) <= 4 Then
                            iVectorEveryMS = _Cast(Integer, Val(sCompareValu))
                        Else
                            iVectorEveryMS = cVectorEveryMS
                        End If
                    Else
                        iVectorEveryMS = cVectorEveryMS
                    End If

                Case LCase$("MinDX"):
                    If IsNumber%(sCompareValu) Then
                        If Len(sCompareValu) <= 4 Then
                            iMinDX = _Cast(Integer, Val(sCompareValu))
                        Else
                            iMinDX = cMinDX
                        End If
                    Else
                        iMinDX = cMinDX
                    End If

                Case LCase$("MaxDX"):
                    If IsNumber%(sCompareValu) Then
                        If Len(sCompareValu) <= 4 Then
                            iMaxDX = _Cast(Integer, Val(sCompareValu))
                        Else
                            iMaxDX = cMaxDX
                        End If
                    Else
                        iMaxDX = cMaxDX
                    End If

                Case LCase$("MaxScale"):
                    If IsNumber%(sCompareValu) Then
                        If Len(sCompareValu) <= 4 Then
                            iMaxScale = _Cast(Integer, Val(sCompareValu))
                        Else
                            iMaxScale = cMaxScale
                        End If
                    Else
                        iMaxScale = cMaxScale
                    End If

                Case LCase$("ScaleIncrement"):
                    If IsNumber%(sCompareValu) Then
                        If Len(sCompareValu) <= 4 Then
                            iScaleIncrement = _Cast(Long, Val(sCompareValu))
                        Else
                            iScaleIncrement = cScaleIncrement
                        End If
                    Else
                        iScaleIncrement = cScaleIncrement
                    End If
                    ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
                   
            End Select
        Next iLoop1
    Else
        ' VALUES DIDN'T LOAD - WRITE A NEW CONFIG FILE
        sNextError = SaveConfig$
        If Len(sNextError) = 0 Then
            'sError = "ReadConfig$ failed: " + sError + chr$(13) + "Saved settings to new config file."
            sError = ""
        Else
            sError = "ReadConfig$ failed: " + sError + Chr$(13) + "SaveConfig$ failed: " + sNextError
        End If
    End If
   
    LoadConfig$ = sError
End Function ' LoadConfig$

' /////////////////////////////////////////////////////////////////////////////
' NameValueArray$ must be declared:
' ReDim NameValueArray$(-1, 0 To 1)

Function ReadConfig$ (NameArray$(), ValuArray$())
    Dim RoutineName As String:: RoutineName = "ReadConfig$"
    Dim sError As String: sError = ""
    Dim sFName As String
    Dim sFile As String
    Dim sLine As String
    Dim sName As String
    Dim sValue As String
    'Dim iLineNum As Integer
   
    ' Get file name
    If Len(sError) = 0 Then
        sFName = Left$(m_ProgramName$, _InStrRev(m_ProgramName$, ".")) + "cfg"
        sFile = m_ProgramPath + sFName
    End If
   
    ' Make sure file exists
    If Len(sError) = 0 Then
        If _FileExists(sFile) = _FALSE Then
            sError = "File not found: " + Chr$(34) + sFile + Chr$(34)
        End If
    End If

    ' Read data from file
    If Len(sError) = 0 Then
        Open sFile For Input As #1
        'iLineNum = 0
        While Not EOF(1)
            Line Input #1, sLine ' read entire text file line
            'iLineNum = iLineNum + 1
            sLine = Replace$(sLine, Chr$(160), Chr$(32))
            GetPair sLine, sName, sValue
            If Len(sName) > 0 Then
                ReDim _Preserve NameArray$(0 To UBound(NameArray$) + 1)
                NameArray$(UBound(NameArray$)) = sName
               
                ReDim _Preserve ValuArray$(0 To UBound(ValuArray$) + 1)
                ValuArray$(UBound(ValuArray$)) = sValue
            End If
        Wend
        Close #1

        If LBound(NameArray$) <> LBound(ValuArray$) Then
            sError = sError + _IIf(Len(sError) = 0, "", "; ") + "lbounds do not match"
        End If
        If UBound(NameArray$) <> UBound(ValuArray$) Then
            sError = sError + _IIf(Len(sError) = 0, "", "; ") + "ubounds do not match"
        End If
    End If
   
    ' Return any error messages (blank string means no errors)
    ReadConfig$ = sError
End Function ' ReadConfig$

' /////////////////////////////////////////////////////////////////////////////

Sub GetPair (MyString As String, MyName As String, MyValue As String)
    Dim sLine As String
    Dim iPos As Long
    sLine = LTrim$(MyString)
    If Len(sLine) > 0 _AndAlso Left$(sLine, 1) <> "#" _AndAlso Left$(sLine, 1) <> "[" Then
        iPos = InStr(1, sLine, "=")
        If iPos > 0 Then
            MyName = _Trim$(Left$(sLine, iPos - 1))
            MyValue = LTrim$(Right$(sLine, Len(sLine) - iPos))
        Else
            MyName = _Trim$(sLine)
        End If
    Else
        MyName = "": MyValue = ""
    End If
End Sub ' GetPair
'Sub GetPairTest
'    GetPairTest1 "# A comment"
'    GetPairTest1 ""
'    GetPairTest1 "[My Section]"
'    GetPairTest1 "name1 = value1 "
'    GetPairTest1 " name2 =   value2"
'    GetPairTest1 "#name3 = value3"
'    GetPairTest1 "  #   name4   =      value4"
'    GetPairTest1 "# name5 =# value5 "
'    GetPairTest1 "name6"
'    GetPairTest1 "name7="
'End Sub ' GetPairTest
'Sub GetPairTest1 (MyString As String)
'    Static iCount As Integer
'    Dim n$, v$
'    iCount = iCount + 1
'    GetPair MyString, n$, v$
'    Print _ToStr$(iCount) + ". " + Chr$(34) + MyString + Chr$(34) + " returns " + _IIf(Len(n$) > 0, "MyName=" + Chr$(34) + n$ + Chr$(34) + " Myvalue=" + Chr$(34) + v$ + Chr$(34), "(Blank)")
'End Sub ' GetPairTest1

' /////////////////////////////////////////////////////////////////////////////

Function SaveConfig$
    Dim RoutineName As String:: RoutineName = "SaveConfig$"
    Dim sError As String: sError = ""
    Dim sFName As String
    Dim sFile As String
    Dim sText As String
    Dim sValue As String
   
    ' Get file name
    If Len(sError) = 0 Then
        sFName = Left$(m_ProgramName$, _InStrRev(m_ProgramName$, ".")) + "cfg"
        sFile = m_ProgramPath + sFName
    End If
   
    ' Populate template
    If Len(sError) = 0 Then
        sText = ""
        sText = sText + "# Van Halenizer saved settings" + Chr$(13)
        sText = sText + "" + Chr$(13)
        sText = sText + "# =====================================================================================================================================================================" + Chr$(13)
        sText = sText + "# SETTING                     VARIABLE                  VALID VALUES                DESCRIPTION                                                                        " + Chr$(13)
        sText = sText + "# -------------------------   -----------------------   --------------------        -----------------------------------------------------------------------------------" + Chr$(13)
        sText = sText + "# ScreenWidth                 iScreenWidth              0 or 640 - _DesktopWidth    Horizontal screen resolution to use (0 = autodetect).                              " + Chr$(13)
        sText = sText + "# ScreenHeight                iScreenHeight             0 or 480 - _DesktopHeight   Vertical   screen resolution to use (0 = autodetect).                              " + Chr$(13)
        sText = sText + "# ScaleToPercent              iScaleToPercent           1-1000 (percent)            Percent to scale stripe sizes up or down (100 = 100%).                             " + Chr$(13)
        sText = sText + "#                                                                                   Resolution     Recommended Value                                                   " + Chr$(13)
        sText = sText + "#                                                                                    640 x  480    45-55                                                               " + Chr$(13)
        sText = sText + "#                                                                                    800 x  600    55-65                                                               " + Chr$(13)
        sText = sText + "#                                                                                   1024 x  768    65-75                                                               " + Chr$(13)
        sText = sText + "#                                                                                   1280 x 1024    95-105                                                              " + Chr$(13)
        sText = sText + "#                                                                                   1920 x 1200    105-115                                                             " + Chr$(13)
        sText = sText + "#                                                                                   3840 x 2160    125+                                                                " + Chr$(13)
        sText = sText + "# EnableAnimation             bEnableAnimation          True / False                If True, stripes appear animated.                                                  " + Chr$(13)
        sText = sText + "# DelayMS                     iDelayMS                  0-60000 (ms)                # milliseconds additional time delay to optionally slow down animation.            " + Chr$(13)
        sText = sText + "# MiniMenuIsVisible           bMiniMenuIsVisible        True / False                If True, 1-line mini menu is displayed over images.                                " + Chr$(13)
        sText = sText + "# FilenameTemplate            sFilenameTemplate         (any valid filename)        Filename template for image save. Supports the following tags:                     " + Chr$(13)
        sText = sText + "# `                           `                         `                               Timestamp tags: {yyyy}{mm}{dd}{hh}{nn}{ss}                                     " + Chr$(13)
        sText = sText + "# `                           `                         `                               Filename  tags: {ext}                                                          " + Chr$(13)
        sText = sText + "# EnableSaveResultsPopup      bEnableSaveResultsPopup   False                       If True, when image saved, inputbox opens with file path for copying to clipboard. " + Chr$(13)
        sText = sText + "# CycleEverySeconds           iCycleEverySeconds        0-86400 (seconds)           # seconds before cycling to the next color scheme (0 = don't cycle color schemes). " + Chr$(13)
        sText = sText + "# RepaintEverySeconds         iRepaintEverySeconds      0-3600 (seconds)            # seconds before generating a new set of stripes  (0 = don't regenerate).          " + Chr$(13)
        sText = sText + "# ColorSchemeIndex            iColorSchemeIndex         0-7                         Index of the color scheme to start with (0 = the first one).                       " + Chr$(13)
        sText = sText + "# BaseStripeCount             iBaseStripeCount          1-500 (count)               Base value to calculate # stripes to generate (percentage depends on color scheme)." + Chr$(13)
        sText = sText + "# StripeVariance              iStripeVariance           1-250 (count)               For determining # of stripes, controls how random / wide a range # stripes can be. " + Chr$(13)
        sText = sText + "# -------------------------   -----------------------   --------------------        -----------------------------------------------------------------------------------" + Chr$(13)
        sText = sText + "# EnableFlyingVH              bEnableFlyingVH           True / False                If True, flying VH logos adorn the screen                                          " + Chr$(13)
        sText = sText + "# NumVectors                  iNumVectors               1-100 (count)               Max # of flying VH symbols on screen at a time                                     " + Chr$(13)
        sText = sText + "# VectorEveryMS               iVectorEveryMS            50-9999 (ms)                # milliseconds between spawning a new flying VH symbol                             " + Chr$(13)
        sText = sText + "# MinDX                       iMinDX                    0-8192 (px)                 Minimum speed spawn point can move (pixels)                                        " + Chr$(13)
        sText = sText + "# MaxDX                       iMaxDX                    0-8192 (px)                 Maximum speed spawn point can move (pixels)                                        " + Chr$(13)
        sText = sText + "# MaxScale                    iMaxScale                 1-1000 (percent)            Maximum size (%) flying VH symbols reach before they disappear off the screen      " + Chr$(13)
        sText = sText + "# ScaleIncrement              iScaleIncrement           1000-5000 (micro %)         How fast (micro %) flying VH symbols grow (move toward you)                        " + Chr$(13)
        sText = sText + "# =====================================================================================================================================================================" + Chr$(13)
        sText = sText + "# For more 'psychedelic' effects try NumVectors=50 or 75, VectorEveryMS=50 or 75, MinDX=1, MaxDX=5 or 10, it can get slow with too many VHs but looks really cool      " + Chr$(13)
        sText = sText + "# =====================================================================================================================================================================" + Chr$(13)
        sText = sText + "[Settings]" + Chr$(13)
        sText = sText + "ScreenWidth = {ScreenWidth}" + Chr$(13)
        sText = sText + "ScreenHeight = {ScreenHeight}" + Chr$(13)
        sText = sText + "ScaleToPercent = {ScaleToPercent}" + Chr$(13)
        sText = sText + "EnableAnimation = {EnableAnimation}" + Chr$(13)
        sText = sText + "DelayMS = {DelayMS}" + Chr$(13)
        sText = sText + "MiniMenuIsVisible = {MiniMenuIsVisible}" + Chr$(13)
        sText = sText + "FilenameTemplate = {FilenameTemplate}" + Chr$(13)
        sText = sText + "EnableSaveResultsPopup = {EnableSaveResultsPopup}" + Chr$(13)
        sText = sText + "CycleEverySeconds = {CycleEverySeconds}" + Chr$(13)
        sText = sText + "RepaintEverySeconds = {RepaintEverySeconds}" + Chr$(13)
        sText = sText + "ColorSchemeIndex = {ColorSchemeIndex}" + Chr$(13)
        sText = sText + "BaseStripeCount = {BaseStripeCount}" + Chr$(13)
        sText = sText + "StripeVariance = {StripeVariance}" + Chr$(13)
        ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
        sText = sText + "EnableFlyingVH = {EnableFlyingVH}" + Chr$(13)
        sText = sText + "NumVectors = {NumVectors}" + Chr$(13)
        sText = sText + "VectorEveryMS = {VectorEveryMS}" + Chr$(13)
        sText = sText + "MinDX = {MinDX}" + Chr$(13)
        sText = sText + "MaxDX = {MaxDX}" + Chr$(13)
        sText = sText + "MaxScale = {MaxScale}" + Chr$(13)
        sText = sText + "ScaleIncrement = {ScaleIncrement}" + Chr$(13)
       
        sText = Replace$(sText, "`", Chr$(34))
       
        If iScreenWidth = _DesktopWidth Then
            sText = Replace$(sText, "{ScreenWidth}", "0")
        Else
            sText = Replace$(sText, "{ScreenWidth}", _ToStr$(iScreenWidth))
        End If
       
        If iScreenHeight = _DesktopHeight Then
            sText = Replace$(sText, "{ScreenHeight}", "0")
        Else
            sText = Replace$(sText, "{ScreenHeight}", _ToStr$(iScreenHeight))
        End If
       
        sText = Replace$(sText, "{ScaleToPercent}", _ToStr$(iScaleToPercent))
        sText = Replace$(sText, "{EnableAnimation}", _IIf(bEnableAnimation = _TRUE, "True", "False"))
        sText = Replace$(sText, "{DelayMS}", _ToStr$(iDelayMS))
        sText = Replace$(sText, "{MiniMenuIsVisible}", _IIf(bMiniMenuIsVisible = _TRUE, "True", "False"))
        sText = Replace$(sText, "{FilenameTemplate}", sFilenameTemplate)
        sText = Replace$(sText, "{EnableSaveResultsPopup}", _IIf(bEnableSaveResultsPopup = _TRUE, "True", "False"))
        sText = Replace$(sText, "{CycleEverySeconds}", _ToStr$(iCycleEverySeconds))
        sText = Replace$(sText, "{RepaintEverySeconds}", _ToStr$(iRepaintEverySeconds))
        sText = Replace$(sText, "{ColorSchemeIndex}", _ToStr$(iColorSchemeIndex))
        sText = Replace$(sText, "{BaseStripeCount}", _ToStr$(iBaseStripeCount))
        sText = Replace$(sText, "{StripeVariance}", _ToStr$(iStripeVariance))
        ' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
        sText = Replace$(sText, "{EnableFlyingVH}", _IIf(bEnableFlyingVH = _TRUE, "True", "False"))
        sText = Replace$(sText, "{NumVectors}", _ToStr$(iNumVectors))
        sText = Replace$(sText, "{VectorEveryMS}", _ToStr$(iVectorEveryMS))
        sText = Replace$(sText, "{MinDX}", _ToStr$(iMinDX))
        sText = Replace$(sText, "{MaxDX}", _ToStr$(iMaxDX))
        sText = Replace$(sText, "{MaxScale}", _ToStr$(iMaxScale))
        sText = Replace$(sText, "{ScaleIncrement}", _ToStr$(iScaleIncrement))
    End If
   
    ' Write file
    If Len(sError) = 0 Then
        sError = PrintFile$(sFile, sText, _FALSE)
    End If
   
    ' Return results
    SaveConfig$ = sError
End Function ' SaveConfig$

' /////////////////////////////////////////////////////////////////////////////
' Save the image image1 as PNG and JPG (format specified in sSaveFileFormat)
' Returns _TRUE if file was saved or _FALSE if not.

' _SAVEIMAGE saves the contents of an image or screen page to an image file.
' https://qb64phoenix.com/qb64wiki/index.php/SAVEIMAGE
' Syntax: _SAVEIMAGE fileName$[, ImageHandleArray&][, requirements$]
' Parameters
'     fileName$ is literal or variable STRING file name value.
'     Optional ImageHandleArray& is a LONG image handle or a valid screen page number.
'     Optional requirements$ STRING values can be:
'         BMP: Saves the image as Windows Bitmap if no file extension is specified.
'         GIF: Saves the image as Graphics Interchange Format if no file extension is specified.
'         HDR: Saves the image as Radiance HDR if no file extension is specified.
'         ICO: Saves the image as Windows Icon if no file extension is specified.
'         JPG: Saves the image as Joint Photographic Experts Group if no file extension is specified.
'         PNG: Saves the image as Portable Network Graphics if no file extension is specified.
'         QOI: Saves the image as Quite OK Image if no file extension is specified.
'         TGA: Saves the image as Truevision TARGA if no file extension is specified.

Function SaveImage%% (image1 As Long, sSaveFileFormat As String)
    Dim bResult As _Byte
    Dim sName As String
    Dim sTimestamp As String
    Dim arrDateTime As String
    Dim sFileName As String
    Dim in$

    ' TODO: Prompt user to select a folder
    'Dim SelectedPath$
    ''SelectedPath$ = _HOPEN$
    'SelectedPath$ = _SelectFolderDialog$("Select folder to save image to", m_ProgramPath$)
    'sFileName = SelectedPath$ + m_ProgramName + "_" + CurrentTimeStamp$ + "." + sSaveFileFormat
    '_SaveImage sFileName, GridImage&, "JPG"

    'OLD Construct filename: sFileName = m_ProgramPath + m_ProgramName + "_" + CurrentTimeStamp$ + "." + sSaveFileFormat

    ' Construct filename
    sName = sFilenameTemplate ' "VanHalen_{yyyy}{mm}{dd}_{hh}{nn}{ss}.{ext}"
    sTimestamp = CurrentDateTime$ ' returns in format "2025-10-23 18:46:51"
    ' Insert date/time parts, grab from positions in sTimestamp:
    '          1111111111
    ' 1234567890123456789
    ' 2025-10-23 18:46:51
    sName = Replace$(sName, "{yyyy}", Left$(sTimestamp, 4))
    sName = Replace$(sName, "{yy}", Mid$(sTimestamp, 3, 2))
    sName = Replace$(sName, "{mm}", Mid$(sTimestamp, 6, 2))
    sName = Replace$(sName, "{dd}", Mid$(sTimestamp, 9, 2))
    sName = Replace$(sName, "{hh}", Mid$(sTimestamp, 12, 2))
    sName = Replace$(sName, "{nn}", Mid$(sTimestamp, 15, 2))
    sName = Replace$(sName, "{ss}", Mid$(sTimestamp, 18, 2))
    sName = Replace$(sName, "{ext}", LCase$(sSaveFileFormat))
    sFileName = m_ProgramPath + sName

    ' Try to save file
    'Locate MaxRow - 2, 1: Print "Saving image..."
    _SaveImage sFileName, image1, sSaveFileFormat

    ' Make sure file exists
    If _FileExists(sFileName) = _TRUE Then
        bResult = _TRUE

        ' Show file name in popup inputbox they can copy to clipboard
        If bEnableSaveResultsPopup = _TRUE Then
            in$ = _InputBox$(m_ProgramName, "Saved image", sFileName)
        End If
    Else
        bResult = _FALSE

        ' Show error in popup
        If bEnableSaveResultsPopup = _TRUE Then
            in$ = _InputBox$(m_ProgramName, "Error, not saved", sFileName)
        End If
    End If

    SaveImage%% = bResult
End Function ' SaveImage%%

' /////////////////////////////////////////////////////////////////////////////

Function StripeName$ (iStripeNum%)
    Dim MyString$: MyString$ = ""
    Select Case iStripeNum%
        Case cStripe_1_8:
            MyString$ = "1/8" + Chr$(34)
        Case cStripe_1_4:
            MyString$ = "1/4" + Chr$(34)
        Case cStripe_1_2:
            MyString$ = "1/2" + Chr$(34)
        Case cStripe_3_4:
            MyString$ = "3/4" + Chr$(34)
    End Select
    StripeName$ = MyString$
End Function ' StripeName$

' /////////////////////////////////////////////////////////////////////////////

Function VHLogo$
    Dim vh$
    vh$ = ""

    If CurrentWidth >= 1024 And CurrentHeight >= 768 Then
        '             12345678901234567890123456789012345678901234567890123456789012345678901234567890123
        vh$ = vh$ + "0                                                                                   " + Chr$(13)
        vh$ = vh$ + "0                               = The Van Halenizer =                               " + Chr$(13)
        vh$ = vh$ + "0                                                                                   " + Chr$(13)
        vh$ = vh$ + "0                               by Softintheheadware                                " + Chr$(13)
        vh$ = vh$ + "0                                                                                   " + Chr$(13)
        vh$ = vh$ + "1_____________________     ___________________    ______      ______________________" + Chr$(13)
        vh$ = vh$ + "1\888888888888888888888\   \88888888888888888/\ /888888/\   /888888888888888888888//" + Chr$(13)
        vh$ = vh$ + "1 \888888888888888888888\   \888888888888888/  /888888/ /  /888888888888888888888// " + Chr$(13)
        vh$ = vh$ + "1                    \888\    \________/888/  /888/\__\/  /888/                     " + Chr$(13)
        vh$ = vh$ + "1  \8888888888888888888888\           /888/  /888/ /     /888888888888888888888//   " + Chr$(13)
        vh$ = vh$ + "1   \8888888888888888888888\         /888/  /888/ /     /888888888888888888888//    " + Chr$(13)
        vh$ = vh$ + "1                       \888\       /888/  /888/ /     /888/                        " + Chr$(13)
        vh$ = vh$ + "1     \8888888888888888888888\     /888/  /888/ /     /8888888888888888888888//     " + Chr$(13)
        vh$ = vh$ + "1      \8888888888888888888888\   /888/  /8888888888888888888888888888888888/       " + Chr$(13)
        vh$ = vh$ + "1        \_________________\888\ /888/  /888888888888888/\ _______________/         " + Chr$(13)
        vh$ = vh$ + "1                           \888.888/  /888/  /    /888/  /                         " + Chr$(13)
        vh$ = vh$ + "1                            \88888/  /888/  /    /888/  /                          " + Chr$(13)
        vh$ = vh$ + "1                             \888/  /888/  /    /888/  /                           " + Chr$(13)
        vh$ = vh$ + "1                              \8/  /888/  /    /888/  /                            " + Chr$(13)
        vh$ = vh$ + "1                               \  /888/  /    /888/  /                             " + Chr$(13)
        vh$ = vh$ + "1                                 /888/  /    /888/  /                              " + Chr$(13)
        vh$ = vh$ + "1                                 \88/  /    /888/  /                               " + Chr$(13)
        vh$ = vh$ + "1                                  \/  /    /888/  /                                " + Chr$(13)
        vh$ = vh$ + "1                                    \/    /888/  /                                 " + Chr$(13)
        vh$ = vh$ + "1                                         /888/  /                                  " + Chr$(13)
        vh$ = vh$ + "1                                        /888/  /                                   " + Chr$(13)
        vh$ = vh$ + "1                                        \88/  /                                    " + Chr$(13)
        vh$ = vh$ + "1                                         \/  /                                     " + Chr$(13)
        vh$ = vh$ + "1                                           \/                                      " + Chr$(13)
        vh$ = vh$ + "1                                                                                   " + Chr$(13)
    ElseIf CurrentWidth >= 800 And CurrentHeight >= 600 Then
        vh$ = vh$ + "0                 = The Van Halenizer by Softintheheadware =                        " + Chr$(13)
        vh$ = vh$ + "1_____________________     ___________________    ______      ______________________" + Chr$(13)
        vh$ = vh$ + "1\888888888888888888888\   \88888888888888888/\ /888888/\   /888888888888888888888//" + Chr$(13)
        vh$ = vh$ + "1 \888888888888888888888\   \888888888888888/  /888888/ /  /888888888888888888888// " + Chr$(13)
        vh$ = vh$ + "1                    \888\    \________/888/  /888/\__\/  /888/                     " + Chr$(13)
        vh$ = vh$ + "1  \8888888888888888888888\           /888/  /888/ /     /888888888888888888888//   " + Chr$(13)
        vh$ = vh$ + "1   \8888888888888888888888\         /888/  /888/ /     /888888888888888888888//    " + Chr$(13)
        vh$ = vh$ + "1                       \888\       /888/  /888/ /     /888/                        " + Chr$(13)
        vh$ = vh$ + "1     \8888888888888888888888\     /888/  /888/ /     /8888888888888888888888//     " + Chr$(13)
        vh$ = vh$ + "1      \8888888888888888888888\   /888/  /8888888888888888888888888888888888/       " + Chr$(13)
        vh$ = vh$ + "1        \_________________\888\ /888/  /888888888888888/\ _______________/         " + Chr$(13)
        vh$ = vh$ + "1                           \888.888/  /888/  /    /888/  /                         " + Chr$(13)
        vh$ = vh$ + "1                            \88888/  /888/  /    /888/  /                          " + Chr$(13)
        vh$ = vh$ + "1                             \888/  /888/  /    /888/  /                           " + Chr$(13)
        vh$ = vh$ + "1                              \8/  /888/  /    /888/  /                            " + Chr$(13)
        vh$ = vh$ + "1                               \  /888/  /    /888/  /                             " + Chr$(13)
        vh$ = vh$ + "1                                 /888/  /    /888/  /                              " + Chr$(13)
        vh$ = vh$ + "1                                 \88/  /    /888/  /                               " + Chr$(13)
        vh$ = vh$ + "1                                  \/  /    /888/  /                                " + Chr$(13)
        vh$ = vh$ + "1                                    \/    /888/  /                                 " + Chr$(13)
        vh$ = vh$ + "1                                         /888/  /                                  " + Chr$(13)
        vh$ = vh$ + "1                                        /888/  /                                   " + Chr$(13)
        vh$ = vh$ + "1                                        \88/  /                                    " + Chr$(13)
        vh$ = vh$ + "1                                         \/  /                                     " + Chr$(13)
        vh$ = vh$ + "1                                           \/                                      " + Chr$(13)
    Else
        vh$ = vh$ + "0  = The Van Halenizer =     " + Chr$(13)
        vh$ = vh$ + "0  by  Softintheheadware     " + Chr$(13)
        vh$ = vh$ + "1______      __  __   ______ " + Chr$(13)
        vh$ = vh$ + "1   ___\      / /    /___    " + Chr$(13)
        vh$ = vh$ + "1    ___\    / /___ /___     " + Chr$(13)
        vh$ = vh$ + "1        \  / /    /         " + Chr$(13)
        vh$ = vh$ + "1         \/ /    /          " + Chr$(13)
        vh$ = vh$ + "1           /    /           " + Chr$(13)
        vh$ = vh$ + "1               /            " + Chr$(13)
        vh$ = vh$ + "1              /             " + Chr$(13)
    End If
   
    VHLogo$ = vh$
End Function ' VHLogo$

' /////////////////////////////////////////////////////////////////////////////

Sub PrintLogo (x1 As Long, y1 As Long)
    Dim bg0 As _Unsigned Long
    Dim fg0 As _Unsigned Long
    Dim bg1 As _Unsigned Long
    Dim fg1 As _Unsigned Long
    Dim bg2 As _Unsigned Long
    Dim fg2 As _Unsigned Long
    Dim vh$
    Dim LineArray(-1) As String
    Dim iLine As Integer
    Dim sLine As String
    Dim sMode As String
    Dim iChar As Integer
    Dim sChar As String
    Dim x As Long
    Dim y As Long
    Dim OffsetX As Long
    Dim r, g, b As Integer
    Dim r1, g1, b1 As Single
    Dim r2, g2, b2 As Single
    Dim dr, dg, db As Single
    Dim iSteps As Integer
    Dim iLen As Integer
    Dim sIndent As String
    Dim bgNone As _Unsigned Long
    Dim fgNone As _Unsigned Long

    MaxRow = _Height / _FontHeight
    MaxCol = _Width / _FontWidth
    x = x1
    y = y1
    bgNone = _RGBA32(0, 0, 0, 0)
    fgNone = bgNone
    fg0 = _RGB32(255, 255, 255)
    bg0 = _RGBA32(0, 0, 0, 0)
    fg1 = _RGB32(0, 128, 255)
    bg1 = _RGBA32(0, 0, 0, 0)
    'bg1 = _RGB32(0, 0, 128)
    fg2 = _RGB32(96, 96, 96)
    bg2 = _RGBA32(0, 0, 0, 0)
    vh$ = VHLogo$
    split vh$, Chr$(13), LineArray()
   
    ' Calculate gradient:
    ' steps = UBound(LineArray) + 1 = 25
    ' start = 220, 220, 220
    ' end   = 169, 50, 45
    ' r start = 220, end = 255
    ' r journey = end - start = 255 - 220 = 35
    ' r increment = journey / steps = 35 / 25 = 1.4
    ' g start = 220, end = 0
    iSteps = 0
    '
    ' Also, find the average line length
    iLen = 0
   
    For iLine = LBound(LineArray) To UBound(LineArray)
        iLen = iLen + (Len(LineArray(iLine)) - 1)
        If Left$(LineArray(iLine), 1) = "1" Then iSteps = iSteps + 1
    Next iLine
   
    ' Find average width
    iLen = iLen / ((UBound(LineArray) - LBound(LineArray)) - 1)
   
    ' Calculate the indent
    iLen = (MaxCol - iLen) / 2
    sIndent = Space$(iLen)
   
    ' Calculate gradient
    r1 = 220: g1 = 220: b1 = 220 ' start color (at top)
    r2 = 255: g2 = 50: b2 = 45 ' end color (at bottom)
    dr = (r2 - r1) / iSteps: dg = (g2 - g1) / iSteps: db = (b2 - b1) / iSteps

    For iLine = LBound(LineArray) To UBound(LineArray)
        sLine = LineArray(iLine)
        sMode = Left$(sLine, 1)
        sLine = Right$(sLine, Len(sLine) - 1)
        'OffsetX = (MaxCol - Len(sLine)) / 2
        'sLine = String$(OffsetX, " ") + sLine
        sLine = sIndent + sLine

        If sMode = "1" Then
            r = _Cast(Integer, r1)
            g = _Cast(Integer, g1)
            b = _Cast(Integer, b1)
            fg1 = _RGB32(r, g, b)
            r1 = r1 + dr: g1 = g1 + dg: b1 = b1 + db
        End If

        For iChar = 1 To Len(sLine)
            sChar = Mid$(sLine, iChar, 1)
            If sMode = "0" Then
                If sChar = " " Then
                    Color fg0, bg0
                Else
                    Color fg1, bg1
                End If
            ElseIf sMode = "1" Then
                If sChar = " " Then
                    Color fgNone, bgNone
                Else
                    Color fg1, bg1
                End If
            ElseIf sMode = "2" Then
                Color fg2, bg2
            End If
            x = iChar - 1
            _PrintString (x * _FontWidth, y * _FontHeight), sChar
        Next iChar
        y = y + 1: x = x1
    Next iLine

    ' Return current row, column
    y1 = y
    x1 = x
End Sub ' PrintLogo

' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' BEGIN GENERAL PURPOSE UTILITY ROUTINES #GEN
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

' /////////////////////////////////////////////////////////////////////////////
' Simple timestamp function

Function CurrentDateTime$
    CurrentDateTime$ = Mid$(Date$, 7, 4) + "-" + _
        Mid$(Date$, 1, 5) + " " + _
        Time$
End Function ' CurrentDateTime$

' /////////////////////////////////////////////////////////////////////////////

Function CurrentTimeStamp$
    Dim MyDate$
    MyDate$ = CurrentDateTime$
    MyDate$ = Replace$(MyDate$, " ", "_")
    MyDate$ = Replace$(MyDate$, "-", "")
    MyDate$ = Replace$(MyDate$, ":", "")
    CurrentTimeStamp$ = MyDate$
End Function ' CurrentTimeStamp$

' /////////////////////////////////////////////////////////////////////////////

Function ExtendedTimer##
    'Simplified version of the TimeStamp routine, streamlined to only give positive values based on the current timer.
    'Note:  Only good until the year 2100, as we don't do all the fancy calculations for leap years.
    'A timer should work quickly and efficiently in the background; and the less we do, the less lag we might insert
    'into a program.
    Dim m As Integer, d As Integer, y As Integer
    Dim s As _Float, day As String
    day = Date$
    m = Val(Left$(day, 2))
    d = Val(Mid$(day, 4, 2))
    y = Val(Right$(day, 4)) - 1970
    Select Case m 'Add the number of days for each previous month passed
        Case 2: d = d + 31
        Case 3: d = d + 59
        Case 4: d = d + 90
        Case 5: d = d + 120
        Case 6: d = d + 151
        Case 7: d = d + 181
        Case 8: d = d + 212
        Case 9: d = d + 243
        Case 10: d = d + 273
        Case 11: d = d + 304
        Case 12: d = d + 334
    End Select
    If (y Mod 4) = 2 And m > 2 Then d = d + 1 'add a day if this is leap year and we're past february
    d = (d - 1) + 365 * y 'current month days passed + 365 days per each standard year
    d = d + (y + 2) \ 4 'add in days for leap years passed
    s = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
    ExtendedTimer## = (s + Timer)
End Function ' ExtendedTimer##

' /////////////////////////////////////////////////////////////////////////////

Sub FreeImage (ThisImage&)
    If ThisImage& < -1 Or ThisImage& > 0 Then _FreeImage ThisImage&
End Sub ' FreeImage

' /////////////////////////////////////////////////////////////////////////////

Sub InitImage (ThisImage&, iWidth&, iHeight&, bgColor~&)
    FreeImage ThisImage&
    ThisImage& = _NewImage(iWidth&, iHeight&, 32)
    _Dest ThisImage&: Cls , bgColor~&
End Sub ' InitImage

' /////////////////////////////////////////////////////////////////////////////
' Returns _TRUE if number n is even
' https://slaystudy.com/qbasic-program-to-check-if-number-is-even-or-odd/
' see also: IsOdd%

Function IsEven% (n)
    If n Mod 2 = 0 Then
        IsEven% = _TRUE
    Else
        IsEven% = _FALSE
    End If
End Function ' IsEven%

' /////////////////////////////////////////////////////////////////////////////
' Returns _TRUE if value OriginalString$ is numeric.

' Re: Does a Is Number function exist in QB64?
' https://www.qb64.org/forum/index.php?topic=896.15

' Version 2 by madscijr
' Returns _TRUE (-1) if string is an integer, _FALSE (0) if not

' Version 1 by MWheatley
' Reply #18 on: January 01, 2019, 11:24:30 AM
' returns 1 if string is an integer, 0 if not

Function IsNumber% (OriginalString$)
    Dim bResult%: bResult% = _FALSE
    Dim iLoop%
    Dim TestString$
    'Dim bNegative%
    Dim iDecimalCount%
    Dim sNextChar$

    'THEY SHOULD TRIM OUTSIDE THE FUNCTION!
    'TestString$ = _TRIM$(OriginalString$)

    If Len(OriginalString$) > 0 Then
        TestString$ = ""
        If Left$(OriginalString$, 1) = "+" Then
            TestString$ = Right$(OriginalString$, Len(OriginalString$) - 1)
            'bNegative% = _FALSE
        ElseIf Left$(OriginalString$, 1) = "-" Then
            TestString$ = Right$(OriginalString$, Len(OriginalString$) - 1)
            'bNegative% = _TRUE
        Else
            TestString$ = OriginalString$
            'bNegative% = _FALSE
        End If
        If Len(TestString$) > 0 Then
            bResult% = _TRUE
            iDecimalCount% = 0
            For iLoop% = 1 To Len(TestString$)
                sNextChar$ = Mid$(TestString$, iLoop%, 1)
                If sNextChar$ = "." Then
                    iDecimalCount% = iDecimalCount% + 1
                    If iDecimalCount% > 1 Then
                        ' TOO MANY DECIMAL POINTS, INVALID!
                        bResult% = _FALSE
                        Exit For
                    End If
                ElseIf Asc(sNextChar$) < 48 Or Asc(sNextChar$) > 57 Then
                    ' NOT A NUMERAL OR A DECIMAL, INVALID!
                    bResult% = _FALSE
                    Exit For
                End If
            Next iLoop%
        End If
    End If
    IsNumber% = bResult%
End Function ' IsNumber%

' /////////////////////////////////////////////////////////////////////////////
' Returns _TRUE if number n is odd
' https://slaystudy.com/qbasic-program-to-check-if-number-is-even-or-odd/
' see also: IsEven%

Function IsOdd% (n)
    If n Mod 2 = 1 Then
        IsOdd% = _TRUE
    Else
        IsOdd% = _FALSE
    End If
End Function ' IsOdd%

' /////////////////////////////////////////////////////////////////////////////

Function PadLeft$ (sValue As String, iWidth As Integer)
    PadLeft$ = Right$(String$(iWidth, " ") + sValue, iWidth)
End Function ' PadLeft$

' /////////////////////////////////////////////////////////////////////////////

Function PadRight$ (sValue As String, iWidth As Integer)
    PadRight$ = Left$(sValue + String$(iWidth, " "), iWidth)
End Function ' PadRight$

' /////////////////////////////////////////////////////////////////////////////
' Writes sText to a debug file in the EXE folder.
' Debug file is named the same thing as the program EXE name with ".txt" at the end.
' For example the program "C:\QB64\MyProgram.BAS" running as
' "C:\QB64\MyProgram.EXE" would have an output file "C:\QB64\MyProgram.EXE.txt".
' If the file doesn't exist, it is created, otherwise it is appended to.

Sub PrintDebugFile (sText As String)
    Dim sFileName As String
    Dim sError As String
    Dim sOut As String

    sFileName = m_ProgramPath$ + m_ProgramName$ + ".txt"
    sError = ""
    If _FileExists(sFileName) = _FALSE Then
        sOut = ""
        sOut = sOut + "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + Chr$(13) + Chr$(10)
        sOut = sOut + "PROGRAM : " + m_ProgramName$ + Chr$(13) + Chr$(10)
        sOut = sOut + "RUN DATE: " + CurrentDateTime$ + Chr$(13) + Chr$(10)
        sOut = sOut + "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + Chr$(13) + Chr$(10)
        sError = PrintFile$(sFileName, sOut, _FALSE)
    End If
    If Len(sError) = 0 Then
        sError = PrintFile$(sFileName, sText, _TRUE)
    End If
    If Len(sError) <> 0 Then
        Print CurrentDateTime$ + " PrintDebugFile FAILED: " + sError
    End If
End Sub ' PrintDebugFile

' /////////////////////////////////////////////////////////////////////////////
' Writes sText to file sFileName.
' If bAppend=_TRUE appends to file, else overwrites it.

' Returns blank if successful else returns error message.

' Example:
' ProgramPath$ = Left$(Command$(0), _InStrRev(Command$(0), "\"))
' ProgramName$: m_ProgramName$ = Mid$(Command$(0), _InStrRev(Command$(0), "\") + 1)
' sFileName = ProgramPath$ + ProgramName$ + ".OUT.txt"
' sText = "This is a test." + chr$(13) + "Here is line 2." + chr$(13) + "End."
' sError = PrintFile$(sFileName, sText, _FALSE)

Function PrintFile$ (sFileName As String, sText As String, bAppend As Integer)
    Dim sError As String: sError = ""

    If (bAppend = _TRUE) Then
        If _FileExists(sFileName) Then
            Open sFileName For Append As #1 ' opens an existing file for appending
        Else
            sError = "Error in PrintFile$ : File not found. Cannot append."
        End If
    Else
        Open sFileName For Output As #1 ' opens and clears an existing file or creates new empty file
    End If

    If Len(sError) = 0 Then
        ' NOTE: WRITE places text in quotes in the file
        'WRITE #1, x, y, z$
        'WRITE #1, sText

        ' PRINT does not put text inside quotes
        Print #1, sText

        Close #1
    End If

    PrintFile$ = sError
End Function ' PrintFile$

' /////////////////////////////////////////////////////////////////////////////
' Generate random value between Min and Max inclusive.

Function RandomNumber% (Min%, Max%)
    Dim NumSpread%

    ' SET RANDOM SEED
    Randomize Timer

    NumSpread% = (Max% - Min%) + 1

    RandomNumber% = Int(Rnd * NumSpread%) + Min% ' GET RANDOM # BETWEEN Max% AND Min%
End Function ' RandomNumber%

' /////////////////////////////////////////////////////////////////////////////
' FROM: String Manipulation
' found at abandoned, outdated and now likely malicious qb64 dot net website
' http://www.qb64.[net]/forum/index_topic_5964-0/
'
'SUMMARY:
'   Purpose:  A library of custom functions that transform strings.
'   Author:   Dustinian Camburides (dustinian@gmail.com)
'   Platform: QB64 (www.qb64.org)
'   Revision: 1.6
'   Updated:  5/28/2012

'SUMMARY:
'[Replace$] replaces all instances of the [Find] sub-string with the [Add] sub-string within the [Text] string.
'INPUT:
'Text: The input string; the text that's being manipulated.
'Find: The specified sub-string; the string sought within the [Text] string.
'Add: The sub-string that's being added to the [Text] string.

Function Replace$ (Text1 As String, Find1 As String, Add1 As String)
    ' VARIABLES:
    Dim Text2 As String
    Dim Find2 As String
    Dim Add2 As String
    Dim lngLocation As Long ' The address of the [Find] substring within the [Text] string.
    Dim strBefore As String ' The characters before the string to be replaced.
    Dim strAfter As String ' The characters after the string to be replaced.

    ' INITIALIZE:
    ' MAKE COPIESSO THE ORIGINAL IS NOT MODIFIED (LIKE ByVal IN VBA)
    Text2 = Text1
    Find2 = Find1
    Add2 = Add1

    lngLocation = InStr(1, Text2, Find2)

    ' PROCESSING:
    ' While [Find2] appears in [Text2]...
    While lngLocation
        ' Extract all Text2 before the [Find2] substring:
        strBefore = Left$(Text2, lngLocation - 1)

        ' Extract all text after the [Find2] substring:
        strAfter = Right$(Text2, ((Len(Text2) - (lngLocation + Len(Find2) - 1))))

        ' Return the substring:
        Text2 = strBefore + Add2 + strAfter

        ' Locate the next instance of [Find2]:
        lngLocation = InStr(1, Text2, Find2)

        ' Next instance of [Find2]...
    Wend

    ' OUTPUT:
    Replace$ = Text2
End Function ' Replace$

' /////////////////////////////////////////////////////////////////////////////
' https://www.qb64.org/forum/index.php?topic=3605.0
' Quote from: SMcNeill on Today at 03:53:48 PM
'
' Sometimes, you guys make things entirely too  complicated.
' There ya go!  Three functions to either round naturally,
' always round down, or always round up, to whatever number of digits you desire.
' EDIT:  Modified to add another option to round scientific,
' since you had it's description included in your example.

' Receives + returns _FLOAT myVar## (-1.18E-4932 to +1.18E+4932)

' =============================================================================
' ROUNDING FUNCTIONS FOR TYPE _FLOAT

' old name: RoundNatural##
Function Round## (num##, digits%)
    Round## = Int(num## * 10 ^ digits% + .5) / 10 ^ digits%
End Function

Function RoundUp## (num##, digits%)
    RoundUp## = _Ceil(num## * 10 ^ digits%) / 10 ^ digits%
End Function

Function RoundDown## (num##, digits%)
    RoundDown## = Int(num## * 10 ^ digits%) / 10 ^ digits%
End Function

' old name: Round_Scientific##
Function RoundScientific## (num##, digits%)
    RoundScientific## = _Round(num## * 10 ^ digits%) / 10 ^ digits%
End Function

' =============================================================================
' ROUNDING FUNCTIONS FOR TYPE DOUBLE

Function RoundDouble# (num#, digits%)
    RoundDouble# = Int(num# * 10 ^ digits% + .5) / 10 ^ digits%
End Function

Function RoundUpDouble# (num#, digits%)
    RoundUpDouble# = _Ceil(num# * 10 ^ digits%) / 10 ^ digits%
End Function

Function RoundDownDouble# (num#, digits%)
    RoundDownDouble# = Int(num# * 10 ^ digits%) / 10 ^ digits%
End Function

Function RoundScientificDouble# (num#, digits%)
    RoundScientificDouble# = _Round(num# * 10 ^ digits%) / 10 ^ digits%
End Function

' =============================================================================
' ROUNDING FUNCTIONS FOR TYPE SINGLE

Function RoundSingle! (num!, digits%)
    RoundSingle! = Int(num! * 10 ^ digits% + .5) / 10 ^ digits%
End Function

' NOTE: not sure this one works: when digits%=3, it rounds .31 to .32
Function RoundUpSingle! (num!, digits%)
    RoundUpSingle! = _Ceil(num! * 10 ^ digits%) / 10 ^ digits%
End Function

Function RoundDownSingle! (num!, digits%)
    RoundDownSingle! = Int(num! * 10 ^ digits%) / 10 ^ digits%
End Function

Function RoundScientificSingle! (num!, digits%)
    RoundScientificSingle! = _Round(num! * 10 ^ digits%) / 10 ^ digits%
End Function

' /////////////////////////////////////////////////////////////////////////////
' Split and join strings
' https://www.qb64.org/forum/index.php?topic=1073.0
'
' FROM luke, QB64 Developer
' Date: February 15, 2019, 04:11:07 AM
'
' Given a string of words separated by spaces (or any other character),
' splits it into an array of the words. I've no doubt many people have
' written a version of this over the years and no doubt there's a million
' ways to do it, but I thought I'd put mine here so we have at least one
' version. There's also a join function that does the opposite
' array -> single string.
'
' Code is hopefully reasonably self explanatory with comments and a little demo.
' Note, this is akin to Python/JavaScript split/join, PHP explode/implode.

'Split in$ into pieces, chopping at every occurrence of delimiter$. Multiple consecutive occurrences
'of delimiter$ are treated as a single instance. The chopped pieces are stored in result$().
'
'delimiter$ must be one character long.
'result$() must have been REDIMmed previously.

' Modified to handle multi-character delimiters

Sub split (in$, delimiter$, result$())
    Dim start As Integer
    Dim finish As Integer
    Dim iDelimLen As Integer
    ReDim result$(-1)

    iDelimLen = Len(delimiter$)

    start = 1
    Do
        'While Mid$(in$, start, 1) = delimiter$
        While Mid$(in$, start, iDelimLen) = delimiter$
            'start = start + 1
            start = start + iDelimLen
            If start > Len(in$) Then
                Exit Sub
            End If
        Wend
        finish = InStr(start, in$, delimiter$)
        If finish = 0 Then
            finish = Len(in$) + 1
        End If

        ReDim _Preserve result$(0 To UBound(result$) + 1)

        result$(UBound(result$)) = Mid$(in$, start, finish - start)
        start = finish + 1
    Loop While start <= Len(in$)
End Sub ' split

' /////////////////////////////////////////////////////////////////////////////

Function TrueFalse$ (MyValue%)
    TrueFalse$ = _IIf(MyValue% = _TRUE, "_TRUE", "_FALSE")
End Function ' TrueFalse$

' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' END GENERAL PURPOSE UTILITY ROUTINES @GEN
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' BEGIN LINE DRAWING ROUTINES
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

' /////////////////////////////////////////////////////////////////////////////

Function DegTo! (x1, y1, x2, y2)
    ' returns an angle in degrees from point x1,y1 to point x2,y2
    DegTo! = _Atan2((y2 - y1), (x2 - x1)) / 0.01745329
End Function ' DegTo!

' /////////////////////////////////////////////////////////////////////////////
' RotoLine Demo by James D, Jarvis October 11,2023
' a program that demonstrates how to use rotozoom and related commands to draw lines thicker than one pixel
' https://qb64phoenix.com/forum/showthread.php?tid=2081&pid=20561#pid20561

' RotoLine  x1 As Single, y1 As Single, x2 As Single, y2 As Single, thk As Single, klr As _Unsigned Long

Sub RotoLine (x1 As Single, y1 As Single, x2 As Single, y2 As Single, thk As Single, klr As _Unsigned Long)
    Dim cx As Single
    Dim cy As Single
    Dim o&
    Dim rtn As Single
    Dim lnth As Single

    'use rotozoom to draw a line of thickness thk of color klr from x1,y1 to x2,y2
    cx = (x1 + x2) / 2
    cy = (y1 + y2) / 2
    o& = _Dest
    _Dest dot&
    PSet (0, 0), klr
    _Dest o&
    rtn = DegTo!(x1, y1, x2, y2)
    lnth = Sqr(Abs(x2 - x1) * Abs(x2 - x1) + Abs(y2 - y1) * Abs(y2 - y1))
    RotoZoom23d cx, cy, dot&, lnth, thk, rtn
End Sub ' RotoLine

' /////////////////////////////////////////////////////////////////////////////
' This sub gives really nice control over displaying an Image.
' by BPlus

' Scale rotate font text strings by B+
' https://qb64phoenix.com/forum/showthread.php?tid=414&highlight=rotate+text

Sub RotoZoom23d (centerX As Single, centerY As Single, Image As Long, xScale As Single, yScale As Single, Rotation As Single)
    'rotate an image with Rotation defined in units of degrees, 0 is along x axis to the right gogin clockwise
    Dim px(3) As Single
    Dim py(3) As Single
    Dim Wi&
    Dim Hi&
    Dim W&
    Dim H&
    Dim sinr!
    Dim cosr!
    Dim i&
    Dim x2&
    Dim y2&

    Wi& = _Width(Image&)
    Hi& = _Height(Image&)
    W& = Wi& / 2 * xScale
    H& = Hi& / 2 * yScale
    px(0) = -W&: py(0) = -H&: px(1) = -W&: py(1) = H&
    px(2) = W&: py(2) = H&: px(3) = W&: py(3) = -H&
    sinr! = Sin(-0.01745329 * Rotation)
    cosr! = Cos(-0.01745329 * Rotation)
    For i& = 0 To 3
        x2& = (px(i&) * cosr! + sinr! * py(i&)) + centerX
        y2& = (py(i&) * cosr! - px(i&) * sinr!) + centerY
        px(i&) = x2&: py(i&) = y2&
    Next
    _MapTriangle (0, 0)-(0, Hi& - 1)-(Wi& - 1, Hi& - 1), Image& To(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
    _MapTriangle (0, 0)-(Wi& - 1, 0)-(Wi& - 1, Hi& - 1), Image& To(px(0), py(0))-(px(3), py(3))-(px(2), py(2))
End Sub ' RotoZoom23d

' /////////////////////////////////////////////////////////////////////////////

Function SngRoundedToStr$ (sngValue As Single, intNumPlaces As Integer)
    Dim sngNew As Single
    sngNew = RoundSingle!(sngValue, intNumPlaces)
    SngRoundedToStr$ = SngToStr$(sngNew)
End Function ' SngRoundedToStr$

' /////////////////////////////////////////////////////////////////////////////
' Scientific notation - QB64 Wiki
' https://www.qb64.org/wiki/Scientific_notation

' Example: A string function that displays extremely small or large exponential decimal values.

Function SngToStr$ (n!)
    Dim result$: result$ = ""
    Dim value$
    Dim Xpos%
    Dim expo%
    Dim sign$
    Dim valu$
    Dim dot%
    Dim L%
    Dim add$
    Dim min$
    Dim DP$
    Dim n%
    Dim num$

    value$ = UCase$(LTrim$(Str$(n!)))
    Xpos% = InStr(value$, "D") + InStr(value$, "E") ' only D or E can be present
    If Xpos% Then
        expo% = Val(Mid$(value$, Xpos% + 1))
        If Val(value$) < 0 Then
            sign$ = "-"
            valu$ = Mid$(value$, 2, Xpos% - 2)
        Else
            valu$ = Mid$(value$, 1, Xpos% - 1)
        End If
        dot% = InStr(valu$, ".")
        L% = Len(valu$)
        If expo% > 0 Then
            add$ = String$(expo% - (L% - dot%), "0")
        End If
        If expo% < 0 Then
            min$ = String$(Abs(expo%) - (dot% - 1), "0")
            DP$ = "."
        End If
        For n% = 1 To L%
            If Mid$(valu$, n%, 1) <> "." Then
                num$ = num$ + Mid$(valu$, n%, 1)
            End If
        Next n%
        result$ = _Trim$(sign$ + DP$ + min$ + num$ + add$)
    Else
        result$ = value$
    End If

    SngToStr$ = result$
End Function ' SngToStr$

' /////////////////////////////////////////////////////////////////////////////
' From: SMcNeill
' Date: 01-02-2024, 08:19 AM
' https://qb64phoenix.com/forum/showthread.php?tid=2372&pid=22425#pid22425

' ThickLine x1,           y1,           x2,           y2,           thk,           kolor AS _UNSIGNED LONG

Sub ThickLine (x1, y1, x2, y2, thk, kolor As _Unsigned Long)
    'draw a line of thickness thk on color klr from x1,y1 to x2,y2
    'orientation of line is set in the middle of line thickness
    Static tempimage As Long
    Static m As _MEM
    Dim cang As Single
    Dim ta As Single
    Dim tb As Single
    Dim v As Single
    Dim tax1 As Single
    Dim tay1 As Single
    Dim tax4 As Single
    Dim tay4 As Single
    Dim tax2 As Single
    Dim tay2 As Single
    Dim tax3 As Single
    Dim tay3 As Single

    If tempimage = 0 Then tempimage = _NewImage(1, 1, 32): m = _MemImage(tempimage)
    $Checking:Off
    _MemPut m, m.OFFSET, kolor
    $Checking:On
    cang = _Atan2((y2 - y1), (x2 - x1)) 'get the angle from x1,y1 to x2,y2
    ta = cang + _Pi(.5)
    tb = ta + _Pi
    tax1 = x1 + (thk / 2) * Cos(ta)
    tay1 = y1 + (thk / 2) * Sin(ta)
    tax4 = x1 + (thk / 2) * Cos(tb)
    tay4 = y1 + (thk / 2) * Sin(tb)
    tax2 = x2 + (thk / 2) * Cos(ta)
    tay2 = y2 + (thk / 2) * Sin(ta)
    tax3 = x2 + (thk / 2) * Cos(tb)
    tay3 = y2 + (thk / 2) * Sin(tb)

    _MapTriangle (0, 0)-(0, 0)-(0, 0), tempimage To(tax1, tay1)-(tax2, tay2)-(tax4, tay4)
    _MapTriangle (0, 0)-(0, 0)-(0, 0), tempimage To(tax2, tay2)-(tax3, tay3)-(tax4, tay4)
End Sub ' ThickLine

' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' END LINE DRAWING ROUTINES
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
















' 0.824 seconds Paint
' 2.692 seconds Fill enableBlend%=_FALSE
' 5.494 seconds Fill enableBlend%=_TRUE
' 1.759 seconds Paint2
' 813.184 seconds Paint3
' 5.000 seconds PaintMask

' ################################################################################################################################################################
' # BEGIN Steve's fill code
' ################################################################################################################################################################

' /////////////////////////////////////////////////////////////////////////////

Sub Fill (x, y, Kolor As _Unsigned Long, EnableBlend%)
    Dim As _Unsigned Long OC, BC
    OC = Point(x, y) 'original color

    If EnableBlend% = _TRUE Then
        ' blend is enabled
        If _Alpha32(Kolor) <> 255 Or _Alpha32(OC) <> 255 Then ' we're going to blend
            PSet (x, y), Kolor
            BC = Point(x, y) ' blended color
        End If
        BlendFiller x, y, Kolor, OC, BC
    Else
        Filler x, y, Kolor, OC
    End If
End Sub ' Fill

' /////////////////////////////////////////////////////////////////////////////
' Paintbucket fill (opaque)

Sub Filler (x, y, Kolor As _Unsigned Long, OC As _Unsigned Long)
    Dim l, r, i
    If Kolor = OC Then Exit Sub
    l = x: r = x 'find left/right to fill
    Do Until l = 0
        If Point(l - 1, y) = OC Then l = l - 1 Else Exit Do
    Loop 'find the left boundry
    Do Until r = _Width - 1
        If Point(r + 1, y) = OC Then r = r + 1 Else Exit Do
    Loop 'find the right boundry
    Line (l, y)-(r, y), Kolor, BF
    For i = l To r
        If Point(i, y + 1) = OC Then Filler i, y + 1, Kolor, OC
    Next
    For i = l To r
        If Point(i, y - 1) = OC Then Filler i, y - 1, Kolor, OC
    Next
End Sub ' Filler

' /////////////////////////////////////////////////////////////////////////////
' Paintbucket fill with blending colors w/alpha < 255

Sub BlendFiller (x, y, Kolor As _Unsigned Long, OC As _Unsigned Long, BC As _Unsigned Long)
    Dim l, r, i
    If Kolor = OC Or Kolor = BC Then Exit Sub
    l = x: r = x 'find left/right to fill
    Do Until l = 0
        If Point(l - 1, y) = BC Then Exit Do
        If Point(l - 1, y) = OC Then l = l - 1 Else Exit Do
    Loop 'find the left boundry
    Do Until r = _Width - 1
        If Point(r + 1, y) = BC Then Exit Do
        If Point(r + 1, y) = OC Then r = r + 1 Else Exit Do
    Loop 'find the right boundry
    Line (l, y)-(r, y), Kolor, BF
    For i = l To r
        If Point(i, y + 1) = BC Then _Continue
        If Point(i, y + 1) = OC Then BlendFiller i, y + 1, Kolor, OC, BC
    Next
    For i = l To r
        If Point(i, y - 1) = BC Then _Continue
        If Point(i, y - 1) = OC Or Point(l - 1, y) = BC Then BlendFiller i, y - 1, Kolor, OC, BC
    Next
End Sub ' BlendFiller

' ################################################################################################################################################################
' # END Steve's fill code
' ################################################################################################################################################################

' ################################################################################################################################################################
' # BEGIN Petr's fill code
' ################################################################################################################################################################

' /////////////////////////////////////////////////////////////////////////////
' Petr, Mini-Mod
' 4/24/2025 9:42 AM
' https://qb64phoenix.com/forum/showthread.php?tid=1507

Sub Paint2 (x, y, c~&)
    Dim W
    Dim H
    Dim Virtual As Long
    Dim position&
    Dim Clr2~&
    Dim D&
    Dim CLR~&

    W = _Width: H = _Height
    Virtual = _NewImage(W, H, 32)

    Dim m As _MEM, n As _MEM, Bck As _Unsigned Long

    m = _MemImage(_Source)
    n = _MemImage(Virtual)

    'create mask (2 color image)
    position& = (y * W + x) * 4

    _MemGet m, m.OFFSET + position&, Bck

    Clr2~& = _RGB32(_Red32(Bck) - 1, _Green32(Bck) - 1, _Blue32(Bck) - 1, _Alpha32(Bck) - 1)

    D& = 0

    Do Until D& = n.SIZE
        CLR~& = _MemGet(m, m.OFFSET + D&, _Unsigned Long)
        If CLR~& = Bck~& Then _MemPut n, n.OFFSET + D&, CLR~& Else _MemPut n, n.OFFSET + D&, Clr2~&
        D& = D& + 4
    Loop

    D& = _Dest

    _Dest Virtual
    Paint (x, y), c~&, Clr2~&
    _Dest D&
    _ClearColor Clr2~&, Virtual
    _PutImage , Virtual, D&

    _MemFree m
    _MemFree n
    _FreeImage Virtual
End Sub ' Paint2
' ################################################################################################################################################################
' # END Petr's fill code
' ################################################################################################################################################################

' ################################################################################################################################################################
' # BEGIN madscijr's fill code
' ################################################################################################################################################################

' /////////////////////////////////////////////////////////////////////////////
' Does a paintbucket fill with paint
' using _CLEARCOLOR to protect unwanted colors
' from being painted over.
' Receives arrIgnoreColors() an array of "protected" colors
' which are removed from temp image with _CLEARCOLOR,
' First we look at point x0, y0 to get the target color,
' then _CLEARCOLOR all the colors in arrIgnoreColors()
' then _PUTIMAGE to a temp image, paint does fill on that,
' then _PUTIMAGE that back to the original.

' * v2 eliminates a temp image we didn't need.

' Prequisites:
' _SOURCE & _DEST must be set to the target image.

' -----------------------------------------------------------------------------
' NOTES:
' The _CLEARCOLOR statement sets a specific color to be treated as
' transparent when an image is later put (via _PUTIMAGE) onto another image.
' Syntax: _CLEARCOLOR {color&|_NONE}[, Dest_Handle&]

' The _SETALPHA statement sets the alpha channel transparency level of some
' or all of the pixels of an image.
' Syntax: _SETALPHA alpha&[, color1&][ TO colour2&] [, imageHandle&]
' -----------------------------------------------------------------------------

' Example usage:
' PaintMask2 x0, y0, FillColor, IgnoreColor

Sub PaintMask2 (x0, y0, FillColor~&, IgnoreColor~&)
    Dim imgSource&
    Dim imgFill&
    Dim index%
    Dim bgColor~&

    ' Reference original image
    imgSource& = _Dest

    ' Get target color
    bgColor~& = Point(x0, y0)
    If bgColor~& <> FillColor~& Then
       
        ' Clear any colors not the target color or fill color
        'For index% = LBound(arrIgnoreColors) To UBound(arrIgnoreColors)
        If IgnoreColor~& <> FillColor~& Then
            'If arrIgnoreColors(index%) <> FillColor~& Then
            'If arrIgnoreColors(index%) <> bgColor~& Then
            If IgnoreColor~& <> bgColor~& Then
                _ClearColor IgnoreColor~&, imgSource&
            End If
        End If
        'Next index%

        ' Create fill target
        InitImage imgFill&, _Width(imgSource&), _Height(imgSource&), _RGB32(0, 0, 0, 0)
        '_PUTIMAGE , sourceHandle&, destHandle& 'size full source to fit full destination area
        _PutImage , imgSource&, imgFill&

        ' Fill in fill target
        _Dest imgFill&
        'PAINT [STEP] (column%, row%), fillColor[, borderColor%]
        Paint (x0, y0), FillColor~&, _RGB32(0, 0, 0, 0)

        ' Copy fill target onto main image
        '_PUTIMAGE , sourceHandle&, destHandle& 'size full source to fit full destination area
        _PutImage , imgFill&, imgSource& 'size full source to fit full destination area

        ' Point back at main image
        _Source imgSource&
        _Dest imgSource&

        ' Undo _CLEARCOLOR
        _ClearColor _None, imgSource&
        '_ClearColor _RGB32(0, 0, 0, 0), imgSource& ' re-add the color we use for transparent?

        ' Cleanup
        FreeImage imgFill&
    End If
End Sub ' PaintMask2

' ################################################################################################################################################################
' # END madscijr's fill code
' ################################################################################################################################################################

























' ################################################################################################################################################################
' BEGIN COLOR ARRAY FUNCTIONS #COLR
' ################################################################################################################################################################

' /////////////////////////////////////////////////////////////////////////////

Sub AddColor (ColorValue As _Unsigned Long, arrColor() As _Unsigned Long)
    ReDim _Preserve arrColor(0 To UBound(arrColor) + 1) As _Unsigned Long
    arrColor(UBound(arrColor)) = ColorValue
End Sub ' AddColor

' /////////////////////////////////////////////////////////////////////////////

Sub AddColors (ColorValue As _Unsigned Long, arrColor() As _Unsigned Long, HowMany As Long)
    Dim iLoop As Integer
    For iLoop = 1 To HowMany
        AddColor ColorValue, arrColor()
    Next iLoop
End Sub ' AddColors

' /////////////////////////////////////////////////////////////////////////////

Sub AddSpectrumColors (arrColor() As _Unsigned Long)
    Dim iNum As Integer
    iNum = 1
    AddColors cRed, arrColor(), iNum
    AddColors cOrangeRed, arrColor(), iNum
    AddColors cDarkOrange, arrColor(), iNum
    AddColors cOrange, arrColor(), iNum
    AddColors cGold, arrColor(), iNum
    AddColors cYellow, arrColor(), iNum
    AddColors cChartreuse, arrColor(), iNum
    AddColors cOliveDrab1, arrColor(), iNum
    AddColors cLime, arrColor(), iNum
    AddColors cMediumSpringGreen, arrColor(), iNum
    AddColors cSpringGreen, arrColor(), iNum
    AddColors cCyan, arrColor(), iNum
    AddColors cDeepSkyBlue, arrColor(), iNum
    AddColors cDodgerBlue, arrColor(), iNum
    AddColors cSeaBlue, arrColor(), iNum
    AddColors cBlue, arrColor(), iNum
    AddColors cBluePurple, arrColor(), iNum
    AddColors cDeepPurple, arrColor(), iNum
    AddColors cPurple, arrColor(), iNum
    AddColors cPurpleRed, arrColor(), iNum
End Sub ' AddSpectrumColors

' ################################################################################################################################################################
' END COLOR ARRAY FUNCTIONS @COLR
' ################################################################################################################################################################

' ################################################################################################################################################################
' BEGIN COLOR FUNCTIONS #COLOR
' ################################################################################################################################################################

Function cRed~& ()
    cRed = _RGB32(255, 0, 0)
End Function

Function cOrangeRed~& ()
    cOrangeRed = _RGB32(255, 69, 0)
End Function ' cOrangeRed~&

Function cDarkOrange~& ()
    cDarkOrange = _RGB32(255, 140, 0)
End Function ' cDarkOrange~&

Function cOrange~& ()
    cOrange = _RGB32(255, 165, 0)
End Function ' cOrange~&

Function cGold~& ()
    cGold = _RGB32(255, 215, 0)
End Function ' cGold~&

Function cYellow~& ()
    cYellow = _RGB32(255, 255, 0)
End Function ' cYellow~&

' LONG-HAIRED FRIENDS OF JESUS OR NOT,
' THIS IS NOT YELLOW ENOUGH (TOO CLOSE TO LIME)
' TO USE FOR OUR COMPLEX RAINBOW SEQUENCE:
Function cChartreuse~& ()
    cChartreuse = _RGB32(127, 255, 0)
End Function ' cChartreuse~&

' WE SUBSTITUTE THIS CSS3 COLOR FOR INBETWEEN LIME AND YELLOW:
Function cOliveDrab1~& ()
    cOliveDrab1 = _RGB32(192, 255, 62)
End Function ' cOliveDrab1~&

Function cLime~& ()
    cLime = _RGB32(0, 255, 0)
End Function ' cLime~&

Function cMediumSpringGreen~& ()
    cMediumSpringGreen = _RGB32(0, 250, 154)
End Function ' cMediumSpringGreen~&

' ADDED THIS FOR THE GAUGE COLOR:
Function cSpringGreen~& ()
    cSpringGreen = _RGB32(0, 255, 160)
End Function ' cSpringGreen~&

Function cCyan~& ()
    cCyan = _RGB32(0, 255, 255)
End Function ' cCyan~&

Function cDeepSkyBlue~& ()
    cDeepSkyBlue = _RGB32(0, 191, 255)
End Function ' cDeepSkyBlue~&

Function cDodgerBlue~& ()
    cDodgerBlue = _RGB32(30, 144, 255)
End Function ' cDodgerBlue~&

Function cSeaBlue~& ()
    cSeaBlue = _RGB32(0, 64, 255)
End Function ' cSeaBlue~&

Function cBlue~& ()
    cBlue = _RGB32(0, 0, 255)
End Function ' cBlue~&

Function cBluePurple~& ()
    cBluePurple = _RGB32(64, 0, 255)
End Function ' cBluePurple~&

Function cDeepPurple~& ()
    cDeepPurple = _RGB32(96, 0, 255)
End Function ' cDeepPurple~&

Function cPurple~& ()
    cPurple = _RGB32(128, 0, 255)
End Function ' cPurple~&

Function cPurpleRed~& ()
    cPurpleRed = _RGB32(128, 0, 192)
End Function ' cPurpleRed~&

Function cDarkRed~& ()
    cDarkRed = _RGB32(160, 0, 64)
End Function ' cDarkRed~&

Function cBrickRed~& ()
    cBrickRed = _RGB32(192, 0, 32)
End Function ' cBrickRed~&

Function cDarkGreen~& ()
    cDarkGreen = _RGB32(0, 100, 0)
End Function ' cDarkGreen~&

Function cGreen~& ()
    cGreen = _RGB32(0, 128, 0)
End Function ' cGreen~&

Function cOliveDrab~& ()
    cOliveDrab = _RGB32(107, 142, 35)
End Function ' cOliveDrab~&

Function cLightPink~& ()
    cLightPink = _RGB32(255, 182, 193)
End Function ' cLightPink~&

Function cHotPink~& ()
    cHotPink = _RGB32(255, 105, 180)
End Function ' cHotPink~&

Function cDeepPink~& ()
    cDeepPink = _RGB32(255, 20, 147)
End Function ' cDeepPink~&

Function cMagenta~& ()
    cMagenta = _RGB32(255, 0, 255)
End Function ' cMagenta~&

Function cBlack~& ()
    cBlack = _RGB32(0, 0, 0)
End Function ' cBlack~&

Function cDimGray~& ()
    cDimGray = _RGB32(105, 105, 105)
End Function ' cDimGray~&

Function cGray~& ()
    cGray = _RGB32(128, 128, 128)
End Function ' cGray~&

Function cDarkGray~& ()
    cDarkGray = _RGB32(169, 169, 169)
End Function ' cDarkGray~&

Function cSilver~& ()
    cSilver = _RGB32(192, 192, 192)
End Function ' cSilver~&

Function cLightGray~& ()
    cLightGray = _RGB32(211, 211, 211)
End Function ' cLightGray~&

Function cGainsboro~& ()
    cGainsboro = _RGB32(220, 220, 220)
End Function ' cGainsboro~&

Function cWhiteSmoke~& ()
    cWhiteSmoke = _RGB32(245, 245, 245)
End Function ' cWhiteSmoke~&

Function cWhite~& ()
    cWhite = _RGB32(255, 255, 255)
End Function ' cWhite~&

Function cDarkBrown~& ()
    cDarkBrown = _RGB32(128, 64, 0)
End Function ' cDarkBrown~&

Function cLightBrown~& ()
    cLightBrown = _RGB32(196, 96, 0)
End Function ' cLightBrown~&

Function cKhaki~& ()
    cKhaki = _RGB32(240, 230, 140)
End Function ' cKhaki~&

Function cEmpty~& ()
    cEmpty = _RGB32(0, 0, 0, 0)
End Function ' cEmpty~&

' ################################################################################################################################################################
' END COLOR FUNCTIONS @COLOR
' ################################################################################################################################################################

' ################################################################################################################################################################
' #REFERENCE = SOME USEFUL STUFF FOR REFERENCE

' Type Name               Type suffix symbol   Minimum value                  Maximum value                Size in Bytes
' ---------------------   ------------------   ----------------------------   --------------------------   -------------
' _BIT                    `                    -1                             0                            1/8
' _BIT * n                `n                   -128                           127                          n/8
' _UNSIGNED _BIT          ~`                   0                              1                            1/8
' _BYTE                   %%                   -128                           127                          1
' _UNSIGNED _BYTE         ~%%                  0                              255                          1
' INTEGER                 %                    -32,768                        32,767                       2
' _UNSIGNED INTEGER       ~%                   0                              65,535                       2
' LONG                    &                    -2,147,483,648                 2,147,483,647                4
' _UNSIGNED LONG          ~&                   0                              4,294,967,295                4
' _INTEGER64              &&                   -9,223,372,036,854,775,808     9,223,372,036,854,775,807    8
' _UNSIGNED _INTEGER64    ~&&                  0                              18,446,744,073,709,551,615   8
' SINGLE                  ! or none            -2.802597E-45                  +3.402823E+38                4
' DOUBLE                  #                    -4.490656458412465E-324        +1.797693134862310E+308      8
' _FLOAT                  ##                   -1.18E-4932                    +1.18E+4932                  32(10 used)
' _OFFSET                 %&                   -9,223,372,036,854,775,808     9,223,372,036,854,775,807    Use LEN
' _UNSIGNED _OFFSET       ~%&                  0                              18,446,744,073,709,551,615   Use LEN
' _MEM                    none                 combined memory variable type  N/A                          Use LEN

' div: int1% = num1% \ den1%
' mod: rem1% = num1% MOD den1%

'                                _KEYDOWN Keyboard Values
'
' Esc  F1    F2    F3    F4    F5    F6    F7    F8    F9    F10   F11   F12   Sys  ScL Pause
'  27 15104 15360 15616 15872 16128 16384 16640 16896 17152 17408 34048 34304 +316 +302 +019
' `~  1!  2@  3#  4$  5%  6^  7&  8*  9(  0) -_ =+ BkSp   Ins   Hme   PUp   NumL   /     *    -
' 126 33  64  35  36  37  94  38  42  40  41 95 43   8   20992 18176 18688 +300   47    42   45
'  96 49  50  51  52  53  54  55  56  57  48 45 61
' Tab Q   W   E   R   T   Y   U   I   O   P  [{  ]}  \|   Del   End   PDn   7Hme  8/?   9PU   +
'  9  81  87  69  82  84  89  85  73  79  80 123 125 124 21248 20224 20736 18176 18432 18688 43
'    113 119 101 114 116 121 117 105 111 112  91  93  92                    55    56    57
' CapL   A   S   D   F   G   H   J   K   L   ;:  '" Enter                   4/?-   5    6/-?
' +301  65  83  68  70  71  72  74  75  76  58  34  13                     19200 19456 19712  E
'       97 115 100 102 103 104 106 107 108  59  39                          52    53    54    n
' Shift   Z   X   C   V   B   N   M   ,<  .>  /?    Shift       ?           1End  2/?   3PD   t
' +304   90  88  67  86  66  78  77  60  62  63    +303       18432        20224 20480 20736  e
'       122 120  99 118  98 110 109  44  46  47                             49    50    51    r
' Ctrl   Win  Alt     Spacebar      Alt  Win  Menu  Ctrl   ?-   ?   -?      0Ins        .Del
' +306  +311 +308       32         +307 +312 +319  +305 19200 20480 19712  20992       21248 13

' Width   Height   Aspect Ratio   Name                    Comments
'   320      240   4:3            Standard resolution     Classic arcade games & console emulators
'   640      480   4:3            VGA                     really really old PCs
'   800      600   4:3            SVGA                    really old PCs
'  1024      768   4:3            XGA                     old PCs
'  1280     1024   5:4            SXGA                    old PCs
'   720     1600   5.33:1         HD+                     budged phones
'  1080     2400   9:20           FHD+                    mid-range premium phones
'  1280      720   16:9           WXGA HD                 budget laptops
'  1366      768   16:9           HD / 720p               budget laptops
'  1440     3200   20:9           QHD+ (Quad HD+)         high-end mobile
'  1900     1080   16:9           Full HD (FHD) / 1080p   HD monitors
'  2560     1440   16:9           QHD (Quad HD)           Lenovo, Thinkpad, Pixel XL
'  3840     2160   16:9           4k UHD                  4k monitors
'  2208     1768   1.25:1         QXGA+                   foldable display (Galaxy Z fold)
'  1344     2772   1:2.06         ? (some list as QHD)    foldable display (Galaxy Z fold)

' @REFERENCE
' ################################################################################################################################################################


Attached Files
.bas   VanHalenizer2-05.bas (Size: 210.42 KB / Downloads: 13)
Reply
#2
go ahead and jump!

do it to julia!

that's why i liked pointer sisters song which was also called "jump."  or something like that.

orwell's book was really deep stuff.  but "animal farm" was better.

sorry for off-topic.  but i had to say something about the baby in the album cover.
Reply
#3
Thank you for replying, I posted this and... nothing! 
I guess not a lot of music fans on here (or Halen fans at least!)

Animal Farm was good, 1984 too. These days it feels like both of those are more relevant than ever, what with big tech and surveillance and insane politicians on both sides. 

Let's hear it for the Pointer Sisters! 
Tongue
Reply
#4
We have an update - see Post #1 for the latest v2.0
Reply
#5
Sorry, I looked, thought WTF! and as such a response was not required! in my defence I am English so Queen, Elton John, Beetles, Pulp, Blur, Oasis...no worries! But Van Halen! Sorry...whilst im sure if you told me a line from a song i may recognise it but for the life of me, I cant name a single song! 

Also, as for no replies! You've got to that plateau....the OG's here consider you the same as them...they/I will run your code but feel no need to reply...it's actually complimentary! Trust me, they/I run it, smile and with the knowledge that you can and probably will make any mods in the future...feel that commenting is spurious! Often we forget to say "Well Done" to those from whom we have come to expect "Extra Level" code...I'm much the same as you, I spend days making a new, in my view amazing library and get >100 view with no replies! Makes me feel down hearted on occasion too! 

Keep it coming...you are an amazing asset to QB64 PE! 

John
Reply
#6
Thanks @UnseenMachine, you're the best! 
I'm updating the code for the last time this weekend, it now auto-detects screen resolution (for Windows at least) so it'll fill the full screen now.
Reply
#7
I wanted to do one more update before the next big update (turning into a proper screensaver). 
New features: 
  • Lets you specify how many seconds each color scheme runs.
  • Lets you specify seconds to display each screen.
  • Reloads settings from last session & saves settings when you exit.
  • Option to show/hide "save image / redraw / return to menu" menu.
  • Runs at full resolution/full screen - no need for header files or calculating window size

See original post above, updated with latest code.
Reply
#8
Added more config options and tweaks 
  • Specify screen resolution (still runs fullscreen, but you can run at lower res than your _displayWidth / _displayHeight)
  • Added logic to fit logo/menu on screen at lower resolutions.
  • Time delay setting to run slower.
  • Optional inputbox popup after saving image, shows filename (can be copied to clipboard).

See original post above, updated with latest.
Reply
#9
(10-26-2025, 12:18 AM)Unseen Machine Wrote: But Van Halen! Sorry...whilst im sure if you told me a line from a song i may recognise it but for the life of me, I cant name a single song! 

it was the guy with the guitar showing off.  at least recall that.  no need to recall lyrics.

the "1984" album.  was actually a silly record.  also because d.l.roth was their lead singer at the time.  after that ran off to do his own even siller record.  this is just my opinion.

now i have to figure out.  exactly which vanhalen video.  i saw eddie showing off.
Reply
#10
(11-04-2025, 06:17 PM)hsiangch_ong Wrote:
(10-26-2025, 12:18 AM)Unseen Machine Wrote: But Van Halen! Sorry...whilst im sure if you told me a line from a song i may recognise it but for the life of me, I cant name a single song! 

it was the guy with the guitar showing off.  at least recall that.  no need to recall lyrics.

the "1984" album.  was actually a silly record.  also because d.l.roth was their lead singer at the time.  after that ran off to do his own even siller record.  this is just my opinion.

now i have to figure out.  exactly which vanhalen video.  i saw eddie showing off.
A guy showing off on guitar made me instantly think of Hendrix...and Tenacious D!

John
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  PDF Generation update justsomeguy 7 576 12-22-2025, 12:34 AM
Last Post: justsomeguy
  Pointlessly Walk or Run forever! (update 0002) Cobalt 14 2,648 01-24-2025, 07:04 PM
Last Post: bplus
  Need for Speed High Stakes menu simulator (update 1) paulel 0 538 03-30-2024, 08:50 PM
Last Post: paulel

Forum Jump:


Users browsing this thread: 1 Guest(s)