Author Topic: Plot  (Read 7770 times)

0 Members and 1 Guest are viewing this topic.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Plot
« on: September 15, 2019, 01:50:36 am »
Why go outside QB64 for plotting data?

Code: QB64: [Select]
  1. _TITLE "Plot > Point mouse for coordinates" ' started 2019-09-14 B+
  2. ' from: Remake Eval  2018-09-16 5:53 PM
  3. ' goals test remake eval and plot math functions
  4. '2019-09-14 basic plot working, now for sensable ticks on axis
  5. ' OK maybe do some replotting with mousedown and setting up new rectangle area
  6. ' On the other hand we could plot data from a file?
  7.  
  8.  
  9. CONST XMAX = 1200
  10. CONST YMAX = 700
  11.  
  12. SCREEN _NEWIMAGE(XMAX, YMAX, 32)
  13.  
  14. debug = 0
  15.  
  16. 'evaluate$ and evalW setup
  17. DIM SHARED DFlag AS _BIT, EvalErr$, GlobalX AS _FLOAT, RAD AS _FLOAT, DEG AS _FLOAT
  18. DFlag = 0
  19. EvalErr$ = ""
  20. GlobalX = 5 'changeable
  21. RAD = _PI / 180.0
  22. DEG = 180 / _PI
  23. REDIM SHARED fList(1 TO 1) AS STRING
  24. Split "int, sin, cos, tan, asin, acos, atan, log, exp, sqr, rad, deg,", ", ", fList()
  25. REDIM SHARED oList(1 TO 1) AS STRING
  26. Split "^, %, /, *, -, +, =, <, >, <=, >=, <>, or, and, not", ", ", oList()
  27.  
  28. 'DIM x
  29.  
  30. plot "(X+3)*(X+2)*(X-1)*(X-4), -5, 5"
  31. 'plot "sin(x)^2 + sin(x), -6.3, 6.3"
  32.  
  33. SUB plot (pList$)
  34.     REDIM p(1 TO 1) AS STRING, table(1000)
  35.     DIM func$, LX, UX, dx, dy, x, y, LY, UY, clicks, midY, s$, mx, my, gx, gy
  36.     Split pList$, ",", p()
  37.     func$ = p(1): LX = VAL(p(2)): UX = VAL(p(3))
  38.     dx = (UX - LX) / 1000
  39.     FOR x = 0 TO 1000
  40.         GlobalX = LX + x * dx
  41.         table(x) = VAL(Evaluate$(func$))
  42.         IF x = 0 THEN
  43.             LY = table(x): UY = table(x)
  44.         ELSE
  45.             IF table(x) < LY THEN LY = table(x)
  46.             IF table(x) > UY THEN UY = table(x)
  47.         END IF
  48.     NEXT
  49.     dy = (UY - LY) / 500
  50.  
  51.     clicks = (UX - LX) / 20
  52.     COLOR &HFF000000, &HFFFFFFFF: CLS
  53.     yCP 42, "Plot " + func$
  54.     yCP 640, "For x = " + dp2$(LX) + " to " + dp2$(UX) + " steps every " + dp2$(clicks)
  55.     LINE (100, 100)-STEP(1000, 500), , B
  56.     FOR x = 0 TO 20
  57.         LINE (100 + x * 50, 595)-STEP(0, 10)
  58.     NEXT
  59.     IF 0 > LX AND 0 < UX THEN LINE ((0 - LX) / dx + 100, 100)-STEP(0, 500): _PRINTSTRING ((0 - LX) / dx + 100 - 12, 605), "x=0"
  60.  
  61.     clicks = (UY - LY) / 10
  62.     IF 0 > LY AND 0 < UY THEN
  63.         midY = 600 - (0 - LY) / dy
  64.         LINE (100, midY)-STEP(1000, 0): _PRINTSTRING (43, midY + -8), "F(x)=0"
  65.         FOR y = -500 TO 500 STEP 50
  66.             IF midY + y >= 100 AND midY + y <= 600 AND y <> 0 THEN
  67.                 LINE (95, midY + y)-STEP(10, 0)
  68.                 s$ = RIGHT$(SPACE$(11) + dp2$(clicks * y / -50), 11)
  69.                 _PRINTSTRING (0, midY + y - 8), s$
  70.             END IF
  71.         NEXT
  72.     ELSE
  73.         FOR y = 0 TO 10
  74.             LINE (95, 100 + y * 50)-STEP(10, 0)
  75.             s$ = RIGHT$(SPACE$(11) + dp2$(LY + clicks * y), 11)
  76.             _PRINTSTRING (0, 600 - y * 50 - 8), s$
  77.         NEXT
  78.     END IF
  79.     FOR x = 0 TO 1000
  80.         y = (table(x) - LY) / dy
  81.         LINE (x + 100, 600 - y)-STEP(2, 2), &HFF0000FF, BF
  82.         PSET (x + 100, 600 - y), &HFF0000FF
  83.     NEXT
  84.     WHILE _KEYDOWN(27) = 0
  85.         WHILE _MOUSEINPUT: WEND
  86.         mx = _MOUSEX: my = _MOUSEY
  87.         IF mx <= 1100 AND mx >= 100 THEN
  88.             IF my >= 100 AND my <= 600 THEN
  89.                 yCP 80, SPACE$(50)
  90.                 gx = (mx - 100) / 1000 * (UX - LX) + LX
  91.                 gy = (600 - my) / 500 * (UY - LY) + LY
  92.                 yCP 80, "X = " + dp2$(gx) + ", Y = " + dp2$(gy)
  93.             END IF
  94.         END IF
  95.         _DISPLAY
  96.         _LIMIT 200
  97.     WEND
  98.  
  99. 'this preps e$ string for actual evaluation function and makes call to it,
  100. 'checks results for error returns that or string form of result calculation
  101. 'the new goal is to do string functions along side math
  102. FUNCTION Evaluate$ (e$)
  103.     DIM b$, c$
  104.     DIM i AS INTEGER, po AS INTEGER
  105.     ' isolateNeg = 0
  106.     b$ = "" 'rebuild string with padded spaces
  107.     'this makes sure ( ) + * / % ^ are wrapped with spaces, on your own with - sign
  108.     FOR i = 1 TO LEN(e$) 'filter chars and count ()
  109.         c$ = LCASE$(MID$(e$, i, 1))
  110.         IF c$ = ")" THEN
  111.             po = po - 1: b$ = b$ + " ) "
  112.         ELSEIF c$ = "(" THEN
  113.             po = po + 1: b$ = b$ + " ( "
  114.         ELSEIF INSTR("+*/%^", c$) > 0 THEN
  115.             b$ = b$ + " " + c$ + " "
  116.         ELSEIF c$ = "-" THEN
  117.             IF LEN(b$) > 0 THEN
  118.                 IF INSTR(".0123456789abcdefghijklmnopqrstuvwxyz)", RIGHT$(RTRIM$(b$), 1)) > 0 THEN
  119.                     b$ = b$ + " " + c$ + " "
  120.                 ELSE
  121.                     b$ = b$ + " " + c$
  122.                 END IF
  123.             ELSE
  124.                 b$ = b$ + " " + c$
  125.             END IF
  126.         ELSEIF INSTR(" .0123456789abcdefghijklmnopqrstuvwxyz<>=", c$) > 0 THEN
  127.             b$ = b$ + c$
  128.         END IF
  129.         IF po < 0 THEN EvalErr$ = "Too many )": EXIT FUNCTION
  130.     NEXT
  131.     IF po <> 0 THEN EvalErr$ = "Unbalanced ()": EXIT FUNCTION
  132.     REDIM ev(1 TO 1) AS STRING
  133.     Split b$, " ", ev()
  134.     FOR i = LBOUND(ev) TO UBOUND(ev) 'subst constants
  135.         IF ev(i) = "pi" THEN
  136.             ev(i) = LTRIM$(STR$(_PI))
  137.         ELSEIF ev(i) = "x" THEN
  138.             ev(i) = LTRIM$(STR$(GlobalX))
  139.         ELSEIF ev(i) = "e" THEN
  140.             ev(i) = LTRIM$(STR$(EXP(1)))
  141.         END IF
  142.     NEXT
  143.     c$ = evalW$(ev())
  144.     IF EvalErr$ <> "" THEN Evaluate$ = EvalErr$ ELSE Evaluate$ = c$
  145.  
  146.  
  147. ' the recursive part of EVAL
  148. FUNCTION evalW$ (a() AS STRING)
  149.     IF EvalErr$ <> "" THEN EXIT FUNCTION
  150.  
  151.     DIM fun$, test$, innerV$, m$, op$
  152.     DIM pop AS INTEGER, lPlace AS INTEGER, i AS INTEGER, rPlace AS INTEGER
  153.     DIM po AS INTEGER, p AS INTEGER, o AS INTEGER, index AS INTEGER
  154.     DIM recurs AS INTEGER
  155.     DIM innerVal AS _FLOAT, a AS _FLOAT, b AS _FLOAT
  156.     IF debug THEN
  157.         PRINT "evalW rec'd a() as:"
  158.         FOR i = LBOUND(a) TO UBOUND(a)
  159.             PRINT a(i); ", ";
  160.         NEXT
  161.         PRINT: INPUT "OK enter"; test$: PRINT
  162.     END IF
  163.     pop = find%(a(), "(") 'parenthesis open place
  164.     WHILE pop > 0
  165.         IF pop = 1 THEN
  166.             fun$ = "": lPlace = 1
  167.         ELSE
  168.             test$ = a(pop - 1)
  169.             IF find%(fList(), test$) > 0 THEN
  170.                 fun$ = test$: lPlace = pop - 1
  171.             ELSE
  172.                 fun$ = "": lPlace = pop
  173.             END IF
  174.         END IF
  175.         po = 1
  176.         FOR i = pop + 1 TO UBOUND(a)
  177.             IF a(i) = "(" THEN po = po + 1
  178.             IF a(i) = ")" THEN po = po - 1
  179.             IF po = 0 THEN rPlace = i: EXIT FOR
  180.         NEXT
  181.         REDIM inner(1 TO 1) AS STRING: index = 0: recurs = 0
  182.         FOR i = (pop + 1) TO (rPlace - 1)
  183.             index = index + 1
  184.             REDIM _PRESERVE inner(1 TO index) AS STRING
  185.             inner(index) = a(i)
  186.             IF find%(oList(), a(i)) > 0 THEN recurs = -1
  187.         NEXT
  188.         IF recurs THEN innerV$ = evalW$(inner()) ELSE innerV$ = a(pop + 1)
  189.         innerVal = VAL(innerV$)
  190.  
  191.         SELECT CASE fun$
  192.             CASE "": m$ = innerV$
  193.             CASE "int": m$ = ls$(INT(innerVal))
  194.             CASE "sin": IF DFlag THEN m$ = ls$(SIN(RAD * innerVal)) ELSE m$ = ls$(SIN(innerVal))
  195.             CASE "cos": IF DFlag THEN m$ = ls$(COS(RAD * innerVal)) ELSE m$ = ls$(COS(innerVal))
  196.             CASE "tan": IF DFlag THEN m$ = ls$(TAN(RAD * innerVal)) ELSE m$ = ls$(TAN(innerVal))
  197.             CASE "asin": IF DFlag THEN m$ = ls$(_ASIN(RAD * innerVal)) ELSE m$ = ls$(_ASIN(innerVal))
  198.             CASE "acos": IF DFlag THEN m$ = ls$(_ACOS(RAD * innerVal)) ELSE m$ = ls$(_ACOS(innerVal))
  199.             CASE "atan": IF DFlag THEN m$ = ls$(ATN(RAD * innerVal)) ELSE m$ = ls$(ATN(innerVal))
  200.             CASE "log"
  201.                 IF innerVal > 0 THEN
  202.                     m$ = ls$(LOG(innerVal))
  203.                 ELSE
  204.                     EvalErr$ = "LOG only works on numbers > 0.": EXIT FUNCTION
  205.                 END IF
  206.             CASE "exp" 'the error limit is inconsistent in JB
  207.                 IF -745 <= innerVal AND innerVal <= 709 THEN 'your system may have different results
  208.                     m$ = ls$(EXP(innerVal))
  209.                 ELSE
  210.                     'what the heck???? 708 works fine all alone as limit ?????
  211.                     EvalErr$ = "EXP(n) only works for n = -745 to 709.": EXIT FUNCTION
  212.                 END IF
  213.             CASE "sqr"
  214.                 IF innerVal >= 0 THEN
  215.                     m$ = ls$(SQR(innerVal))
  216.                 ELSE
  217.                     EvalErr$ = "SQR only works for numbers >= 0.": EXIT FUNCTION
  218.                 END IF
  219.             CASE "rad": m$ = ls$(innerVal * RAD)
  220.             CASE "deg": m$ = ls$(innerVal * DEG)
  221.             CASE ELSE: EvalErr$ = "Unidentified function " + fun$: EXIT FUNCTION
  222.         END SELECT
  223.         IF debug THEN
  224.             PRINT "lPlace, rPlace"; lPlace, rPlace
  225.         END IF
  226.         arrSubst a(), lPlace, rPlace, m$
  227.         IF debug THEN
  228.             PRINT "After arrSubst a() is:"
  229.             FOR i = LBOUND(a) TO UBOUND(a)
  230.                 PRINT a(i); " ";
  231.             NEXT
  232.             PRINT: PRINT
  233.         END IF
  234.         pop = find%(a(), "(")
  235.     WEND
  236.  
  237.     'all parenthesis cleared
  238.     'ops$ = "% ^ / * + - = < > <= >= <> and or not" 'all () cleared, now for binary ops (not not binary but is last!)
  239.     FOR o = 1 TO 15
  240.         op$ = oList(o)
  241.         p = find%(a(), op$)
  242.         WHILE p > 0
  243.             a = VAL(a(p - 1))
  244.             b = VAL(a(p + 1))
  245.             IF debug THEN
  246.                 PRINT STR$(a) + op$ + STR$(b)
  247.             END IF
  248.             SELECT CASE op$
  249.                 CASE "%"
  250.                     IF b >= 2 THEN
  251.                         m$ = ls$(INT(a) MOD INT(b))
  252.                     ELSE
  253.                         EvalErr$ = "For a Mod b, b value < 2."
  254.                         EXIT FUNCTION
  255.                     END IF
  256.                 CASE "^"
  257.                     IF INT(b) = b OR a >= 0 THEN
  258.                         m$ = ls$(a ^ b)
  259.                     ELSE
  260.                         EvalErr$ = "For a ^ b, a needs to be >= 0 when b not integer."
  261.                         EXIT FUNCTION
  262.                     END IF
  263.                 CASE "/"
  264.                     IF b <> 0 THEN
  265.                         m$ = ls$(a / b)
  266.                     ELSE
  267.                         EvalErr$ = "Div by 0"
  268.                         EXIT FUNCTION
  269.                     END IF
  270.                 CASE "*": m$ = ls$(a * b)
  271.                 CASE "-": m$ = ls$(a - b)
  272.                 CASE "+": m$ = ls$(a + b)
  273.                 CASE "=": IF a = b THEN m$ = "-1" ELSE m$ = "0"
  274.                 CASE "<": IF a < b THEN m$ = "-1" ELSE m$ = "0"
  275.                 CASE ">": IF a > b THEN m$ = "-1" ELSE m$ = "0"
  276.                 CASE "<=": IF a <= b THEN m$ = "-1" ELSE m$ = "0"
  277.                 CASE ">=": IF a >= b THEN m$ = "-1" ELSE m$ = "0"
  278.                 CASE "<>": IF a <> b THEN m$ = "-1" ELSE m$ = "0"
  279.                 CASE "and": IF a <> 0 AND b <> 0 THEN m$ = "-1" ELSE m$ = "0"
  280.                 CASE "or": IF a <> 0 OR b <> 0 THEN m$ = "-1" ELSE m$ = "0"
  281.                 CASE "not": IF b = 0 THEN m$ = "-1" ELSE m$ = "0" 'use b as nothing should be left of not
  282.             END SELECT
  283.             arrSubst a(), p - 1, p + 1, m$
  284.  
  285.             IF debug THEN
  286.                 PRINT "a() reloaded after " + op$ + " as:"
  287.                 FOR i = LBOUND(a) TO UBOUND(a)
  288.                     PRINT a(i); ", ";
  289.                 NEXT
  290.                 PRINT: PRINT
  291.             END IF
  292.  
  293.             p = find%(a(), op$)
  294.         WEND
  295.     NEXT
  296.     fun$ = ""
  297.     FOR i = LBOUND(a) TO UBOUND(a)
  298.         fun$ = fun$ + " " + a(i)
  299.     NEXT
  300.     evalW$ = LTRIM$(fun$)
  301.  
  302. SUB arrSubst (a() AS STRING, substLow AS LONG, substHigh AS LONG, subst AS STRING)
  303.     DIM i AS LONG, index AS LONG
  304.     a(substLow) = subst: index = substLow + 1
  305.     FOR i = substHigh + 1 TO UBOUND(a)
  306.         a(index) = a(i): index = index + 1
  307.     NEXT
  308.     REDIM _PRESERVE a(LBOUND(a) TO UBOUND(a) + substLow - substHigh)
  309.  
  310. 'notes: REDIM the array(0) to be loaded before calling Split '<<<<<<<<<<<<<<<<<<<<<<< IMPORTANT!!!!
  311. SUB Split (mystr AS STRING, delim AS STRING, arr() AS STRING)
  312.     ' bplus modifications of Galleon fix of Bulrush Split reply #13
  313.     ' http://www.[abandoned, outdated and now likely malicious qb64 dot net website - don’t go there]/forum/index.php?topic=1612.0
  314.     ' this sub further developed and tested here: \test\Strings\Split test.bas
  315.     ' 2018-09-16 modified for base 1 arrays
  316.     DIM copy AS STRING, p AS LONG, curpos AS LONG, arrpos AS LONG, lc AS LONG, dpos AS LONG
  317.     copy = mystr 'make copy since we are messing with mystr
  318.     'special case if delim is space, probably want to remove all excess space
  319.     IF delim = " " THEN
  320.         copy = RTRIM$(LTRIM$(copy))
  321.         p = INSTR(copy, "  ")
  322.         WHILE p > 0
  323.             copy = MID$(copy, 1, p - 1) + MID$(copy, p + 1)
  324.             p = INSTR(copy, "  ")
  325.         WEND
  326.     END IF
  327.     REDIM arr(1 TO 1) 'clear it
  328.     curpos = 1
  329.     arrpos = 1
  330.     lc = LEN(copy)
  331.     dpos = INSTR(curpos, copy, delim)
  332.     DO UNTIL dpos = 0
  333.         arr(arrpos) = MID$(copy, curpos, dpos - curpos)
  334.         arrpos = arrpos + 1
  335.         REDIM _PRESERVE arr(1 TO arrpos + 1) AS STRING
  336.         curpos = dpos + LEN(delim)
  337.         dpos = INSTR(curpos, copy, delim)
  338.     LOOP
  339.     arr(arrpos) = MID$(copy, curpos)
  340.     REDIM _PRESERVE arr(1 TO arrpos) AS STRING
  341.  
  342. 'assume a() is base 1 array so if find comes back as 0 then found nothing
  343. FUNCTION find% (a() AS STRING, s$)
  344.     DIM i%
  345.     FOR i% = LBOUND(a) TO UBOUND(a)
  346.         IF a(i%) = s$ THEN find% = i%: EXIT FUNCTION
  347.     NEXT
  348.  
  349. 'ltrim a number float
  350.     ls$ = LTRIM$(STR$(n))
  351.  
  352. 'FUNCTION xDP$ (x, DP)
  353. '    DIM xs$, dot AS INTEGER
  354.  
  355. '    IF x < 0 THEN xs$ = STR$(x - .5 * 10 ^ -DP)
  356. '    IF x > 0 THEN xs$ = STR$(x + .5 * 10 ^ -DP)
  357. '    IF INSTR(xs$, "D") > 0 OR INSTR(xs$, "E") > 0 THEN EXIT FUNCTION 'not dealing with exponents today
  358. '    dot = INSTR(xs$, ".")
  359. '    IF xs$ = "" OR ABS(x) < .5 * 10 ^ -DP THEN xs$ = "0"
  360. '    IF dot THEN xDP$ = MID$(xs$, 1, dot) + MID$(xs$, dot + 1, DP) ELSE xDP$ = xs$
  361. 'END FUNCTION
  362.  
  363. SUB yCP (y, s$) 'for xmax pixel wide graphics screen Center Print at pixel y row
  364.     _PRINTSTRING ((_WIDTH - LEN(s$) * 8) / 2, y), s$
  365.  
  366. FUNCTION dp2$ (n)
  367.     dp2$ = _TRIM$(STR$(INT(n * 100) / 100))
  368.  
  369.  

 
