plotter.py

Created by laigna

Created on March 14, 2026

5.14 KB

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()

During your visit to our site, NumWorks needs to install "cookies" or use other technologies to collect data about you in order to:

With the exception of Cookies essential to the operation of the site, NumWorks leaves you the choice: you can accept Cookies for audience measurement by clicking on the "Accept and continue" button, or refuse these Cookies by clicking on the "Continue without accepting" button or by continuing your browsing. You can update your choice at any time by clicking on the link "Manage my cookies" at the bottom of the page. For more information, please consult our cookies policy.