как перемещать фигуру по определенной дуге и линии при обработке?

я сделал классические случайные движущиеся круги, как на картинке:

введите здесь описание изображения

И вот что я хочу понять:

когда mouseClicked , круги выстраиваются в линию следующим образом: введите здесь описание изображения

я думаю, что я могу проверить положение всех кругов. если их положение находится в пределах 1/2 высоты, они должны образовать дугу. если они больше 1/2 высоты, они образуют линию.

Но хитрость в том, как сформировать эти формы?

я имею в виду, я знаю, как сформировать круг, просто переместите их центр круга к точке. но LINE (как переместить их x позицию)??? даже АРК??? действительно понятия не имею.

Кто-нибудь знает? Большое спасибо.


person kikkpunk    schedule 09.09.2012    source источник


Ответы (1)


Если я правильно понимаю, вы пытаетесь вычислить промежуточные позиции между двумя точками. Эти точки могут быть либо на линии, либо на дуге.

Получить промежуточные позиции на линии довольно просто, и есть несколько способов решить эту проблему. Одна идея, которая приходит на ум, заключается в использовании функции 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:

  1. вычислить вектор направления (путем вычитания вектора назначения из вектора текущего положения)
  2. найти текущую скорость (или модуль вектора скорости)
  3. масштабировать вектор скорости на основе вектора направления и его скорости. (Это позволит внезапную остановку или замедление, если хотите)

Вот пример кода:

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>

предварительный просмотр эскиза

Несколько быстрых заметок:

  1. Обратите внимание, что я определил еще один метод обновления, в котором я выполняю векторную математику. В 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).
  2. Поначалу терминология векторов может показаться сложной, но когда вы в нее вникнете, она обретет большой смысл. Также это чертовски полезно в компьютерной графике (в Processing или любом другом языке, который вы решите использовать в будущем). Я горячо рекомендую главу Дэниела Шиффмана "Вектор" из журнала Nature of Code по этой теме. Это действительно хорошо объяснено с простыми для понимания примерами. В Processing есть удобный класс PVector.
  3. В будущем, когда вы освоитесь с векторами и захотите изучить более продвинутые алгоритмы движения, не стесняйтесь исследовать Автономное управление поведением/Boids. (Это сложная тема, хотя и интересная/забавная).

ХТН

person George Profenza    schedule 10.09.2012
comment
может я не ясно выразился. Я на самом деле хочу ПЕРЕМЕЩАТЬ эти круги, а не просто менять их местоположение. Я хочу видеть, как они ПЕРЕМЕЩАЮТСЯ из случайных позиций в линию. Как возможен этот процесс? - person kikkpunk; 18.09.2012
comment
Я вижу, изначально я понял, что вы просто хотите правильно разместить круг. Тем не менее, большая часть вышеизложенного применима, поскольку: вы знаете текущее положение окружности (скажем, точка A), но вам нужно вычислить пункт назначения (точка B - место на дуге/линии), как описано выше. Все, что осталось после того, как вы вычислили точку B, - это интерполировать между A и B, чтобы получить движение, довольно внезапное обновление. - person George Profenza; 18.09.2012
comment
В яблочко. И здесь кроется проблема: плавная анимация. Я думаю о вычислении расстояния от исходных позиций кругов до точки в середине линии. Более близкие круги приближаются к точке. но эта идея все еще размыта, и я не знаю, как поместить это в код. - person kikkpunk; 18.09.2012
comment
@i'mnotaprogrammer Я добавил дополнительные пояснения, которые, надеюсь, будут полезны. Также обратите внимание, что для более близких кругов приближайтесь к точке, вам нужно найти ближайшую точку к текущему (i) кругу и установить ее в качестве пункта назначения. В идеале вы должны вычислить эти места назначения в setup() один раз и повторно использовать их в draw(), когда вы анимируете их. Я не кодировал это, так как вам нужно выяснить, что является пунктом назначения (где линия/дуга начинается и заканчивается на экране), но я предполагаю, что вы сможете справиться с этим. ХТН - person George Profenza; 18.09.2012