Plot functions/Waves

hondros 0 Tallied Votes 235 Views Share

This is a nice command prompt I wrote up a month ago. It's still in the Beta stages however. Anyways, here's a brief overlay of what it does:
1) Takes user input of a function, creates an x/y table and plots it
2) Change the x-range
3) Graph waves (Main thing I wanted to do)

Okay, here's how you use the program:
When ran, you'll enter a prompt:
y = ...
You can type the following commands it knows:
1) help():
- Prints out to the terminal what functions it knows
2) frange(start, end, step):
- Defines the x range. Step can be a float number ie (0.5); useful for the waves because you don't want to see a huge graph of waves; default x range is -100 - 100
3) set(radian):
- Just converts the x-range into a radian range. Takes no arguments
4) polar():
- Change the graph to a polar graph
5) line():
- Change the graph to a line graph (default)
6) show():
- Show the graph

It is beneficial to follow these steps to get it to show correctly:
optional(1) y = frange(0, 10, 0.01))
optional(2) y = line()) or (2) y = polar())
3) show()
4) any functions

All functions have to be completely typed out
Such as the y = 2*(x**2)
Nothing is implied as of now

Sometimes, it'll say that a functions is not recognized even though it already performed it. Don't know why as of yet; if I change the if statements to else if statements, it still does it

from numpy import *
from pylab import *

#http://paws.kettering.edu/~drussell/Demos/superposition/superposition.html
#http://catcode.com/trig/trig10.html

def frange(start, end=None, inc=None):
    "A range function, that does accept float increments..."

    if end == None:
        end = start + 0.0
        start = 0.0

    if inc == None:
        inc = 1.0

    L = []
    while 1:
        next = start + len(L) * inc
        if inc > 0 and next >= end:
            break
        elif inc < 0 and next <= end:
            break
        L.append(next)
        
    return L

def funceval(function, numlist=frange(-100, 100, 0.01), splitat='x'):
    y = []
    for x in numlist:
        try:
            numfunction = ''
            for c in function.split(splitat)[:-1]:
                numfunction += c
                numfunction += '(%f)' %(x)
            numfunction += function.split(splitat)[-1]
            y.append(eval(numfunction))
        except:
            numlist.pop(numlist.index(x))
    return y

def ranradians(ran):
    a = []
    for x in ran:
        a.append(radians(x))
    return a

def menuhelp():
    print("Function Plotter v2.0")
    print("Help")
    print("")
    print("Functions can be in any form, so long as there is an 'x'")
    print("Standard form for parabola's typing in the function is:")
    print("A*(x**2)+B*x+C")
    print("Carry this form to any function")
    print("Make sure to type in every symbol, none are implied")
    print("Pi is: pi")
    print("Using sine or similar functions: sin(x)")
    print("""Functions usable are:
'abs'
'absolute'
'angle'
'sqrt'
'log'
'sin'
'sinh'
'arcsin'
'cos'
'cosh'
'arccos'
'tan'
'tanh'
'arctan'
""")
    print("Sawtooth wave form:")
    print("arctan(tan(x))")
    print("Triangle wave form:")
    print("arcsin(sin(x))")
    print("Type 'polar' to change to a circle graph")
    print("Type 'line' to change to a line graph")
    print("For further help, please look at the source code to figure it out,")
    print("Or, e-mail me at: alexpolosky@gmail.com")

def polarmenu(ran=frange(-100, 100, 0.01), once=False):
    if once != True:
        print("Function Plotter v2.0")
        print("~Hondros")
        print("Type in a function, or type 'help' for function commands")
        print("Type frange(x, y) for a specific x range)")
        print("Type set(radian) to convert list of numbers to radians (for sin)")
        print("Type show() to show the plot window")
        print("example: 2*sin(x)")
        print("")
    while True:
        try:
            f = raw_input("y = ")
            if f == 'help':
                menuhelp()
            if f[0:6] == 'frange':
                menu(eval(f), True)
            if f == 'set(radian)':
                menu(ranradians(ran), True)
            if f == 'show':
                show()
            if f == 'polar':
                polarmenu(ran, True)
            if f == 'line':
                menu(ran, True)
            else:
                y = funceval(f, ran)
                polar(y, ran)
        except:
            print("The function %s is invalid" %(f))

def menu(ran=frange(-100, 100, 0.01), once=False):
    if once != True:
        print("Function Plotter v2.0")
        print("~Hondros")
        print("Type in a function, or type 'help' for function commands")
        print("Type frange(x, y) for a specific x range)")
        print("Type set(radian) to convert list of numbers to radians (for sin)")
        print("Type show() to show the plot window")
        print("example: 2*sin(x)")
        print("")
    while True:
        try:
            f = raw_input("y = ")
            if f == 'help':
                menuhelp()
            if f[0:6] == 'frange':
                menu(eval(f), True)
            if f == 'set(radian)':
                menu(ranradians(ran), True)
            if f == 'show':
                show()
            if f == 'polar':
                polarmenu(ran, True)
            if f == 'line':
                menu(ran, True)
            else:
                y = funceval(f, ran)
                plot(ran, y)
        except:
            print("The function %s is invalid" %(f))

menu()

Dani AI

Generated

Nice little CLI plotter from ; a few focused changes will make it far more reliable and easier to extend.

The "function not recognized" symptom is almost certainly caused by the menu having many independent if statements with a single else attached only to the last if. Handled commands still fall through and hit the else branch, so strings like help get sent to the evaluator and produce the error. Replace the chain with if / elif / else or use early continue after handling. Example dispatch pattern:

f = input("y = ").strip()
if f == 'help':
    menuhelp()
    continue
elif f.startswith('frange'):
    menu(eval(f), True)
elif f == 'set(radian)':
    menu(ranradians(ran), True)
elif f == 'show':
    show()
else:
    y = funceval_safe(f, ran)
    plot(ran, y)

The evaluator logic is brittle: splitting on 'x' breaks identifiers (e.g. exp(x)), and mutating the x-list while iterating is unsafe. Use a restricted eval namespace and pass x as a variable (or evaluate with x as a NumPy array for vectorized speed). Example safe evaluator:

import numpy as np
SAFE = {'sin': np.sin, 'cos': np.cos, 'pi': np.pi, 'sqrt': np.sqrt, 'abs': np.abs}

def funceval_safe(expr, xs):
    ys = []
    for x in xs:
        try:
            ys.append(eval(expr, {"__builtins__": None}, dict(SAFE, x=x)))
        except Exception:
            ys.append(np.nan)
    return np.array(ys)

Other practical improvements: stop mutating lists during iteration; use numpy.arange/linspace (or np.deg2rad for radians); avoid from numpy import * and from pylab import *—use import numpy as np and import matplotlib.pyplot as plt; print tracebacks (via traceback.print_exc()) or use the logging module to capture where errors occur; for polar plots, use ax = plt.subplot(projection='polar') and ax.plot(theta, r). Modernizing to Python 3 requires changing raw_input to input. Packaging the logic into a small class or using Python’s cmd module will make adding a history/log and clearer error reporting straightforward.

hondros 25 Junior Poster

Stuff I want to work on:
1) Fix the (functions not valid) problem;
2) Change this whole thing into a class (simple enough);
3) Create a log of functions you make;
4) Show where the error was in your program

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.