QB64.org Forum

Active Forums => Programs => Topic started by: bplus on July 15, 2019, 11:20:40 am

Title: and another one for your toolbox...
Post by: bplus on July 15, 2019, 11:20:40 am
OK I am sure this has been done 10x's over again and again...

But without a toolbox, one has to figure it out again and again when the occasion arises.

First the story, I was checking into XOR even before Steve provided the link in the Xbox tunnel of love thread when I discovered that &H has the reverse HEX$ and &O has the reverse OCT$ but poor 'ole &B has squat!

So here is BIN$ for your edification and toolbox:
Code: QB64: [Select]
  1. 'does QB64 reverse &B numbers conver HEX$ for base 16, OCT$ for base 8, no base 2?
  2.     INPUT "Enter integer to convert to binary (0 to quit) "; test
  3.     PRINT "Dec2Base$(test, 2) = "; Dec2Base$(test, 2), "BIN$(test) = "; BIN$(test)
  4.     PRINT "and  VAL(" + CHR$(34) + "&B" + CHR$(34) + " + BIN$(test)) ="; VAL("&B" + BIN$(test))
  5.     PRINT
  6. LOOP UNTIL test = 0
  7.  
  8. FUNCTION Dec2Base$ (nDec AS INTEGER, bs AS INTEGER)
  9.     DIM n AS INTEGER, p AS INTEGER, pow AS INTEGER, d AS INTEGER, b$
  10.     n = nDec 'copy this because or nDec gets changed effecting main code
  11.     WHILE bs ^ p <= n
  12.         p = p + 1
  13.     WEND
  14.     FOR pow = p TO 0 STEP -1
  15.         IF n >= bs ^ pow THEN
  16.             d = INT(n / bs ^ pow)
  17.             IF d < 10 THEN
  18.                 b$ = b$ + _TRIM$(STR$(d))
  19.             ELSE
  20.                 b$ = b$ + CHR$(65 + d - 10)
  21.             END IF
  22.             n = n - d * bs ^ pow
  23.         ELSE
  24.             IF b$ <> "" THEN b$ = b$ + "0"
  25.         END IF
  26.     NEXT
  27.     IF b$ = "" THEN b$ = "0"
  28.     Dec2Base$ = b$
  29.  
  30. '2019-12-02 latest eliminates &B prefix
  31. FUNCTION BIN$ (integerBase10 AS _INTEGER64) 'no more &B because easier to add than to remove
  32.     DIM j AS INTEGER
  33.     IF integerBase10 = 0 THEN BIN$ = "0": EXIT FUNCTION
  34.     WHILE 2 ^ j <= integerBase10
  35.         IF (integerBase10 AND 2 ^ j) > 0 THEN BIN$ = "1" + BIN$ ELSE BIN$ = "0" + BIN$
  36.         j = j + 1
  37.     WEND
  38.  

Who needs Bill, the toolbox librarian, just start your own thread! ;- +

BTW, wouldn't it be better if HEX$(base10Integer) returned the string as "&H...." and OCT$ as "&O..." ?

You know, label the number base if it is not your standard base 10.

EDIT: For library code, probably should DIM everything, something a librarian might remind us of if one were present. ;-))

EDIT: 2019-12-01 more elegant version of BIN$
EDIT: 2019-12-02 reconsidered again with RhoSigma's input no &B prefix, also get rid of the limit test for integer and the other junk in demo! see reasons in reply #46


UPDATE: This stuff has been incorporated into QB64 since v1.3 I think, 1.4 for sure.
Title: Re: and another one for your toolbox...
Post by: bplus on July 15, 2019, 12:19:52 pm
It is important to have a palindrome detector handy, so that when one is in our midst, we might know:

Code: QB64: [Select]
  1. 'Palindrome tester.txt for JB 2015-11-01 MGA/B+ thanks to BP.org and Wiki
  2. DIM t$
  3. WHILE t$ <> "END"
  4.     READ t$
  5.     PRINT t$;
  6.     IF isPalindrome(t$) THEN PRINT " is a palindrome." ELSE PRINT " is NOT a palindrome."
  7. DATA "A dog, a plan, a canal: pagoda!","A dog, a panic, in a pagoda."
  8. DATA "A man, a plan, a cat, a ham, a yak, a yam, a hat, a canal-Panama!"
  9. DATA "Yada yada day a day"
  10. DATA "RATS","END"
  11.  
  12. FUNCTION isPalindrome (phrase$)
  13.     DIM copy$, b$, p$, char$, i AS INTEGER
  14.     copy$ = UCASE$(phrase$)
  15.     FOR i = 1 TO LEN(copy$) ' I don't see palindromes getting much longer than 32000
  16.         char$ = MID$(copy$, i, 1)
  17.         IF ASC(char$) > 64 AND ASC(char$) < 91 THEN b$ = b$ + char$: p$ = char$ + p$
  18.     NEXT
  19.     IF b$ = p$ THEN isPalindrome = -1
  20.  

Today we have a classic one in our midst.  ;-)) which reminded me of this old code exercise for people who need to get comfortable with using SUBs and FUNCTIONs.

EDIT: line 19 too long, too many redundant calls to MID$ for same thing. I bit the bullet and added another variable for clarity.

UPDATE: Hardy an auspicious start for a mighty thread ;-))
Title: Re: and another one for your toolbox...
Post by: johnno56 on July 15, 2019, 04:38:19 pm
"Madam, I'm Adam."
Title: Re: and another one for your toolbox...
Post by: bplus on July 28, 2019, 05:18:12 pm
Top Ten Code:
Code: QB64: [Select]
  1. _Title "Top Ten Tester" ' B+ started 2019-07-28
  2. '2019-07-28 All enclosed in a sub, possible feed it score and see how it stacks up.
  3. '2019-07-28 fix to fill all 10 slots.
  4. '2019-07-29 2nd fix with an n > 10 check, change to a function and get GoAgain reply.
  5.  
  6. ' ref https://www.qb64.org/forum/index.php?topic=1511.msg107586#msg107586
  7.  
  8. Dim score, again$
  9. 'testing function with random scores, would be good to start with
  10. For score = 1 To 15
  11.     again$ = topTenGoAgain$(score * 5 * Rnd)
  12.     If Len(again$) Then Print "Player wants to quit.": End Else Print "Player wants to go again."
  13.     If score = 15 Then
  14.         Print "End of test."
  15.     End If
  16.  
  17. ' This FUNCTION creates a file in the same folder as your .bas source or .exe
  18. 'EDIT: 2019-07-29 change to FUNCTION to combine Top Ten update functions with Go Again reply.
  19. Function topTenGoAgain$ (compareScore As Integer)
  20.     Dim fName$, n As Integer, names$(1 To 10), scores(1 To 10), name$, score As Integer
  21.     Dim settleScore As Integer, i As Integer, dummy$
  22.     fName$ = "Top 10 Scores.txt" '<<<  since this is toolbox code change this as needed for app
  23.     Cls: Print: Print "Your score was:"; compareScore: Print: Print "Top Ten Scorers and Scores:"
  24.     If _FileExists(fName$) Then
  25.         Open fName$ For Input As #1
  26.         While EOF(1) = 0 And n < 10
  27.             n = n + 1
  28.             Input #1, name$
  29.             Input #1, score
  30.             If compareScore >= score And settleScore = 0 Then
  31.                 Print "You have made the Top Ten!"
  32.                 Input "Type your name here: ", names$(n)
  33.                 scores(n) = compareScore
  34.                 settleScore = -1
  35.                 n = n + 1
  36.                 If n <= 10 Then names$(n) = name$: scores(n) = score
  37.             Else
  38.                 scores(n) = score: names$(n) = name$
  39.             End If
  40.         Wend
  41.         Close #1
  42.         If n < 10 And settleScore = 0 Then
  43.             Print "There is a slot open for your name and score."
  44.             Input "Type your name here: ", name$
  45.             If name$ <> "" Then n = n + 1: names$(n) = name$: scores(n) = compareScore
  46.         End If
  47.         Open fName$ For Output As #1
  48.         If n > 10 Then n = 10
  49.         For i = 1 To n
  50.             Print #1, names$(i): Print #1, scores(i)
  51.             Print i, names$(i), scores(i)
  52.         Next
  53.         Close #1
  54.     Else
  55.         Print "You are first into file!"
  56.         Input "Type your name here:"; name$
  57.         Open fName$ For Output As #1
  58.         Print #1, name$: Print #1, compareScore
  59.         Close #1
  60.     End If
  61.     Print: Input "Press <Enter> to play again, <q (or any) + Enter> to quit... "; dummy$
  62.     topTenGoAgain$ = dummy$
  63.  
  64.  
  65.  

Works OK for me. How did name$ get to be NAME$ here in forum post?

Update: Sorry, I did not test the sub enough, only with increasingly good scores. But what happens if score is not better and you have 9 slots left in Top Ten? You fill the next slot, Fixed.

Update: 2019-07-29 found another thing to fix, check if n > 10 Also changed the SUB to a FUNCTION and used it to get Go Again information from the player. It seems the perfect time to ask the player if they want to go again and combining functions saves the player a 2nd question and key press answer to make. This is demo'd in the test code.

2021-10-27 Modified for new Function change in QB64 v 2.0
Title: Re: and another one for your toolbox...
Post by: SMcNeill on July 31, 2019, 07:39:09 am
Here's one I think all toolboxes should have (if bplus doesn't mind me adding to his collection here):

Code: [Select]
SUB SubState (toggle)
    STATIC DefaultColor, BackGroundColor, Font, Dest, Source
    IF toggle THEN
        _FONT Font
        COLOR DefaultColor, BackGroundColor
        _DEST Dest
        _SOURCE Source
    ELSE
        Font = _FONT
        DefaultColor = _DEFAULTCOLOR
        BackGroundColor = _BACKGROUNDCOLOR
        Dest = _DEST
        Source = _SOURCE
    END IF
END SUB

When writing a lot of subs, I've found that a majority of them tend to require the same things at the top and bottom of them so that they play nicely with my main routines -- a means to save the main program state and then restore it upon exit.

For example:

Code: QB64: [Select]
  1. SUB QPrint (x, y, kolor, text$)
  2.    LOCATE x,y
  3.    COLOR kolor
  4.    PRINT text$

Now, the above SUB isn't very library friendly, and it's a PITA to plug into most programs.  It changes the default color that we we're printing with when we entered the sub, and it's now something completely different when we exit the sub.  It just doesn't play nice with others.

So how to fix that?


Code: QB64: [Select]
  1. SUB QPrint (x, y, kolor, text$)
  2.    SubState 1
  3.    LOCATE x,y
  4.    COLOR kolor
  5.    PRINT text$
  6.    SubState 0

Note:  This version of SubState isn't for use with recursive routines, or for routines which call others which alter default values on us.  If you guys are interested in it, I'll share it as well for you.  Use this for quick save/exit routines for single call SUB/FUNCTION as illustrated above.  ;)
Title: Re: and another one for your toolbox...
Post by: bplus on July 31, 2019, 08:22:35 am
Thanks Steve that's a great idea.

Here is the tail end (currently, it keeps needing more things) to my mBox (messageBox) sub and inputBox sub:
Code: QB64: [Select]
  1.     'put things back
  2.     COLOR _RGB32(255, 255, 255), _RGB32(0, 0, 0): CLS
  3.     _PUTIMAGE , backScrn
  4.     _DISPLAY
  5.     COLOR fg, bg
  6.     _FREEIMAGE backScrn
  7.     _FREEIMAGE mbx
  8.     'clear key presses
  9.     _KEYCLEAR
  10.     LOCATE curRow, curCol
  11.     'clear mouse clicks
  12.     mb = _MOUSEBUTTON(1)
  13.     IF mb THEN
  14.         DO
  15.             WHILE _MOUSEINPUT: WEND
  16.             mb = _MOUSEBUTTON(1)
  17.             _LIMIT 100
  18.         LOOP UNTIL mb = 0
  19.     END IF
  20.  

I was wondering if I was missing more items. I did not do _FONT nor _SOURCE but I do put back row and col, and _AUTODISPLAY / _DISPLAY setting and found the need to clear both mouse click and key press (esc or enter mainly need to be cleared so main program isn't short circuited).

Collecting all these settings and then putting them back really distracts from the code for the procedure, so this could be very useful to use with tool box procedures. Thanks again, I will put a version of this in my toolbox.
Title: Re: and another one for your toolbox...
Post by: bplus on July 31, 2019, 08:57:28 am
MessageBox sub that you can drag around the screen called: mBox title$, message$
(Employs a scnState sub discussed earlier in this thread suggested by SMcNeill.)


Speaking of message Box here is my current version which I updated recently with better mouse button clear (I think) and adding of _DISPLAY / _AUTODISPLAY resets.

The advantage is you don't need Windows and since it is Tool Box code and not Library code, you can copy / paste into your program and are free to modify to needs of program eg, you won't like my colors for message box (I don't like my colors) but you are free to change to fit program you are using message box in. Trying to anticipate every mod change that might be needed for message box turns it into a complex Swiss Army Knife too awkward to be of practical use IMO. So this has the simple parameters of Title$ and message$:

Code: QB64: [Select]
  1. '
  2. ' 2019-05-18 new version of mBox developed from scrollWindow test code (Array Window.bas)
  3. ' hmm... old version saved curRow and curCol ? put back in
  4. ' new version clears mouse clicks
  5.  
  6. ' 2019-07-25  save _autodisplay setting and set it back before leaving, rework clearing mb
  7. 'test #1 does message box work in screen 0
  8. 'mBox "Well?", "Test mBox in Screen 0"    NO! for graphic screens ONLY!!!
  9.  
  10. '2019-07-31 attempt to assimulate Steve's idea for saving and restoring the screen state
  11. ' before a sub messes with all the settings, so in future mBox and inputBox$ will need this
  12. ' helper sub. Damn the fan is back!  Also fix this to make it easier to change colors.
  13. ' OK tests here now try scrollWindow and Geometry Circle Intersects...
  14.  
  15. SCREEN _NEWIMAGE(800, 600, 32)
  16. _SCREENMOVE 100, 20
  17.  
  18. DIM m$, enter$, k$, kh AS LONG, mNum AS INTEGER
  19. COLOR &HFF000000, &HFF888888
  20. PRINT "REMINDER: press escape, enter or spacebar to exit the message box. Escape (again?) to exit this test."
  21. PRINT "Here is some stuff on screen."
  22.  
  23. m$ = "This is the message for mBox in 800x 600 _NEWIMAGE setup, "
  24. m$ = m$ + "how does it handle a long line?   Yeah! grab title bar and move this box around!" 'great
  25. mBox "This is Demo Title", m$
  26.  
  27. PRINT "here is more stuff, is it printing exactly below last stuff?" ' OK now with center fix too!
  28. INPUT "For next test [h | m] for mBox, [esc | space | enter] to quit it and then [esc] again to quit, enter for next test...", enter$
  29.  
  30. 'draw stuff, until h or m press, then show message box
  31.  
  32.     k$ = INKEY$
  33.     IF k$ = "m" OR k$ = "h" THEN doMessage: mNum = mNum + 1: PRINT mNum; "Message done..."
  34.     kh = 0
  35.     LINE (RND * _WIDTH, RND * _HEIGHT)-STEP(RND * 80, RND * 60), _RGB32(RND * 255, RND * 255, RND * 255), BF
  36.     kh = _KEYHIT
  37.     IF kh = 27 THEN EXIT WHILE
  38.     ' _DISPLAY  '<<<< comment out to see if _AUTODISPLAY mode, yep! good
  39.     _LIMIT 60
  40. PRINT "OK where is this print line going to end up, hopefully under the last '# message done'." 'yes! Excellent!
  41.  
  42. SUB doMessage
  43.     DIM title$, mess$
  44.     mess$ = "This is line 1 followed by LF" + CHR$(10)
  45.     mess$ = mess$ + "This is line 2 followed by CR + LF" + CHR$(13) + CHR$(10)
  46.     mess$ = mess$ + "Line 3 is followed by a double LF to make a space." + CHR$(10) + CHR$(10)
  47.     mess$ = mess$ + "Here is a long line that hopefully won't all fit across the screen, so I just keep going on and on ..."
  48.     mess$ = mess$ + "until the screen is half full of this single long, long line so I can test the wrapping my code does "
  49.     mess$ = mess$ + "with this long thing! ending with a double CR." + CHR$(13) + CHR$(13)
  50.     mess$ = mess$ + CHR$(9) + "This line started with a tab which should be replaced by spaces so now this line should be running too long. The End."
  51.     title$ = "123456789012345678901234567890123456789012345678901234567890"
  52.     mBox title$, mess$
  53.  
  54. 'title$ limit is 57 chars, all lines are 58 chars max, version 2019-07-31
  55. 'THIS SUB NOW NEEDS SUB scnState(restoreTF) for saving and restoring screen settings
  56. SUB mBox (title AS STRING, m AS STRING)
  57.  
  58.     bg = &H33404040
  59.     fg = &HFF33AAFF
  60.  
  61.     'first screen dimensions and items to restore at exit
  62.     DIM sw AS INTEGER, sh AS INTEGER
  63.     DIM curScrn AS LONG, backScrn AS LONG, mbx AS LONG 'some handles
  64.     DIM ti AS INTEGER, limit AS INTEGER 'ti = text index for t$(), limit is number of chars per line
  65.     DIM i AS INTEGER, j AS INTEGER, ff AS _BIT, add AS _BYTE 'index, flag and
  66.     DIM bxH AS INTEGER, bxW AS INTEGER 'first as cells then as pixels
  67.     DIM mb AS INTEGER, mx AS INTEGER, my AS INTEGER, mi AS INTEGER, grabx AS INTEGER, graby AS INTEGER
  68.     DIM tlx AS INTEGER, tly AS INTEGER 'top left corner of message box
  69.     DIM lastx AS INTEGER, lasty AS INTEGER, t AS STRING, b AS STRING, c AS STRING, tail AS STRING
  70.     DIM d AS STRING, r AS SINGLE, kh AS LONG
  71.  
  72.     'screen and current settings to restore at end ofsub
  73.     scnState 0
  74.     sw = _WIDTH: sh = _HEIGHT
  75.  
  76.     _KEYCLEAR '<<<<<<<<<<<<<<<<<<<< do i still need this?   Yes 2019-08-06
  77.  
  78.     'screen snapshot
  79.     curScrn = _DEST
  80.     backScrn = _NEWIMAGE(sw, sh, 32)
  81.     _PUTIMAGE , curScrn, backScrn
  82.  
  83.     'setup t() to store strings with ti as index, linit 58 chars per line max, b is for build
  84.     REDIM t(0) AS STRING: ti = 0: limit = 58: b = ""
  85.     FOR i = 1 TO LEN(m)
  86.         c = MID$(m, i, 1)
  87.         'are there any new line signals, CR, LF or both? take CRLF or LFCR as one break but dbl LF or CR means blank line
  88.         SELECT CASE c
  89.             CASE CHR$(13) 'load line
  90.                 IF MID$(m, i + 1, 1) = CHR$(10) THEN i = i + 1
  91.                 t(ti) = b: b = "": ti = ti + 1: REDIM _PRESERVE t(ti) AS STRING
  92.             CASE CHR$(10)
  93.                 IF MID$(m, i + 1, 1) = CHR$(13) THEN i = i + 1
  94.                 t(ti) = b: b = "": ti = ti + 1: REDIM _PRESERVE t(ti)
  95.             CASE ELSE
  96.                 IF c = CHR$(9) THEN c = SPACE$(4): add = 4 ELSE add = 1
  97.                 IF LEN(b) + add > limit THEN
  98.                     tail = "": ff = 0
  99.                     FOR j = LEN(b) TO 1 STEP -1 'backup until find a space, save the tail end for next line
  100.                         d = MID$(b, j, 1)
  101.                         IF d = " " THEN
  102.                             t(ti) = MID$(b, 1, j - 1): b = tail + c: ti = ti + 1: REDIM _PRESERVE t(ti)
  103.                             ff = 1 'found space flag
  104.                             EXIT FOR
  105.                         ELSE
  106.                             tail = d + tail 'the tail grows!
  107.                         END IF
  108.                     NEXT
  109.                     IF ff = 0 THEN 'no break? OK
  110.                         t(ti) = b: b = c: ti = ti + 1: REDIM _PRESERVE t(ti)
  111.                     END IF
  112.                 ELSE
  113.                     b = b + c 'just keep building the line
  114.                 END IF
  115.         END SELECT
  116.     NEXT
  117.     t(ti) = b
  118.     bxH = ti + 3: bxW = limit + 2
  119.  
  120.     'draw message box
  121.     mbx = _NEWIMAGE(60 * 8, (bxH + 1) * 16, 32)
  122.     _DEST mbx
  123.     COLOR _RGB32(128, 0, 0), _RGB32(225, 225, 255)
  124.     LOCATE 1, 1: PRINT LEFT$(SPACE$((bxW - LEN(title) - 3) / 2) + title + SPACE$(bxW), bxW)
  125.     COLOR _RGB32(225, 225, 255), _RGB32(200, 0, 0)
  126.     LOCATE 1, bxW - 2: PRINT " X "
  127.     COLOR fg, bg
  128.     LOCATE 2, 1: PRINT SPACE$(bxW);
  129.     FOR r = 0 TO ti
  130.         LOCATE 1 + r + 2, 1: PRINT LEFT$(" " + t(r) + SPACE$(bxW), bxW);
  131.     NEXT
  132.     LOCATE 1 + bxH, 1: PRINT SPACE$(limit + 2);
  133.  
  134.     'now for the action
  135.     _DEST curScrn
  136.  
  137.     'convert to pixels the top left corner of box at moment
  138.     bxW = bxW * 8: bxH = bxH * 16
  139.     tlx = (sw - bxW) / 2: tly = (sh - bxH) / 2
  140.     lastx = tlx: lasty = tly
  141.     'now allow user to move it around or just read it
  142.     WHILE 1
  143.         CLS
  144.         _PUTIMAGE , backScrn
  145.         _PUTIMAGE (tlx, tly), mbx, curScrn
  146.         _DISPLAY
  147.         WHILE _MOUSEINPUT: WEND
  148.         mx = _MOUSEX: my = _MOUSEY: mb = _MOUSEBUTTON(1)
  149.         IF mb THEN
  150.             IF mx >= tlx AND mx <= tlx + bxW AND my >= tly AND my <= tly + 16 THEN 'mouse down on title bar
  151.                 IF mx >= tlx + bxW - 24 THEN EXIT WHILE
  152.                 grabx = mx - tlx: graby = my - tly
  153.                 DO WHILE mb 'wait for release
  154.                     mi = _MOUSEINPUT: mb = _MOUSEBUTTON(1)
  155.                     mx = _MOUSEX: my = _MOUSEY
  156.                     IF mx - grabx >= 0 AND mx - grabx <= sw - bxW AND my - graby >= 0 AND my - graby <= sh - bxH THEN
  157.                         'attempt to speed up with less updates
  158.                         IF ((lastx - (mx - grabx)) ^ 2 + (lasty - (my - graby)) ^ 2) ^ .5 > 10 THEN
  159.                             tlx = mx - grabx: tly = my - graby
  160.                             CLS
  161.                             _PUTIMAGE , backScrn
  162.                             _PUTIMAGE (tlx, tly), mbx, curScrn
  163.                             lastx = tlx: lasty = tly
  164.                             _DISPLAY
  165.                         END IF
  166.                     END IF
  167.                     _LIMIT 400
  168.                 LOOP
  169.             END IF
  170.         END IF
  171.         kh = _KEYHIT
  172.         IF kh = 27 OR kh = 13 OR kh = 32 THEN EXIT WHILE
  173.         _LIMIT 400
  174.     WEND
  175.  
  176.     'put things back
  177.     COLOR _RGB32(255, 255, 255), _RGB32(0, 0, 0): CLS '
  178.     _PUTIMAGE , backScrn
  179.     _DISPLAY
  180.     _FREEIMAGE backScrn
  181.     _FREEIMAGE mbx
  182.     scnState 1 'Thanks Steve McNeill
  183.  
  184. ' for saving and restoring screen settins
  185. SUB scnState (restoreTF AS INTEGER) 'Thanks Steve McNeill
  186.     STATIC Font AS LONG, DefaultColor AS _UNSIGNED LONG, BackGroundColor AS _UNSIGNED LONG, Dest AS LONG, Source AS LONG
  187.     STATIC row AS INTEGER, col AS INTEGER, autodisplay AS INTEGER, mb AS INTEGER
  188.     IF restoreTF THEN
  189.         _FONT Font
  190.         COLOR DefaultColor, BackGroundColor
  191.         _DEST Dest
  192.         _SOURCE Source
  193.         LOCATE row, col
  194.         IF autodisplay THEN _AUTODISPLAY ELSE _DISPLAY
  195.         _KEYCLEAR
  196.         WHILE _MOUSEINPUT: WEND 'clear mouse clicks
  197.         mb = _MOUSEBUTTON(1)
  198.         IF mb THEN
  199.             DO
  200.                 WHILE _MOUSEINPUT: WEND
  201.                 mb = _MOUSEBUTTON(1)
  202.                 _LIMIT 100
  203.             LOOP UNTIL mb = 0
  204.         END IF
  205.     ELSE
  206.         Font = _FONT: DefaultColor = _DEFAULTCOLOR: BackGroundColor = _BACKGROUNDCOLOR
  207.         Dest = _DEST: Source = _SOURCE
  208.         row = CSRLIN: col = POS(0): autodisplay = _AUTODISPLAY
  209.     END IF
  210.  
  211.  

This will be soon be modified with my version of Steve's new subState (toggle) along with InputBox$

Update: 2019-07-31 11AM: OK modified with more complete srnState(restoreTF) SUB as promised but I see now Steve has improved his further into two SUBs, sheez hard to keep up! I also made it easier to modify colors in mBox sub, see fg, bg settings at top of sub for foreground and background.

Anyway mBox works well and tested in a couple of programs from which I caught bugs earlier:
 

Here I try semi-transparent background in mBox SUB.


2019-08-06 Update: _KEYCLEAR was needed when starting mBox, I found the test code that proved it.
comment removed from this line:
_KEYCLEAR '<<<<<<<<<<<<<<<<<<<< do i still need this?
Title: Re: and another one for your toolbox...
Post by: SMcNeill on July 31, 2019, 09:52:59 am
And, as promised, here's my expanded routines to save states:

Code: [Select]
SCREEN _NEWIMAGE(120, 25, 0)

PRINT "Currently my print cursor is here =>";
SLEEP
QPrint 10, 10, 4, "Then I jump to 10,10"
SLEEP
PRINT "But I'm still here! =>";
SLEEP
RQPrint 3, 3, 1, "Hello World"
SLEEP
PRINT "And, even after all the recursive work, I'm still here!!"




SUB QPrint (y, x, kolor, text$)
    s = SaveState
    LOCATE x, y
    COLOR kolor
    PRINT text$
    RestoreState s
END SUB

SUB RQPrint (y, x, kolor, text$)
    s = SaveState
    IF kolor < 15 THEN RQPrint x + 1, y + 1, kolor + 1, text$ + "."
    LOCATE x, y
    COLOR kolor
    PRINT text$
    RestoreState s
END SUB




FUNCTION SaveState
    TYPE SaveStateType
        InUse AS INTEGER
        DC AS INTEGER
        BG AS INTEGER
        F AS INTEGER
        D AS INTEGER
        S AS INTEGER
        Disp AS INTEGER
        CurX AS INTEGER
        CurY AS INTEGER
    END TYPE
    DIM SS AS SaveStateType, Temp AS SaveStateType
    SHARED NSS AS LONG 'Number of Saved States
    SHARED SaveMem AS _MEM
    IF _MEMEXISTS(SaveMem) THEN
        'do nothing
    ELSE
        SaveMem = _MEMNEW(LEN(SS) * 255) 'Save up to 255 save states; More than 255 and we toss an error
        $CHECKING:OFF
        _MEMFILL SaveMem, SaveMem.OFFSET, SaveMem.SIZE, 0 AS _UNSIGNED _BYTE
        $CHECKING:ON
    END IF

    'Data to Save
    SS.InUse = -1
    SS.F = _FONT
    SS.DC = _DEFAULTCOLOR
    SS.BG = _BACKGROUNDCOLOR
    SS.D = _DEST
    SS.S = _SOURCE
    SS.Disp = _AUTODISPLAY
    SS.CurX = POS(0)
    SS.CurY = CSRLIN
    $CHECKING:OFF
    FOR i = 1 TO NSS
        o = (i - 1) * LEN(SS)
        _MEMGET SaveMem, SaveMem.OFFSET + o, Temp
        IF Temp.InUse = 0 THEN
            _MEMPUT SaveMem, SaveMem.OFFSET + o, SS
            SaveState = i
            EXIT FUNCTION
        END IF
    NEXT
    _MEMPUT SaveMem, SaveMem.OFFSET + NSS * LEN(SS), SS
    $CHECKING:ON
    NSS = NSS + 1
    SaveState = NSS
END SUB

SUB RestoreState (WhichOne)
    DIM SS AS SaveStateType
    SHARED NSS AS LONG 'Number of Saved States
    SHARED SaveMem AS _MEM
    $CHECKING:ON
    _MEMGET SaveMem, SaveMem.OFFSET + (WhichOne - 1) * LEN(SS), SS
    IF SS.InUse THEN
        SS.InUse = 0 'Let the routine know that we're no longer in use for this handle
        _MEMPUT SaveMem, SaveMem.OFFSET + (WhichOne - 1) * LEN(SS), SS
        _FONT SS.F
        COLOR SS.DC, SS.BG
        _DEST SS.D
        _SOURCE SS.S
        IF SS.Disp THEN _AUTODISPLAY ELSE _DISPLAY
        LOCATE SS.CurY, SS.CurX
    END IF
    $CHECKING:ON
END SUB

Notice here that we get a handle for a saved state, and then when we exit the sub we restore that handle.  This allows us to have one sub call another sub, or recursively call itself, and yet preserves the original information without any problem for us.

One thing to note with this method:  Just like GOSUB can run out of stack space if you call them and exit without using RETURN, this also has a limit of 255 states being saved at a time.  IMHO, that's more than enough for any program I write, unless I've got a glitch somewhere where I'm exiting the sub/function without restoring values. (In which case, I've created a memory leak, and I'd want to find it and fix it anyway...)
Title: Re: and another one for your toolbox...
Post by: bplus on July 31, 2019, 11:32:45 am
Hi Steve,

You know I just started wondering about multiple mBox windows going... how the setting for different windows up might be saved. You anticipated that.

But what happens if you close the windows / screens out of the order they were opened? You would be restoring the wrong settings. Or what happens should any of the other screens start messing with the their settings in between.

Might be a can or worms here, unless keep everything modal.
Title: Re: and another one for your toolbox...
Post by: SMcNeill on July 31, 2019, 12:00:10 pm
Sometimes you just have to do things manually.

Let’s say I have a red screen and print stuff on it.

Now, open a green box in the center of it, and preserve the background so you can restore it later.

Now, open a blue box in the center of that one, and preserve the background so you can restore it later.

If you close the blue box, then the green box, your background will go back to looking as it originally did.

If you close the green box first, then the blue box, your background is now red, with a green box where the blue one used to be.  (You restored the original background when you closed the green box, but closing the blue box put the background behind it — the part of the green box it covered up — back onto the screen.)

As long as you call the routines and exit the routines in order, all is good.  If they’re going to be truly “independent boxes” and not “pop-up boxes” which require closing them in order, then sequentially stacking settings won’t be possible.  You’re looking at Z-order style handling at that point, which is a bit more than the routines above handle.
Title: Re: and another one for your toolbox...
Post by: Petr on July 31, 2019, 12:16:24 pm
We seem to have encountered the same problem that I solved, BPlus. When opening multiple windows, I enter a number in the TYPE field that indicates in which order this  window is  (it is added by _PRESERVE to the window field and at start this number is the same as the record number in the window field). When this window is clicked, the window display order for all windows is changed from the window number. Try the following code. More  complicated code is in objecteditor, which develop version will be released as soon as possible. There is solved also window positioning.

Example for four windows.
Code: QB64: [Select]
  1. SCREEN _NEWIMAGE(1800, 1600, 256)
  2. DIM x(4), y(4), position(1 TO 4)
  3. g = 1
  4. FOR f = 0 TO 1350 STEP 450
  5.     x(g) = f
  6.     g = g + 1
  7. g = 1
  8. FOR f = 0 TO 1200 STEP 400
  9.     y(g) = f
  10.     g = g + 1
  11. FOR N = 1 TO 4
  12.     position(N) = N
  13.     FOR d = 1 TO 4
  14.         LINE (x(d), y(d))-(x(d) + 400, y(d) + 350), , B
  15.         _PRINTSTRING (x(d), y(d)), STR$(position(d))
  16.         IF _MOUSEX >= x(d) AND _MOUSEX <= x(d) + 400 THEN
  17.             IF _MOUSEY >= x(y) AND _MOUSEY <= y(d) + 350 THEN
  18.                 IF _MOUSEBUTTON(1) THEN
  19.                     old = position(d)
  20.                     IF position(d) <> 1 THEN
  21.                         position(d) = 1
  22.                         FOR R = 1 TO 4
  23.                             IF R <> d THEN
  24.                                 position(R) = position(R) + 1
  25.                                 IF position(R) > 4 THEN position(R) = old + 1
  26.                             END IF
  27.                         NEXT
  28.                     END IF
  29.                 END IF
  30.             END IF
  31.         END IF
  32.     NEXT
  33.  

Previous code show you windows positions.

For correct view on screen then use Window nr.1 as UBOUND next array, Window nr.2 as second,  Window nr.3 as third and Window with nr.4 as last - LBOUND.

For correct view on screen then just use

FOR ViewIt = Lbound(WindowArray) Ubound(WindowArray)
....
....
graphic operations
NEXT

As you see, UBOUND is last, so window nr.1 as UBOUND in array is always on top.


And you get the answer to the question - what if I close the window in a different order? Window numbers are reduced by one, minimized, or closed is deleted from the field FOR VIEW (so I suggest using an extra field only for window rendering and another field with window position, text, and so on) (so there will only be three), the same loop  render it. There will be another graphic loop below the windows that will not be overwritten by the window array at this location if the window is closed before calling _DISPLAY. Pseudocode:

Loop for background drawing on screen
Loop for rendering windows
_DISPLAY

This way you can make transparent windows, behind which will run  - spiders :).
Title: Re: and another one for your toolbox...
Post by: bplus on July 31, 2019, 01:52:54 pm
Hi Petr,

You guys and your giant screens! Your demo does not fit my screen but OK because I am totally unprepared to take on a Windows management discussion in this thread. For this thread I have other fish to fry... ;-))

