สำหรับโพสต์นี้ y = f (t) โดยที่ t คือพารามิเตอร์ที่คุณเปลี่ยนแปลง (เวลา / ความคืบหน้า) และ y คือระยะทางไปยังเป้าหมาย ดังนั้นฉันจะพูดในแง่ของคะแนนในแปลง 2 มิติที่แกนนอนคือเวลา / ความคืบหน้าและแนวตั้งคือระยะทาง
ฉันคิดว่าคุณสามารถสร้างโค้ง Bezier ลูกบาศก์โดยมีจุดแรกที่ (0, 1) และจุดที่สี่ (สุดท้าย) ที่ (1, 0) จุดกึ่งกลางสองจุดสามารถวางแบบสุ่ม (x = rand, y = rand) ภายในสี่เหลี่ยม 1 ต่อ 1 ฉันไม่สามารถยืนยันการวิเคราะห์นี้ได้ แต่จากการเล่นกับแอปเพล็ต (ใช่ไปข้างหน้าแล้วหัวเราะ) ดูเหมือนว่าเส้นโค้ง Bezier จะไม่ลดลงด้วยข้อ จำกัด ดังกล่าว
นี่จะเป็นฟังก์ชั่นพื้นฐานของคุณ b (p1, p2) ซึ่งให้เส้นทางที่ไม่ลดลงจากจุด p1 ไปยังจุด p2
ตอนนี้คุณสามารถสร้าง ab (p (1) = (0, 1), p (n) = (1, 0)) และเลือกจำนวน p (i) ตามเส้นโค้งนี้เช่นที่ 1
โดยพื้นฐานแล้วคุณกำลังสร้างเส้นทาง "ทั่วไป" หนึ่งเส้นทางจากนั้นแยกเป็นกลุ่มและสร้างแต่ละส่วนใหม่
เนื่องจากคุณต้องการฟังก์ชั่นทางคณิตศาสตร์: สมมติว่าขั้นตอนข้างต้นได้รับการบรรจุไว้ในฟังก์ชั่นหนึ่ง y = f (t, s) ซึ่งให้ระยะทางที่ t สำหรับฟังก์ชันของเมล็ด s คุณจะต้องการ:
- 4 ตัวเลขแบบสุ่มสำหรับการวางจุดกลาง 2 จุดของเส้นแบ่ง Bezier หลัก (จาก (0, 1) ถึง (1, 0))
- หมายเลข n-1 สำหรับขอบเขตของแต่ละเซกเมนต์หากคุณมีเซกเมนต์ n ส่วน (ส่วนแรกจะเริ่มต้นที่ (0, 1) คือ t = 0 และสิ้นสุดสุดท้ายที่ (1,0) ie t = 1)
- 1 หมายเลขถ้าคุณต้องการสุ่มจำนวนเซ็กเมนต์
- 4 ตัวเลขเพิ่มเติมสำหรับการวางจุดกลางของเส้นโค้งของส่วนที่คุณที่ดินของคุณที่
ดังนั้นแต่ละเมล็ดต้องจัดหาอย่างใดอย่างหนึ่งต่อไปนี้:
- 7 + n จำนวนจริงระหว่าง 0 ถึง 1 (ถ้าคุณต้องการควบคุมจำนวนของกลุ่ม)
- 7 จำนวนจริงและหนึ่งจำนวนเต็มมากกว่า 1 (สำหรับจำนวนเซ็กเมนต์สุ่ม)
ฉันจินตนาการว่าคุณสามารถทำสิ่งเหล่านี้ได้โดยเพียงแค่ใส่จำนวนตัวเลขเป็นเมล็ด หรือคุณสามารถทำสิ่งต่าง ๆ เช่นจัดหาหนึ่งหมายเลขเป็นเมล็ดแล้วเรียกตัวสร้างตัวเลขสุ่มในตัวด้วย rand (s), rand (s + 1), rand (s + 2) และอื่น ๆ (หรือเริ่มต้นด้วย s แล้วเรียก rand.NextNumber ต่อไป)
โปรดทราบว่าแม้ว่าฟังก์ชั่นทั้งหมด f (t, s) ประกอบด้วยหลายกลุ่มคุณจะประเมินเพียงหนึ่งส่วนสำหรับแต่ละ t คุณจะต้องคำนวณขอบเขตของเซกเมนต์ซ้ำ ๆ ด้วยวิธีนี้เนื่องจากคุณจะต้องเรียงลำดับเพื่อให้แน่ใจว่าไม่มีเซกเมนต์ซ้อนทับกันสองส่วน คุณอาจจะปรับให้เหมาะสมและกำจัดงานพิเศษนี้และหาจุดสิ้นสุดของหนึ่งเซ็กเมนต์สำหรับการโทรแต่ละครั้ง แต่ตอนนี้ฉันยังไม่เห็นชัดเจน
นอกจากนี้ไม่จำเป็นต้องใช้เส้นโค้ง Bezier เส้นโค้งใด ๆ ที่เหมาะสมจะทำ
ฉันสร้างตัวอย่างการนำ Matlab มาใช้
ฟังก์ชัน Bezier (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
ฟังก์ชั่นสารประกอบ Bezier ที่อธิบายไว้ข้างต้น (จงใจเปิดโปงซ้ายเพื่อให้ชัดเจนว่าจำเป็นต้องมีการประเมินจำนวนเท่าใดสำหรับการโทรแต่ละครั้ง):
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
สคริปต์ที่พล็อตฟังก์ชั่นสำหรับเมล็ดสุ่ม (โปรดทราบว่านี่เป็นที่เดียวที่มีการเรียกใช้ฟังก์ชั่นสุ่มตัวแปรสุ่มไปยังรหัสอื่น ๆ ทั้งหมดจะถูกถ่ายทอดจากอาร์เรย์สุ่มอันนี้):
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');
นี่คือผลลัพธ์ตัวอย่าง:
ดูเหมือนจะเป็นไปตามเกณฑ์ส่วนใหญ่ของคุณ อย่างไรก็ตาม:
- มี "มุม" สิ่งนี้อาจคล้อยตามโดยใช้เส้นโค้ง Bezier อย่างเหมาะสมยิ่งขึ้น
- มัน "ชัด" ดูเหมือนว่า splines แม้ว่าคุณจะไม่สามารถคาดเดาได้จริง ๆ ว่ามันจะทำอะไรหลังจากช่วงเวลาที่ไม่สำคัญนอกเสียจากว่าคุณรู้จักเมล็ด
- มันไม่ค่อยเบี่ยงเบนไปทางมุมมากเกินไป (สามารถแก้ไขได้ด้วยการเล่นกับการกระจายของเครื่องกำเนิดเมล็ด)
- ฟังก์ชั่นลูกบาศก์เบซิเยร์ไม่สามารถไปถึงบริเวณใกล้มุมได้เนื่องจากข้อ จำกัด เหล่านี้
f'(x)>0
ดังนั้นการรวมปกติของค่าสัมบูรณ์ของฟังก์ชั่นเสียงใด ๆ จะตอบสนองความต้องการของคุณทั้งหมด น่าเสียดายที่ฉันไม่รู้วิธีง่ายๆในการคำนวณสิ่งนั้น แต่อาจมีคนอื่นทำ :)