11 functions with pixel-by-pixel plotting, auto-scaling Y axis, pan/zoom controls, grid and axes
# Function Plotter - NumWorks # Created by Alvar Laigna - https://alvarlaigna.com # Arrows:pan/zoom OK:choose Back:back from kandinsky import fill_rect as F,draw_string as D from ion import keydown as K from time import sleep as Z from math import pi,sqrt,sin,cos,tan,log,exp SW,SH=320,222 BK=(0,)*3;WH=(255,)*3 BL=(50,110,230);TL=(0,190,190) YL=(220,200,60);GN=(60,190,60) DG=(120,)*3 GR=(40,40,40);LG=(30,30,30) okv=1 if K(4)or K(52) else 0 def okd(): return K(4)or K(52) def okp(): global okv d=okd() if d and not okv: okv=1 return True if not d:okv=0 return False def wup(): while okd():Z(0.02) Z(0.12) # Plot area PL,PT=10,2 PW,PH=300,190 PB=PT+PH # Function definitions def f_x2(x):return x*x def f_x3(x):return x*x*x def f_sin(x):return sin(x) def f_cos(x):return cos(x) def f_tan(x): c=cos(x) if abs(c)<1e-10:return None return tan(x) def f_inv(x): if abs(x)<1e-10:return None return 1.0/x def f_sqrt(x): if x<0:return None return sqrt(x) def f_exp2(x):return 2.0**x def f_abs(x):return abs(x) FNS=[ ("x^2",f_x2), ("x^3",f_x3), ("sin(x)",f_sin), ("cos(x)",f_cos), ("tan(x)",f_tan), ("1/x",f_inv), ("sqrt(x)",f_sqrt), ("2^x",f_exp2), ("|x|",f_abs), ("ax2+bx+c",None), ("a*sin(bx+c)",None), ] def get_params(kind): F(0,0,SW,SH,BK) if kind==0: D("Custom Quadratic",10,10,TL,BK) D("y = ax^2 + bx + c",10,30,WH,BK) else: D("Custom Sine Wave",10,10,TL,BK) D("y = a*sin(bx + c)",10,30,WH,BK) D("Enter a:",10,60,YL,BK) a=input() D("Enter b:",10,80,YL,BK) b=input() D("Enter c:",10,100,YL,BK) c=input() try: a,b,c=float(a),float(b),float(c) except: a,b,c=1.0,0.0,0.0 return a,b,c def make_quad(a,b,c): def fn(x):return a*x*x+b*x+c return fn def make_asin(a,b,c): def fn(x):return a*sin(b*x+c) return fn def draw_axes(xn,xx,yn,yx): # Grid lines ix=1 rng=xx-xn if rng>20:ix=5 elif rng>10:ix=2 iy=ix # Vertical grid gx=int(xn)-1 while gx<=int(xx)+1: if gx%ix==0: px=PL+int((gx-xn)*PW/(xx-xn)) if PL<=px<PL+PW: for yy in range(PT,PB,2): F(px,yy,1,1,LG) gx+=1 # Horizontal grid gy=int(yn)-1 while gy<=int(yx)+1: if gy%iy==0: py=PB-int((gy-yn)*PH/(yx-yn)) if PT<=py<PB: for xx2 in range(PL,PL+PW,2): F(xx2,py,1,1,LG) gy+=1 # X axis if yn<=0<=yx: ay=PB-int((0-yn)*PH/(yx-yn)) if PT<=ay<PB: F(PL,ay,PW,1,DG) # Y axis if xn<=0<=xx: ax=PL+int((0-xn)*PW/(xx-xn)) if PL<=ax<PL+PW: F(ax,PT,1,PH,DG) # Origin label if xn<=0<=xx and yn<=0<=yx: ox=PL+int((0-xn)*PW/(xx-xn)) oy=PB-int((0-yn)*PH/(yx-yn)) if PL+5<=ox<PL+PW-10 and PT+2<=oy<PB-12: D("0",ox+2,oy+1,DG,BK) def plot(fn,name,xn,xx,yn,yx): F(0,0,SW,SH,BK) draw_axes(xn,xx,yn,yx) # Plot curve pr=-1 for i in range(PW): x=xn+i*(xx-xn)/PW try: y=fn(x) if y is None: pr=-1 continue py=PB-int((y-yn)*PH/(yx-yn)) if PT<=py<PB: F(PL+i,py,2,2,TL) if pr>=0 and abs(py-pr)>2 and abs(py-pr)<PH: mn=min(py,pr);mx=max(py,pr) mn=max(mn,PT);mx=min(mx,PB-1) for yf in range(mn,mx+1): F(PL+i,yf,1,1,TL) pr=py else: pr=-1 except: pr=-1 # Info bar F(0,PB+1,SW,SH-PB-1,BK) D("y="+name,5,PB+4,YL,BK) rx="x:["+str(round(xn,1))+","+str(round(xx,1))+"]" D(rx,160,PB+4,DG,BK) D("Arrows:pan/zoom Back:menu",5,PB+18,DG,BK) def auto_y(fn,xn,xx): mn=1e9;mx=-1e9 for i in range(PW): x=xn+i*(xx-xn)/PW try: y=fn(x) if y is None:continue if abs(y)>1e6:continue if y<mn:mn=y if y>mx:mx=y except: pass if mn>=mx:mn=-5;mx=5 mg=(mx-mn)*0.15 if mg<0.5:mg=0.5 return mn-mg,mx+mg def viewer(fn,name): xn,xx=-5.0,5.0 yn,yx=auto_y(fn,xn,xx) plot(fn,name,xn,xx,yn,yx) wup() while True: mv=False dx=(xx-xn)*0.15 if K(0): xn-=dx;xx-=dx;mv=True if K(3): xn+=dx;xx+=dx;mv=True if K(1): cx=(xn+xx)/2;cy=(yn+yx)/2 hw=(xx-xn)*0.35;hh=(yx-yn)*0.35 xn=cx-hw;xx=cx+hw yn=cy-hh;yx=cy+hh mv=True if K(2): cx=(xn+xx)/2;cy=(yn+yx)/2 hw=(xx-xn)*0.65;hh=(yx-yn)*0.65 xn=cx-hw;xx=cx+hw yn=cy-hh;yx=cy+hh mv=True if K(17): Z(0.2);return if mv: plot(fn,name,xn,xx,yn,yx) Z(0.12) Z(0.03) def menu(): items=["x^2","x^3","sin(x)","cos(x)","tan(x)", "1/x","sqrt(x)","2^x","|x|","ax2+bx+c","a*sin(bx+c)"] N=len(items) sel=0 sc=0 VIS=9 def draw_menu(): F(0,0,SW,SH,BK) D("Function Plotter",60,5,TL,BK) D("Select function:",10,28,DG,BK) for i in range(VIS): idx=sc+i if idx>=N:break y=45+i*19 if idx==sel: F(5,y-1,310,18,BL) D("y = "+items[idx],15,y,WH,BL) else: D("y = "+items[idx],15,y,WH,BK) D("OK:select Back:quit",60,SH-16,DG,BK) draw_menu() wup() while True: r=False if K(2): if sel<N-1: sel+=1 if sel>=sc+VIS:sc+=1 r=True if K(1): if sel>0: sel-=1 if sel<sc:sc-=1 r=True if okp(): wup() if sel<9: fn=FNS[sel][1] nm=items[sel] viewer(fn,nm) draw_menu();wup() elif sel==9: a,b,c=get_params(0) fn=make_quad(a,b,c) nm=str(a)+"x2+"+str(b)+"x+"+str(c) viewer(fn,nm) draw_menu();wup() elif sel==10: a,b,c=get_params(1) fn=make_asin(a,b,c) nm=str(a)+"sin("+str(b)+"x+"+str(c)+")" viewer(fn,nm) draw_menu();wup() if K(17): Z(0.2);return if r: draw_menu() Z(0.15) Z(0.03) def run(): menu() run()