忍者ブログ
[PR]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

【2024年04月29日08:31 】 |
C#3D立方体ワイヤーフレーム 序(第1回) for VS2013 Express
C#3D立方体 for VS2013 Express




参考:Visual C++ を使ってルービックキューブを作ってみよう

参考URLを元にC#用に3D立方体を作成した。
投稿できる量に制限があるので4回に分けることにする。




ソリューションエクスプローラー内で右クリックして、クラスを追加する。


全てをForm1.csのファイルにまとめていいのだが、
さすがに煩雑になるので、クラス毎にファイルを分けることにした。
C#の機能で「public partial」で囲えば、一つのファイルとして扱ってくれる。みたいだ。

public partial class Form1 : Form

初期状態は以下のような感じなので赤い部分を追加する。



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
     class Class1
     {
     }
 }
}



CThreeDクラス

#CThreeD.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
namespace WindowsFormsApplication1
{
    
    public partial class Form1 : Form
    {
        //絶対座標の中心
        static public int RUBIC_X = 300;
        static public int RUBIC_Y = 300;
        public class MyPoint3D
        {
            public int x;
            public int y;
            public int z;
            public List<int> bond = new List<int>();//接続先
            public int num;
            public MyPoint3D() { }
            public MyPoint3D(int _x, int _y, int _z)
            {
                x = _x;
                y = _y;
                z = _z;
            }
        }
        //デカルト座標
        public class Vertex
        {
            public int group;     //グループ
            public List<int> bond = new List<int>();//接続先
            public int x, y, z;
            public Point point;
            public Vertex() { }
        }
        //極座標
        public class Polar
        {
            public int r;
            public double p, q;
        }
        //線の構造体
        public class Bond
        {
            public Vertex[] vertex;
            public Vertex center = new Vertex(); //相対座標
        }
        //面の構造体
        public class Face
        {
            public Vertex[] vertex = new Vertex[4];
            public Vertex center = new Vertex();
        }
        class CThreeD
        {
            
            /*=============================================================================
             機能  2次元座標系に変換する
             引数  ViewPolar : 視点の極座標
                   vertex    : 変換したい座標
            =============================================================================*/
            public void 
                TransferScreen(Polar ViewPolar, ref Vertex pVertex)
            {
                double a;
                double[,] matrix = new double[3, 3];
                Vertex vertex = new Vertex();
                a = ViewPolar.r * Math.Cos(ViewPolar.q);
                /****************************************************************
                      回転行列を作る
                ****************************************************************/
                
                matrix[0, 0] = -1 * Math.Sin(ViewPolar.p);
                matrix[0, 1] = Math.Cos(ViewPolar.p);
                matrix[0, 2] = 0;
                matrix[1, 0] = -1 * Math.Sin(ViewPolar.q) * Math.Cos(ViewPolar.p);
                matrix[1, 1] = -1 * Math.Sin(ViewPolar.q) * Math.Sin(ViewPolar.p);
                matrix[1, 2] = Math.Cos(ViewPolar.q);
                matrix[2, 0] = -1 * Math.Cos(ViewPolar.q) * Math.Cos(ViewPolar.p);
                matrix[2, 1] = -1 * Math.Cos(ViewPolar.q) * Math.Sin(ViewPolar.p);
                matrix[2, 2] = -1 * Math.Sin(ViewPolar.q);
                
                /*
                 X軸とY軸
                     -sin.p         cos.p         0
                     -sin.q*cps.p  -sin.q*sin.p   cos.q
                     -cos.q*cos.p  -cos.q*sin.p  -sin.q
                  
                */
                TransferVertex(matrix, ViewPolar, ref pVertex);
            }
            /*=============================================================================
             機能  2次元座標系に変換する
             引数  matrix[][3] : 回転行列
                   ViewVertex  : 視点の座標
            =============================================================================*/
            public void TransferVertex(double[,] matrix, Polar ViewPolar, ref Vertex pVertex)
            {
                double x, y, z;
                Vertex vertex = new Vertex();
                PolarToVertex(ViewPolar, ref vertex);
                x = matrix[0, 0] * (pVertex.x - vertex.x)
                  + matrix[0, 1] * (pVertex.y - vertex.y)
                  + matrix[0, 2] * (pVertex.z - vertex.z);
                y = matrix[1, 0] * (pVertex.x - vertex.x)
                  + matrix[1, 1] * (pVertex.y - vertex.y)
                  + matrix[1, 2] * (pVertex.z - vertex.z);
                z = matrix[2, 0] * (pVertex.x - vertex.x)
                  + matrix[2, 1] * (pVertex.y - vertex.y)
                  + matrix[2, 2] * (pVertex.z - vertex.z);
                z = z / 300;
                pVertex.point.X = (int)(RUBIC_X + x / z);
                pVertex.point.Y = (int)(RUBIC_Y + y / z);
            }
            /*=============================================================================
             機能  極座標を直行座標に変換する
             引数  polar    : 極座標の座標値
                   pVertex  : 直行座標へのポインタ
            =============================================================================*/
            public void PolarToVertex(Polar polar, ref Vertex pVertex)
            {
                pVertex = new Vertex();
                pVertex.x = (int)(polar.r * Math.Cos(polar.q) * Math.Cos(polar.p));
                pVertex.y = (int)(polar.r * Math.Cos(polar.q) * Math.Sin(polar.p));
                pVertex.z = (int)(polar.r * Math.Sin(polar.q));
            }
            /*=============================================================================
             機能  極座標を回転移動する
             引数  new_polar : 回転後の座標
                   old_polar : 回転させたい極座標の座標値
                   rotateP   : Z軸を中心とした回転量
                   rotateQ   : 原点を中心としてXY平面と垂直な回転量
            =============================================================================*/
            public void TurnPolar( ref Polar new_polar, Polar old_polar, double rotateP, double rotateQ )
            {
                new_polar.p = old_polar.p += rotateP;
                new_polar.q = old_polar.q += rotateQ;
            }
        }
    }
}