Plot sin curves.PNG


Thick heavy line? or Thin precise line?

 
Plot poly.PNG
« Last Edit: September 15, 2019, 01:53:37 am by bplus »

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Plot
« Reply #1 on: September 15, 2019, 04:47:53 am »
Beautiful bplus. I remember creating and deleting my own version(s) of this ad infinitum in the past. The version I ended up keeping was text-only, go figure...
ss1.png
* ss1.png (Filesize: 454.25 KB, Dimensions: 1366x768, Views: 233)
ss2.png
* ss2.png (Filesize: 459.2 KB, Dimensions: 1366x768, Views: 226)
You're not done when it works, you're done when it's right.

Offline Qwerkey

  • Forum Resident
  • Posts: 755
    • View Profile
Re: Plot
« Reply #2 on: September 15, 2019, 06:24:10 am »

Thick heavy line?


Ah, the plot thickens!

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Plot
« Reply #3 on: September 15, 2019, 02:14:04 pm »
Um bro that's far from true. I've seen straight up  Excel graphs published and presented at real math conferences before, if that flies then give bplus an aplus, will ya?
You're not done when it works, you're done when it's right.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Plot
« Reply #4 on: September 15, 2019, 02:44:14 pm »
Hi BSpinoza,

Ha! It was your graph that inspired this as you might guess.

