old Microsoft demo (3d torus)

Screenshots, descriptions and links to applications compiled with LB Booster, to illustrate how it is being used
tsh73
Posts: 44
Joined: Fri Apr 06, 2018 7:58 pm

old Microsoft demo (3d torus)

Post by tsh73 »

Here it is at QB64 site
qb64.com/samples/torus-demo/
( this version was probably converted to brand new QB64 so I did not managed to run it as is )

Now it works in LB/LBB/(even JB if change API call with commented-out filled triangle sub by Andy Amaya)
Image
Image

It was rather complex conversion (long program), and I used Notepad++/LBB/JB/LB
I used LBB to spot erroneous lines and convert it one-by-one
- because it just selected next errorand stop there
(while JB made error message without particular line to fix)
I used JB to actually debug stuff (because I am used to it)
I used LB to put fast API call (polygon) instead of slow filling triangle by lines
Then I run it in LBB and found black rectangles all over the colored tiles
Happened to be (topic in LBB help section on Troubleshooting)
that LBB needs some wait statements to "reconcile" API-drawn graphics
(or so I got)
So I changed busy loop to Timer/Wait, now program works on both LB and LBB.
'from qb64.com/samples/torus-demo/
'conversion to JB by tsh73
'March 2024

'-----------------------------------------------------------------------------------------------------
'                                TORUS
'   This program draws a Torus figure. The program accepts user input
'   to specify various TORUS parameters. It checks the current system
'   configuration and takes appropriate action to set the best possible
'   initial mode.
'-----------------------------------------------------------------------------------------------------

global FALSE, TRUE
global C.RNDM, C.START, C.CONTINUE
global VGA', MCGA, EGA256, EGA64, MONO, HERC, CGA
global BACK$
BACK$ = "black"
'BACK$ = "darkblue"

'Sub TorusDefine
global TOR.Thick, TOR.Bord$, TOR.Panel, TOR.Sect, TOR.XDegree, TOR.YDegree, TOR.Delay

'Sub SetConfig
global VC.Colors, VC.Atribs, VC.XPix, VC.YPix, VC.TCOL, VC.TROW, VC.Scrn

global QuitRequested, Pi

'Sub TileDraw
Global T.x1,T.x2,T.x3,T.x4,T.y1,T.y2,T.y3,T.y4,T.z1,T.xc,T.yc,T.TColor

'Sub TorusCalc
'indices for columns in T(tile, column)
Global Ix1, Ix2, Ix3, Ix4, Iy1, Iy2, Iy3, Iy4, Iz1, Ixc, Iyc, ITColor

Dim T(10, 12)    'to be redimmed
Ix1=1:Ix2=2:Ix3=3:Ix4=4:Iy1=5:Iy2=6:Iy3=7:Iy4=8:Iz1=9:Ixc=10:Iyc=11:ITColor=12

'Sub TorusColor
global Max

'Sub TorusRotate    , to preserve between calls
global FirstClr

Pi=acs(-1)

' General purpose constants
FALSE = 0: TRUE = Not( FALSE)
BACK = 0
TROW = 24: TCOL = 60

' Rotation flags
C.RNDM = -1: C.START = 0: C.CONTINUE = 1

' Constants for best Available screen mode
VGA = 12    'set on this
' MCGA = 13
' EGA256 = 9
' EGA64 = 8
' MONO = 10
' HERC = 3
' CGA = 1

' User-defined type for tiles - an array of these make a torus
' used array T(numTiles, 12) instead, with colNumbers Ix1, Ix2, ..., ITColor
' Type Tile
    ' x1 As Single
    ' x2 As Single
    ' x3 As Single
    ' x4 As Single
    ' y1 As Single
    ' y2 As Single
    ' y3 As Single
    ' y4 As Single
    ' z1 As Single
    ' xc As Single
    ' yc As Single
    ' TColor As Integer
' End Type

