Программный код для моделирования:
—————————————————————————
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.path import Path
import matplotlib.patches as patches
from scipy.optimize import fsolve
# — Параметры эллипсов —
# rx — горизонтальная полуось, ry — вертикальная полуопись
rx = 2.0
ry = 1.0
# Расстояние от центра эллипса до фокуса
c = np.sqrt(rx**2 — ry**2)
# — Центры и фокусы каждого из четырех эллипсов —
# Эллипс 1 (справа вверху)
h_ell1_x, k_ell1_y = rx, 1.0
foci1_x1, foci1_y1 = h_ell1_x — c, k_ell1_y
foci1_x2, foci1_y2 = h_ell1_x + c, k_ell1_y
# Эллипс 2 (слева вверху)
h_ell2_x, k_ell2_y = -rx, 1.0
foci2_x1, foci2_y1 = h_ell2_x — c, k_ell2_y
foci2_x2, foci2_y2 = h_ell2_x + c, k_ell2_y
# Эллипс 3 (справа внизу)
h_ell3_x, k_ell3_y = rx, -1.0
foci3_x1, foci3_y1 = h_ell3_x — c, k_ell3_y
foci3_x2, foci3_y2 = h_ell3_x + c, k_ell3_y
# Эллипс 4 (слева внизу)
h_ell4_x, k_ell4_y = -rx, -1.0
foci4_x1, foci4_y1 = h_ell4_x — c, k_ell4_y
foci4_x2, foci4_y2 = h_ell4_x + c, k_ell4_y
# — Углы для построения полного эллипса —
theta = np.linspace(0, 2 * np.pi, 200)
# — Вспомогательная функция для получения координат эллипса —
def get_ellipse_coords(h, k, rx, ry, theta_vals):
x = h + rx * np.cos(theta_vals)
y = k + ry * np.sin(theta_vals)
return x, y
# — Вспомогательная функция для уравнения эллипса (используется для поиска пересечений) —
def ellipse_equation(coords, h, k, rx, ry):
«»»Возвращает значение уравнения эллипса для данных координат.»»»
x, y = coords
return ((x — h)**2 / rx**2) + ((y — k)**2 / ry**2) — 1
# — Функция для нахождения точек пересечения двух эллипсов —
def find_ellipse_intersection(h1, k1, h2, k2, rx, ry, initial_guess):
«»»
Находит одну точку пересечения двух эллипсов, используя численное решение.
Требует хорошего начального приближения (initial_guess) для сходимости.
«»»
def equations(coords):
eq1 = ellipse_equation(coords, h1, k1, rx, ry)
eq2 = ellipse_equation(coords, h2, k2, rx, ry)
return [eq1, eq2]
solution = fsolve(equations, initial_guess)
return solution
# — Функция для получения координат на дуге эллипса (для Псевдоэллипсоида) —
def get_ellipse_arc_coords_for_pseudospheroid(h, k, rx, ry, start_point, end_point, num_points=50):
«»»
Получает координаты точек на ‘внутренней’ дуге эллипса между двумя заданными точками.
«»»
angle_start = np.arctan2(start_point[1] — k, start_point[0] — h)
angle_end = np.arctan2(end_point[1] — k, end_point[0] — h)
# Нормализация углов в диапазон [0, 2*pi) для корректного сравнения
angle_start = angle_start % (2 * np.pi)
angle_end = angle_end % (2 * np.pi)
# Выбираем кратчайший путь между углами (внутренняя дуга)
angle_diff = angle_end — angle_start
if angle_diff > np.pi:
angle_end -= 2 * np.pi
elif angle_diff < -np.pi:
angle_end += 2 * np.pi
angles_segment = np.linspace(angle_start, angle_end, num_points)
x_seg, y_seg = get_ellipse_coords(h, k, rx, ry, angles_segment)
return x_seg, y_seg
# — Настройка графика —
plt.figure(figsize=(10, 10))
ax = plt.gca()
ax.set_title(‘Псевдоэллипсоид 2-го Порядка и Образующие Эллипсы‘, fontsize=16)
# — Построение контуров всех четырех эллипсов —
x_ell1, y_ell1 = get_ellipse_coords(h_ell1_x, k_ell1_y, rx, ry, theta)
x_ell2, y_ell2 = get_ellipse_coords(h_ell2_x, k_ell2_y, rx, ry, theta)
x_ell3, y_ell3 = get_ellipse_coords(h_ell3_x, k_ell3_y, rx, ry, theta)
x_ell4, y_ell4 = get_ellipse_coords(h_ell4_x, k_ell4_y, rx, ry, theta)
# Рисуем все эллипсы тонкими серыми линиями
ax.plot(x_ell1, y_ell1, color=’gray’, linestyle=’-‘, linewidth=1.5, alpha=0.7, label=’Исходные эллипсы‘)
ax.plot(x_ell2, y_ell2, color=’gray’, linestyle=’-‘, linewidth=1.5, alpha=0.7)
ax.plot(x_ell3, y_ell3, color=’gray’, linestyle=’-‘, linewidth=1.5, alpha=0.7)
ax.plot(x_ell4, y_ell4, color=’gray’, linestyle=’-‘, linewidth=1.5, alpha=0.7)
# — Нахождение точек пересечения для формирования «Псевдоэллипсоида» —
p1 = find_ellipse_intersection(h_ell1_x, k_ell1_y, h_ell2_x, k_ell2_y, rx, ry, initial_guess=[0, 1])
p2 = find_ellipse_intersection(h_ell2_x, k_ell2_y, h_ell4_x, k_ell4_y, rx, ry, initial_guess=[-rx, 0])
p3 = find_ellipse_intersection(h_ell4_x, k_ell4_y, h_ell3_x, k_ell3_y, rx, ry, initial_guess=[0, -1])
p4 = find_ellipse_intersection(h_ell3_x, k_ell3_y, h_ell1_x, k_ell1_y, rx, ry, initial_guess=[rx, 0])
# — Построение КОНТУРА Псевдоэллипсоида 2-го Порядка (без заливки) —
# Сегменты для внутренней фигуры
x_inner_seg1, y_inner_seg1 = get_ellipse_arc_coords_for_pseudospheroid(h_ell1_x, k_ell1_y, rx, ry, p4, p1) # Ellipse 1 (P4 to P1)
x_inner_seg2, y_inner_seg2 = get_ellipse_arc_coords_for_pseudospheroid(h_ell2_x, k_ell2_y, rx, ry, p1, p2) # Ellipse 2 (P1 to P2)
x_inner_seg3, y_inner_seg3 = get_ellipse_arc_coords_for_pseudospheroid(h_ell4_x, k_ell4_y, rx, ry, p2, p3) # Ellipse 4 (P2 to P3)
x_inner_seg4, y_inner_seg4 = get_ellipse_arc_coords_for_pseudospheroid(h_ell3_x, k_ell3_y, rx, ry, p3, p4) # Ellipse 3 (P3 to P4)
# Объединяем сегменты для внутренней фигуры
pseudospheroid_inner_x = np.concatenate((x_inner_seg1, x_inner_seg2, x_inner_seg3, x_inner_seg4))
pseudospheroid_inner_y = np.concatenate((y_inner_seg1, y_inner_seg2, y_inner_seg3, y_inner_seg4))
# Рисуем контур Псевдоэллипсоида 2-го порядка жирной черной линией
ax.plot(pseudospheroid_inner_x, pseudospheroid_inner_y, color=’black’, linewidth=3, label=’Псевдоэллипсоид 2-го порядка’)
# — Отображение фокусов —
foci_x_all = [foci1_x1, foci1_x2, foci2_x1, foci2_x2, foci3_x1, foci3_x2, foci4_x1, foci4_x2]
foci_y_all = [foci1_y1, foci1_y2, foci2_y1, foci2_y2, foci3_y1, foci3_y2, foci4_y1, foci4_y2]
ax.plot(foci_x_all, foci_y_all, ‘o’, color=’red’, markersize=6, zorder=5, label=’Фокусы эллипсов‘)
# — Подписи фокусов —
ax.text(foci1_x1, foci1_y1 + 0.1, ‘F1′, color=’red’, fontsize=10, ha=’center’, va=’bottom’)
ax.text(foci1_x2, foci1_y2 + 0.1, ‘F2′, color=’red’, fontsize=10, ha=’center’, va=’bottom’)
ax.text(foci2_x1, foci2_y1 + 0.1, ‘F3′, color=’darkorange’, fontsize=10, ha=’center’, va=’bottom’)
ax.text(foci2_x2, foci2_y2 + 0.1, ‘F4′, color=’darkorange’, fontsize=10, ha=’center’, va=’bottom’)
ax.text(foci3_x1, foci3_y1 — 0.1, ‘F5′, color=’red’, fontsize=10, ha=’center’, va=’top’)
ax.text(foci3_x2, foci3_y2 — 0.1, ‘F6′, color=’red’, fontsize=10, ha=’center’, va=’top’)
ax.text(foci4_x1, foci4_y1 — 0.1, ‘F7′, color=’darkorange’, fontsize=10, ha=’center’, va=’top’)
ax.text(foci4_x2, foci4_y2 — 0.1, ‘F8′, color=’darkorange’, fontsize=10, ha=’center’, va=’top’)
# — Настройки осей и легенды —
ax.set_xlabel(‘X-ось’)
ax.set_ylabel(‘Y-ось‘)
ax.grid(True)
ax.set_aspect(‘equal’, adjustable=’box’)
plt.xlim([-5, 5])
plt.ylim([-3, 3])
# Размещение легенды
handles, labels = ax.get_legend_handles_labels()
unique_labels = dict(zip(labels, handles))
ax.legend(unique_labels.values(), unique_labels.keys(), loc=’upper left’, bbox_to_anchor=(1.05, 1),
fancybox=True, shadow=True, ncol=1, fontsize=’small’)
plt.tight_layout(rect=[0, 0.03, 0.8, 0.95])
plt.show()
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.path import Path
import matplotlib.patches as patches
from scipy.optimize import fsolve
from mpl_toolkits.mplot3d import Axes3D
# — Параметры эллипсов (из вашего кода) —
rx = 2.0
ry = 1.0
c = np.sqrt(rx**2 — ry**2)
# — Центры эллипсов (из вашего кода) —
h_ell1_x, k_ell1_y = rx, 1.0
h_ell2_x, k_ell2_y = -rx, 1.0
h_ell3_x, k_ell3_y = rx, -1.0
h_ell4_x, k_ell4_y = -rx, -1.0
# — Фокусы эллипсов (из вашего кода) —
# Для Эллипса 1 (справа вверху)
foci1_x1, foci1_y1 = h_ell1_x — c, k_ell1_y
foci1_x2, foci1_y2 = h_ell1_x + c, k_ell1_y
# Для Эллипса 2 (слева вверху)
foci2_x1, foci2_y1 = h_ell2_x — c, k_ell2_y
foci2_x2, foci2_y2 = h_ell2_x + c, k_ell2_y
# Для Эллипса 3 (справа внизу)
foci3_x1, foci3_y1 = h_ell3_x — c, k_ell3_y
foci3_x2, foci3_y2 = h_ell3_x + c, k_ell3_y
# Для Эллипса 4 (слева внизу)
foci4_x1, foci4_y1 = h_ell4_x — c, k_ell4_y
foci4_x2, foci4_y2 = h_ell4_x + c, k_ell4_y
# Собираем все фокусы в список для удобства
all_foci = [
(foci1_x1, foci1_y1), (foci1_x2, foci1_y2),
(foci2_x1, foci2_y1), (foci2_x2, foci2_y2),
(foci3_x1, foci3_y1), (foci3_x2, foci3_y2),
(foci4_x1, foci4_y1), (foci4_x2, foci4_y2)
]
# — Вспомогательная функция для получения координат эллипса —
def get_ellipse_coords(h, k, rx, ry, theta_vals):
x = h + rx * np.cos(theta_vals)
y = k + ry * np.sin(theta_vals)
return x, y
# — Вспомогательная функция для уравнения эллипса —
def ellipse_equation(coords, h, k, rx, ry):
x, y = coords
return ((x — h)**2 / rx**2) + ((y — k)**2 / ry**2) — 1
# — Функция для нахождения точек пересечения двух эллипсов —
def find_ellipse_intersection(h1, k1, h2, k2, rx, ry, initial_guess):
def equations(coords):
eq1 = ellipse_equation(coords, h1, k1, rx, ry)
eq2 = ellipse_equation(coords, h2, k2, rx, ry)
return [float(eq1), float(eq2)]
solution = fsolve(equations, initial_guess, xtol=1e-8, maxfev=1000)
return solution
# — Функция для получения координат на дуге эллипса (для Псевдоэллипсоида) —
def get_ellipse_arc_coords_for_pseudospheroid(h, k, rx, ry, start_point, end_point, num_points=50):
angle_start = np.arctan2(start_point[1] — k, start_point[0] — h)
angle_end = np.arctan2(end_point[1] — k, end_point[0] — h)
angle_start = angle_start % (2 * np.pi)
angle_end = angle_end % (2 * np.pi)
angle_diff = angle_end — angle_start
if angle_diff > np.pi:
angle_end -= 2 * np.pi
elif angle_diff < -np.pi:
angle_end += 2 * np.pi
angles_segment = np.linspace(angle_start, angle_end, num_points)
x_seg, y_seg = get_ellipse_coords(h, k, rx, ry, angles_segment)
return x_seg, y_seg
# — Нахождение точек пересечения для формирования «Псевдоэллипсоида» —
p1 = find_ellipse_intersection(h_ell1_x, k_ell1_y, h_ell2_x, k_ell2_y, rx, ry, initial_guess=[0, 1])
p2 = find_ellipse_intersection(h_ell2_x, k_ell2_y, h_ell4_x, k_ell4_y, rx, ry, initial_guess=[-rx, 0])
p3 = find_ellipse_intersection(h_ell4_x, k_ell4_y, h_ell3_x, k_ell3_y, rx, ry, initial_guess=[0, -1])
p4 = find_ellipse_intersection(h_ell3_x, k_ell3_y, h_ell1_x, k_ell1_y, rx, ry, initial_guess=[rx, 0])
# — Построение КОНТУРА Псевдоэллипсоида 2-го Порядка —
x_inner_seg1, y_inner_seg1 = get_ellipse_arc_coords_for_pseudospheroid(h_ell1_x, k_ell1_y, rx, ry, p4, p1)
x_inner_seg2, y_inner_seg2 = get_ellipse_arc_coords_for_pseudospheroid(h_ell2_x, k_ell2_y, rx, ry, p1, p2)
x_inner_seg3, y_inner_seg3 = get_ellipse_arc_coords_for_pseudospheroid(h_ell4_x, k_ell4_y, rx, ry, p2, p3)
x_inner_seg4, y_inner_seg4 = get_ellipse_arc_coords_for_pseudospheroid(h_ell3_x, k_ell3_y, rx, ry, p3, p4)
# Объединяем сегменты в один массив для образующей
full_2d_contour_arr = np.vstack([
np.column_stack((x_inner_seg1, y_inner_seg1)),
np.column_stack((x_inner_seg2, y_inner_seg2)),
np.column_stack((x_inner_seg3, y_inner_seg3)),
np.column_stack((x_inner_seg4, y_inner_seg4))
])
# Удаляем дубликаты точек на стыках, если они есть
_, idx = np.unique(full_2d_contour_arr.round(decimals=6), axis=0, return_index=True)
full_2d_contour_arr = full_2d_contour_arr[np.sort(idx)]
# Замыкаем контур
if not np.allclose(full_2d_contour_arr[0], full_2d_contour_arr[-1]):
full_2d_contour_arr = np.vstack([full_2d_contour_arr, full_2d_contour_arr[0]])
# — Функция для создания 3D-поверхности вращения —
def create_surface_of_revolution(contour_2d_points, axis_of_revolution=’x’, num_phi_points=50):
if contour_2d_points.size == 0:
return np.array([]), np.array([]), np.array([])
phi_angles = np.linspace(0, 2 * np.pi, num_phi_points)
X_surf = np.zeros((len(contour_2d_points), num_phi_points))
Y_surf = np.zeros((len(contour_2d_points), num_phi_points))
Z_surf = np.zeros((len(contour_2d_points), num_phi_points))
for i, (x_2d, y_2d) in enumerate(contour_2d_points):
radius = np.abs(y_2d) # Вращение вокруг X-оси, радиус = |y|
Y_circle = radius * np.cos(phi_angles)
Z_circle = radius * np.sin(phi_angles)
X_surf[i, :] = x_2d
Y_surf[i, :] = Y_circle
Z_surf[i, :] = Z_circle
return X_surf, Y_surf, Z_surf
# — Функция для создания 3D-колец вокруг фокусов —
def create_foci_rings(foci_list, num_points_per_ring=50):
rings_coords = []
theta_ring = np.linspace(0, 2 * np.pi, num_points_per_ring)
for fx, fy in foci_list:
# Центр кольца в 3D будет (fx, 0, 0), так как вращаем вокруг X
# Радиус кольца будет |fy|
# Если fy очень близко к нулю, кольцо будет слишком маленьким или невидимым,
# можно установить минимальный радиус или пропустить.
if np.isclose(fy, 0, atol=1e-5): # Фокус лежит на оси вращения
continue
radius = np.abs(fy)
# Координаты кольца в 3D
x_ring = np.full(num_points_per_ring, fx) # X-координата постоянна
y_ring = radius * np.cos(theta_ring)
z_ring = radius * np.sin(theta_ring)
rings_coords.append(np.column_stack((x_ring, y_ring, z_ring)))
return rings_coords
# — Создание 3D-графика —
fig = plt.figure(figsize=(12, 10))
ax = fig.add_subplot(111, projection=’3d’)
ax.set_title(‘3D Псевдоэллипсоид 2-го Порядка с Кольцами Фокусов‘, fontsize=16)
# — Создание и отрисовка 3D-поверхности вращения —
X_surf, Y_surf, Z_surf = create_surface_of_revolution(full_2d_contour_arr, axis_of_revolution=’x’)
if X_surf.size > 0:
ax.plot_surface(X_surf, Y_surf, Z_surf, color=’green’, alpha=0.6, rstride=1, cstride=1, edgecolor=’k’, linewidth=0.5)
else:
print(«Не удалось создать 2D-контур псевдоэллипсоида. Проверьте параметры и пересечения.»)
# — Добавление 3D-колец фокусов —
foci_rings_3d = create_foci_rings(all_foci)
for ring in foci_rings_3d:
ax.plot(ring[:, 0], ring[:, 1], ring[:, 2], color=’red’, linestyle=’—‘, linewidth=2, alpha=0.8) # Красные пунктирные кольца
# — Настройки осей —
ax.set_xlabel(‘X-ось’)
ax.set_ylabel(‘Y-ось‘)
ax.set_zlabel(‘Z-ось‘)
# Устанавливаем равный масштаб для всех осей для корректного отображения формы
if X_surf.size > 0:
max_range = np.array([X_surf.max()-X_surf.min(), Y_surf.max()-Y_surf.min(), Z_surf.max()-Z_surf.min()]).max() / 2.0
mid_x = (X_surf.max()+X_surf.min()) * 0.5
mid_y = (Y_surf.max()+Y_surf.min()) * 0.5
mid_z = (Z_surf.max()+Z_surf.min()) * 0.5
ax.set_xlim(mid_x — max_range, mid_x + max_range)
ax.set_ylim(mid_y — max_range, mid_y + max_range)
ax.set_zlim(mid_z — max_range, mid_z + max_range)
else:
ax.set_xlim([-3, 3])
ax.set_ylim([-3, 3])
ax.set_zlim([-3, 3])
ax.set_aspect(‘auto’, adjustable=’box’)
# — Добавляем оси для наглядности —
ax.plot([ax.get_xlim()[0], ax.get_xlim()[1]], [0, 0], [0, 0], color=’gray’, linestyle=’—‘, linewidth=1) # Ось X
ax.plot([0, 0], [ax.get_ylim()[0], ax.get_ylim()[1]], [0, 0], color=’gray’, linestyle=’—‘, linewidth=1) # Ось Y
ax.plot([0, 0], [0, 0], [ax.get_zlim()[0], ax.get_zlim()[1]], color=’gray’, linestyle=’—‘, linewidth=1) # Ось Z
plt.show()
—————————————————————————
Математическое описание построения псевдоэллипсоида 2-го порядка
Псевдоэллипсоид 2 порядка формируется вращением замкнутой 2D-фигуры вокруг оси X. Эта 2D-фигура является комбинацией дуг четырех эллипсов.
Определение Образующей Фигуры (2D Контур)
Параметры исходных эллипсов:
Каждый из четырех эллипсов имеет одинаковые полуоси:
- Горизонтальная полуось: rx
- Вертикальная полуось: ry
Их центры расположены в точках:
- Эллипс 1 (правый верхний): C1=(rx,1)
- Эллипс 2 (левый верхний): C2=(−rx,1)
- Эллипс 3 (правый нижний): C3=(rx,−1)
- Эллипс 4 (левый нижний): C4=(−rx,−1)
Уравнение каждого эллипса i с центром (hi,ki) задается как: rx2(x−hi)2+ry2(y−ki)2=1
Точки пересечения (вершины образующей фигуры):
Образующая фигура формируется путем соединения «внутренних» дуг этих эллипсов. Точками соединения являются точки пересечения соседних эллипсов. Пусть эти точки будут Pj=(xj,yj):
- P1: Пересечение Эллипса 1 и Эллипса 2.
- P2: Пересечение Эллипса 2 и Эллипса 4.
- P3: Пересечение Эллипса 4 и Эллипса 3.
- P4: Пересечение Эллипса 3 и Эллипса 1.
Эти точки находятся путем решения системы двух уравнений эллипсов. Например, для P1 (пересечение Эллипса 1 и Эллипса 2):
rx2(x−rx)2+ry2(y−1)2=1
rx2(x−(−rx))2+ry2(y−1)2=1
Численно эти точки можно найти с помощью метода, подобного fsolve в Python, как показано в коде.
Сегменты образующей фигуры:
Образующая фигура состоит из четырех дуг эллипсов:
- Дуга Эллипса 1: от P4 до P1.
- Дуга Эллипса 2: от P1 до P2.
- Дуга Эллипса 4: от P2 до P3.
- Дуга Эллипса 3: от P3 до P4.
Каждая дуга эллипса с центром (h,k) и полуосями rx,ry может быть параметризована углом θ: x(θ)=h+rxcos(θ) y(θ)=k+rysin(θ)Для построения дуги между двумя точками Pstart=(xstart,ystart) и Pend=(xend,yend) сначала определяются их углы в параметрическом представлении относительно центра эллипса:θstart=atan2(ystart−k,xstart−h) θend=atan2(yend−k,xend−h) Затем генерируются точки для θ в диапазоне от θstart до θend, выбирая кратчайший угловой путь.
Построение 3D псевдоэллипсоида вращения
Полученный 2D-контур, представляющий собой набор точек (x2D,y2D), вращается вокруг оси X на 360 градусов (от 0 до 2π).
Для каждой точки (x2D,y2D) на 2D-контуре, ее 3D-координаты (X,Y,Z) после вращения определяются следующим образом:
- X=x2D (X-координата остается неизменной, так как вращение происходит вокруг оси X)
- Радиус вращения R=∣y2D∣ (абсолютное значение Y-координаты 2D-точки)
- Y=Rcos(ϕ)
- Z=Rsin(ϕ)
Где ϕ — это угол вращения в плоскости YZ, варьирующийся от 0 до 2π.
Таким образом, 3D-поверхность псевдоэллипсоида 2 порядка описывается набором точек (X,Y,Z), где X берется из 2D-контура, а Y и Z образуют окружность с радиусом, равным абсолютному значению Y-координаты соответствующей точки 2D-контура.
Кольца Фокусов (для справки)
Для визуализации дополнительных свойств, но не для самой формы псевдоэллипсоида, мы добавляем кольца, соответствующие фокусам исходных 2D-эллипсов.
Каждый эллипс имеет два фокуса F=(fx,fy). Расстояние от центра эллипса до его фокуса по главной оси определяется как c=rx2−ry2 (при условии, что rx>ry).
В 3D-пространстве каждое кольцо фокуса j будет:
- Лежать в плоскости X=fxj.
- Центрировано на оси X в точке (fxj,0,0).
- Иметь радиус RF=∣fyj∣.
Эти кольца демонстрируют, как фокусы исходных 2D-эллипсов «вытягиваются» в окружности при вращении.