This is only first day out and I like the programming challenges graphing data or functions pose. For instance, how to get a better scale going along the left side for y axis, regular intervals of .1, 1, 10, 100... Would polar coordinate graphs require a whole different system? How about data from files or even clipboard?

How to work around the D or E of Scientific or Exponential notation, the following was rounding nicely but messed up by Ds or Es between 0 and .5 which is why I canned it for Steve's method used in dp2$ (which stands for 2 decimal places)
Code: QB64: [Select]
  1. 'FUNCTION xDP$ (x, DP)
  2. '    DIM xs$, dot AS INTEGER
  3.  
  4. '    IF x < 0 THEN xs$ = STR$(x - .5 * 10 ^ -DP)
  5. '    IF x > 0 THEN xs$ = STR$(x + .5 * 10 ^ -DP)
  6. '    IF INSTR(xs$, "D") > 0 OR INSTR(xs$, "E") > 0 THEN EXIT FUNCTION 'not dealing with exponents today
  7. '    dot = INSTR(xs$, ".")
  8. '    IF xs$ = "" OR ABS(x) < .5 * 10 ^ -DP THEN xs$ = "0"
  9. '    IF dot THEN xDP$ = MID$(xs$, 1, dot) + MID$(xs$, dot + 1, DP) ELSE xDP$ = xs$
  10. 'END FUNCTION
  11.  

I do think having the mouse point out the coordinates is a great feature. We might be able to zoom in rectangular areas selected by the mouse.

My goal is not to get published in tech journals so much as to help visualize math functions or data relationships.

So you have answered why someone might go to outside sources :) they are accepted by the science community.

Thanks STxAxTIC for your support and Qwerkey for your wordplay, I had nice laugh :)
« Last Edit: September 15, 2019, 02:49:06 pm by bplus »

Offline Ashish

  • Forum Resident
  • Posts: 630
  • Never Give Up!
    • View Profile
Re: Plot
« Reply #5 on: September 19, 2019, 08:35:28 am »
Good Program!
1. If you add abs() function, then it will fantastic.
2. I see nothing for log(x) graph.
if (Me.success) {Me.improve()} else {Me.tryAgain()}


My Projects - https://github.com/AshishKingdom?tab=repositories
OpenGL tutorials - https://ashishkingdom.github.io/OpenGL-Tutorials

Offline Jack002

  • Forum Regular
  • Posts: 123
  • Boss, l wanna talk about arrays
    • View Profile
Re: Plot
« Reply #6 on: September 19, 2019, 10:52:33 am »
That is cool! I've been thinking about doing the same thing. If you can do this, who needs a graphing calc?
QB64 is the best!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Plot
« Reply #7 on: September 19, 2019, 11:05:46 am »
Good Program!
1. If you add abs() function, then it will fantastic.
2. I see nothing for log(x) graph.


Hi Ashish,

Thank you for your interest. Adding functions to code is relatively easy just add name to list near top of code:
Code: QB64: [Select]
  1. REDIM SHARED fList(1 TO 1) AS STRING
  2. Split "int, sin, cos, tan, asin, acos, atan, log, exp, sqr, rad, deg,", ", ", fList()
  3.  

And then add the application of it to values found inside formula while evaluate processes the function values, this section in evalW$ SUB:
Code: QB64: [Select]
  1.         SELECT CASE fun$
  2.             CASE "": m$ = innerV$
  3.             CASE "int": m$ = ls$(INT(innerVal))
  4.             CASE "sin": IF DFlag THEN m$ = ls$(SIN(RAD * innerVal)) ELSE m$ = ls$(SIN(innerVal))
  5.             CASE "cos": IF DFlag THEN m$ = ls$(COS(RAD * innerVal)) ELSE m$ = ls$(COS(innerVal))
  6.             CASE "tan": IF DFlag THEN m$ = ls$(TAN(RAD * innerVal)) ELSE m$ = ls$(TAN(innerVal))
  7.             CASE "asin": IF DFlag THEN m$ = ls$(_ASIN(RAD * innerVal)) ELSE m$ = ls$(_ASIN(innerVal))
  8.             CASE "acos": IF DFlag THEN m$ = ls$(_ACOS(RAD * innerVal)) ELSE m$ = ls$(_ACOS(innerVal))
  9.             CASE "atan": IF DFlag THEN m$ = ls$(ATN(RAD * innerVal)) ELSE m$ = ls$(ATN(innerVal))
  10.             CASE "log"
  11.                 IF innerVal > 0 THEN
  12.                     m$ = ls$(LOG(innerVal))
  13.                 ELSE
  14.                     EvalErr$ = "LOG only works on numbers > 0.": EXIT FUNCTION
  15.                 END IF
  16.             CASE "exp" 'the error limit is inconsistent in JB
  17.                 IF -745 <= innerVal AND innerVal <= 709 THEN 'your system may have different results
  18.                     m$ = ls$(EXP(innerVal))
  19.                 ELSE
  20.                     'what the heck???? 708 works fine all alone as limit ?????
  21.                     EvalErr$ = "EXP(n) only works for n = -745 to 709.": EXIT FUNCTION
  22.                 END IF
  23.             CASE "sqr"
  24.                 IF innerVal >= 0 THEN
  25.                     m$ = ls$(SQR(innerVal))
  26.                 ELSE
  27.                     EvalErr$ = "SQR only works for numbers >= 0.": EXIT FUNCTION
  28.                 END IF
  29.             CASE "rad": m$ = ls$(innerVal * RAD)
  30.             CASE "deg": m$ = ls$(innerVal * DEG)
  31.             CASE ELSE: EvalErr$ = "Unidentified function " + fun$: EXIT FUNCTION
  32.         END SELECT
  33.  

I see that I do already have LOG, so let's try using it and see how results look on my new scaling system for the Y axis in PLOT 2. Rules for LOG are seen in QB64 help: X must be > 0

Lets try this:
http://www.sosmath.com/algebra/logs/log4/log47/log47.html

f(x) = 7*LOG(3*X) - 15   f(x) = 0 at x ~ 46.31...

If answer is around 46 then let's try a range of x from 10 to 100 putting x = 45 right smack in the middle of graph (no, smack in middle of that would be 55 :P  ).
So here is the call to PLOT:
Code: QB64: [Select]
  1. plot "7*LOG(3*X) - 15, 10, 100"
  2.  

Notice: when we call the plot SUB, all the arguments are in just one string. That is because I plan on allowing an unlimited! number of functions to plot when I get all details of displaying one function worked out.

Opps! that didn't work! Why? because in example LOG is log 10 not log e, easy to convert LOG10(x) = LOG(x)/LOG(10) so
Code: QB64: [Select]
  1. 'lets plot over giant range of 10 to 100 for x because quite frankly we've not a clue what's going on :D
  2. plot "LOG(3*x)/LOG(10) - 15/7, 10, 100"
  3.  
see attach 
grapg with LOG10 X range 10 to 100.PNG

Running the code and moving the mouse over to where f(x) = 0 I see a very gross estimate of x at 46 something, maybe .3, but let's refine the x range
from 46 to 47 to narrow in on x = 46.31 something...
see 2nd attach 
graph with LOG10 X range between 46 and 47.PNG


Looks like I need to alter my decimal places display as the precision is increased, 2 decimal places doesn't cut it for display.

I will attach a really cool graph of the polynomial with sequential roots for 1 to 7.
 
Plot seq roots 1 to 7.PNG


Thanks Jack002, all those showing interest in this project keeps me going trying to improve it.