' User-defined type to hold information about the mode
' Type Config
    ' Scrn As Integer
    ' Colors As Integer
    ' Atribs As Integer
    ' XPix As Integer
    ' YPix As Integer
    ' TCOL As Integer
    ' TROW As Integer
' End Type

'''Dim VC As Config
'only single instance
'used global vars VC.Scrn etc instead

' User-defined type to hold information about current Torus
' Type TORUS
    ' Panel As Integer
    ' Sect As Integer
    ' Thick As Single
    ' XDegree As Integer
    ' YDegree As Integer
    ' Bord As String * 3
    ' Delay As Single
' End Type

''Dim TOR As TORUS, Max As Integer
'only single instance
'used global vars TORUS.Panel etc instead

' A palette of colors to paint with
Dim Pal(300) 'As Long
'added to use with JB
Dim Pal$(300)
Dim Colr$(300)


STRUCT PolyPoints,_
    x1 as long,_
    y1 as long,_
    x2 as long,_
    y2 as long,_
    x3 as long,_
    y3 as long,_
    x4 as long,_
    y4 as long

' The code of the module-level program begins here

' Initialize defaults
TOR.Thick = 3: TOR.Bord$ = "YES"
TOR.Panel = 8: TOR.Sect = 14
TOR.XDegree = 60: TOR.YDegree = 165

' Get best configuration and set initial graphics mode to it
'just set for VGA for now
VC.Scrn = VGA

Do While TRUE ' Loop forever (exit is from within a SUB)

    ' Get Torus definition from user
    call TorusDefine

    ' Dynamically dimension arrays
    Tmp = TOR.Panel
    Max = TOR.Panel * TOR.Sect

    ' Array for indexes
    ReDim Index(Max - 1)
    ' Array for tiles
    ReDim T(Max - 1, 12) ''As Tile

    ' Initialize array of indexes
    For Til = 0 To Max - 1
        Index(Til) = Til
    Next

    ' Calculate the points of each tile on the torus
    call Message "Calculating"
    call TorusCalc    '' T(max, 12),  and arrays are global in JB

    ' Sort the tiles by their "distance" from the screen
    call Message "Sorting"
    call TorusSort 0, Max - 1

    'open corresponding gr window
    '    ajust for borders
    desiredWidth = VC.XPix+1
    desiredHeight = VC.YPix+1
    gosub [ajustWindow]

    UpperLeftX=int((DisplayWidth-WindowWidth)/2)
    UpperLeftY=int((DisplayHeight-WindowHeight)/2)

    open "Torus" for graphics_nsb_nf as #gr
    #gr, "trapclose [quit]"
    #gr, "down; fill ";BACK$
    #gr, "flush"

    ' Mix a palette of colors
    call SetPalette

    ' Color each tile in the torus.
    call TorusColor

    ' Set logical window with variable thickness
    ' Center is 0, up and right are positive, down and left are negative
    ''Window (-(TOR.Thick + 1), -(TOR.Thick + 1))-(TOR.Thick + 1, TOR.Thick + 1)
    global width, minX, maxX, height, minY, maxY
    width=VC.XPix+1
    minX=0-(TOR.Thick + 1)
    maxX=TOR.Thick + 1
    height=VC.YPix+1
    minY=0-(TOR.Thick + 1)
    maxY=TOR.Thick + 1

    global hdc
    h=hwnd(#gr)  'window handle

    'get device context for window:
    calldll #user32, "GetDC",_
    h as ulong,_ 'window handle
    hdc as ulong 'returns handle to device context

    ' Draw and paint the tiles, the farthest first and nearest last
    call Message "Drawing"
    call TorusDraw

    ' Rotate the torus by rotating the color palette
    Do While 1''InKey$ = ""
        SCAN
        'call Delay TOR.Delay
        'if QuitRequested then [quit]
        timer TOR.Delay*1000, [waitABit]
        wait
[waitABit]
        timer 0

        #gr, "discard"
        call TorusRotate C.CONTINUE
        call Message "Drawing"
        call TorusDraw
    Loop
Loop

[quit]
    calldll #user32, "ReleaseDC",_
    h as ulong,_    'window handle
    hdc as ulong,_  'device context
    ret as long

    timer 0
    close #gr
end

' ============================ CountTiles ==============================
'   Displays number of the tiles currently being calculated or sorted.
' ======================================================================
'
Sub CountTiles T1, T2
    Print "Tile "; Using (" ###", T1); Using (" ###", T2)
End Sub

' ============================ DegToRad ================================
'   Convert degrees to radians, since BASIC trigonometric functions
'   require radians.
' ======================================================================
'
Function DegToRad (Degrees)
    DegToRad = (Degrees * 2 * Pi) / 360
End Function

' ============================= Message ================================
'   Displays a status message followed by blinking dots.
' ======================================================================
'
Sub Message Text$
    Print "-";
        print time$();".";time$("ms") mod 1000;
        print "-------------------------"
    'Print "-22:17:30.421-------------------------"
    Print Text$
    Print "--------------------------------------"
End Sub

' ============================ SetConfig ===============================
'   Sets the correct values for each field of the VC variable. They
'   vary depending on Mode and on the current configuration.
' ======================================================================
'
Sub SetConfig mode 'use VGA for now
    'Case 12 ' 16-color very high-res graphics for VGA
        VC.Colors = 216
        VC.Atribs = 16
        VC.XPix = 639
        VC.YPix = 479
            'VC.XPix = 319
            'VC.YPix = 239
        VC.TCOL = 80
        VC.TROW = 30
    VC.Scrn = mode
End Sub

' ============================ SetPalette ==============================
'   Mixes palette colors in an array.
' ======================================================================
'
Sub SetPalette
    VC.Colors = TOR.Sect    'this makes each section to have same color
    ' VC.Colors = TOR.Sect*2
    ' VC.Colors = TOR.Sect*TOR.Panel    '==Max == number of tiles
    ' VC.Colors = Max
    ' VC.Colors =256
    for i = 0 to VC.Colors-1
        Colr$(i)=rainbow$(i/VC.Colors)
    next

    ' Assign colors

    call TorusRotate C.RNDM
    ' print "--- SetPalette -----"
    ' print "VC.Colors",VC.Colors
    ' print "Index", Index
    ' for i = 0 to VC.Colors-1
        ' print i, Colr$(i)
    ' next
    ' print "--- //SetPalette ---"
End Sub

' ============================ TileDraw ================================
'   Draw and optionally paint a tile. Tiles are painted if there are
'   more than two atributes and if the inside of the tile can be found.
' ======================================================================
'
Sub TileDraw
    'copyToGlobT is called before

    'fill the tile - as 2 triangles
    activeColr=(T.TColor+FirstClr) mod VC.Colors
    'print "activeColr ",activeColr, Colr$(activeColr)
    'LB way - API call
    #gr "backcolor ";Colr$(activeColr)
    nCount=4   'number of x,y pairs in STRUCT
    PolyPoints.x1.struct = sx(T.x1)
    PolyPoints.y1.struct = sy(T.y1)
    PolyPoints.x2.struct = sx(T.x2)
    PolyPoints.y2.struct = sy(T.y2)
    PolyPoints.x3.struct = sx(T.x3)
    PolyPoints.y3.struct = sy(T.y3)
    PolyPoints.x4.struct = sx(T.x4)
    PolyPoints.y4.struct = sy(T.y4)

    calldll #gdi32, "Polygon",_
    hdc as ulong,_        'device context of window or control
    PolyPoints as struct,_'array of points
    nCount as long,_      'number of x,y pairs in array
    result as long

    ''''JB way
    ' #gr "color ";Colr$(activeColr)
    ' call fillTriangle "#gr",sx(T.x1),sy(T.y1),sx(T.x2),sy(T.y2),sx(T.x3),sy(T.y3)
    ''paint over possible diagonal line
        ' #gr "size 2"
        ' #gr "line ";sx(T.x1);" ";sy(T.y1);" ";sx(T.x3);" ";sy(T.y3)
        ' #gr "size 1"
    ' call fillTriangle "#gr",sx(T.x1),sy(T.y1),sx(T.x4),sy(T.y4),sx(T.x3),sy(T.y3)

    ' A border drawn with the background color looks like a border.
    ' One drawn with the tile color doesn't look like a border.
    If TOR.Bord$ = "YES" Then
        Border$ = BACK$
    Else
        Border$ = Colr$(activeColr)
    End If
    ' Redraw with the final border
    ' Line (T.x1, T.y1)-(T.x2, T.y2), Border
    ' Line -(T.x3, T.y3), Border
    ' Line -(T.x4, T.y4), Border
    ' Line -(T.x1, T.y1), Border
    #gr "color ";Border$ 
    #gr "line ";sx(T.x1);" ";sy(T.y1);" ";sx(T.x2);" ";sy(T.y2)
    #gr "goto ";sx(T.x3);" ";sy(T.y3)
    #gr "goto ";sx(T.x4);" ";sy(T.y4)
    #gr "goto ";sx(T.x1);" ";sy(T.y1)
