Если я правильно понимаю, вы пытаетесь вычислить промежуточные позиции между двумя точками. Эти точки могут быть либо на линии, либо на дуге.
Получить промежуточные позиции на линии довольно просто, и есть несколько способов решить эту проблему. Одна идея, которая приходит на ум, заключается в использовании функции lerp() (которая выполняет линейную интерполяцию). . Вот очень простой пример:
//draw stuff
smooth();strokeWeight(5);
//line stuff
PVector start = new PVector(10,10);
PVector end = new PVector(90,90);
int numPts = 5;
float increment = 1.0/numPts;
for(int i = 0; i < numPts; i++){//for each point that should be on the line
float t = increment * i ; //'traversal' on the line (0.0 is at start 1.0 is at end)
point(lerp(start.x,end.x,t),//interpolate and draw
lerp(start.y,end.y,t));
}
Запустите это в новом скетче, чтобы понять, что я имею в виду. Это также можно сделать вручную, используя класс PVector и формулу интерполяции:
point(percentage) = point(start) + ((point(end)-point(start)) * percentage)
следовательно:
//draw stuff
smooth();strokeWeight(5);
//line stuff
PVector start = new PVector(10,10);
PVector end = new PVector(90,90);
int numPts = 5;
float increment = 1.0/numPts;
for(int i = 0; i < numPts; i++){//for each point that should be on the line
float t = increment * i ; //'traversal' on the line (0.0 is at start 1.0 is at end)
PVector current = PVector.add(start,PVector.mult(PVector.sub(end,start),t));
point( current.x, current.y );
}
Но lerp()
кажется более бесполезным и может легко вписаться в вашу существующую настройку.
Для дуги все немного сложнее, так как вам понадобится немного тригонометрии: преобразование декартовых координат в полярные. Это звучит немного сложнее, чем должно быть, но это не так сложно, если вы мысленно визуализируете вещи. Представьте, что вы смотрите на часы.
Вы можете точно сказать, который час, по положению двух стрелок (одна для часов, другая для минут). Используя положение часов или «координаты», вы можете легко определить, когда сейчас полдень или полночь. Точно так же вы можете преобразовать обратно из «координат часов» и сказать, что стрелка позиционирует в полдень/полночь.
Глядя на часы, вы также можете представить наложенную декартову систему: 0,0 находится в центре, а полдень/полночь в декартовой системе будет (0,1), если 1 будет единицей, используемой для длины иглы. . Для 15:15 вы получите (1,0), (0,-1) для 18:30, (-1,0) для 20:45 и т. д. Вы конвертируете из одной 2D-системы координат (декартовой с x и y) в другое ("часы" с часами и минутами)
Очень похожим образом вы можете преобразовать декартову форму (использует x и y) в полярную (использует угол и радиус) и обратно. Например, 12:00 будет означать (0,1), но также может быть выражено как (90 градусов, длина иглы).
Теперь вернемся к дуге: вы, вероятно, знаете начальный и конечный угол, и вы знаете радиус (расстояние от центра круга), поэтому у вас есть полярные координаты. Вам просто нужно преобразовать в декартовы (x, y) координаты, что можно сделать с помощью этой формулы:
x = cos(angle) * radius;
y = sin(angle) * radius;
Здесь стоит отметить, что все тригонометрические функции (sin/cos/tan/atan/и т. д.) используют радианы. К счастью, Processing уже предоставляет radians(), который упрощает преобразование градусов в радианы.
Вот базовый набросок, иллюстрирующий идею:
//draw stuff
smooth();strokeWeight(5);
//arc stuff
float distance = 35;//100 pixels away from the centre
float startAngle = radians(30);
float endAngle = radians(120);
int numPts = 10;
float increment = 1.0/numPts;
for(int i = 0; i < numPts; i++){//for each point on the arc
float intermediaryAngle = lerp(startAngle,endAngle,increment*i);
float x = cos(intermediaryAngle) * distance;
float y = sin(intermediaryAngle) * distance;
point(x+50,y+50);//50 is offset to draw from the centre of the sketch
}
Я предполагаю, что вы должны проверить, меньше ли координата y ваших объектов, чем высота/2, и вычислить либо начальные/конечные позиции для позиционирования линии, либо начальный/конечный угол, радиус/расстояние и смещение для позиционирования дуги.
ОБНОВЛЕНИЕ Если вы хотите анимировать/интерполировать от текущей позиции к вычисленной позиции (будь то на линии или на дуге), вам нужно обработать это, поскольку приведенный выше код обрабатывает только расчет пунктов назначения. Вот базовый пример того, что я имею в виду, на основе вашего кода:
int maxCircle = 10;
Circle[] circles = new Circle[maxCircle];
float increment = (1.0/maxCircle);
float traversal = 0.0;
void setup(){
size(400,400);
smooth();strokeWeight(5);
for(int i=0;i<maxCircle;i++){
circles[i] = new Circle(random(width),random(height),random(2,20));
}
}
void draw(){
background(255);
for(int i=0;i<maxCircle;i++){
if(!mousePressed) circles[i].update(width,height);//default
else{//if some event happens
//compute destination
float x,y;
float offx = width/2;
float offy = height/2;
//move to line
float startX = 0;
float endX = width;
float t = increment * i;
x = lerp(startX,endX,t);
y = offy-10;
//interpolate/move to computed position
if(traversal < 1.0){//if circle hasn't reached destination yet
traversal += 0.0001;//move closer to the destination
circles[i].x = lerp(circles[i].x,x,traversal);
circles[i].y = lerp(circles[i].y,y,traversal);
}
}
circles[i].display();
}
}
void mouseReleased(){
traversal = 0;
}
class Circle{
float x,y,vx,vy,r,speed;
Circle(float tempx, float tempy, float tempr){
x=tempx;
y=tempy;
vx=random(-1,1);
vy=random(-1,1);
r=tempr;
}
void update(int w,int h){
x+=vx;
y+=vy;
if(x<r || x>w-r){
vx*=-1;};
if(y<r || y>h-r){
vy*=-1;};
}
void display(){
fill(0,50);
noStroke();
ellipse(x,y,r,r);
}
}
Когда мышь нажата, круги будут двигаться к позициям линий. Другой способ сделать что-то похожее на вышеприведенное — использовать скорости Circle
:
- вычислить вектор направления (путем вычитания вектора назначения из вектора текущего положения)
- найти текущую скорость (или модуль вектора скорости)
- масштабировать вектор скорости на основе вектора направления и его скорости. (Это позволит внезапную остановку или замедление, если хотите)
Вот пример кода:
int maxCircle = 10;
Circle[] circles = new Circle[maxCircle];
void setup() {
size(400, 400);
smooth();
strokeWeight(5);
for (int i=0;i<maxCircle;i++) {
circles[i] = new Circle(random(width), random(height), random(2, 20));
}
}
void draw() {
background(255);
for (int i=0;i<maxCircle;i++) {
if (!mousePressed) circles[i].update(width, height);//default
else {//if some event happens
//compute destination
float x = map(i,0,maxCircle,0,width);
float y = (height * .5) - 10;
//update to destination
circles[i].update(x,y,2);
}
circles[i].display();
}
}
class Circle {
float x, y, vx, vy, r, speed;
Circle(float tempx, float tempy, float tempr) {
x=tempx;
y=tempy;
vx=random(-1, 1);
vy=random(-1, 1);
r=tempr;
}
void update(int w, int h) {
x+=vx;
y+=vy;
if (x<r || x>w-r) {
vx*=-1;
};
if (y<r || y>h-r) {
vy*=-1;
};
}
void update(float x,float y,float speed){
//compute direction vector
float dx = x - this.x;
float dy = y - this.y;
//find the current 'speed': vector's length or magnitude
float len = sqrt(dx*dx + dy*dy);//PVector's mag() does this for you
//normalize the vector
dx /= len;
dy /= len;
//scale the vector
dx *= speed;
dy *= speed;
//interpolate/move to computed position
if(dist(this.x,this.y,x,y) > 2){//if circle hasn't reached destination yet (isn't close enough)
this.x += dx;
this.y += dy;
}
}
void display() {
fill(0, 50);
noStroke();
ellipse(x, y, r, r);
}
}
Что вы можете запустить ниже:
var maxCircle = 10;
var circles = new Array(maxCircle);
function setup() {
createCanvas(400, 400);
smooth();
fill(0,50);
noStroke();
for (var i=0;i<maxCircle;i++) {
circles[i] = new Circle(random(width), random(height), random(2, 20));
}
}
function draw() {
background(255);
for (var i=0;i<maxCircle;i++) {
if (!isMousePressed) circles[i].updateBounds(width, height);//default
else {//if some event happens
//compute destination
var x = map(i,0,maxCircle,0,width);
var y = (height * .5) - 10;
//update to destination
circles[i].update(x,y,2);
}
circles[i].display();
}
}
function Circle(tempx, tempy, tempr){
this.x=tempx;
this.y=tempy;
this.vx=random(-1, 1);
this.vy=random(-1, 1);
this.r=tempr;
this.updateBounds = function(w,h) {
this.x+=this.vx;
this.y+=this.vy;
if(this.x < this.r || this.x>this.w-this.r) {
this.vx*=-1;
}
if (this.y<this.r || this.y>this.h-this.r) {
this.vy*=-1;
}
}
this.update = function(ax,ay,speed){
//compute direction vector
var dx = ax - this.x;
var dy = ay - this.y;
//find the current 'speed': vector's length or magnitude
var len = sqrt(dx*dx + dy*dy);//PVector's mag() does this for you
//normalize the vector
dx /= len;
dy /= len;
//scale the vector
dx *= speed;
dy *= speed;
//varerpolate/move to computed position
if(dist(this.x,this.y,ax,ay) > 2){//if circle hasn't reached destination yet (isn't close enough)
this.x += dx;
this.y += dy;
}
}
this.display = function() {
ellipse(this.x, this.y, this.r, this.r);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.4/p5.min.js"></script>
Несколько быстрых заметок:
- Обратите внимание, что я определил еще один метод обновления, в котором я выполняю векторную математику. В Processing/Java у вас может быть метод/функция с тем же именем, но с разными параметрами, иногда это может пригодиться. В этом случае
void update(float x,float y,float speed)
также может быть void seek(float x,float y,float speed)
или void moveTo(float x,float y,float speed)
.
- Поначалу терминология векторов может показаться сложной, но когда вы в нее вникнете, она обретет большой смысл. Также это чертовски полезно в компьютерной графике (в Processing или любом другом языке, который вы решите использовать в будущем). Я горячо рекомендую главу Дэниела Шиффмана "Вектор" из журнала Nature of Code по этой теме. Это действительно хорошо объяснено с простыми для понимания примерами. В Processing есть удобный класс PVector.
- В будущем, когда вы освоитесь с векторами и захотите изучить более продвинутые алгоритмы движения, не стесняйтесь исследовать Автономное управление поведением/Boids. (Это сложная тема, хотя и интересная/забавная).
ХТН
person
George Profenza
schedule
10.09.2012