Правильный способ получить правильное соотношение сторон в 3D-графиках?

Прежде всего полный отказ от ответственности, я новичок в python, и мой вопрос может быть глупым, но, насколько мне показали мои исследования, кажется, что в matplotlib нет поддерживаемого способа делать то, что я хочу.

Мне нужно построить траектории задачи трех тел, и я пытаюсь сделать это с помощью Python, чтобы меньше зависеть от Matlab (это то, чему учат в моем университете). В этих сюжетах фигурируют и Земля, и Луна, и мне важно, чтобы они выглядели сферическими. Следующий код дает базовый пример того, что мне нужно построить:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D, proj3d # Needed to 3Dplots
plt.close('all') # Close plots of previous scripts

#Data
Er = 0.0166         # Earth radius
Mr = 0.0045         # Moon radius
mu = 0.01215        # Mass parameter
#
def plotCC(ax,rad,pos): # Plot celestial bodies
     u = np.linspace(0, 2 * np.pi, 53)
     v = np.linspace(0, np.pi, 53)
     x = pos +  rad * np.outer(np.cos(u), np.sin(v))
     y = rad * np.outer(np.sin(u), np.sin(v))
     z = rad * np.outer(np.ones(np.size(u)), np.cos(v))
     ax.plot_surface(x, y, z)

# Dummy Halo orbit    
u   = np.linspace(0, 2 * np.pi, 30)
xh  = (1-mu)*np.ones(len(u))
yh  = 20*Mr*np.sin(u)
zh  = 0.12 + 30*Mr*np.cos(u)

# Dummy trajectory
xt  = np.linspace(1-mu,-mu,20)
yt  = (xt-(1-mu))*(xt+mu)*-1.2
zt  = (xt-(1-mu))*(xt+mu)*-0.8

Используя это, я могу построить все, что мне нужно, с помощью:

# Plots
fig     = plt.figure(figsize=(8,8))
ax      = fig.gca(projection='3d',proj_type = 'ortho')
ax.plot(xh,yh,zh, lw=1.2)   # Plot Halo orbit
ax.plot(xt,yt,zt, lw=1.2)   # Plot trajectory
plotCC(ax,Mr,1-mu)          # Plot Moon
plotCC(ax,Er,-mu)           # Plot Earth

Что дает в результате это:

Сюжет без изменений

Это далеко не то, что мне нужно, поскольку ни одно из небесных тел не представлено в виде сфер. Я думал, что соотношение сторон можно настроить так, чтобы оно соответствовало представленным данным, но ни один из найденных мной поддерживаемых методов, похоже, не делает именно этого. Это методы: ax.axis('equal'), ax.set_aspect('equal'), ax.axis('scaled'), ax.set_aspect('equal', adjustable='box'), ax.set_aspect('equal', adjustable='datalim'). Ни один из них не выполняет эту работу.

Первое, что я нашел, это ответ, который в основном создает поле, чтобы все оси измерялись одинаково. Это работает, но в моем случае одно из измерений намного больше, чем другие, и это приводит к большому количеству потраченного впустую пространства:

Сюжет с решением, создающим поле

Наконец, лучшее, что я смог найти, — это решение здесь, которое требует изменить файл axes.py так, чтобы get_proj() принимал соотношение сторон, которое вы даете ему с помощью:

ay = (ax.get_ylim3d()[1]-ax.get_ylim3d()[0])/(ax.get_xlim3d()[1]-ax.get_xlim3d()[0])
az = (ax.get_zlim3d()[1]-ax.get_zlim3d()[0])/(ax.get_xlim3d()[1]-ax.get_xlim3d()[0])
ax.pbaspect = list(1.8*np.array([1,ay,az]))

Результатом будет это. Если вы внимательно посмотрите, график не центрирован, и в зависимости от ориентации не все данные находятся внутри области рисования. Пока я буду использовать этот последний метод, но после всех исследований, которые я провел, я все еще не могу поверить, что что-то подобное не поддерживается должным образом инструментом построения графиков (который во всем остальном просто фантастический, насколько я понимаю). знаю), и поэтому мой вопрос: я упускаю что-то очевидное?

Заранее спасибо :)


person Jose Manuel    schedule 21.02.2019    source источник
comment
Краткий ответ: Нет, вы ничего не упускаете. pbaspect не был принят в matplotlib, потому что это может вызвать некоторые проблемы (я думаю, одну из них вы нашли здесь). Вы пробовали этот ответ?   -  person ImportanceOfBeingErnest    schedule 21.02.2019
comment
@ImportanceOfBeingErnest Я видел этот пост, но хотя ответ в основном делал то же самое, что и метод bpaspect (по крайней мере, я так думал). Я только что попробовал это, и это дает хороший результат, с той разницей, что вы не можете изменить размер графика (как я сделал в ax.pbaspect = list(1.8*np.array([1,ay,az])), так что ничего многообещающего...   -  person Jose Manuel    schedule 21.02.2019
comment
Размер должен определяться размером осей; так что это не зависит от аспекта. Это не должно быть критерием для оценки решений.   -  person ImportanceOfBeingErnest    schedule 21.02.2019