End Sub

' =========================== TorusCalc ================================
'   Calculates the x and y coordinates for each tile.
' ======================================================================
'
Sub TorusCalc ''(T() As Tile) Static    'now use T(tile, column)

    ' Calculate sine and cosine of the angles of rotation
    XRot = DegToRad(TOR.XDegree)
    YRot = DegToRad(TOR.YDegree)
    CXRot = Cos(XRot)
    SXRot = Sin(XRot)
    CYRot = Cos(YRot)
    SYRot = Sin(YRot)

    ' Calculate the angle to increment between one tile and the next.
    XInc = 2 * Pi / TOR.Sect
    YInc = 2 * Pi / TOR.Panel

    ' First calculate the first point, which will be used as a reference
    ' for future points. This point must be calculated separately because
    ' it is both the beginning and the end of the center seam.
    FirstY = (TOR.Thick + 1) * CYRot

    ' Starting point is x1 of 0 section, 0 panel     last     0
    T(0,Ix1) = FirstY                            ' +------+------+
    ' Also x2 of tile on last section, 0 panel   ' |      |      | last
    T(TOR.Sect - 1,Ix2) = FirstY                 ' |    x3|x4    |
    ' Also x3 of last section, last panel        ' +------+------+
    T(Max - 1,Ix3) = FirstY                      ' |    x2|x1    |  0
    ' Also x4 of 0 section, last panel           ' |      |      |
    T(Max - TOR.Sect,Ix4) = FirstY               ' +------+------+
    ' A similar pattern is used for assigning all points of Torus

    ' Starting Y point is 0 (center)
    T(0,Iy1) = 0
    T(TOR.Sect - 1,Iy2) = 0
    T(Max - 1,Iy3) = 0
    T(Max - TOR.Sect,Iy4) = 0

    ' Only one z coordinate is used in sort, so other three can be ignored
    T(0,Iz1) = 0-(TOR.Thick + 1) * SYRot

    ' Starting at first point, work around the center seam of the Torus.
    ' Assign points for each section. The seam must be calculated separately
    ' because it is both beginning and of each section.
    For XSect = 1 To TOR.Sect - 1

        ' X, Y, and Z elements of equation
        sx = (TOR.Thick + 1) * Cos(XSect * XInc)
        sy = (TOR.Thick + 1) * Sin(XSect * XInc) * CXRot
        sz = (TOR.Thick + 1) * Sin(XSect * XInc) * SXRot
        ssx = (sz * SYRot) + (sx * CYRot)

        T(XSect,Ix1) = ssx
        T(XSect - 1,Ix2) = ssx
        T(Max - TOR.Sect + XSect - 1,Ix3) = ssx
        T(Max - TOR.Sect + XSect,Ix4) = ssx

        T(XSect,Iy1) = sy
        T(XSect - 1,Iy2) = sy
        T(Max - TOR.Sect + XSect - 1,Iy3) = sy
        T(Max - TOR.Sect + XSect,Iy4) = sy

        T(XSect,Iz1) = (sz * CYRot) - (sx * SYRot)
    Next

    ' Now start at the first seam between panel and assign points for
    ' each section of each panel. The outer loop assigns the initial
    ' point for the panel. This point must be calculated separately
    ' since it is both the beginning and the end of the seam of panels.
    For YPanel = 1 To TOR.Panel - 1

        ' X, Y, and Z elements of equation
        sx = TOR.Thick + Cos(YPanel * YInc)
        sy = 0-Sin(YPanel * YInc) * SXRot
        sz = Sin(YPanel * YInc) * CXRot
        ssx = (sz * SYRot) + (sx * CYRot)

        ' Assign X points for each panel
        ' Current ring, current side
        T(TOR.Sect * YPanel,Ix1) = ssx
        ' Current ring minus 1, next side
        T(TOR.Sect * (YPanel + 1) - 1,Ix2) = ssx
        ' Current ring minus 1, previous side
        T(TOR.Sect * YPanel - 1,Ix3) = ssx
        ' Current ring, previous side
        T(TOR.Sect * (YPanel - 1),Ix4) = ssx

        ' Assign Y points for each panel
        T(TOR.Sect * YPanel,Iy1) = sy
        T(TOR.Sect * (YPanel + 1) - 1,Iy2) = sy
        T(TOR.Sect * YPanel - 1,Iy3) = sy
        T(TOR.Sect * (YPanel - 1),Iy4) = sy

        ' Z point for each panel
        T(TOR.Sect * YPanel,Iz1) = (sz * CYRot) - (sx * SYRot)

        ' The inner loop assigns points for each ring (except the first)
        ' on the current side.
        For XSect = 1 To TOR.Sect - 1

            ' Display section and panel
            call CountTiles XSect, YPanel

            ty = (TOR.Thick + Cos(YPanel * YInc)) * Sin(XSect * XInc)
            tz = Sin(YPanel * YInc)
            sx = (TOR.Thick + Cos(YPanel * YInc)) * Cos(XSect * XInc)
            sy = ty * CXRot - tz * SXRot
            sz = ty * SXRot + tz * CXRot
            ssx = (sz * SYRot) + (sx * CYRot)

            T(TOR.Sect * YPanel + XSect,Ix1) = ssx
            T(TOR.Sect * YPanel + XSect - 1,Ix2) = ssx
            T(TOR.Sect * (YPanel - 1) + XSect - 1,Ix3) = ssx
            T(TOR.Sect * (YPanel - 1) + XSect,Ix4) = ssx

            T(TOR.Sect * YPanel + XSect,Iy1) = sy
            T(TOR.Sect * YPanel + XSect - 1,Iy2) = sy
            T(TOR.Sect * (YPanel - 1) + XSect - 1,Iy3) = sy
            T(TOR.Sect * (YPanel - 1) + XSect,Iy4) = sy

            T(TOR.Sect * YPanel + XSect,Iz1) = (sz * CYRot) - (sx * SYRot)
        Next
    Next
    ' Erase message
    call CountTiles -1, -1
