How to rotate a 4x4 matrix (C#/XNA) - c#

I've got an array of [4,4]
X is the only one I "know", the rest is calculated with a simple double for-loop.
x 0 0 0
0 0 1 0
0 1 1 0
0 1 0 0
I want a function that take this array, and rotate it 90 degrees + / - while the position of x stays the same. (It's supposed to be tetris)
x 0 0 0
1 1 0 0
0 1 1 0
0 0 0 0
I know some way to hardcode the permutations but what that wouldn't learn me anything and it's frankly quite boring.
Would appreciate the help :>

I'm not sure how exactly you intend to rotate a matrix by 90 degrees and yet still have the top left X in the top left of the rotated version, but to rotate something by 90 degrees, I'd just make a new array, swap rows and columns and flip horisontally.
int[][] start = new int[4][];
start[0] = new int[4] { x, 0, 0, 0 }
start[1] = new int[4] { 0, 0, 1, 0 }
start[2] = new int[4] { 0, 1, 1, 0 }
start[3] = new int[4] { 0, 1, 0, 0 }
int[][] rotate = new int[4][];
for (int i=0; i<4; i++) rotate[i] = new int[4];
for (int i=0; i<4; i++)
for (int j=0; j<4; j++)
rotate[i][j] = start[j][i];
Rotate finishes with:
0, 0, 0, 0,
0, 0, 1, 1,
0, 1, 1, 0,
0, 0, 0, 0,
Now this is a diagonal flip (EDIT: It just occurs to me that this will keep x in the same position: perhaps this is what you mean?), but just do a horisontal flip and it should be fine:
for (int i=0; i<4; i++)
for (int j=0; j<4; j++)
rotate[i][3-j] = start[j][i];
Rotate finishes with:
0, 0, 0, 0,
1, 1, 0, 0,
0, 1, 1, 0,
0, 0, 0, 0,
(To tilt other way: rotate[i][j] = start[j][3-i];)
:)

Related

Uniformly sampling points in a cube using a coordinate system [C#, Unity]

I have a cube with six vertices of form (x, y, z), in a 3D coordinate system. All information about cube is available, including vertices, edge lengths, etc.
I would like an efficient way to uniformly return n ^ 3 points, with n points on each edge, something like the coordinate system itself. An image is attached (ignore the colouring) (source: Sebastian Lague's video on marching cubes):
I've more or less been able to implement this in Python, and I'd like a C# version.
An example of what I want:
I've used a unit cube at the origin as an example, what I'm after is pseudocode or logic that works for any cube at any coordinates.
More information: I'd like to have code that works for cubes in any orientation, but failing that code that works for cubes aligned with the grid but in any position.
Thanks to Jon Skeet for the help and clarification!
/*
Input:
(0, 0, 0)
(1, 0, 0)
(1, 1, 0)
(1, 1, 1)
(0, 1, 1)
(0, 0, 1)
(0, 1, 0)
(1, 0, 1)
27 points to be generated: n = 3
3 points per edge (INCLUDING VERTICES)
Output:
(0, 0, 0)
(1, 0, 0)
(1, 1, 0)
(1, 1, 1)
(0, 1, 1)
(0, 0, 1)
(0, 1, 0)
(1, 0, 1)
(0, 0.5, 0)
(0.5, 0, 0)
(0, 0, 0.5)
(0.5, 0.5, 0)
(0.5, 0, 0.5)
(0.5, 0.5, 0.5)
(0, 0.5, 0.5)
(1, 0.5, 0)
... etc
*/
I'm using it in Unity, so answers with Vector3 and such would work too.
Some pseudocode:
// MagicPointGenerator is really what I'm after
float[][] GeneratePoints(float[][] vertices, int pointsPerEdge)
{
float[][] points = new float[(int)Math.Pow(pointsPerEdge, 3)][3];
for (int i = 0; i <= pointsPerEdge; i++)
{
for (int j = 0; j <= pointsPerEdge; j++)
{
for (int k = 0; k <= pointsPerEdge; k++)
{
points[i + j + k] = MagicPointGenerator(i, j, k);
}
}
}
return points;
}

Unexpected output of Transform3D.Transform method in WPF

I need to define a 3D transformation and apply it to a point p ={1000, 0, 0}.
For example, say one needs to apply a Pi/2 rotation around the z axis. I defined the transformation using a MatrixTransform3D. From the code below:
EXPECTED OUTPUT: trPoint = {0, 1000, 0}
ACTUAL OUTPUT: trPoint = {0, -1000, 0}.
QUESTION: Perhaps the Transform3D.Transform method applies the inverse transform instead?
private void testTransformationMat() {
Point3D p = new Point3D(1000, 0, 0);
double angle = System.Math.PI / 2;
double cos = System.Math.Cos(angle);
double sin = System.Math.Sin(angle);
Matrix3D mat_z = new Matrix3D(cos, -sin, 0, 0,sin, cos, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
Transform3D tr = new MatrixTransform3D(mat_z);
Point3D trPoint = tr.Transform(p);
Debug.WriteLine(trPoint);
}
EDIT:
To get things straight, as far as I've always known, a homogeneous transformation is applied multiplying a 4x4 matrix with a 4x1 vector. For a +45deg rotation around the z axis, under right-hand convention, we obtain:
0.707 -0.707 0 0 1000 707
0.707 0.707 0 0 X 0 = 707
0 0 1 0 0 0
0 0 0 1 1 0
If we invert the matrix, the multiplication returns a negative y component which is not coherent with a +45deg rotation around Z.
0.707 0.707 0 0 1000 707
-0.707 0.707 0 0 X 0 = -707
0 0 1 0 0 0
0 0 0 1 1 0
From 3-D Transformations Overview
Note:Windows Presentation Foundation (WPF) 3-D is a right-handed
system, which means that a positive angle value for a rotation results
in a counter-clockwise rotation about the axis.

PCACompute() does not touch the Matrix<> ouput array parameter

I'm using the EmguCV 3.0.0 wrapper to OpenCV 3.0. The EmguCV PCACompute method wraps the OpenCV PCA::operator() method.
The following code compiles and runs. The comments should explain the intent just fine.
// Populate the 5 row by 8 column input array (5 samples of 8 dimensions).
// The sample dimensions (columns) vary like this:
// - low variance: 0, 1, 4, 5, 6, 7
// - high variance: 2, 3
Matrix<double> input = new Matrix<double>(5, 8);
var r = new Random();
for (int row = 0; row < 5; row++) {
input.Data[row,0] = r.Next(0, 10); // low variance
input.Data[row,1] = r.Next(0, 20); // low variance
input.Data[row,2] = r.Next(80, 210); // high variance
input.Data[row,3] = r.Next(0, 240); // highest variance
input.Data[row,4] = r.Next(20, 21); // very low variance
input.Data[row,5] = r.Next(0, 10); // low variance
input.Data[row,6] = r.Next(0, 10); // low variance
input.Data[row,7] = r.Next(200, 210); // low variance
}
// create output array for PCACompute()
var eigenvectors = new Matrix<double>(8, 8);
// create *empty* mean array so that PCACompute() calculates its own means
var means = new Mat();
// HERE IS THE MAGIC.
CvInvoke.PCACompute(input, means, eigenvectors);
But the magic is broken. eigenvectors is all zeros after all that. This pretty print code:
// print each eigenvector on its own line
for (int vectorIdx = 0; vectorIdx < eigenvectors.Rows; vectorIdx++) {
string vectorStr = "";
for(int dimension = 0; dimension < eigenvectors.Cols; dimension++) {
vectorStr += eigenvectors.Data[vectorIdx, dimension].ToString() + ", ";
}
Console.WriteLine("{ " + vectorStr.Substring(0, vectorStr.Length - 2) + " }");
}
gives this output:
{ 0, 0, 0, 0, 0, 0, 0, 0 }
{ 0, 0, 0, 0, 0, 0, 0, 0 }
{ 0, 0, 0, 0, 0, 0, 0, 0 }
{ 0, 0, 0, 0, 0, 0, 0, 0 }
{ 0, 0, 0, 0, 0, 0, 0, 0 }
{ 0, 0, 0, 0, 0, 0, 0, 0 }
{ 0, 0, 0, 0, 0, 0, 0, 0 }
{ 0, 0, 0, 0, 0, 0, 0, 0 }
In fact, if I set a member of eigenvectors before passing it to PCACompute:
eigenvectors.Data[1,1] = 42;
CvInvoke.PCACompute(input, means, eigenvectors);
the pretty print shows that eigenvectors was completely untouched by PCACompute:
{ 0, 0, 0, 0, 0, 0, 0, 0 }
{ 0, 42, 0, 0, 0, 0, 0, 0 }
{ 0, 0, 0, 0, 0, 0, 0, 0 }
{ 0, 0, 0, 0, 0, 0, 0, 0 }
{ 0, 0, 0, 0, 0, 0, 0, 0 }
{ 0, 0, 0, 0, 0, 0, 0, 0 }
{ 0, 0, 0, 0, 0, 0, 0, 0 }
{ 0, 0, 0, 0, 0, 0, 0, 0 }
Is this a bug, or am I doing this wrong?
Despite being compatible according to the type system, the 3rd parameter passed to PCACompute cannot be of Matrix<> type. Everything works fine if it is a Mat type, though!
Matrix<double> input = new Matrix<double>(5, 8);
var r = new Random();
for (int row = 0; row < 5; row++) {
input.Data[row,0] = r.Next(0, 10); // low variance
input.Data[row,1] = r.Next(0, 20); // low variance
input.Data[row,2] = r.Next(80, 210); // high variance
input.Data[row,3] = r.Next(0, 240); // highest variance
input.Data[row,4] = r.Next(20, 21); // very low variance
input.Data[row,5] = r.Next(0, 10); // low variance
input.Data[row,6] = r.Next(0, 10); // low variance
input.Data[row,7] = r.Next(200, 210); // low variance
}
// CHANGE HERE
// Matrix<> was not changed by PCACompute(), so change eigenvectors to a Mat
var eigenvectors = new Mat(8, 8, DepthType.Cv64F, 1);
// create *empty* mean array so that PCACompute() calculates its own means
var means = new Mat();
// Now the magic works fine.
CvInvoke.PCACompute(input, means, eigenvectors);
Now eigenvectors contains more reasonable data (though it takes different accessor code in the pretty print function)
> eigenvectors.GetValueRange().Max
0.999418
> eigenvectors.GetValueRange().Min
-0.6326281

First isometric game

I have a problem with my first isometric game. I do not know how to do to my player able to approach the edge of the wall. In this moment player maybe move about in green area.
My map:
int[,] map = new int[,]
{
{1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1}
};
Variables:
int TileWidth = 50;
int TileHeight = 50;
int posX = 2; // map X position
int posY = 2; // map Y position
float playerX = 2 * 50; // player X position
float playerY = 2 * 50; // player Y position
Detect wall:
public bool detectSolidTile(int x, int y)
{
if (map[y, x] == 1) return true; else return false;
}
Movemet:
posX = (int)(Math.Floor((playerX) / 50));
posY = (int)(Math.Floor(playerY / 50));
(...)
if (slide == 1 && !detectSolidTile(posX + 1, posY))
{
playerX++;
}
if (slide == 2 && !detectSolidTile(posX - 1, posY))
{
playerX--;
}
Image -> http://s16.postimg.org/cxkfomemd/tiles.jpg
What I need improve to be able to move from wall to wall?
best regards, Krzysiek
Try making all the map where you can navigate 0, and then the map where you can not 1.
When trying to make a new move check if the future position will be either a 1 or a 0. If it's 0 you let him move, otherwise you stop him.
if (slide == 1 && !detectSolidTile(posX + 1, posY))
{
playerX++;
}
This will detect the map with 1 as being outside the movable region, thus stopping it from going where you want.
Do you understand where the problem is now?
Another solution is to understand the size of the matrix, and as soon x or y reach the maximum value, you stop incrementing them. But if you want to add obstacles later on, keep with what you are doing now, but make sure 0 is for map, 1 is for outside the map.
So here is a edit for BartoszKP:
You might be right that this "!" does not fix his code, I was just explaining how he should do it.
The code I would use for his issues is slightly different.
First of all drop the playerX and playerY since it is the exact same thing as posX and posY, you just do extra calculations.
now this being said you movement algorithm would look something like this:
Variables:
int TileWidth = 50;
int TileHeight = 50;
int posX = 2; // map X position - same thing as playerX
int posY = 2; // map Y position - same thing as playerY
//posX and posY will now hold the position of your player on the map since your conversion from playerX to playerY will only work if you increment a value by 50 or more.
Detect a wall:
//if the tile at coordinates x and y is a wall I return true
//else return false
public bool detectSolidTile(int x, int y)
{
if (map[y, x] == 1) return true;
else return false;
}
Movement:
posX = (int)(Math.Floor((playerX) / 50)); //drop this you don't need it
posY = (int)(Math.Floor(playerY / 50)); //drop this too, you are using posX and posY to store locations
(...)
if (slide == 1 && !detectSolidTile(posX + 1, posY))
{
posX++;
}
if (slide == 2 && !detectSolidTile(posX - 1, posY))
{
posX--;
}
if (slide == 3 && !detectSolidTile(posX, posY+1))
{
posY++;
}
if (slide == 4 && !detectSolidTile(posX, posY-1))
{
posY--;
}
This should work quite well if you use posX and posY as the position of the player on the map.
Making 1 type of coordinate for the player and one type for the map just makes it more confusing at this point. But if you indeed need to use different coordinates for them, you should always refer to playerX and playerY when trying to calculate the movement like this:
if (slide == 1 && !detectSolidTile((int)(Math.Floor((playerX+1) / 50)) + 1, posY))
{
playerX++;
}
And this is because you are changing the value of playerX by 1 not the value of posX, and this will restrict you movement. If this is confusing let me know and I will try to explain this better.

How do I recolor an image? (see images)

How do I achieve this kind of color replacement programmatically?
So this is the function I have used to replace a pixel:
Color.FromArgb(
oldColorInThisPixel.R + (byte)((1 - oldColorInThisPixel.R / 255.0) * colorToReplaceWith.R),
oldColorInThisPixel.G + (byte)((1 - oldColorInThisPixel.G / 255.0) * colorToReplaceWith.G),
oldColorInThisPixel.B + (byte)((1 - oldColorInThisPixel.B / 255.0) * colorToReplaceWith.B)
)
Thank you, CodeInChaos!
The formula for calculating the new pixel is:
newColor.R = OldColor;
newColor.G = OldColor;
newColor.B = 255;
Generalizing to arbitrary colors:
I assume you want to map white to white and black to that color. So the formula is newColor = TargetColor + (White - TargetColor) * Input
newColor.R = OldColor + (1 - oldColor / 255.0) * TargetColor.R;
newColor.G = OldColor + (1 - oldColor / 255.0) * TargetColor.G;
newColor.B = OldColor + (1 - oldColor / 255.0) * TargetColor.B;
And then just iterate over the pixels of the image(byte array) and write them to a new RGB array. There are many threads on how to copy an image into a byte array and manipulate it.
Easiest would be to use ColorMatrix for processing images, you will even be able to process on fly preview of desired effect - this is how many color filters are made in graphic editing applications. Here and here you can find introductions to color effects using Colormatrix in C#. By using ColorMatrix you can make colorizing filter like you want, as well as sepia, black/white, invert, range, luminosity, contrast, brightness, levels (by multi-pass) etc.
EDIT: Here is example (update - fixed color matrix to shift darker values into blue instead of previous zeroing other than blue parts - and - added 0.5f to blue because on picture above black is changed into 50% blue):
var cm = new ColorMatrix(new float[][]
{
new float[] {1, 0, 0, 0, 0},
new float[] {0, 1, 1, 0, 0},
new float[] {0, 0, 1, 0, 0},
new float[] {0, 0, 0, 1, 0},
new float[] {0, 0, 0.5f, 0, 1}
});
var img = Image.FromFile("C:\\img.png");
var ia = new ImageAttributes();
ia.SetColorMatrix(cm);
var bmp = new Bitmap(img.Width, img.Height);
var gfx = Graphics.FromImage(bmp);
var rect = new Rectangle(0, 0, img.Width, img.Height);
gfx.DrawImage(img, rect, 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, ia);
bmp.Save("C:\\processed.png", ImageFormat.Png);
You'll want to use a ColorMatrix here. The source image is grayscale, all its R, G and B values are equal. Then it is just a matter of replacing black with RGB = (0, 0, 255) for dark blue, white with RGB = (255, 255, 255) to get white. The matrix thus can look like this:
1 0 0 0 0 // not changing red
0 1 0 0 0 // not changing green
0 0 0 0 0 // B = 0
0 0 0 1 0 // not changing alpha
0 0 1 0 1 // B = 255
This sample form reproduces the right side image:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private Image mImage;
protected override void OnPaint(PaintEventArgs e) {
if (mImage != null) e.Graphics.DrawImage(mImage, Point.Empty);
base.OnPaint(e);
}
private void button1_Click(object sender, EventArgs e) {
using (var srce = Image.FromFile(#"c:\temp\grayscale.png")) {
if (mImage != null) mImage.Dispose();
mImage = new Bitmap(srce.Width, srce.Height);
float[][] coeff = {
new float[] { 1, 0, 0, 0, 0 },
new float[] { 0, 1, 0, 0, 0 },
new float[] { 0, 0, 0, 0, 0 },
new float[] { 0, 0, 0, 1, 0 },
new float[] { 0, 0, 1, 0, 1 }};
ColorMatrix cm = new ColorMatrix(coeff);
var ia = new ImageAttributes();
ia.SetColorMatrix(new ColorMatrix(coeff));
using (var gr = Graphics.FromImage(mImage)) {
gr.DrawImage(srce, new Rectangle(0, 0, mImage.Width, mImage.Height),
0, 0, mImage.Width, mImage.Height, GraphicsUnit.Pixel, ia);
}
}
this.Invalidate();
}
}
Depends a lot on what your image format is and what your final format is going to be.
Also depends on what tool you wanna use.
You may use:
GDI
GD+
Image Processing library such as OpenCV
GDI is quite fast but can be quite cumbersome. You need to change the palette.
GDI+ is exposed in .NET and can be slower but easier.
OpenCV is great but adds dependency.
(UPDATE)
This code changes the image to blue-scales instead of grey-scales - image format is 32 bit ARGB:
private static unsafe void ChangeColors(string imageFileName)
{
const int noOfChannels = 4;
Bitmap img = (Bitmap) Image.FromFile(imageFileName);
BitmapData data = img.LockBits(new Rectangle(0,0,img.Width, img.Height), ImageLockMode.ReadWrite, img.PixelFormat);
byte* ptr = (byte*) data.Scan0;
for (int j = 0; j < data.Height; j++)
{
byte* scanPtr = ptr + (j * data.Stride);
for (int i = 0; i < data.Stride; i++, scanPtr++)
{
if (i % noOfChannels == 3)
{
*scanPtr = 255;
continue;
}
if (i % noOfChannels != 0)
{
*scanPtr = 0;
}
}
}
img.UnlockBits(data);
img.Save(Path.Combine( Path.GetDirectoryName(imageFileName), "result.png"), ImageFormat.Png);
}
This code project article covers this and more: http://www.codeproject.com/KB/GDI-plus/Image_Processing_Lab.aspx
It uses the AForge.NET library to do a Hue filter on an image for a similar effect:
// create filter
AForge.Imaging.Filters.HSLFiltering filter =
new AForge.Imaging.Filters.HSLFiltering( );
filter.Hue = new IntRange( 340, 20 );
filter.UpdateHue = false;
filter.UpdateLuminance = false;
// apply the filter
System.Drawing.Bitmap newImage = filter.Apply( image );
It also depends on what you want: do you want to keep the original and only adjust the way it is shown? An effect or pixelshader in WPF might do the trick and be very fast.
If any Android devs end up looking at this, this is what I came up with to gray scale and tint an image using CodesInChaos's formula and the android graphics classes ColorMatrix and ColorMatrixColorFilter.
Thanks for the help!
public static ColorFilter getColorFilter(Context context) {
final int tint = ContextCompat.getColor(context, R.color.tint);
final float R = Color.red(tint);
final float G = Color.green(tint);
final float B = Color.blue(tint);
final float Rs = R / 255;
final float Gs = G / 255;
final float Bs = B / 255;
// resultColor = oldColor + (1 - oldColor/255) * tintColor
final float[] colorTransform = {
1, -Rs, 0, 0, R,
1, -Gs, 0, 0, G,
1, -Bs, 0, 0, B,
0, 0, 0, 0.9f, 0};
final ColorMatrix grayMatrix = new ColorMatrix();
grayMatrix.setSaturation(0f);
grayMatrix.postConcat(new ColorMatrix(colorTransform));
return new ColorMatrixColorFilter(grayMatrix);
}
The ColorFilter can then be applied to an ImageView
imageView.setColorFilter(getColorFilter(imageView.getContext()));

Categories

Resources