Good to know, you have worked on this problem though and are ready with help. Also I am so proud that spiders made such on impression on you! :)
Title: Re: and another one for your toolbox...
Post by: Petr on July 31, 2019, 02:00:26 pm
Yes BPlus, Spiders are perfect. I hope I understood the topic correctly and did not respond quite badly? If so, just say it and I delete the post, I don't want to spam your tools thread.
Title: Re: and another one for your toolbox...
Post by: bplus on July 31, 2019, 07:33:11 pm
Yes BPlus, Spiders are perfect. I hope I understood the topic correctly and did not respond quite badly? If so, just say it and I delete the post, I don't want to spam your tools thread.

Hi Petr, you are fine coder, no way a spammer!

To all,

Here is the a complementary tool to mBox that I wanted to get posted with mBox, inputBox$.

This lets you get some information from user without any disturbance of the screen. Like mBox you can grab the title bar and move it around the screen.

I combined that with cText, a way to print any size text around the midpoint x, y and overhauled the Top Ten screen used in Invaders bplus style.

inputBox$ and demo:
Code: QB64: [Select]
  1. _TITLE "inputBox$ tester.bas started 2018-10-26 need an input box for WS Editor"
  2. ' 2019-07-32 assimulate scnState(restoreTF) used to save and restore screen settings
  3. ' so sub can do it's thing and restore settings, Thanks Steve McNeill for starter code and idea.
  4.  
  5. SCREEN _NEWIMAGE(800, 600, 32)
  6. _SCREENMOVE 100, 20
  7. DIM well$, enter$, k$, kh AS LONG
  8.  
  9. COLOR &HFFFFFF00, &HFF880000
  10. PRINT "Here is some stuff on screen."
  11. well$ = inputBox$("Well?", "Test inputBox$", 20)
  12. PRINT "inputBox$ returned: "; well$; ". Is this line printing exactly below last stuff sentence?" ' OK now with center fix too!
  13. INPUT "OK? enter for next test, use h or m keypress to invoke inputBox$...", enter$
  14.  
  15. 'draw stuff, until h or m press, then show message box
  16.     k$ = INKEY$
  17.     IF k$ = "m" OR k$ = "h" THEN
  18.         well$ = inputBox$("Well?", "Test call inputBox", 36)
  19.         PRINT "inputBox$() returned: *"; well$; "*"
  20.     END IF
  21.     'kh = 0  'should not need this to stop esc keypress in input box
  22.     LINE (RND * _WIDTH, RND * _HEIGHT)-STEP(RND * 80, RND * 60), _RGB32(RND * 255, RND * 255, RND * 255), BF
  23.     kh = _KEYHIT
  24.     IF kh = 27 THEN EXIT WHILE
  25.     '_DISPLAY   '<< should not need this
  26.     _LIMIT 5
  27. PRINT "OK where is this print line going to end up, hopefully under the last inputBox returned." 'yes! Excellent!
  28. PRINT "InputBox$() last returned: "; well$; ",  Goodbye!"
  29.  
  30. ' You can grab this box by title and drag it around screen for full viewing while answering prompt.
  31. ' Only one line allowed for prompt$
  32. ' boxWidth is 4 more than the allowed length of input, it needs to be longer than title$ and prompt$ also
  33. ' Utilities > Input Box > Input Box 1 tester v 2019-07-31
  34. FUNCTION inputBox$ (prompt$, title$, boxWidth AS _BYTE)
  35.     DIM ForeColor AS _UNSIGNED LONG, BackColor AS _UNSIGNED LONG, White AS _UNSIGNED LONG
  36.     DIM sw AS INTEGER, sh AS INTEGER, curScrn AS LONG, backScrn AS LONG, ibx AS LONG 'some handles
  37.  
  38.     'colors
  39.     ForeColor = &HFF000055 '<  change as desired  prompt text color, back color or type in area
  40.     BackColor = &HFF6080CC '<  change as desired  used fore color in type in area
  41.     White = &HFFFFFFFF
  42.  
  43.     'items to restore at exit
  44.     scnState 0
  45.  
  46.     'screen snapshot
  47.     sw = _WIDTH: sh = _HEIGHT: curScrn = _DEST
  48.     backScrn = _NEWIMAGE(sw, sh, 32)
  49.     _PUTIMAGE , curScrn, backScrn
  50.  
  51.     'moving box around on screen
  52.     DIM bxW AS INTEGER, bxH AS INTEGER
  53.     DIM mb AS INTEGER, mx AS INTEGER, my AS INTEGER, mi AS INTEGER, grabx AS INTEGER, graby AS INTEGER
  54.     DIM tlx AS INTEGER, tly AS INTEGER 'top left corner of message box
  55.     DIM lastx AS INTEGER, lasty AS INTEGER
  56.     DIM inp$, kh&
  57.  
  58.     'draw message box
  59.     bxW = boxWidth * 8: bxH = 7 * 16
  60.     ibx = _NEWIMAGE(bxW, bxH, 32)
  61.     _DEST ibx
  62.     COLOR &HFF880000, White
  63.     LOCATE 1, 1: PRINT LEFT$(SPACE$(INT((boxWidth - LEN(title$) - 3)) / 2) + title$ + SPACE$(boxWidth), boxWidth)
  64.     COLOR White, &HFFBB0000
  65.     LOCATE 1, boxWidth - 2: PRINT " X "
  66.     COLOR ForeColor, BackColor
  67.     LOCATE 2, 1: PRINT SPACE$(boxWidth);
  68.     LOCATE 3, 1: PRINT LEFT$(SPACE$((boxWidth - LEN(prompt$)) / 2) + prompt$ + SPACE$(boxWidth), boxWidth);
  69.     LOCATE 4, 1: PRINT SPACE$(boxWidth);
  70.     LOCATE 5, 1: PRINT SPACE$(boxWidth);
  71.     LOCATE 6, 1: PRINT SPACE$(boxWidth);
  72.     inp$ = ""
  73.     GOSUB finishBox
  74.  
  75.     'convert to pixels the top left corner of box at moment
  76.     bxW = boxWidth * 8: bxH = 5 * 16
  77.     tlx = (sw - bxW) / 2: tly = (sh - bxH) / 2
  78.     lastx = tlx: lasty = tly
  79.     _KEYCLEAR
  80.     'now allow user to move it around or just read it
  81.     WHILE 1
  82.         CLS
  83.         _PUTIMAGE , backScrn
  84.         _PUTIMAGE (tlx, tly), ibx, curScrn
  85.         _DISPLAY
  86.         WHILE _MOUSEINPUT: WEND
  87.         mx = _MOUSEX: my = _MOUSEY: mb = _MOUSEBUTTON(1)
  88.         IF mb THEN
  89.             IF mx >= tlx AND mx <= tlx + bxW AND my >= tly AND my <= tly + 16 THEN 'mouse down on title bar
  90.                 IF mx >= tlx + bxW - 24 THEN EXIT WHILE
  91.                 grabx = mx - tlx: graby = my - tly
  92.                 DO WHILE mb 'wait for release
  93.                     mi = _MOUSEINPUT: mb = _MOUSEBUTTON(1)
  94.                     mx = _MOUSEX: my = _MOUSEY
  95.                     IF mx - grabx >= 0 AND mx - grabx <= sw - bxW AND my - graby >= 0 AND my - graby <= sh - bxH THEN
  96.                         'attempt to speed up with less updates
  97.                         IF ((lastx - (mx - grabx)) ^ 2 + (lasty - (my - graby)) ^ 2) ^ .5 > 10 THEN
  98.                             tlx = mx - grabx: tly = my - graby
  99.                             CLS
  100.                             _PUTIMAGE , backScrn
  101.                             _PUTIMAGE (tlx, tly), ibx, curScrn
  102.                             lastx = tlx: lasty = tly
  103.                             _DISPLAY
  104.                         END IF
  105.                     END IF
  106.                     _LIMIT 400
  107.                 LOOP
  108.             END IF
  109.         END IF
  110.         kh& = _KEYHIT
  111.         SELECT CASE kh& 'whew not much for the main event!
  112.             CASE 13: EXIT WHILE
  113.             CASE 27: inp$ = "": EXIT WHILE
  114.             CASE 32 TO 128: IF LEN(inp$) < boxWidth - 4 THEN inp$ = inp$ + CHR$(kh&): GOSUB finishBox ELSE BEEP
  115.             CASE 8: IF LEN(inp$) THEN inp$ = LEFT$(inp$, LEN(inp$) - 1): GOSUB finishBox ELSE BEEP
  116.         END SELECT
  117.  
  118.         _LIMIT 60
  119.     WEND
  120.  
  121.     'put things back
  122.     scnState 1 'need fg and bg colors set to cls
  123.     CLS '? is this needed YES!!
  124.     _PUTIMAGE , backScrn
  125.     _DISPLAY
  126.     _FREEIMAGE backScrn
  127.     _FREEIMAGE ibx
  128.     scnState 1 'because we have to call _display, we have to call this again
  129.     inputBox$ = inp$
  130.  
  131.     finishBox:
  132.     _DEST ibx
  133.     COLOR BackColor, ForeColor
  134.     LOCATE 5, 2: PRINT LEFT$(" " + inp$ + SPACE$(boxWidth - 2), boxWidth - 2)
  135.     _DEST curScrn
  136.     RETURN
  137.  
  138. 'from mBox v 2019-07-31 update
  139. ' for saving and restoring screen settins
  140. SUB scnState (restoreTF AS INTEGER) 'Thanks Steve McNeill
  141.     STATIC Font AS LONG, DefaultColor AS _UNSIGNED LONG, BackGroundColor AS _UNSIGNED LONG, Dest AS LONG, Source AS LONG
  142.     STATIC row AS INTEGER, col AS INTEGER, autodisplay AS INTEGER, mb AS INTEGER
  143.     IF restoreTF THEN
  144.         _FONT Font
  145.         COLOR DefaultColor, BackGroundColor
  146.         _DEST Dest
  147.         _SOURCE Source
  148.         LOCATE row, col
  149.         IF autodisplay THEN _AUTODISPLAY ELSE _DISPLAY
  150.         _KEYCLEAR
  151.         WHILE _MOUSEINPUT: WEND 'clear mouse clicks
  152.         mb = _MOUSEBUTTON(1)
  153.         IF mb THEN
  154.             DO
  155.                 WHILE _MOUSEINPUT: WEND
  156.                 mb = _MOUSEBUTTON(1)
  157.                 _LIMIT 100
  158.             LOOP UNTIL mb = 0
  159.         END IF
  160.     ELSE
  161.         Font = _FONT: DefaultColor = _DEFAULTCOLOR: BackGroundColor = _BACKGROUNDCOLOR
  162.         Dest = _DEST: Source = _SOURCE
  163.         row = CSRLIN: col = POS(0): autodisplay = _AUTODISPLAY
  164.     END IF
  165.  
  166.  

Here is cText, text and demo:
Code: QB64: [Select]
  1. _TITLE "Text sub tests" 'B+ started 2019-03-25
  2.  
  3. CONST xmax = 1200
  4. CONST ymax = 700
  5.  
  6. SCREEN _NEWIMAGE(xmax, ymax, 32)
  7. _SCREENMOVE 100, 40
  8.  
  9. DIM y, i
  10.  
  11. COLOR _RGB32(0, 0, 255)
  12. PRINT "Hello World"
  13. 'test& = _NEWIMAGE(xmax, ymax, 32)
  14. 'DEST test&
  15. y = ymax
  16. FOR i = 1 TO 60 STEP .5
  17.     CLS
  18.     y = y - 60 / i
  19.     cText xmax / 2, y, 240 / i, _RGB32(255, 0, 0), "So how is this looking?"
  20.     _DISPLAY
  21.     _LIMIT 5
  22. '_DEST 0
  23. '_PUTIMAGE , test&, 0
  24. PRINT "OK, blue?"
  25.  
  26. SUB Text (x, y, textHeight, K AS _UNSIGNED LONG, txt$)
  27.     DIM fg AS _UNSIGNED LONG, cur&, I&, mult, xlen
  28.     fg = _DEFAULTCOLOR
  29.     'screen snapshot
  30.     cur& = _DEST
  31.     I& = _NEWIMAGE(8 * LEN(txt$), 16, 32)
  32.     _DEST I&
  33.     COLOR K, _RGBA32(0, 0, 0, 0)
  34.     _PRINTSTRING (0, 0), txt$
  35.     mult = textHeight / 16
  36.     xlen = LEN(txt$) * 8 * mult
  37.     _PUTIMAGE (x, y)-STEP(xlen, textHeight), I&, cur&
  38.     COLOR fg
  39.     _FREEIMAGE I&
  40.  
  41. 'center the text
  42. SUB cText (x, y, textHeight, K AS _UNSIGNED LONG, txt$)
  43.     DIM fg AS _UNSIGNED LONG, cur&, I&, mult, xlen
  44.     fg = _DEFAULTCOLOR
  45.     'screen snapshot
  46.     cur& = _DEST
  47.     I& = _NEWIMAGE(8 * LEN(txt$), 16, 32)
  48.     _DEST I&
  49.     COLOR K, _RGBA32(0, 0, 0, 0)
  50.     _PRINTSTRING (0, 0), txt$
  51.     mult = textHeight / 16
  52.     xlen = LEN(txt$) * 8 * mult
  53.     _PUTIMAGE (x - .5 * xlen, y - .5 * textHeight)-STEP(xlen, textHeight), I&, cur&
  54.     COLOR fg
  55.     _FREEIMAGE I&
  56.  
  57.  

