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

Код, который у меня есть, изменен на основе ответа Python / matplotlib: построение трехмерного куба, сферы и вектора?, добавляя возможность указывать начало координат x, y и z и радиус сферы. Многие похожие вопросы были написаны на C++, Java и C#, которые я вообще не могу понять (я почти не знаю, что делаю, так что не торопитесь).
Мой код:
import numpy as np
def make_sphere_x(x, radius):
u, v = np.mgrid[0:2 * np.pi:5000j, 0:np.pi:2500j]
x += radius * np.cos(u) * np.sin(v)
return x
def make_sphere_y(y, radius):
u, v = np.mgrid[0:2 * np.pi:5000j, 0:np.pi:2500j]
y += radius * np.sin(u) * np.sin(v)
return y
def make_sphere_z(z, radius):
u, v = np.mgrid[0:2 * np.pi:5000j, 0:np.pi:2500j]
z += radius * np.cos(v)
return z
#x values
sphere_1_x = make_sphere_x(0, 2)
sphere_2_x = make_sphere_x(1, 3)
sphere_3_x = make_sphere_x(-1, 4)
#y values
sphere_1_y = make_sphere_y(0, 2)
sphere_2_y = make_sphere_y(1, 3)
sphere_3_y = make_sphere_y(0, 4)
#z values
sphere_1_z = make_sphere_z(0, 2)
sphere_2_z = make_sphere_z(1, 3)
sphere_3_z = make_sphere_z(-2, 4)
#intercept of x-values
intercept_x = list(filter(lambda x: x in sphere_1_x, sphere_2_x))
intercept_x = list(filter(lambda x: x in intercept_x, sphere_3_x))
print(intercept_x)
Проблемы:
Очевидно, должен быть лучший способ найти перехватчики. Прямо сейчас код генерирует точки с равными интервалами, причем количество интервалов, которое я указываю под мнимым числом в np.mgrid. Если это увеличить, шансы на пересечение должны увеличиться (я думаю), но когда я пытаюсь увеличить его до 10000j или выше, он просто выдает ошибку памяти.
В массиве есть очевидные пробелы, и этот метод, скорее всего, будет ошибочным, даже если у меня есть доступ к суперкомпьютеру и я могу увеличить значение до неприличного значения. Прямо сейчас код приводит к нулевому набору.
Код крайне неэффективен, не то чтобы это было приоритетом, но людям нравится тройка, не так ли?
Не стесняйтесь обвинять меня в ошибках новичка в кодировании или задавать вопросы на Stack Overflow. Ваша помощь очень ценится.
Мой совет - сначала поработайте над более простой задачей, а именно над пересечением двух кругов.
Пересечение двух сфер представляет собой круг. (Это можно легко определить. Обновлено: если обе сферы имеют одинаковый радиус.) Теперь все, что вам нужно сделать, это найти пересечение третьей сферы и вышеупомянутого круга. (Это можно легко определить. Обновлено: если все сферы имеют одинаковый радиус.)
На самом деле, я думаю, что проблема становится проще, если использовать ротацию. (а-ля конъюгация R T R ^ -1) Тогда я думаю, что мое предложение легче обобщить на произвольные радиусы.
Добавление ссылки на этот вопрос в качестве связанной, но немного другой проблемы. Вы можете найти в этом вдохновение. stackoverflow.com/questions/1406375/…
Возможный дубликат Поиск точек пересечения между 3 сферами