End Sub

' =========================== TorusColor ===============================
'   Assigns color atributes to each tile.
' ======================================================================
'
Sub TorusColor

    ' Cycle through each attribute until all tiles are done
    For Til = 0 To Max - 1
        T(Til,ITColor) = Til mod VC.Colors
        print "Colr",Til, T(Til,ITColor)
    Next

End Sub

' ============================ TorusDefine =============================
'   Define the attributes of a Torus based on information from the
'   user, the video configuration, and the current screen mode.
' ======================================================================
'
Sub TorusDefine 'LB window to setup params
    WindowWidth = 328
    WindowHeight = 260

    UpperLeftX=int((DisplayWidth-WindowWidth)/2)
    UpperLeftY=int((DisplayHeight-WindowHeight)/2)

    statictext #main.statictext1, "Thickness", 22, 16, 144, 20
    textbox #main.txtThick, 190, 11, 100, 25
    statictext #main.statictext3, "Panels per Section", 22, 41, 144, 20
    textbox #main.txt.Panel, 190, 36, 100, 25
    statictext #main.statictext5, "Sections per Torus", 22, 66, 144, 20
    textbox #main.txt.Sect, 190, 61, 100, 25
    statictext #main.statictext7, "Tilt around Horizontal Axis", 22, 91, 144, 20
    textbox #main.txtXDegree, 190, 86, 100, 25
    statictext #main.statictext9, "Tilt around Vertical Axis", 22, 116, 144, 20
    textbox #main.txtYDegree, 190, 111, 100, 25
    statictext #main.statictext11, "Tile Border", 22, 141, 144, 20
    textbox #main.txtBord, 190, 136, 100, 25
    statictext #main.statictext13, "Screen Mode", 22, 166, 144, 20
    statictext #main.lblScrn, "12 (640x480)", 190, 166, 144, 20
    button #main.button16, "Start", [btnStartClick], UL, 22, 191, 122, 25
    button #main.button17, "Quit", [btnQuitClick], UL, 174, 191, 122, 25
    open "Torus" for window_nf as #main
    print #main, "trapclose [quit.main]"

    print #main, "font ms_sans_serif 10"
    #main.txtThick  TOR.Thick
    #main.txt.Panel TOR.Panel
    #main.txt.Sect  TOR.Sect
    #main.txtXDegree TOR.XDegree
    #main.txtYDegree TOR.YDegree
    #main.txtBord   TOR.Bord$
    ' #main.lblScrn    VC.Scrn

    #main.button16, "!setfocus"
    wait

