如何在加工过程中在一定的弧线和线条中移动形状?

时间:2012-09-09 03:05:12

标签: design-patterns move processing linear-algebra shape

我制作了经典的随机移动圆圈,如图所示:

enter image description here

这就是我想要实现的目标:

当鼠标点击时,圆圈排列如下: enter image description here

我想我可以查看所有圈子的位置。如果它们的位置在1/2的高度之内,它们应该形成弧形。 如果它们大于1/2高度,则形成直线。

但诀窍是:如何形成这些形状?

我的意思是,我知道如何形成一个圆形,只需将它们的圆心移向一个点。 但LINE(如何移动他们的x位置)??? ARC甚至?真的不知道。

有谁知道吗? 非常感谢你。

1 个答案:

答案 0 :(得分: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 / strong>即可。 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,并计算线定位的起点/终点位置或起始角/终点角,半径/距离和弧定位的偏移量< / p>

<强>更新 如果要从当前位置动画/插值到计算位置(无论是在线上还是在弧上),您需要处理它,因为上面的代码只处理计算目标。基于您的一些代码,这是我的意思的基本示例:

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. 根据方向矢量及其速度缩放速度矢量。 (如果你愿意,这将允许突然停止或减速)
  4. 这是一个代码示例:

    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>

    sketch preview

    一些快速说明:

    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. 矢量术语起初可能看起来很复杂,但是一旦你进入它就会很有意义。此外,它在计算机图形学中非常有用(处理或您将来决定使用的任何其他语言)。我热烈推荐Daniel Shiffman's Vector chapter from Nature of Code这个主题。通过易于理解的示例可以很好地解释它。 Processing提供了方便的PVector课程。
    3. 将来,如果您对矢量感到满意,并且想要探索更高级的运动算法,请随时浏览Autonomous Steering Behaviors/Boids。 (虽然这是一个有趣/有趣的主题,但这是一个高级主题。)
    4. HTH