Проблему лучше решить с помощью тригонометрии.
Сводя задачу к двумерным кругам, мы могли бы:
import math
import numpy
class Circle():
def __init__(self, cx, cy, r):
"""initialise Circle and set main properties"""
self.centre = numpy.array([cx, cy])
self.radius = r
def find_intercept(self, c2):
"""find the intercepts between the current Circle and a second c2"""
#Find the distance between the circles
s = c2.centre - self.centre
self.dx, self.dy = s
self.d = math.sqrt(numpy.sum(s**2))
#Test if there is an overlap. Note: this won't detect if one circle completly surrounds the other.
if self.d > (self.radius + c2.radius):
print("no interaction")
else:
#trigonometry
self.theta = math.atan2(self.dy,self.dx)
#cosine rule
self.cosA = (c2.radius**2 - self.radius**2 + self.d**2)/(2*c2.radius*self.d)
self.A = math.acos(self.cosA)
self.Ia = c2.centre - [math.cos(self.A+self.theta)*c2.radius, math.sin(self.A+self.theta)*c2.radius]
self.Ib = c2.centre - [math.cos(self.A-self.theta)*c2.radius,-math.sin(self.A-self.theta)*c2.radius]
print("Interaction points are : ", self.Ia, " and: ", self.Ib)
#define two arbitrary circles
c1 = Circle(2,5,5)
c2 = Circle(1,6,4)
#find the intercepts
c1.find_intercept(c2)
#test results by reversing the operation
c2.find_intercept(c1)
Используя scipy.optimize.fsolve, вы можете найти корень заданной функции, исходя из первоначального предположения, которое находится где-то в диапазоне вашего решения. Я использовал этот подход для решения вашей проблемы, и, похоже, он работает для меня. Единственным недостатком является то, что он предоставляет вам только один перекресток. Чтобы найти второй, вам придется повозиться с начальными условиями, пока fsolve не найдет второй корень.
Сначала мы определяем наши сферы, определяя (произвольные) радиусы и центры для каждой сферы:
a1 = np.array([0,0,0])
r1 = .4
a2 = np.array([.3,0,0])
r2 = .5
a3 = np.array([0,.3,0])
r3 = .5
Затем мы определяем, как преобразовать обратно в декартовы координаты, заданные углы u,v
def position(a,r,u,v):
return a + r*np.array([np.cos(u)*np.sin(v),np.sin(u)*np.sin(v),np.cos(v)])
Теперь мы думаем, из какого уравнения нам нужно найти корень. Для любой точки пересечения считается, что для идеального u1,v1,u2,v2,u3,v3 положения position(a1,r1,u1,v1) = position(a2,r2,u2,v2) = position(a3,r3,u3,v3) равны. Таким образом, мы находим три уравнения, которые должны быть нулями, а именно разности двух векторов положения. Фактически, поскольку каждый вектор имеет 3 компонента, у нас есть 9 уравнений, которых более чем достаточно, чтобы определить наши 6 переменных.
Мы находим функцию, которую нужно минимизировать, как:
def f(args):
u1,v1,u2,v2,u3,v3,_,_,_ = args
pos1 = position(a1,r1,u1,v1)
pos2 = position(a2,r2,u2,v2)
pos3 = position(a3,r3,u3,v3)
return np.array([pos1 - pos2, pos1 - pos3, pos2 - pos3]).flatten()
fsolve требует одинакового количества входных и выходных аргументов. Поскольку у нас есть 9 уравнений, но только 6 переменных, я просто использовал 3 фиктивные переменные, чтобы размеры совпадали. Сглаживание массива в последней строке необходимо, поскольку fsolve принимает только 1D-массивы.
Теперь пересечение можно найти с помощью fsolve и (довольно случайного) предположения:
guess = np.array([np.pi/4,np.pi/4,np.pi/4,np.pi/4,np.pi/4,np.pi/4,0,0,0])
x0 = fsolve(f,guess)
u1,v1,u2,v2,u3,v3,_,_,_ = x0
Вы можете проверить правильность результата, подключив полученные углы к функции position.
+1 Как бы это выглядело на пересечении четырех сфер? Я не думаю, что сильно отличается, но я не уверен.
@KeithMadison Код должен выглядеть одинаково для четырех сфер, просто добавьте дополнительную позицию 'pos4' в определение f и три дополнительных уравнения 'pos1 - pos4', 'pos2 - pos4', 'pos3 - pos4'. Однако, в отличие от трех сфер, я не уверен, существует ли правило, говорящее нам, действительно ли существует решение.
В общем, эту проблему нельзя решить точками выборки - именно из-за дискретности (а также из-за неточности числовых значений).