[quit.main]
    Close #main
    END


[btnStartClick]
    'get data and return
    errList$=chr$(13)
    #main.txtThick "!contents? TOR.Thick"   '1, 9
    errList$=errList$+chkRange$("TOR.Thick", TOR.Thick, 1, 9)
    #main.txt.Panel "!contents? TOR.Panel"  '6, 20
    errList$=errList$+chkRange$("TOR.Panel", TOR.Panel, 6, 20)
    #main.txt.Sect  "!contents? TOR.Sect"   '6, 20
    errList$=errList$+chkRange$("TOR.Sect", TOR.Sect, 6, 20)
    #main.txtXDegree "!contents? TOR.XDegree"   '0, 345, by 15deg
    errList$=errList$+chkRange$("TOR.XDegree", TOR.XDegree, 0, 345)
    #main.txtYDegree "!contents? TOR.YDegree"   '0, 345, by 15deg
    errList$=errList$+chkRange$("TOR.YDegree", TOR.YDegree, 0, 345)
    #main.txtBord   "!contents? TOR.Bord$"      'YES NO
    if (TOR.Bord$<>"YES") and (TOR.Bord$<>"NO") then
        errList$=errList$+"TOR.Bord$ value (";TOR.Bord$;") should be YES or NO"
    end if
    if trim$(errList$)<>"" then
        notice "Errors found: ";errList$ 
        wait
    end if
    Close #main

    call SetConfig VC.Scrn

    ' Set different delays depending on mode
        'Case Else
    TOR.Delay = 1    '.05    'drawing torus take lots of time

    ' Get new random seed for this torus
    '    JB uses new random each run

    exit sub
    wait