Here is topTenGoAgain$ code and tester reworked with cText and inputBox$:
Code: QB64: [Select]
  1. _TITLE "Top Ten mod with cText tester" ' B+ started 2019-07-31
  2. '2019-07-28 All enclosed in a sub, possible feed it score and see how it stacks up.
  3. '2019-07-28 fix to fill all 10 slots.
  4. '2019-07-29 2nd fix with an n > 10 check, change to a function and get GoAgain reply.
  5. '2019-07-31 modify Top Ten Screen with cText and inputBox$
  6. ' 1. to make it look better, 2
  7. ' 2. for demo in thread "another one for the tool box"
  8. ' 3. improve Invaders look
  9. ' 4. test new revsions to inputBox$
  10.  
  11. SCREEN _NEWIMAGE(800, 600, 32)
  12. _SCREENMOVE 300, 20
  13.  
  14. DIM score, again$
  15.  
  16. 'for testing
  17. 'IF _FILEEXISTS("Top 10 Scores.txt") THEN KILL "Top 10 Scores.txt"
  18.  
  19. 'testing function with random scores, would be good to start with
  20. FOR score = 1 TO 15
  21.     again$ = topTenGoAgain$(score * 5 * RND)
  22.     CLS
  23.  
  24.     IF LEN(again$) THEN PRINT "Player wants to quit." ELSE PRINT "Player wants to go again."
  25.     IF score = 15 OR LEN(again$) THEN
  26.         PRINT "End of test.": END
  27.     ELSE
  28.         INPUT "Press <enter> to continue testing TopTenGoAgain$ Function.", again$
  29.     END IF
  30.  
  31.  
  32. ' This FUNCTION creates a file in the same folder as your .bas source or .exe
  33. 'EDIT: 2019-07-31 this function needs:
  34. ' SUB cText(x, y, pixelTextHeight, Colr)
  35. ' SUB inputBox$(prompt$, title$, maxBoxWidth)
  36. ' which needs scnState(restoreTF)
  37. FUNCTION topTenGoAgain$ (compareScore AS INTEGER)
  38.     DIM fName$, n AS INTEGER, names$(1 TO 10), scores(1 TO 10), name$, score AS INTEGER
  39.     DIM settleScore AS INTEGER, i AS INTEGER, yc, s$
  40.  
  41.     fName$ = "Top 10 Scores.txt" '<<<  since this is toolbox code change this as needed for app
  42.     CLS
  43.     cText _WIDTH / 2, _HEIGHT / 8, 20, &HFF0000FF, "Your score was:" + STR$(compareScore)
  44.     IF _FILEEXISTS(fName$) THEN
  45.         OPEN fName$ FOR INPUT AS #1
  46.         WHILE EOF(1) = 0 AND n < 10
  47.             n = n + 1
  48.             INPUT #1, name$
  49.             INPUT #1, score
  50.             IF compareScore >= score AND settleScore = 0 THEN
  51.                 names$(n) = inputBox$("Please enter your name here:", "You have made the Top Ten!", 40)
  52.                 scores(n) = compareScore
  53.                 settleScore = -1
  54.                 n = n + 1
  55.                 IF n <= 10 THEN names$(n) = name$: scores(n) = score
  56.             ELSE
  57.                 scores(n) = score: names$(n) = name$
  58.             END IF
  59.         WEND
  60.         CLOSE #1
  61.         IF n < 10 AND settleScore = 0 THEN
  62.             name$ = inputBox$("Please enter your name here:", "Top Ten has slot open for you:", 40)
  63.             IF name$ <> "" THEN n = n + 1: names$(n) = name$: scores(n) = compareScore
  64.         END IF
  65.         IF n > 10 THEN n = 10
  66.         yc = (_HEIGHT - 20 * (n + 2)) / 2
  67.         cText _WIDTH / 2, yc, 40, &HFFFFFF00, "Top Ten Scorers and Scores:"
  68.         OPEN fName$ FOR OUTPUT AS #1
  69.         FOR i = 1 TO n
  70.             PRINT #1, names$(i): PRINT #1, scores(i)
  71.             s$ = RIGHT$(" " + STR$(i), 2) + "  " + LEFT$(names$(i) + STRING$(25, "."), 20)
  72.             s$ = s$ + RIGHT$(SPACE$(10) + STR$(scores(i)), 10)
  73.             cText _WIDTH / 2, yc + 30 + i * 20, 20, &HFF00FFFF, s$
  74.         NEXT
  75.         _DISPLAY
  76.         _DELAY 3.5
  77.         CLOSE #1
  78.     ELSE
  79.         name$ = inputBox("Please enter your name here:", "You are first into Top Ten file.", 40)
  80.         OPEN fName$ FOR OUTPUT AS #1
  81.         PRINT #1, name$: PRINT #1, compareScore
  82.         CLOSE #1
  83.     END IF
  84.     topTenGoAgain$ = inputBox$("Press <Enter> to play again, enter q (or any) to quit... ", "Play Again?", 66)
  85.  
  86. 'center the text around (x, y) point, needs a graphics screen!
  87. SUB cText (x, y, textHeight, K AS _UNSIGNED LONG, txt$)
  88.     DIM fg AS _UNSIGNED LONG, cur&, I&, mult, xlen
  89.     fg = _DEFAULTCOLOR
  90.     'screen snapshot
  91.     cur& = _DEST
  92.     I& = _NEWIMAGE(8 * LEN(txt$), 16, 32)
  93.     _DEST I&
  94.     COLOR K, _RGBA32(0, 0, 0, 0)
  95.     _PRINTSTRING (0, 0), txt$
  96.     mult = textHeight / 16
  97.     xlen = LEN(txt$) * 8 * mult
  98.     _PUTIMAGE (x - .5 * xlen, y - .5 * textHeight)-STEP(xlen, textHeight), I&, cur&
  99.     COLOR fg
  100.     _FREEIMAGE I&
  101.  
  102. ' You can grab this box by title and drag it around screen for full viewing while answering prompt.
  103. ' Only one line allowed for prompt$
  104. ' boxWidth is 4 more than the allowed length of input, it needs to be longer than title$ and prompt$ also
  105. ' Utilities > Input Box > Input Box 1 tester v 2019-07-31
  106. ' This FUNCTION needs scnState(restroreTF)
  107. FUNCTION inputBox$ (prompt$, title$, boxWidth AS _BYTE)
  108.     DIM ForeColor AS _UNSIGNED LONG, BackColor AS _UNSIGNED LONG, White AS _UNSIGNED LONG
  109.     DIM sw AS INTEGER, sh AS INTEGER, curScrn AS LONG, backScrn AS LONG, ibx AS LONG 'some handles
  110.  
  111.     'colors
  112.     ForeColor = &HFF000055 '<  change as desired  prompt text color, back color or type in area
  113.     BackColor = &HFF6080CC '<  change as desired  used fore color in type in area
  114.     White = &HFFFFFFFF
  115.  
  116.     'items to restore at exit
  117.     scnState 0
  118.  
  119.     'screen snapshot
  120.     sw = _WIDTH: sh = _HEIGHT: curScrn = _DEST
  121.     backScrn = _NEWIMAGE(sw, sh, 32)
  122.     _PUTIMAGE , curScrn, backScrn
  123.  
  124.     'moving box around on screen
  125.     DIM bxW AS INTEGER, bxH AS INTEGER
  126.     DIM mb AS INTEGER, mx AS INTEGER, my AS INTEGER, mi AS INTEGER, grabx AS INTEGER, graby AS INTEGER
  127.     DIM tlx AS INTEGER, tly AS INTEGER 'top left corner of message box
  128.     DIM lastx AS INTEGER, lasty AS INTEGER
  129.     DIM inp$, kh&
  130.  
  131.     'draw message box
  132.     bxW = boxWidth * 8: bxH = 7 * 16
  133.     ibx = _NEWIMAGE(bxW, bxH, 32)
  134.     _DEST ibx
  135.     COLOR &HFF880000, White
  136.     LOCATE 1, 1: PRINT LEFT$(SPACE$(INT((boxWidth - LEN(title$) - 3)) / 2) + title$ + SPACE$(boxWidth), boxWidth)
  137.     COLOR White, &HFFBB0000
  138.     LOCATE 1, boxWidth - 2: PRINT " X "
  139.     COLOR ForeColor, BackColor
  140.     LOCATE 2, 1: PRINT SPACE$(boxWidth);
  141.     LOCATE 3, 1: PRINT LEFT$(SPACE$((boxWidth - LEN(prompt$)) / 2) + prompt$ + SPACE$(boxWidth), boxWidth);
  142.     LOCATE 4, 1: PRINT SPACE$(boxWidth);
  143.     LOCATE 5, 1: PRINT SPACE$(boxWidth);
  144.     LOCATE 6, 1: PRINT SPACE$(boxWidth);
  145.     inp$ = ""
  146.     GOSUB finishBox
  147.  
  148.     'convert to pixels the top left corner of box at moment
  149.     bxW = boxWidth * 8: bxH = 5 * 16
  150.     tlx = (sw - bxW) / 2: tly = (sh - bxH) / 2
  151.     lastx = tlx: lasty = tly
  152.     _KEYCLEAR
  153.     'now allow user to move it around or just read it
  154.     WHILE 1
  155.         CLS
  156.         _PUTIMAGE , backScrn
  157.         _PUTIMAGE (tlx, tly), ibx, curScrn
  158.         _DISPLAY
  159.         WHILE _MOUSEINPUT: WEND
  160.         mx = _MOUSEX: my = _MOUSEY: mb = _MOUSEBUTTON(1)
  161.         IF mb THEN
  162.             IF mx >= tlx AND mx <= tlx + bxW AND my >= tly AND my <= tly + 16 THEN 'mouse down on title bar
  163.                 IF mx >= tlx + bxW - 24 THEN EXIT WHILE
  164.                 grabx = mx - tlx: graby = my - tly
  165.                 DO WHILE mb 'wait for release
  166.                     mi = _MOUSEINPUT: mb = _MOUSEBUTTON(1)
  167.                     mx = _MOUSEX: my = _MOUSEY
  168.                     IF mx - grabx >= 0 AND mx - grabx <= sw - bxW AND my - graby >= 0 AND my - graby <= sh - bxH THEN
  169.                         'attempt to speed up with less updates
  170.                         IF ((lastx - (mx - grabx)) ^ 2 + (lasty - (my - graby)) ^ 2) ^ .5 > 10 THEN
  171.                             tlx = mx - grabx: tly = my - graby
  172.                             CLS
  173.                             _PUTIMAGE , backScrn
  174.                             _PUTIMAGE (tlx, tly), ibx, curScrn
  175.                             lastx = tlx: lasty = tly
  176.                             _DISPLAY
  177.                         END IF
  178.                     END IF
  179.                     _LIMIT 400
  180.                 LOOP
  181.             END IF
  182.         END IF
  183.         kh& = _KEYHIT
  184.         SELECT CASE kh& 'whew not much for the main event!
  185.             CASE 13: EXIT WHILE
  186.             CASE 27: inp$ = "": EXIT WHILE
  187.             CASE 32 TO 128: IF LEN(inp$) < boxWidth - 4 THEN inp$ = inp$ + CHR$(kh&): GOSUB finishBox ELSE BEEP
  188.             CASE 8: IF LEN(inp$) THEN inp$ = LEFT$(inp$, LEN(inp$) - 1): GOSUB finishBox ELSE BEEP
  189.         END SELECT
  190.  
  191.         _LIMIT 60
  192.     WEND
  193.  
  194.     'put things back
  195.     scnState 1 'need fg and bg colors set to cls
  196.     CLS '? is this needed YES!!
  197.     _PUTIMAGE , backScrn
  198.     _DISPLAY
  199.     _FREEIMAGE backScrn
  200.     _FREEIMAGE ibx
  201.     scnState 1 'because we have to call _display, we have to call this again
  202.     inputBox$ = inp$
  203.  
  204.     finishBox:
  205.     _DEST ibx
  206.     COLOR BackColor, ForeColor
  207.     LOCATE 5, 2: PRINT LEFT$(" " + inp$ + SPACE$(boxWidth - 2), boxWidth - 2)
  208.     _DEST curScrn
  209.     RETURN
  210.  
  211. 'from mBox v 2019-07-31 update
  212. ' for saving and restoring screen settins
  213. SUB scnState (restoreTF AS INTEGER) 'Thanks Steve McNeill
  214.     STATIC Font AS LONG, DefaultColor AS _UNSIGNED LONG, BackGroundColor AS _UNSIGNED LONG, Dest AS LONG, Source AS LONG
  215.     STATIC row AS INTEGER, col AS INTEGER, autodisplay AS INTEGER, mb AS INTEGER
  216.     IF restoreTF THEN
  217.         _FONT Font
  218.         COLOR DefaultColor, BackGroundColor
  219.         _DEST Dest
  220.         _SOURCE Source
  221.         LOCATE row, col
  222.         IF autodisplay THEN _AUTODISPLAY ELSE _DISPLAY
  223.         _KEYCLEAR
  224.         WHILE _MOUSEINPUT: WEND 'clear mouse clicks
  225.         mb = _MOUSEBUTTON(1)
  226.         IF mb THEN
  227.             DO
  228.                 WHILE _MOUSEINPUT: WEND
  229.                 mb = _MOUSEBUTTON(1)
  230.                 _LIMIT 100
  231.             LOOP UNTIL mb = 0
  232.         END IF
  233.     ELSE
  234.         Font = _FONT: DefaultColor = _DEFAULTCOLOR: BackGroundColor = _BACKGROUNDCOLOR
  235.         Dest = _DEST: Source = _SOURCE
  236.         row = CSRLIN: col = POS(0): autodisplay = _AUTODISPLAY
  237.     END IF
  238.  

and a screen shot of the revised screen after installed in Invaders b0_4:
 
Title: Re: and another one for your toolbox...
Post by: SMcNeill on August 06, 2019, 01:41:10 am
And another rather simple routine for your toolbox collection -- a quick mouse handler.

Code: QB64: [Select]
  1.     CLS
  2.     held$ = ""
  3.     SELECT CASE MBS
  4.         CASE 1: left = left + 1
  5.         CASE 2: right = right + 1
  6.         CASE 3: middle = middle + 1
  7.         CASE 4: held$ = "Left held"
  8.         CASE 5: held$ = "Right held"
  9.         CASE 6: held$ = "Middle held"
  10.     END SELECT
  11.  
  12.     PRINT "MouseX: "; _MOUSEX
  13.     PRINT "MouseY: "; _MOUSEY
  14.     PRINT "Left pressed: "; left
  15.     PRINT "Right pressed: "; right
  16.     PRINT "Middle pressed: "; middle
  17.     PRINT held$
  18.     _LIMIT 10
  19.  
  20.  
  21. FUNCTION MBS 'Mouse Button Status
  22.     STATIC StartTimer AS _FLOAT
  23.     STATIC ButtonDown AS INTEGER
  24.     STATIC ClickCount AS INTEGER
  25.     CONST ClickLimit## = 0.2 '2/10th of a second to down, up a key to count as a CLICK.  Down longer counts as a HOLD event.
  26.  
  27.     WHILE _MOUSEINPUT: WEND 'Remark out this line, if mouse main input/clear is going to be handled manually in main program.
  28.  
  29.     IF StartTimer = 0 THEN
  30.         IF _MOUSEBUTTON(1) THEN 'If a button is pressed, start the timer to see what it does (click or hold)
  31.             ButtonDown = 1: StartTimer = TIMER(0.01)
  32.         ELSEIF _MOUSEBUTTON(2) THEN
  33.             ButtonDown = 2: StartTimer = TIMER(0.01)
  34.         ELSEIF _MOUSEBUTTON(3) THEN
  35.             ButtonDown = 3: StartTimer = TIMER(0.01)
  36.         END IF
  37.     ELSE
  38.         BD = ButtonDown MOD 3
  39.         IF BD = 0 THEN BD = 3
  40.         IF TIMER(0.01) - StartTimer <= ClickLimit THEN 'Button was down, then up, within time limit.  It's a click
  41.             IF _MOUSEBUTTON(BD) = 0 THEN MouseHandler = ButtonDown: ButtonDown = 0: StartTimer = 0
  42.         ELSE
  43.             IF _MOUSEBUTTON(BD) = 0 THEN 'hold event has now ended
  44.                 MouseHandler = 0: ButtonDown = 0: StartTimer = 0
  45.             ELSE 'We've now started the hold event
  46.                 MouseHandler = ButtonDown + 3
  47.             END IF
  48.         END IF
  49.     END IF

One quick command to tell you if a button was clicked or held, for any of the 3 mouse buttons.
Title: Re: and another one for your toolbox...
Post by: bplus on August 06, 2019, 09:11:46 am
And another rather simple routine for your toolbox collection -- a quick mouse handler.

Code: QB64: [Select]
  1.     CLS
  2.     held$ = ""
  3.     SELECT CASE MBS
  4.         CASE 1: left = left + 1
  5.         CASE 2: right = right + 1
  6.         CASE 3: middle = middle + 1
  7.         CASE 4: held$ = "Left held"
  8.         CASE 5: held$ = "Right held"
  9.         CASE 6: held$ = "Middle held"
  10.     END SELECT
  11.  
  12.     PRINT "MouseX: "; _MOUSEX
  13.     PRINT "MouseY: "; _MOUSEY
  14.     PRINT "Left pressed: "; left
  15.     PRINT "Right pressed: "; right
  16.     PRINT "Middle pressed: "; middle
  17.     PRINT held$
  18.     _LIMIT 10
  19.  
  20.  
  21. FUNCTION MBS 'Mouse Button Status
  22.     STATIC StartTimer AS _FLOAT
  23.     STATIC ButtonDown AS INTEGER
  24.     STATIC ClickCount AS INTEGER
  25.     CONST ClickLimit## = 0.2 '2/10th of a second to down, up a key to count as a CLICK.  Down longer counts as a HOLD event.
  26.  
  27.     WHILE _MOUSEINPUT: WEND 'Remark out this line, if mouse main input/clear is going to be handled manually in main program.
  28.  
  29.     IF StartTimer = 0 THEN
  30.         IF _MOUSEBUTTON(1) THEN 'If a button is pressed, start the timer to see what it does (click or hold)
  31.             ButtonDown = 1: StartTimer = TIMER(0.01)
  32.         ELSEIF _MOUSEBUTTON(2) THEN
  33.             ButtonDown = 2: StartTimer = TIMER(0.01)
  34.         ELSEIF _MOUSEBUTTON(3) THEN
  35.             ButtonDown = 3: StartTimer = TIMER(0.01)
  36.         END IF
  37.     ELSE
  38.         BD = ButtonDown MOD 3
  39.         IF BD = 0 THEN BD = 3
  40.         IF TIMER(0.01) - StartTimer <= ClickLimit THEN 'Button was down, then up, within time limit.  It's a click
  41.             IF _MOUSEBUTTON(BD) = 0 THEN MouseHandler = ButtonDown: ButtonDown = 0: StartTimer = 0
  42.         ELSE
  43.             IF _MOUSEBUTTON(BD) = 0 THEN 'hold event has now ended
  44.                 MouseHandler = 0: ButtonDown = 0: StartTimer = 0
  45.             ELSE 'We've now started the hold event
  46.                 MouseHandler = ButtonDown + 3
  47.             END IF
  48.         END IF
  49.     END IF

One quick command to tell you if a button was clicked or held, for any of the 3 mouse buttons.

Hi Steve,

Shall we assume you intended to set the Function MBS with the ButtonDown value before exiting function?
Code: QB64: [Select]
  1. _TITLE "MBS mouse button status by Steve" ' 2019-08-06
  2.  
  3.     CLS
  4.     held$ = ""
  5.     SELECT CASE MBS
  6.         CASE 1: left = left + 1
  7.         CASE 2: right = right + 1
  8.         CASE 3: middle = middle + 1
  9.         CASE 4: held$ = "Left held"
  10.         CASE 5: held$ = "Right held"
  11.         CASE 6: held$ = "Middle held"
  12.     END SELECT
  13.  
  14.     PRINT "MouseX: "; _MOUSEX
  15.     PRINT "MouseY: "; _MOUSEY
  16.     PRINT "Left pressed: "; left
  17.     PRINT "Right pressed: "; right
  18.     PRINT "Middle pressed: "; middle
  19.     PRINT held$
  20.     _LIMIT 10
  21.  
  22.  
  23. FUNCTION MBS 'Mouse Button Status
  24.     STATIC StartTimer AS _FLOAT
  25.     STATIC ButtonDown AS INTEGER
  26.     STATIC ClickCount AS INTEGER
  27.     CONST ClickLimit## = 0.2 '2/10th of a second to down, up a key to count as a CLICK.  Down longer counts as a HOLD event.
  28.  
  29.     WHILE _MOUSEINPUT: WEND 'Remark out this line, if mouse main input/clear is going to be handled manually in main program.
  30.  
  31.     IF StartTimer = 0 THEN
  32.         IF _MOUSEBUTTON(1) THEN 'If a button is pressed, start the timer to see what it does (click or hold)
  33.             ButtonDown = 1: StartTimer = TIMER(0.01)
  34.         ELSEIF _MOUSEBUTTON(2) THEN
  35.             ButtonDown = 2: StartTimer = TIMER(0.01)
  36.         ELSEIF _MOUSEBUTTON(3) THEN
  37.             ButtonDown = 3: StartTimer = TIMER(0.01)
  38.         END IF
  39.     ELSE
  40.         BD = ButtonDown MOD 3
  41.         IF BD = 0 THEN BD = 3
  42.         IF TIMER(0.01) - StartTimer <= ClickLimit THEN 'Button was down, then up, within time limit.  It's a click
  43.             IF _MOUSEBUTTON(BD) = 0 THEN MouseHandler = ButtonDown: ButtonDown = 0: StartTimer = 0
  44.         ELSE
  45.             IF _MOUSEBUTTON(BD) = 0 THEN 'hold event has now ended
  46.                 MouseHandler = 0: ButtonDown = 0: StartTimer = 0
  47.             ELSE 'We've now started the hold event
  48.                 MouseHandler = ButtonDown + 3
  49.             END IF
  50.         END IF
  51.     END IF
  52.     MBS = ButtonDown '<<< fix set function with ButtonDown ???
  53.  

hmm... second time in couple days you post a fine function but don't set the value, makes me wonder how much you test code before posting... maybe it's the meds or the summer heat?

wait... just making bplus feel useful ;-))


Actually I found this mouse code and tester interesting that Steve had posted a while back:
Code: QB64: [Select]
  1. _TITLE "Mouse events using device" 'Steve McNeill 2019-02-21
  2. DIM SHARED MouseX AS INTEGER, MouseY AS INTEGER
  3. DIM SHARED MouseWheel AS INTEGER
  4. DIM SHARED LeftMouse AS INTEGER, RightMouse AS INTEGER, MiddleMouse AS INTEGER
  5. DIM SHARED ClickThreshold AS SINGLE
  6. ClickThreshold = 0.2 'down and up in threshold of a second to count as a click.  If down for more then threshold, we just have a hold event.
  7.  
  8.  
  9. SCREEN _NEWIMAGE(640, 480, 32)
  10.  
  11.  
  12.  
  13.     _LIMIT 60
  14.  
  15.     UpdateMouseInfo
  16.     LOCATE 1, 1
  17.     PRINT MouseX, MouseY
  18.     PRINT MouseWheel
  19.     PRINT LeftMouse, MiddleMouse, RightMouse
  20.     IF LeftMouse AND 2 THEN leftclick = leftclick + 1
  21.     IF MiddleMouse AND 2 THEN middleclick = middleclick + 1
  22.     IF RightMouse AND 2 THEN rightclick = rightclick + 1
  23.     PRINT leftclick, middleclick, rightclick
  24.  
  25. LOOP UNTIL INKEY$ = CHR$(27) 'escape key exit
  26.  
  27. SUB UpdateMouseInfo
  28.     DIM SW AS INTEGER, SH AS INTEGER
  29.     DIM LM AS INTEGER, MM AS INTEGER, RM AS INTEGER
  30.  
  31.     STATIC leftdown AS SINGLE, middledown AS SINGLE, rightdown AS SINGLE
  32.  
  33.     WHILE _DEVICEINPUT(2): MouseWheel = MouseWheel + _WHEEL(3): WEND 'clear and update the mouse buffer
  34.  
  35.     SW = _WIDTH \ 2: SH = _HEIGHT \ 2
  36.     MouseX = _AXIS(1) * SW + SW: MouseY = _AXIS(2) * SH + SH
  37.  
  38.  
  39.  
  40.     LM = _BUTTON(1): MM = _BUTTON(2): RM = _BUTTON(3)
  41.  
  42.     IF leftdown THEN 'if it was down
  43.         IF LM = 0 THEN 'and is now up
  44.             IF TIMER - leftdown < ClickThreshold THEN
  45.                 LeftMouse = 2 'clicked
  46.             ELSE 'if it's still down
  47.                 LeftMouse = 0 'the mouse was just released
  48.             END IF
  49.             leftdown = 0 'timer is cleared either way
  50.         ELSE
  51.             LeftMouse = 1 'the left mouse is down , timer should have already been set
  52.         END IF
  53.     ELSE
  54.         IF LM THEN
  55.             leftdown = TIMER 'set the timer to see if we have click or hold events
  56.             LeftMouse = 1 'the left mouse is down
  57.         ELSE
  58.             LeftMouse = 0
  59.         END IF
  60.     END IF
  61.  
  62.     IF middledown THEN 'if it was down
  63.         IF MM = 0 THEN 'and is now up
  64.             IF TIMER - middledown < ClickThreshold THEN
  65.                 MiddleMouse = 2 'clicked
  66.             ELSE 'if it's still down
  67.                 MiddleMouse = 0 'the mouse was just released
  68.             END IF
  69.             middledown = 0 'timer is cleared either way
  70.         ELSE
  71.             MiddleMouse = 1 'the middle mouse is down , timer should have already been set
  72.         END IF
  73.     ELSE
  74.         IF MM THEN
  75.             middledown = TIMER 'set the timer to see if we have click or hold events
  76.             MiddleMouse = 1 'the middle mouse is down
  77.         ELSE
  78.             MiddleMouse = 0
  79.         END IF
  80.     END IF
  81.  
  82.     IF rightdown THEN 'if it was down
  83.         IF RM = 0 THEN 'and is now up
  84.             IF TIMER - rightdown < ClickThreshold THEN
  85.                 RightMouse = 2 'clicked
  86.             ELSE 'if it's still down
  87.                 RightMouse = 0 'the mouse was just released
  88.             END IF
  89.             rightdown = 0 'timer is cleared either way
  90.         ELSE
  91.             RightMouse = 1 'the right mouse is down , timer should have already been set
  92.         END IF
  93.     ELSE
  94.         IF RM THEN
  95.             rightdown = TIMER 'set the timer to see if we have click or hold events
  96.             RightMouse = 1 'the right mouse is down
  97.         ELSE
  98.             RightMouse = 0
  99.         END IF
  100.     END IF
  101.  
  102.  
  103.  
  104.  

I thought this useful because it handles the mouse wheel (my mouse has a wheel not a middle button) and left and right buttons.




Title: Re: and another one for your toolbox...
Post by: SMcNeill on August 06, 2019, 10:15:20 am
Hi Steve,

Shall we assume you intended to set the Function MBS with the ButtonDown value before exiting function?

hmm... second time in couple days you post a fine function but don't set the value, makes me wonder how much you test code before posting... maybe it's the meds or the summer heat?

wait... just making bplus feel useful ;-))

Actually I found this mouse code and tester interesting that Steve had posted a while back:

I blame it on the stupid medication.  :P

Tested, it works -- as long as I don't go in and halfway rename things to make them "simpler" when I finish them.  (MouseHandler was the original name for the routine, but I renamed it to a simple "MBS" and didn't change everything like I should have, like a big idiot...)

Here's the version once again -- even enhanced this time to handle the middle scroll button for you!  :P

Code: QB64: [Select]
  1. _TITLE "MBS mouse button status by Steve" ' 2019-08-06
  2.  
  3.     CLS
  4.     held$ = ""
  5.     SELECT CASE MBS
  6.         CASE 1: left = left + 1
  7.         CASE 2: right = right + 1
  8.         CASE 3: middle = middle + 1
  9.         CASE 4: held$ = "Left held"
  10.         CASE 5: held$ = "Right held"
  11.         CASE 6: held$ = "Middle held"
  12.         CASE ELSE: scroll = scroll + MBS \ 10
  13.     END SELECT
  14.  
  15.     PRINT "MouseX: "; _MOUSEX
  16.     PRINT "MouseY: "; _MOUSEY
  17.     PRINT "Left pressed: "; left
  18.     PRINT "Right pressed: "; right
  19.     PRINT "Middle pressed: "; middle
  20.     PRINT "Mouse Wheel Scrolled: "; scroll
  21.     PRINT held$
  22.     _LIMIT 10
  23.  
  24.  
  25. FUNCTION MBS 'Mouse Button Status
  26.     STATIC StartTimer AS _FLOAT
  27.     STATIC ButtonDown AS INTEGER
  28.     STATIC ClickCount AS INTEGER
  29.     CONST ClickLimit## = 0.2 '2/10th of a second to down, up a key to count as a CLICK.  Down longer counts as a HOLD event.
  30.  
  31.     WHILE _MOUSEINPUT 'Remark out this block, if mouse main input/clear is going to be handled manually in main program.
  32.         scroll = scroll + 10 * _MOUSEWHEEL
  33.         IF scroll THEN MBS = scroll: EXIT FUNCTION
  34.     WEND
  35.  
  36.     IF StartTimer = 0 THEN
  37.         IF _MOUSEBUTTON(1) THEN 'If a button is pressed, start the timer to see what it does (click or hold)
  38.             ButtonDown = 1: StartTimer = TIMER(0.01)
  39.         ELSEIF _MOUSEBUTTON(2) THEN
  40.             ButtonDown = 2: StartTimer = TIMER(0.01)
  41.         ELSEIF _MOUSEBUTTON(3) THEN
  42.             ButtonDown = 3: StartTimer = TIMER(0.01)
  43.         END IF
  44.     ELSE
  45.         BD = ButtonDown MOD 3
  46.         IF BD = 0 THEN BD = 3
  47.         IF TIMER(0.01) - StartTimer <= ClickLimit THEN 'Button was down, then up, within time limit.  It's a click
  48.             IF _MOUSEBUTTON(BD) = 0 THEN MBS = ButtonDown: ButtonDown = 0: StartTimer = 0
  49.         ELSE
  50.             IF _MOUSEBUTTON(BD) = 0 THEN 'hold event has now ended
  51.                 MBS = 0: ButtonDown = 0: StartTimer = 0
  52.             ELSE 'We've now started the hold event
  53.                 MBS = ButtonDown + 3
  54.             END IF
  55.         END IF
  56.     END IF
  57.  

Title: Re: and another one for your toolbox...
Post by: bplus on August 06, 2019, 12:37:58 pm
Since we are talking about mouse input, here is what I have come to use or modify for specific needs of app for getting mouse clicks, getClick(mouseX, mouseY, keypress) returns whatever occurs first a left mouse click or keypress AFTER all previous key press and mouse button down detection has been cleared (before the sub was called), it also clears mouse button and key press before return execution to caller code.

This was needed when running complex dialog code like mBox or inputBox, it is important then to get the mouse and key press from dialog to NOT interfere with other mouse and key press events in caller code and vice versa...

It has been an ever evolving process to get everything to work together, here is getClick tested with latest version of mBox and inputBox from which I discovered that a _KEYCLEAR line I had commented out in last mBOX update was still needed (fixed in code above)!