CThreeD クラスの説明
このクラスは、デカルト座標情報をVertexに入れて
 TransferScreen()関数で、2次元座標に投影している。

ここさえ押さえれば、後は様々な立体の3次元情報を
このクラスに与えればいいだけにゃ~♪

なので、ここが肝なので、ちょっと深入りしてみる。


3次元空間の座標移動を、3次元アフィン変換っていうらしいにゃ。
参考URL
ロール(φ)ピッチ(θ)ヨー(ψ)で回転する場合



しかし、このプログラムでは、
TransferVertex()にて、PolarToVertex()で極座標を
直行座標に返還後、X軸回転、Y軸回転を行っている。

マウスで、グリグリと動かす際、縦方向と横方向の
どちらかをX軸固定、Y軸固定と見立ている仕組みですね。
つまり、2つの情報しかインプットできないので、Z軸固定回転は使用していない。




まずは、2次元の場合の座標移動について考えてみる。


下記図の、座標P(x ,y)からQ(x', y')の2次元アフィン変換は

x' = x *cosθ - y*sinθ
y' = x *sinθ + y*cosθ

となる。この式は、加法定理から求まる。






加法定理

三角形OPQを考える、余弦定理より 
PQ^2 = 2 - 2cos(αーβ) ・・・(1)
線分PQを座標で表した長さは
PQ^2=( cosβ ー cosα)^2  + ( sinβ ー sinα)^2  ・・・(2)
(1)(2)より
cos(αーβ)=cosα*cosβ +sinα* sinβ ・・・(3)
となる。(3)を用いて、
cos(α+β)=cos(αー(ーβ))
    =cosα*cos(ーβ) +sinα* sin(ーβ)
    =cosα*cosβ ー sinα* sinβ ・・・(4)
が求まる。sin(α+β)の場合は(4)を用いて
sin(α+β)=cos(90-(α+β))
    =cos((90-α)ーβ)
    =cos(90-α)*cosβ +sin(90-α)* sinβ
    =sinα*cosβ + cosα* sinβ ・・・(5)





いま、求めた 座標P(x ,y)からQ(x', y')の2次元アフィン変換は
Z軸を固定して回転した場合である。