[btnQuitClick]    'Perform action for the button named 'button17'
    goto [quit.main]
end sub

' =========================== TorusDraw ================================
'   Draws each tile of the torus starting with the farthest and working
'   to the closest. Thus nearer tiles overwrite farther tiles to give
'   a three-dimensional effect. Notice that the index of the tile being
'   drawn is actually the index of an array of indexes. This is because
'   the array of tiles is not sorted, but the parallel array of indexes
'   is. See TorusSort for an explanation of how indexes are sorted.
' ======================================================================
'
Sub TorusDraw

    For Til = 0 To Max - 1
        call copyToGlobT Til 'T(Index(Til)) - >T.*
        'print "Tile ",Til,
        call TileDraw ''T(Index(Til))
    Next
End Sub

' =========================== TorusRotate ==============================
'   Rotates the Torus. This can be done more successfully in some modes
'   than in others. There are three methods:
'
'     1. Rotate the palette colors assigned to each attribute
'     2. Draw, erase, and redraw the torus (two-color modes)
'     3. Rotate between two palettes (CGA and MCGA screen 1)
'
'   Note that for EGA and VGA screen 2, methods 1 and 2 are both used.
' ======================================================================
'
Sub TorusRotate First

    ' Argument determines whether to start at next color, first color,
    ' or random color
    Select Case First
        Case C.RNDM
            FirstClr = Int(Rnd(0) * VC.Colors)
        Case C.START
            FirstClr = 0
        Case Else
            FirstClr = (FirstClr+1) mod VC.Colors
    End Select

