Bu yazı için, y = f (t) burada t, değiştirdiğiniz parametredir (zaman / ilerleme) ve y, hedefe olan mesafedir. Bu yüzden, yatay eksenin zaman / ilerleme ve dikey mesafenin mesafe olduğu 2D çizimlerdeki noktalar açısından konuşacağım.
Sanırım ilk noktası (0, 1) ve dördüncü (son) noktası (1, 0) olan bir kübik Bezier eğrisi oluşturabilirsiniz. İki orta nokta bu 1'e 1 dikdörtgen içine rastgele yerleştirilebilir (x = rand, y = rand). Bunu analitik olarak doğrulayamıyorum, ama sadece bir uygulama ile oynamaktan (evet, devam edin ve gülün) Bezier eğrisinin böyle bir kısıtlamayla asla azalmayacağı anlaşılıyor.
Bu, p1 noktasından p2 noktasına azalmayan bir yol sağlayan temel işleviniz b (p1, p2) olacaktır.
Şimdi ab (p (1) = (0, 1), p (n) = (1, 0)) üretebilir ve bu eğri boyunca bir dizi p (i) 'yi seçebilirsiniz, böylece 1
Esasen, bir "genel" yol üretiyorsunuz ve sonra bunu parçalara ayırıyorsunuz ve her segmenti yeniden üretiyorsunuz.
Matematiksel bir işlev istediğiniz için: Yukarıdaki yordamın tohum = işlevi için t'deki mesafeyi veren y = f (t, s) bir işlevde paketlendiğini varsayalım. İhtiyacın olacak:
- Ana Bezier spline'ın 2 orta noktasını yerleştirmek için 4 rastgele sayı ((0, 1) ila (1, 0) arasında)
- n segmentiniz varsa her segmentin sınırları için n-1 sayıları (ilk segment her zaman (0, 1) ile başlar, yani t = 0 ve son uçlar (1,0) ile t = 1'de başlar)
- Segment sayısını rasgele seçmek istiyorsanız 1 sayı
- Arazinizin bulunduğu segmentin spline orta noktalarını yerleştirmek için 4 numara daha
Bu nedenle, her tohum aşağıdakilerden birini sağlamalıdır:
- 0 ile 1 arasında 7 + n gerçek sayı (segment sayısını kontrol etmek istiyorsanız)
- 7 gerçek sayı ve 1'den büyük bir tam sayı (rastgele sayıda segment için)
Bunların ikisini de tohum olarak bir dizi sayı sağlayarak başarabileceğinizi düşünüyorum. Alternatif olarak, tohum olarak bir sayı s sağlayın ve sonra rand (s), rand (s + 1), rand (s + 2) vb. İle yerleşik rasgele sayı üretecini çağırabilirsiniz (veya s ve sonra rand.NextNumber aramaya devam edin).
F (t, s) işlevinin tamamı birçok bölümden oluşmasına rağmen, her bir t için yalnızca bir bölüm değerlendirdiğinizi unutmayın. Sen edecek Emin hiçbir iki segment üst üste olmak için bunları sıralamak zorunda kalacak, çünkü tekrar tekrar bu yöntemle segmentlerin sınırlarını hesaplamak gerekir. Muhtemelen bu ekstra çalışmayı optimize edebilir ve kurtulabilirsiniz ve her arama için sadece bir segmentin uç noktalarını bulabilirsiniz, ancak şu anda benim için açık değil.
Ayrıca, Bezier eğrileri gerekli değildir, herhangi uygun bir spline olacaktır.
Örnek bir Matlab uygulaması oluşturdum.
Bezier işlevi (vectorized):
function p = bezier(t, points)
% p = bezier(t, points) takes 4 2-dimensional points defined by 2-by-4 matrix
% points and gives the value of the Bezier curve between these points at t.
%
% t can be a number or 1-by-n vector. p will be an n-by-2 matrix.
coeffs = [
(1-t').^3, ...
3*(1-t').^2.*t', ...
3*(1-t').*t'.^2, ...
t'.^3
];
p = coeffs * points;
end
Yukarıda açıklanan bileşik Bezier işlevi (her çağrı için ne kadar değerlendirmenin gerekli olduğunu netleştirmek için kasıtlı olarak vektörünü bırakmadan bırakılır):
function p = bezier_compound(t, ends, s)
% p = bezier(t, points) takes 2 2-dimensional endpoints defined by a 2-by-2
% matrix ends and gives the value of a "compound" Bezier curve between
% these points at t.
%
% t can be a number or 1-by-n vector. s must be a 1-by-7+m vector of random
% numbers from 0 to 1. p will be an n-by-2 matrix.
%% Generate a list of segment boundaries
seg_bounds = [0, sort(s(9:end)), 1];
%% Find which segment t falls on
seg = find(seg_bounds(1:end-1)<=t, 1, 'last');
%% Find the points that segment boundaries evaluate to
points(1, :) = ends(1, :);
points(2, :) = [s(1), s(2)];
points(3, :) = [s(3), s(4)];
points(4, :) = ends(2, :);
p1 = bezier(seg_bounds(seg), points);
p4 = bezier(seg_bounds(seg+1), points);
%% Random middle points
p2 = [s(5), s(6)] .* (p4-p1) + p1;
p3 = [s(7), s(8)] .* (p4-p1) + p1;
%% Gather together these points
p_seg = [p1; p2; p3; p4];
%% Find what part of this segment t falls on
t_seg = (t-seg_bounds(seg))/(seg_bounds(seg+1)-seg_bounds(seg));
%% Evaluate
p = bezier(t_seg, p_seg);
end
Rastgele bir tohum için işlevi çizen komut dosyası (rastgele bir işlevin çağrıldığı tek yer olduğuna dikkat edin, diğer tüm koda rastgele değişkenler bu rastgele diziden yayılır):
clear
clc
% How many samples of the function to plot (higher = higher resolution)
points = 1000;
ends = [
0, 0;
1, 1;
];
% a row vector of 12 random points
r = rand(1, 12);
p = zeros(points, 2);
for i=0:points-1
t = i/points;
p(i+1, :) = bezier_compound(t, ends, r);
end
% We take a 1-p to invert along y-axis here because it was easier to
% implement a function for slowly moving away from a point towards another.
scatter(p(:, 1), 1-p(:, 2), '.');
xlabel('Time');
ylabel('Distance to target');
İşte bir örnek çıktı:
Ölçütlerinizin çoğunu karşılıyor gibi görünüyor. Ancak:
- "Köşeler" var. Bu Bezier eğrileri daha uygun bir şekilde kullanılarak yapılabilir.
- "Açıkça" spline benziyor, ancak tohum bilmediğiniz sürece önemsiz bir süre sonra ne yapacağını gerçekten tahmin edemezsiniz.
- Çok nadiren köşeye çok fazla sapar (tohum jeneratörünün dağılımı ile oynayarak sabitlenebilir).
- Kübik Bezier işlevi, bu kısıtlamalar göz önüne alındığında köşeye yakın bir alana ulaşamaz.
f'(x)>0
, böylece herhangi bir gürültü işlevinin mutlak değerinin normalleştirilmiş entegrasyonu tüm gereksinimlerinizi karşılayacaktır. Maalesef bunu hesaplamanın kolay bir yolunu bilmiyorum, ama belki başka biri yapıyor. :)