Lost in the Matrix

Started by
8 comments, last by abominableCodeman 6 years, 8 months ago

Basically I wanted to become more familiar with matrix usage, starting 2D, so I put up a basic setup where I have an "interactive wheel" that spits out whatever angle its handle is set to (image below)

Now I have my basisVector y and x because I get the feeling those are needed, and I have a horizontal line floating on top of y.

My goal would be to rotate that line with a vector Matrix multiplication based on the angle I get from my interactive wheel, as you would do in 3d I guess (the whole point of this is to get me familiar with matrices usage after all, so it need to be done that way), the problem is that from here I don't know how to proceed to build my rotation matrix, where to place my elements, what multiply with what...I am kind of lost.

so this below is the code situation, and I need some help to get from where I am to where I want to be... :S


float angle = mHandle->getHandleAngle();
D2D1_POINT_2F basisX = { 1,0 };
D2D1_POINT_2F basisY = { 0,1 };

//draw line
mBrush->SetColor(D2D1::ColorF(0.87f, 0.3f, 0.36f, 1.f));
D2D1_POINT_2F lineP1 = D2D1::Point2F(mOriginX - 100, mOriginY - 150);
D2D1_POINT_2F lineP2 = D2D1::Point2F(mOriginX + 100, mOriginY - 150);
mRenderTarget->DrawLine(lineP1, lineP2, mBrush, 2.f);

//replace draw line above with a line rotated by a rotation matrix
mRenderTarget->DrawLine(D2D1::Point2F(),//Begin point
			D2D1::Point2F(),//End point
			mBrush, 2.f);

HY5KITZ.png

Advertisement

Check out this article here https://msdn.microsoft.com/en-us/library/windows/desktop/ff684172(v=vs.85).aspx. The classic clock exercise will give you a good idea as to how rotations work and how to use them in Direct2D.

Basically you will need to use the D2D1::Matrix3x2F class (particularly the D2D1::Matrix3x2F::Rotation(angle, center) function) to create a rotation matrix and then apply it to both lineP1 and lineP2 before rendering them. 

This for understanding: https://open.gl/transformations

This for playing with it: https://webglfundamentals.org/webgl/lessons/webgl-2d-rotation.html

thanks ninnghazad, awesome links, I'll come back when I'm done reading :)

Apologize samoan, for some reason I didn't saw your reply the first time trough, that' weird :P

I'll check that too, thanks! :D

@ninnghazad:  that link really helped, thanks again :D I started reading the openGL link, didn't finished it because I wanted to test with code after reading about the rotation matrix, and it works. I'll go back finish reading since I need a better grasp on this, meanwhile, below the code and gif of the thing working :P

Ignore the fact I am re-calculating the shape point at every draw even though those won't change, I just needed to see it all together in a single page.


void Matrix2DClient::Update(float dt)
{
	mHandle->update();
}