End Sub

' ============================ TorusSort ===============================
'   Sorts the tiles of the Torus according to their Z axis (distance
'   from the "front" of the screen). When the tiles are drawn, the
'   farthest will be drawn first, and nearer tiles will overwrite them
'   to give a three-dimensional effect.
'
'   To make sorting as fast as possible, the Quick Sort algorithm is
'   used. Also, the array of tiles is not actually sorted. Instead a
'   parallel array of tile indexes is sorted. This complicates things,
'   but makes the sort much faster, since two-byte integers are swapped
'   instead of 46-byte Tile variables.
' ======================================================================
'
Sub TorusSort Low, High
'basically, qsort of indices T(Index(i),Iz1)
    If Low < High Then
        ' If only one, compare and swap if necessary
        ' The SUB procedure only stops recursing when it reaches this point
        If High - Low = 1 Then
            If T(Index(Low),Iz1) > T(Index(High),Iz1) Then
                call CountTiles High, Low
                call swapIndex Low,High
            End If
        Else
            ' If more than one, separate into two random groups
            RandIndex = Int(Rnd * (High - Low + 1)) + Low
            call CountTiles High, Low
            call swapIndex High, RandIndex
            Partition = T(Index(High),Iz1)
            ' Sort one group
            Do
                i = Low: j = High
                ' Find the largest
                Do While (i < j) And (T(Index(i),Iz1) <= Partition)
                    i = i + 1
                Loop
                ' Find the smallest
                Do While (j > i) And (T(Index(j),Iz1) >= Partition)
                    j = j - 1
                Loop
                ' Swap them if necessary
                If i < j Then
                    call CountTiles High, Low
                    call swapIndex i, j
                End If
            Loop While i < j

            ' Now get the other group and recursively sort it
            call CountTiles High, Low
            call swapIndex i, High
            If (i - Low) < (High - i) Then
                call TorusSort Low, i - 1
                call TorusSort i + 1, High
            Else
                call TorusSort i + 1, High
                call TorusSort Low, i - 1
            End If
        End If
    End If
End Sub

'- aux funcs by tsh73, for TileDraw ------------------------------
'should be global
'Global T.x1,T.x2,T.x3,T.x4,T.y1,T.y2,T.y3,T.y4,T.z1,T.xc,T.yc,T.TColor

sub copyToGlobT Til
    T.x1=T(Index(Til), Ix1)
    T.x2=T(Index(Til), Ix2)
    T.x3=T(Index(Til), Ix3)
    T.x4=T(Index(Til), Ix4)
    T.y1=T(Index(Til), Iy1)
    T.y2=T(Index(Til), Iy2)
    T.y3=T(Index(Til), Iy3)
    T.y4=T(Index(Til), Iy4)
    T.z1=T(Index(Til), Iz1)
    T.xc=T(Index(Til), Ixc)
    T.yc=T(Index(Til), Iyc)
    T.TColor=T(Index(Til), ITColor)
end sub

