Brad Nelson Posted November 9, 2023 Share Posted November 9, 2023 I don't know if I've ever used the String$ function in Microsoft Basic II. Maybe because it wasn't all that useful. But it's defined in the manual as: Returns a string composed of a specified number of replications of A$. Example: X$ = STRING$(100,"A") But what the heck. If we were looking for strict utility we wouldn't be working on 40-year-old hardware. But let's do it one better. Let's pass a couple of parameters. The first will tell it how many repeats. The second tells it how many spaces to put between repeats. "0" equals no space between repeats. "1" is one space, etc. "-1" means to repeat on consecutive lines (as with a string printed without the trailing semicolon). Because we can't pass strings as a parameter, you need to set the string that you want to repeat to REPEAT$. If we could pass a string as a parameter, we could also add a special string character or characters to print between the repeats. What fun that would be. REPEAT$="FastBasic" EXEC STRINGS 4,-1 PROC STRINGS REPS SPCS SPC$=" " IF SPCS=-1 FOR Y = 1 TO REPS ? REPEAT$ NEXT Y EXIT ELSE FOR Y = 1 TO SPCS REPEAT$ =+ " " NEXT Y FOR X = 1 TO REPS ? REPEAT$; NEXT X ENDIF ENDPROC Quote Link to comment Share on other sites More sharing options...
Brad Nelson Posted November 9, 2023 Share Posted November 9, 2023 (edited) Okay, maybe this is overkill. But I updated the STRINGS procedure (for FastBasic) to take a third parameter. The third parameter signifies the ATASCII code for a single character. It will be added (after any spaces) between repetitions of the main string (REPEAT$). "-1" will add no coded ATASCII character. Again, the first parameter is the number of repetitions of the main string (that you assign to REPEAT$ somewhere in your program...likely just before you call the procedure). The second parameter is how many spaces between repetitions. (Zero will give you no spaces, -1 means to repeat on consecutive lines as if printing without the trailing semicolon.) This is a way around not being able to pass a string as a parameter to a procedure in FastBasic. Most will certainly remember that you can use ASC("string") to get the ATASCII code for whatever character you put inside the quotes. It will only ever give you the code value for the first character of the string in case your string is several characters long. Edited: Added a more aesthetically pleasing padding of spaces (if any) around the ATASCII character (if any). REPEAT$="FastBasic" EXEC STRINGS 40,2,42 PROC STRINGS REPS SPCS CSA SPC$=" " IF SPCS=-1 REPEAT$=+CHR$(CSA) FOR Y = 1 TO REPS ? REPEAT$ NEXT Y EXIT ELIF CSA<>-1 FOR Z = 1 TO SPCS REPEAT$ =+ " " NEXT Z REPEAT$ =+ CHR$(CSA) FOR W=1 TO SPCS REPEAT$ =+ " " NEXT W FOR V = 1 TO REPS ? REPEAT$; NEXT V EXIT ELSE FOR Y = 1 TO SPCS REPEAT$ =+ " " NEXT Y REPEAT$=+CHR$(CSA) FOR X = 1 TO REPS ? REPEAT$; NEXT X ENDIF ENDPROC Edited November 9, 2023 by Brad Nelson Quote Link to comment Share on other sites More sharing options...
Brad Nelson Posted November 9, 2023 Share Posted November 9, 2023 Here's one more string procedure for FastBasic. I suppose we could create a library of useful commands for the back-in-the-day BASIC programmer. There could have been two procedures: One for converting to lowercase and one for uppercase. But why not combine them? The parameter of 1 will change to UPPERCASE. The parameter of 2 will change to lowercase. 3 will ask ChatGPT to turn it all into gibberish. CASECHG$="?Fast_Basic*" EXEC CASE 2 ?CASECHG$ PROC CASE C 'C=1 CHANGE TO UC : C=2 CHANGE TO LC DIM UP(LEN(CASECHG$)) IF C=1 FOR X = 1 TO LEN(CASECHG$) IF ASC(CASECHG$[X,1]) <97 OR ASC(CASECHG$[X,1])>122 UP(X)=ASC(CASECHG$[X,1]) ELSE UP(X)=ASC(CASECHG$[X,1])-32 ENDIF REFORM$ =+ CHR$(UP(X)) NEXT X ENDIF IF C=2 FOR X = 1 TO LEN(CASECHG$) IF ASC(CASECHG$[X,1]) <65 OR ASC(CASECHG$[X,1])>90 UP(X)=ASC(CASECHG$[X,1]) ELSE UP(X)=ASC(CASECHG$[X,1])+32 ENDIF REFORM$ =+ CHR$(UP(X)) NEXT X ENDIF CASECHG$=REFORM$ ENDPROC Quote Link to comment Share on other sites More sharing options...
Brad Nelson Posted November 9, 2023 Share Posted November 9, 2023 (edited) I was looking through some Python string operations and thought, "Hey, why not Title Case?" Fast Basic is fast, but a few of these procedures certainly tax it a bit. Still, it works fairly well. No doubt the code could be optimized. But step one is "Make it work." Now you can pass 3 as the parameter and you get Title Case (1=UPPER CASE, 2 =lower case). Quotations marks and dashes (-) are treated as spaces for purposes of determining initial caps. If you were to insert anything but a space, quotation mark, or dash in front of the initial letter of a word, it won't properly change the case of that word. You can't have everything, I case. But I'm not sure that you'd need more than that. Also, it does not ignore articles or minor words (a, an, the, in, for, of, and, but, or, etc.). I suppose it could be extended to do so. And as Mark said, these could all be translated into Turbo-BASIC XL easily enough (at least I think "easy" is the word). CASECHG$="""fast-basic is fast""" EXEC CASE 3 ?CASECHG$ PROC CASE C 'C=1 CHANGE TO UC : C=2 CHANGE TO LC : C=3 CHANGE TO Title Case DIM UP(LEN(CASECHG$)) IF C=1 FOR X = 1 TO LEN(CASECHG$) IF ASC(CASECHG$[X,1]) <97 OR ASC(CASECHG$[X,1])>122 UP(X)=ASC(CASECHG$[X,1]) ELSE UP(X)=ASC(CASECHG$[X,1])-32 ENDIF REFORM$ =+ CHR$(UP(X)) NEXT X ENDIF IF C=2 OR C=3 FOR X = 1 TO LEN(CASECHG$) IF ASC(CASECHG$[X,1]) <65 OR ASC(CASECHG$[X,1])>90 UP(X)=ASC(CASECHG$[X,1]) ELSE UP(X)=ASC(CASECHG$[X,1])+32 ENDIF REFORM$ =+ CHR$(UP(X)) NEXT X ENDIF IF C=3 ' IMPLIMENTATION OF "C=2 OR C=3" PREVIOUSLY CHANGED ALL TO LC TO KEEP MATTERS SIMPLE REFORM$ = "" FOR X = 1 TO LEN(CASECHG$) IF X>1 AND (ASC(CASECHG$[X,1]) >96 AND ASC(CASECHG$[X,1]) <123) 'HANDLE ALL BUT 1ST CHAR FOR LC IF ASC(CASECHG$[X-1,1]) = 32 OR ASC(CASECHG$[X-1,1]) = 34 OR ASC(CASECHG$[X-1,1]) = 45 'PREV CHAR SPC, ", OR - UP(X)=ASC(CASECHG$[X,1])-32 ELSE UP(X)=ASC(CASECHG$[X,1]) ' LEAVE ALONE - CHAR IS NOT ALPHABETIC ENDIF ELIF X=1 AND (ASC(CASECHG$[1,1]) >96 AND ASC(CASECHG$[1,1]) <123) 'CONVERT 1ST CHAR IF LC UP(X)=ASC(CASECHG$[X,1])-32 ELSE UP(X)=ASC(CASECHG$[X,1]) ' LEAVE ALONE - 1ST CHAR IS NOT ALPHABETIC ENDIF REFORM$ =+ CHR$(UP(X)) NEXT X ENDIF CASECHG$=REFORM$ ENDPROC Edited November 9, 2023 by Brad Nelson Quote Link to comment Share on other sites More sharing options...
Brad Nelson Posted November 10, 2023 Share Posted November 10, 2023 (edited) Never mind. Edited November 10, 2023 by Brad Nelson Quote Link to comment Share on other sites More sharing options...
Brad Nelson Posted November 11, 2023 Share Posted November 11, 2023 (edited) This is a search-and-replace procedure for FastBasic. Use "1" as the parameter and it will "replace all." Any other value (or no value) will replace just the first instance (if found). If not found, it will leave the string alone. Searches are case-sensitive. I had posted this earlier but found some more bugs. I think it's mostly working now. If you really stress the search-and-replace "all" (for instance, by substituting two "EE's" for one "E" in a long sentence), sometimes (but not always) the very last "E" will be a triple "E." I've found some other repeating-letter anomalies as well. But the single search-and-replace seems (knock on wood) rock sold. Bang around in this and see if you can break it in new and creative ways. INPUT "ENTER SOURCE STRING: ";SRC$ INPUT "ENTER SUBSTRING TO FIND: ";FND$ INPUT "REPLACE WITH: ";RPL$ INPUT "REPLACE ALL(1)? ";AMNT EXEC FIND_REPLACE AMNT ? SRC$ PROC FIND_REPLACE ALL 'EXPECTS 3 STRINGS TO EXIST: '1) SRC$(THE STRING TO MANIPULATE) '2) FND$(THE STRING TO FIND) '3) RPL$(THE STRING TO REPLACE FND$) '*PARAMETER:(1) REPLACES ALL. ANY OTHER 'VALUE FINDS-AND-REPLACES 1ST INSTANCE ONLY. LOC1=0:S=1 C=LEN(SRC$):D=LEN(FND$):E=LEN(RPL$) REPEAT FOR A = S TO C IF SRC$[A,D]=FND$ LOC1=A S=A+E EXIT ENDIF NEXT A IF LOC1=0 THEN EXIT PART1$=SRC$[1,LOC1-1] PART2$=SRC$[LOC1+D] SRC$="" SRC$=+PART1$:SRC$=+RPL$:SRC$=+PART2$ C=LEN(SRC$) IF AMNT <> 1 THEN EXIT UNTIL A>=C-E'-1 ENDPROC Edited November 11, 2023 by Brad Nelson Quote Link to comment Share on other sites More sharing options...
Brad Nelson Posted November 12, 2023 Share Posted November 12, 2023 (edited) Speaking of speed… I found that in the Altirra emulator under System>Configure System...>Firmware> that you can choose the AltirraOS 3.33 for XL/XE/XEGS as opposed to the standard Atari XL/XE OS ver. 2. I used a "draw a circle" program that I found here. I have a version attached for both Turbo-BASIC XL and FastBasic. The only difference is the timing routines I've inserted for both. The first three listings were run on the Altirra emulator in Windows 10. That last one was run in the Atari800WinPlus emulator. AltirraOS 3.33 for XL/XE/XEGS Turbo-BASIC XL: 3.2 seconds (drawing a similar circle in Graphics 8 using the built-in CIRCLE command: 0.5 seconds) FastBasic: 3.12 seconds FastBasic (run as a compiled XEX): 3.12 seconds Atari XL/XE OS ver. 2 Turbo-BASIC XL: 3.2 seconds FastBasic: 6.63 seconds FastBasic (run as a compiled XEX): 6.63 seconds Atari OS Rev 2 (1983) (Atari) (800XL-65XE-130XE)-FASTFP Turbo-BASIC XL: 3.33 seconds FastBasic: 3.35 seconds FastBasic (run as a compiled XEX): 3.35 seconds Atari800WinPlus 4.1 using updated fast ROMS Turbo-BASIC XL: 3.33 FastBasic: 3.35 FastBasic (run as a compiled XEX): 3.35 I guess the story is that if you don't want to mess around with installing new ROMS in Altirra, you can use their built-in AltirraOS 3.33 for XL/XE/XEGS ROMS. circle4.fba circle4.lst Edited November 12, 2023 by Brad Nelson Quote Link to comment Share on other sites More sharing options...
Brad Nelson Posted November 12, 2023 Share Posted November 12, 2023 (edited) Here's the revised search-and-replace procedure for FastBasic. It seems to be working fine now. It was hard to find the bugs because I'd probably asked the routines and loops to do too much. Whether this is useful or not, I don't know. But it's sort of my science fair project. This is the first time (outside of HyperCard) I've written in BASIC without line numbers. I thus relied on using FastBasic's procedures and various loops. And other than the propensity to type "RUN" on the keyboard (I still can't quite buck that habit), this hasn't been a particularly difficult transition. I really do enjoy using Turbo-BASIC XL and will continue to do so. And some skills learned in FastBasic can certainly be transferred to TBXL. I realize that dmsc has set some tight limits on the length of the program. But I would advise him to consider breaking those limits. This is some really good work here and to stop development because of trying to stay within 8k or seems seems unnecessary in this day of emulators and SD cards (or cartridges that can be burned with, I believe, megabytes of ROM). From using it a bit on a truly amateur level (which, I do believe, is something that dmsc particularly wants to facilitate), I have these comments and suggestions. - I really like the method of copying-and-pasting. It's simple and doesn't try to do too much. But it would be nice if there was also a cut-and-paste option. What if (after having marked the line with Control+C as usual) instead of Control+V for copy/paste you hit Option(or Alt)+V for cut-and-paste? That way you wouldn't have to always go back and delete the line(s) you were moving. - It's a small thing, but I would add a little bullet or asterisk at the top (to the left or right of the file name) to mark if there are any unsaved changes to the file. - Include error messages that maybe gave a little more info on what went wrong. - The ability to add strings as parameters to procedures. - Some kind of preferences or batch file where you could, for example, have EFAST load automatically, as well as setting editor colors, margins, key repeat behavior, etc. - When saving, the delete (as opposed to the "backspace") and insert keys don't work for editing the file name. They simply "escape" you out of the save option. It would be useful to be able to use those keys. - auto-indenting of loops? INPUT "ENTER SOURCE STRING: ";SRC$ INPUT "ENTER SUBSTRING TO FIND: ";FND$ INPUT "REPLACE WITH: ";RPL$ INPUT "REPLACE ALL(1)? ";AMNT EXEC FIND_REPLACE AMNT ? SRC$ PROC FIND_REPLACE ALL 'EXPECTS 3 STRINGS TO EXIST: '1) SRC$(THE STRING TO MANIPULATE) '2) FND$(THE STRING TO FIND) '3) RPL$(THE STRING TO REPLACE FND$) '*PARAMETER:(1) REPLACES ALL. ANY OTHER 'VALUE FINDS-AND-REPLACES 1ST INSTANCE ONLY. LOC1=0:S=1:Y=0 C=LEN(SRC$):D=LEN(FND$):E=LEN(RPL$) REPEAT FOR A = S TO C IF SRC$[A,D]=FND$ LOC1=A S=A+E EXIT ENDIF NEXT A IF LOC1=0 THEN EXIT PART1$=SRC$[1,A-1] PART2$=SRC$[A+D] TEMP=LEN(SRC$):SRC$="" IF LEN(PART1$) < TEMP SRC$=+PART1$:SRC$=+RPL$:SRC$=+PART2$ ELSE SRC$=+PART1$ ENDIF C=LEN(SRC$) IF AMNT <> 1 THEN EXIT UNTIL A>=C-E ENDPROC Edited November 12, 2023 by Brad Nelson 1 Quote Link to comment Share on other sites More sharing options...
Brad Nelson Posted November 13, 2023 Share Posted November 13, 2023 One of the most frustrating problems I'm having with FastBasic is the difficulty in working out parsing errors. Here's an example. FOR ANGLE = 0 TO 6283 STEP 31 ' 6283 IS 2 * PI * 100 X1 = X + (RADIUS * COS(ANGLE / 100)) Y1 = Y + (RADIUS * OBLIQUENESS * SIN(ANGLE / 100)) PLOT X1 / 100, Y1 / 100 NEXT ANGLE I'm playing around with a routine to draw a circle. The above syntax (with the variables, of course, assigned values) works fine in Turbo-BASIC XL. In FastBasic, if I just ask it to print: ?(RADIUS * COS(ANGLE / 100)) it will do so. But if I try to assign a variable to that: X=(RADIUS * COS(ANGLE / 100)) it has a parsing error. I can't find the magic parsing solution. Quote Link to comment Share on other sites More sharing options...
dmsc Posted November 13, 2023 Share Posted November 13, 2023 (edited) Hi! 8 hours ago, Brad Nelson said: One of the most frustrating problems I'm having with FastBasic is the difficulty in working out parsing errors. Here's an example. FOR ANGLE = 0 TO 6283 STEP 31 ' 6283 IS 2 * PI * 100 X1 = X + (RADIUS * COS(ANGLE / 100)) Y1 = Y + (RADIUS * OBLIQUENESS * SIN(ANGLE / 100)) PLOT X1 / 100, Y1 / 100 NEXT ANGLE I'm playing around with a routine to draw a circle. The above syntax (with the variables, of course, assigned values) works fine in Turbo-BASIC XL. In FastBasic, if I just ask it to print: ?(RADIUS * COS(ANGLE / 100)) it will do so. But if I try to assign a variable to that: X=(RADIUS * COS(ANGLE / 100)) it has a parsing error. I can't find the magic parsing solution. This is simple: the expression "RADIUS * COS(ANGLE / 100)" is a floating point expression - because "COS" gives a floating point. And you can't assign a floating point to an integer variable. Also, your program has another bug: the "ANGLE/100" will perform an integer division, as both "ANGLE" and "100" are integers, so it will give incorrect results. This is the fixed program, note the use of "*0.01" instead of "/100" to force floating point and make it faster (multiplication is faster than division): FOR ANGLE = 0 TO 6283 STEP 31 ' 6283 IS 2 * PI * 100 X1 = INT(0.01 * (X + RADIUS * COS(ANGLE * 0.01))) Y1 = INT(0.01 * (Y + RADIUS * OBLIQUENESS * SIN(ANGLE * 0.01))) PLOT X1, Y1 NEXT ANGLE You should use integer variables as much as possible, and try to perform as little FP arithmetic inside loops as possible: ' Pre-multiply values before the loop: RADX% = 0.01 * RADIUS : RADY% = 0.01 * RADIUS * OBLIQUENESS XF% = X * 0.01 : YF% = Y * 0.01 ' Now, optimized loop: FOR ANGLE = 0 TO 6283 STEP 31 ' 6283 IS 2 * PI * 100 ANG% = ANGLE * 0.01 PLOT INT(XF% + RADX% * COS(ANG%)), INT(YF% + RADY% * SIN(ANG%)) NEXT ANGLE Have Fun! Edited November 13, 2023 by dmsc 3 Quote Link to comment Share on other sites More sharing options...
markmiller Posted November 13, 2023 Author Share Posted November 13, 2023 @Brad Nelson - It's the same problem you ran into earlier with assigning a floating-point value to an integer variable. Something I immediately noticed with FastBasic is that it reverses the "normal" syntax for "what's an integer" and "what's floating-point." In Microsoft Basic, a variable V is floating-point. If you want an integer variable, you need to use V%. In FastBasic, it's the opposite. A variable V is an integer variable. If you want floating-point, you need to use V%. 1 Quote Link to comment Share on other sites More sharing options...
Brad Nelson Posted November 13, 2023 Share Posted November 13, 2023 (edited) Thanks for the help, dmsc. It's very much appreciated. My goal is to learn more about FastBasic and I have a vague idea of creating a library of procedures...perhaps including a few graphic primitives. I found a much faster algorithm (code immediately below) for a circle here. I adapted it to FastBasic, including some temporary timing provisions. It works well but has no provision for obliqueness (at least I couldn't figure out a way to add one). And this one is fairly fast at about 0.5 seconds (Graphics 8). I will go with that unless perhaps Mark (please please) can add an obliqueness parameter. TIMER GRAPHICS 8 COLOR 1 SETCOLOR 2,0,0 A=160 ' X COORDINATE FOR CENTER B=80 ' Y COORDINATE FOR CENTER R=60 ' RADIUS R2=0 ' ADD OBLIQUE IF POSSIBLE PHI=0 ' DEFAULT WAS 0 Y1=0 X1=R EXEC CIRCLE 160,80,60 ELAPSED%=TIME ?"ELAPSED TIME = ";ELAPSED%/60;" SECONDS" PROC CIRCLE X Y R WHILE X1>=Y1 PHIY=PHI+Y1+Y1+1 PHIXY=PHIY-X1-X1+1 PLOT A+X1,B+Y1 PLOT A-X1,B+Y1 PLOT A+X1,B-Y1 PLOT A-X1,B-Y1 PLOT A+Y1,B+X1 PLOT A-Y1,B+X1 PLOT A+Y1,B-X1 PLOT A-Y1,B-X1 PHI=PHIY Y1=Y1+1 IF ABS(PHIXY)<ABS(PHIY) PHI=PHIXY:X1=X1-1 ENDIF WEND ENDPROC The code for a circle that I did find (that included obliqueness) is the one that you fixed. And I'm not married to this code because, even with your fixes, it's much too slow...even for FastBasic. But just for purposes of learning, the integer/floating-point distinction is still besting me with this one. For instance, in your fixed code below I can't assign a floating point number to the variable, OBLIQUENESS. And if I try to make it floating point (OBLIQUENESS%) I get a parsing error when putting it inside the formula (on the second line of the listing below). That's why I had to hard-wire "0.5" into the formula to make it work (to test if the obliqueness still works...it still does). I realize you are not ChatDMSC. And I apologize in advance for my own obliqueness. But I just wondered if you might have an answer for this. Thanks in advance. PROC DRAWCIRCLE X Y RADIUS OBLIQUENESS RADX% = 0.01 * RADIUS : RADY% = 0.01 * RADIUS * 0.5 ' OBLIQUENESS XF% = X * 0.01 : YF% = Y * 0.01 FOR ANGLE = 0 TO 628 STEP 1 ANG% = ANGLE * 0.01 PLOT INT(XF% + RADX% * COS(ANG%)), INT(YF% + RADY% * SIN(ANG%)) NEXT ANGLE ENDPROC Edited November 13, 2023 by Brad Nelson Quote Link to comment Share on other sites More sharing options...
Brad Nelson Posted November 14, 2023 Share Posted November 14, 2023 (edited) Okay, I figured it out...or at least one way to do it. I simply multiplied the variable, OBLIQUENESS, by 0.01. Then for user input (and this input is just for testing purposes), the range is 1 to 100. (1) gives an almost flat line (but not quite). (100) is unaltered. (0) draws a flat line and (I think) goes into an error state that requires you to use BREAK. Stuff such as that could easily be handled. It's funny because you can put in numbers over 100 (up to about 190) and simply get vertical obliqueness instead of horizontal. Over 200 you start getting some wacky, even beautiful curves. But this routine is quite slow. I really want to use the one posted just above and somehow add the ability to change the obliqueness of the circle (of the ellipse, of course...blah blah blah...if it was somewhat oblique, it wouldn't be a circle anymore). EXEC MAIN PROC DRAWCIRCLE X Y RADIUS OBLIQUENESS RADX% = 0.01 * RADIUS : RADY% = 0.01 * RADIUS * (0.01*OBLIQUENESS) XF% = X * 0.01 : YF% = Y * 0.01 FOR ANGLE = 0 TO 628 STEP 1 ANG% = ANGLE * 0.01 PLOT INT(XF% + RADX% * COS(ANG%)), INT(YF% + RADY% * SIN(ANG%)) NEXT ANGLE ENDPROC PROC MAIN GRAPHICS 0 SETCOLOR 2,0,0:COLOR 1 INPUT "ENTER OBLIQUENESS (1-100): ";OBLIQUENESS GRAPHICS 8+16 EXEC DRAWCIRCLE 16000, 9600, 8000, OBLIQUENESS ENDPROC Edited November 14, 2023 by Brad Nelson Quote Link to comment Share on other sites More sharing options...
+Stephen Posted November 14, 2023 Share Posted November 14, 2023 That is going to be slow because there are 3 FP multiplications and 2 FP additions done inside a loop that is executing 628 times. So that's 3,768 FP ops. 1 Quote Link to comment Share on other sites More sharing options...
markmiller Posted November 14, 2023 Author Share Posted November 14, 2023 (edited) @Brad Nelson - Looking at your code more closely, I can see you're trying to work around the fact that you're limited to integer parameters for the procedure call. In that case, I find it's easier to just skip the explicit parameters. I reworked your program a little. Hopefully, I didn't mangle your logic. I gave it a test run with obliqueness of 1 (what you were calling 100), and it seemed to work out. GRAPHICS 0 SETCOLOR 2,0,0:COLOR 1 INPUT "INPUT X (1-318) (ACCOUNT FOR RADIUS) ";X% INPUT "INPUT Y (1-190) (ACCOUNT FOR RADIUS) ";Y% INPUT "INPUT RADIUS ";RAD% INPUT "INPUT OBLIQUENESS (0.01-1.0) ";OBLIQUENESS% GRAPHICS 8+16 EXEC DRAWCIRCLE END PROC DRAWCIRCLE RADX%=RAD%:RADY%=RAD%*OBLIQUENESS% FOR ANGLE=0 TO 628 ANG%=ANGLE*0.01 PLOT INT(X%+RADX%*COS(ANG%)),INT(Y%+RADY%*SIN(ANG%)) NEXT ANGLE ENDPROC I know basic Trig., but I get lost being asked about something like obliqueness. So, I don't have a better algorithm for you. Edited November 14, 2023 by markmiller Quote Link to comment Share on other sites More sharing options...
Brad Nelson Posted November 14, 2023 Share Posted November 14, 2023 I can see you're trying to work around the fact that you're limited to integer parameters for the procedure call. In that case, I find it's easier to just skip the explicit parameters. Thanks for doing your version, Mark. Yes, not trying to pass obliqueness as a parameter works great. Add that to my requests for improvements to FastBasic: The ability for floating-point parameters. What I eventually hope to do (if only for my own uses) is turn these procedures (including the ones I've posted previously...at least the versions that worked) into a library or collection. They would obviously be stripped of all inputs, timing data, etc. etc., but with appropriate comments for the input values expected. Thus the more one can use parameters, perhaps the easier they will be to use. Note (again) none of these al-gore-rhythms for circles is of my own creation. They come from that Atari Archives page. And as Stephen noted, this particular one is much too slow, although that other one I posted (which comes from the same Atari Archives page) is pretty fast. In fact, a similar sized Graphics 8 circle drawn in Turbo-BASIC XL via its built-in CIRCLE command is only slightly faster than the code below (about 0.5 seconds in TBXL compared to about 0.6 for theFastBasic version below). I just haven't been able to find a way to make the code below handle obliqueness. And I'm certainly no trig guru. In fact, quite the opposite. So, the following code is pretty fast but has no built-in means for obliqueness. I've been messing with it but haven't been able to make anything work as I'd like. Sort of stumbling around in the dark. Maybe someone here has an idea. TIMER GRAPHICS 8:COLOR 1:SETCOLOR 2,0,0 A=160 ' X COORDINATE FOR CENTER B=80 ' Y COORDINATE FOR CENTER R=60 ' RADIUS R2=0 ' ADD OBLIQUE IF POSSIBLE EXEC CIRCLE 160,80,60 ELAPSED%=TIME ?"ELAPSED TIME = ";ELAPSED%/60;" SECONDS" PROC CIRCLE A B R PHI=0:Y1=0:X1=R WHILE X1>=Y1 PHIY=PHI+Y1+Y1+1 PHIXY=PHIY-X1-X1+1 PLOT A+X1,B+Y1 PLOT A-X1,B+Y1 PLOT A+X1,B-Y1 PLOT A-X1,B-Y1 PLOT A+Y1,B+X1 PLOT A-Y1,B+X1 PLOT A+Y1,B-X1 PLOT A-Y1,B-X1 PHI=PHIY Y1=Y1+1 IF ABS(PHIXY)<ABS(PHIY) PHI=PHIXY:X1=X1-1 ENDIF WEND ENDPROC Quote Link to comment Share on other sites More sharing options...
Brad Nelson Posted November 14, 2023 Share Posted November 14, 2023 (edited) This is one of those times "by hook or by crook" has meaning. Not that I didn't have some vague idea that I had to shrink the x- and expand the y-axis where the points were plotted (or vice versa). But I admit completely stumbling into this solution with about zero understanding of trig (which this method doesn't use anyway, so that was actually a help). I did the same trick as previously to get around the inability to pass floating-point variables as parameters. You can therefore choose obliqueness from 1 to 100 in whole number increments. No trapping is in place yet to handle illegal values. The extra code slows the circle down a bit. But for my purposes...say...inventing the first FastBasic LOGO...it's fine. And no doubt clever people out there can speed it up a bit. And I did certainly take to heart dmsc's advice to do as much math as I could outside the loops. Thank you again for that advice. The obliqueness expands the circle in the direction of the y-axis. It should be simple enough to reverse this behavior with another parameter or using values from the existing one (such as 101-200 to stretch the circle in the x-axis). TIMER GRAPHICS 8 COLOR 1 SETCOLOR 2,0,0 ' A= X COORDINATE FOR CENTER ' B= Y COORDINATE FOR CENTER ' R= RADIUS ' OBL= OBLIQUENESS EXEC CIRCLE 160,80,60,30 ELAPSED%=TIME ?"ELAPSED TIME = ";ELAPSED%/60;" SECONDS" PROC CIRCLE A B R OBL PHI=0:Y1=0:X1=R:H=0 R2%=(.01*OBL) W=INT(X1*R2%) WHILE X1>=Y1 PHIY=PHI+Y1+Y1+1 PHIXY=PHIY-X1-X1+1 PLOT A+W,B+Y1 PLOT A-W,B+Y1 PLOT A+W,B-Y1 PLOT A-W,B-Y1 PLOT A+H,B+X1 PLOT A-H,B+X1 PLOT A+H,B-X1 PLOT A-H,B-X1 PHI=PHIY Y1=Y1+1 IF ABS(PHIXY)<ABS(PHIY) PHI=PHIXY:X1=X1-1 ENDIF W=INT(X1*R2%):H=INT(Y1*R2%) WEND ENDPROC Edited November 14, 2023 by Brad Nelson Quote Link to comment Share on other sites More sharing options...
markmiller Posted November 14, 2023 Author Share Posted November 14, 2023 (edited) @Brad Nelson - I got some ideas for how to speed up your circle+obliqueness algorithm. I noticed the symmetry, and thought I could speed it up by having it do some less expensive calculations, but mostly fewer iterations. I divided the circle into quadrants, and I have each draw a part of the circle "simultaneously." I had to do more trig. to make this work, but I got the time down to a little more than half the time of your algorithm. GRAPHICS 0 SETCOLOR 2,0,0:COLOR 1 INPUT "INPUT X (1-318) (ACCOUNT FOR RADIUS) ";X% INPUT "INPUT Y (1-190) (ACCOUNT FOR RADIUS) ";Y% INPUT "INPUT RADIUS ";RAD% INPUT "INPUT OBLIQUENESS (0.01-2.0) ";OBLIQUENESS% GR.8+16 EXEC DRAWCIRCLE END PROC DRAWCIRCLE 'HPI="Half of Pi" (90 degrees) RADX%=RAD%:RADY%=RAD%*OBLIQUENESS%:HPI%=1.571 FOR ANGLE=0 TO 157 ANG%=ANGLE*0.01:BASEX%=RADX%*COS(ANG%):BASEY%=RADY%*SIN(ANG%) ROT90X%=RADX%*COS(ANG%+HPI%):ROT90Y%=RADY%*SIN(ANG%+HPI%) PL.INT(X%+BASEX%),INT(Y%+BASEY%) PL.INT(X%-BASEX%),INT(Y%-BASEY%) PL.INT(X%+ROT90X%),INT(Y%+ROT90Y%) PL.INT(X%-ROT90X%),INT(Y%-ROT90Y%) N. ANGLE ENDPROC I timed yours and mine in FastBasic. Yours took 86 seconds. By dividing it up into quadrants, I got it down to 44.5 seconds. I had the thought that doing the extra trig. might be slowing it down more than necessary. So, I tried cutting it out, and just drawing the circle in a mirror image (two halves), and that was actually a bit slower, 45.1 seconds. Though, this illustrates how expensive the extra trig. is, because cutting the halves in half again, the way I did it, takes almost as long as doing two halves. Also, I can see now how the obliqueness algorithm works. It adds a "warp" factor to the trig., but only on one of the axes. So, rather than scaling the whole circle, it just scales one of the axes, creating something that looks elliptical. Edited November 14, 2023 by markmiller 1 Quote Link to comment Share on other sites More sharing options...
Brad Nelson Posted November 14, 2023 Share Posted November 14, 2023 (edited) Thanks, Mark. That's pretty cool. In the meantime I was able to add not only obliqueness to that other and faster version (the one that doesn't use trig), but also was able (in a somewhat brute force way) to add the option to stretch the ellipse along either the x- or y-axis. That is listed below. And any ideas for speeding it up will be appreciated. You sound as if you have some great and bold ideas (cutting up a circle...ouch..but it if works!) TIMER GRAPHICS 8 COLOR 1 SETCOLOR 2,0,0 ' A= X COORDINATE FOR CENTER ' B= Y COORDINATE FOR CENTER ' R= RADIUS ' OBL= OBLIQUENESS ' (1-100 STRETCH ON Y-AXIS, 101-200 ON X-AXIS) EXEC CIRCLE 160,80,60,150 ELAPSED%=TIME ?"ELAPSED TIME = ";ELAPSED%/60;" SECONDS" PROC CIRCLE A B R OBL IF OBL < 1 OR OBL > 200 THEN EXIT PHI=0:Y1=0:X1=R:H=0 R2%=(.01*OBL) W=INT(X1*R2%) IF OBL>100 TEMP1=A:TEMP2=B:A=TEMP2:B=TEMP1 OBL=OBL-100 R2%=(.01*OBL) W=INT(X1*R2%) WHILE X1>=Y1 PHIY=PHI+Y1+Y1+1 PHIXY=PHIY-X1-X1+1 PLOT B+Y1,A+W PLOT B+Y1,A-W PLOT B-Y1,A+W PLOT B-Y1,A-W PLOT B+X1,A+H PLOT B+X1,A-H PLOT B-X1,A+H PLOT B-X1,A-H PHI=PHIY Y1=Y1+1 IF ABS(PHIXY)<ABS(PHIY) PHI=PHIXY:X1=X1-1 ENDIF W=INT(X1*R2%):H=INT(Y1*R2%) WEND ELSE WHILE X1>=Y1 PHIY=PHI+Y1+Y1+1 PHIXY=PHIY-X1-X1+1 PLOT A+W,B+Y1 PLOT A-W,B+Y1 PLOT A+W,B-Y1 PLOT A-W,B-Y1 PLOT A+H,B+X1 PLOT A-H,B+X1 PLOT A+H,B-X1 PLOT A-H,B-X1 PHI=PHIY Y1=Y1+1 IF ABS(PHIXY)<ABS(PHIY) PHI=PHIXY:X1=X1-1 ENDIF W=INT(X1*R2%):H=INT(Y1*R2%) WEND ENDIF ENDPROC Edited November 14, 2023 by Brad Nelson Quote Link to comment Share on other sites More sharing options...
markmiller Posted November 14, 2023 Author Share Posted November 14, 2023 (edited) @Brad Nelson - Okay, my trig. from school is coming back to me. I realized there's yet a faster way, by using the fact that cos(a1 + a2) = cos(a1) cos(a2) − sin(a1) sin(a2), and sin(a1 + a2) = sin(a1) cos(a2) + cos(a1) sin(a2). GR.0 SET.2,0,0:C.1 INPUT "INPUT X (1-318) (ACCOUNT FOR RADIUS) ";X% INPUT "INPUT Y (1-190) (ACCOUNT FOR RADIUS) ";Y% INPUT "INPUT RADIUS ";RAD% INPUT "INPUT OBLIQUENESS (0.01-2.0) ";OBLIQUENESS% GR.8+16 T%=TIME EXEC DRAWCIRCLE GR.0 ?"TIME: ";(TIME-T%)/60.0;" SEC." END PROC DRAWCIRCLE RADX%=RAD%:RADY%=RAD%*OBLIQUENESS%:HPI%=1.571 CHPI%=COS(HPI%):SHPI%=SIN(HPI%) FOR ANGLE=0 TO 157 ANG%=ANGLE*0.01:CANG%=COS(ANG%):SANG%=SIN(ANG%) BASEX%=RADX%*CANG%:BASEY%=RADY%*SANG% ROT90X%=RADX%*(CANG%*CHPI%-SANG%*SHPI%):ROT90Y%=RADY%*(SANG%*CHPI%+CANG%*SHPI%) PL.INT(X%+BASEX%),INT(Y%+BASEY%) PL.INT(X%-BASEX%),INT(Y%-BASEY%) PL.INT(X%+ROT90X%),INT(Y%+ROT90Y%) PL.INT(X%-ROT90X%),INT(Y%-ROT90Y%) N. ANGLE ENDPROC I got the time down to 32.1 seconds, now less than half the original, because I'm doing the same trig. calculations as you did in your original, and getting the rest in less time, because the sin() and cos() for the 2nd angle I'm using is only calculated once, before entering the loop. There's more multiplying inside the loop (expensive, but less expensive than what I had before), but less trig. than I had before, and I'm still doing quadrants. BTW, I tried this in TB, 12.1 seconds. Edited November 14, 2023 by markmiller 1 Quote Link to comment Share on other sites More sharing options...
Brad Nelson Posted November 14, 2023 Share Posted November 14, 2023 We may have to make a Trig Guru hat for you, Mark. That's neat that you could speed things up so much. And I'm surprised it's so much faster in TBXL. Nice work. Quote Link to comment Share on other sites More sharing options...
+Stephen Posted November 14, 2023 Share Posted November 14, 2023 Great article here: https://www.atarimagazines.com/compute/issue38/066_1_CIRCLES.php 2 1 Quote Link to comment Share on other sites More sharing options...
Brad Nelson Posted November 14, 2023 Share Posted November 14, 2023 (edited) Thanks for the link, Stephen. I had also found similar info here. And at that link you can download the programs and try them out. I tried out the "fastest" one there (Program 7), the "BASIC call to machine language." And once it all gets loaded in, it is indeed fast. The first run is slow as it loads in all that data. But the second run (load in Program 6 for that) was 0.018 seconds in Turbo-BASIC XL. The expected run time listed for it in the program listing is 0.1833. I expect that's the time when run in Atari Basic. I've attached that BASIC file below. P153P7A.BAS Edited November 15, 2023 by Brad Nelson 1 1 Quote Link to comment Share on other sites More sharing options...
+Stephen Posted November 15, 2023 Share Posted November 15, 2023 1 hour ago, Brad Nelson said: Thanks for the link, Stephen. I had also found similar info here. And at that link you can download the programs and try them out. I tried out the "fastest" one there (Program 7), the "BASIC call to machine language." And once it all gets loaded in, it is indeed fast. The first run is slow as it loads in all that data. But the second run (load in Program 6 for that) was 0.018 seconds in Turbo-BASIC XL. The expected run time listed for it in the program listing is 0.1833. I expect that's the time when run in Atari Basic. I've attached that BASIC file below. P153P7A.BAS 3.42 kB · 1 download I typed in the program, but there were so many errors in the assembly, I haven't yet been able to debug it. The errors are all from bad OCR. I'd love to try it though, I definitely have a use for it in mind. 1 Quote Link to comment Share on other sites More sharing options...
+Philsan Posted November 15, 2023 Share Posted November 15, 2023 20 hours ago, Stephen said: Great article here: https://www.atarimagazines.com/compute/issue38/066_1_CIRCLES.php Very interesting, thanks! Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.