How to traverse mesh vertices based on their location? - c#

I am using Point Cloud Free Viewer to visualize Point Clouds in Unity. It has a script and it parses .off files and creates meshes without triangulating. However, the code creates multiple meshes since its index format is 16bit. I modified the code for utilizing 32 bit format and i have a mesh with 2 million points:
What i want to do is creating a grid like geometry and color this point cloud based on point density. I want to find a rough volume of this point cloud by multiplying differences between max and min x,y,z values and divide this volume into equal boxes. Each of these boxes will be colored based of how many points they contain. I would be happy if someone can offer me a lead. I tried KDTree approach but it is a bit slow since i have 2 million points. I also tried sorting points before creating the mesh but it takes too much time as well. Is there a way to traverse mesh vertices based on the location without visiting all vertices considering they are indexed randomly? I believe i am looking for a solution like mesh.bounds.contains() but i do not know if a method like spatial search exists.

Not really, a full solution, more a hint towards a direction I would pursue: divide your vertex pool into smaller groups first, I.e into cubes (seperate meshes maybe), precalculate this, then you only have to search within a much smaller region, after an initial search for a set of cubes that neighbour (or touch) your region.

It sounds to me like you want an octree.
First, load all of the points into memory (2 million points really isn't that many - assuming doubles, that's 2,000,000 * 3 * 8 bytes ~= 45 MB). While you are parsing the file and loading the points into memory, record the min and max x, y, and z coordinates. You can then build your octree which bounds that volume in N*LogN. Then, for each of your grid volumes, you can very quickly query the tree to get only the points in that region. I'm pretty sure this is the most efficient way to do what you want.
I would suggest checking the quadtree article for its implementation of queryRange to see how this would be done. An octree is just a 3-d implementation of a quadtree, so the underlying code is more or less the same (with each node containing 8 children instead of 4).

For those who might visit this question later i found a really fast solution based on Nico's comment. I am traversing whole points by parsing my scan file using this script
for (int i = 0; i < numPoints; i++)
{
buffer = sr.ReadLine().Split();
points[i] = new Vector3(float.Parse(buffer[0]) , float.Parse(buffer[1]) , -float.Parse(buffer[2]) );
//Finding minX, minY, minZ
if (points[i].x < minX)
minX = points[i].x;
if (points[i].y < minY)
minY = points[i].y;
if (points[i].z < minZ)
minZ = points[i].z;
//Finding maxX, maxY, maxZ
if (points[i].x > maxX)
maxX = points[i].x;
if (points[i].y > maxY)
maxY = points[i].y;
if (points[i].z > maxZ)
maxZ = points[i].z;
}
Here is my and variables i use with itFindPointIndex function.
deltaX = maxX - minX;
deltaY = maxY - minY;
deltaZ = maxZ - minZ;
gridCountX = Mathf.CeilToInt(deltaX / gridSize);
gridCountY = Mathf.CeilToInt(deltaY / gridSize);
gridCountZ = Mathf.CeilToInt(deltaZ / gridSize);
Resolution = gridCountX * gridCountY * gridCountZ;
Histogram = new int[Resolution];
int FindPointIndex(Vector3 point)
{
//Finds the grid index of the point
int index = Mathf.FloorToInt((point.x - minX) / gridSize) + ((Mathf.FloorToInt((point.z - minZ) / gridSize)) * gridCountX)
+ Mathf.FloorToInt((point.y - minY) / gridSize) * gridCountX * gridCountZ;
if (index < 0)
{
index = 0;
}
return index;
}
Then i can traverse the points again to increment index for each of them to see how many points each grid holds like this:
for (int i = 0; i < numPoints; i++)
{
Histogram[FindPointIndex(points[i])]++;
}
At the end using this histogram i can color the point cloud with another loop.

Related

How to handle sand in unity3d

