Python: пересечение сфер

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

Параметрическое представление сферы:

Python: пересечение сфер

Код, который у меня есть, изменен на основе ответа 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)

Проблемы:

  1. Очевидно, должен быть лучший способ найти перехватчики. Прямо сейчас код генерирует точки с равными интервалами, причем количество интервалов, которое я указываю под мнимым числом в np.mgrid. Если это увеличить, шансы на пересечение должны увеличиться (я думаю), но когда я пытаюсь увеличить его до 10000j или выше, он просто выдает ошибку памяти.

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

  3. Код крайне неэффективен, не то чтобы это было приоритетом, но людям нравится тройка, не так ли?

Не стесняйтесь обвинять меня в ошибках новичка в кодировании или задавать вопросы на Stack Overflow. Ваша помощь очень ценится.

В общем, эту проблему нельзя решить точками выборки - именно из-за дискретности (а также из-за неточности числовых значений).

DYZ 08.01.2019 02:54

Мой совет - сначала поработайте над более простой задачей, а именно над пересечением двух кругов.

Robert Dodier 08.01.2019 03:33

Пересечение двух сфер представляет собой круг. (Это можно легко определить. Обновлено: если обе сферы имеют одинаковый радиус.) Теперь все, что вам нужно сделать, это найти пересечение третьей сферы и вышеупомянутого круга. (Это можно легко определить. Обновлено: если все сферы имеют одинаковый радиус.)

Mateen Ulhaq 08.01.2019 05:58

На самом деле, я думаю, что проблема становится проще, если использовать ротацию. (а-ля конъюгация R T R ^ -1) Тогда я думаю, что мое предложение легче обобщить на произвольные радиусы.

Mateen Ulhaq 08.01.2019 06:04

Добавление ссылки на этот вопрос в качестве связанной, но немного другой проблемы. Вы можете найти в этом вдохновение. stackoverflow.com/questions/1406375/…

Eskapp 08.01.2019 15:07

Возможный дубликат Поиск точек пересечения между 3 сферами

MBo 08.01.2019 17:13
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
11
6
2 788
2

Ответы 2

Проблему лучше решить с помощью тригонометрии.

Сводя задачу к двумерным кругам, мы могли бы:

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 Как бы это выглядело на пересечении четырех сфер? Я не думаю, что сильно отличается, но я не уверен.

10GeV 15.11.2020 17:24

@KeithMadison Код должен выглядеть одинаково для четырех сфер, просто добавьте дополнительную позицию 'pos4' в определение f и три дополнительных уравнения 'pos1 - pos4', 'pos2 - pos4', 'pos3 - pos4'. Однако, в отличие от трех сфер, я не уверен, существует ли правило, говорящее нам, действительно ли существует решение.

ggian 16.11.2020 18:17

Другие вопросы по теме