给定一组伪随机2D点,如下所示:
points = random.sample([[x, y] for x in xrange(width) for y in yrange(height)], 100)
我希望能够添加一个或多个非随机吸引点,围绕这些点将绘制其他点。我不打算为此设置动画,因此它不需要非常高效,但我希望能够指定如何绘制每个吸引点的随机点(例如基于正方形距离和给定的引力常数)然后将我的点提供给返回原始列表的修改版本的函数:
points = random.sample([[x, y] for x in xrange(width) for y in xrange(height)], 100)
attractors = [(25, 102), (456, 300), (102, 562)]
def attract(random_points_list, attractor_points_list):
(...)
return modified_points_list
new_points_list = attract(points, attractors)
然后,这个新的点列表将用于播种Voronoi图(不是此问题的一部分)。
答案 0 :(得分:1)
这比我最初估计的纯粹的实施工作任务更具挑战性。困难在于定义一个好的吸引力模型。
模拟吸引子上的平滑自由落体(好像在由多点质量创建的真实重力场中)是有问题的,因为您必须指定此过程的持续时间。如果持续时间足够短,则位移将很小并且吸引子周围的聚集将不会明显。如果持续时间足够长,那么所有点都将落在吸引子上或者距离它们太近。
一次计算每个点的新位置(不进行基于时间的模拟)比较简单,但问题是每个点的最终位置是否必须受 all 的影响吸引者或只有最接近的。后一种方法(吸引到最接近一种)被证明产生了更具视觉吸引力的结果。我用前一种方法无法取得好成绩(但请注意,我只尝试过相对简单的吸引功能)。
使用matplotlib
进行可视化的Python 3.4代码如下:
#!/usr/bin/env python3
import random
import numpy as np
import matplotlib.pyplot as plt
def dist(p1, p2):
return np.linalg.norm(np.asfarray(p1) - np.asfarray(p2))
def closest_neighbor_index(p, attractors):
min_d = float('inf')
closest = None
for i,a in enumerate(attractors):
d = dist(p, a)
if d < min_d:
closest, min_d = i, d
return closest
def group_by_closest_neighbor(points, attractors):
g = []
for a in attractors:
g.append([])
for p in points:
g[closest_neighbor_index(p, attractors)].append(p)
return g
def attracted_point(p, a, f):
a = np.asfarray(a)
p = np.asfarray(p)
r = p - a
d = np.linalg.norm(r)
new_d = f(d)
assert(new_d <= d)
return a + r * new_d/d
def attracted_point_list(points, attractor, f):
result=[]
for p in points:
result.append(attracted_point(p, attractor, f))
return result
# Each point is attracted only to its closest attractor (as if the other
# attractors don't exist).
def attract_to_closest(points, attractors, f):
redistributed_points = []
grouped_points = group_by_closest_neighbor(points, attractors)
for a,g in zip(attractors, grouped_points):
redistributed_points.extend(attracted_point_list(g,a,f))
return redistributed_points
def attraction_translation(p, a, f):
return attracted_point(p, a, f) - p
# Each point is attracted by multiple attracters.
# The resulting point is the average of the would-be positions
# computed for each attractor as if the other attractors didn't exist.
def multiattract(points, attractors, f):
redistributed_points = []
n = float(len(attractors))
for p in points:
p = np.asfarray(p)
t = np.zeros_like(p)
for a in attractors:
t += attraction_translation(p,a,f)
redistributed_points.append(p+t/n)
return redistributed_points
def attract(points, attractors, f):
""" Draw points toward attractors
points and attractors must be lists of points (2-tuples of the form (x, y)).
f maps distance of the point from an attractor to the new distance value,
i.e. for a single point P and attractor A, f(distance(P, A)) defines the
distance of P from A in its new (attracted) location.
0 <= f(x) <= x must hold for all non-negative values of x.
"""
# multiattract() doesn't work well with simple attraction functions
# return multiattract(points, attractors, f);
return attract_to_closest(points, attractors, f);
if __name__ == '__main__':
width=400
height=300
points = random.sample([[x, y] for x in range(width) for y in range(height)], 100)
attractors = [(25, 102), (256, 256), (302, 62)]
new_points = attract(points, attractors, lambda d: d*d/(d+100))
#plt.scatter(*zip(*points), marker='+', s=32)
plt.scatter(*zip(*new_points))
plt.scatter(*zip(*attractors), color='red', marker='x', s=64, linewidths=2)
plt.show()
答案 1 :(得分:0)
你说你希望能够改变这个功能,所以我们需要在f
的参数中添加一个函数attract
。这个函数应该需要一个参数,即点的距离,它应该返回一个数字。
import math
def attract(random_point_list, attractor_point_list, f = lambda x: 1/x**2):
for x0, y0 in attractor_point_list:
x0, y0 = float(x0), float(y0)
modified_point_list = []
for x, y in random_point_list:
rx, ry = x0 - x, y0 - y # the relative position of the attractor
distance = math.sqrt(rx**2 + ry**2)
attraction = f(d)
modified_point_list.append((x + attraction*rx/distance, y + attraction*ry/distance)) # (rx, ry) is a vector with length distance -> by dividing with distance we get a vector (rx / distance, ry / distance) with length 1. Multiplying it with attraction, we get the difference of the original and the new location.
random_point_list = modified_point_list
return modified_point_list