I have Crane Animation which will pick the sand (through its jaw) and drop it to the truck. I tried to find the way to handle sand in Unity but unfortunately found nothing.
So I made custom sand object (low poly) in max and bring them it Unity3d and apply box collider and rigidbody to them. But as I play the game, my FPS drops and player hardly moving. Profiler tell that physics is so heavy (and it should be as too many colliders with too many rigidbodies). I tried to optimize physics by collision matrix and layer but it didn't improve the performance.
Is this right approach to handle sand interaction with a crane?
You could have the sand be a terrain and use the terrain.SetHeights() Method to lower a specific area of the terrain where you used the crane.
It would look like those classic terrain editing tools when you lower or heigher terrain.
Then wherever you "drop" off the sand you could heigher the area a bit.
I found this thread with some code examples to help you get going:
https://forum.unity.com/threads/edit-terrain-in-real-time.98410/
private void raiseTerrain(Vector3 point)
{
int terX =(int)((point.x / myTerrain.terrainData.size.x) * xResolution);
int terZ =(int)((point.z / myTerrain.terrainData.size.z) * zResolution);
float[,] height = myTerrain.terrainData.GetHeights(terX - 4,terZ - 4,9,9); //new float[1,1];
for(int tempY = 0; tempY < 9; tempY++)
for(int tempX = 0; tempX < 9; tempX++)
{
float dist_to_target = Mathf.Abs((float)tempY - 4f) + Mathf.Abs ((float)tempX - 4f);
float maxDist = 8f;
float proportion = dist_to_target / maxDist;
height[tempX,tempY] += 0.001f * (1f - proportion);
heights[terX - 4 + tempX,terZ - 4 + tempY] += 0.01f * (1f - proportion);
}
myTerrain.terrainData.SetHeights(terX - 4, terZ - 4, height);
}

Polygon Area in Bing Maps