Code: QB64: [Select]
  1. _TITLE "getClick testing with mBox and inputBox" ' by bplus 2018-05-12"
  2. '2019-01-02 added _KEYCLEAR   to clear keys hit waiting for enter press at INPUT
  3. '2019-03-02 discovered this does not work with mBox, can it be tested and fixed?
  4. ' Nope, impossible.
  5. 'can I get mBox to use getClick? No because I want to allow mouse to move mBox by grabbing title bar
  6. 'OK added stuff to clear old mb and they are now working together
  7.  
  8. '2019-08-06 More tests with mBox and now inputBox$()
  9. ' OK test if getClick plays nice with last update of mBox (2019-07-31) and vice versa
  10. ' and also inputBox. Discovered 2019-08-06 mBox did need a _KEYCLEAR! inputBox already had it.
  11.  
  12. CONST xmax = 800
  13. CONST ymax = 600
  14. SCREEN _NEWIMAGE(xmax, ymax, 32)
  15. _SCREENMOVE 360, 60
  16. m$ = "To exit this mBox click X box, press enter, spacebar or escape, to exit this tester press escape (again?) after mBox is off screen."
  17. m$ = m$ + CHR$(10) + "Otherwise when this box is off screen, click screen or enter letters."
  18. m$ = m$ + CHR$(10) + "Once screen is clicked or enter is pressed you should see an inputBox$ come up."
  19.  
  20.     'CLS
  21.     toggle = (toggle + 1) MOD 2
  22.     IF toggle THEN 'test mBox
  23.         mBox "Test mBox with getClick", m$
  24.         PRINT "mBox (Message Box) is a sub that returns nothing, it only provides info. "
  25.         PRINT "If you pressed esacpe from mBox, press again if want to quit."
  26.     ELSE
  27.         'test inputBox
  28.         r$ = inputBox$("Enter a string for inputBox to return to main program.", "Testing getClick with inputBox function.", 76)
  29.         IF r$ = "" THEN PRINT "You entered nothing or escaped inputBox." ELSE PRINT "inputBox Returned: *" + r$ + "*"
  30.     END IF
  31.     PRINT "     To test getClick (now), click (and drag if want) screen or make a single keypress."
  32.     PRINT "What you did should be detected by getClick and reported back to you here on main screen."
  33.  
  34.     'x = -1: y = -1: k = 0      'need this?
  35.     getClick x, y, k
  36.     IF k = 27 OR (k > 31 AND k < 126) THEN
  37.         PRINT "You pressed "; CHR$(k)
  38.         IF k = 27 OR k = 113 OR k = 81 THEN PRINT "Goodbye": END
  39.     ELSE
  40.         PRINT "(" + STR$(x) + ", " + STR$(y) + ")"
  41.     END IF
  42.     PRINT "All additional mouse clicks or keypresses will be ignored until:"
  43.     INPUT "PRESS ENTER TO CONTINUE with another mBox or inputBox$() test... "; wate$
  44.     _LIMIT 30
  45.  
  46. 'getClick returns the mouse x, y position WHEN THE MOUSE WAS RELEASED! or keypress ASC 27 or 32 to 125
  47. '2019-08-06 Test now with new mBox and inputBox procedures
  48. 'found  mBox needed a _KEYCLEAR, how about inputBox?  OK had _KEYCLEAR already
  49. SUB getClick (mx, my, q)
  50.     mb = _MOUSEBUTTON(1)
  51.     WHILE mb
  52.         WHILE _MOUSEINPUT: WEND '<<<<<<<<<<<<<<<<<<<<  clear previous mb
  53.         mb = _MOUSEBUTTON(1)
  54.     WEND
  55.     _KEYCLEAR 'clear previous key presses
  56.     mx = -1: my = -1: q = 0
  57.     DO WHILE mx = -1 AND my = -1
  58.         q = _KEYHIT
  59.         IF q = 27 OR (q > 31 AND q < 126) THEN _KEYCLEAR: EXIT SUB
  60.         i = _MOUSEINPUT: mb = _MOUSEBUTTON(1)
  61.         'IF mb THEN
  62.         DO WHILE mb 'wait for release
  63.             q = _KEYHIT
  64.             IF q = 27 OR (q > 31 AND q < 126) THEN EXIT SUB
  65.             i = _MOUSEINPUT: mb = _MOUSEBUTTON(1): mx = _MOUSEX: my = _MOUSEY
  66.             _LIMIT 1000
  67.         LOOP
  68.         _LIMIT 1000
  69.     LOOP
  70.  
  71. 'title$ limit is 57 chars, all lines are 58 chars max, version 2019-08-06
  72. 'THIS SUB NOW NEEDS SUB scnState(restoreTF) for saving and restoring screen settings
  73. SUB mBox (title AS STRING, m AS STRING)
  74.  
  75.     bg = &HBB000033
  76.     fg = &HFF33AAFF
  77.  
  78.     'first screen dimensions and items to restore at exit
  79.     DIM sw AS INTEGER, sh AS INTEGER
  80.     DIM curScrn AS LONG, backScrn AS LONG, mbx AS LONG 'some handles
  81.     DIM ti AS INTEGER, limit AS INTEGER 'ti = text index for t$(), limit is number of chars per line
  82.     DIM i AS INTEGER, j AS INTEGER, ff AS _BIT, add AS _BYTE 'index, flag and
  83.     DIM bxH AS INTEGER, bxW AS INTEGER 'first as cells then as pixels
  84.     DIM mb AS INTEGER, mx AS INTEGER, my AS INTEGER, mi AS INTEGER, grabx AS INTEGER, graby AS INTEGER
  85.     DIM tlx AS INTEGER, tly AS INTEGER 'top left corner of message box
  86.     DIM lastx AS INTEGER, lasty AS INTEGER, t AS STRING, b AS STRING, c AS STRING, tail AS STRING
  87.     DIM d AS STRING, r AS SINGLE, kh AS LONG
  88.  
  89.     'screen and current settings to restore at end ofsub
  90.     scnState 0
  91.     sw = _WIDTH: sh = _HEIGHT
  92.  
  93.     _KEYCLEAR '<<<<<<<<<<<<<<<<<<<< do i still need this?   YES! 2019-08-06 update!
  94.  
  95.     'screen snapshot
  96.     curScrn = _DEST
  97.     backScrn = _NEWIMAGE(sw, sh, 32)
  98.     _PUTIMAGE , curScrn, backScrn
  99.  
  100.     'setup t() to store strings with ti as index, linit 58 chars per line max, b is for build
  101.     REDIM t(0) AS STRING: ti = 0: limit = 58: b = ""
  102.     FOR i = 1 TO LEN(m)
  103.         c = MID$(m, i, 1)
  104.         'are there any new line signals, CR, LF or both? take CRLF or LFCR as one break but dbl LF or CR means blank line
  105.         SELECT CASE c
  106.             CASE CHR$(13) 'load line
  107.                 IF MID$(m, i + 1, 1) = CHR$(10) THEN i = i + 1
  108.                 t(ti) = b: b = "": ti = ti + 1: REDIM _PRESERVE t(ti) AS STRING
  109.             CASE CHR$(10)
  110.                 IF MID$(m, i + 1, 1) = CHR$(13) THEN i = i + 1
  111.                 t(ti) = b: b = "": ti = ti + 1: REDIM _PRESERVE t(ti)
  112.             CASE ELSE
  113.                 IF c = CHR$(9) THEN c = SPACE$(4): add = 4 ELSE add = 1
  114.                 IF LEN(b) + add > limit THEN
  115.                     tail = "": ff = 0
  116.                     FOR j = LEN(b) TO 1 STEP -1 'backup until find a space, save the tail end for next line
  117.                         d = MID$(b, j, 1)
  118.                         IF d = " " THEN
  119.                             t(ti) = MID$(b, 1, j - 1): b = tail + c: ti = ti + 1: REDIM _PRESERVE t(ti)
  120.                             ff = 1 'found space flag
  121.                             EXIT FOR
  122.                         ELSE
  123.                             tail = d + tail 'the tail grows!
  124.                         END IF
  125.                     NEXT
  126.                     IF ff = 0 THEN 'no break? OK
  127.                         t(ti) = b: b = c: ti = ti + 1: REDIM _PRESERVE t(ti)
  128.                     END IF
  129.                 ELSE
  130.                     b = b + c 'just keep building the line
  131.                 END IF
  132.         END SELECT
  133.     NEXT
  134.     t(ti) = b
  135.     bxH = ti + 3: bxW = limit + 2
  136.  
  137.     'draw message box
  138.     mbx = _NEWIMAGE(60 * 8, (bxH + 1) * 16, 32)
  139.     _DEST mbx
  140.     COLOR _RGB32(128, 0, 0), _RGB32(225, 225, 255)
  141.     LOCATE 1, 1: PRINT LEFT$(SPACE$((bxW - LEN(title) - 3) / 2) + title + SPACE$(bxW), bxW)
  142.     COLOR _RGB32(225, 225, 255), _RGB32(200, 0, 0)
  143.     LOCATE 1, bxW - 2: PRINT " X "
  144.     COLOR fg, bg
  145.     LOCATE 2, 1: PRINT SPACE$(bxW);
  146.     FOR r = 0 TO ti
  147.         LOCATE 1 + r + 2, 1: PRINT LEFT$(" " + t(r) + SPACE$(bxW), bxW);
  148.     NEXT
  149.     LOCATE 1 + bxH, 1: PRINT SPACE$(limit + 2);
  150.  
  151.     'now for the action
  152.     _DEST curScrn
  153.  
  154.     'convert to pixels the top left corner of box at moment
  155.     bxW = bxW * 8: bxH = bxH * 16
  156.     tlx = (sw - bxW) / 2: tly = (sh - bxH) / 2
  157.     lastx = tlx: lasty = tly
  158.     'now allow user to move it around or just read it
  159.     WHILE 1
  160.         CLS
  161.         _PUTIMAGE , backScrn
  162.         _PUTIMAGE (tlx, tly), mbx, curScrn
  163.         _DISPLAY
  164.         WHILE _MOUSEINPUT: WEND
  165.         mx = _MOUSEX: my = _MOUSEY: mb = _MOUSEBUTTON(1)
  166.         IF mb THEN
  167.             IF mx >= tlx AND mx <= tlx + bxW AND my >= tly AND my <= tly + 16 THEN 'mouse down on title bar
  168.                 IF mx >= tlx + bxW - 24 THEN EXIT WHILE
  169.                 grabx = mx - tlx: graby = my - tly
  170.                 DO WHILE mb 'wait for release
  171.                     mi = _MOUSEINPUT: mb = _MOUSEBUTTON(1)
  172.                     mx = _MOUSEX: my = _MOUSEY
  173.                     IF mx - grabx >= 0 AND mx - grabx <= sw - bxW AND my - graby >= 0 AND my - graby <= sh - bxH THEN
  174.                         'attempt to speed up with less updates
  175.                         IF ((lastx - (mx - grabx)) ^ 2 + (lasty - (my - graby)) ^ 2) ^ .5 > 10 THEN
  176.                             tlx = mx - grabx: tly = my - graby
  177.                             CLS
  178.                             _PUTIMAGE , backScrn
  179.                             _PUTIMAGE (tlx, tly), mbx, curScrn
  180.                             lastx = tlx: lasty = tly
  181.                             _DISPLAY
  182.                         END IF
  183.                     END IF
  184.                     _LIMIT 400
  185.                 LOOP
  186.             END IF
  187.         END IF
  188.         kh = _KEYHIT
  189.         IF kh = 27 OR kh = 13 OR kh = 32 THEN EXIT WHILE
  190.         _LIMIT 400
  191.     WEND
  192.  
  193.     'put things back
  194.     COLOR _RGB32(255, 255, 255), _RGB32(0, 0, 0): CLS '
  195.     _PUTIMAGE , backScrn
  196.     _DISPLAY
  197.     _FREEIMAGE backScrn
  198.     _FREEIMAGE mbx
  199.     scnState 1 'Thanks Steve McNeill
  200.  
  201. ' for saving and restoring screen settins
  202. SUB scnState (restoreTF AS INTEGER) 'Thanks Steve McNeill
  203.     STATIC Font AS LONG, DefaultColor AS _UNSIGNED LONG, BackGroundColor AS _UNSIGNED LONG, Dest AS LONG, Source AS LONG
  204.     STATIC row AS INTEGER, col AS INTEGER, autodisplay AS INTEGER, mb AS INTEGER
  205.     IF restoreTF THEN
  206.         _FONT Font
  207.         COLOR DefaultColor, BackGroundColor
  208.         _DEST Dest
  209.         _SOURCE Source
  210.         LOCATE row, col
  211.         IF autodisplay THEN _AUTODISPLAY ELSE _DISPLAY
  212.         _KEYCLEAR
  213.         WHILE _MOUSEINPUT: WEND 'clear mouse clicks
  214.         mb = _MOUSEBUTTON(1)
  215.         IF mb THEN
  216.             DO
  217.                 WHILE _MOUSEINPUT: WEND
  218.                 mb = _MOUSEBUTTON(1)
  219.                 _LIMIT 100
  220.             LOOP UNTIL mb = 0
  221.         END IF
  222.     ELSE
  223.         Font = _FONT: DefaultColor = _DEFAULTCOLOR: BackGroundColor = _BACKGROUNDCOLOR
  224.         Dest = _DEST: Source = _SOURCE
  225.         row = CSRLIN: col = POS(0): autodisplay = _AUTODISPLAY
  226.     END IF
  227.  
  228. ' You can grab this box by title and drag it around screen for full viewing while answering prompt.
  229. ' Only one line allowed for prompt$
  230. ' boxWidth is 4 more than the allowed length of input, it needs to be longer than title$ and prompt$ also
  231. ' Utilities > Input Box > Input Box 1 tester v 2019-07-31
  232. FUNCTION inputBox$ (prompt$, title$, boxWidth AS _BYTE)
  233.     DIM ForeColor AS _UNSIGNED LONG, BackColor AS _UNSIGNED LONG, White AS _UNSIGNED LONG
  234.     DIM sw AS INTEGER, sh AS INTEGER, curScrn AS LONG, backScrn AS LONG, ibx AS LONG 'some handles
  235.  
  236.     'colors
  237.     ForeColor = &HFF000055 '<  change as desired  prompt text color, back color or type in area
  238.     BackColor = &HFF6080CC '<  change as desired  used fore color in type in area
  239.     White = &HFFFFFFFF
  240.  
  241.     'items to restore at exit
  242.     scnState 0
  243.  
  244.     'screen snapshot
  245.     sw = _WIDTH: sh = _HEIGHT: curScrn = _DEST
  246.     backScrn = _NEWIMAGE(sw, sh, 32)
  247.     _PUTIMAGE , curScrn, backScrn
  248.  
  249.     'moving box around on screen
  250.     DIM bxW AS INTEGER, bxH AS INTEGER
  251.     DIM mb AS INTEGER, mx AS INTEGER, my AS INTEGER, mi AS INTEGER, grabx AS INTEGER, graby AS INTEGER
  252.     DIM tlx AS INTEGER, tly AS INTEGER 'top left corner of message box
  253.     DIM lastx AS INTEGER, lasty AS INTEGER
  254.     DIM inp$, kh&
  255.  
  256.     'draw message box
  257.     bxW = boxWidth * 8: bxH = 7 * 16
  258.     ibx = _NEWIMAGE(bxW, bxH, 32)
  259.     _DEST ibx
  260.     COLOR &HFF880000, White
  261.     LOCATE 1, 1: PRINT LEFT$(SPACE$(INT((boxWidth - LEN(title$) - 3)) / 2) + title$ + SPACE$(boxWidth), boxWidth)
  262.     COLOR White, &HFFBB0000
  263.     LOCATE 1, boxWidth - 2: PRINT " X "
  264.     COLOR ForeColor, BackColor
  265.     LOCATE 2, 1: PRINT SPACE$(boxWidth);
  266.     LOCATE 3, 1: PRINT LEFT$(SPACE$((boxWidth - LEN(prompt$)) / 2) + prompt$ + SPACE$(boxWidth), boxWidth);
  267.     LOCATE 4, 1: PRINT SPACE$(boxWidth);
  268.     LOCATE 5, 1: PRINT SPACE$(boxWidth);
  269.     LOCATE 6, 1: PRINT SPACE$(boxWidth);
  270.     inp$ = ""
  271.     GOSUB finishBox
  272.  
  273.     'convert to pixels the top left corner of box at moment
  274.     bxW = boxWidth * 8: bxH = 5 * 16
  275.     tlx = (sw - bxW) / 2: tly = (sh - bxH) / 2
  276.     lastx = tlx: lasty = tly
  277.     _KEYCLEAR
  278.     'now allow user to move it around or just read it
  279.     WHILE 1
  280.         CLS
  281.         _PUTIMAGE , backScrn
  282.         _PUTIMAGE (tlx, tly), ibx, curScrn
  283.         _DISPLAY
  284.         WHILE _MOUSEINPUT: WEND
  285.         mx = _MOUSEX: my = _MOUSEY: mb = _MOUSEBUTTON(1)
  286.         IF mb THEN
  287.             IF mx >= tlx AND mx <= tlx + bxW AND my >= tly AND my <= tly + 16 THEN 'mouse down on title bar
  288.                 IF mx >= tlx + bxW - 24 THEN EXIT WHILE
  289.                 grabx = mx - tlx: graby = my - tly
  290.                 DO WHILE mb 'wait for release
  291.                     mi = _MOUSEINPUT: mb = _MOUSEBUTTON(1)
  292.                     mx = _MOUSEX: my = _MOUSEY
  293.                     IF mx - grabx >= 0 AND mx - grabx <= sw - bxW AND my - graby >= 0 AND my - graby <= sh - bxH THEN
  294.                         'attempt to speed up with less updates
  295.                         IF ((lastx - (mx - grabx)) ^ 2 + (lasty - (my - graby)) ^ 2) ^ .5 > 10 THEN
  296.                             tlx = mx - grabx: tly = my - graby
  297.                             CLS
  298.                             _PUTIMAGE , backScrn
  299.                             _PUTIMAGE (tlx, tly), ibx, curScrn
  300.                             lastx = tlx: lasty = tly
  301.                             _DISPLAY
  302.                         END IF
  303.                     END IF
  304.                     _LIMIT 400
  305.                 LOOP
  306.             END IF
  307.         END IF
  308.         kh& = _KEYHIT
  309.         SELECT CASE kh& 'whew not much for the main event!
  310.             CASE 13: EXIT WHILE
  311.             CASE 27: inp$ = "": EXIT WHILE
  312.             CASE 32 TO 128: IF LEN(inp$) < boxWidth - 4 THEN inp$ = inp$ + CHR$(kh&): GOSUB finishBox ELSE BEEP
  313.             CASE 8: IF LEN(inp$) THEN inp$ = LEFT$(inp$, LEN(inp$) - 1): GOSUB finishBox ELSE BEEP
  314.         END SELECT
  315.  
  316.         _LIMIT 60
  317.     WEND
  318.  
  319.     'put things back
  320.     scnState 1 'need fg and bg colors set to cls
  321.     CLS '? is this needed YES!!
  322.     _PUTIMAGE , backScrn
  323.     _DISPLAY
  324.     _FREEIMAGE backScrn
  325.     _FREEIMAGE ibx
  326.     scnState 1 'because we have to call _display, we have to call this again
  327.     inputBox$ = inp$
  328.  
  329.     finishBox:
  330.     _DEST ibx
  331.     COLOR BackColor, ForeColor
  332.     LOCATE 5, 2: PRINT LEFT$(" " + inp$ + SPACE$(boxWidth - 2), boxWidth - 2)
  333.     _DEST curScrn
  334.     RETURN
  335.  
  336.  

Title: Re: and another one for your toolbox...
Post by: johnno56 on August 06, 2019, 05:57:00 pm
bplus,

Excellent menu. Very cool.

J
Title: Re: and another one for your toolbox...
Post by: bplus on August 06, 2019, 07:18:23 pm
Hi Johnno,

Menu? but thanks :D

I just got a new one today (palindrome):
Never odd or even.
Title: Re: and another one for your toolbox...
Post by: SMcNeill on August 08, 2019, 09:51:23 pm
How about an extended timer, to prevent those annoying bugs where you're running a program at midnight and the clock resets?

Code: QB64: [Select]
  1. SHELL "https://www.epochconverter.com/"
  2.     CLS
  3.     PRINT TIMER, INT(ExtendedTimer)
  4.     PRINT "Compare to the time at https://www.epochconverter.com/"
  5.     _DISPLAY
  6.     _LIMIT 10
  7.  
  8. FUNCTION ExtendedTimer##
  9.     d$ = DATE$
  10.     l = INSTR(d$, "-")
  11.     l1 = INSTR(l + 1, d$, "-")
  12.     m = VAL(LEFT$(d$, l))
  13.     d = VAL(MID$(d$, l + 1))
  14.     y = VAL(MID$(d$, l1 + 1)) - 1970
  15.     FOR i = 1 TO m
  16.         SELECT CASE i 'Add the number of days for each previous month passed
  17.             CASE 1: d = d 'January doestn't have any carry over days.
  18.             CASE 2, 4, 6, 8, 9, 11: d = d + 31
  19.             CASE 3: d = d + 28
  20.             CASE 5, 7, 10, 12: d = d + 30
  21.         END SELECT
  22.     NEXT
  23.     FOR i = 1 TO y
  24.         d = d + 365
  25.     NEXT
  26.     FOR i = 2 TO y STEP 4
  27.         IF m > 2 THEN d = d + 1 'add an extra day for leap year every 4 years, starting in 1970
  28.     NEXT
  29.     d = d - 1 'for year 2000
  30.     s~&& = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
  31.     ExtendedTimer## = (s~&& + TIMER)
  32.  
Title: Re: and another one for your toolbox...
Post by: bplus on October 23, 2019, 08:02:28 pm
Here is a math one, for calculating nCr without fully processed factorials to get around the 21 limit on factorials.
Code: QB64: [Select]
  1. OPTION _EXPLICIT ' B+ started 2019-10-23
  2. _TITLE "Alternate Calc of nCr, the number of Combinations of n things taken r at a time."
  3. ' Though this method is still quite limited at least it does not depend upon the full calculation
  4. ' of a factorial to return a result.
  5. ' You can do n up to at least 50 with r's at 1/2 n, and smaller r's for higher n's.
  6. ' Be very careful accepting returns as correct.
  7.  
  8. PRINT "The number of hands of 13 in a deck is ";
  9. PRINT USING "###,###,###,###,###.###"; nCr(52, 13)
  10. PRINT "                                compare to 635,013,559,600"
  11.     INPUT "Enter n, r to calculate nCr (0's to quit) "; n, r
  12.     IF n > 0 AND r > 0 AND r < n THEN
  13.         PRINT " nCr = "; nCr(n, r)
  14.     END IF
  15. LOOP UNTIL n = 0 OR r = 0
  16.  
  17. FUNCTION nCr~&& (n AS INTEGER, r AS INTEGER)
  18.     'Note: I am using notes from a successful calc to generalize this method.
  19.     ' How many hands of 13 from a deck?
  20.     ' Formula for Combination of N items taken R at a time = N!/(R! * (N-R)!) so we need
  21.     ' 52! / (39! * 13!) =    52 * 51 * 50 * 49 * ... * 40  / 13 * 12 * 11 * ...      * 1
  22.  
  23.     DIM a AS INTEGER, i AS INTEGER, j AS INTEGER, found AS INTEGER
  24.     DIM numerator AS _UNSIGNED _INTEGER64, denomenator AS _UNSIGNED LONG
  25.     a = n - r
  26.     IF a < r THEN r = a 'results are symmteric for r and n-r so use smaller of 2
  27.     a = n - r
  28.  
  29.     DIM numer(1 TO r) AS INTEGER, denom(1 TO r) AS INTEGER
  30.     ' DIM a52_39(1 TO 13) AS INTEGER, a13(1 TO 13) AS INTEGER
  31.     FOR i = 1 TO r
  32.         numer(i) = a + i ' = 40, 41, 42, 43, ... 52 numerator multipliers
  33.         denom(i) = i '     =  1,  2,  3,  4, ... 13 denominator multipliers
  34.     NEXT
  35.  
  36.     ' To keep the numerator from getting too big reduce by demoinator terms that divide evenly
  37.     FOR i = r TO 2 STEP -1
  38.         found = 0
  39.         FOR j = 1 TO r 'is the array item divisible by i
  40.             IF numer(j) MOD i = 0 THEN numer(j) = numer(j) \ i: found = 1: EXIT FOR
  41.         NEXT
  42.         IF found = 1 THEN denom(i) = 1
  43.     NEXT
  44.  
  45.     ' multiply whats left in numerator and denomiator
  46.     numerator = 1: denomenator = 1
  47.     FOR i = 1 TO r
  48.         numerator = numerator * numer(i) 'multiple numerators left
  49.         denomenator = denomenator * denom(i) 'multiply denominators left
  50.     NEXT
  51.     nCr~&& = numerator \ denomenator
  52.  

Here is a demo: https://www.qb64.org/forum/index.php?topic=1792.msg110362#msg110362

Update: 2019-10-28 You are safe using this function to get you past 20! with straight calculation nCR to n=52 after 52 you might want to check calculations against a ball park estimate. If nCr gets the amount of digits right then it is likely precise down to last digit. If the number of digits are off then obviously the precision doesn't matter, it wont be of use.

UPDATE: String Math$ has solved problem of extended factorials check this thread out starting about here:
https://www.qb64.org/forum/index.php?topic=2921.msg121797#msg121797
Title: Re: and another one for your toolbox...
Post by: STxAxTIC on October 26, 2019, 05:25:40 am
I like this work-around bplus. Maybe a few functions along the lines of combinatorics wouldn't be a bad idea... And the problem of large numbers reminds me of something else...
Title: Re: and another one for your toolbox...
Post by: bplus on October 27, 2019, 03:00:40 pm
Thanks [banned user] for getting me to write this up. It is a must have for this collection:
Code: QB64: [Select]
  1. _TITLE "Quick demo of Fisher-Yates " 'B+ 2019-10-27
  2. ' 2019-10-28 slight change from Steve suggestion
  3.  
  4. low = -5
  5. high = 5
  6. DIM deck(low TO high) AS INTEGER
  7.  
  8. PRINT "Deck: ";
  9. FOR c = low TO high
  10.     deck(c) = c
  11.     PRINT c; ", ";
  12.  
  13. FOR i = 1 TO 5
  14.     PRINT "Shuffling..."
  15.     'F-Y shuffle, now 3 liner thanks to Steve suggestion
  16.     FOR c = high TO low + 1 STEP -1
  17.         SWAP deck(c), deck(INT(RND * (c - low + 1)) + low)
  18.     NEXT
  19.  
  20.     'show deck
  21.     PRINT "Deck: ";
  22.     FOR c = low TO high
  23.         PRINT deck(c); ", ";
  24.     NEXT
  25.     PRINT: PRINT
  26.  


Update: FY Shuffle now 3 liner thanks to Steve's suggestion, removed the variable assignment.
Title: Re: and another one for your toolbox...
Post by: bplus on October 27, 2019, 03:05:55 pm
A very common problem, recently brought up by acjacques
https://www.qb64.org/forum/index.php?topic=1785.0

