Я пытаюсь перенести на Python алгоритм «Контролируемая упаковка круга с обработкой», который я нашел здесь:
На данный момент моя цель - заставить его работать, прежде чем я буду настраивать его под свои нужды. Этот вопрос не о том, как лучше всего делать упаковку кругов.
Пока вот что у меня есть:
#!/usr/bin/python
# coding: utf-8
import numpy as np
import matplotlib.pyplot as plt
from random import uniform
class Ball:
def __init__(self, x, y, radius):
self.r = radius
self.acceleration = np.array([0, 0])
self.velocity = np.array([uniform(0, 1),
uniform(0, 1)])
self.position = np.array([x, y])
@property
def x(self):
return self.position[0]
@property
def y(self):
return self.position[1]
def applyForce(self, force):
self.acceleration = np.add(self.acceleration, force)
def update(self):
self.velocity = np.add(self.velocity, self.acceleration)
self.position = np.add(self.position, self.velocity)
self.acceleration *= 0
class Pack:
def __init__(self, radius, list_balls):
self.list_balls = list_balls
self.r = radius
self.list_separate_forces = [np.array([0, 0])] * len(self.list_balls)
self.list_near_balls = [0] * len(self.list_balls)
def _normalize(self, v):
norm = np.linalg.norm(v)
if norm == 0:
return v
return v / norm
def run(self):
for i in range(300):
print(i)
for ball in self.list_balls:
self.checkBorders(ball)
self.checkBallPositions(ball)
self.applySeparationForcesToBall(ball)
def checkBorders(self, ball):
if (ball.x - ball.r) < - self.r or (ball.x + ball.r) > self.r:
ball.velocity[0] *= -1
ball.update()
if (ball.y - ball.r) < -self.r or (ball.y + ball.r) > self.r:
ball.velocity[1] *= -1
ball.update()
def checkBallPositions(self, ball):
list_neighbours = [e for e in self.list_balls if e is not ball]
for neighbour in list_neighbours:
d = self._distanceBalls(ball, neighbour)
if d < (ball.r + neighbour.r):
return
ball.velocity[0] = 0
ball.velocity[1] = 0
def getSeparationForce(self, c1, c2):
steer = np.array([0, 0])
d = self._distanceBalls(c1, c2)
if d > 0 and d < (c1.r + c2.r):
diff = np.subtract(c1.position, c2.position)
diff = self._normalize(diff)
diff = np.divide(diff, d)
steer = np.add(steer, diff)
return steer
def _distanceBalls(self, c1, c2):
x1, y1 = c1.x, c1.y
x2, y2 = c2.x, c2.y
dist = np.sqrt((x2 - x1)**2 + (y2 - y1)**2)
return dist
def applySeparationForcesToBall(self, ball):
i = self.list_balls.index(ball)
list_neighbours = [e for e in self.list_balls if e is not ball]
for neighbour in list_neighbours:
j = self.list_balls.index(neighbour)
forceij = self.getSeparationForce(ball, neighbour)
if np.linalg.norm(forceij) > 0:
self.list_separate_forces[i] = np.add(self.list_separate_forces[i], forceij)
self.list_separate_forces[j] = np.subtract(self.list_separate_forces[j], forceij)
self.list_near_balls[i] += 1
self.list_near_balls[j] += 1
if self.list_near_balls[i] > 0:
self.list_separate_forces[i] = np.divide(self.list_separate_forces[i], self.list_near_balls[i])
if np.linalg.norm(self.list_separate_forces[i]) > 0:
self.list_separate_forces[i] = self._normalize(self.list_separate_forces[i])
self.list_separate_forces[i] = np.subtract(self.list_separate_forces[i], ball.velocity)
self.list_separate_forces[i] = np.clip(self.list_separate_forces[i], a_min=0, a_max=np.array([1]))
separation = self.list_separate_forces[i]
ball.applyForce(separation)
ball.update()
list_balls = list()
for i in range(10):
b = Ball(0, 0, 7)
list_balls.append(b)
p = Pack(30, list_balls)
p.run()
plt.axes()
# Big container
circle = plt.Circle((0, 0), radius=30, fc='none', ec='k')
plt.gca().add_patch(circle)
for c in list_balls:
ball = plt.Circle((c.x, c.y), radius=c.r, picker=True, fc='none', ec='k')
plt.gca().add_patch(ball)
plt.axis('scaled')
plt.show()
Первоначально код был написан с помощью Processing, я старался использовать вместо этого numpy. Я не совсем уверен в своем checkBallPosition
, исходный автор использует переменную count
, которая мне кажется бесполезной. Мне также интересно, почему вектор steer
в исходном коде имеет размерность 3.
Вот что дает мой код:
Круги (мне пришлось переименовать их в шары, чтобы не конфликтовать с Circle из matplotlib) перекрываются и, похоже, не уходят друг от друга. Я не думаю, что я очень далеко, но мне понадобится небольшая помощь, чтобы найти, что не так с моим кодом. Не могли бы вы мне помочь, пожалуйста ?
РЕДАКТИРОВАТЬ: я понимаю, что мне, вероятно, нужно сделать несколько проходов. Может быть, пакет обработки (язык?) Запускает функцию run
несколько раз. Для меня это действительно имеет смысл, эта проблема очень похожа на оптимизацию молекулярной механики, и это повторяющийся процесс.
Мой вопрос теперь может быть немного более конкретным: похоже, функция checkBorders
не выполняет свою работу должным образом и не восстанавливает круги должным образом. Но, учитывая его простоту, я бы сказал, что ошибка в applySeparationForcesToBall
, я, вероятно, неправильно приложу усилия.
r
(радиус) в некоторых местах на 2. - person Arndt Jonasson   schedule 26.03.2018r/2
мне тоже кажется странным. Я одного не понимаю. Что касается вашего второго комментария, я так не думаю, если они перекрываются,checkBallPositions
не обнуляет их скорости (которые инициализируются случайным образом), поэтому в какой-то момент они не будут перекрываться. - person JPFrancoia   schedule 26.03.2018applySeparationForcesToBall
, теперь круги выбрасываются из контейнера. - person JPFrancoia   schedule 26.03.2018