Hi I have a problem calculating the area of a polygon in Bing maps. I'm using this code to calculate area.
public static double PolygonArea(LocationCollection points, double resolution)
{
int n = points.Count;
var partialSum = 0.0;
var sum = 0.0;
for (int i = 0; i < n - 1; i++)
{
partialSum = (points[i].Longitude * points[i + 1].Latitude) -
(points[i + 1].Longitude * points[i].Latitude);
sum += partialSum;
}
var area = 0.5 * sum / Math.Pow(resolution, 2);
area = Math.Abs(area);
return area;
}
This is the resolution method
public static double Resolution(double latitude, double zoomLevel)
{
double groundResolution = Math.Cos(latitude * Math.PI / 180) *
2 * Math.PI * EARTH_RADIUS_METERS / (256 * Math.Pow(2, zoomLevel));
return groundResolution;
}
How can I trasform it in m^2?
EDIT1: I tried your answer but I noticed that area change if I change zoom level.
I try to explain my problem from another point of you. I have to make the porting of an iOS app that uses this algorithm to calculate area
-(long double)calcArea :(CLLocationCoordinate2D*) pastureCordinates :(long) count {
long double area = 0.0;
long double scale = 0.0;
for (int cnt = 1; cnt < count; cnt++) {
area += (MKMapPointForCoordinate(pastureCordinates[cnt - 1]).x)
* (MKMapPointForCoordinate(pastureCordinates[cnt]).y)
- (MKMapPointForCoordinate(pastureCordinates[cnt]).x)
* (MKMapPointForCoordinate(pastureCordinates[cnt - 1]).y);
}
area += (MKMapPointForCoordinate(pastureCordinates[count - 1]).x)
* (MKMapPointForCoordinate(pastureCordinates[0]).y)
- (MKMapPointForCoordinate(pastureCordinates[0]).x)
* (MKMapPointForCoordinate(pastureCordinates[count - 1]).y);
scale = MKMapPointsPerMeterAtLatitude(pastureCordinates[count -1].latitude);
area = (area / (long double)2) / pow(scale,2);
area = fabsl(area);
return area;
}
I used the functions found here: https://msdn.microsoft.com/en-us/library/bb259689.aspx to calculate the scale, the ground resolution but the results are different compared to the iOS solution.
Ok, I've played around with some code and put together a simple method that calculates the area fairly accurately without having to use really in-depth spatial mathematics.
private double CalculateArea(LocationCollection locs)
{
double area = 0;
for (var i = 0; i < locs.Count - 1; i++)
{
area += Math.Atan(
Math.Tan(Math.PI / 180 * (locs[i + 1].Longitude - locs[i].Longitude) / 2) *
Math.Sin(Math.PI / 180 * (locs[i + 1].Latitude + locs[i].Latitude) / 2) /
Math.Cos(Math.PI / 180 * (locs[i + 1].Latitude - locs[i].Latitude) / 2));
}
if (area < 0)
{
area *= -1;
}
return area * 2 * Math.Pow(6378137.0, 2);
}
Testing this with various polygons and comparing them to the calculated area in SQL, I found that in the worse case the difference was about 0.673% when using a ridiculously large polygon. When testing against a polygon that was about 0.5 sq KM in size, the difference was about 0.06%. Note that this method returns an area in sq meters.
Calculating the area of a polygon on a map is very complicated as the world is a sphere and you are actually trying to calculate the area of a polygon stretched on the surface of a sphere. Since you are using WPF I'd suggest to make things easy and make use of the spatial library available in SQL server. All the spatial functionalities in SQL server are available as a dll which you can use in your WPF application. You can easily use this library to calculate the area of a polygon accurately and do a lot of other really powerful things as well. To start off with, if you have SQL instelled you can find the SQL Spatial Library (Microsoft.SqlServer.Types) located in the C:\Program Files (x86)\Microsoft SQL Server\110\Shared directory. If you don't have SQL Server installed, don't worry, you don't have to install it, this library is available as a Nuget package here: https://www.nuget.org/packages/Microsoft.SqlServer.Types
Take a look at this hands on lab for information using SQL spatial tools in .NET: http://view.officeapps.live.com/op/view.aspx?src=http%3A%2F%2Fecn.channel9.msdn.com%2Fo9%2Flearn%2FSQL2008R2TrainingKit%2FLabs%2FUsingSpatialDataInManagedCode%2FLab.docx
Once you have this library you can create an SQL Geography object from your polygon. Once this is done you can then use the STArea method to calculate the area of the polygon. There is a ton of other spatial methods available as well which you can use to create a really powerful mapping application.

c# / XNA 4.0- Move textures to give illusion of infinite plane