With less keystrokes than STR$(number), you can string it and trim it!
Code: QB64: [Select]
  1. FUNCTION ts$ (number)  'ts stands for trim string
  2.     ts$ = _TRIM$(STR$(number))
  3.  
Title: Re: and another one for your toolbox...
Post by: bplus on October 27, 2019, 03:15:04 pm
LoadSort but you have to adjust code to the Type of Array you are building and the type of Sort ie ascending of descending. Just modify the IF line as needed for the sort condition.

Just as the _TITLE says: Load and Sort an array at the same time, handy to automatically sort an array while you are loading it. Not intended for giant arrays that need number crunching routines to save time.

Code: QB64: [Select]
  1. _TITLE "LoadSort array" 'b+ 2020-06-07 from loadSortD
  2. 'this better than loadSortD because you don't need to share the array nor over DIM it
  3. ' It works like append filing the item into the array list
  4.     REDIM d(0) AS STRING 'clear last array, d(0) stays empty
  5.     FOR i = 1 TO 10
  6.         r$ = RIGHT$("  " + STR$(RND * 100 \ 1), 2)
  7.         IF r$ = "00" THEN r$ = " 0" '0 to 100 range
  8.         loadSort r$, d()
  9.         PRINT r$
  10.     NEXT
  11.     PRINT "Final array:"
  12.     FOR i = 1 TO 10 'note we never use d(0) left empty
  13.         PRINT i, d(i)
  14.     NEXT
  15.     INPUT "Press enter to do again, any other quits "; quit$
  16.     CLS
  17. LOOP UNTIL LEN(quit$)
  18.  
  19. SUB loadSort (insertN AS STRING, dynArr() AS STRING) ' note this leaves dynArr(0) empty! so ubound of array is also number of items in list
  20.     DIM ub, j, k
  21.     ub = UBOUND(dynArr) + 1
  22.     REDIM _PRESERVE dynArr(LBOUND(dynArr) TO ub) AS STRING
  23.     FOR j = 1 TO ub - 1
  24.         IF insertN > dynArr(j) THEN ' GT to LT according to descending or ascending sort
  25.             FOR k = ub TO j + 1 STEP -1
  26.                 dynArr(k) = dynArr(k - 1)
  27.             NEXT
  28.             EXIT FOR
  29.         END IF
  30.     NEXT
  31.     dynArr(j) = insertN
  32.  
  33.  

Embed code like this into place you are loading a smallish array you want sorted.

Edited with Steve's suggestion, thanks Steve!
x2 Edit thanks again Steve!
x3 Edit found another huge line savings, cut 4 lines, demo using Type Definition for array: https://www.qb64.org/forum/index.php?topic=1833.msg110748#msg110748

2020-06-07 Edit #4 now? While working on Gin Rummy, I needed to load card arrays according to highest points first. This new version is a sub routine, does not need oversized array already DIM and SHARED.

It is better than sAppend! if you need a sorted array while loading it.
Title: Re: and another one for your toolbox...
Post by: SMcNeill on October 27, 2019, 03:35:19 pm
You can remove that variable “stored” in the above and save some memory and processing.

Instead of IF stored = 0 THEN, change that statement to IF j = i THEN..

When you store a value, you exit J early, so it’s always less than I, but if it searches the whole loop, it terminates at (I - 1) + 1, which is I.

It saves setting and resetting the variable each pass of I.  ;)
Title: Re: and another one for your toolbox...
Post by: bplus on October 27, 2019, 04:05:22 pm
Beautiful! Thanks Steve, code has been edited.
Title: Re: and another one for your toolbox...
Post by: SMcNeill on October 27, 2019, 05:01:00 pm
Actually, you can simplify it even more.  Just remove the IF completely and make n(j) = num.

In the loop, you set n(j) then exit the loop.
Outside the loop, if I = J then you set n(I)...  but since I = J, you could set n(J) instead...

And, if you set n(j) inside the loop, as well as outside the loop, you can simplify down to just *always* setting it once at the end of the loop.  No IF needed, and no need to set it inside the loop either.  ;)
Title: Re: and another one for your toolbox...
Post by: bplus on October 27, 2019, 07:02:22 pm
Ha! the less lines the more I like it, thanks again Steve!

Code edited again.
Title: Re: and another one for your toolbox...
Post by: bplus on November 01, 2019, 01:03:29 pm
Another common problem brought up by Zeppelin here:
https://www.qb64.org/forum/index.php?topic=1822.0

inputG SUB
A quick way to get an INPUT variable on a graphics screen.
Code: QB64: [Select]
  1. _TITLE "Graphics Input, inputG test" 'b+ 2019-11-01
  2. '2019-11-01 TempodiBasic makes a fine point, 2 little changes
  3. SCREEN _NEWIMAGE(800, 600, 32)
  4. _SCREENMOVE 300, 20
  5. y = 200: x = 100
  6.     P$ = "Enter something like *testing 1, 2, 3... * about 20 chars max > "
  7.     expectedEnterLen% = 20
  8.     inputG x, y, P$, inpt$, expectedEnterLen%
  9.     IF inpt$ = "" THEN EXIT DO
  10.     PRINT "You entered: "; inpt$
  11.     y = y + 25
  12.     x = x + 30
  13.     IF x + (LEN(P$) + expectedEnterLen% + 5) * 8 > _WIDTH THEN x = 1
  14. PRINT "Test is done when empty string returned by inputG."
  15.  
  16. 'INPUT for Graphics screen
  17. SUB inputG (x, y, prmpt$, var$, expectedLenVar%) 'input for a graphics screen x, y is where the prompt will start , returns through var$
  18.     DIM tmp$, k$, saveAD
  19.     saveAD = _AUTODISPLAY
  20.     _KEYCLEAR
  21.     _PRINTSTRING (x, y), prmpt$ + " {}"
  22.     IF saveAD <> -1 THEN _DISPLAY  ' EDIT: 2019-12-05 added this line
  23.     DO
  24.         k$ = INKEY$
  25.         IF LEN(k$) = 1 THEN
  26.             SELECT CASE ASC(k$)
  27.                 CASE 13: var$ = tmp$: EXIT SUB
  28.                 CASE 27: var$ = "": EXIT SUB
  29.                 CASE 8 'backspace
  30.                     IF LEN(tmp$) THEN
  31.                         IF LEN(tmp$) = 1 THEN tmp$ = "" ELSE tmp$ = LEFT$(tmp$, LEN(tmp$) - 1)
  32.                     END IF
  33.                 CASE ELSE: IF ASC(k$) > 31 THEN tmp$ = tmp$ + k$
  34.             END SELECT
  35.             _PRINTSTRING (x, y), prmpt$ + " {" + tmp$ + "}" + SPACE$(expectedLenVar% - LEN(tmp$)) 'spaces needed at end to clear backspace chars
  36.             IF saveAD <> -1 THEN _DISPLAY
  37.         END IF
  38.     LOOP
  39.  

I expect this will be edited as refinements are brought up.

Steve recommended Terry Ritchie's library but the learning curve for that! Plus he's not here any more to answer questions... this kind of formality is for computer scientists IMHO.

Anyway, another good one or at least a good start for a handy tool.

EDIT #1: TempodiBasic makes a fine point and now 2 little changes have been made.
EDIT #2: Sorry Terry, I can't remember to spell your name correctly.
EDIT #3: 2019-12-05 Discovered a much needed _DISPLAY line when _DISPLAY has been activated.
Title: Re: and another one for your toolbox...
Post by: TempodiBasic on November 01, 2019, 05:14:29 pm
Fine !
The bag of tool has another function!

PS: one little feedback INKEY$ brings also the keys with two bytes into the input taken, for example  arrow keys are added to the string returned by the SUB inputG.
Title: Re: and another one for your toolbox...
Post by: bplus on November 01, 2019, 11:40:32 pm
Fine !
The bag of tool has another function!

PS: one little feedback INKEY$ brings also the keys with two bytes into the input taken, for example  arrow keys are added to the string returned by the SUB inputG.

You make fine point, I make 2 changes 1=2 ;-))
Title: Re: and another one for your toolbox...
Post by: TempodiBasic on November 02, 2019, 01:40:19 pm
Great Bplus
your improvement is clear and makes the SUB inputG more powerful and useful!
Title: Re: and another one for your toolbox...
Post by: bplus on November 02, 2019, 02:12:55 pm
Thank you for your feedback, TempodiBasic.
Title: Re: and another one for your toolbox...
Post by: bplus on November 03, 2019, 09:38:25 pm
Update on Load and Sort, cut 4 more lines when applied here using Type definition:
https://www.qb64.org/forum/index.php?topic=1833.msg110748#msg110748

so this Load and Sort tool is on it's 3rd improvement!
https://www.qb64.org/forum/index.php?topic=1511.msg110459#msg110459

hmm... wonder how Steve missed that? ;-) busy on the farm I bet!
Title: Re: and another one for your toolbox...
Post by: bplus on November 20, 2019, 12:55:41 am
Really handy for drawing ftri and fquad to avoid Paint fills for polygons.

Here is demo using fquad (which uses ftri twice) to make a cube:
Code: QB64: [Select]
  1. _TITLE "Cube It"
  2. CONST xmax = 1200, ymax = 600
  3. SCREEN _NEWIMAGE(xmax, ymax, 32)
  4. _SCREENMOVE 100, 40
  5.  
  6. PRINT " bplus was here "
  7. DIM section(127, 16)
  8. FOR y = 0 TO 16
  9.     FOR x = 0 TO 127
  10.         IF POINT(x, y) <> _RGB32(0, 0, 0) THEN section(x, y) = 1
  11.     NEXT
  12.  
  13. FOR l = 0 TO -32 * 4 STEP -4
  14.     FOR y = 8 TO 8 * 16 STEP 8
  15.         FOR x = 127 * 8 TO 0 STEP -8
  16.             IF section(x / 8, y / 8) THEN cube2 x - l + 25, 350 + y + l, 16, &HFF88BBFF
  17.         NEXT
  18.     NEXT
  19.     _DISPLAY
  20.     _LIMIT 30
  21.  
  22. SUB cube2 (x, y, side, c~&)
  23.     sd2 = side / 2
  24.     LINE (x + sd2, y)-STEP(sd2 - 1, sd2 - 1), c~&, BF
  25.     r = _RED32(c~&): g = _GREEN32(c~&): b = _BLUE32(c~&)
  26.     fquad x + sd2, y, x + sd2, y + sd2, x, y + side, x, y + sd2, _RGB32(.25 * r, .5 * g, .75 * b)
  27.     fquad x, y + side, x + sd2, y + sd2, x + side, y + sd2, x + sd2, y + side, _RGB32(.75 * r, .3 * g, .3 * b)
  28.  
  29. ' EDIT 2019-11-20 Improvement by Steve, add a little more speed
  30. SUB ftri (x1, y1, x2, y2, x3, y3, K AS _UNSIGNED LONG)
  31.     STATIC a&
  32.     D = _DEST
  33.     IF a& = 0 THEN a& = _NEWIMAGE(1, 1, 32)
  34.      _DEST a&
  35.     PSET (0, 0), K
  36.     _DEST D
  37.     _MAPTRIANGLE _SEAMLESS(0, 0)-(0, 0)-(0, 0), a& TO(x1, y1)-(x2, y2)-(x3, y3)
  38.  
  39. 'need 4 non linear points (not all on 1 line) list them clockwise so x2, y2 is opposite of x4, y4
  40. SUB fquad (x1, y1, x2, y2, x3, y3, x4, y4, K AS _UNSIGNED LONG)
  41.     ftri x1, y1, x2, y2, x4, y4, K
  42.     ftri x3, y3, x2, y2, x4, y4, K
  43.  
  44.  

EDIT: Update 2019-11-20 Steve strikes again, with Static no longer need to _FREEIMAGE, saves some time plus we preserve old DEST and restore after drawing ftri.
Title: Re: and another one for your toolbox...
Post by: SMcNeill on November 21, 2019, 12:04:56 pm
A quick function to tell if a string is a number or not:

Code: [Select]
FUNCTION VerifyNumber (text$)
    t$ = LTRIM$(RTRIM$(text$))
    v = VAL(t$)
    t1$ = LTRIM$(STR$(v))
    IF t$ = t1$ THEN VerifyNumber = -1
END FUNCTION

Works in most cases, unless you overload it and it changes formats on you, such as from 100000000000000000000000000000000 to 1.0E+20 (or whatever that number of 1s and 0s actually is).

We can push it to the point where this little function fails, but in most ordinary cases, it works like a charm.  :)
Title: Re: and another one for your toolbox...
Post by: SMcNeill on November 29, 2019, 01:31:28 pm
And here's one you might like -- a smart PAUSE routine:

Code: QB64: [Select]
  1. SUB Pause (time AS _FLOAT)
  2.     DIM ExitTime AS _FLOAT
  3.     _KEYCLEAR 'clear the keyboard buffer so we don't automatically exit the routine
  4.     IF time <= 0 THEN ExitTime = 1.18E+1000 ELSE ExitTime = time + Timer
  5.     oldmouse = -1 ‘assume the mouse starts in an invalid down state
  6.     DO
  7.         WHILE _MOUSEINPUT: WEND: IF _MOUSEBUTTON(1) AND NOT oldmouse THEN EXIT SUB
  8.         k = _KEYHIT: IF k > 0 THEN _KEYCLEAR: EXIT SUB 'clear any stray key events so they don't mess with code outside the Pause.
  9.         oldmouse = _MOUSEBUTTON(1)
  10.         _LIMIT 10
  11.     LOOP UNTIL ExitTime < Timer

I suggest using it with ExtendedTimer instead, so you don't have any pesky little "it acts goofy at midnight" glitches, but this works just like SLEEP for us, except it also supports a mouse click as a valid button press to break it.
Title: Re: and another one for your toolbox...
Post by: TempodiBasic on November 30, 2019, 06:08:09 am
I like this toolbox  as more as time goes on.
thanks to share.
Title: Re: and another one for your toolbox...
Post by: bplus on November 30, 2019, 05:15:13 pm
I think this will get us past the midnight problem without dependency on another subroutine:
Code: QB64: [Select]
  1. OPTION _EXPLICIT 'B+ 2019-11-30 get past midnight problem without another routine
  2.  
  3. PRINT "Hello World, press key, click mouse or wait 5 secs for goodbye."
  4. cSleep 5
  5. PRINT "goodbye world"
  6.  
  7. 'c for click + SLEEP, this does force you to commit to a max time to wait
  8. SUB cSleep (secsWait AS DOUBLE) 'wait for keypress or mouseclick, solves midnight problem nicely I think
  9.     DIM wayt AS INTEGER, oldMouse AS INTEGER, k AS LONG, startTime AS DOUBLE
  10.  
  11.     startTime = TIMER
  12.     wayt = 1
  13.     _KEYCLEAR
  14.     WHILE wayt
  15.         WHILE _MOUSEINPUT: WEND
  16.         IF _MOUSEBUTTON(1) AND oldMouse = 0 THEN wayt = 0
  17.         oldMouse = _MOUSEBUTTON(1) ' <<< this is Steve's cool way to get clear of mouse click
  18.         k = _KEYHIT: IF k > 0 THEN _KEYCLEAR: wayt = 0
  19.         IF TIMER - startTime < 0 THEN 'past midnight
  20.             IF TIMER + 24 * 60 * 60 - startTime > secsWait THEN wayt = 0
  21.         ELSE
  22.             IF TIMER - startTime >= secsWait THEN wayt = 0
  23.         END IF
  24.         _LIMIT 30
  25.     WEND
  26.  
Title: Re: and another one for your toolbox...
Post by: bplus on December 01, 2019, 11:00:12 am
Here is nicer code for BIN$, I picked this version up at JB but I think I saw Petr and/or Steve using similar. It looks more elegant than my previous version.
The &B is my main contribution for a base ID, compare with &H and &O that QB64 uses.

Code: QB64: [Select]
  1. FUNCTION BIN$ (integerBase10 AS _INTEGER64)
  2.     DIM j AS INTEGER, B$
  3.     IF integerBase10 = 0 THEN BIN$ = "&B0": EXIT FUNCTION
  4.     WHILE 2 ^ j <= integerBase10
  5.         IF (integerBase10 AND 2 ^ j) > 0 THEN B$ = "1" + B$ ELSE B$ = "0" + B$
  6.         j = j + 1
  7.     WEND
  8.     BIN$ = "&B" + B$
  9.  

The Librarian may pickup on this and update code in official Toolbox?

Oh we've gone full circle, I will update OP!
Title: Re: and another one for your toolbox...
Post by: RhoSigma on December 02, 2019, 08:13:46 am
Here's yet another one ...

Code: QB64: [Select]
  1. FUNCTION BIN$ (value&&)
  2. DIM v&&, vi&&
  3. BIN$ = "": v&& = value&&
  4.     vi&& = INT(v&& / 2)
  5.     IF v&& / 2 = vi&& THEN BIN$ = "0" + BIN$: ELSE BIN$ = "1" + BIN$
  6.     v&& = vi&&
  7. LOOP UNTIL v&& = 0
  8.  

BTW - It doesn't add the &B prefix, just as HEX$ and OCT$ doesn't add &H and &O respectivly.
Title: Re: and another one for your toolbox...
Post by: bplus on December 02, 2019, 10:34:01 am
Quote
BTW - It doesn't add the &B prefix, just as HEX$ and OCT$ doesn't add &H and &O respectivly.

Seems a shame too. Look at 10000 that has 4 different possible values.
Title: Re: and another one for your toolbox...
Post by: RhoSigma on December 02, 2019, 10:55:55 am
Seems a shame too. Look at 10000 that has 4 different possible values.

For sure, I'd like it too, if each function would add its respective prefix, but it would break compatiblity with legacy code. But on the other side HEX$, OCT$ and now BIN$ belong to the same type of function and should behave the same way, that's why I didn't add the &B prefix.

From a different point of view, &B is a new addition added in or after v1.0, that's basically the reason why QB64 misses the built-in BIN$ function. So if BIN$ is QB64 specific, shouldn't it be _BIN$ intead? And what do we do with the binary prefix then, _&B ??
Title: Re: and another one for your toolbox...
Post by: bplus on December 02, 2019, 11:03:49 am
For sure, I'd like it too, if each function would add its respective prefix, but it would break compatiblity with legacy code. But on the other side HEX$, OCT$ and now BIN$ belong to the same type of function and should behave the same way, that's why I didn't add the &B prefix.

From a different point of view, &B is a new addition added in or after v1.0, that's basically the reason why QB64 misses the built-in BIN$ function. So if BIN$ is QB64 specific, shouldn't it be _BIN$ intead? And what do we do with the binary prefix then, _&B ??


