Привет, Хабр! Я хотел бы рассказать, как написать программу для построения графиков функций. Программу будем писать на 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 бота. Если будет интересно, то я могу написать об этом. Так как я заметил, что на Хабре есть только ознакомительные статьи про ботов.