I've been having trouble implementing an algorithm to shift my textures given their positions and the cameras position. The first two pictures in the image explain what I'm trying to accomplish, but I can't figure out how to move them accordingly. I had created a program once upon a time that did this, but I've gone and lost it. Any ideas?
If it helps any, the Cameras/Viewports width and height are the same as the textures' width and height. The goal is the get them to shift positions, giving the illusion of an infinite plane. (With out having to draw an infinite plane, lol.)
You do not really need to move your regions, enough to decide where to draw them. Lets assume you have a terrain containing N*M blocks (in this case N=M=2), each of them are size of A*A (in this case the screen hase the same size, but this doesn't matter), and the Tiles are continously following each other.
int LeftColumn = Camera.X / A; // let it round to nearest lower int
int TopRow = Camera.Y / A;
LeftColumn = LeftColumn % N; // Calculate the first tile
TopRow = TopRow % M;
for (int i = LeftColumn+N; i < LeftColumn+2*N; i++)
for (int l = TopRow+M; l < TopRow+2*M; l++)
// you may check here if the tile is visible or not based on the screen size
{
Tile[i % N, l % M].Draw(i*A, l*A); // Or do whatever you like
}
Is this clear?
After a couple hours of trial and error, I finally figured out how to get the regions/textures/rectangles to move accordingly. For those who want the solution,
if ((int)Math.Abs(region.X - camPos.X) > region.Width * 2)
{
region.X += region.Width * 2;
}
if (camPos.X < region.X - region.Width)
{
region.X -= region.Width * 2;
}
if ((int)Math.Abs(region.Y - camPos.Y) > region.Height * 2)
{
region.Y += region.Height * 2;
}
if (camPos.Y < region.Y - region.Height)
{
region.Y -= region.Height * 2;
}
Where camPos is the camera position, and region is the region/texture/rectangle/whatever.
This code works for a 4 square region (2 regions by 2 regions). To change for more regions, simply change all the *2s to *3s or *4s for a 9 square region and 16 square region, respectively.

Drawing an envelope around a curve

In my C# WinForms application I have a picturebox that hosts 2 curves (Resulted from a voltage/current measurement). The X axis is voltage and Y axis is current. The voltage axis is ranged from -5 to 5 but the current axis is a much smaller scale ranged from -10 uA to 10 uA. The task is to see if the second curve is within 10% of the first curve.
For visual inspection I am trying to draw an envelope around the first curve (Blue one). The curve is just a PointF array. At the moment since I have no idea how to draw a correct envelope around the blue curve, I just draw two other curves that are result of X points of the actual curve added and subtracted by 10% of the original curve. Of course this is a bad approach, but atleast for the section of the curve that is noticably vertical, it works. But as soon as the curve is on its non vertical section, this trick does not work anymore, as you can see in the picture below:
Here is the code that I am using to draw the envelope:
public Bitmap DrawEnvelope(double[,] pinData, float vLimit, float iLimit)
{
g = Graphics.FromImage(box);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
PointF[] u = new PointF[pinData.GetLength(0)]; //Up line
PointF[] d = new PointF[pinData.GetLength(0)]; //Down Line
List<PointF> joinedCurves = new List<PointF>();
float posX = xMaxValue * (vLimit / 100);
float minX = posX * -1;
for (int i = 0; i < pinData.GetLength(0); i++)
{
u[i] = new PointF(400 * (1 + (((float)pinData[i, 0]) + minX) / (xMaxValue + vExpand)), 400 * (1 - ((float)pinData[i, 1] * GetInvers((yMaxValue + iExpand)))));
}
for (int i = 0; i < pinData.GetLength(0); i++)
{
d[i] = new PointF(400 * (1 + (((float)pinData[i, 0]) + posX) / (xMaxValue + vExpand)), 400 * (1 - ((float)pinData[i, 1] * GetInvers((yMaxValue + iExpand)))));
}
Pen pengraph = new Pen(Color.FromArgb(50, 0 ,0 ,200), 1F);
pengraph.Alignment = PenAlignment.Center;
joinedCurves.AddRange(u);
joinedCurves.AddRange(d.Reverse());
PointF[] fillPoints = joinedCurves.ToArray();
SolidBrush fillBrush = new SolidBrush(Color.FromArgb(40, 0, 0, 250));
FillMode newFillMode = FillMode.Alternate;
g.FillClosedCurve(fillBrush, fillPoints, newFillMode, 0);
g.Dispose();
return box;
}
The green circles are added by myself, and they indicate the region that the second curve (Red one) is potentially has a difference bigger than 10% from the orginal curve.
Would be nice if someone put me in the right way, what should I look to to achive a nice envelope around original curve?
UPDATE
Because I am so noob I cant find a way to implement the answers given to this question until now, So put a bounty to see if somone can kindly show me atleast a coding approach to this problem.
You could try finding the gradient between each pair of points and calculating two points either side that are on the orthogonal that passes through the midpoint.
You would then have two more lines defined as a set of points that you could use to draw the envelope.
Your best bet is to iterate your point array and to calculate a perpendicular vector to two consecutive points each time (see Calculating a 2D Vector's Cross Product for implementation clues). Project in either direction along these perpendicular vectors to generate the two point arrays of your envelope.
This function generates them roughly using segment midpoints (as long as the point count is high and your offset is not too small it should look ok when plotted):
private void GetEnvelope(PointF[] curve, out PointF[] left, out PointF[] right, float offset)
{
left = new PointF[curve.Length - 1];
right = new PointF[curve.Length - 1];
for (int i = 1; i < curve.Length; i++)
{
PointF normal = new PointF(curve[i].Y - curve[i - 1].Y, curve[i - 1].X - curve[i].X);
float length = (float)Math.Sqrt(normal.X * normal.X + normal.Y * normal.Y);
normal.X /= length;
normal.Y /= length;
PointF midpoint = new PointF((curve[i - 1].X + curve[i].X) / 2F, (curve[i - 1].Y + curve[i].Y) / 2F);
left[i - 1] = new PointF(midpoint.X - (normal.X * offset), midpoint.Y - (normal.Y * offset));
right[i - 1] = new PointF(midpoint.X + (normal.X * offset), midpoint.Y + (normal.Y * offset));
}
}
It all depends on the way you want the envelop to be sized.
You could calculate/guestimate the slope of the curve in each point by calculating the slope to the next point and the slope to the previous point, average these and then calculate a perpendicular vector to the slope.
Add this vector to the point of the curve; this gives you the right-hand edge of the envelop.
Subtract this vector from the point of the curve; this gives you the left-hand edge of the envelop.
This method will fail if the points are too far apart or very sudden changes in the points appear.
This is probably a dumb suggestion. Perhaps instead of drawing the envelope yourself, maybe you could let winforms do it for you. Try drawing the envelope as a line with a pen that has a larger width. Perhaps it might work.
If you look at this msdn example on varying the pen width, you might see what I mean.
http://msdn.microsoft.com/en-us/library/3bssbs7z.aspx
2 (probably incorrect) possibilities.
Do what you did originally to get the pale blue wide area, but also do it in the vertical direction (not just the horizontal)
Do what Dan suggested with a REALLY thick line (in pale blue) then draw it again, then draw the original (thin) line on top of it.

How to compute bounding box/sphere across multiple meshes (C#)

I load multiple meshs from .x files in different mesh variables.
Now I would like to calculate the bounding sphere across all the meshes I have loaded (and which are being displayed)
Please guide me how this could be achieved.
Can VertexBuffers be appended togather in one variable and the boundingSphere be computed using that? (if yes how are they vertexBuffers added togather)
Otherwise what alternative would you suggest!?
Thankx
Its surprisingly easy to do this:
You need to, firstly, average all your vertices. This gives you the center position.
This is done as follows in C++ (Sorry my C# is pretty rusty but it should give ya an idea):
D3DXVECTOR3 avgPos;
const rcpNum = 1.0f / (float)numVerts; // Do this here as divides are far more epxensive than multiplies.
int count = 0;
while( count < numVerts )
{
// Instead of adding everything up and then dividing by the number (which could lead
// to overflows) I'll divide by the number as I go along. The result is the same.
avgPos.x += vert[count].pos.x * rcpNum;
avgPos.y += vert[count].pos.y * rcpNum;
avgPos.z += vert[count].pos.z * rcpNum;
count++;
}
Now you need to go through every vert and work out which vert is the furthest away from the center point.
Something like this would work (in C++):
float maxSqDist = 0.0f;
int count = 0;
while( count < numVerts )
{
D3DXVECTOR3 diff = avgPos - vert[count].pos;
// Note we may as well use the square length as the sqrt is very expensive and the
// maximum square length will ALSO be the maximum length and yet we only need to
// do one sqrt this way :)
const float sqDist = D3DXVec3LengthSq( diff );
if ( sqDist > maxSqDist )
{
maxSqDist = sqDist;
}
count++;
}
const float radius = sqrtf( maxSqDist );
And you now have your center position (avgPos) and your radius (radius) and, thus, all the info you need to define a bounding sphere.
I have an idea, what I would do is that I would determine the center of every single mesh object, and then determine the center of the collection of mesh objects by using the aforementioned information ...

Categories

Resources