I need to find out the front measure of chest for any individual using Kinect while facing the camera. My current solution is:
When a MultiFrameSource arrives get the color (to display the body in the ui) body (to get the Joints), and bodyIndex frames.
copy the BodyIndexFrame to an byte[] _bodyData by using:
bodyIndexFrame.CopyFrameDataToArray(_bodyData);
I get the Joint objects for: spineShoulder and spineMid. I have assumed that the chest will always be between those points.
I convert both Joints to CameraSpacePoint (x,y,z) and from CameraSpacePoint to DepthSpacePoint (x,y) by using
_sensor.CoordinateMapper.MapCameraPointToDepthSpace(jointPosition);
I still keep a reference to the z value of spineShoulder.
Second assumption => Starting from spineShoulderY to spineMidY I try to find the widest point which is in the player area. in order to do so I will try to find the longest segment between spineShoulderX and the first left region found which does not belong to the player and the longest segment between spineShoulderX and first right side region found which does not belong to the player. Both x segments found must be in the same y coordinate.
/***************
* Returns the distance between 2 points
*/
private static int getDistanceToMid(int pointX, int midX)
{
if (midX > pointX)
{
return (midX - pointX);
}
else if (pointX > midX)
{
return (pointX - midX);
}
else
{
return 0;
}
}
/*********
* Loops through the bodyData array
* It will look for the longest x distance from midX to the last left x value
* which still belongs to a player in the y coordinate
*/
private static int findFarLeftX(byte[] bodyData,int depthWidth, int midX, int y)
{
int farLeftX = -1;
for (int x = midX; x >= 0; --x)
{
int depthIndex = (y * depthWidth) + x;
if (depthIndex > 0 && depthIndex < bodyData.Length)
{
byte player = bodyData[depthIndex];
if (player != 0xff){
if (farLeftX == -1 || farLeftX > x)
{
farLeftX = x;
}
} else{
return farLeftX;
}
}
}
return farLeftX;
}
/*********
* Loops through the bodyData array
* It will look for the longest x distance from midX to the last right x value
* which still belongs to a player in the y coordinate
*/
private static int findFarRightX(byte[] bodyData, int depthWidth, int midX, int y)
{
int farRightX = -1;
for (int x = midX; x < depthWidth; ++x)
{
int depthIndex = (y * depthWidth) + x;
if (depthIndex > 0 && depthIndex < bodyData.Length)
{
byte player = bodyData[depthIndex];
if (player != 0xff)
{
if (farRightX == -1 || farRightX < x)
{
farRightX = x;
} else{
return farRightX;
}
}
}
}
return farRightX;
}
private static BodyMember findElement(byte[] bodyData, int depthHeight, int depthWidth, int startX, int startY, int endY)
{
BodyMember member = new BodyMember(-1, -1, -1, -1);
int totalMaxSum = 0;
int farLeftX = -1;
int farRightX = -1;
int selectedY = -1;
for (int y = startY; y < depthHeight && y <= endY; ++y)
{
int leftX = findFarLeftX(bodyData, depthWidth, startX, y);
int rightX = findFarRightX(bodyData, depthWidth, startX, y);
if (leftX > -1 && rightX > -1)
{
int leftToMid = getDistanceToMid(leftX, startX);
int rightToMid = getDistanceToMid(rightX, startX);
int sum = leftToMid + rightToMid;
if (sum > totalMaxSum)
{
totalMaxSum = sum;
farLeftX = leftX;
farRightX = rightX;
selectedY = y;
}
}
}
member.setFarLeftX(farLeftX);
member.setFarLeftY(selectedY);
member.setFarRightX(farRightX);
member.setFarRightY(selectedY);
return member;
}
findElement will return a BodyMember object which contains farLeftX, farRightX, farLeftY and farRightY.
I create 2 DepthSpacePoint objects:
DepthSpacePoint chestX1 = new DepthSpacePoint();
chestX1.X = bodyMemberObj.getFarLeftX();
chestX1.Y = bodyMemberObj.getFarLeftY();
DepthSpacePoint chestX2 = new DepthSpacePoint();
chestX2.X = bodyMemberObj.getFarRightX();
chestX2.Y = bodyMemberObj.getFarRightY();
In order to get real world coordinates in meters these points must be converted to CameraSpacePoint object. In order to do so I will use the joint's z value that I kept a reference to back in point 4.
CameraSpacePoint chestLeft = _sensor.CoordinateMapper.MapDepthPointToCameraSpace(chestX1,spineShoulderZ);
CameraSpacePoint chestRight = _sensor.CoordinateMapper.MapDepthPointToCameraSpace(chestX1,spineShoulderZ);
Now,If my code and assumptions are right I should be able to get the correct distance in meters for the front chest.
double chestLength = (chestLeft.X > chestRight.X) ? chestLeft - chestRight : chestRight - chestLeft;
However this does not seem to be returning the correct values. I have been looking into a solution for this during weeks but I seem to be stuck.
I have worked with Kinect V2, and can say that skeleton data alone wild be insufficient to get reliable results. Even clothes has impact how Kinect interprets body parts, so you will have to combine results from other sensors data.
Additionally I suggest you to be creative about how you approach this,
for example, you could investigate some possible anatomic correlations about human body, most likely height is a proxy indicator, maybe age from face-recognition and height is another hint, etc.
Are you using using kinect One? Measurements in the old kinects are probably not accurate enough to fit your requirements.
However, from my point of view it would be also worth to do extraction of the moving object inside the frames (if there is only one person in front of the Kinect you will receive this person's contour, but to be sure you can compare its position with Kinect skeleton).
You can tell person to rise their hands for a short period of time. Than use Kinect distance to the persons skeleton and based on the size of the contour and given distance - calculate your final chest measurement just below the shoulders (position taken from the skeleton). I'm also not fully aware how accurately Kinect handles "bigger" people.
Here is described one of the methods: http://onlinelibrary.wiley.com/doi/10.1002/scj.10237/abstract You can also google many other papers with given subject (for free).
What do you think about this idea?
Cheers
Related
The title says it all. I'm trying to make a collision with the help of the per pixel collision method that I have work for all my enemies. My method works fine with if im not using a list. Here's the code im using:
public bool IntersectPixels(
Matrix transformA, int widthA, int heightA, Color[] dataA,
Matrix transformB, int widthB, int heightB, Color[] dataB)
{
// Calculate a matrix which transforms from A's local space into
// world space and then into B's local space
Matrix transformAToB = transformA * Matrix.Invert(transformB);
// For each row of pixels in A
for (int yA = 0; yA < heightA; yA++)
{
// For each pixel in this row
for (int xA = 0; xA < widthA; xA++)
{
// Calculate this pixel's location in B
Vector2 positionInB =
Vector2.Transform(new Vector2(xA, yA), transformAToB);
// Round to the nearest pixel
int xB = (int)Math.Round(positionInB.X);
int yB = (int)Math.Round(positionInB.Y);
// If the pixel lies within the bounds of B
if (0 <= xB && xB < widthB &&
0 <= yB && yB < heightB)
{
// Get the colors of the overlapping pixels
Color colorA = dataA[xA + yA * widthA];
Color colorB = dataB[xB + yB * widthB];
// If both pixels are not completely transparent,
if (colorA.A != 0 && colorB.A != 0)
{
// then an intersection has been found
return true;
}
}
}
}
// No intersection found
return false;
}
To check the collision I use this bit of code:
public bool Update(Matrix PersonTransform2,Color[] data2)
{
personTransform1 = Matrix.CreateTranslation(new Vector3(new Vector2(position.X, position.Y), 0.0f));
if (game.IntersectPixels(PersonTransform2, texture.Width, texture.Height, data2,
PersonTransform1, texture.Width, texture.Height, data1))
{
return true;
}
return false;
}
My question here is how I can transform this bit of code to be able to work with a list.
You could add an overload and use ToArray() from Linq:
public bool Update(Matrix personTransform2, List<Color> data2)
{
Update(Matrix personTransform2, data2.ToArray())
}
but because of performance (the above creates a copy) I would probably replace the Color[] by List<Color> in both signatures.
On the other end, for these kind of calculations you probably shouldn't be using the List<Color> to start with. If you care about performance work with array's directly.
Mistakes were made... I forgot to put a foreach loop that went through all my list... The code was:
for(int i = 0; i < lista.Count; i++)
{
if (lista[i].Update(blockTransform, data2))
{
touched = true;
}
}
Gonna be honest, I probably have a bad title there, so allow me to explain. (Title suggestions are welcome!)
First and foremost, I have a 'List' aptly named TileList. Tile is a class that contains a few variables, but the important ones are these two: public int xCoor and public int yCoor.
TileList is automatically filled by an outside function. In this case, it's filled with exactly 100 instances of the Tile class, each with a different set of xCoor and yCoor- with xCoors from 0 to 9 as well as yCoors from 0 to 9, basically simulating a 10x10 grid.
As it's filled by an outside function, it's not always 10x10, it can also be 5x15 or 20x20 and so on.
I have a function that grabs a specific tile, named FindTile- that one works perfectly thus far...
But now I need one that selects a diamond shape from the grid, based on a starting point and a radius. And my own attempt, the FindDiamond function.. is very messy and takes forever to load.. and in fact sometimes doesn't even work.
This is basically what I want to do: Grid example
Code is down below:
public class Tile
{
public int xCoor;
public int yCoor;
//more variables
}
public class TileFinder
{
public List<Tile> TileList = new List<Tile>();
//Give back tiles when generated
public void SaveGeneratedTiles(List<Tile> Save)
{
TileList = Save;
}
public Tile FindTile(int tileX, int tileY)
{
for (int i = 1; i < TileList.Count; i++)
{
if (TileList[i].xCoor == tileX && TileList[i].yCoor == tileY)
{
return TileList[i];
}
}
return null;
}
public List<Tile> FindDiamond(int tileX, int tileY, int radius)
{
List<Tile> ReturnList = new List<Tile>();
for (int r = 0; r <= radius; r++)
{
int curx = tileX + r;
int cury = tileY;
if (curx == tileX) { FindTile(curx, cury); }
else
{
while (curx != -r)
{
curx--;
cury--;
ReturnList.Add(FindTile(curx, cury));
}
while (cury != -r)
{
curx++;
cury--;
ReturnList.Add(FindTile(curx, cury));
}
while (curx != r)
{
curx++;
cury++;
ReturnList.Add(FindTile(curx, cury));
}
while (curx != -r)
{
curx--;
cury++;
ReturnList.Add(FindTile(curx, cury));
}
}
}
return ReturnList;
}
}
Frankly, I'm looking for a better way to make this work, that does end in a returned List at the end.. Other parts of my code function off of this, which in fact may also not be the best way, but I'm still blundering my way through this. :)
TL;DR I'm trying to emulate something like Advance Wars, and I need the diamond shape to select ranges for fog of war vision, indirect attacks, etc.
First and foremost, to represent a grid, a 2d array would be a much better choice than a list. A list would work as well but is redundant.
If you know the grid to only contain 10x10 tiles, and you can guarantee their order, finding a specific tile by x and y coordinates should be an o(1) operation, not an o(n) linear search like you currently have in FindTile. Also in that function, why are you starting from index 1 and not 0?
To create a diamond pattern should be very straightforward.
Start with the row (Y coordinate) of the starting point. From there go radius steps backward on the X and radius steps forward on the X. For radius rows below and above the starting point, do the same thing, but for each of those rows, decrease radius by 1.
Something like this? Haven't tested it.
public List<Tile> FindDiamond(int tileX, int tileY, int radius)
{
List<Tile> ReturnList = new List<Tile>();
for (int x = 0; x < radius; x++)
{
for (int y = 0; y < radius-x; y++)
{
ReturnList.Add(FindTile(tileX + x, tileY + y));
ReturnList.Add(FindTile(tileX + x, tileY - y));
ReturnList.Add(FindTile(tileX - x, tileY + y));
ReturnList.Add(FindTile(tileX - x, tileY - y));
}
}
return ReturnList;
}
The "diamond" can be represented as a circle with distances measured using Manhattan Distance.
Manhattan Distance is measured by the sum of absolute differences of the X and Y coordinates of the points in question.
static int ManhattanDistance(int x1, int y1, int x2, int y2) {
return Math.Abs(x1 - x2) + Math.Abs(y1 - y2)
}
This simplifies your FindDiamond method to a single line (if using LINQ).
List<Tile> FindDiamond(int centerX, int centerY, int radius) {
return TileList.Where(tile => ManhattanDistance(tile.xCoor, tile.yCoor, tileX, tileY) <= radius).ToList();
}
FindTile can also be simplified to a one-liner using LINQ:
Tile FindTile(int tileX, int tileY) {
return TileList.FirstOrDefault(tile => tile.xCoor == tileX && tile.yCoor = tileY);
}
If, however you cannot use LINQ for some reason, just loop through TileList in FindDiamond and return all the Tile satisfying the clause inside Where.
I suggest that you improve your design for efficiency though, like #Rotem says. It'll make your code more efficient and quick. FindTile is a method I can imagine being called hundreds of times each frame, so it's best to make the code as efficient as possible. Even with variable width and height the method stated by #Rotem will work.
I also recommend that you create your own Point structure for easy storage and using blah.X and blah.Y instead of using blahX and blahY everywhere. It can also include useful helpers like ManhattanDistance and so on.
A sample Point structure:
struct Point {
public int X, Y;
public int ManhattanDistance(Point other) {
return Point.ManhattanDistance(this, other);
}
public static int ManhattanDistance(Point a, Point b) {
return Math.Abs(a.X- b.X) + Math.Abs(a.Y - b.Y)
}
}
I was originally using a 2d Array of "Tile"s to store a procedural generated map with its various contents.
Each Tile contains a List adjacent which allows every single tile to know which vertices are closest to it, touching it on 8 different sides (straight adjacent and diagonally adjacent).
The general idea was taken from Amit's polygonal map generation, but I attempted to simplify it by using a grid setup instead of voronois, however I've run into more trouble than I originally thought would be possible. My current predicament is figuring out adjacency when I've scrapped 2d Arrays.
This is how I was doing it before changing to a list:
private void ConstructAdjacencyList() {
// Create Adjacency List
for (int x = 0; x < mapWidth; x++) {
for (int y = 0; y < mapHeight; y++) {
// Bool to find position of point
bool omitLeft = false; bool omitRight = false;
bool omitTop = false; bool omitBottom = false;
// Enable bools based on position, reset on each loop
if (x == 0)
omitLeft = true;
else if (x == mapWidth - 1)
omitRight = true;
if (y == 0)
omitTop = true;
else if (y == mapHeight - 1)
omitBottom = true;
// Add entries to list based on bool settings
if (!omitLeft) {
// Left center
islandMap[x,y].adjacent.Add(islandMap[x-1,y]);
if (!omitTop)
islandMap[x,y].adjacent.Add(islandMap[x-1,y-1]);
if (!omitBottom)
islandMap[x,y].adjacent.Add(islandMap[x-1,y+1]);
}
if (!omitTop) // Top Center
islandMap[x,y].adjacent.Add(islandMap[x,y-1]);
if (!omitBottom) // Bottom Center
islandMap[x,y].adjacent.Add(islandMap[x,y+1]);
if (!omitRight) {
// Right Center
islandMap[x,y].adjacent.Add(islandMap[x+1,y]);
if (!omitTop)
islandMap[x,y].adjacent.Add(islandMap[x+1,y-1]);
if (!omitBottom)
islandMap[x,y].adjacent.Add(islandMap[x+1,y+1]);
}
}
} // End Adjacency
Debug.Log ("Adjacencies Built");
}
The x, y values now are held in islandMap.point (A Vector 2d storing the x and y values generated as follows:)
public MapController() {
width = height = (int)Mathf.Sqrt (tileCount);
// Lists for points
var points = new List<Vector2>();
// Build a random set of points.
for (float x = 0; x < width; x++) {
for (float y = 0; y < height; y++) {
points.Add(new Vector2(x,y));
}
}
map = new Map (points, width, height, lakeTreshold);
}
And the Map itself has the following currently:
public class Map {
Func<Vector2, bool> inside; // Contains function to randomly seed area
bool needsMoreRandomness;
public List<Tile> islandMap; // Previously was Tile[,] islandMap
public int mapWidth { get; private set; } // Calculated as Sqrt(totalPoints)
public int mapHeight { get; private set; }
Along with other methods such as the ConstructAdjacencyList() method I'm currently stuck on.
So how can I go on about constructing an adjacency list of surrounding points without relying on array positioning? Could I temporarily reference the entire list from an array, place references to each tile in the entire list in this 2d array, setup adjacencies and then remove the array without losing the information? I believe it would only use references, so it should be fine... Each tile contains an index to store the order with which it was built like so:
foreach (var point in points) {
var p = new Tile { index = islandMap.Count, point = point };
p.border = point.x == 0 || point.x == mapWidth || point.y == 0 || point.y == mapHeight;
islandMap.Add (p);
tileLookup[point] = p;
}
Sorry if this is too long... I just realized it's quite massive -.-
Assuming you haven't done anything to mess with the order of the points, you can treat a 1D list/array as 2D list/array. The example I linked is in C, but the idea language agnostic.
That being said, assuming that you're only going to do this once during the initialization & given the relatively low number of points, you could just as easily brute force it with a function that loops through the point list & picks out the neighbors as needed. My C# is a bit rusty, but I'm talking about something like this:
private List<Vector2> getAdjacenctPointList(List<Vector2> pointsList, Vector2 point){
var adjacencyList = new List<Vector2>();
foreach (var pt in pointList){
var offset = Math.abs(pt.x - point.x) + Math.abs(pt.y - point.y);
if(offset > 0 && offset <= 1.0){
adjacencyList.add(pt);
}
}
return adjacencyList;
}
The final answer I went with which was the least amount of work involved and allowed me to recycle my original 2d Array code posted above was to simply construct a temporary 2d array and store all the references to it in it - create all adjacencies as necessary and then dispose (simply by losing scope) of the 2d Array.
Here's the actual method with the 2d array system in place. First couple statements followed by the nested for loop was all it took:
private void ConstructAdjacencyList() {
Tile[,] tempArray = new Tile[mapWidth, mapHeight];
int count = 0;
// Populate the temp 2D array with list references
for (int x = 0; x < mapWidth; x++) {
for (int y = 0; y < mapHeight; y++) {
tempArray[x,y] = islandMap[count];
count++;
}
}
// Create Adjacency List using our TempArray
for (int x = 0; x < mapWidth; x++) {
for (int y = 0; y < mapHeight; y++) {
// Bool to find position of point
bool omitLeft = false; bool omitRight = false;
bool omitTop = false; bool omitBottom = false;
// Enable bools based on position, reset on each loop
if (x == 0)
omitLeft = true;
else if (x == mapWidth - 1) // Optimize with if else to split checks in half.
omitRight = true;
if (y == 0)
omitTop = true;
else if (y == mapHeight - 1)
omitBottom = true;
// Add entries to list based on bool settings
if (!omitLeft) {
// Left center
tempArray[x,y].adjacent.Add(tempArray[x-1,y]);
if (!omitTop)
tempArray[x,y].adjacent.Add(tempArray[x-1,y-1]);
if (!omitBottom)
tempArray[x,y].adjacent.Add(tempArray[x-1,y+1]);
}
if (!omitTop) // Top Center
tempArray[x,y].adjacent.Add(tempArray[x,y-1]);
if (!omitBottom) // Bottom Center
tempArray[x,y].adjacent.Add(tempArray[x,y+1]);
if (!omitRight) {
// Right Center
tempArray[x,y].adjacent.Add(tempArray[x+1,y]);
if (!omitTop)
tempArray[x,y].adjacent.Add(tempArray[x+1,y-1]);
if (!omitBottom)
tempArray[x,y].adjacent.Add(tempArray[x+1,y+1]);
}
}
} // End Adjacency
Debug.Log ("Adjacencies Built");
}
To ensure this worked out as I wanted it to, I tested it visually by setting one random cube and tested various points all around the area to ensure there were no mistakes and there weren't.
The end result is as expected, depicted below:
Thanks for the help :)
Currently I have a random point being generated from Random. I am trying to check if the given random point is too close to any other existing points in the plane, if it is far enough away from ALL other points, it will be added to the list of other points. I start with one given point and the first hundered points in the list are being generated this way. My problem is, when I go to draw all the points on the list onto the screen, often the points are much closer than they should be allowed to be.
public void generateFirstMap()
{
int count = 0;
do
{
int randXpixels = Main.rand.Next(24, Main.screenWidth - 16); //leave outer 16 pixels of screen empty (planet sprite has diameter of 8 pixels)
int randYpixels = Main.rand.Next(27, Main.screenHeight - 27);
Tuple<int, int> coord = Tuple.Create(randXpixels, randYpixels);
if (distance(closestPoint(coord), coord) < 200)
{
continue;
}
points.Add(coord); //List<Tuple<int,int>> points;
count++;
} while(count < 100);
public Tuple<int, int> closestPoint (Tuple<int, int> p1)
{
Tuple<int, int> p2 = Tuple.Create(0, 0);
bool firstRun = true;
foreach (Tuple<int, int> point in points)
{
if (firstRun)
{
p2 = point;
firstRun = false;
}
else if (distance(p1, p2) < distance(p1, point))
{
p2 = point;
}
}
return p2;
}
public double distance(Tuple<int, int> p1, Tuple<int, int> p2)
{
Vector2 line = new Vector2((p2.Item1 - p1.Item1), (p2.Item2 - p1.Item2));
return Math.Abs(line.Length());
}
Edit: to be clear, the number for how close they can be is just a number I threw out there (for now) it can be anything >~30
Edit2: Changed all tuples to Vector2 and used suggested code, still hasn't changed the problem
Edit 3: Using a for loop to loop through all the points (inside of the while loops) and using break seems to have fixed the problem.
This should do:
(Requires you to remove alls Tuples and replace them with the Vector2 type, which makes far more sense in general. Also you want to adjust your naming.
public Vector2 GetClosestPoint (Vector2 v1)
{
return points.OrderByDescending(v2 => GetDistance(v1,v2)).First();
}
It seems like you'd want this to be fairly fast because it's a pretty slow algorithm (it's O(N^2) I think). An obvious optimisation is to just calculate the distance squared and use that to compare distances - this does away with a square root operation.
However you have a much more serious problem. If your OP is correct, then you are trying to place 100 points on a screen such that none of them are closer than 200 pixels apart. This is not likely to happen with randomly selected points, because the chances are that they will occur in positions such that after a number have been placed there simply isn't room for any more.
Anyway, here's some code which will attempt to calculate the points - but it will never terminate, because the points won't fit unless you make the screen VERY big. Try running it with different seeds for the Random() constructor and see what happens.
using System;
using System.Collections.Generic;
using System.Drawing;
namespace ConsoleApplication2
{
class Program
{
private static void Main()
{
var points = new List<Point>();
int screenWidth = 1920;
int screenHeight = 1280;
int numPointsWanted = 100;
long nearestAllowedSquared = 200*200;
Random rng = new Random(12345);
while (points.Count < numPointsWanted)
{
int randXpixels = rng.Next(24, screenWidth - 16);
int randYpixels = rng.Next(27, screenHeight - 27);
var point = new Point(randXpixels, randYpixels);
if (distanceToNearestPointSquared(point, points) >= nearestAllowedSquared)
{
points.Add(point);
Console.WriteLine(points.Count);
}
}
}
private static long distanceToNearestPointSquared(Point point, IEnumerable<Point> points)
{
long nearestDistanceSquared = long.MaxValue;
foreach (var p in points)
{
int dx = p.X - point.X;
int dy = p.Y - point.Y;
long distanceSquared = dx*dx + dy*dy;
nearestDistanceSquared = Math.Min(nearestDistanceSquared, distanceSquared);
}
return nearestDistanceSquared;
}
}
}
You can iterate through the points in the plane and use this formula to calculate the distance with the new random point you get.
What about something like this?
public void generateFirstMap()
{
int count = 0;
do
{
bool addPoint = true;
int randXpixels = Main.rand.Next(24, Main.screenWidth - 16);
int randYpixels = Main.rand.Next(27, Main.screenHeight - 27);
for (int a = 0; a < points.Count; a++)
{
int dX = randXpixels - points[a].X;
int dY = randYpixels - points[a].Y;
if (dX * dX + dY * dY < 200)
{
addPoint = false;
break;
}
}
if(addPoint)
points.Add(new Point(randXpixels,randYpixels));
count++;
} while (count < 100);
}
Just change the Point class to your tuple or whatever you are using
I have a binary object in a 3d array located in the origin (0,0,0). I need rotate this object dinamically in z axis. How can I rotate that multidimensional array with no fixed size in any angle?
I have created a 3d Point Class:
public class Point3
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
}
I'm thinking in do a foreach in each point and rotate it:
foreach (Point3 p in listPoint3)
{
RotatePoint(p, angle);
}
Any suggestion?
You colud create a routine to rotate each point using the parametric equation for rotate the 3d object.
x' = x*cos(o)-y*sin(o)
y' = y*sin(o)-y*cos(o)
z' = z
private Point3 RotatePoint(Point3 p0, int angle)
{
Point3 p = new Point3()
{
X = p0.X * Math.Cos(angle) - p0.Y * Math.Sin(angle),
Y = p0.X * Math.Sin(angle) + p0.Y * Math.Cos(angle),
Z = p0.Z,
};
return p;
}
You need to know what axis you want to rotate on. But if this is only a question where to look at. (namespace System.Windows.Media.Media3D)
You can try this:
double angle = 45;
RotateTransform3D zrotation = new RotateTransform3D(new AxisAngleRotation3D(
new Vector3D(0, 0, 1), angle));
foreach (Point3D p in listPoint3)
{
Point3D rotatedPoint = zrotation.Transform(p);
}
You should use the build-in Point3D
Also if you want to Stack those: (multiple transforms)
double zAngle = 45;
double xAngle = 10;
Transform3DGroup group = new Transform3DGroup();
group.Children.Add( new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(1, 0, 0), xAngle)));
group.Children.Add( new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 0, 1), zAngle)));
foreach (Point3D p in listPoint3)
{
Point3D rotatedPoint = group.Transform(p);
}
So you have a "monochrome object" which is stored like a 3D bitmap which you want to rotate around the Z axis. You must first understand that after a number of rotations you will end up with optical aberrations caused by the fact that you're using an array index which is a natural number to represent a coordinate of an object's component.
Upon rotation, any integer value will most likely become an irrational number. The fact that traditionally (not talking about special programs and frameworks) people store approximations of irrational numbers in double or float or decimal variables (which can only store a small subset of the rational numbers set) is nothing compared to the approximation of an irrational number by storing it in an integer (an array index).
Furthermore, even if that quality loss if of no big importance in the case of your application, you must be sure you understand that mathematically speaking, after a number of rotations, your 3d shape will be trimmed by the cylinder which is inscribed in the original parallelepiped, alongside the Z axis.
It goes like this. You said you already made a class called Point3:
public class Point3 {
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
}
Maybe you should follow #Jeroen van Langen's advice and use a standard class, if such a class already exists. The benefit would be that if someone out there already built or will ever build a library which uses that class you can start using the library right away.
But that's not so important right now.
#Alpert already gave a great C# code for rotating a point around the oZ axis. This is n "extension method" adaption of that code:
public static class RotationHelpers {
public static Point3 RotatePoint(this Point3 point, int angle) {
var result = new Point3() {
X = point.X * Math.Cos(angle) - point.Y * Math.Sin(angle),
Y = point.X * Math.Sin(angle) + point.Y * Math.Cos(angle),
Z = point.Z,
};
return result;
}
...
}
You can go even further and make an extension method which rotates a sequence of points around the oZ axis:
public static class RotationHelpers {
...
public static IEnumerable<Point3> RotatePoints(this IEnumerable<Point3> points, int angle) {
foreach (var point in points)
yield return point.RotatePoint(angle);
}
...
}
Now you said you have a 3d primitive matrix with 1s and 0s in it:
int[,,] matrix;
You need to somehow convert the intrinsically defined points in that matrix into a sequence of Point3 instances, rotate those and then convert the resulting sequence back into an int[,,] matrix.
That could be achieved like so (remember about the loss of quality I was talking about earlier):
public static class RotationHelpers {
...
public static IEnumerable<Point3> ToPoints(this int[,,] matrix) {
int lx = matrix.GetLength(0);
int ly = matrix.GetLength(1);
int lz = matrix.GetLength(2);
for (int x = 0; x < lx; x++)
for (int y = 0; y < ly; y++)
for (int z = 0; z < lz; z++) {
bool is1 = matrix[x, y, z] != 0;
if (is1)
yield return new Point3 {
X = x - lx / 2,
Y = y - ly / 2,
Z = z - lz / 2
};
}
}
...
}
That will take all of the cells in the WIDTH x HEIGHT x DEPTH matrix and for every cell which is not equal to 0 it will yield a new Point3 instance with the coordinates of that particular position.
That sequence can then be rotate by an angle using the earlier described RotatePoints method and then the following method could be used to "render" back the resulting sequence of Point3 instances into the array:
public static class RotationHelpers {
...
public static void AssignPoints(this int[,,] matrix, IEnumerable<Point3> points) {
int lx = matrix.GetLength(0);
int ly = matrix.GetLength(1);
int lz = matrix.GetLength(2);
for (int x = 0; x < lx; x++)
for (int y = 0; y < ly; y++)
for (int z = 0; z < lz; z++)
matrix[x, y, z] = 0;
foreach (var point in points) {
// this is when quality is lost, because things like 1.7 and 1.71
// will both become =2
var x = (int)Math.Round(point.X) + lx / 2;
var y = (int)Math.Round(point.Y) + ly / 2;
var z = (int)Math.Round(point.Z) + lz / 2;
// this is where you loose parts of the object because
// it doesn't fit anymore inside the parallelepiped
if ((x >= 0) && (y >= 0) && (z >= 0) &&
(x < lx) && (y < ly) && (z < lz))
matrix[x, y, z] = 1;
}
}
...
}
To wrap it up, you can use all of these methods like so:
int[,,] matrix = ...
int angle = ...
IEnumerable<Point3> points = matrix.ToPoints();
IEnumerable<Point3> rotatedPoints = points.RotatePoints(angle);
matrix.AssignPoints(rotatedPoints);
// now you have the original matrix, rotated by angle