前提
javascriptで図形のXY座標を計算しています。
図形がそのままの場合のXY座標は分かっておりますが、
回転した場合のXY座標の計算方法が分かりません。
実現したいこと
図形が回転した場合のXY座標の計算方法が分かりません。
恐らくサイン&コサイン??を使って回転した場合の座標を計算すると思うのですが、中学生の頃に勉強しただけで今は分かりません。
添付ファイルのような場合の45度に回転した場合のXY座標の計算方法をご教授頂けないでしょうか?
分かっていること
・回転していない場合のXY座標
・回転している角度
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答2件
0
canvasを使うならrotateメソッドで簡単に回転描画できると思いますが、関数にしてみました。
js
1const radian = degree => degree * Math.PI / 180; 2const sin = degree => Math.sin(radian(degree)); 3const cos = degree => Math.cos(radian(degree)); 4const rotate = (x, y, centerX, centerY, degree) => { 5 x -= centerX; 6 y -= centerY; 7 return [x * cos(degree) - y * sin(degree) + centerX, 8 x * sin(degree) + y * cos(degree) + centerY] 9}; 10 11console.log(rotate(0, 0, 50, 50, 45), rotate(100, 100, 50, 50, 45)) 12console.log(rotate(0, 0, 50, 50, 90), rotate(100, 100, 50, 50, 90)) 13console.log(rotate(0, 0, 50, 50, 180), rotate(100, 100, 50, 50, 180)) 14console.log(rotate(0, 0, 50, 50, 270), rotate(100, 100, 50, 50, 270)) 15console.log(rotate(0, 0, 50, 50, 360), rotate(100, 100, 50, 50, 360))
text:実行結果
1[ 49.99999999999999, -20.710678118654755 ] [ 50.00000000000001, 120.71067811865476 ] 2[ 100, 0 ] [ 0, 100 ] 3[ 100, 100 ] [ -7.105427357601002e-15, 7.105427357601002e-15 ] 4[ 7.105427357601002e-15, 100 ] [ 100, -7.105427357601002e-15 ] 5[ -1.4210854715202004e-14, 1.4210854715202004e-14 ] [ 100.00000000000001, 99.99999999999999 ]
投稿2022/09/23 00:13
編集2022/09/23 00:26総合スコア5406
0
ベストアンサー
回転の中心になる点の座標が質問に書いてありませんが、回転前の正方形の対角線の交点(50, 50)を中心に時計回りの方向を回転角の正方向という前提で回答します。
この前提で答えを先にいうと、
- 左上の(0, 0)は(50, 50-50√2)に、
- 右下の(100, 100)は(50, 50+50√2)に
移動します。(※これの算出は追記3を参照)
(0, 0)や(100, 100)に限らず任意の点を、45度に限らず任意の角度で回転した後の座標を計算で求めるときに基本になるのは(数学で通常使う)xy平面における点(x, y)を原点(0,0)を中心として反時計回りの方向にθ(ラジアンあるいは度)回転した座標を(x', y') としたときの
x' = x・cosθ - y・sinθ
y' = x・sinθ + y・cosθ
です。学校だとこれは平面ベクトルの一次変換ですので高2あたりで習うのではないかと思います。(このあたりを復習したい場合に参考になるサイトはいくつもありますが、例えば 高校数学の基本問題 などがあります)
これを質問の与件に使おうとすると、3つ気をつけることがあります。
- 質問の座標系は(数学で通常使うxy平面と違って)下にいくほどy座標が増える。
- 質問にある回転は原点中心の回転ではなく (50, 50) を中心とする回転である。
- 上記の(x, y)から(x',y')を得る式のθは反時計回りを正としているが質問の与件では時計回りを正としている。
これらに気をつけつつ、質問の与題を上記の(x, y)から(x',y')を得る基本の式を使って計算しようとすると、通常のxy平面の原点を中心とする回転の問題として扱えるように回転前の座標を適宜変換してから回転を行って、元の座標系に戻すというやり方が考えられます。以下はこれを行うために、通常のxy平面上の点(x, y)を表すクラスを実装したものです。
javascript
1// 通常の xy平面上の点(x, y)を表すクラス 2class PointXY { 3 constructor(x, y) { 4 this.x = x; 5 this.y = y; 6 } 7 8 // 平行移動 9 add(dx, dy) { 10 this.x += dx; 11 this.y += dy; 12 return this; 13 } 14 15 // x軸に関する折り返し 16 refX() { 17 this.y *= -1; 18 return this; 19 } 20 21 // 原点を中心として反時計回りに θ(theta)ラジアン回転 22 rotate(theta) { 23 [this.x, this.y] = [ 24 this.x * Math.cos(theta) - this.y * Math.sin(theta), 25 this.x * Math.sin(theta) + this.y * Math.cos(theta) 26 ] 27 return this; 28 } 29 30 toString() { 31 return `(${this.x}, ${this.y})`; 32 } 33}
上記のようなクラス PointXY
を作っておくと、質問の与題にある左上(0, 0) の回転後の座標p0
、および右下(100,100) の回転後の座標p1
は以下で計算できます。
javascript
1const p0 = new PointXY(0, 0).add(-50, -50).refX().rotate(-Math.PI / 4).refX().add(50, 50); 2console.log(`${p0}`); // => (49.99999999999999, -20.710678118654755)
javascript
1const p1 = new PointXY(100, 100).add(-50, -50).refX().rotate(-Math.PI / 4).refX().add(50, 50); 2console.log(`${p1}`); // => (50.00000000000001, 120.71067811865476)
これらは冒頭に書いた(50, 50-50√2) および (50, 50+50√2) となっています。(プログラムでの計算結果のx座標が正確に50ぴったりにならないのはコンピュータで数値計算する上で避けられない誤差です。)
追記1
上記の回答では、高校で習う(数学で通常使う)xy座標における原点中心の反時計回りの回転の問題に置き換えていますが、これは考え方の根本を示したものです。しかしコンピュータグラフィクスでは左上を原点として右方向をxの正方向、下方向をyの正方向とすることが常であることを考えると、この(画面の)下をy座標の正方向とする座標系での時計回りを正とする回転による写像を、いちいち数学のxy座標での変換に置き換えないで求める式を得ておきたいところです。これは以下のように導出できます。
- 数学で通常使うxy座標で、原点を中心に反時計回りにθ回転させる変換行列をR(θ)とし、x軸に関しての対称移動の行列を A とします。
- コンピューター座標における、原点を中心に時計回りにθ回転させる変換行列をR'(θ)とします。これが求めたい行列です。
そうすると、R, R', A の間には以下の関係が成り立ちます。
R'(θ) = A・R(-θ)・A
この等式の右辺に出てくる3つの行列の積を計算すると実は R(θ)に等しくなります。つまり得たかったR'(θ) としては R(θ) をそのまま使えるのでした。このことを使いかつ回転の中心が原点ではない (a, b) であった場合は、回転の前後に (a, b)分の平行移動をすることを加味すればよいです。
上記をコードとしてまとめると、質問で求められている変換後の座標を求める関数を以下のように書けます。(回転の角度の引数を deg
として度数で与えることができるようにしています。また引数の a
, b
は中心のx,y座標です。)
javascript
1function rotate(point, deg=0, a=0, b=0) { 2 const theta = deg * Math.PI / 180; 3 const dx = point.x - a; 4 const dy = point.y - b; 5 const x = dx * Math.cos(theta) - dy * Math.sin(theta) + a; 6 const y = dx * Math.sin(theta) + dy * Math.cos(theta) + b; 7 return { x, y }; 8} 9 10console.log(rotate({ x: 0, y: 0 }, 45, 50, 50)); // => { x: 49.99999999999999, y: -20.710678118654755 } 11console.log(rotate({ x: 100, y: 100 }, 45, 50, 50)); // => { x: 50.00000000000001, y: 120.71067811865476 }
追記2
上記の追記1では、
R'(θ) = A・R(-θ)・A
から R'(θ) が R(θ)に等しいことを得ましたが、コンピュータグラフィクスを専門にしている方など線形代数に多少なりとも慣れている人だったら基底ベクトルの行き先をだけを考慮して、より直裁にこう考えるかもしれません。
- 下がyの正方向の(左手系の)xy座標においても、時計回りを回転角θの正方向とするのであれば、基底ベクトル (1, 0) と(0, 1)が、それぞれ (cosθ, sinθ) および (-sinθ, cosθ)に写像されることは通常の(右手系の)xy座標のときと変わらない。従って、R'(θ) は R(θ) と等しい。
追記3
回答の冒頭で
この前提で答えを先にいうと、
・左上の(0, 0)は(50, 50-50√2)に、
・右下の(100, 100)は(50, 50+50√2)に移動します。
と書きましたが、これらの座標は「左上と右下の45度回転後の座標のみを求めればよい問題」として以下のように問題を設定して求解することができます。
問題の設定
上記の図において、以下の1. 〜 5 . が条件として与えられている。
- x軸は水平の右向き。y軸は垂直の下向き
- 四角形ABCDは一辺の長さが100の正方形
- 対角線ACはY軸と平行(したがって対角線BDはX軸と平行)
- 正方形ABCDの中心(=2つの対角線ACとBDの交点)をPとする。
- P の座標は (50, 50)である。
このとき、点Aと点Cの座標を求めよ。
解法
-
対角線ACがy軸と平行なので、点Aと点Cおよび正方形の中心P のx座標は同じである。Pのx座標は50なので、AとCのx座標もまた50である。
-
次にAとCの各y座標を求める。
-
PA の長さを h とおくと、Aのy座標は 50-h である。 ---- ①
-
また、PC=PA なので、Cのy座標は 50+h である。---- ②
-
そこで h についての方程式を立ててそれを解き、h の値が得られれば①、②から AおよびCのy座標が得られる。
-
hの方程式を立てるために三角形ABCに着目する。これは角ABCが直角で辺BAと辺BCの長さが等しい直角二等辺三角形である。
-
直角二等辺三角形において斜辺と他の一辺の比は√2 対 1であるので、
AC : AB = √2 : 1
これに AB = 100 と AC = PA + PC = h + h = 2h を代入して以下の比例式を得る。
2h : 100 = √2 : 1
- 比例式の内項の積と外項の積は等しいので
2h × 1 = 100 × √2 ∴ 2h = 100√2
この h についての方程式を解けば、
h = 50√2
を得る。
- よって、①からAのy座標は50-50√2 、 ②からCのy座標は50+50√2 となり、両点のx座標は50であるからこれらの点の座標は以下である。
A (50, 50-50√2 )
C (50, 50+50√2 )
なお上記の「解法」の中の
直角二等辺三角形において斜辺と他の一辺の比は√2 対 1である
というのは、中学の終わりのほうで習う三平方の定理(あるいはピタゴラスの定理)から導かれます。
投稿2022/09/22 22:02
編集2022/09/29 01:40退会済みユーザー
総合スコア0
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。