Эпитрохоида, построение функций на pygame в python
Проблема:
- У меня linux open suse leap ~15 + idle3 + pygame. Работаю с рядами Фурье и эпициклами, эпитрохоидами и в том же духе и так далее. Но все что я нашел это формулу построения.
- Мне нужно как-то реализовать на питоне параметрическое уравнение
тригонометрического ряда Фурье
с 3 и более окружностями. У меня пока 2. - Все что я сделал, так это реализация на python с либой pygame. Управление: cтрелочки,enter, w,s,d,a,f. Для увеличени или уменьшения на
0.01
Все параметры выстроены так, чтобы не равны были 0. Иначе сбой.
Полезно:
Будет не лишним:
До нормализации добавить выравнивание по Безье или Лагранжу я это пока не успел.
Вот мой код:
#authors: Arthur Alunts
#object:Trochoid, epicycles.
#Если запустить без En раскладки клавы то не будет работать.
# Нужно:
# №1) Cделать экстраполирование по Лагранжу или Безье.
#Короче: Все что нужно это менять константы.
#Со строчки 130 в цикле: Здесь изменяем переменные K0, R0, r0, в цикле, если хочешь инактивируй и напиши чтобы они изменяются согасно частотам**.
import pygame
import math
import time
import random
import sys
#sys.path.append("./My_Lagranzh_Interpolation")
#from L import A
#print("from Lib: ", A.Z1)
# Вот параметры которые нужно менять но менять их нкужно в цикле.
Timee= 33.0 #Задержка в милисекундах (влияет на скорость).
N= 400 #Число Эйлера.
I= 0 #Номер итерации.
Size0=800 #Размер фигуры0.
Maxim0= 8.1 #Пик изменения в цикле.(влияет на скорость).
Minim0= 1.1
A0 = 1.741
A1 = 1.001 #Радиус 1.
R0 = 1.001 #Радиус 1.
r0 = 2.001 #Радиус 1.
h0 = 3.001
dd=0.01 #Шаг0 изменения.
Sadness=2 #Толщина линии.
delta1= 22.0 #Положение на экране для первой фигуры.
delta0= 22.0 #Положение на экране для второй фигуры.
game_over = False #Создаём переменную, которая поможет нам контролировать выход.
count=0 #Просто некчемная переменная.
F0000=1 #Просто некчемная переменная.
Const0= 3.14 #Просто число пи.
#Зададим размер массива числом N.
#Заполним массивы размером N нулевыми значениями.
x= [0.0]*(N) #x для первой фигуры.
y= [0.0]*(N) #y для первой фигуры.
X= [0.0]*N #X уже нормализованной фигуры.
Y= [0.0]*N #Y уже нормализованной фигуры.
XL= [0.0]*N #x уже для нормализованной фигуры.
YL= [0.0]*N #y уже нормализованной фигуры.
#Заполним массивы X и Y, а также массивы XL и YL.
#tt=[0.0]*(N0000+1)#Угол.
#Главный рассчет переменных: Переменные для контроля которые ты должен изменять я добавил в конце ***.
#Некоторые переменные не должны быть равны нулю, или будет сбой.
pygame.init()# Это нужно просто для запуска графики скорее всего нативная фигня.
YY=1012
XX=1012
dis=pygame.display.set_mode((YY, XX))#`од.
#w, h = pygame.display.get_surface().get_size()
#print((w,h))
#pygame.draw.line(dis, ChangeColor(), [(11, 12), (122, 22)], 20) .
pygame.display.set_caption("Arthuricus's interface" ) #Добавляем название игры.
font1= pygame.font.SysFont("None", 66)
#font1 = pygame.font.Font(None, 50)#Задаем шрифт.
#pygame.font.init()
game_over = False
pygame.display.update()
coin0=0
XXX=600
YYY=200
#Это графическая часть рисуем все, что нужно и обновляем методом .
'''Рисуем Кртинку .png'''
#$image_path = '0.png' #Имя картинки в папке с проектом.
#$image = pygame.image.load(image_path) #Загружаем картинку в переменную image.
#$def drawImage0000(image,x,y):
#$ dis.blit(image, (x, y))
def Calculating():
global x
global y
global X
global Y
global k
global img0
global img1
global img2
global img3
global img4
global img5
global delta1 #Положение на экране для по X фигуры.
global delta0 #Положение на экране для по Y фигуры.
global Size0 #Размер графика.
global Const0 #Просто число пи.
global d0 #Шаг изменения.
global d1 #Шаг изменения.
global d2 #Шаг изменения.
global d3 #Шаг изменения.
global A0 #Шаг для фигуры ***.
global A1 #Шаг для фигуры ***.
global P0 #Специальный Шаг.
global P1 #Специальный Шаг.
global R1 #Радиус1 ***.
global R0 #Радиус1 ***.
global r0 #Радиус2 ***.
global h0 #Радиус3 ***.
global Maxim0
global font1 #Шрифт
global alpha #Параметр фигуры
MyString0= "Timee: "+ str(round(Timee,4)) #округляем до 4 знаков и преобразуем число в строку и выводим на экран.
MyString1= "A1: "+ str(round(A1,4)) #округляем до 4 знаков и преобразуем число в строку и выводим на экран.
MyString2= "A0: "+ str(round(A0,4)) #округляем до 4 знаков и преобразуем число в строку и выводим на экран.
MyString3= "R0: "+ str(round(R0,4)) #округляем до 4 знаков и преобразуем число в строку и выводим на экран.
MyString4= "r0: "+ str(round(r0,4)) #округляем до 4 знаков и преобразуем число в строку и выводим на экран.
MyString5= "h0: "+ str(round(h0,4)) #округляем до 4 знаков и преобразуем число в строку и выводим на экран.
img0 = font1.render(MyString0, True, (0,122,122)) #рисуем текст 1.
img1 = font1.render(MyString1, True, (0,122,122)) #рисуем текст 2.
img2 = font1.render(MyString2, True, (0,122,122)) #рисуем текст 3.
img3 = font1.render(MyString3, True, (0,122,122)) #рисуем текст 3.
img4 = font1.render(MyString4, True, (0,122,122)) #рисуем текст 3.
img5 = font1.render(MyString5, True, (0,122,122)) #рисуем текст 3.
#Flag0...
#Здесь управляем переменными т.к. метод вызывается в цикле то соостветственно R0, K0, r0.
#=> нарастают на d0, d1, d2, до тех пор пока не достигнут Maxim0, а затем сбрасываются до 1.
'''
h0=h0+dd
if h0+dd>Maxim0:
h0=Minim0
r0=r0+dd
if r0+dd>Maxim0:
r0=Minim0
R0=R0+dd
if R0+dd>Maxim0:
R0=Minim0
A0=A0+dd
if A1+dd>Maxim0:
A1=Minim0
A0=A0+dd
if A0+dd>Maxim0:
A0=Minim0
'''
#Рассчет x[], y[] перемнных после изменных параметров.
#Имена группа: X[i] + + + , Y[i] + - +
# : X[i] + - + , Y[i] + - +
for i in range(0,N,1):
if not (A0==0 and r0==0):
P0=(A1*3.14)/A0 #Формула рассчета угла.
else:
A0=1.1
r0=1.1
x[i] = ((R0+r0)*(math.sin(i*P0)-h0*math.sin((i*P0)*(R0+r0)/r0))) #Формула рассчета переменной x.
y[i] = ((R0+r0)*(math.cos(i*P0)-h0*math.cos((i*P0)*(R0+r0)/r0))) #Формула рассчета переменной y.
'''
Это вторая поэзия, пока - не написана.
A=k(i+b)
xx[i] = R*sin(k*a)
yy[i] = R*cos(k*a)
'''
def ChangeColor(A,O,U): #изменяем цвет рандомно и возвращает рандомный цвет.
if A==1:
A= random.randint(0, 255)
if O==1:
O= random.randint(0, 255)
if U==1:
U= random.randint(0, 255)
return (A,O,U)
def Normalize(x,y): # Преобразует каждую точку графика: x[] y[] в корректные точки X[] Y[] дабы он не уменьшался и был всегда по центру.
global X
global Y
xm = max(x)
ym = max(y)
xn = min(x)
yn = min(y)
for i in range(len(x)):
if (not ((ym-yn)==0) and (not(xm-xn)==0)):# Проверяем: чтобы часть формулы не была равна 0,
X[i]=(x[i]-xn)/(xm-xn)*(Size0-322)+delta0 # а то будет ошибка т.е. на 0 делить нельзя.
Y[i]=(y[i]-yn)/(ym-yn)*(Size0-322)+delta1
else:
print("Not Normal ")
X[i]=x[i]
Y[i]=y[i]
#gives: x y return must hav: X Y
#Прорисовка
def Draw0000():
global Y
global X
pygame.draw.rect(dis, (0,0,0), (0,0,XX,YY))
for i in range(0,(N-1),1):
pygame.draw.lines(dis,ChangeColor(222,222,1),True, [(X[i], Y[i]), (X[i+1], Y[i+1])], Sadness)
def Pause(tt):
time.sleep(tt/1000)
YYY=46
def FullMethode():
Calculating() #рассчитываем переменные с учетом изменения.
Normalize(x, y) #нормализизируем чтобы фиксировать размер и центровку.
Draw0000() #рисуем согласно переменным.
#drawImage0000(image, -500,520) #Рисуем изображение по координатам x,y,.
dis.blit(img0, (XXX, 1*YYY+50)) #Выводим на экран параметры/
dis.blit(img1, (XXX, 2*YYY+50)) #Выводим на экран параметры/
dis.blit(img2, (XXX, 3*YYY+50)) #Выводим на экран параметры/
dis.blit(img3, (XXX, 4*YYY+50)) #Выводим на экран параметры/
dis.blit(img4, (XXX, 5*YYY+50)) #Выводим на экран параметры/
dis.blit(img5, (XXX, 6*YYY+50)) #Выводим на экран параметры/
pygame.display.update() #обновляем экран
FullMethode()
while not game_over:
#Pause(Timee) # пауза в коде на время Timee (милисекунды).
#pygame.display.update() #обновляем экран
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
print('App Closed')
keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
A1=A1+dd
if keys[pygame.K_DOWN]:
A0=A0+dd
if keys[pygame.K_LEFT]:
r0=r0+dd
if keys[pygame.K_RIGHT]:
R0=R0+dd
if keys[pygame.K_RETURN]:
h0=h0+dd
if keys[pygame.K_w]:
A1=A1-dd
if keys[pygame.K_s]:
A0=A0-dd
if keys[pygame.K_a]:
r0=r0-dd
if keys[pygame.K_d]:
R0=R0-dd
if keys[pygame.K_f]:
h0=h0-dd
FullMethode()
pygame.display.update()
Pause(Timee)
#quit()
#Это нужно чтобы гладкие линии были плавные переходы или по Bezier или по Lagranzh.
'''эээ'''
'''
def LagranzhInterpolation(X,Y, N):
L=[]
L=[0.0]*100
global XL
global YL
for i in range(N, 20):
X= X[i]/20
X= N*20
W0= Y[i]* ( (V-X[i+1])* (V-X[i+2])* (V-X[i+3] ))/ ((X[i]-X[i+1])* (X[i]-X[i+2])* (X[i]-X[i+3]))
W1= Y[i+1]* ( (V-X[i])* (V-X[i+2])* (V-X[i+3] ))/ ((X[i+1]-X[i])* (X[i+1]-X[i+2])* (X[i+1]-X[i+3]))
W2= Y[i+2]* ( (V-X[i])* (V-X[i+2])* (V-X[i+3] ))/ ((X[i+2]-X[i])* (X[i+2]-X[i+1])* (X[i+2]-X[i+3]))
W3= Y[i+3]* ( (V-X[i])* (V-X[i+2])* (V-X[i+3] ))/ ((X[i+3]-X[i])* (X[i+3]-X[i+1])* (X[i+3]-X[i+2]))
L[i]=W0+W1+W2+W3
Z=0+0+\
+0+\
+0
'''
# print("ff: " , XY)
#LagranzhInterpolation(1,3)
Ответы (1 шт):
Не знаю, то ли это, что вам надо, но вопрос я понял так: нужно представить замкнутую кривую на плоскости в виде набора эпициклов. Под эпициклами мы понимаем вращающиеся круги, где центр одного круга движется по окружности другого, верно?
Что-то вроде такого? Приближение пятилучевой звезды эпициклами:
Решение элементарно: нужно представить кривую как кривую на комплексной плоскости z(t)
и разложить её в ряд Фурье. Последовательные элементы ряда будут эпициклами этой кривой.
Функция zepicycles
раскладывает комплексную кривую на набор эпициклов:
import numpy as np
import numpy.fft as fft
def zepicycles(z):
N = len(z)
F = fft.fft(z)
f = N*fft.fftfreq(N)
t = np.linspace(0, 2j*np.pi, N)
center = F[0]/N
cycles = [ (F[i]*np.exp(t*f[i]) + F[-i]*np.exp(t*f[-i]))/N for i in range(1,N//2)]
return center, cycles
Эта функция предназначена для разложения замкнутой кривой, представленной набором точек на комплексной плоскости (входной параметр z), на набор эпициклов.
Вот пошаговое объяснение:
F = fft.fft(z)
: Собсна, вот оно!fft.fft
- это функция из модуляnumpy.fft
(импортированного какfft
), которая выполняет быстрое преобразование Фурье. РезультатF
- это комплексный массив, содержащий комплексные амплитуды по частотам. Самих частот в этом массиве нет.f = N*fft.fftfreq(N)
: Эта строка вычисляет частоты, соответствующие каждому компоненту в F.fft.fftfreq
генерирует массив частот на основе количества точек данных (N).t = np.linspace(0, 2j*np.pi, N)
: заготовка для массива отсечек времени для движения по кругу. При проходе по массивуt
первый эпицикл делает один оборот, аi
-й делаетi
оборотов.center = F[0]/N
: Эта строка определяет центральную точку системы эпициклов. Она вычисляется как первый элемент преобразования Фурье (F[0]
), деленный на общее количество точек (N
).cycles = [(F[i]*np.exp(t*f[i]) + F[-i]*np.exp(t*f[-i]))/N for i in range(1,N//2)]
: Это ядро вычисления эпициклов. Здесь используется списковое включение для создания списка эпициклов. Для каждого частотного компонента вF
он вычисляет соответствующий эпицикл. Здесь частотные компоненты из преобразования Фурье переводятся в соответствующие им круговые движения.При вычислении эпициклов нужно учитывать, что функция
fft
возвращает в разложении одну и ту же частоту дважды: один разf[i]
положительное, второй разf[-i]
отрицательное. Поэтому для вычисления эпициклов нужно суммировать оба слагаемых.return center, cycles
: функция возвращает вычисленную центральную точку и список эпициклов.
Для рисования эпициклов добавим функцию, которая вычисляет n-е приближение орбиты
def zorbit(center, epicycles, n):
return center + sum(epicycles[:n])
Она принимает центральную точку (center), список комплексных эпициклов epicycles
и степень приближения n
в качестве входных данных. Затем она вычисляет положение на кривой, суммируя вклады первых n эпициклов и добавляя это к center
.
В блокноте jupyter есть примеры разложения на эпициклы для квадрата и пятилучевой звезды.