x' = x *cosθ - y*sinθ
y' = x *sinθ + y*cosθ

X軸、Y軸を固定した場合の2次元アフィン変換については以下。





X軸固定とY軸固定の行列を掛けたのが以下(A)の行列となる。

       cos.p           0           sin.p 
       sin.q*sin.p     cos.q    -cos.p*sin.q
       -cos.q*sin.p     sin.q    cos.q*cos.p

で、実際のプログラムは以下の(B)の行列

       -sin.p           cos.p           0
       -sin.q*cos.p   -sin.q*sin.p     cos.q
       -cos.q*cos.p   -cos.q*sin.p   -sin.q


(A)と(B)が異なるのでなぜなんだろうと悩んだ。
列を入れ替えても同じだから それはよしとしても、符号が異なる。
試しに(A)の行列で実行してみた場合(以下)、

    //テストしてみた行列
     matrix[0, 0] = 1 * Math.Sin(ViewPolar.p);
                matrix[0, 1] = Math.Cos(ViewPolar.p);
                matrix[0, 2] = 0;
                matrix[1, 0] = -1 * Math.Sin(ViewPolar.q) * Math.Cos(ViewPolar.p);
                matrix[1, 1] = 1 * Math.Sin(ViewPolar.q) * Math.Sin(ViewPolar.p);
                matrix[1, 2] = Math.Cos(ViewPolar.q);
                
                matrix[2, 0] =  1 * Math.Cos(ViewPolar.q) * Math.Cos(ViewPolar.p);
                matrix[2, 1] = -1 * Math.Cos(ViewPolar.q) * Math.Sin(ViewPolar.p);
                matrix[2, 2] =  1 * Math.Sin(ViewPolar.q);

ちゃんと投影されるわけだがなんだか、違和感のある変な動きとなる。
なので(B)の行列を利用している。


ちゃんと理由があるはずなんだろうが、
頭が痛くなってきたので、これでよしとするかにゃ~(*´ω`*)。




この(B)の行列をつかうことで、X軸、Y軸それぞれで固定した座標変換を行った後に
次のPolarToVertex()関数で直行座標に変換している。


   public void PolarToVertex(Polar polar, ref Vertex pVertex)
            {
                pVertex = new Vertex();
                pVertex.x = (int)(polar.r * Math.Cos(polar.q) * Math.Cos(polar.p));
                pVertex.y = (int)(polar.r * Math.Cos(polar.q) * Math.Sin(polar.p));
                pVertex.z = (int)(polar.r * Math.Sin(polar.q));
            }


この関数では、極座標から、直行座標への変換を行っている。

 


以上が、座標を2次元に投影する仕組みである。
ざっくりと理解できたところで、



どこに座標を保持しているかというと、一旦MyPoint3Dクラスに保持している。


        public class MyPoint3D
        {
            public int x;
            public int y;
            public int z;
            public List<int> bond = new List<int>();//接続先
            public int num;
            public MyPoint3D() { }
            public MyPoint3D(int _x, int _y, int _z)
            {
                x = _x;
                y = _y;
                z = _z;
            }
        }

List<int>型は、C++のVector<int>に相当する。
ある座標の点が、どの座標の点と接続しているか分からなければ
線を引けないので、情報をもたせることにした。


格納の仕方は bond.Add()で格納する。
取り出し方は、bond.Countで要素数が分かるので
for(int i=0; bond.Count>i; i++) boud[i] ~
のようにする。

        //デカルト座標
        public class Vertex
        {
            public int group;     //グループ
            public List<int> bond = new List<int>();//接続先
            public int x, y, z;
            public Point point;
            public Vertex() { }
        }
いわゆる、XYZ座標で表現するのをデカルト座標という。
MyPoint3Dでワンクッション置いて、Vertexに入れている。


次回は、CBoundクラスで立方体の座標を決定する。


終わり。
PR
【2016年09月11日14:52 】 | グラフィック | コメント(0)
コメントの投稿













前ページ | ホーム | 次ページ

忍者ブログ [PR]