Of course _&B is silly but hmm... _HEX$, _OCT$, _BIN$ that use prefixes all! not terrible ;)
Title: Re: and another one for your toolbox...
Post by: bplus on December 02, 2019, 01:30:12 pm
Reconsidered again, I use the string all the time without the &B (we have the number value already when we call the function so not likely need to convert back Plus it is easier to add than to remove, so
Code: QB64: [Select]
  1. FUNCTION BIN$ (integerBase10 AS _INTEGER64) 'no more &B because easier to add than to remove
  2.     DIM j AS INTEGER
  3.     IF integerBase10 = 0 THEN BIN$ = "0": EXIT FUNCTION
  4.     WHILE 2 ^ j <= integerBase10
  5.         IF (integerBase10 AND 2 ^ j) > 0 THEN BIN$ = "1" + BIN$ ELSE BIN$ = "0" + BIN$
  6.         j = j + 1
  7.     WEND
  8.  

The function just needs 1 variable, so I use something of Rho's
Title: Re: and another one for your toolbox...
Post by: RhoSigma on December 02, 2019, 03:47:23 pm
And finally ... a speed test:

Code: QB64: [Select]
  1. _TITLE "BIN$ speed test..."
  2. PRINT "BIN$ speed test ... System: "; _OS$: PRINT
  3. PRINT "TB = current version from Toolbox board"
  4. PRINT "BP = latest version from B+"
  5. PRINT "RS = version from RhoSigma"
  6.  
  7. '--- for a fair test we need the same numbers for all
  8. '--- functions, so we first fill an array with numbers
  9. REDIM num&(1000000)
  10. FOR i& = 0 TO 1000000
  11.     num&(i&) = INT(RND(1) * 1000000000) + 1000000000
  12. NEXT i&
  13.  
  14. '--- Toolbox ---
  15. st# = TIMER(0.001)
  16. PRINT "TB-Start:"; st#
  17. FOR i& = 0 TO 1000000
  18.     b$ = BIN_TB$(num&(i&))
  19. NEXT i&
  20. et# = TIMER(0.001)
  21. IF et# = st# THEN et# = et# + 0.001 'avoid div by zero below
  22. PRINT "TB-End..:"; et#
  23. PRINT "Run-Time:"; et# - st#; "sec."
  24. PRINT "Speed...:"; 1000000 / (et# - st#); "LONGs/sec."
  25.  
  26. '--- bplus ---
  27. st# = TIMER(0.001)
  28. PRINT "BP-Start:"; st#
  29. FOR i& = 0 TO 1000000
  30.     b$ = BIN_BP$(num&(i&))
  31. NEXT i&
  32. et# = TIMER(0.001)
  33. IF et# = st# THEN et# = et# + 0.001 'avoid div by zero below
  34. PRINT "BP-End..:"; et#
  35. PRINT "Run-Time:"; et# - st#; "sec."
  36. PRINT "Speed...:"; 1000000 / (et# - st#); "LONGs/sec."
  37.  
  38. '--- RhoSigma ---
  39. st# = TIMER(0.001)
  40. PRINT "RS-Start:"; st#
  41. FOR i& = 0 TO 1000000
  42.     b$ = BIN_RS$(num&(i&))
  43. NEXT i&
  44. et# = TIMER(0.001)
  45. IF et# = st# THEN et# = et# + 0.001 'avoid div by zero below
  46. PRINT "RS-End..:"; et#
  47. PRINT "Run-Time:"; et# - st#; "sec."
  48. PRINT "Speed...:"; 1000000 / (et# - st#); "LONGs/sec."
  49. '--- cleanup & wait ---
  50. PRINT "...done, press any key"
  51. ERASE num&
  52.  
  53. '================================
  54. '=== AND HERE COMES THE MAGIC ===
  55. '================================
  56.  
  57. FUNCTION BIN_TB$ (integerBase10 AS _INTEGER64)
  58.     DIM i AS _INTEGER64, pow AS INTEGER, b$
  59.     IF integerBase10 = 0 THEN BIN_TB$ = "&B0": EXIT FUNCTION
  60.     i = integerBase10 'copy
  61.     pow = 0
  62.     WHILE i > 0
  63.         IF i AND 2 ^ pow THEN
  64.             b$ = "1" + b$
  65.             i = i - 2 ^ pow
  66.         ELSE
  67.             b$ = "0" + b$
  68.         END IF
  69.         pow = pow + 1
  70.     WEND
  71.     BIN_TB$ = "&B" + b$
  72.  
  73. FUNCTION BIN_BP$ (integerBase10 AS _INTEGER64) 'no more &B because easier to add than to remove
  74.     DIM j AS INTEGER
  75.     IF integerBase10 = 0 THEN BIN_BP$ = "0": EXIT FUNCTION
  76.     WHILE 2 ^ j <= integerBase10
  77.         IF (integerBase10 AND 2 ^ j) > 0 THEN BIN_BP$ = "1" + BIN_BP$ ELSE BIN_BP$ = "0" + BIN_BP$
  78.         j = j + 1
  79.     WEND
  80.  
  81. FUNCTION BIN_RS$ (value&&)
  82.     DIM v&&, vi&&
  83.     BIN_RS$ = "": v&& = value&&
  84.     DO
  85.         vi&& = INT(v&& / 2)
  86.         IF v&& / 2 = vi&& THEN BIN_RS$ = "0" + BIN_RS$: ELSE BIN_RS$ = "1" + BIN_RS$
  87.         v&& = vi&&
  88.     LOOP UNTIL v&& = 0
  89.  
  90.  
Title: Re: and another one for your toolbox...
Post by: RhoSigma on December 02, 2019, 04:39:25 pm
And here is my final one,
it's even twice as fast as my first one (which is in the speed test) by simply avoiding the temporary string operations done under the QB64 hood when adding the "0" or "1" into an existing string. It cuts the times to approx. 3.05 sec. with the x86 build and 2.54 sec. with x64.

Code: QB64: [Select]
  1. FUNCTION BIN$ (value&&)
  2.     'chp% = char position, msp% = most significant position
  3.     'tlv&& = temporary loop value, ivq&& = integer value quotient
  4.     DIM chp%, msp%, tlv&&, ivq&&
  5.     tlv&& = value&&
  6.     BIN$ = STRING$(64, "0"): chp% = 64: msp% = 64
  7.     DO
  8.         ivq&& = INT(tlv&& / 2)
  9.         IF tlv&& / 2 <> ivq&& THEN MID$(BIN$, chp%, 1) = "1": msp% = chp%
  10.         chp% = chp% - 1: tlv&& = ivq&&
  11.     LOOP UNTIL tlv&& = 0
  12.     BIN$ = MID$(BIN$, msp%)
  13.  
  14.  
Title: Re: and another one for your toolbox...
Post by: bplus on December 02, 2019, 07:25:43 pm
Dang now I have to revise mine! OK very nice lesson about concatenation!

Here is my new version (and NOT final version) against RhoSigma's, had to add another 0 to the test and dump the time consuming array:
Code: QB64: [Select]
  1. _TITLE "BIN$ speed test..." 'Bplus mod of RhoSigma's 2019-12-03
  2. PRINT "BIN$ speed test ... System: "; _OS$: PRINT
  3. PRINT "BP = latest version from B+"
  4. PRINT "RS = latest from RhoSigma"
  5.  
  6. '--- for a fair test we need the same numbers for all
  7. '--- functions, so we first fill an array with numbers
  8.  
  9. PRINT "First test print BIN$ of 0:"
  10. PRINT "Bplus has "; bpBIN$(0)
  11. PRINT "RhoSigma has "; rsBIN$(0)
  12. PRINT "Press key to continue... "
  13. FOR i = 1 TO 7
  14.     r = INT(RND * 10 ^ i)
  15.     PRINT "For random number r = "; r
  16.     PRINT "   Bplus has "; bpBIN$(r)
  17.     PRINT "RhoSigma has "; rsBIN$(r)
  18. PRINT "Press key to continue... "
  19. '--- bplus ---
  20. st# = TIMER(0.001)
  21. PRINT "BP-Start:"; st#
  22. FOR i&& = 0 TO 10000000
  23.     b$ = bpBIN$(i&&)
  24. et# = TIMER(0.001)
  25. IF et# = st# THEN et# = et# + 0.001 'avoid div by zero below
  26. PRINT "BP-End..:"; et#
  27. PRINT "Run-Time:"; et# - st#; "sec."
  28.  
  29. '--- RhoSigma ---
  30. st# = TIMER(0.001)
  31. PRINT "RS-Start:"; st#
  32. FOR i&& = 0 TO 10000000
  33.     b$ = rsBIN$(i&&)
  34. et# = TIMER(0.001)
  35. IF et# = st# THEN et# = et# + 0.001 'avoid div by zero below
  36. PRINT "RS-End..:"; et#
  37. PRINT "Run-Time:"; et# - st#; "sec."
  38. '--- cleanup & wait ---
  39. PRINT "...done, press any key"
  40.  
  41.  
  42. '================================
  43. '=== AND HERE COMES THE MAGIC ===
  44. '================================
  45.  
  46. FUNCTION rsBIN$ (value&&)
  47.     'chp% = char position, msp% = most significant position
  48.     'tlv&& = temporary loop value, ivq&& = integer value quotient
  49.     DIM chp%, msp%, tlv&&, ivq&&
  50.     tlv&& = value&&
  51.     rsBIN$ = STRING$(64, "0"): chp% = 64: msp% = 64
  52.     DO
  53.         ivq&& = INT(tlv&& / 2)
  54.         IF tlv&& / 2 <> ivq&& THEN MID$(rsBIN$, chp%, 1) = "1": msp% = chp%
  55.         chp% = chp% - 1: tlv&& = ivq&&
  56.     LOOP UNTIL tlv&& = 0
  57.     rsBIN$ = MID$(rsBIN$, msp%)
  58.  
  59. FUNCTION bpBIN$ (integerBase10 AS _INTEGER64) 'no more &B because easier to add than to remove
  60.     DIM j AS _INTEGER64, place AS INTEGER
  61.     j = 1: place = 64: bpBIN$ = STRING$(64, "0")
  62.     WHILE j <= integerBase10
  63.         IF (integerBase10 AND j) > 0 THEN MID$(bpBIN$, place, 1) = "1" ELSE MID$(bpBIN$, place, 1) = "0"
  64.         j = j * 2: place = place - 1
  65.     WEND
  66.     bpBIN$ = MID$(bpBIN$, place + 1)
  67.     IF bpBIN$ = "" THEN bpBIN$ = "0"
  68.  
  69.  
 [ This attachment cannot be displayed inline in 'Print Page' view ]  
Title: Re: and another one for your toolbox...
Post by: RhoSigma on December 03, 2019, 04:56:24 am
The really final one ??? :)

used bitshift instead of division, and unrolled the loop for the smallest integer type (_BYTE). Have also tweaked yours a bit B+, j=j+j (instead j*2) and removed the "0" (ELSE) branch from your condition. Your string consists of zeros, no need to write them for each tested bit.

Make sure your QB64 version is recent enough to have the _SHR instruction...

Code: QB64: [Select]
  1. _TITLE "BIN$ speed test..." 'Bplus mod of RhoSigma's 2019-12-03
  2. PRINT "BIN$ speed test ... System: "; _OS$: PRINT
  3. PRINT "BP = latest version from B+"
  4. PRINT "RS = latest from RhoSigma"
  5.  
  6. '--- for a fair test we need the same numbers for all
  7. '--- functions, so we first fill an array with numbers
  8.  
  9. PRINT "First test print BIN$ of 0:"
  10. PRINT "Bplus has "; bpBIN$(0)
  11. PRINT "RhoSigma has "; rsBIN$(0)
  12. PRINT "Press key to continue... "
  13. FOR i = 1 TO 7
  14.     r = INT(RND * 10 ^ i)
  15.     PRINT "For random number r = "; r
  16.     PRINT "   Bplus has "; bpBIN$(r)
  17.     PRINT "RhoSigma has "; rsBIN$(r)
  18. PRINT "Press key to continue... "
  19. '--- bplus ---
  20. st# = TIMER(0.001)
  21. PRINT "BP-Start:"; st#
  22. FOR i&& = 0 TO 10000000
  23.     b$ = bpBIN$(i&&)
  24. et# = TIMER(0.001)
  25. IF et# = st# THEN et# = et# + 0.001 'avoid div by zero below
  26. PRINT "BP-End..:"; et#
  27. PRINT "Run-Time:"; et# - st#; "sec."
  28.  
  29. '--- RhoSigma ---
  30. st# = TIMER(0.001)
  31. PRINT "RS-Start:"; st#
  32. FOR i&& = 0 TO 10000000
  33.     b$ = rsBIN$(i&&)
  34. et# = TIMER(0.001)
  35. IF et# = st# THEN et# = et# + 0.001 'avoid div by zero below
  36. PRINT "RS-End..:"; et#
  37. PRINT "Run-Time:"; et# - st#; "sec."
  38. '--- cleanup & wait ---
  39. PRINT "...done, press any key"
  40.  
  41.  
  42. '================================
  43. '=== AND HERE COMES THE MAGIC ===
  44. '================================
  45.  
  46. FUNCTION rsBIN$ (value&&)
  47.     'tlv&& = temporary loop value
  48.     'chp% = char position, msp% = most significant position
  49.     DIM tlv&&, chp%, msp%
  50.     tlv&& = value&&
  51.     rsBIN$ = STRING$(64, "0"): chp% = 64: msp% = 64
  52.     DO
  53.         IF (tlv&& AND 1) THEN MID$(rsBIN$, chp%, 1) = "1": msp% = chp%
  54.         IF (tlv&& AND 2) THEN MID$(rsBIN$, chp% - 1, 1) = "1": msp% = chp% - 1
  55.         IF (tlv&& AND 4) THEN MID$(rsBIN$, chp% - 2, 1) = "1": msp% = chp% - 2
  56.         IF (tlv&& AND 8) THEN MID$(rsBIN$, chp% - 3, 1) = "1": msp% = chp% - 3
  57.         IF (tlv&& AND 16) THEN MID$(rsBIN$, chp% - 4, 1) = "1": msp% = chp% - 4
  58.         IF (tlv&& AND 32) THEN MID$(rsBIN$, chp% - 5, 1) = "1": msp% = chp% - 5
  59.         IF (tlv&& AND 64) THEN MID$(rsBIN$, chp% - 6, 1) = "1": msp% = chp% - 6
  60.         IF (tlv&& AND 128) THEN MID$(rsBIN$, chp% - 7, 1) = "1": msp% = chp% - 7
  61.         chp% = chp% - 8
  62.         tlv&& = _SHR(tlv&&, 8)
  63.     LOOP UNTIL tlv&& = 0
  64.     rsBIN$ = MID$(rsBIN$, msp%)
  65.  
  66. FUNCTION bpBIN$ (integerBase10 AS _INTEGER64) 'no more &B because easier to add than to remove
  67.     DIM j AS _INTEGER64, place AS INTEGER
  68.     j = 1: place = 64: bpBIN$ = STRING$(64, "0")
  69.     WHILE j <= integerBase10
  70.         IF (integerBase10 AND j) > 0 THEN MID$(bpBIN$, place, 1) = "1"
  71.         j = j + j: place = place - 1
  72.     WEND
  73.     bpBIN$ = MID$(bpBIN$, place + 1)
  74.     IF bpBIN$ = "" THEN bpBIN$ = "0"
  75.  
  76.  
Title: Re: and another one for your toolbox...
Post by: RhoSigma on December 03, 2019, 08:20:56 am
And now my really really really FINAL one, yes REALLY :)

improved using _MEM and CHECKING:OFF

Once again, make sure your QB64 version is recent enough to have the _SHR instruction...

Code: QB64: [Select]
  1. _TITLE "BIN$ speed test..." 'Bplus mod of RhoSigma's 2019-12-03
  2. PRINT "BIN$ speed test ... System: "; _OS$: PRINT
  3. PRINT "BP = latest version from B+"
  4. PRINT "RS = latest from RhoSigma"
  5.  
  6. '--- for a fair test we need the same numbers for all
  7. '--- functions, so we first fill an array with numbers
  8.  
  9. PRINT "First test print BIN$ of 0:"
  10. PRINT "Bplus has "; bpBIN$(0)
  11. PRINT "RhoSigma has "; rsBIN$(0)
  12. PRINT "Press key to continue... "
  13. FOR i = 1 TO 7
  14.     r = INT(RND * 10 ^ i)
  15.     PRINT "For random number r = "; r
  16.     PRINT "   Bplus has "; bpBIN$(r)
  17.     PRINT "RhoSigma has "; rsBIN$(r)
  18. PRINT "Press key to continue... "
  19. '--- bplus ---
  20. st# = TIMER(0.001)
  21. PRINT "BP-Start:"; st#
  22. FOR i&& = 0 TO 10000000
  23.     b$ = bpBIN$(i&&)
  24. et# = TIMER(0.001)
  25. IF et# = st# THEN et# = et# + 0.001 'avoid div by zero below
  26. PRINT "BP-End..:"; et#
  27. PRINT "Run-Time:"; et# - st#; "sec."
  28.  
  29. '--- RhoSigma ---
  30. st# = TIMER(0.001)
  31. PRINT "RS-Start:"; st#
  32. FOR i&& = 0 TO 10000000
  33.     b$ = rsBIN$(i&&)
  34. et# = TIMER(0.001)
  35. IF et# = st# THEN et# = et# + 0.001 'avoid div by zero below
  36. PRINT "RS-End..:"; et#
  37. PRINT "Run-Time:"; et# - st#; "sec."
  38. '--- cleanup & wait ---
  39. PRINT "...done, press any key"
  40.  
  41.  
  42. '================================
  43. '=== AND HERE COMES THE MAGIC ===
  44. '================================
  45.  
  46. FUNCTION rsBIN$ (value&&)
  47.     'tlv&& = temporary loop value, b$64 = fixed length temporary string
  48.     'chp% = char position mem offset, msp% = most significant string position
  49.     'one% = ascii code of "1", mp = memory pointer to b$64
  50.     DIM tlv&&, b$64, chp%, msp%, mp AS _MEM
  51.     tlv&& = value&&
  52.     b$64 = STRING$(64, "0"): chp% = 63: msp% = 64: one% = 49
  53.     mp = _MEM(b$64)
  54.     DO
  55.         IF (tlv&& AND 1) THEN _MEMPUT mp, mp.OFFSET + chp%, one% AS _BYTE: msp% = chp% + 1
  56.         IF (tlv&& AND 2) THEN _MEMPUT mp, mp.OFFSET + chp% - 1, one% AS _BYTE: msp% = chp%
  57.         IF (tlv&& AND 4) THEN _MEMPUT mp, mp.OFFSET + chp% - 2, one% AS _BYTE: msp% = chp% - 1
  58.         IF (tlv&& AND 8) THEN _MEMPUT mp, mp.OFFSET + chp% - 3, one% AS _BYTE: msp% = chp% - 2
  59.         IF (tlv&& AND 16) THEN _MEMPUT mp, mp.OFFSET + chp% - 4, one% AS _BYTE: msp% = chp% - 3
  60.         IF (tlv&& AND 32) THEN _MEMPUT mp, mp.OFFSET + chp% - 5, one% AS _BYTE: msp% = chp% - 4
  61.         IF (tlv&& AND 64) THEN _MEMPUT mp, mp.OFFSET + chp% - 6, one% AS _BYTE: msp% = chp% - 5
  62.         IF (tlv&& AND 128) THEN _MEMPUT mp, mp.OFFSET + chp% - 7, one% AS _BYTE: msp% = chp% - 6
  63.         chp% = chp% - 8
  64.         tlv&& = _SHR(tlv&&, 8)
  65.     LOOP UNTIL tlv&& = 0
  66.     _MEMFREE mp
  67.     rsBIN$ = MID$(b$64, msp%)
  68.  
  69. FUNCTION bpBIN$ (integerBase10 AS _INTEGER64) 'no more &B because easier to add than to remove
  70.     DIM j AS _INTEGER64, place AS INTEGER
  71.     j = 1: place = 64: bpBIN$ = STRING$(64, "0")
  72.     WHILE j <= integerBase10
  73.         IF (integerBase10 AND j) > 0 THEN MID$(bpBIN$, place, 1) = "1"
  74.         j = j + j: place = place - 1
  75.     WEND
  76.     bpBIN$ = MID$(bpBIN$, place + 1)
  77.     IF bpBIN$ = "" THEN bpBIN$ = "0"
  78.  
  79.  
Title: Re: and another one for your toolbox...
Post by: SMcNeill on December 03, 2019, 09:20:39 am
I still like this version, for myself:

Code: QB64: [Select]
  1. n$ = bin$(12, 1)
  2. PRINT n$, VAL(n$)
  3.  
  4. n$ = bin$(-2, 1) 'byte
  5. PRINT n$, VAL(n$)
  6.  
  7. n$ = bin$(-2, 2) 'integer
  8. PRINT n$, VAL(n$)
  9.  
  10. FUNCTION bin$ (n AS _INTEGER64, Rbytes AS _BYTE)
  11.     STATIC m AS _MEM
  12.     DIM b AS _BYTE, i AS INTEGER, l AS LONG, i64 AS _INTEGER64
  13.     IF n < 1 THEN 'the user needs to tell us how many return bytes they expect
  14.         SELECT CASE Rbytes 'assign the value to the proper mem type
  15.             CASE 1: m = _MEM(b): b = n '1 for a byte
  16.             CASE 2: m = _MEM(i): i = n '2 for an integer
  17.             CASE 4:: m = _MEM(l): l = n '4 for a long
  18.             CASE 8: m = _MEM(i64): i64 = n '8 for an integer64
  19.             CASE ELSE: bin$ = "WRONG RETURN TYPE!": EXIT FUNCTION
  20.         END SELECT
  21.     ELSE
  22.         m = _MEM(n): l = 8 'just use the number as we passed it
  23.     END IF
  24.  
  25.     FOR i1 = 0 TO Rbytes - 1
  26.         FOR j = 0 TO 7 '8 bytes per length and 8 bits in each byte
  27.             IF _MEMGET(m, m.OFFSET + i1, _BYTE) AND 2 ^ j THEN b$ = "1" + b$ ELSE b$ = "0" + b$
  28.     NEXT j, i1
  29.     bin$ = "&B" + MID$(b$, INSTR(b$, "1"))
  30.  

Unlike all the rest, this is isn't built with speed being it's deciding factor -- flexibility is.  It returns BIN$ for both positive and negative values, and for various variable types.  I don't usually find myself needing to be concerned with "how fast can I turn a number into a binary string", so this is designed more for "how useful can it be, when turning a number into a binary string". 

n$ = bin$(-2, 1) <--- This would return the binary value of -2, if I was dealing with bytes.
n$ = bin$(-2, 4) <--- This would return the binary value of -2, if I was dealing with longs.

Not only does it work for positive numbers, but it also works with negative ones, and returns them back to us for whatever variable type which we might want it to.
Title: Re: and another one for your toolbox...
Post by: bplus on December 03, 2019, 10:57:33 am
Yeah I suspected this was going to get into bit shifting and MEM but did not expect for Type variables, ha ;-))

And agree it's getting kind of crazy to be completely focused on speed. (Specially since I don't think I am going to get a faster one than RhoSigma. LOL)

@Steve, you really have an actual use for binary strings for different variable types or are you going to make one up now? ;) Even for MEM stuff I would think you need number values not strings?
Title: Re: and another one for your toolbox...
Post by: SMcNeill on December 03, 2019, 11:07:36 am
Yeah I suspected this was going to get into bit shifting and MEM but did not expect for Type variables, ha ;-))

And agree it's getting kind of crazy to be completely focused on speed.

@Steve, you really have an actual use for binary strings for different variable types or are you going to make one up now? Even for MEM stuff I would think you need number values not strings?

Mainly for teaching purposes, to help explain to people why COLOR -1 in 32-bit is the same as color &HFFFFFFFF.   (A shortcut I use all the time!)

As you say, normally you use AND bit_value, rather than AND VAL(MID$(BIN$(number), position,1)).  ;)
Title: Re: and another one for your toolbox...
Post by: bplus on December 03, 2019, 12:21:04 pm
I put this in edit but decided it's best in full reply to RhoSigma's efforts:
Update: oh heck I missed the ELSE replace with "0" thing, nice catch.

RhoSigma, really, really nice work! you make my version better and then cut that time in half or so... :)
Title: Re: and another one for your toolbox...
Post by: RhoSigma on December 04, 2019, 03:50:20 am
Thanks B+,

I saw it already in your edit. And yes, in general I do also prefer readability end elegance over speed (GuiTools is the best prove), but nevertheless it was a nice brain training to see what's possible and what you can do to take out as many as possible of the time consuming "under the hood" operations, which regulary nobody even think of.
Title: Re: and another one for your toolbox...
Post by: bplus on December 05, 2019, 11:06:49 pm
Added a much needed line to InputG function when _DISPLAY is activated.
https://www.qb64.org/forum/index.php?topic=1511.msg110651#msg110651
Title: Re: and another one for your toolbox...
Post by: bplus on December 16, 2019, 10:39:30 pm
A number of tools have been updated or added, here is best versions of some routines discussed earlier in this thread.

8 routines added or improved to my toolbox today 2019-12-16, what a day!
Code: QB64: [Select]
  1. ' 2019-12-16 updates to my Toolbox Listing
  2.  
  3. '2019-12-16 fix by Steve saves some time with STATIC and saves and restores last dest
  4. SUB ftri (x1, y1, x2, y2, x3, y3, K AS _UNSIGNED LONG)
  5.     DIM D AS LONG
  6.     STATIC a&
  7.     D = _DEST
  8.     IF a& = 0 THEN a& = _NEWIMAGE(1, 1, 32)
  9.     _DEST a&
  10.     _DONTBLEND a& '  '<<<< new 2019-12-16 fix
  11.     PSET (0, 0), K
  12.     _BLEND a& '<<<< new 2019-12-16 fix
  13.     _DEST D
  14.     _MAPTRIANGLE _SEAMLESS(0, 0)-(0, 0)-(0, 0), a& TO(x1, y1)-(x2, y2)-(x3, y3)
  15.  
  16. 'update 2019-12-16 needs updated fTri 2019-12-16  I like this ordering of points better
  17. 'need 4 non linear points (not all on 1 line) list them clockwise so x2, y2 is opposite of x4, y4
  18. SUB fquad (x1, y1, x2, y2, x3, y3, x4, y4, K AS _UNSIGNED LONG)
  19.     ftri x1, y1, x2, y2, x3, y3, K
  20.     ftri x3, y3, x4, y4, x1, y1, K
  21.  
  22. ' 2019-12-16 this should have been in here earlier
  23. SUB EllipseFill (CX AS INTEGER, CY AS INTEGER, a AS INTEGER, b AS INTEGER, C AS _UNSIGNED LONG)
  24.     ' CX = center x coordinate
  25.     ' CY = center y coordinate
  26.     '  a = semimajor axis
  27.     '  b = semiminor axis
  28.     '  C = fill color
  29.     IF a = 0 OR b = 0 THEN EXIT SUB
  30.     DIM h2 AS _INTEGER64
  31.     DIM w2 AS _INTEGER64
  32.     DIM h2w2 AS _INTEGER64
  33.     DIM x AS INTEGER
  34.     DIM y AS INTEGER
  35.     w2 = a * a
  36.     h2 = b * b
  37.     h2w2 = h2 * w2
  38.     LINE (CX - a, CY)-(CX + a, CY), C, BF
  39.     DO WHILE y < b
  40.         y = y + 1
  41.         x = SQR((h2w2 - y * y * w2) \ h2)
  42.         LINE (CX - x, CY + y)-(CX + x, CY + y), C, BF
  43.         LINE (CX - x, CY - y)-(CX + x, CY - y), C, BF
  44.     LOOP
  45.  
  46. 'thanks STxAxTIC from Toolbox
  47. SUB EllipseTilt (CX, CY, a, b, ang, C AS _UNSIGNED LONG)
  48.     DIM k, i, j
  49.     '  CX = center x coordinate
  50.     '  CY = center y coordinate
  51.     '   a = semimajor axis  major radius
  52.     '   b = semiminor axis  minor radius
  53.     ' ang = clockwise orientation of semimajor axis in radians (0 default)
  54.     '   C = fill color
  55.     FOR k = 0 TO 6.283185307179586 + .025 STEP .025 'not sure about the stepper it should depend on a and b
  56.         i = a * COS(k) * COS(ang) + b * SIN(k) * SIN(ang)
  57.         j = -a * COS(k) * SIN(ang) + b * SIN(k) * COS(ang)
  58.         i = i + CX
  59.         j = -j + CY
  60.         IF k <> 0 THEN
  61.             LINE -(i, j), C
  62.         ELSE
  63.             PSET (i, j), C
  64.         END IF
  65.     NEXT
  66.  
  67. 'relace toolbox code  2019-12-16
  68. 'this needs RotoZoom3 to rotate image and EllipseFill to make the image BUT it can now scale it also!
  69. SUB fTiltEllipse (destH AS LONG, ox AS INTEGER, oy AS INTEGER, majorRadius AS INTEGER, minorRadius AS INTEGER, radianAngle AS SINGLE, c AS _UNSIGNED LONG)
  70.     'setup isolated area, draw fFlatEllipse and then RotoZoom the image into destination
  71.     'ox, oy is center of ellipse
  72.     'majorRadius is 1/2 the lonest axis
  73.     'minorRadius is 1/2 the short axis
  74.     'radianAngle is the Radian Angle of Tilt
  75.     'c is of course color
  76.     DIM sd&, temp&
  77.     sd& = _DEST
  78.     temp& = _NEWIMAGE(2 * majorRadius, 2 * minorRadius, 32)
  79.     _DEST temp&
  80.     _DONTBLEND temp& '<< test 12-16
  81.     'fEllipse majorRadius, minorRadius, majorRadius, minorRadius, c
  82.     EllipseFill majorRadius, minorRadius, majorRadius, minorRadius, c
  83.     _BLEND temp& '<< test 12-16
  84.     _DEST destH
  85.     RotoZoom3 ox, oy, temp&, 1, 1, radianAngle
  86.     _FREEIMAGE temp&
  87.     _DEST sd&
  88.  
  89. SUB RotoZoom3 (X AS LONG, Y AS LONG, Image AS LONG, xScale AS SINGLE, yScale AS SINGLE, radianRotation AS SINGLE)
  90.     ' this assumes you have set your drawing location with _dest or default to screen
  91.     ' X, Y is where you want to put the middle of the image
  92.     ' Image is the handle assigned with _LOADIMAGE
  93.     ' xScale, yScale are shrinkage < 1 or magnification > 1 on the given axis
  94.     ' radianRotation is the Angle in Radian units to rotate the image
  95.  
  96.     DIM px(3) AS SINGLE: DIM py(3) AS SINGLE ' simple arrays for x, y to hold the 4 corners of image
  97.     DIM W&, H&, sinr!, cosr!, i&, x2&, y2& '   variables for image manipulation
  98.     W& = _WIDTH(Image&): H& = _HEIGHT(Image&)
  99.     px(0) = -W& / 2: py(0) = -H& / 2 'left top corner
  100.     px(1) = -W& / 2: py(1) = H& / 2 ' left bottom corner
  101.     px(2) = W& / 2: py(2) = H& / 2 '  right bottom
  102.     px(3) = W& / 2: py(3) = -H& / 2 ' right top
  103.     sinr! = SIN(-radianRotation): cosr! = COS(-radianRotation) ' rotation helpers
  104.     FOR i& = 0 TO 3 ' calc new point locations with rotation and zoom
  105.         x2& = xScale * (px(i&) * cosr! + sinr! * py(i&)) + X: y2& = yScale * (py(i&) * cosr! - px(i&) * sinr!) + Y
  106.         px(i&) = x2&: py(i&) = y2&
  107.     NEXT
  108.     _MAPTRIANGLE _SEAMLESS(0, 0)-(0, H& - 1)-(W& - 1, H& - 1), Image TO(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
  109.     _MAPTRIANGLE _SEAMLESS(0, 0)-(W& - 1, 0)-(W& - 1, H& - 1), Image TO(px(0), py(0))-(px(3), py(3))-(px(2), py(2))
  110.  
  111. '=================================================================================================================== String stuff
  112. '---------------------------------------------------------------------
  113. 'RhoSigma keeps saying final version but then...
  114. 'here is reference: https://www.qb64.org/forum/index.php?topic=1933.msg112096#msg112096
  115. 'Function:  Convert any given dec/hex/oct number into a binary string.
  116. '           Can handle positive and negative values and works in that
  117. '           similar to the QB64 built-in HEX$ and OCT$ functions.
  118. '
  119. 'Synopsis:  binary$ = BIN$ (value&&)
  120. '
  121. 'Result:    binary$ --> the binary representation string of the given
  122. '                       number without leading zeros for positive values
  123. '                       and either 8/16/32 or 64 chars for negatives,
  124. '                       depending on the input size
  125. '
  126. 'Inputs:    value&& --> the pos./neg. number to convert, may also be
  127. '                       given as &H or &O prefixed value
  128. '
  129. 'Notes:     You may also pass in floating point values, as long as its
  130. '           represented value fits into the _INTEGER64 (&&) input, hence
  131. '           approx. -9.223372036854776E+18 to 9.223372036854776E+18.
  132. '           Different from HEX$ and OCT$, BIN$ won't throw an overflow
  133. '           error, if this range is exceeded, but the result is probably
  134. '           wrong in such a case.
  135. '---------------------------------------------------------------------
  136. FUNCTION BIN$ (value&&)
  137. '--- option _explicit requirements ---
  138. DIM temp~&&, charPos%, highPos%
  139. '--- init ---
  140. temp~&& = value&&
  141. BIN$ = STRING$(64, "0"): charPos% = 64: highPos% = 64
  142. '--- convert ---
  143.     IF (temp~&& AND 1) THEN MID$(BIN$, charPos%, 1) = "1": highPos% = charPos%
  144.     charPos% = charPos% - 1: temp~&& = temp~&& \ 2
  145. LOOP UNTIL temp~&& = 0
  146. '--- adjust negative size ---
  147. IF value&& < 0 THEN
  148.     IF -value&& < &HFFFFFFFF~& THEN highPos% = 33
  149.     IF -value&& < &H0000FFFF~& THEN highPos% = 49
  150.     IF -value&& < &H000000FF~& THEN highPos% = 57
  151.     IF -value&& < &H00000000~& THEN highPos% = 1
  152. '--- set result ---
  153. BIN$ = MID$(BIN$, highPos%)
  154.  
  155. 'update 2019-12-16 in ...test\graphics\fTri tests.bas  SLEEP with a click
  156. SUB cSleep (secsWait AS DOUBLE) 'wait for keypress or mouseclick, solves midnight problem nicely I think
  157.     DIM wayt AS INTEGER, oldMouse AS INTEGER, k AS LONG, startTime AS DOUBLE
  158.     oldMouse = -1 '2019-12-16 2nd fix today assume an old mouse click is still active
  159.     startTime = TIMER
  160.     wayt = 1
  161.     _KEYCLEAR
  162.     WHILE wayt
  163.         WHILE _MOUSEINPUT: WEND
  164.         IF _MOUSEBUTTON(1) AND oldMouse = 0 THEN wayt = 0
  165.         oldMouse = _MOUSEBUTTON(1) ' <<< this is Steve's cool way to get clear of mouse click
  166.         k = _KEYHIT: IF k > 0 THEN _KEYCLEAR: wayt = 0
  167.         IF TIMER - startTime < 0 THEN 'past midnight
  168.             IF TIMER + 24 * 60 * 60 - startTime > secsWait THEN wayt = 0
  169.         ELSE
  170.             IF TIMER - startTime >= secsWait THEN wayt = 0
  171.         END IF
  172.         _LIMIT 30
  173.     WEND
  174.  
  175.  

EDIT: 2019-12-17 RhoSigma changed final version of BIN$ again

EDIT: 2020-03-03 Discovered big error in revised RotoZoom3 now fixed
Title: Re: and another one for your toolbox...
Post by: FellippeHeitor on December 16, 2019, 10:41:16 pm
Quote
8 routines added or improved to my toolbox today 2019-12-16, what a day!

It is wholesome to see this community so active and going so strong. Moment of appreciation here.
Title: Re: and another one for your toolbox...
Post by: SMcNeill on December 16, 2019, 10:45:55 pm
I’ve been working on setting up my forums to help organize and sort toolbox type code.  Take a look at them, bplus, and if they’d help you find/organize your stuff better, feel free to add to them.  http://www.smcneill.online/mybb/index.php

The more tools in a toolset, the more powerful and versatile that toolset becomes.  (As long as you can find the proper tool you need for the job at hand.)
Title: Re: and another one for your toolbox...
Post by: bplus on December 16, 2019, 10:57:36 pm
Thanks Steve, your expertise is invaluable! Well I guess I have to sign up to see your Christmas thing ;-))
What's happening with your other forum?




