2016-09-11 86 views
3

添加一個或多個吸引來考慮,像這樣產生了一系列的僞2D點了一組隨機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

我的意思是,有趣的目標,但你有實現這個想法有什麼問題? –

回答

0

你說你希望能夠改變函數,所以我們需要添加一個函數fattract的參數。這個函數應該需要一個參數,點的距離,它應該返回一個數字。

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 
+0

這種方法的問題是吸引子是按順序「應用」的。因此重新排列吸引子會影響最終結果。 – Leon

+0

這是事實,但它適用於平方反比法和類似的功能。 – CodenameLambda

1

原來這是一個比我最初估計的更具挑戰性的問題,它是一個純粹的實施工作任務。困難在於定義一個好的吸引力模型。

  1. 模擬上吸引普通自由落體(彷彿在通過多點羣衆創造一個真正的重力場)是有問題的,因爲你必須指定此過程的持續時間。如果持續時間足夠短,位移將很小,並且吸引子周圍的聚類不會被注意到。如果持續時間足夠長,那麼所有點將落在吸引子上或與它們太靠近。

  2. 計算每個點的一個炮打響的新位置(而不進行基於時間的仿真)是簡單的,但問題是,是否每一個點的最終位置必須由所有吸引或只有最接近的影響其中一個。後一種方法(吸引到最接近一個)證明會產生更具視覺吸引力的結果。我以前的方法無法取得好成績(但請注意,我只嘗試過相對簡單的吸引功能)。

的Python 3.4的代碼使用matplotlib可視化如下:

#!/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() 
+0

感謝Leon,這非常接近我希望實現的目標!多吸引子方法並不是必需的,因爲我的確在尋找一種視覺上吸引人的結果,而不是物理上正確的結果。因此,在這裏不需要解決三體問題。我玩過許多不同的功能,我似乎無法找到一個能夠使距離外邊緣最近的點保持不動或幾乎不變的點。這是一個1000×1000規則間隔網格(比隨機散射更易於觀察)的f(d)=d²/ d + 900的示例。 [情節](http://i.imgur.com/oBD6Wsu.png) – tohox

相關問題