私がこれを正しく理解していれば、あなたは 2 点間の中間位置を計算しようとしています。これらの点は、線上または円弧上にある可能性があります。
ライン上の中間位置を取得するのは非常に簡単で、これに取り組む方法は複数あります。頭に浮かぶアイデアの 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()
より失われているようで、既存のセットアップに簡単に適合する可能性があります.
円弧の場合、デカルト座標から極座標への変換という三角法が少し必要になるため、物事は少し複雑です。本来よりも少し複雑に聞こえますが、物事を頭の中で視覚化すれば、それほど難しいことではありません。時計を見ていると想像してください。
2 本の針 (1 つは時、もう 1 つは分)の位置を見ることで、現在の時刻を正確に知ることができます。時計の位置、または「座標」を使用すると、正午/真夜中を簡単に知ることができます。同様に、「時計座標」から逆変換して、正午/真夜中の針の位置を言うことができます。
時計を見ると、直交座標系がオーバーレイされていることも想像できます。針の長さの単位が 1 の場合、0,0 は中央にあり、正午/真夜中のデカルト座標は (0,1) になります。15:15 の場合は (1,0)、18:30 の場合は (0,-1)、20:45 の場合は (-1,0) などになります。1 つの 2D 座標系から変換しています (デカルトとxとy ) 別の (時間と分の「時計」)
非常によく似た方法で、デカルト ( xとyを使用) から極座標 ( angleとradiusを使用) に変換して戻すことができます。たとえば、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より小さいかどうかを確認し、線の配置の開始/終了位置、または弧の配置の開始角度/終了角度、半径/距離、およびオフセットを計算できるはずだと思います
UPDATE
現在の位置から計算された位置(線上または円弧上)までアニメーション化/補間したい場合は、上記のコードが目的地の計算のみを処理するため、それを処理する必要があります。あなたのコードのいくつかに基づいて、私が意味することの基本的な例を次に示します。
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>
いくつかの簡単なメモ:
- ベクトル計算を行う別の update メソッドを定義したことに注意してください。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 の Daniel Shiffman の Vector の章を強くお勧めします。分かりやすい例でとてもよく説明されています。Processing には、自由に使える便利なPVectorクラスがあります。
- 将来、ベクトルに慣れたら、より高度な移動アルゴリズムを調べたい場合は、自由にAutonomous Steering Behaviors/Boidsを調べてください。(これは高度なトピックですが、興味深い/楽しいものではあります)。
HTH