Here is complete source code to plot 2 with LOG10 work shown above not commented out:
Code: QB64: [Select]
  1. _TITLE "Plot > Point mouse for coordinates" ' started 2019-09-14 B+
  2. ' from: Remake Eval  2018-09-16 5:53 PM
  3. ' goals test remake eval and plot math functions
  4. '2019-09-14 basic plot working, now for sensable ticks on axis
  5. ' OK maybe do some replotting with mousedown and setting up new rectangle area
  6. ' On the other hand we could plot data from a file?
  7.  
  8.  
  9. CONST XMAX = 1200
  10. CONST YMAX = 700
  11.  
  12. SCREEN _NEWIMAGE(XMAX, YMAX, 32)
  13.  
  14. debug = 0
  15.  
  16. 'evaluate$ and evalW setup
  17. DIM SHARED DFlag AS _BIT, EvalErr$, GlobalX AS _FLOAT, RAD AS _FLOAT, DEG AS _FLOAT
  18. DFlag = 0
  19. EvalErr$ = ""
  20. GlobalX = 5 'changeable
  21. RAD = _PI / 180.0
  22. DEG = 180 / _PI
  23. REDIM SHARED fList(1 TO 1) AS STRING
  24. Split "int, sin, cos, tan, asin, acos, atan, log, exp, sqr, rad, deg,", ", ", fList()
  25. REDIM SHARED oList(1 TO 1) AS STRING
  26. Split "^, %, /, *, -, +, =, <, >, <=, >=, <>, or, and, not", ", ", oList()
  27.  
  28.  
  29. '     quick check Example #2 with QB64 printout between 46 and 47
  30. 'DIM x       'check the http://www.sosmath.com/algebra/logs/log4/log47/log47.html Example #2
  31.  
  32. '    when the graph f(x) = 0 that is root to when LOG10(3*x) = 15/7
  33.  
  34. 'FOR x = 46 TO 47 STEP .1
  35. '    PRINT x, LOG(3 * x) / LOG(10) - 15 / 7 'ah that's better!  breaks at 46.3 something
  36. 'NEXT
  37. 'END
  38.  
  39. 'lets plot over giant range because quite frankly we've not a clue what's going on
  40. 'plot "LOG(3*x)/LOG(10) - 15/7, 10, 100"
  41.  
  42. ' OK narrow in on our solution x between 46 and 47!
  43. plot "LOG(3*x)/LOG(10) - 15/7, 46, 47"
  44.  
  45. ' this is really cool PLOT!
  46. 'plot "(x -1) * (x - 2) * (x - 3) * (x - 4)*(x-5)*(x-6)*(x-7) , .9, 7.1"
  47. 'plot "sin(x)^2 + sin(x), -6.3, 6.3"
  48.  
  49. SUB plot (pList$)
  50.     REDIM p(1 TO 1) AS STRING, table(1000)
  51.     DIM func$, LX, UX, dx, dy, x, y, LY, UY, clicks, midY, s$, mx, my, gx, gy, p10
  52.     Split pList$, ",", p()
  53.     func$ = p(1): LX = VAL(p(2)): UX = VAL(p(3))
  54.     dx = (UX - LX) / 1000
  55.     FOR x = 0 TO 1000
  56.         GlobalX = LX + x * dx
  57.         table(x) = VAL(Evaluate$(func$))
  58.         IF x = 0 THEN
  59.             LY = table(x): UY = table(x)
  60.         ELSE
  61.             IF table(x) < LY THEN LY = table(x)
  62.             IF table(x) > UY THEN UY = table(x)
  63.         END IF
  64.     NEXT
  65.     dy = (UY - LY) / 500
  66.  
  67.     clicks = (UX - LX) / 20
  68.     COLOR &HFF000000, &HFFFFFFFF: CLS
  69.     yCP 42, "Plot " + func$
  70.     yCP 640, "For x = " + xDP$(LX, 2) + " to " + xDP$(UX, 2) + " steps every " + xDP$(clicks, 2)
  71.     LINE (100, 100)-STEP(1000, 500), , B
  72.     FOR x = 0 TO 20
  73.         LINE (100 + x * 50, 595)-STEP(0, 10)
  74.     NEXT
  75.     IF 0 > LX AND 0 < UX THEN LINE ((0 - LX) / dx + 100, 100)-STEP(0, 500): _PRINTSTRING ((0 - LX) / dx + 100 - 12, 605), "x=0"
  76.  
  77.     drawscale LY, UY
  78.     'clicks = (UY - LY) / 10
  79.  
  80.     FOR x = 0 TO 1000
  81.         y = (table(x) - LY) / dy
  82.         'LINE (x + 99, 599 - y)-STEP(2, 2), &HFF0000FF, BF
  83.         PSET (x + 100, 600 - y), &HFF0000FF 'use a fine line because we have very large x Range
  84.     NEXT
  85.     WHILE _KEYDOWN(27) = 0
  86.         WHILE _MOUSEINPUT: WEND
  87.         mx = _MOUSEX: my = _MOUSEY
  88.         IF mx <= 1100 AND mx >= 100 THEN
  89.             IF my >= 100 AND my <= 600 THEN
  90.                 yCP 80, SPACE$(50)
  91.                 gx = (mx - 100) / 1000 * (UX - LX) + LX
  92.                 gy = (600 - my) / 500 * (UY - LY) + LY
  93.                 yCP 80, "X = " + xDP$(gx, 2) + ", Y = " + xDP$(gy, 2)
  94.             END IF
  95.         END IF
  96.         _DISPLAY
  97.         _LIMIT 200
  98.     WEND
  99.  
  100. SUB drawscale (LY, UY)
  101.     DIM clicks, p10 AS INTEGER, digit AS INTEGER, units AS SINGLE, midY, ty, gy, s$
  102.  
  103.     clicks = (UY - LY) / 10
  104.  
  105.     p10 = -10
  106.     WHILE ABS(clicks) > 10 ^ p10
  107.         p10 = p10 + 1
  108.     WEND
  109.     p10 = p10 - 1
  110.     digit = INT(clicks / (10 ^ p10))
  111.     units = digit * 10 ^ p10
  112.     'PRINT p10, digit, units
  113.  
  114.     LINE (100, 100)-STEP(1000, 500), , B
  115.     _PRINTSTRING (1105, 100 - 8), xDP$(UY, 2)
  116.     _PRINTSTRING (1105, 600 - 8), xDP$(LY, 2)
  117.     IF 0 > LY AND 0 < UY THEN
  118.         midY = pixY(0, LY, UY)
  119.         LINE (100, midY)-STEP(1000, 0): _PRINTSTRING (43, midY + -8), "F(x)=0"
  120.         ty = units
  121.         WHILE pixY(ty, LY, UY) >= 100 'from 0 + unit go up unit by unit
  122.             gy = pixY(ty, LY, UY)
  123.             LINE (95, gy)-STEP(10, 0)
  124.             s$ = RIGHT$(SPACE$(11) + xDP$(ty, 2), 11)
  125.             _PRINTSTRING (0, gy - 8), s$
  126.             ty = ty + units
  127.         WEND
  128.         ty = -units
  129.         WHILE pixY(ty, LY, UY) <= 600 'from 0 - unit go down unit by unit
  130.             gy = pixY(ty, LY, UY)
  131.             LINE (95, gy)-STEP(10, 0)
  132.             s$ = RIGHT$(SPACE$(11) + xDP$(ty, 2), 11)
  133.             _PRINTSTRING (0, gy - 8), s$
  134.             ty = ty - units
  135.         WEND
  136.     ELSE
  137.         ty = 0
  138.         WHILE pixY(ty, LY, UY) < 600 'find first unit below or at bottom of graph
  139.             ty = ty - units
  140.         WEND
  141.         ty = ty + units
  142.         ' now go up drawing marks
  143.         WHILE pixY(ty, LY, UY) >= 100
  144.             gy = pixY(ty, LY, UY)
  145.             IF gy <= 600 THEN
  146.                 LINE (95, gy)-STEP(10, 0)
  147.                 s$ = RIGHT$(SPACE$(11) + xDP$(ty, 2), 11)
  148.                 _PRINTSTRING (0, gy - 8), s$
  149.             END IF
  150.             ty = ty + units
  151.         WEND
  152.     END IF
  153.  
  154. FUNCTION pixY (graphY, lowY, UpY)
  155.     pixY = 600 - 500 * (graphY - lowY) / (UpY - lowY)
  156.  
  157. 'this preps e$ string for actual evaluation function and makes call to it,
  158. 'checks results for error returns that or string form of result calculation
  159. 'the new goal is to do string functions along side math
  160. FUNCTION Evaluate$ (e$)
  161.     DIM b$, c$
  162.     DIM i AS INTEGER, po AS INTEGER
  163.     ' isolateNeg = 0
  164.     b$ = "" 'rebuild string with padded spaces
  165.     'this makes sure ( ) + * / % ^ are wrapped with spaces, on your own with - sign
  166.     FOR i = 1 TO LEN(e$) 'filter chars and count ()
  167.         c$ = LCASE$(MID$(e$, i, 1))
  168.         IF c$ = ")" THEN
  169.             po = po - 1: b$ = b$ + " ) "
  170.         ELSEIF c$ = "(" THEN
  171.             po = po + 1: b$ = b$ + " ( "
  172.         ELSEIF INSTR("+*/%^", c$) > 0 THEN
  173.             b$ = b$ + " " + c$ + " "
  174.         ELSEIF c$ = "-" THEN
  175.             IF LEN(b$) > 0 THEN
  176.                 IF INSTR(".0123456789abcdefghijklmnopqrstuvwxyz)", RIGHT$(RTRIM$(b$), 1)) > 0 THEN
  177.                     b$ = b$ + " " + c$ + " "
  178.                 ELSE
  179.                     b$ = b$ + " " + c$
  180.                 END IF
  181.             ELSE
  182.                 b$ = b$ + " " + c$
  183.             END IF
  184.         ELSEIF INSTR(" .0123456789abcdefghijklmnopqrstuvwxyz<>=", c$) > 0 THEN
  185.             b$ = b$ + c$
  186.         END IF
  187.         IF po < 0 THEN EvalErr$ = "Too many )": EXIT FUNCTION
  188.     NEXT
  189.     IF po <> 0 THEN EvalErr$ = "Unbalanced ()": EXIT FUNCTION
  190.     REDIM ev(1 TO 1) AS STRING
  191.     Split b$, " ", ev()
  192.     FOR i = LBOUND(ev) TO UBOUND(ev) 'subst constants
  193.         IF ev(i) = "pi" THEN
  194.             ev(i) = LTRIM$(STR$(_PI))
  195.         ELSEIF ev(i) = "x" THEN
  196.             ev(i) = LTRIM$(STR$(GlobalX))
  197.         ELSEIF ev(i) = "e" THEN
  198.             ev(i) = LTRIM$(STR$(EXP(1)))
  199.         END IF
  200.     NEXT
  201.     c$ = evalW$(ev())
  202.     IF EvalErr$ <> "" THEN Evaluate$ = EvalErr$ ELSE Evaluate$ = c$
  203.  
  204.  
  205. ' the recursive part of EVAL
  206. FUNCTION evalW$ (a() AS STRING)
  207.     IF EvalErr$ <> "" THEN EXIT FUNCTION
  208.  
  209.     DIM fun$, test$, innerV$, m$, op$
  210.     DIM pop AS INTEGER, lPlace AS INTEGER, i AS INTEGER, rPlace AS INTEGER
  211.     DIM po AS INTEGER, p AS INTEGER, o AS INTEGER, index AS INTEGER
  212.     DIM recurs AS INTEGER
  213.     DIM innerVal AS _FLOAT, a AS _FLOAT, b AS _FLOAT
  214.     IF debug THEN
  215.         PRINT "evalW rec'd a() as:"
  216.         FOR i = LBOUND(a) TO UBOUND(a)
  217.             PRINT a(i); ", ";
  218.         NEXT
  219.         PRINT: INPUT "OK enter"; test$: PRINT
  220.     END IF
  221.     pop = find%(a(), "(") 'parenthesis open place
  222.     WHILE pop > 0
  223.         IF pop = 1 THEN
  224.             fun$ = "": lPlace = 1
  225.         ELSE
  226.             test$ = a(pop - 1)
  227.             IF find%(fList(), test$) > 0 THEN
  228.                 fun$ = test$: lPlace = pop - 1
  229.             ELSE
  230.                 fun$ = "": lPlace = pop
  231.             END IF
  232.         END IF
  233.         po = 1
  234.         FOR i = pop + 1 TO UBOUND(a)
  235.             IF a(i) = "(" THEN po = po + 1
  236.             IF a(i) = ")" THEN po = po - 1
  237.             IF po = 0 THEN rPlace = i: EXIT FOR
  238.         NEXT
  239.         REDIM inner(1 TO 1) AS STRING: index = 0: recurs = 0
  240.         FOR i = (pop + 1) TO (rPlace - 1)
  241.             index = index + 1
  242.             REDIM _PRESERVE inner(1 TO index) AS STRING
  243.             inner(index) = a(i)
  244.             IF find%(oList(), a(i)) > 0 THEN recurs = -1
  245.         NEXT
  246.         IF recurs THEN innerV$ = evalW$(inner()) ELSE innerV$ = a(pop + 1)
  247.         innerVal = VAL(innerV$)
  248.  
  249.         SELECT CASE fun$
  250.             CASE "": m$ = innerV$
  251.             CASE "int": m$ = ls$(INT(innerVal))
  252.             CASE "sin": IF DFlag THEN m$ = ls$(SIN(RAD * innerVal)) ELSE m$ = ls$(SIN(innerVal))
  253.             CASE "cos": IF DFlag THEN m$ = ls$(COS(RAD * innerVal)) ELSE m$ = ls$(COS(innerVal))
  254.             CASE "tan": IF DFlag THEN m$ = ls$(TAN(RAD * innerVal)) ELSE m$ = ls$(TAN(innerVal))
  255.             CASE "asin": IF DFlag THEN m$ = ls$(_ASIN(RAD * innerVal)) ELSE m$ = ls$(_ASIN(innerVal))
  256.             CASE "acos": IF DFlag THEN m$ = ls$(_ACOS(RAD * innerVal)) ELSE m$ = ls$(_ACOS(innerVal))
  257.             CASE "atan": IF DFlag THEN m$ = ls$(ATN(RAD * innerVal)) ELSE m$ = ls$(ATN(innerVal))
  258.             CASE "log"
  259.                 IF innerVal > 0 THEN
  260.                     m$ = ls$(LOG(innerVal))
  261.                 ELSE
  262.                     EvalErr$ = "LOG only works on numbers > 0.": EXIT FUNCTION
  263.                 END IF
  264.             CASE "exp" 'the error limit is inconsistent in JB
  265.                 IF -745 <= innerVal AND innerVal <= 709 THEN 'your system may have different results
  266.                     m$ = ls$(EXP(innerVal))
  267.                 ELSE
  268.                     'what the heck???? 708 works fine all alone as limit ?????
  269.                     EvalErr$ = "EXP(n) only works for n = -745 to 709.": EXIT FUNCTION
  270.                 END IF
  271.             CASE "sqr"
  272.                 IF innerVal >= 0 THEN
  273.                     m$ = ls$(SQR(innerVal))
  274.                 ELSE
  275.                     EvalErr$ = "SQR only works for numbers >= 0.": EXIT FUNCTION
  276.                 END IF
  277.             CASE "rad": m$ = ls$(innerVal * RAD)
  278.             CASE "deg": m$ = ls$(innerVal * DEG)
  279.             CASE ELSE: EvalErr$ = "Unidentified function " + fun$: EXIT FUNCTION
  280.         END SELECT
  281.         IF debug THEN
  282.             PRINT "lPlace, rPlace"; lPlace, rPlace
  283.         END IF
  284.         arrSubst a(), lPlace, rPlace, m$
  285.         IF debug THEN
  286.             PRINT "After arrSubst a() is:"
  287.             FOR i = LBOUND(a) TO UBOUND(a)
  288.                 PRINT a(i); " ";
  289.             NEXT
  290.             PRINT: PRINT
  291.         END IF
  292.         pop = find%(a(), "(")
  293.     WEND
  294.  
  295.     'all parenthesis cleared
  296.     'ops$ = "% ^ / * + - = < > <= >= <> and or not" 'all () cleared, now for binary ops (not not binary but is last!)
  297.     FOR o = 1 TO 15
  298.         op$ = oList(o)
  299.         p = find%(a(), op$)
  300.         WHILE p > 0
  301.             a = VAL(a(p - 1))
  302.             b = VAL(a(p + 1))
  303.             IF debug THEN
  304.                 PRINT STR$(a) + op$ + STR$(b)
  305.             END IF
  306.             SELECT CASE op$
  307.                 CASE "%"
  308.                     IF b >= 2 THEN
  309.                         m$ = ls$(INT(a) MOD INT(b))
  310.                     ELSE
  311.                         EvalErr$ = "For a Mod b, b value < 2."
  312.                         EXIT FUNCTION
  313.                     END IF
  314.                 CASE "^"
  315.                     IF INT(b) = b OR a >= 0 THEN
  316.                         m$ = ls$(a ^ b)
  317.                     ELSE
  318.                         EvalErr$ = "For a ^ b, a needs to be >= 0 when b not integer."
  319.                         EXIT FUNCTION
  320.                     END IF
  321.                 CASE "/"
  322.                     IF b <> 0 THEN
  323.                         m$ = ls$(a / b)
  324.                     ELSE
  325.                         EvalErr$ = "Div by 0"
  326.                         EXIT FUNCTION
  327.                     END IF
  328.                 CASE "*": m$ = ls$(a * b)
  329.                 CASE "-": m$ = ls$(a - b)
  330.                 CASE "+": m$ = ls$(a + b)
  331.                 CASE "=": IF a = b THEN m$ = "-1" ELSE m$ = "0"
  332.                 CASE "<": IF a < b THEN m$ = "-1" ELSE m$ = "0"
  333.                 CASE ">": IF a > b THEN m$ = "-1" ELSE m$ = "0"
  334.                 CASE "<=": IF a <= b THEN m$ = "-1" ELSE m$ = "0"
  335.                 CASE ">=": IF a >= b THEN m$ = "-1" ELSE m$ = "0"
  336.                 CASE "<>": IF a <> b THEN m$ = "-1" ELSE m$ = "0"
  337.                 CASE "and": IF a <> 0 AND b <> 0 THEN m$ = "-1" ELSE m$ = "0"
  338.                 CASE "or": IF a <> 0 OR b <> 0 THEN m$ = "-1" ELSE m$ = "0"
  339.                 CASE "not": IF b = 0 THEN m$ = "-1" ELSE m$ = "0" 'use b as nothing should be left of not
  340.             END SELECT
  341.             arrSubst a(), p - 1, p + 1, m$
  342.  
  343.             IF debug THEN
  344.                 PRINT "a() reloaded after " + op$ + " as:"
  345.                 FOR i = LBOUND(a) TO UBOUND(a)
  346.                     PRINT a(i); ", ";
  347.                 NEXT
  348.                 PRINT: PRINT
  349.             END IF
  350.  
  351.             p = find%(a(), op$)
  352.         WEND
  353.     NEXT
  354.     fun$ = ""
  355.     FOR i = LBOUND(a) TO UBOUND(a)
  356.         fun$ = fun$ + " " + a(i)
  357.     NEXT
  358.     evalW$ = LTRIM$(fun$)
  359.  
  360. SUB arrSubst (a() AS STRING, substLow AS LONG, substHigh AS LONG, subst AS STRING)
  361.     DIM i AS LONG, index AS LONG
  362.     a(substLow) = subst: index = substLow + 1
  363.     FOR i = substHigh + 1 TO UBOUND(a)
  364.         a(index) = a(i): index = index + 1
  365.     NEXT
  366.     REDIM _PRESERVE a(LBOUND(a) TO UBOUND(a) + substLow - substHigh)
  367.  
  368. 'notes: REDIM the array(0) to be loaded before calling Split '<<<<<<<<<<<<<<<<<<<<<<< IMPORTANT!!!!
  369. SUB Split (mystr AS STRING, delim AS STRING, arr() AS STRING)
  370.     ' bplus modifications of Galleon fix of Bulrush Split reply #13
  371.     ' http://www.[abandoned, outdated and now likely malicious qb64 dot net website - don’t go there]/forum/index.php?topic=1612.0
  372.     ' this sub further developed and tested here: \test\Strings\Split test.bas
  373.     ' 2018-09-16 modified for base 1 arrays
  374.     DIM copy AS STRING, p AS LONG, curpos AS LONG, arrpos AS LONG, lc AS LONG, dpos AS LONG
  375.     copy = mystr 'make copy since we are messing with mystr
  376.     'special case if delim is space, probably want to remove all excess space
  377.     IF delim = " " THEN
  378.         copy = RTRIM$(LTRIM$(copy))
  379.         p = INSTR(copy, "  ")
  380.         WHILE p > 0
  381.             copy = MID$(copy, 1, p - 1) + MID$(copy, p + 1)
  382.             p = INSTR(copy, "  ")
  383.         WEND
  384.     END IF
  385.     REDIM arr(1 TO 1) 'clear it
  386.     curpos = 1
  387.     arrpos = 1
  388.     lc = LEN(copy)
  389.     dpos = INSTR(curpos, copy, delim)
  390.     DO UNTIL dpos = 0
  391.         arr(arrpos) = MID$(copy, curpos, dpos - curpos)
  392.         arrpos = arrpos + 1
  393.         REDIM _PRESERVE arr(1 TO arrpos + 1) AS STRING
  394.         curpos = dpos + LEN(delim)
  395.         dpos = INSTR(curpos, copy, delim)
  396.     LOOP
  397.     arr(arrpos) = MID$(copy, curpos)
  398.     REDIM _PRESERVE arr(1 TO arrpos) AS STRING
  399.  
  400. 'assume a() is base 1 array so if find comes back as 0 then found nothing
  401. FUNCTION find% (a() AS STRING, s$)
  402.     DIM i%
  403.     FOR i% = LBOUND(a) TO UBOUND(a)
  404.         IF a(i%) = s$ THEN find% = i%: EXIT FUNCTION
  405.     NEXT
  406.  
  407. 'ltrim a number float
  408.     ls$ = LTRIM$(STR$(n))
  409.  
  410. SUB yCP (y, s$) 'for xmax pixel wide graphics screen Center Print at pixel y row
  411.     _PRINTSTRING ((_WIDTH - LEN(s$) * 8) / 2, y), s$
  412.  
  413. FUNCTION xDP$ (x, DP)
  414.     DIM xx
  415.     xx = x + .5 * 10 ^ -DP
  416.     xDP$ = _TRIM$(STR$(INT(xx * 10 ^ DP) / 10 ^ DP))
  417.  