void Matrix2DClient::Draw()
{
	mRenderTarget->Clear(D2D1::ColorF(0.34f, 0.36f, 0.4f, 1.0f));
	makeGrid(100, 45.f);

	mHandle->draw();


	float angle = mHandle->getHandleAngle();//angle in degree

	D2D1_POINT_2F basisX = { cos(rad(angle)), sin(rad(-angle)) }; //for angle == 0°,  (1,0)
	D2D1_POINT_2F basisY = { -sin(rad(-angle)), cos(rad(angle)) };//for angle == 0°,  (0,1)
	{
		float displayLenght = 100;
		//draw basisX
		mBrush->SetColor(D2D1::ColorF(0.86f, 0.1f, 0.2f, 1.f));
		float basisXnewX = mOriginX + basisX.x * displayLenght; //draw axis at origin
		float basisXnewY = mOriginY + basisX.y * displayLenght; //draw axis at origin
		mRenderTarget->DrawLine(D2D1::Point2F(mOriginX, mOriginY),
								D2D1::Point2F(basisXnewX, basisXnewY), mBrush, 2.f);
		mRenderTarget->DrawTextW(L"x", 1, mTextFormat, D2D1::RectF(basisXnewX, basisXnewY, 1000, 1000), mBrush);
		
		//draw basisY
		mBrush->SetColor(D2D1::ColorF(0.1f, 0.86f, 0.2f, 1.f));
		float basisYnewX = mOriginX + -basisY.x * displayLenght; //draw axis at origin
		float basisYnewY = mOriginY + -basisY.y * displayLenght; //draw axis at origin
		mRenderTarget->DrawLine(D2D1::Point2F(mOriginX, mOriginY),
								D2D1::Point2F(basisYnewX, basisYnewY), mBrush, 2.f);
		mRenderTarget->DrawTextW(L"y", 1, mTextFormat, D2D1::RectF(basisYnewX, basisYnewY, 1000, 1000), mBrush);
	}

	//star image points
	const unsigned points = 5;
	float r = 100.f;
	D2D1_POINT_2F linePoint[points] = {
	D2D1::Point2F(   r*cos(rad(90))  ,    r* -sin(rad( 90))),//1
	D2D1::Point2F(   r*cos(rad(234)) ,   -(r*sin(rad(234)))),//2
	D2D1::Point2F(    r*cos(rad( 18)),     r*-sin(rad( 18))),//3
	D2D1::Point2F(    r*cos(rad(162)),     r*-sin(rad(162))),//4
	D2D1::Point2F(    r*cos(rad(306)),  -(r* sin(rad(306)))),//5
	};
	//offset 
	float offsetX = 200;
	float offsetY = 20;
	for (auto& p : linePoint)
	{
		p.x += offsetX;
		p.y -= offsetY;
	}

	//rotation matrix|  3rd column is for translate the points to the origin (center of screen)
	  	     //columns-> |   1   | |   2   |  |   3   |
	float R[3][3] = /*row1*/{ basisX.x, basisY.x, mOriginX ,

			/*row2*/  basisX.y, basisY.y, mOriginY ,

			/*row3*/	 0    ,     0   ,       1
	};

	//for every point, apply rotation matrix
	for (size_t i = 0; i < points; i++)
	{
		D2D1_POINT_2F newPoint = D2D1::Point2F(
			R[0][0] * linePoint[i].x + R[0][1] * linePoint[i].y + R[0][2] * 1,//X
			R[1][0] * linePoint[i].x + R[1][1] * linePoint[i].y + R[1][2] * 1 //Y
		);
		linePoint[i] = newPoint;
	}

	//draw line
	mBrush->SetColor(D2D1::ColorF(0.87f, 0.3f, 0.36f, 1.f));
	for (size_t i = 0; i < points; i++)
	{
		if (i == points - 1) {//connect last point with first
			mRenderTarget->DrawLine(linePoint[i], linePoint[0], mBrush, 2.f);
		} else {
			mRenderTarget->DrawLine(linePoint[i], linePoint[i + 1], mBrush, 2.f);
		}

	}

}

 

VY5wCU3.gif

 

I've took it a step further, added the third dimension using direct2D, feels like cheating :D

I was so focused on having it work trough trials and errors that the code became real ugly, so I'll refrain from pasting that xD

I guess now is only a matter of deleting the code and do it again, until I can do all the steps without any mistakes :P

rGoTTgn.gif

Experiments went too far, the cube took life and seems threatening, I'll call it the "slimy dancing cube of spite" and proceed to abort experiments. It looks fun and I thought was worth sharing :D

Also the bad random code for it in the spoiler, in case someone need a slimy dancing cube 

image:

tte6lRG.gif

Spoiler


Matrix2DClient::Matrix2DClient(HINSTANCE instance, std::wstring caption, float clientWidth, float clientHeight)
    :D2DApp(instance, caption, clientWidth, clientHeight), mOriginX{ 0 }, mOriginY{ 0 }
{
    std::array<std::array<float, 3>, 9> tempCubePoint = {
        -70.82,   -112.90,   -40.75,//0
        -0.34 ,   -170.82,      0.2,//1
        0.11,   -112.90,    81.71,//2
        -70.59,    -54.97,    40.75,//3
        0.34,      2.38,     -0.2,//4
        0.11,    -55.54,   -81.71,//5
        70.59,   -113.46,   -40.75,//6
        70.82,    -55.54,    40.75, //7
        1,1,1
    };

    cubePoint = tempCubePoint;
    //cubes is an array of 250 cubes
    for (auto& c : cubes) { c = tempCubePoint; }
}