Title: Re: and another one for your toolbox...
Post by: SMcNeill on December 16, 2019, 11:10:35 pm
Thanks Steve, your expertise is invaluable! Well I guess I have to sign up to see your Christmas thing ;-))
What's happening with your other forum?

It’s still up and going, but it has nasty upload limits and such which I don’t care for (as well as pop ups and advertisements).  I’m too cheap to pay a monthly fee to remove those adds and increase upload limits, so I figured a personal forum hosted on my own server will eliminate all those annoyances for me.  ;)
Title: Re: and another one for your toolbox...
Post by: SMcNeill on December 17, 2019, 05:05:12 pm
Another tool which all toolboxes should have, in some form or another -- an easy to use routine to display a large list on the screen.

Code: [Select]
DIM Array(10) AS STRING
DATA Apple,Banana,Cherry,Date,Fig,Grape,Huckleberry,Iced Fruit,Jambolan,Kiwi,Lemon
FOR i = 0 TO 10
    READ Array(i)
NEXT



choice = 0
DO
    CLS
    DisplayList 10, 10, 20, 5, choice, Array(), -1
    k = _KEYHIT
    SELECT CASE k
        CASE 18432
            choice = choice - 1
            IF choice < 0 THEN choice = 0
        CASE 20480
            choice = choice + 1
            IF choice > 10 THEN choice = 10
    END SELECT
    _DISPLAY
    _LIMIT 30
LOOP



SUB DisplayList (x AS INTEGER, y AS INTEGER, w AS INTEGER, l AS INTEGER, s AS INTEGER, choices() AS STRING, numbered AS _BYTE)
    'x/y location to place the start of our list
    'w is the width of our list on the screen
    'l is the length of the list items we want to display at a time
    's is the starting element that we want to display on the screen
    'choices() is the array that holds the actual list for us
    'numbered is the toggle for if we want to autonumber our list or not.
    '     0 says we don't want to number the list; just display it.
    '     A value less than 0 says we want to display the number index of the visible list
    '     A value greater than 0 says we want to display the number of visible elements from the list on the screen.


    'Some basic error checking is in need here
    IF s < LBOUND(choices) THEN s = LBOUND(choices)
    IF s + l - 1 > UBOUND(choices) THEN l = UBOUND(choices) - s + 1

    LOCATE x
    start = s: finish = s + l - 1
    FOR i = start TO finish
        counter = counter + 1
        IF numbered > 0 THEN counter$ = LTRIM$(STR$(counter)) + ") "
        IF numbered < 0 THEN counter$ = LTRIM$(STR$(counter + start - 1)) + ") "
        LOCATE , y: PRINT counter$ + LEFT$(choices(i), w - LEN(counter$))
    NEXT

END SUB

This is about as simple as it gets, with the actual code here in the sub being smaller than the comments to explain the sub...  Works in all screen modes, with use of simple LOCATE coordinates for placement.  ;)
Title: Re: and another one for your toolbox...
Post by: Qbee on December 17, 2019, 06:42:12 pm
Hello SMcNeill,

nice tool and works fine!

What I found is that you use x for rows (vertical position, top -> down) and y for columns (horizontal position, left -> right).
Title: Re: and another one for your toolbox...
Post by: bplus on December 17, 2019, 07:16:20 pm
Quote
Another tool which all toolboxes should have, in some form or another -- an easy to use routine to display a large list on the screen.

Yeah mine looks like this which was pretty much copied from Wiki for mouse wheel demo:
Code: QB64: [Select]
  1. DIM a(1 TO 100) AS STRING
  2. FOR i = LBOUND(a) TO UBOUND(a)
  3.     a(i) = "This is line #" + STR$(i)
  4.  
  5. show a() 'from toolbox
  6.  
  7. 'this uses 20 lines on screen to display an array
  8. SUB show (arr() AS STRING)
  9.     DIM lb AS LONG, ub AS LONG, top AS LONG, i AS LONG, row AS LONG, prevrow AS LONG, n AS LONG
  10.     lb = LBOUND(arr): ub = UBOUND(arr)
  11.     IF ub - lb + 1 < 21 THEN top = ub ELSE top = lb + 19
  12.     CLS: PRINT "press any key to quit scroller..."
  13.     LOCATE 2, 1
  14.     FOR i = lb TO top
  15.         PRINT arr(i)
  16.     NEXT
  17.     DO
  18.         IF ub - lb + 1 > 20 THEN
  19.             DO WHILE _MOUSEINPUT
  20.                 IF row >= lb THEN row = row + _MOUSEWHEEL ELSE row = lb 'prevent under scrolling
  21.                 IF row > ub - 19 THEN row = ub - 19 'prevent over scrolling
  22.                 IF prevrow <> row THEN 'look for a change in row value
  23.                     IF row >= lb AND row <= ub - 19 THEN
  24.                         CLS: PRINT "press any key to quit scroller..."
  25.                         LOCATE 2, 1
  26.                         FOR n = row TO row + 19
  27.                             PRINT arr(n)
  28.                         NEXT
  29.                     END IF
  30.                 END IF
  31.                 prevrow = row 'store previous row value
  32.             LOOP
  33.         END IF
  34.     LOOP UNTIL INKEY$ > ""
  35.  
  36.  
Title: Re: and another one for your toolbox...
Post by: bplus on January 15, 2020, 12:11:54 pm
Best Topic now is jumping you to most recent addition that you might find handy in your tool box if you dont have already.

Developed in Deal or No Deal, https://www.qb64.org/forum/index.php?topic=2092.0