As you can see what I did for the Y scale, I need to do for the X horizontal scale but haven't figured how to make nice display of x ticks unless draw digits / numbers with 90 degree rotation. Plus there is the problem of deciding the precision or number of decimals needed for displaying.
« Last Edit: September 19, 2019, 11:30:45 am by bplus »

Offline SierraKen

  • Forum Resident
  • Posts: 1454
    • View Profile
Re: Plot
« Reply #8 on: September 19, 2019, 01:51:28 pm »
I just tried this program and all I get is a diagonal line and coordinates, etc. Isn't this supposed to be like a Plotting Calculator? How do you change the plots? There's no directions. Of course everyone else can figure it out, except me. lol

Offline Jack002

  • Forum Regular
  • Posts: 123
  • Boss, l wanna talk about arrays
    • View Profile
Re: Plot
« Reply #9 on: September 19, 2019, 02:13:59 pm »
This is a graph of LOG, its just a short narrow capture
find the line with this on it
plot "LOG(3*x)/LOG(10) - 15/7, 46, 47"
Change the 46, 47 to what you want x to be. try 0, 100
[edit]
I tried this, and it worked, it looks more like what you wanted to see
plot "LOG(3*x)/LOG(10) , 1, 20"
Even better, go up a couple lines, look for this
plot "LOG(3*x)/LOG(10) - 15/7, 10, 100"
take off the ' from the front. and comment the
plot "LOG(3*x)/LOG(10) - 15/7, 46, 47"
line.

