今日、「HTML5+JavaScript アニメーションの基礎」という本を勉強していたとき、第 8 章の 3 番目のセクションで、3 つのバネを使って 3 つの点を結び、ストレッチ動作を実行する方法について話しました。
例題が終わった後、4点だったらどうなるか、5点だったらどうなるか考えてみました。
コードを書き直してポイント数を可変にしました。最終的な効果は、バランスをとるために各ポイントの最終的なストレッチ動作を実現することですが、ポイント間の接続はあまり見栄えがよくなく、いくつかは交差しています。
そこで、この領域を最適化できないか考えていました。
線を回転させる前の例の点はすべてランダムな位置にあるため、接続は制御できません。ということで、まずはこれから始めたいと思います。
まず、ある点を基準点として、その点に対する他の点の角度を求めます。
これらの点を小さい角度から大きい角度まで接続すると、正多角形を描くことができます。
おおよその実装コードは次のとおりです。
let ball = [];let ballNum = 6;let firstBall = null;while(ballNum--) { let ball = new Ball(20, parseColor(Math.random() * 0xffffff)) ball.x = Math.random( ) * 幅; ball.y = Math.random() * 高さ; ball.push(ball) if (!firstBall) { firstBall = ボール ball.angle = 0 } else { const dx = ball.x - firstBall.x, dy = ball.y - firstBall.y; ball.angle = Math.atan2(dy, dx) }}// ボールを結ぶ線を正多角形のボールにしてみます。 = ボール .sort((ballA, ballB) => { return ballA.angle - ballB.angle})
このようにして、最終的に接続を描画するときに、配列を横断することによって角度を小さいものから大きいものまで描画することができます。
効果は次のとおりです。
これにより、交差する線の数を大幅に減らすことができますが、それでも完全に回避することはできません。
次に、このソリューションを最適化してみます。たとえば、Math.abs を使用して角度を修正したり、各点を結ぶ最小の角度を持つ点を見つけたりします。しかし結果はダメ、一線越えは避けられない。
中心点に基づいて回転しますそこで別のアイデアが思い浮かびました。多角形の中心点が決定できれば、中心点に対するすべての点の角度を個別に計算でき、これらの点を時計回りまたは反時計回りに接続できます。
しかし、インターネットで長時間検索した結果、すべての点アルゴリズムでは、特定の時計回りの順序で配置された一連の点が必要であることがわかりました。
しかし、これらの点があれば、すでに多角形を描くことができます。諦めなければならなかった
X軸2極セグメンテーション絶望のあまり、Google で検索しなければならなかったのですが、Zhihu で非常に良い答えを見つけました。平面上の無秩序な点のグループを単純な多角形に接続するにはどうすればよいですか?
特定のアルゴリズムの説明については、答えを見てください。詳細には触れません。
ただし、実際には、上のチェーンと下のチェーンを接続するときは、上のチェーンが X 軸の降順に接続され、下のチェーンが X 軸の昇順に接続されるようにするだけで済みます (図では、反時計回り方向)。 X 軸が同じ点については、Y 軸が大きいか小さいかは関係ありません。
実装されると、回答のアルゴリズムに従って厳密に実装されます。
ある点が上のチェーンに属するか、下のチェーンに属するかを判断する場合、最初の考え方は、2 つの点に基づいて直線の関数方程式を求め、次にその点の座標を導入して計算することです。しかし、後で考えたのですが、すべての点は左端の極を使用して斜角を計算し、それを角度の大きさに応じて分割する方が視覚的に理解しやすいです。
おおよそのコードは次のとおりです。
let ball = [];let tempBalls = [];let ballNum = 6;let isDragingBall = false;while(ballNum--) { let ball = new Ball(10, parseColor(Math.random() * 0xffffff)) ボール。 x = Math.random() * 幅; ball.y = Math.random() * 高さ;点を tempBalls.length -1];let smallXBalls = tempBalls.filter(ball => ball.x === firstBall.x) で昇順に並べ替えます。 bigXBalls = tempBalls.filter(ball => ball.x === lastBall.x)// 複数の左右の極がある状況を処理します if (smallXBalls.length > 1) { smallXBalls.sort((ballA, ballB) => { return ballB.y - ballA.y })}if (bigXBalls.length > 1) { bigXBalls.sort((ballA, ballB) => { return ballB.y - ballA.y })}firstBall = smallXBalls[0]lastBall = bigXBalls[0]//極線の角度を取得 let splitLineAngle = Math.atan2(lastBall.y - firstBall.y, lastBall.x - firstBall.x);let upperBalls = [], lowerBalls = [];//他のすべての点は firstBall で角度を計算します// SplitLineAngle より大きいものはダウンチェーンです // その他はアップチェーンです tempBalls.forEach(ball => { if (ball === firstBall || ball === lastBall) { return false } let angle = Math.atan2(ball.y - firstBall.y, ball.x - firstBall.x); if (角度 > splitLineAngle) { lowerBalls.push(ball) } else { upperBalls.push(ball) }})// 同じ状況の X 軸での並べ替えを処理 lowerBalls = lowerBalls.sort((ballA, ballB) => { if (ballA.x !== ballB.x) { return ballA.x - ballB.x } return ballB.y - ballA.y})upperBalls = upperBalls.sort((ballA, ballB) => { if (ballA.x !== ballB.x) { return ballB.x - ballA.x } return ballB.y - ballB.x})// すべての点を反時計回りに接続します ball = [firstBall].concat( lowerBalls, [lastBall], upperBalls)balls = ball. map((ball, i) => { ball.text = i + 1; ボールを返す})
最終的に返されるボールは、反時計回りにソートされたポリゴン ポイントです。
効果は次のとおりです。
各ボールの内部状態は次のとおりです。
以上がこの記事の全内容です。皆様の学習のお役に立てれば幸いです。また、VeVb Wulin Network をご支援いただければ幸いです。