A comma money format or comma fixed decimal places for type Double#:
Code: QB64: [Select]
  1. _TITLE "commaD$ and money$ test" 'b+ 2020-01-14
  2.  
  3. FOR i = 1 TO 20
  4.     test# = (-10) ^ (i - 1) + RND
  5.     PRINT i - 1, commaD$(test#, 6);
  6.     LOCATE i, 50: PRINT money$(test#)
  7. PRINT "As you can see the integer part is limited to 15 places,"
  8. PRINT " Trillions for Type Double#."
  9.  
  10. FUNCTION commaD$ (n#, nDecPlaces%) 'only works right for double# type
  11.     DIM place AS INTEGER, s$, front$, back$
  12.     commaD$ = _TRIM$(STR$(n#))
  13.     IF LEFT$(commaD$, 1) = "-" THEN s$ = "-": commaD$ = MID$(commaD$, 2) ELSE s$ = ""
  14.     place = INSTR(commaD$, ".")
  15.     IF place = 0 THEN place = LEN(commaD$) + 1
  16.     WHILE place > 4
  17.         commaD$ = MID$(commaD$, 1, place - 4) + "," + MID$(commaD$, place - 3)
  18.         place = INSTR(commaD$, ",")
  19.     WEND
  20.     'fix to nDecPlaces
  21.     place = INSTR(commaD$, ".")
  22.     IF nDecPlaces% THEN
  23.         IF place THEN
  24.             front$ = MID$(commaD$, 1, place)
  25.             back$ = MID$(commaD$, place + 1)
  26.             IF LEN(back$) > nDecPlaces% THEN commaD$ = front$ + LEFT$(back$, nDecPlaces%)
  27.             IF LEN(back$) < nDecPlaces% THEN commaD$ = front$ + LEFT$(back$ + STRING$(nDecPlaces%, "0"), nDecPlaces%)
  28.         ELSE
  29.             commaD$ = commaD$ + "." + STRING$(nDecPlaces%, "0")
  30.         END IF
  31.     ELSE
  32.         IF place THEN commaD$ = MID$(commaD$, 1, place - 1)
  33.     END IF
  34.     commaD$ = s$ + commaD$
  35.  
  36. 'this might make a nice Money format
  37. FUNCTION money$ (n#) 'only works right for double# type
  38.     money$ = _TRIM$(STR$(n#))
  39.     IF LEFT$(money$, 1) = "-" THEN s$ = "-": money$ = MID$(money$, 2) ELSE s$ = ""
  40.     place = INSTR(money$, ".")
  41.     IF place = 0 THEN place = LEN(money$) + 1
  42.     WHILE place > 4
  43.         money$ = MID$(money$, 1, place - 4) + "," + MID$(money$, place - 3)
  44.         place = INSTR(money$, ",")
  45.     WEND
  46.  
  47.     'fix this for 2 places after decimal
  48.     place = INSTR(money$, ".")
  49.     IF place THEN
  50.         front$ = MID$(money$, 1, place)
  51.         back$ = MID$(money$, place + 1)
  52.         IF LEN(back$) > 2 THEN money$ = front$ + LEFT$(back$, 2)
  53.         IF LEN(back$) < 2 THEN money$ = front$ + LEFT$(back$ + "00", 2)
  54.     ELSE
  55.         money$ = money$ + ".00"
  56.     END IF
  57.     money$ = "$" + s$ + money$
  58.  
  59.  
Title: Re: and another one for your toolbox...
Post by: bplus on January 25, 2020, 02:51:11 pm
I don't know it this will get used much but here is STxAxTIC's Curve Smoother modified into a Toolbox SUB and demo's here: https://www.qb64.org/forum/index.php?topic=2114.msg113585#msg113585

Code: QB64: [Select]
  1. '======================= Feature SUB =======================================================================
  2. ' This code takes a dynamic points array and adds and modifies points to smooth out the data,
  3. ' to be used as Toolbox SUB. b+ 2020-01-24 adapted and modified from:
  4. ' Curve smoother by STxAxTIC https://www.qb64.org/forum/index.php?topic=184.msg963#msg963
  5. SUB Smooth (arr() AS XY, targetPoints AS INTEGER, smoothIterations AS INTEGER)
  6.     'TYPE XY
  7.     '    x AS SINGLE
  8.     '    y AS SINGLE
  9.     'END TYPE
  10.     ' targetPoints is the number of points to be in finished smoothed out array
  11.     ' smoothIterations is number of times to try and round out corners
  12.  
  13.     DIM rad2Max, kmax, k, numPoints, xfac, yfac, rad2, j
  14.     numPoints = UBOUND(arr)
  15.     REDIM _PRESERVE arr(0 TO targetPoints) AS XY
  16.     REDIM temp(0 TO targetPoints) AS XY
  17.     DO
  18.         '
  19.         ' Determine the pair of neighboring points that have the greatest separation of all pairs.
  20.         '
  21.         rad2Max = -1
  22.         kmax = -1
  23.         FOR k = 1 TO numPoints - 1
  24.             xfac = arr(k).x - arr(k + 1).x
  25.             yfac = arr(k).y - arr(k + 1).y
  26.             rad2 = xfac ^ 2 + yfac ^ 2
  27.             IF rad2 > rad2Max THEN
  28.                 kmax = k
  29.                 rad2Max = rad2
  30.             END IF
  31.         NEXT
  32.         '
  33.         ' Starting next to kmax, create a `gap' by shifting all other points by one index.
  34.         '
  35.         FOR j = numPoints TO kmax + 1 STEP -1
  36.             arr(j + 1).x = arr(j).x
  37.             arr(j + 1).y = arr(j).y
  38.         NEXT
  39.  
  40.         '
  41.         ' Fill the gap with a new point whose position is determined by the average of its neighbors.
  42.         '
  43.         arr(kmax + 1).x = .5 * (arr(kmax).x + arr(kmax + 2).x)
  44.         arr(kmax + 1).y = .5 * (arr(kmax).y + arr(kmax + 2).y)
  45.  
  46.         numPoints = numPoints + 1
  47.     LOOP UNTIL (numPoints = targetPoints)
  48.     '
  49.     ' At this stage, the curve still has all of its sharp edges. Use a `relaxation method' to smooth.
  50.     ' The new position of a point is equal to the average position of its neighboring points.
  51.     '
  52.     FOR j = 1 TO smoothIterations
  53.         FOR k = 2 TO numPoints - 1
  54.             temp(k).x = .5 * (arr(k - 1).x + arr(k + 1).x)
  55.             temp(k).y = .5 * (arr(k - 1).y + arr(k + 1).y)
  56.         NEXT
  57.         FOR k = 2 TO numPoints - 1
  58.             arr(k).x = temp(k).x
  59.             arr(k).y = temp(k).y
  60.         NEXT
  61.     NEXT
  62.  

A really nice update to yCP:
Code: QB64: [Select]
  1. 'update 2020-01-24 now with _printwidth so can use any FONT
  2. SUB yCP (y, s$) 'for xmax pixel wide graphics screen Center Print at pixel y row
  3.     _PRINTSTRING ((_WIDTH - _PRINTWIDTH(s$)) / 2, y), s$


Title: Re: and another one for your toolbox...
Post by: bplus on February 15, 2020, 11:42:00 am
Feature for February 2020: cText demo
Code: QB64: [Select]
  1. _TITLE "Blinking and more with text string" 'b+ 2020-02-15
  2.  
  3. '===================================================================================
  4. ' Lets blink between colors white and blue, expanding and shrinking text for 10 secs
  5. '===================================================================================
  6.  
  7. s$ = "Blink colors white and blue, expanding and shrinking centered text for 10 secs"
  8. SCREEN _NEWIMAGE(800, 600, 32)
  9. th = 16 'Text Height - start normal
  10. dh = 1  'change height
  11. flashTimes = 100 'with limit 10 this will take 10 times a second and be done in 100/10 secs
  12. start$ = TIME$
  13. WHILE _KEYDOWN(27) = 0
  14.     CLS
  15.     PRINT start$; ", ";
  16.     IF flashTimes THEN
  17.         IF toggle = 1 THEN C~& = &HFFFFFFFF ELSE C~& = &HFF0000FF
  18.         cText _WIDTH / 2, _HEIGHT / 2, th, C~&, s$
  19.         toggle = 1 - toggle
  20.         th = th + dh
  21.         IF th > 64 THEN th = 64: dh = -dh
  22.         IF th < 6 THEN th = 6: dh = -dh
  23.         flashTimes = flashTimes - 1
  24.         lastFlash$ = TIME$
  25.     ELSE
  26.         cText _WIDTH / 2, _HEIGHT / 2, 16, &HFFFFFF00, s$
  27.     END IF
  28.     PRINT lastFlash$; " <<<< notice these numbers are not flashing even though we CLS every frame"
  29.     _DISPLAY '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> eliminates blinking screens when use CLS
  30.     _LIMIT 10 '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  allows maximum number of loops of 10 per sec
  31.  
  32. 'center the text at x, y with given height and color
  33. SUB cText (x, y, textHeight, K AS _UNSIGNED LONG, txt$)
  34.     DIM fg AS _UNSIGNED LONG, cur&, I&, mult, xlen
  35.     fg = _DEFAULTCOLOR
  36.     'screen snapshot
  37.     cur& = _DEST
  38.     I& = _NEWIMAGE(8 * LEN(txt$), 16, 32)
  39.     _DEST I&
  40.     COLOR K, _RGBA32(0, 0, 0, 0)
  41.     _PRINTSTRING (0, 0), txt$
  42.     mult = textHeight / 16
  43.     xlen = LEN(txt$) * 8 * mult
  44.     _PUTIMAGE (x - .5 * xlen, y - .5 * textHeight)-STEP(xlen, textHeight), I&, cur&
  45.     COLOR fg
  46.     _FREEIMAGE I&
  47.  
Title: Re: and another one for your toolbox...
Post by: bplus on March 03, 2020, 02:46:43 pm
I am posting this as a candidate for the forum Toolbox after seeing the issue of image rotation come up once again last night. Starting about here: https://www.qb64.org/forum/index.php?topic=2219.msg115122#msg115122

I found my own latest version of Rotozoom posted in this thread needed a little fix (8 updates in one day https://www.qb64.org/forum/index.php?topic=1511.msg112211#msg112211) and I want this demo posted to be sure I don't have such an obvious mistake go into Toolbox. So last chance to break this guys.

I have researched forum starting with STxAxTIC and Fellippe's code in above link, found nice stuff from Steve (working with text 3 subs and at least 100 lines...) and William33's (has more paramenters than needed IMHO but short and sweet too), found [banned user] and Old Moses citing my Rotozoom2, so I think I meet STxAxTIC's stringent criteria.

Code: QB64: [Select]
  1. _TITLE "Another RotoZoom Demo" 'b+ started 2020-03-02
  2.  
  3. CONST xmax = 1200, ymax = 700, xc = 600, yc = 350
  4. SCREEN _NEWIMAGE(xmax, ymax, 32)
  5. _SCREENMOVE 100, 40
  6. DIM SHARED s&, ao
  7. DIM a, x, y, x1, y1, xs, dxs, ddxs, ys, dys, ddys, maxScale
  8.  
  9. ' Starting from an image I pulled from Internet, I used Paint 3D to give it a transparent background.
  10. s& = _LOADIMAGE("tspike.png") 't for transparent background
  11.  
  12.  
  13. ' Standard Rotation about the image center on a given X, Y location. Rotating image in middle of screen,
  14. ' I used something like this to find ideal angle for level point on left head on right.
  15. WHILE _KEYDOWN(27) = 0
  16.     a = a + _PI(1 / 36)
  17.     IF a > _PI(1.999) THEN a = 0
  18.     CLS
  19.     RotoZoom3 xc, yc, s&, 1, 1, a
  20.     PRINT "Raw image rotation:": PRINT
  21.     PRINT "radian angle:"; a; "   degrees:"; _R2D(a) \ 1; " press key for next angle... esc to rotate on y axis"
  22.     WHILE LEN(INKEY$) = 0: _LIMIT 60: WEND
  23.  
  24. ao = _PI(.27) ' I have to offset the image angle by this amount so that the spike point is on the left
  25. '               and the head is on the right at 0 degrees or radians.
  26.  
  27.  
  28. 'Demo of the independent x and y scale for axis rotations
  29. maxScale = 4: dxs = .01: ddxs = 1: xs = maxScale:
  30.     CLS
  31.     PRINT "Press any for rotation on x axis..."
  32.     RotoZoom3 xc, yc, s&, xs, maxScale, ao
  33.     IF xs + dxs > maxScale OR xs + dxs < -maxScale THEN ddxs = ddxs * -1
  34.     xs = xs + dxs * ddxs
  35.     _DISPLAY
  36.     _LIMIT 60
  37.  
  38. ys = maxScale: dys = .01: ddys = 1
  39.     CLS
  40.     PRINT "Press any to see layout of image over whole screen and end demo..."
  41.     RotoZoom3 xc, yc, s&, maxScale, ys, ao
  42.     IF ys + dys > maxScale OR ys + dys < -maxScale THEN ddys = ddys * -1
  43.     ys = ys + dys * ddys
  44.     _DISPLAY
  45.     _LIMIT 60
  46.  
  47. ' Demo of an applied layout on screen
  48. COLOR , &HFFBBBBBB: CLS ' the image has slight gray halo so hide with gray background
  49. FOR x = 40 TO _WIDTH - 40 STEP 20
  50.     RotoZoom3 x, 15, s&, .2, .2, _PI(1.5 + .27)
  51.     RotoZoom3 x, _HEIGHT - 15, s&, .2, .2, _PI(.5 + .27)
  52. FOR y = 40 TO _HEIGHT - 40 STEP 20
  53.     RotoZoom3 15, y, s&, .2, .2, _PI(1 + .27)
  54.     RotoZoom3 _WIDTH - 15, y, s&, .2, .2, _PI(.27)
  55. FOR a = 0 TO _PI(2) STEP _PI(1 / 6)
  56.     x1 = xc + 200 * COS(a)
  57.     y1 = yc + 200 * SIN(a)
  58.     RotoZoom3 x1, y1, s&, 2, 2, a + ao
  59.  
  60. 'And finally a little show. What is better than a knife thrower throwing bananas?
  61. WHILE _KEYDOWN(27) = 0
  62.     CLS
  63.     drawKite xc, .9 * ymax, 600, a + ao
  64.     _DISPLAY
  65.     _LIMIT 30
  66.     a = a + _PI(2 / 360)
  67.  
  68. SUB drawKite (x, y, s, a)
  69.     RotoZoom3 x, y, s&, s / _WIDTH(s&), s / _HEIGHT(s&), a + ao
  70.     IF s > 10 THEN
  71.         drawKite x + .5 * s * COS(_PI(2) - a), (y - .25 * s) + .25 * s * SIN(_PI(2) - a), s / 1.5, a
  72.         drawKite x + .5 * s * COS(_PI + a), (y - .25 * s) + .25 * s * SIN(_PI + a), s / 1.5, a
  73.     END IF
  74.  
  75. ' Description:
  76. ' Started from a mod of Galleon's in Wiki that both scales and rotates an image.
  77. ' This version scales the x-axis and y-axis independently allowing rotations of image just by changing X or Y Scales
  78. ' making this tightly coded routine a very powerful and versatile image tool.
  79. SUB RotoZoom3 (X AS LONG, Y AS LONG, Image AS LONG, xScale AS SINGLE, yScale AS SINGLE, radianRotation AS SINGLE)
  80.     ' This assumes you have set your drawing location with _DEST or default to screen.
  81.     ' X, Y - is where you want to put the middle of the image
  82.     ' Image - is the handle assigned with _LOADIMAGE
  83.     ' xScale, yScale - are shrinkage < 1 or magnification > 1 on the given axis, 1 just uses image size.
  84.     ' These are multipliers so .5 will create image .5 size on given axis and 2 for twice image size.
  85.     ' radianRotation is the Angle in Radian units to rotate the image
  86.     ' note: Radian units for rotation because it matches angle units of other Basic Trig functions
  87.     '       and saves a little time converting from degree.
  88.     '       Use the _D2R() function if you prefer to work in degree units for angles.
  89.  
  90.     DIM px(3) AS SINGLE: DIM py(3) AS SINGLE ' simple arrays for x, y to hold the 4 corners of image
  91.     DIM W&, H&, sinr!, cosr!, i&, x2&, y2& '   variables for image manipulation
  92.     W& = _WIDTH(Image&): H& = _HEIGHT(Image&)
  93.     px(0) = -W& / 2: py(0) = -H& / 2 'left top corner
  94.     px(1) = -W& / 2: py(1) = H& / 2 ' left bottom corner
  95.     px(2) = W& / 2: py(2) = H& / 2 '  right bottom
  96.     px(3) = W& / 2: py(3) = -H& / 2 ' right top
  97.     sinr! = SIN(-radianRotation): cosr! = COS(-radianRotation) ' rotation helpers
  98.     FOR i& = 0 TO 3 ' calc new point locations with rotation and zoom
  99.         x2& = xScale * (px(i&) * cosr! + sinr! * py(i&)) + X: y2& = yScale * (py(i&) * cosr! - px(i&) * sinr!) + Y
  100.         px(i&) = x2&: py(i&) = y2&
  101.     NEXT
  102.     _MAPTRIANGLE _SEAMLESS(0, 0)-(0, H& - 1)-(W& - 1, H& - 1), Image TO(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
  103.     _MAPTRIANGLE _SEAMLESS(0, 0)-(W& - 1, 0)-(W& - 1, H& - 1), Image TO(px(0), py(0))-(px(3), py(3))-(px(2), py(2))
  104.  

EDIT: 2020-03-04 added a little show at end of demo after fixing some minor details, the zip bas file has been updated as shown above.

Here is demo in zip for the test image:
Title: Re: and another one for your toolbox...
Post by: STxAxTIC on March 03, 2020, 05:25:56 pm
Cool bplus!

So by all means, feel free to add that to where ever you see fit. I'm so vocal about the filtration process so eventually I dont have to do it. There will be a prevailing style which will, if broken, stand out on it's own as irregular. Since it's still early I'm bull-dogging this but soon that can be over.

Anyway, post away. Make things minimal and explain parameters nicely like we did in the ellipse work.... actually nvm, I see you've covered that
Title: Re: and another one for your toolbox...
Post by: bplus on March 03, 2020, 06:30:05 pm
Thanks for your support STxAxTIC :)

I am still finding little bugs, fortunately it's in the demo. I am going to check this again with fresh eyes a couple more times, try applying in more situations wait if someone catches something. Oh, I am curious how this might work as a _PUTIMAGE substitute.
Title: Re: and another one for your toolbox...
Post by: TempodiBasic on March 04, 2020, 09:14:28 am
Great Bplus!
Is it a nail that goes around?
Title: Re: and another one for your toolbox...
Post by: bplus on March 04, 2020, 10:48:48 am
Great Bplus!
Is it a nail that goes around?

Just what I was thinking this morning, what a great demo that would be, like a knife thrower only with bananas.
Title: Re: and another one for your toolbox...
Post by: TempodiBasic on March 04, 2020, 04:18:03 pm
Hey Friend 
about
Quote
Just what I was thinking this morning, what a great demo that would be, like a knife thrower only with bananas.
I think that in a female prison it would be a great success!  :-) As for the nail in your demo it changes continuosly point of view! ;-)
Title: Re: and another one for your toolbox...
Post by: OldMoses on March 04, 2020, 07:44:27 pm
Rotozoom is definitely one for the toolbox, it's worked flawlessly in everything I've used it in.
Title: Re: and another one for your toolbox...
Post by: STxAxTIC on March 04, 2020, 07:55:25 pm
Thanks for your input Moses - *this* is why code needs to cook out in Programs for a while before being ordained.
Title: Re: and another one for your toolbox...
Post by: bplus on March 04, 2020, 09:32:53 pm
Yes to cooking! But I know OldMoses is referring to RotoZoom2. 3 has a slight change using the Rotation Units of Radians instead of Degrees.

I just cooked up a little show in the demo to show RotoZoom3 in action loop, see Best Answer. I also wanted to show how _WIDTH(imageHandle&) and _HEIGHT(imageHandle&) can be used with the x and y Scale parameters.
Title: Re: and another one for your toolbox...
Post by: bplus on March 07, 2020, 10:58:18 pm
OK I posted RotoZoom3.

Funny story, I was looking all through samples for it after STxAxTIC assimilated the Toolbox into the Child-boards of Samples. Where is it? Oh, you haven't posted it yet, you ninny!

So I guess it's time to post it.
https://www.qb64.org/forum/index.php?topic=2313.msg115354#msg115354
Title: Re: and another one for your toolbox...
Post by: bplus on April 09, 2020, 02:49:35 pm
Thanks to Petr who helped Craz1000 and probably Steve who set Petr up with MEM stuff?

This looks useful and in my opinion didn't need fixing, you just have to keep track of all the new images you are making with _COPYIMAGE. Maybe I am missing something?

Anyway the latest addition to my Toolbox, swapColor Function with demo:
Code: QB64: [Select]
  1. SCREEN _NEWIMAGE(800, 600, 32) ' b+ tested 2020-04-09
  2. t& = _LOADIMAGE("Templar thru 3d.png")
  3.  
  4. _PUTIMAGE , t& 'check our current image , OK
  5. _TITLE "SwapColor test"
  6. sColor~& = POINT(400, 300)
  7. g = 10
  8.     PRINT "Press any to swap color..."
  9.     SLEEP
  10.     newT& = swapColor&(t&, sColor~&, _RGB32(0, g, 0))
  11.     CLS
  12.     _PUTIMAGE , newT&
  13.     _FREEIMAGE newT&
  14.     g = g + 10
  15.     IF g > 255 THEN g = 10
  16.  
  17. ' create new Image Handle for an image copy with a color changed from original image, returns new Handle&
  18. 'https://www.qb64.org/forum/index.php?topic=2451.0
  19. ' from Petr to Craz1000
  20. FUNCTION swapColor& (oldHandle&, oldcolor~&, newcolor~&)
  21.     DIM m AS _MEM, c AS _UNSIGNED LONG
  22.     swapColor& = _COPYIMAGE(oldHandle&, 32)
  23.     m = _MEMIMAGE(swapColor)
  24.     DO UNTIL a& = m.SIZE - 4
  25.         a& = a& + 4
  26.         c~& = _MEMGET(m, m.OFFSET + a&, _UNSIGNED LONG)
  27.         IF c~& = oldcolor~& THEN _MEMPUT m, m.OFFSET + a&, newcolor~&
  28.     LOOP
  29.     _MEMFREE m
  30.  
  31.  

Image for demo:
Title: Re: and another one for your toolbox...
Post by: bplus on June 08, 2020, 12:11:45 am
loadSort improved again! 4th time now.

https://www.qb64.org/forum/index.php?topic=1511.msg110459#msg110459
Title: Re: and another one for your toolbox...
Post by: bplus on August 18, 2020, 03:47:07 pm
String Math$ Check it out here:
https://www.qb64.org/forum/index.php?topic=2921.msg121886#msg121886
Title: Re: and another one for your toolbox...
Post by: bplus on September 03, 2020, 07:51:55 pm
GetLists for Files and Directory's something Steve has worked up and we've debugged, still may have more to go? Works for Windows and Linux (Linux recently confirmed finally).

Code: QB64: [Select]
  1. ' 2019-08-22 orig post at https://www.qb64.org/forum/index.php?topic=1646.msg108682#msg108682
  2.  
  3. ' direntry.h needs to be in QB64 folder
  4.     FUNCTION load_dir& (s AS STRING)
  5.     FUNCTION has_next_entry& ()
  6.     SUB close_dir ()
  7.     SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
  8. REDIM SHARED DIRs(0) AS STRING, FILs(0) AS STRING
  9.  
  10. GetLists ".", DIRs(), FILs()
  11. FOR i = LBOUND(fils) TO UBOUND(fils)
  12.     PRINT i, FILs(i)
  13.     IF i MOD 20 = 19 THEN PRINT "Press any to cont..": SLEEP
  14. PRINT "Press any to cont.. next Folder up": SLEEP
  15. CHDIR ".."
  16. GetLists ".", DIRs(), FILs()
  17. FOR i = LBOUND(fils) TO UBOUND(fils)
  18.     PRINT i, FILs(i)
  19.     IF i MOD 20 = 19 THEN PRINT "Press any to cont..": SLEEP
  20.  
  21.  
  22. SUB GetLists (SearchDirectory AS STRING, DirList() AS STRING, FileList() AS STRING)
  23.  
  24.     CONST IS_DIR = 1
  25.     CONST IS_FILE = 2
  26.     DIM flags AS LONG, file_size AS LONG, DirCount AS INTEGER, FileCount AS INTEGER, length AS LONG
  27.     DIM nam$
  28.     REDIM _PRESERVE DirList(100), FileList(100)
  29.     DirCount = 0: FileCount = 0
  30.  
  31.     IF load_dir(SearchDirectory + CHR$(0)) THEN
  32.         DO
  33.             length = has_next_entry
  34.             IF length > -1 THEN
  35.                 nam$ = SPACE$(length)
  36.                 get_next_entry nam$, flags, file_size
  37.                 IF (flags AND IS_DIR) THEN
  38.                     DirCount = DirCount + 1
  39.                     IF DirCount > UBOUND(DirList) THEN REDIM _PRESERVE DirList(UBOUND(DirList) + 100)
  40.                     DirList(DirCount) = nam$
  41.                 ELSEIF (flags AND IS_FILE) THEN
  42.                     FileCount = FileCount + 1
  43.                     IF FileCount > UBOUND(filelist) THEN REDIM _PRESERVE FileList(UBOUND(filelist) + 100)
  44.                     FileList(FileCount) = nam$
  45.                 END IF
  46.             END IF
  47.         LOOP UNTIL length = -1
  48.         'close_dir 'move to after end if  might correct the multi calls problem
  49.     ELSE
  50.     END IF
  51.     close_dir 'this  might correct the multi calls problem
  52.  
  53.     REDIM _PRESERVE DirList(DirCount)
  54.     REDIM _PRESERVE FileList(FileCount)
  55.  
  56.  
  57.  
Title: Re: and another one for your toolbox...
Post by: FilipeEstima on September 11, 2020, 12:36:16 am
Very interesting, I never knew this _PRESERVE was available. If I only knew it before... Every time I need to DIM an array that may have a different number of files, I have to SHELL _HIDE a DIR command, read the output file and count the number of lines, for only then make a DIM. With REDIM _PRESERVE it's so much more practical.
Title: Re: and another one for your toolbox...
Post by: bplus on September 11, 2020, 12:42:29 am
Very interesting, I never knew this _PRESERVE was available. If I only knew it before... Every time I need to DIM an array that may have a different number of files, I have to SHELL _HIDE a DIR command, read the output file and count the number of lines, for only then make a DIM. With REDIM _PRESERVE it's so much more practical.

yes, _PRESERVE is nice option but remember to start the array with REDIM not DIM or use DYNAMIC keyword somewhere in beginning which I don't do. STATIC is default.
Title: Re: and another one for your toolbox...
Post by: bplus on September 12, 2020, 12:47:39 pm
Ordered Listings of Combinations and Permutations

You may not need it today or tomorrow but someday you might wish you had a listing of combinations you can make with n things:
https://www.qb64.org/forum/index.php?topic=2999.msg122579#msg122579

It is easy to code any word into a letter and then decode letter back as an element of  combination.

In the same vein, here is a copy of way to get listings of permutations:
https://www.qb64.org/forum/index.php?topic=2961.msg122575#msg122575

Thanks to Danilin for getting me to share these goodies with you. :)
Title: Re: and another one for your toolbox...
Post by: bplus on September 12, 2020, 12:52:48 pm
Thanks to Steve McNeil for this gem!

Exponential Notation Remover

I don't know how many hours wasted dealing with exponential notation popping up and ruining my display of numbers:
https://www.qb64.org/forum/index.php?topic=2985.msg122404#msg122404

The final return format I settled on is here N2S$ but you might like Steve's in reply above (just don't use his "fix" there because he had it right from the start):
https://www.qb64.org/forum/index.php?topic=2985.msg122585#msg122585

Another lesson from there: be careful which Type variable you use for exponents! you might get unexpected results using default SINGLE type.
Title: Re: and another one for your toolbox...
Post by: bplus on September 12, 2020, 01:01:53 pm
In response to CoderVSB12 nascent conception of New Game, I spontaneously came up with this:

Turning a Loaded Image Into a Sprite When _CLEARCOLOR Is Not Enough
by removing dark background; can be done just as easy with light background.

https://www.qb64.org/forum/index.php?topic=3007.msg122645#msg122645

and better 3D effect with same image:
https://www.qb64.org/forum/index.php?topic=3009.msg122648#msg122648


Removing light background for dark object in foreground, this case a shark fin:
https://www.qb64.org/forum/index.php?topic=3307.msg125977#msg125977
Title: Re: and another one for your toolbox...
Post by: SMcNeill on December 03, 2020, 10:07:40 am
A little function which probably doesn't need a whole topic, but which might be useful for someone's tool box -- GetKeyName:

Code: QB64: [Select]
  1.     k = _KEYHIT
  2.     IF k > 0 THEN PRINT GetKeyName(k)
  3.     _LIMIT 30
  4.  
  5. FUNCTION GetKeyName$ (code)
  6.     SELECT CASE code
  7.         CASE 8: GetKeyName$ = "BACK SPACE"
  8.         CASE 9: GetKeyName$ = "TAB"
  9.         CASE 13: GetKeyName$ = "ENTER"
  10.         CASE 27: GetKeyName$ = "ESC"
  11.         CASE 32: GetKeyName$ = "SPACE"
  12.         CASE 33 TO 255: GetKeyName$ = CHR$(code)
  13.         CASE 15104: GetKeyName$ = "F1"
  14.         CASE 15360: GetKeyName$ = "F2"
  15.         CASE 15616: GetKeyName$ = "F3"
  16.         CASE 15872: GetKeyName$ = "F4"
  17.         CASE 16128: GetKeyName$ = "F5"
  18.         CASE 16384: GetKeyName$ = "F6"
  19.         CASE 16640: GetKeyName$ = "F7"
  20.         CASE 16896: GetKeyName$ = "F8"
  21.         CASE 17152: GetKeyName$ = "F9"
  22.         CASE 17408: GetKeyName$ = "F10"
  23.         CASE 34048: GetKeyName$ = "F11"
  24.         CASE 34304: GetKeyName$ = "F12"
  25.         CASE 18432: GetKeyName$ = "UP ARROW"
  26.         CASE 19200: GetKeyName$ = "LEFT ARROW"
  27.         CASE 19712: GetKeyName$ = "RIGHT ARROW"
  28.         CASE 20480: GetKeyName$ = "DOWN ARROW"
  29.  
  30.         CASE 18176: GetKeyName$ = "HOME"
  31.         CASE 18688: GetKeyName$ = "PG UP"
  32.         CASE 20224: GetKeyName$ = "END"
  33.         CASE 20736: GetKeyName$ = "PG DOWN"
  34.         CASE 20992: GetKeyName$ = "INS"
  35.         CASE 21248: GetKeyName$ = "DEL"
  36.  
  37.         CASE 100019: GetKeyName$ = "PAUSE"
  38.         CASE 100300: GetKeyName$ = "NUM LOCK"
  39.         CASE 100301: GetKeyName$ = "CAPS LOCK"
  40.         CASE 100302: GetKeyName$ = "SCROLL LOCK"
  41.         CASE 100303: GetKeyName$ = "R SHIFT"
  42.         CASE 100304: GetKeyName$ = "L SHIFT"
  43.         CASE 100305: GetKeyName$ = "R CTRL"
  44.         CASE 100306: GetKeyName$ = "L CTRL"
  45.         CASE 100307: GetKeyName$ = "R ALT"
  46.         CASE 100308: GetKeyName$ = "L ALT"
  47.         CASE 100309: GetKeyName$ = "L APPLE"
  48.         CASE 100310: GetKeyName$ = "R APPLE"
  49.         CASE 100311: GetKeyName$ = "R WIN"
  50.         CASE 100312: GetKeyName$ = "L WIN"
  51.         CASE 100316: GetKeyName$ = "SYSTEM"
  52.         CASE 100319: GetKeyName$ = "MENU"
  53.     END SELECT
  54.  

Send it a code, get a name of what key it represents.  Good for formatting and display purposes, if you ever need to allow the user to choose their own keys for whatever reason. 

For example: 

Press <ANY KEY> to Jump:

(user presses a key)

Jump key is now: 100319

Now that doesn't make any dang sense to an user!  But, with the above, you get, instead:

Press <ANY KEY> to Jump:

(user presses a key)

Jump key is now: MENU


Oooooohhhhh!  The MENU keys makes my little dude jump now!  YAAAYYY!  That makes sense!  :D
Title: Re: and another one for your toolbox...
Post by: bplus on December 03, 2020, 10:19:39 am
@SMcNeill

What is the Menu or Jump Key?

Is it MS Windows Logo pictured key?

Update: Nope, it's not the airplane either dang it! Lost my Internet connection :P Ha! There are keys I never mess with on my keyboard ;-))
Title: Re: and another one for your toolbox...
Post by: SMcNeill on December 03, 2020, 10:27:57 am
Usually it’s in the bottom row.

CTRL - WIN - ALT - SPACEBAR - ALT - WIN - MENU - CTRL

https://www.howtogeek.com/425623/what-is-the-menu-key-for-and-how-to-remap-it/#:~:text=On%20full-size%20keyboards%2C%20the%20menu%20key%20is%20located,laptop%20keyboards—omit%20the%20menu%20key%20to%20save%20space. (https://www.howtogeek.com/425623/what-is-the-menu-key-for-and-how-to-remap-it/#:~:text=On%20full-size%20keyboards%2C%20the%20menu%20key%20is%20located,laptop%20keyboards—omit%20the%20menu%20key%20to%20save%20space.)
Title: Re: and another one for your toolbox...
Post by: bplus on December 03, 2020, 01:53:11 pm
Usually it’s in the bottom row.

CTRL - WIN - ALT - SPACEBAR - ALT - WIN - MENU - CTRL

https://www.howtogeek.com/425623/what-is-the-menu-key-for-and-how-to-remap-it/#:~:text=On%20full-size%20keyboards%2C%20the%20menu%20key%20is%20located,laptop%20keyboards—omit%20the%20menu%20key%20to%20save%20space. (https://www.howtogeek.com/425623/what-is-the-menu-key-for-and-how-to-remap-it/#:~:text=On%20full-size%20keyboards%2C%20the%20menu%20key%20is%20located,laptop%20keyboards—omit%20the%20menu%20key%20to%20save%20space.)

Bottom row for me is:
Ctrl, Fn, Win, Alt, Space, Alt, Ctrl, 4 arrow keys   then  Number keypad block

And nothing is returned when I press Fn Key. Win key of course Interrupts probably a good thing.
Fn key works like a shift key for letters because those keys are doing double duty too.
Title: Re: and another one for your toolbox...
Post by: SMcNeill on December 03, 2020, 02:53:53 pm
You just don’t have a menu key then.  Not all keyboards do.  They basically work similar to a right mouse click for the keyboard. 
Title: Re: and another one for your toolbox...
Post by: TempodiBasic on December 03, 2020, 05:32:17 pm
on my TOSHIBA the bottom row is Ctrl Fn Windows Alt Space AltGr Menu Ctrl  4 cursors  numeric pad
https://www.ebay.it/itm/TOSHIBA-SATELLITE-PRO-C850-15-6-INTEL-CORE-i3-3rd-GEN-4GB-RAM-250GB-HDD-WIN-10/283549905207?hash=item4204e43137:g:OloAAOSwMXxfft3O (https://www.ebay.it/itm/TOSHIBA-SATELLITE-PRO-C850-15-6-INTEL-CORE-i3-3rd-GEN-4GB-RAM-250GB-HDD-WIN-10/283549905207?hash=item4204e43137:g:OloAAOSwMXxfft3O)
Alt Gr is both  to get the 3rd character on the keyboard or together numerica pad to input ASCII code of the character choosen  both as Alt.
How many variation of the hardware!

Title: Re: and another one for your toolbox...
Post by: bplus on January 31, 2021, 11:50:39 am
Here is a set of handy subs and functions for storing an array of Integers, Floats and Strings (length limit 32) into a string. Thanks to Luke for idea.

One particularly useful application is for storing arrays into a string for a UDT since UDTs cant hold arrays.

For your tool box:
Code: QB64: [Select]
  1. ' words have a length limit of 32, needed some sort of cutoff number not too big, not too small
  2. SUB SetWord (array$, index AS LONG, word32$) ' Luke's Method except option explicit requires mod, no variables needed for one type
  3.     IF LEN(array$) < 32 * (index + 1) THEN array$ = array$ + STRING$(32 * (index + 1) - LEN(array$), SPC(32))
  4.     MID$(array$, index * 32 + 1) = LEFT$(word32$ + SPC(32), 32)
  5.  
  6. ' words have a length limit of 32, needed some sort of cutoff number not too big, not too small
  7. FUNCTION GetWord$ (array$, index AS LONG)
  8.     GetWord$ = _TRIM$(MID$(array$, index * 32 + 1, 32))
  9.  
  10. SUB SetLong (array$, index AS LONG, value&) ' Luke's Method except option explicit requires mod, no variables needed for one type
  11.     IF LEN(array$) < 4 * (index + 1) THEN array$ = array$ + STRING$(4 * (index + 1) - LEN(array$), CHR$(0))
  12.     MID$(array$, index * 4 + 1) = _MK$(LONG, value&)
  13.  
  14. FUNCTION GetLong& (array$, index AS LONG)
  15.     GetLong& = _CV(LONG, MID$(array$, index * 4 + 1, 4))
  16.  
  17. SUB SetDbl (array$, index AS LONG, value#) ' Luke's Method except option explicit requires mod, no variables needed for one type
  18.     IF LEN(array$) < 8 * (index + 1) THEN array$ = array$ + STRING$(8 * (index + 1) - LEN(array$), CHR$(0))
  19.     MID$(array$, index * 8 + 1) = _MK$(DOUBLE, value#)
  20.  
  21. FUNCTION GetDbl# (array$, index AS LONG)
  22.     GetDbl# = _CV(DOUBLE, MID$(array$, index * 8 + 1, 8))
  23.  

And some simple little demo tests of each pair:
Code: QB64: [Select]
  1. ' I modified this for LONG type only do one one for floats DOUBLE 2021-01-31  stored in handy toolbox
  2. DEFLNG A-Z
  3.     s AS STRING
  4. DIM v AS t
  5.  
  6. FOR i = 10 TO 0 STEP -1
  7.     SetLong v.s, i, i ^ 2
  8.  
  9. FOR i = 0 TO 10
  10.     PRINT GetLong&(v.s, i)
  11.  
  12. SUB SetLong (array$, index AS LONG, value&) ' Luke's Method except option explicit requires mod, no variables needed for one type
  13.     IF LEN(array$) < 4 * (index + 1) THEN array$ = array$ + STRING$(4 * (index + 1) - LEN(array$), CHR$(0))
  14.     MID$(array$, index * 4 + 1) = _MK$(LONG, value&)
  15.  
  16. FUNCTION GetLong& (array$, index AS LONG)
  17.     GetLong& = _CV(LONG, MID$(array$, index * 4 + 1, 4))
  18.  

Code: QB64: [Select]
  1. _TITLE "SetDbl GetDbl Test" '2021-01-31 for Handy Toolbox
  2. PRINT LEN(dbl) ' = 8
  3. FOR i = 0 TO 20
  4.     SetDbl rndArr$, i, RND + i
  5. FOR i = 20 TO 0 STEP -1
  6.     PRINT GetDbl#(rndArr$, i)
  7.  
  8. SUB SetDbl (array$, index AS LONG, value#) ' Luke's Method except option explicit requires mod, no variables needed for one type
  9.     IF LEN(array$) < 8 * (index + 1) THEN array$ = array$ + STRING$(8 * (index + 1) - LEN(array$), CHR$(0))
  10.     MID$(array$, index * 8 + 1) = _MK$(DOUBLE, value#)
  11.  
  12. FUNCTION GetDbl# (array$, index AS LONG)
  13.     GetDbl# = _CV(DOUBLE, MID$(array$, index * 8 + 1, 8))
  14.  

Code: QB64: [Select]
  1. _TITLE "SetWord GetWord test" '2021-01-31 for Handy Toolbox
  2.  
  3. FOR i = 0 TO 20
  4.     SetWord wordArr$, i, "Here is word number" + STR$(i)
  5. FOR i = 20 TO 0 STEP -1
  6.     PRINT "'"; GetWord$(wordArr$, i); "'"
  7.  
  8. ' words have a length limit of 32, needed some sort of cutoff number not too big, not too small
  9. SUB SetWord (array$, index AS LONG, word32$) ' Luke's Method except option explicit requires mod, no variables needed for one type
  10.     IF LEN(array$) < 32 * (index + 1) THEN array$ = array$ + STRING$(32 * (index + 1) - LEN(array$), SPC(32))
  11.     MID$(array$, index * 32 + 1) = LEFT$(word32$ + SPC(32), 32)
  12.  
  13. ' words have a length limit of 32, needed some sort of cutoff number not too big, not too small
  14. FUNCTION GetWord$ (array$, index AS LONG)
  15.     GetWord$ = _TRIM$(MID$(array$, index * 32 + 1, 32))
  16.  

Title: Re: and another one for your toolbox...
Post by: bplus on February 20, 2021, 01:47:57 am
Along the lines in the above post, here is a set of tools for Variable Length Array-Like Strings.

https://www.qb64.org/forum/index.php?topic=3681.0
Title: Re: and another one for your toolbox...
Post by: bplus on February 21, 2021, 05:11:12 pm
Evaluate$ - might take a little study to use but look at LOC!

https://www.qb64.org/forum/index.php?topic=3660.msg130074#msg130074





Title: Re: and another one for your toolbox...
Post by: FellippeHeitor on February 21, 2021, 05:28:58 pm
Why are we screaming here?
Title: Re: and another one for your toolbox...
Post by: bplus on February 21, 2021, 06:48:01 pm
We?

Dang it,  lost the link! Now I am ready to scream. ;-))
Title: Re: and another one for your toolbox...
Post by: bplus on March 29, 2021, 12:56:43 pm
The Fval$ Evaluator is, in my humble opinion, light years ahead of Eval evaluator in flexibility ie it does strings and everything can be considered a string, my own private String Theory ;-))

Here I use it as core idea to my oh Interpreter:
https://www.qb64.org/forum/index.php?topic=3723.0

I also use string math, and files tools and others you can find in this thread "and another one for your toolbox"
Don't forget to bookmark this thread for reference to many tools and more importantly ideas bplus uses.

The only thing Eval has going for it is it offers more Basic-like number handling and math like formulas.

Of course if you want to get a little philosophical, physicists are leaning more towards information as the basis or foundation or Theory of Everything, just another String Theory in my book.
Title: Re: and another one for your toolbox...
Post by: SMcNeill on April 03, 2022, 03:47:55 am
Here's a very short little tool for your toolbox:

Code: QB64: [Select]
  1. i = 2
  2.     Print i, Digits(i)
  3.     i = i * 2
  4. Loop Until Digits(i) > 7
  5.  
  6. Function Digits&& (value As Double)
  7.     Digits&& = Int(Log(Abs(value)) / Log(10.#)) + 1
  8.  

Function Digits tells you how many digits a number has, not counting any decimal places.  Can be much faster and simpler than something like Len(_Trim$(Str$(INT(number)))) + (Sgn(number) = -1) , which does the same thing (for integer values) by converting the number to a string first.
Title: Re: and another one for your toolbox...
Post by: STxAxTIC on April 03, 2022, 04:11:17 am
Not bad for fat integers, I think the more complete function captures this behavior and does these examples:

  [ This attachment cannot be displayed inline in 'Print Page' view ]  
Title: Re: and another one for your toolbox...
Post by: bplus on April 03, 2022, 10:48:58 pm
Hey while we have this thread pulled down from a year ago, let's get this one posted too!

MessageBox function:

Code: QB64: [Select]
  1. ' Thank you FellippeHeitor!
  2. $If WIN Then
  3.         Function MessageBox (ByVal ignore&, message$, title$, Byval type&)
  4.     End Declare
  5.     DECLARE LIBRARY ""
  6.     FUNCTION MessageBox (BYVAL ignore&, message$, title$, BYVAL type&)
  7.     END DECLARE
  8. ' answer = MessageBox(0, "Hi, bplus. You can do this.", "This is platform-agnostic", 0)
  9. Dim m$, answer, temp
  10. m$ = "Message: press OK to return 1, press Cancel to return 2."
  11. answer = MessageBox(0, m$, "Test MessageBox", 4097) ' 4097 for OK = 1 Cancel = 2 Modal on top messagebox
  12. If answer = 1 Then
  13.     temp = MessageBox(0, "OK was pressed.", "Test MessageBox", 4096)
  14.     temp = MessageBox(0, "Cancel was pressed.", "Test MessageBox", 4096)
  15.  

Thank you Fellippe! Very lightweight in terms of LOC.
Title: Re: and another one for your toolbox...
Post by: NOVARSEG on April 04, 2022, 12:48:37 am
Ok im all rusty now but heh I remember the good ol days - labyrinth tiles, one line word editors, BMP viewers,  all the stuff we know and love
Title: Re: and another one for your toolbox...
Post by: bplus on April 04, 2022, 02:19:29 pm
Ok im all rusty now but heh I remember the good ol days - labyrinth tiles, one line word editors, BMP viewers,  all the stuff we know and love

I'd ask where have you been, if you don't mind but that's for Discord. That's right they converted me.