B+, I love this split SUB. That is one handy sub we all can use. Any QB programmer should wade around in there and look at the neat stuff this has.

« Last Edit: September 19, 2019, 02:47:33 pm by Jack002 »
QB64 is the best!

Offline SierraKen

  • Forum Resident
  • Posts: 1454
    • View Profile
Re: Plot
« Reply #10 on: September 19, 2019, 04:10:38 pm »
Oh LOL I skipped right over those, sorry about that. Awesome examples B+! This makes me wish I knew enough math to make random terrain hills with like the program Terragen can do. Like add a Z coordinate with X and Y somehow. :) But this is really cool!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Plot
« Reply #11 on: September 19, 2019, 05:01:15 pm »
Thanks Jack002, it is why I recommended split for tool box, though that one has been worked over some more.

For Ken and Ashish, I have made some changes by adding the ABS function and adding a more user friendly startup screen.

Ken, your request was absolutely right.

Hopefully you can input stuff as requested and your graph will pop up, use escape to loop around to inputs again and use function = nothing to quit (if you don't want to quit with X in top right corner).

Code: QB64: [Select]
  1. _TITLE "Plot v2_SierraKen > Point mouse for coordinates" ' started 2019-09-14 B+
  2. ' from: Remake Eval  2018-09-16 5:53 PM
  3. ' goals test remake eval and plot math functions
  4. '2019-09-14 basic plot working, now for sensable ticks on axis
  5. ' OK maybe do some replotting with mousedown and setting up new rectangle area
  6. ' On the other hand we could plot data from a file?
  7. '2019-09-19 post plot 2 with better Y scale except when precision needs to go beyond 2 places!
  8. ' We practice with a LOG(x) example and learned LOG10(x) = LOG(x)/LOG(10) thanks to QB64 Help!
  9. ' Need to do with x scale what we did with y scale after solve prcision problem = picking the
  10. ' right precision for displays.
  11. '2019-09-19 Plot v KenSierra add ABS function for Ashish, now with Opening inputs screen in main loop.
  12.  
  13. CONST XMAX = 1200
  14. CONST YMAX = 700
  15.  
  16. SCREEN _NEWIMAGE(XMAX, YMAX, 32)
  17.  
  18. debug = 0
  19.  
  20. 'evaluate$ and evalW setup
  21. DIM SHARED DFlag AS _BIT, EvalErr$, GlobalX AS _FLOAT, RAD AS _FLOAT, DEG AS _FLOAT
  22. DFlag = 0
  23. EvalErr$ = ""
  24. GlobalX = 5 'changeable
  25. RAD = _PI / 180.0
  26. DEG = 180 / _PI
  27. REDIM SHARED fList(1 TO 1) AS STRING
  28. Split "abs, int, sin, cos, tan, asin, acos, atan, log, exp, sqr, rad, deg, ", ", ", fList()
  29. REDIM SHARED oList(1 TO 1) AS STRING
  30. Split "^, %, /, *, -, +, =, <, >, <=, >=, <>, or, and, not", ", ", oList()
  31. DIM fn$, lowX$, highX$
  32.  
  33.     COLOR &HFFBBBBFF, &HFF000066
  34.     CLS
  35.     _KEYCLEAR
  36.     yCP 40, "Welcome to Plot v2_SierraKen       by B+ 2019-09-19"
  37.     yCP 80, "Here is a listing of functions available so far:"
  38.     yCP 100, "abs, int, sin, cos, tan, asin, acos, atan, log, exp, sqr, rad, deg"
  39.     yCP 140, " Please enter a function to plot in terms of x, ie x^2 + 4*x + 3 (nothing quits) "
  40.     LOCATE 11, 40: INPUT "F(x) = ", fn$
  41.     IF fn$ = "" THEN END
  42.     LOCATE 13, 40: INPUT "Please enter lowest X for range "; lowX$
  43.     LOCATE 15, 40: INPUT "Please enter highest X in range "; highX$
  44.     plot fn$ + "," + lowX$ + "," + highX$
  45.  
  46. SUB plot (pList$)
  47.     REDIM p(1 TO 1) AS STRING, table(1000)
  48.     DIM func$, LX, UX, dx, dy, x, y, LY, UY, clicks, midY, s$, mx, my, gx, gy, p10
  49.     Split pList$, ",", p()
  50.     func$ = p(1): LX = VAL(p(2)): UX = VAL(p(3))
  51.     dx = (UX - LX) / 1000
  52.     FOR x = 0 TO 1000
  53.         GlobalX = LX + x * dx
  54.         table(x) = VAL(Evaluate$(func$))
  55.         IF x = 0 THEN
  56.             LY = table(x): UY = table(x)
  57.         ELSE
  58.             IF table(x) < LY THEN LY = table(x)
  59.             IF table(x) > UY THEN UY = table(x)
  60.         END IF
  61.     NEXT
  62.     dy = (UY - LY) / 500
  63.  
  64.     clicks = (UX - LX) / 20
  65.     COLOR &HFF000000, &HFFFFFFFF: CLS
  66.     yCP 42, "Plot " + func$
  67.     yCP 640, "For x = " + xDP$(LX, 2) + " to " + xDP$(UX, 2) + " steps every " + xDP$(clicks, 2)
  68.     LINE (100, 100)-STEP(1000, 500), , B
  69.     FOR x = 0 TO 20
  70.         LINE (100 + x * 50, 595)-STEP(0, 10)
  71.     NEXT
  72.     IF 0 > LX AND 0 < UX THEN LINE ((0 - LX) / dx + 100, 100)-STEP(0, 500): _PRINTSTRING ((0 - LX) / dx + 100 - 12, 605), "x=0"
  73.  
  74.     drawscale LY, UY
  75.     'clicks = (UY - LY) / 10
  76.  
  77.     FOR x = 0 TO 1000
  78.         y = (table(x) - LY) / dy
  79.         'LINE (x + 99, 599 - y)-STEP(2, 2), &HFF0000FF, BF
  80.         PSET (x + 100, 600 - y), &HFF0000FF 'use a fine line because we have very large x Range
  81.     NEXT
  82.     WHILE _KEYDOWN(27) = 0
  83.         WHILE _MOUSEINPUT: WEND
  84.         mx = _MOUSEX: my = _MOUSEY
  85.         IF mx <= 1100 AND mx >= 100 THEN
  86.             IF my >= 100 AND my <= 600 THEN
  87.                 yCP 80, SPACE$(50)
  88.                 gx = (mx - 100) / 1000 * (UX - LX) + LX
  89.                 gy = (600 - my) / 500 * (UY - LY) + LY
  90.                 yCP 80, "X = " + xDP$(gx, 2) + ", Y = " + xDP$(gy, 2)
  91.             END IF
  92.         END IF
  93.         _DISPLAY
  94.         _LIMIT 200
  95.     WEND
  96.  
  97. SUB drawscale (LY, UY)
  98.     DIM clicks, p10 AS INTEGER, digit AS INTEGER, units AS SINGLE, midY, ty, gy, s$
  99.  
  100.     clicks = (UY - LY) / 10
  101.  
  102.     p10 = -10
  103.     WHILE ABS(clicks) > 10 ^ p10
  104.         p10 = p10 + 1
  105.     WEND
  106.     p10 = p10 - 1
  107.     digit = INT(clicks / (10 ^ p10))
  108.     units = digit * 10 ^ p10
  109.     'PRINT p10, digit, units
  110.  
  111.     LINE (100, 100)-STEP(1000, 500), , B
  112.     _PRINTSTRING (1105, 100 - 8), xDP$(UY, 2)
  113.     _PRINTSTRING (1105, 600 - 8), xDP$(LY, 2)
  114.     IF 0 > LY AND 0 < UY THEN
  115.         midY = pixY(0, LY, UY)
  116.         LINE (100, midY)-STEP(1000, 0): _PRINTSTRING (43, midY + -8), "F(x)=0"
  117.         ty = units
  118.         WHILE pixY(ty, LY, UY) >= 100 'from 0 + unit go up unit by unit
  119.             gy = pixY(ty, LY, UY)
  120.             LINE (95, gy)-STEP(10, 0)
  121.             s$ = RIGHT$(SPACE$(11) + xDP$(ty, 2), 11)
  122.             _PRINTSTRING (0, gy - 8), s$
  123.             ty = ty + units
  124.         WEND
  125.         ty = -units
  126.         WHILE pixY(ty, LY, UY) <= 600 'from 0 - unit go down unit by unit
  127.             gy = pixY(ty, LY, UY)
  128.             LINE (95, gy)-STEP(10, 0)
  129.             s$ = RIGHT$(SPACE$(11) + xDP$(ty, 2), 11)
  130.             _PRINTSTRING (0, gy - 8), s$
  131.             ty = ty - units
  132.         WEND
  133.     ELSE
  134.         ty = 0
  135.         WHILE pixY(ty, LY, UY) < 600 'find first unit below or at bottom of graph
  136.             ty = ty - units
  137.         WEND
  138.         ty = ty + units
  139.         ' now go up drawing marks
  140.         WHILE pixY(ty, LY, UY) >= 100
  141.             gy = pixY(ty, LY, UY)
  142.             IF gy <= 600 THEN
  143.                 LINE (95, gy)-STEP(10, 0)
  144.                 s$ = RIGHT$(SPACE$(11) + xDP$(ty, 2), 11)
  145.                 _PRINTSTRING (0, gy - 8), s$
  146.             END IF
  147.             ty = ty + units
  148.         WEND
  149.     END IF
  150.  
  151. FUNCTION pixY (graphY, lowY, UpY)
  152.     pixY = 600 - 500 * (graphY - lowY) / (UpY - lowY)
  153.  
  154. 'this preps e$ string for actual evaluation function and makes call to it,
  155. 'checks results for error returns that or string form of result calculation
  156. 'the new goal is to do string functions along side math
  157. FUNCTION Evaluate$ (e$)
  158.     DIM b$, c$
  159.     DIM i AS INTEGER, po AS INTEGER
  160.     ' isolateNeg = 0
  161.     b$ = "" 'rebuild string with padded spaces
  162.     'this makes sure ( ) + * / % ^ are wrapped with spaces, on your own with - sign
  163.     FOR i = 1 TO LEN(e$) 'filter chars and count ()
  164.         c$ = LCASE$(MID$(e$, i, 1))
  165.         IF c$ = ")" THEN
  166.             po = po - 1: b$ = b$ + " ) "
  167.         ELSEIF c$ = "(" THEN
  168.             po = po + 1: b$ = b$ + " ( "
  169.         ELSEIF INSTR("+*/%^", c$) > 0 THEN
  170.             b$ = b$ + " " + c$ + " "
  171.         ELSEIF c$ = "-" THEN
  172.             IF LEN(b$) > 0 THEN
  173.                 IF INSTR(".0123456789abcdefghijklmnopqrstuvwxyz)", RIGHT$(RTRIM$(b$), 1)) > 0 THEN
  174.                     b$ = b$ + " " + c$ + " "
  175.                 ELSE
  176.                     b$ = b$ + " " + c$
  177.                 END IF
  178.             ELSE
  179.                 b$ = b$ + " " + c$
  180.             END IF
  181.         ELSEIF INSTR(" .0123456789abcdefghijklmnopqrstuvwxyz<>=", c$) > 0 THEN
  182.             b$ = b$ + c$
  183.         END IF
  184.         IF po < 0 THEN EvalErr$ = "Too many )": EXIT FUNCTION
  185.     NEXT
  186.     IF po <> 0 THEN EvalErr$ = "Unbalanced ()": EXIT FUNCTION
  187.     REDIM ev(1 TO 1) AS STRING
  188.     Split b$, " ", ev()
  189.     FOR i = LBOUND(ev) TO UBOUND(ev) 'subst constants
  190.         IF ev(i) = "pi" THEN
  191.             ev(i) = LTRIM$(STR$(_PI))
  192.         ELSEIF ev(i) = "x" THEN
  193.             ev(i) = LTRIM$(STR$(GlobalX))
  194.         ELSEIF ev(i) = "e" THEN
  195.             ev(i) = LTRIM$(STR$(EXP(1)))
  196.         END IF
  197.     NEXT
  198.     c$ = evalW$(ev())
  199.     IF EvalErr$ <> "" THEN Evaluate$ = EvalErr$ ELSE Evaluate$ = c$
  200.  
  201.  
  202. ' the recursive part of EVAL
  203. FUNCTION evalW$ (a() AS STRING)
  204.     IF EvalErr$ <> "" THEN EXIT FUNCTION
  205.  
  206.     DIM fun$, test$, innerV$, m$, op$
  207.     DIM pop AS INTEGER, lPlace AS INTEGER, i AS INTEGER, rPlace AS INTEGER
  208.     DIM po AS INTEGER, p AS INTEGER, o AS INTEGER, index AS INTEGER
  209.     DIM recurs AS INTEGER
  210.     DIM innerVal AS _FLOAT, a AS _FLOAT, b AS _FLOAT
  211.     IF debug THEN
  212.         PRINT "evalW rec'd a() as:"
  213.         FOR i = LBOUND(a) TO UBOUND(a)
  214.             PRINT a(i); ", ";
  215.         NEXT
  216.         PRINT: INPUT "OK enter"; test$: PRINT
  217.     END IF
  218.     pop = find%(a(), "(") 'parenthesis open place
  219.     WHILE pop > 0
  220.         IF pop = 1 THEN
  221.             fun$ = "": lPlace = 1
  222.         ELSE
  223.             test$ = a(pop - 1)
  224.             IF find%(fList(), test$) > 0 THEN
  225.                 fun$ = test$: lPlace = pop - 1
  226.             ELSE
  227.                 fun$ = "": lPlace = pop
  228.             END IF
  229.         END IF
  230.         po = 1
  231.         FOR i = pop + 1 TO UBOUND(a)
  232.             IF a(i) = "(" THEN po = po + 1
  233.             IF a(i) = ")" THEN po = po - 1
  234.             IF po = 0 THEN rPlace = i: EXIT FOR
  235.         NEXT
  236.         REDIM inner(1 TO 1) AS STRING: index = 0: recurs = 0
  237.         FOR i = (pop + 1) TO (rPlace - 1)
  238.             index = index + 1
  239.             REDIM _PRESERVE inner(1 TO index) AS STRING
  240.             inner(index) = a(i)
  241.             IF find%(oList(), a(i)) > 0 THEN recurs = -1
  242.         NEXT
  243.         IF recurs THEN innerV$ = evalW$(inner()) ELSE innerV$ = a(pop + 1)
  244.         innerVal = VAL(innerV$)
  245.  
  246.         SELECT CASE fun$
  247.             CASE "": m$ = innerV$
  248.             CASE "abs": m$ = ls$(ABS(innerVal))
  249.             CASE "int": m$ = ls$(INT(innerVal))
  250.             CASE "sin": IF DFlag THEN m$ = ls$(SIN(RAD * innerVal)) ELSE m$ = ls$(SIN(innerVal))
  251.             CASE "cos": IF DFlag THEN m$ = ls$(COS(RAD * innerVal)) ELSE m$ = ls$(COS(innerVal))
  252.             CASE "tan": IF DFlag THEN m$ = ls$(TAN(RAD * innerVal)) ELSE m$ = ls$(TAN(innerVal))
  253.             CASE "asin": IF DFlag THEN m$ = ls$(_ASIN(RAD * innerVal)) ELSE m$ = ls$(_ASIN(innerVal))
  254.             CASE "acos": IF DFlag THEN m$ = ls$(_ACOS(RAD * innerVal)) ELSE m$ = ls$(_ACOS(innerVal))
  255.             CASE "atan": IF DFlag THEN m$ = ls$(ATN(RAD * innerVal)) ELSE m$ = ls$(ATN(innerVal))
  256.             CASE "log"
  257.                 IF innerVal > 0 THEN
  258.                     m$ = ls$(LOG(innerVal))
  259.                 ELSE
  260.                     EvalErr$ = "LOG only works on numbers > 0.": EXIT FUNCTION
  261.                 END IF
  262.             CASE "exp" 'the error limit is inconsistent in JB
  263.                 IF -745 <= innerVal AND innerVal <= 709 THEN 'your system may have different results
  264.                     m$ = ls$(EXP(innerVal))
  265.                 ELSE
  266.                     'what the heck???? 708 works fine all alone as limit ?????
  267.                     EvalErr$ = "EXP(n) only works for n = -745 to 709.": EXIT FUNCTION
  268.                 END IF
  269.             CASE "sqr"
  270.                 IF innerVal >= 0 THEN
  271.                     m$ = ls$(SQR(innerVal))
  272.                 ELSE
  273.                     EvalErr$ = "SQR only works for numbers >= 0.": EXIT FUNCTION
  274.                 END IF
  275.             CASE "rad": m$ = ls$(innerVal * RAD)
  276.             CASE "deg": m$ = ls$(innerVal * DEG)
  277.             CASE ELSE: EvalErr$ = "Unidentified function " + fun$: EXIT FUNCTION
  278.         END SELECT
  279.         IF debug THEN
  280.             PRINT "lPlace, rPlace"; lPlace, rPlace
  281.         END IF
  282.         arrSubst a(), lPlace, rPlace, m$
  283.         IF debug THEN
  284.             PRINT "After arrSubst a() is:"
  285.             FOR i = LBOUND(a) TO UBOUND(a)
  286.                 PRINT a(i); " ";
  287.             NEXT
  288.             PRINT: PRINT
  289.         END IF
  290.         pop = find%(a(), "(")
  291.     WEND
  292.  
  293.     'all parenthesis cleared
  294.     'ops$ = "% ^ / * + - = < > <= >= <> and or not" 'all () cleared, now for binary ops (not not binary but is last!)
  295.     FOR o = 1 TO 15
  296.         op$ = oList(o)
  297.         p = find%(a(), op$)
  298.         WHILE p > 0
  299.             a = VAL(a(p - 1))
  300.             b = VAL(a(p + 1))
  301.             IF debug THEN
  302.                 PRINT STR$(a) + op$ + STR$(b)
  303.             END IF
  304.             SELECT CASE op$
  305.                 CASE "%"
  306.                     IF b >= 2 THEN
  307.                         m$ = ls$(INT(a) MOD INT(b))
  308.                     ELSE
  309.                         EvalErr$ = "For a Mod b, b value < 2."
  310.                         EXIT FUNCTION
  311.                     END IF
  312.                 CASE "^"
  313.                     IF INT(b) = b OR a >= 0 THEN
  314.                         m$ = ls$(a ^ b)
  315.                     ELSE
  316.                         EvalErr$ = "For a ^ b, a needs to be >= 0 when b not integer."
  317.                         EXIT FUNCTION
  318.                     END IF
  319.                 CASE "/"
  320.                     IF b <> 0 THEN
  321.                         m$ = ls$(a / b)
  322.                     ELSE
  323.                         EvalErr$ = "Div by 0"
  324.                         EXIT FUNCTION
  325.                     END IF
  326.                 CASE "*": m$ = ls$(a * b)
  327.                 CASE "-": m$ = ls$(a - b)
  328.                 CASE "+": m$ = ls$(a + b)
  329.                 CASE "=": IF a = b THEN m$ = "-1" ELSE m$ = "0"
  330.                 CASE "<": IF a < b THEN m$ = "-1" ELSE m$ = "0"
  331.                 CASE ">": IF a > b THEN m$ = "-1" ELSE m$ = "0"
  332.                 CASE "<=": IF a <= b THEN m$ = "-1" ELSE m$ = "0"
  333.                 CASE ">=": IF a >= b THEN m$ = "-1" ELSE m$ = "0"
  334.                 CASE "<>": IF a <> b THEN m$ = "-1" ELSE m$ = "0"
  335.                 CASE "and": IF a <> 0 AND b <> 0 THEN m$ = "-1" ELSE m$ = "0"
  336.                 CASE "or": IF a <> 0 OR b <> 0 THEN m$ = "-1" ELSE m$ = "0"
  337.                 CASE "not": IF b = 0 THEN m$ = "-1" ELSE m$ = "0" 'use b as nothing should be left of not
  338.             END SELECT
  339.             arrSubst a(), p - 1, p + 1, m$
  340.  
  341.             IF debug THEN
  342.                 PRINT "a() reloaded after " + op$ + " as:"
  343.                 FOR i = LBOUND(a) TO UBOUND(a)
  344.                     PRINT a(i); ", ";
  345.                 NEXT
  346.                 PRINT: PRINT
  347.             END IF
  348.  
  349.             p = find%(a(), op$)
  350.         WEND
  351.     NEXT
  352.     fun$ = ""
  353.     FOR i = LBOUND(a) TO UBOUND(a)
  354.         fun$ = fun$ + " " + a(i)
  355.     NEXT
  356.     evalW$ = LTRIM$(fun$)
  357.  
  358. SUB arrSubst (a() AS STRING, substLow AS LONG, substHigh AS LONG, subst AS STRING)
  359.     DIM i AS LONG, index AS LONG
  360.     a(substLow) = subst: index = substLow + 1
  361.     FOR i = substHigh + 1 TO UBOUND(a)
  362.         a(index) = a(i): index = index + 1
  363.     NEXT
  364.     REDIM _PRESERVE a(LBOUND(a) TO UBOUND(a) + substLow - substHigh)
  365.  
  366. 'notes: REDIM the array(0) to be loaded before calling Split '<<<<<<<<<<<<<<<<<<<<<<< IMPORTANT!!!!
  367. SUB Split (mystr AS STRING, delim AS STRING, arr() AS STRING)
  368.     ' bplus modifications of Galleon fix of Bulrush Split reply #13
  369.     ' http://www.[abandoned, outdated and now likely malicious qb64 dot net website - don’t go there]/forum/index.php?topic=1612.0
  370.     ' this sub further developed and tested here: \test\Strings\Split test.bas
  371.     ' 2018-09-16 modified for base 1 arrays
  372.     DIM copy AS STRING, p AS LONG, curpos AS LONG, arrpos AS LONG, lc AS LONG, dpos AS LONG
  373.     copy = mystr 'make copy since we are messing with mystr
  374.     'special case if delim is space, probably want to remove all excess space
  375.     IF delim = " " THEN
  376.         copy = RTRIM$(LTRIM$(copy))
  377.         p = INSTR(copy, "  ")
  378.         WHILE p > 0
  379.             copy = MID$(copy, 1, p - 1) + MID$(copy, p + 1)
  380.             p = INSTR(copy, "  ")
  381.         WEND
  382.     END IF
  383.     REDIM arr(1 TO 1) 'clear it
  384.     curpos = 1
  385.     arrpos = 1
  386.     lc = LEN(copy)
  387.     dpos = INSTR(curpos, copy, delim)
  388.     DO UNTIL dpos = 0
  389.         arr(arrpos) = MID$(copy, curpos, dpos - curpos)
  390.         arrpos = arrpos + 1
  391.         REDIM _PRESERVE arr(1 TO arrpos + 1) AS STRING
  392.         curpos = dpos + LEN(delim)
  393.         dpos = INSTR(curpos, copy, delim)
  394.     LOOP
  395.     arr(arrpos) = MID$(copy, curpos)
  396.     REDIM _PRESERVE arr(1 TO arrpos) AS STRING
  397.  
  398. 'assume a() is base 1 array so if find comes back as 0 then found nothing
  399. FUNCTION find% (a() AS STRING, s$)
  400.     DIM i%
  401.     FOR i% = LBOUND(a) TO UBOUND(a)
  402.         IF a(i%) = s$ THEN find% = i%: EXIT FUNCTION
  403.     NEXT
  404.  
  405. 'ltrim a number float
  406.     ls$ = LTRIM$(STR$(n))
  407.  
  408. SUB yCP (y, s$) 'for xmax pixel wide graphics screen Center Print at pixel y row
  409.     _PRINTSTRING ((_WIDTH - LEN(s$) * 8) / 2, y), s$
  410.  
  411. FUNCTION xDP$ (x, DP)
  412.     DIM xx
  413.     xx = x + .5 * 10 ^ -DP
  414.     xDP$ = _TRIM$(STR$(INT(xx * 10 ^ DP) / 10 ^ DP))
  415.  
  416.  

plot abs(x).PNG
* plot abs(x).PNG (Filesize: 17.3 KB, Dimensions: 1203x730, Views: 174)
« Last Edit: September 20, 2019, 12:30:28 am by bplus »

Offline SierraKen

  • Forum Resident
  • Posts: 1454
    • View Profile
Re: Plot
« Reply #12 on: September 19, 2019, 10:10:16 pm »
Very nice b+! Now it's a plotting calculator :). Good job.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Plot
« Reply #13 on: September 20, 2019, 01:00:50 am »
Hi Ken,

I am glad it is working for you. Now I have the idea to make it a programmable plotting calculator :D

I've never worked with one! But I have already used evaluate code for a little interpreter so how hard can it be? ;)

Offline SierraKen

  • Forum Resident
  • Posts: 1454
    • View Profile
Re: Plot
« Reply #14 on: September 20, 2019, 01:25:09 pm »
Cool B+, yeah honestly I've never used one either. But so far I think this program is very similar. Plotting calculators were a little bit after my time in school. :)