Pull to refresh

Построение графиков функций на Python. Pygame

Привет, Хабр! Я хотел бы рассказать, как написать программу для построения графиков функций. Программу будем писать на Python с использованием библиотеки PyGame.

P.S. Программа простая и предназначена больше для учебных целей, чем для повседневного использования(хотя и с этим алгоритм может справится).

Основной алгоритм

Основной алгоритм заключается в вычислении функции на интервале: [-10,10]. Так как целых значений немного, то будем увеличивать параметр с малым шагом, к примеру 0.0001.

Функция имеет вид y = f(x). Формат ввода, соответственно, string(так как пользователь вводить функцию с использованием символов).

Получается следующий алгоритм:

Пишем код

Для начала следует импортировать библиотеки. Это библиотека PyGame, с её помощью будем рисовать. И библиотека math, она добавляет больше математических функций(sin, cos, sqrt и т.д.).

from math import *
import pygame 

Теперь напишем несколько системных строк, которые создадут нам окно, в котором будет находится система координат.

pygame.init() # запустить pygame
screen = pygame.display.set_mode((500,500)) # создать окно 500x500
pygame.display.set_caption("func_graph") # название окна
all_sprites=pygame.sprite.Group() # группа спрайтов  
clock = pygame.time.Clock() 
running = True
fps=60

Создадим класс, который будет рисовать координатные оси(ординат и абсцисс).

class Line(pygame.sprite.Sprite):
    def __init__(self,pos,x,y):
        pygame.sprite.Sprite.__init__(self)
        if pos=="x": # ось абсцисс
            self.image=pygame.Surface((3,400)) # линия 3x400
            self.image.fill((0,0,0)) # закрасить черным цветом
            self.rect = self.image.get_rect()
            self.rect.centerx = x # центр по x
            self.rect.centery = y # центр по y
        elif pos=="y": # ось ординат 
            self.image=pygame.Surface((400,3)) # линия 3x400
            self.image.fill((0,0,0)) # закрасить черным цветом
            self.rect = self.image.get_rect()
            self.rect.centerx = x # центр по x
            self.rect.centery = y # центр по y

Теперь класс для точки, так как точек будет очень много, то график будет казаться сплошным.

class Dot(pygame.sprite.Sprite):
    def __init__(self,x,y):
        pygame.sprite.Sprite.__init__(self)
        self.image=pygame.surface.Surface((5,5)) # точка 5x5
        self.image.fill((0,0,0)) # закрасить черным цветом
        self.rect=self.image.get_rect()
        self.rect.centerx=x # центр по x
        self.rect.centery=y # центр по y

Теперь приступим к основному алгоритму. Функцию считает модуль eval, а с ним нужно быть очень аккуратным в плане ввода(т.е. "x" желательно брать в скобки, чтобы модуль учитывал знак минус, степень - это два знака умножить(**), и т.д.). Конечно, можно сделать так, чтобы ввод пользователя преобразовывался в нормальный вид, это аналогично части с заменой "x" на значение(строка 6), но и тут есть свои проблемы.

def Calc(func):
    i=-10 # начальное значение аргумента
    while i<=10: # пока аргумент меньше 10
        mass="" # темп-строка
        for j in func: # для каждого символа в строке func(наша функция)
            if j == "x": # если символ = x, то добавляем i в темп-строку
                mass+=str(i)
            else: # если нет, то добавить исходный символ
                mass+=j
            i+=0.0001 # увеличить аргумент на 0.0001
        try:
          res1=eval(mass) # посчитать функцию и получить результат
        except:
          res1=10000 # если функцию нельзя посчитать, то результат число вне координат(знаю, костыль)
        dot=Dot(250+i*10,250-res1*10) # dot - точка с координатой(0+x,0+y), так как это дисплей, то вектор "y" направлен вниз
        all_sprites.add(dot) # добавить точку в группу спрайтов

Теперь заключительная часть кода.

func = str(input("y = ")) # ввод данных 
calc = Calc(func) # вызвать функцию Calc от func

line = Line("y",250,250) # добавить ось ординат
all_sprites.add(line)
line1 = Line("x",250,250) # добавить ось абсцисс
all_sprites.add(line1)

while running: # основной цикл
    clock.tick(fps)
    for event in pygame.event.get():
        if event.type == pygame.QUIT: # если закрыл окно - завершить программу
            running = False
    screen.fill((255,255,255)) # залить поле белым цветом
    all_sprites.draw(screen) # нарисовать все спрайты(т.е. наши точки)
    pygame.display.flip()
pygame.quit()

Вот такой получился код:

from math import *
import pygame 

pygame.init()
screen = pygame.display.set_mode((500,500))
pygame.display.set_caption("func_graph")
all_sprites=pygame.sprite.Group()
clock = pygame.time.Clock()
running = True
fps=60

class Line(pygame.sprite.Sprite):
    def __init__(self,pos,x,y):
        pygame.sprite.Sprite.__init__(self)
        if pos=="x":
            self.image=pygame.Surface((3,400))
            self.image.fill((0,0,0))
            self.rect = self.image.get_rect()
            self.rect.centerx = x
            self.rect.centery = y
        elif pos=="y":
            self.image=pygame.Surface((400,3))
            self.image.fill((0,0,0))
            self.rect = self.image.get_rect()
            self.rect.centerx = x
            self.rect.centery = y
            
class Dot(pygame.sprite.Sprite):
    def __init__(self,x,y):
        pygame.sprite.Sprite.__init__(self)
        self.image=pygame.surface.Surface((5,5))
        self.image.fill((0,0,0))
        self.rect=self.image.get_rect()
        self.rect.centerx=x
        self.rect.centery=y
            
def Calc(func):
    i=-10
    while i<=10:
        mass=""
        for j in func:
            if j == "x":
                mass+=str(i)
            else:
                mass+=j
            i+=0.0001
        try:
          res1=eval(mass)
        except:
          res1=10000
        dot=Dot(250+i*10,250-res1*10)
        all_sprites.add(dot)

func = str(input("y = "))
calc = Calc(func)

line = Line("y",250,250)
all_sprites.add(line)
line1 = Line("x",250,250)
all_sprites.add(line1)

while running:
    clock.tick(fps)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    screen.fill((255,255,255))
    all_sprites.draw(screen)
    pygame.display.flip()
pygame.quit()

Итоги

В заключение можно сказать, что получилась простая программа, которая справляется со своими функциями, Ба-Дум-Тсс. Конечно, здесь есть свои проблемы(в основном - модуль eval), но главная задача статьи - понять, как компьютер строит графики.

P.S. Код ещё можно доработать. К примеру, я доработал программу до discord бота. Если будет интересно, то я могу написать об этом. Так как я заметил, что на Хабре есть только ознакомительные статьи про ботов.

Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.