void Matrix2DClient::Draw()
{
    static D2D1_COLOR_F clearColor = D2D1::ColorF(0.34f, 0.39f, 0.4f, 1.0f);
    mRenderTarget->Clear(clearColor);
    makeGrid(100, 45.f);

    static float constRotation;
    constRotation += constRotation + 1.7 < 360 ? 1.7 : -constRotation;

    float RX[3][3] = { 1     ,      0     ,     0     ,

                         0       ,cos(rad(constRotation)) ,sin(rad(constRotation)),

                         0     ,-sin(rad(constRotation)),cos(rad(constRotation))
    };

    float RY[3][3] = { cos(rad(constRotation)),     0     ,-sin(rad(constRotation)),

                               0      ,             1     ,           0            ,

                       sin(rad(constRotation)),     0,      cos(rad(constRotation))
    };

    float RZ[3][3] = { cos(rad(0)),  sin(rad(0)),  0,

                      -sin(rad(0)),  cos(rad(0)),  0,

                             0        ,         0       ,  1
    };

    for (size_t c = 0; c < cubes.size(); c++)
    {
        std::array<std::array<float, 3>, 3> finalMatrix = mBasisVectors;
        std::array<std::array<float, 3>, 3> intermediateMatrix1 = mBasisVectors;
        std::array<std::array<float, 3>, 3> intermediateMatrix2 = mBasisVectors;


        //rotate on axis X  (basisVectors x RotationX) = intermediateMatrix1
        for (size_t i = 0; i < 3; i++)
        {
            for (size_t j = 0; j < 3; j++)
            {
                float sum = 0;
                for (size_t k = 0; k < 3; k++)
                {
                    sum += mBasisVectors[i][k] * RX[k][j];
                }
                intermediateMatrix1[i][j] = sum;
            }
        }

        //add scale
        static float size = 0.02f;
        static int sign = 1;
        if (c == 0)
        {
            float increase = 0.0012f;
            if (size + increase*sign > 0.02f || size + increase*sign < 0.01f) { sign *= -1; }
            size += increase*sign;
        }

        intermediateMatrix1[0][0] = intermediateMatrix1[0][0] * size*(((c + 1))/2);
        intermediateMatrix1[1][1] = intermediateMatrix1[1][1] * size*(((c + 1))/2);
        intermediateMatrix1[2][2] = intermediateMatrix1[2][2] * size*(((c + 1))/2);



        //rotate on axis Y (intermediateMatrix1 x RotationY) = intermediateMatrix2
        for (size_t i = 0; i < 3; i++)
        {
            for (size_t j = 0; j < 3; j++)
            {
                float sum = 0;
                for (size_t k = 0; k < 3; k++)
                {
                    sum += intermediateMatrix1[i][k] * RY[k][j];
                }
                intermediateMatrix2[i][j] = sum;
            }
        }


        //rotate on axis Z (intermediateMatrix2 x RotationZ) = finalMatrix
        for (size_t i = 0; i < 3; i++)
        {
            for (size_t j = 0; j < 3; j++)
            {
                float sum = 0;
                for (size_t k = 0; k < 3; k++)
                {
                    sum += intermediateMatrix2[i][k] * RZ[k][j];
                }
                finalMatrix[i][j] = sum;
            }
        }
        //for every point, apply the rotation finalMatrix

        for (size_t i = 0; i < cubePoint.size(); i++)
        {
            cubes[c][i][0] = cubePoint[i][0] * finalMatrix[0][0] + cubePoint[i][1] * finalMatrix[0][1] + cubePoint[i][2] * finalMatrix[0][2];//X
            cubes[c][i][1] = cubePoint[i][0] * finalMatrix[1][0] + cubePoint[i][1] * finalMatrix[1][1] + cubePoint[i][2] * finalMatrix[1][2];//Y
            cubes[c][i][2] = cubePoint[i][0] * finalMatrix[2][0] + cubePoint[i][1] * finalMatrix[2][1] + cubePoint[i][2] * finalMatrix[2][2];//Z
            cubes[c][8][0] = abs(size*(c / 2)); //color
            cubes[c][8][1] = abs(size*(c / 2)); //color
            cubes[c][8][2] = abs(size*(c / 2)); //color
        }
    }


    //draw cube
    for (size_t c = 0; c < cubes.size(); c++)
    {
        mBrush->SetColor(D2D1::ColorF(sin(rad(cubes[c][1][1]*2)), cos(rad(cubes[c][0][0])) , cos(rad(cubes[c][0][0])), 1.f));
        float strokeSize = 2.f;
        mRenderTarget->DrawLine(D2D1::Point2F(mOriginX + cubes[c][0][0], mOriginY + cubes[c][0][1]), D2D1::Point2F(mOriginX + cubes[c][1][0], mOriginY + cubes[c][1][1]), mBrush, strokeSize);
        mRenderTarget->DrawLine(D2D1::Point2F(mOriginX + cubes[c][1][0], mOriginY + cubes[c][1][1]), D2D1::Point2F(mOriginX + cubes[c][6][0], mOriginY + cubes[c][6][1]), mBrush, strokeSize);
        mRenderTarget->DrawLine(D2D1::Point2F(mOriginX + cubes[c][6][0], mOriginY + cubes[c][6][1]), D2D1::Point2F(mOriginX + cubes[c][5][0], mOriginY + cubes[c][5][1]), mBrush, strokeSize);
        mRenderTarget->DrawLine(D2D1::Point2F(mOriginX + cubes[c][5][0], mOriginY + cubes[c][5][1]), D2D1::Point2F(mOriginX + cubes[c][0][0], mOriginY + cubes[c][0][1]), mBrush, strokeSize);
        mRenderTarget->DrawLine(D2D1::Point2F(mOriginX + cubes[c][1][0], mOriginY + cubes[c][1][1]), D2D1::Point2F(mOriginX + cubes[c][2][0], mOriginY + cubes[c][2][1]), mBrush, strokeSize);
        mRenderTarget->DrawLine(D2D1::Point2F(mOriginX + cubes[c][6][0], mOriginY + cubes[c][6][1]), D2D1::Point2F(mOriginX + cubes[c][7][0], mOriginY + cubes[c][7][1]), mBrush, strokeSize);
        mRenderTarget->DrawLine(D2D1::Point2F(mOriginX + cubes[c][5][0], mOriginY + cubes[c][5][1]), D2D1::Point2F(mOriginX + cubes[c][4][0], mOriginY + cubes[c][4][1]), mBrush, strokeSize);
        mRenderTarget->DrawLine(D2D1::Point2F(mOriginX + cubes[c][0][0], mOriginY + cubes[c][0][1]), D2D1::Point2F(mOriginX + cubes[c][3][0], mOriginY + cubes[c][3][1]), mBrush, strokeSize);
        mRenderTarget->DrawLine(D2D1::Point2F(mOriginX + cubes[c][3][0], mOriginY + cubes[c][3][1]), D2D1::Point2F(mOriginX + cubes[c][2][0], mOriginY + cubes[c][2][1]), mBrush, strokeSize);
        mRenderTarget->DrawLine(D2D1::Point2F(mOriginX + cubes[c][2][0], mOriginY + cubes[c][2][1]), D2D1::Point2F(mOriginX + cubes[c][7][0], mOriginY + cubes[c][7][1]), mBrush, strokeSize);
        mRenderTarget->DrawLine(D2D1::Point2F(mOriginX + cubes[c][7][0], mOriginY + cubes[c][7][1]), D2D1::Point2F(mOriginX + cubes[c][4][0], mOriginY + cubes[c][4][1]), mBrush, strokeSize);
        mRenderTarget->DrawLine(D2D1::Point2F(mOriginX + cubes[c][4][0], mOriginY + cubes[c][4][1]), D2D1::Point2F(mOriginX + cubes[c][3][0], mOriginY + cubes[c][3][1]), mBrush, strokeSize);
    }
}

 

 

that's pretty cool

This topic is closed to new replies.

Advertisement