Skin:
[NORMAL]
[BLUE] [DOS] [LIGHT]  / コピーするための表示 / 実行
このファイル: /home/web6047/www/cgi-bin/prj/imgs of index/20200418-/simple.html
1 <html><!--ESCAPEPROCESS-->
2 <head>
3 <meta content="text/html; charset=UTF-8" http-equiv="content-type">
4 <title>CANVASベジェ曲線を3D回転する</title>
5 <script>console.clear();</script>
6 <script>
7 function onloadx() {
8 kaitenKakudo = 0;
9 setInterval( draw, 100 );
10 }
11 function draw() {
12
13 cc = document.getElementById( "test" ).getContext( "2d" );
14 cc.clearRect( 0, 0, cc.canvas.width, cc.canvas.height );
15
16 cc.save(); //translate()等を使うならこれがあったほうが良い。
17
18 //原点を画面の中心に変更
19 cc.translate( cc.canvas.width / 2, cc.canvas.height / 2 );
20
21 if( 1 ) {
22 //ガイド「原点」を描画
23 cc.fillStyle = "red";
24 cc.fillRect( -2, -2, 4, 4 );
25 cc.fillText( " 原点", 0, 0 );
26 }
27
28 //各2D座標(ここでは「小文字の変数は2D座標」として区別します)
29 ax = 0; ay = 0; //a点
30 bx = 0; by = -100; //b点
31 cx = 0; cy = 50; //c点
32 dx = 100; dy = -100; //d点
33
34 //2Dの場合を描画
35 cc.beginPath();
36 cc.moveTo( ax, ay );
37 cc.bezierCurveTo( bx, by, cx, cy, dx, dy );
38 //a点を始点とし、b点、c点をベジェのハンドルとし、d点が終点である。
39 cc.strokeStyle = "blue";
40 cc.stroke();
41 cc.strokeText( "2D", dx, dy );
42
43 //---以上は2D。以降は3D。
44
45 //各点に z 座標を追加する(「大文字の変数は3D座標」として区別します)
46 AX = ax; AY = ay; AZ= 1000; //a点
47 BX = bx; BY = by; BZ= 1000; //b点
48 CX = cx; CY = cy; CZ= 1000; //c点
49 DX = dx; DY = dy; DZ= 1000; //d点
50 //これで3D空間上に同じ形の2Dの線が出現したことになる。
51 //Zが1000なのは、Zが0だと視点(レンズ)の位置に重なってしまうからです。
52 //3D空間上の奥行1000の位置の黒板に2Dの線を描いたと想像するとよい。
53
54 //【1】3D空間内での回転計算
55 kaitenKakudo += 0.1; //draw()が呼ばれるたびに回転を進める
56
57 //回転の中心 //試しに、
58 CENTERX = 0; //この値を10にすると回転の中心が右に10ずれる。
59 CENTERY = 0; //(ここではxz平面だけで回転するのでCENTERYは使用しない)
60 CENTERZ = 1000; //この値を1010にすると回転の中心が奥に10ずれる。
61
62 //xz平面とは、3D空間のx,y,zのうち、xとzだけに着目した平面
63 //のことで、空間を上から見下ろした図と言える。
64 //このプログラムではこれから、空間を上から見下ろした図において回転計算するので、
65 //正面から見ると手前から奥に向かって回転しているように見える。
66
67 if( 1 ) {
68 //ガイド「回転の中心軸」を描画
69 cc.save();
70 cc.fillStyle = "green";
71 cc.fillText( " 回転の中心軸", CENTERX, cc.canvas.height / 2 );
72 cc.beginPath();
73 cc.moveTo( CENTERX, -cc.canvas.height / 2 );
74 cc.lineTo( CENTERX, cc.canvas.height / 2 );
75 cc.strokeStyle = "green";
76 cc.setLineDash( [ 5, 5 ] );
77 cc.stroke();
78 cc.restore();
79 }
80
81 //3D空間上の点a(AX, AY, AZ)をxz平面上で回転(AX, AZ のみ計算される)
82 RES = rotate( CENTERX, CENTERZ, AX, AZ, kaitenKakudo );
83 AX = RES.X;
84 AZ = RES.Y;
85
86 //同様に、3D空間上の点b(BX, BY, BZ)をxz平面上で回転(BX, BZ のみ計算される)
87 RES = rotate( CENTERX, CENTERZ, BX, BZ, kaitenKakudo );
88 BX = RES.X;
89 BZ = RES.Y;
90
91 //同様に、3D空間上の点c(CX, CY, CZ)をxz平面上で回転(CX, CZ のみ計算される)
92 RES = rotate( CENTERX, CENTERZ, CX, CZ, kaitenKakudo );
93 CX = RES.X;
94 CZ = RES.Y;
95
96 //同様に、3D空間上の点d(DX, DY, DZ)をxz平面上で回転(DX, DZ のみ計算される)
97 RES = rotate( CENTERX, CENTERZ, DX, DZ, kaitenKakudo );
98 DX = RES.X;
99 DZ = RES.Y;
100
101 //以上が3D空間内での回転計算
102
103 //【2】3D空間の座標を2Dの画面座標へ変換する計算
104
105 s = 50; //焦点距離(レンズサイズともいう)
106 zoom = 20; //そのままだと小さいので拡大
107
108 //3D座標を2D座標にする計算(3D空間を2D画面へ投影する計算)
109 ah = AX * ( s / AZ ) * zoom; //2D画面横座標
110 av = AY * ( s / AZ ) * zoom; //2D画面縦座標
111 //ah, av は3D計算後の2D座標。
112 //変数名を ax, ayにすると、3D変換前の2D座標と変数名が競合するので、
113 //horizontal(水平)の h と、vertical(垂直)の v を取って、
114 //ah, av とした。
115 //この計算式は下記 Wikipedia のページに掲載されています。
116 //https://ja.wikipedia.org/wiki/3次元コンピュータグラフィックス#透視投影
117
118 //同様に、3D座標を2D座標にする計算(3D空間を2D画面へ投影する計算)
119 bh = BX * ( s / BZ ) * zoom;
120 bv = BY * ( s / BZ ) * zoom;
121
122 //同様に、3D座標を2D座標にする計算(3D空間を2D画面へ投影する計算)
123 ch = CX * ( s / CZ ) * zoom;
124 cv = CY * ( s / CZ ) * zoom;
125
126 //同様に、3D座標を2D座標にする計算(3D空間を2D画面へ投影する計算)
127 dh = DX * ( s / DZ ) * zoom;
128 dv = DY * ( s / DZ ) * zoom;
129
130
131 //3Dの場合を描画(上記の //2Dの場合を描画 と比べてください。内容が同一です)
132 cc.beginPath();
133 cc.moveTo( ah, av );
134 cc.bezierCurveTo( bh, bv, ch, cv, dh, dv );
135 //a点を始点とし、b点、c点をベジェのハンドルとし、d点が終点である。
136 cc.strokeStyle = "magenta";
137 cc.stroke();
138 cc.strokeText( "3D", dh, dv );
139 //描画の内容は同じで、変数の値が3D計算されただけです。
140 //それなのに3Dが描けています。なんて不思議な!
141 //「homepage6047 2020年 4月」の扉絵は
142 //この方法で描いています。
143
144 cc.restore(); //save()とrestore()は、beginとendのようなものだと思うと混乱しない。
145 }
146 function rotate( sx, sy, x, y, theta2 ) {
147 //数学の三角関数の回転 sx,syを中心にx,yを角度theta2だけ回転する
148 //方眼紙上で回転を考えるときの計算そのものです。
149 x -= sx; //原点をsx,syにする
150 y -= sy;
151 theta1 = Math.atan2( y, x ); //原点から見た現在座標の角度
152 hankei = Math.sqrt( x * x + y * y ); //x,yが作る直角三角形の斜辺の長さ(三平方の定理)
153 x = Math.cos( theta1 + theta2 ) * hankei;
154 y = Math.sin( theta1 + theta2 ) * hankei;
155 x += sx; //原点を元に戻す
156 y += sy;
157 return { X: x, Y: y };
158 //theta1, theta2は角度ですが、0~360度の単位ではありません。
159 //1メートルが3.28フィートであるように、
160 //ここで言う角度は、度ではなくラジアンという単位を使っていて、
161 //1ラジアンは57.2度です。
162 //度のほうが断然わかりやすいのに、どうしてラジアンなんて面倒な単位を使っているのでしょうか。
163 //難しい数学においては、ラジアンを使うと、式が簡潔になるのだそうです。
164 //また、難しい数学で度を使うと、誤差が生じる場合があるんだそうです。(Wikipedia「ラジアン」より)
165 }
166 </script>
167 <style>
168 </style>
169 </head>
170 <body onload="onloadx()">
171 <canvas id="test" width="320" height="240" style="
172 width : 640px;
173 image-rendering: crisp-edges;
174 border : solid 1px black;
175 ">
176 there is no canvas.
177 </canvas>
178 </body>
179 </html>