'- aux func by Tsh73, for new Sub TorusDefine  -------------------
function chkRange$(varName$, varVal, mn, mx)
    if (varVal < mn) or (varVal > mx) then
        chkRange$=varName$;" value (";varVal;") is out of range [";mn;", ";mx;"]"+chr$(13)
    end if
end function

sub Delay sec    'now after pause you can check if QuitRequested
    t=time$("ms")
    while time$("ms")<t+sec*1000
        scan
    wend
    exit sub
[quit]
     QuitRequested=1
end sub

sub swapIndex idx1, idx2
    tmp=Index(idx1):Index(idx1)=Index(idx2):Index(idx2)=tmp
end sub

'conversions (logical coords to screen)
function sx(x)
'screen X. Depends on width, minX, maxX
    sx = int((x- minX)/(maxX-minX) * width)
end function

function sy(y)
'screen Y. Depends on height, minY, maxY. Upside down.
    sy = int((1-(y- minY)/(maxY-minY)) * height)
end function

'- Fast Filled Triangle sub by Andy Amaya ------------
Sub fillTriangle h$,x1, y1, x2, y2, x3, y3
    'triangle coordinates must be ordered: where x1 < x2 < x3
    If x2 < x1 Then x  = x2 : y  = y2 : x2 = x1 : y2 = y1 : x1 = x  : y1 = y
    'swap x1, y1, with x3, y3
    If x3 < x1 Then x  = x3 : y  = y3 : x3 = x1 : y3 = y1 : x1 = x  : y1 = y
    'swap x2, y2 with x3, y3
    If x3 < x2 Then x  = x3 : y  = y3 : x3 = x2 : y3 = y2 : x2 = x  : y2 = y
    If x1 <> x3 Then slope1 = (y3-y1)/(x3-x1)
    'draw the first half of the triangle
    length = x2 - x1
    If length <> 0 Then
        slope2 = (y2-y1)/(x2-x1)
        For x = 0 To length
        'if X is not integer, using INT on then will improve timing
            #h$ "Line ";int(x+x1);" ";int(x*slope1+y1);" ";int(x+x1);" ";int(x*slope2+y1)
            '#h$ "Line ";x+x1;" ";int(x*slope1+y1);" ";x+x1;" ";int(x*slope2+y1)
        Next
    End If
    'draw the second half of the triangle
    y = length*slope1+y1 : length = x3-x2
    If length <> 0 Then
        slope3 = (y3-y2)/(x3-x2)
        For x = 0 To length
            #h$ "Line ";int(x+x2);" ";int(x*slope1+y);" ";int(x+x2);" ";int(x*slope3+y2)
            '#h$ "Line ";x+x2;" ";int(x*slope1+y);" ";x+x2;" ";int(x*slope3+y2)
        Next
    End If
End Sub

'---------------------------------------------
' 0..1 into red-green-blue-red continuous colors
function rainbow$(x)
    hi = int((x*6) mod 6)+ 5*(x<0)  'fixed to 0..5
    f = (x*6) mod 1 + (x<0)         'frac, 0..1
    q = (1-f)
    select case hi
    case 0
        r = 1: g = f: b = 0
    case 1
        r = q: g = 1: b = 0
    case 2
        r = 0: g = 1: b = f
    case 3
        r = 0: g = q: b = 1
    case 4
        r = f: g = 0: b = 1
    case 5
        r = 1: g = 0: b = q
    end select
    R = int(r*255)
    G = int(g*255)
    B = int(b*255)
    rainbow$= R;" ";G;" ";B
end function

[ajustWindow]
    UpperLeftX = 20
    UpperLeftY = 20
    WindowWidth = 200   '100 seems to be too much - works different
    WindowHeight = 100
    open "Ajusting..." for graphics_nsb_nf as #gr
'   graphics
'   graphics_nsb
'   graphics_nsb_nf

    #gr, "home ; down ; posxy x y"
    'x, y give us width, height
    width = 2*x : height = 2*y
    close #gr

    slackX = 200-width
    slackY = 100-height

    WindowWidth = desiredWidth + slackX
    WindowHeight = desiredHeight + slackY

return