Find vertices in equilateral triangle mesh originating from a center vertex - c#

I'd like to ask whether there is code out there or if you can give me some help in writing some (C#, but I guess the maths is the same everywhere).
I'd like to specify a center point from which an equilateral triangle mesh is created and get the vertex points of these triangles. The center point should not be a face center, but a vertex itself.
A further input would be the size of the triangles (i.e side length) and a radius to which triangle vertices are generated.
The reason behind it is that I want to create a mesh which is centered nicely on the screen/window center with as little code as possible. I just find mesh generation code, but not a "radial outward propagation" example.
In the end, I'd like to have the subsequently farther away vertices being displaced in a logarithmic fashion, but I guess that's just an easy addition once the mesh code is there.
Can anybody help me with that? Thanks!

You need to specify two things, a radius and the direction that the first triangle points.
The radius will be the distance from the initial point to the vertices of the first triangle. All triangles will have the same radius.
The direction is some specification in radians. I will assume that 0 means pointing to the right (PI would be point to the left).
Finding the vertices of the first triangle can be done like this (pseudo-code, not language specific):
float theta = 0; // The direction, 0 means pointing to the right
float thetaInc = TWO_PI/3; // 3 because you want a triangle
for (int i = 0; i < 3; i++) {
vertX[i] = initialPointX+cos(theta)*radius;
vertY[i] = initialPointY+sin(theta)*radius;
theta += thetaInc;
}
There are many ways to find the center points of the neighboring triangles. One way would be to use the same code but initialize theta = TWO_PI/6, replace radius with foo (see math below), assign new center points of neighboring triangles in the for loop, and then use the same code with an appropriately rotated direction (theta += PI) to find the vertices of those triangles.
Distance from one triangle center to another only knowing radius:
hypotenuse = sqrt(sq(radius)+sq(radius));
halfHypotenuse = hypotenuse/2.0;
Pythagorean theorem to find distance from center of triangle to center of an edge: foo = sqrt(sq(radius)-sq(halfHypotenuse));
Final distance = foo*2.0;
Code to find the center points of the neighboring triangles:
float[] nx = new float[3];
float[] ny = new float[3];
float theta = TWO_PI/6;
float hyp = sqrt(sq(radius)+sq(radius));
float halfHyp = hyp/2.0;
float foo = sqrt((sq(radius)-sq(halfHyp)))*2.0;
for (int i = 0; i < 3; i++) {
nx[i] = initialPointX+cos(theta)*foo;
ny[i] = initialPointY+sin(theta)*foo;
theta += thetaInc;
}

Thank you very much for your answer. I will play around with your code - the propagation part will come handy for sure.
In the meantime I have played around with hexagons instead of triangles and this codes works fairly alright for the same purpose.:
//populate array from the centre hex, going outwards the desired number of hex rings
for (int i = 0; i < numberOfHexagonRings; i++)
{
for (double j = -i; j <= i; j++)
for (double k = -i; k <= i; k++)
for (double l = -i; l <= i; l++)
if ((Math.Abs(j) + Math.Abs(k) + Math.Abs(l) == i * 2) && (j + k + l == 0))
{
positionX = (int)(screenCenterX + ((double)sideLength * (l / 2 + j)));
positionY = (int)(screenCenterY + (3/2 * ((double)sideLength / Math.Sqrt(3)) * l));

Related

How to detect if object collide with each other if all I have is a list of their positions and their size?

I have a lot of circles and I store their position in a list.
All circles have the same radius.
How can I detect if any circle collides with another circle?
What would be the fastest way?
Given the Pythagorean Theorem, a^2 + b^2 = c^2, if you take dx = circle[0].x - circle[1].x, dy = circle[0].y - circle[1].y, then you can get the hypotenuse which is the straight-line distance between centers by hypotenuse = Mathf.Sqrt(dx^2 + dy^2);.
However, you don't actually need the distance/hypotenuse, you just need to know if that value is less than 2*r, or two radii, because if the distance between the two circles is less than that then you know they're colliding.
This means that you don't need to take the square root, you can just leave it squared and compare it to the collision distance squared, or (2*r)^2. That is, you are colliding when:
(dx^2 + dy^2) <= (2*r)^2
In code, it looks like:
public class Circle
{
public float R { get; private set; }
public float X { get; private set; }
public float Y { get; private set; }
}
public static bool IsColliding(Circle c0, Circle c1)
{
var collisionDistance = (c0.R + c1.R)*(c0.R + c1.R);
var dx = c0.X - c1.X;
var dy = c0.Y - c1.Y;
return dx*dx + dy*dy <= collisionDistance;
}
If the radii are all the same then you can cache collisionDistance and not need to recalculate it on each step.
NOTE: My solution here assumes the radii aren't the same; the "2*r" term is replaced by c0.R + c1.R. If they're the same then it reduces to c0.R + c0.R or 2*c0.R.
:EDIT: To check collisions, if you have 4 circles, you'd need to check 1 against 2, 3, and 4. Then, because you already checked 1 against 2 you can skip 2 against 1. You just need to check 2 against 3 and 4, then just 3 against 4.
That is, if you have a List<Circle> circles, you would get:
var nCircles = circles.Count;
for(int i=0; i<nCircles-1; i++)
{
for(int j=i+1; j<nCircles; j++)
{
if(IsColliding(circles[i], circles[j]))
{
// Do something
}
}
}
This would check 0 against 1,2,3; then 1 against 2,3; then 2 against 3.
Assuming you have a lot of circles (e.g. at least 10,000) and you want to find all circle collisions, you can check each circle against all remaining circles, starting with the first. You can optimize the check by using the bounding box around each circle to filter in only nearby circles, and then test against a diamond inside to filter in close circles and finally test against the full circle to filter out corner circles. Since we know the radius is the same in all cases, we are filtering the centers against a circle of 2*r.
Testing only the circles inside the bounding box is about a 10% speedup, filtering in the circles inside the diamond is about another 1% speedup, and testing in the order given is fastest for collisions of 0.15% to 62% of the total list, but if your list is much smaller, extra tests may cost more time than just the simple test against the bounding circle.
Also if you only care for the first collision for each circle, you can add a break statement after the Add in the if to stop testing once a collision is found.
var r = 3.0;
var ans = new List<(int, int)>();
var r2 = 2 * r;
var r_sq = r2 * r2;
for (int j1 = 0; j1 < circles.Count - 1; ++j1) {
var c1 = circles[j1];
for (int j2 = j1 + 1; j2 < circles.Count; ++j2) {
var c2 = circles[j2];
var dx = Math.Abs(c1.x - c2.x);
var dy = Math.Abs(c1.y - c2.y);
if (dx <= r2 && dy <= r2 && ((dx + dy <= r2) || (dx * dx + dy * dy <= r_sq))) {
ans.Add((j1, j2));
}
}
}
If this isn't sufficient, and you can store your circles in a different structure, it is probably time to look at something like k-d trees.

How to find the "best" position using newtons method for n samples

I am trying to find the best position for n sphere intersections using newtons method.
Sphere equation: (x - a)2 + (y - b)2 + (z - c)2 = r2
where a, b, c = center of sphere
where x, y, z = sample location
where r2 = radius squared
for a point to lie on any sphere, it needs to satisfy this equation. So, for a point to lie on (or close) to multiple spheres, it needs to satisfy this equation for multiple spheres.
I think we can make a cost function like this:
sum((x - a_i)2 + (y - b_i)2 + (z - c_i)2 - (r_i)2) == 0;
where a_i, b_i, c_i = center of i sphere
where x, y, z = sample location
where r_i = radius of i sphere
since dot(pos - center) should equal radii squared, if dot(pos - center) - radii squared == 0, it is the same function.
Newtons method works by using taking samples at intervals, and then uses the derivative of a function in order to move it. This equation looks like this:
s2 = s1 - (f(s1) / f`(s1))
where (s2) = second sample location (target)
f(s1) = first sample location's equation
f`(s1) = derivative of the sample
I have written the following function in C#, which solves the problem when I input 2 spheres:
// For maxIterations:
for (int i = 0; i < maxIterations; i++)
{
float3 vDist = float3.zero;
float errorFunction = 0f;
float3 derivative = float3.zero;
// For each sphere
for (int j = 0; j < centers.Length; j++)
{
// Calculate the sqDistance between the sample point and the sphere, and subtract it with the squared radii in order to get an error function
vDist = sampleLocation - centers[j];
errorFunction += math.dot(vDist, vDist) - (radii[j] * radii[j]);
// The derivative of the sphere is needed in order to move the sample
derivative += new float3((sampleLocation.x - centers[j].x) * (sampleLocation.x - centers[j].x), (sampleLocation.y - centers[j].y) * (sampleLocation.y - centers[j].y), (sampleLocation.z - centers[j].z) * (sampleLocation.z - centers[j].z));
}
// If the errorFunction gets within a certain threshold, the position is found
if (errorFunction >= -threshold && errorFunction <= threshold)
{
print("Found position: " + sampleLocation + " in " + i + ", error: " + errorFunction);
break;
}
// Move the sample based on the error function and the derivative of the sphere
sampleLocation = sampleLocation - (errorFunction / derivative);
}
However, when I input 3 spheres, it does not work anymore, I think because the equation becomes non-linear?
I found a good reference on stackoverflow:
Math - 3d positioning/multilateration
but I don't quite understand the answer given there.
Could someone try to help me extend this function to work with multiple spheres?

Unity 3D Mesh Find aligning vertices on the same Y axis Height

I'm trying to find adjacent vertices from just one selected vertex. One condition these neighboring vertices have to fulfill is that they must be on the same height as the selected original vertex.
I have an int[] vertices array, and a Dictionary<int, float> plateauDictionary.
The vertices array holds all vertices of the entire mesh.
The Dictionary holds a collection of vertices that have a height(y-Axis) greater than 0.2f.
Im creating big flat plateaus, so finding adjacent vertices at the same height should be easy as most vertices are adjacent to each other at the same height.
Heres what I wrote so far:
Matrix4x4 localToWorld = transform.localToWorldMatrix;
Vector3[] worldVectors = new Vector3[totalVertices];
foreach (KeyValuePair<int, float> u in plateauDictionary)
worldVectors[u.Key] = localToWorld.MultiplyPoint3x4(mesh.vertices[u.Key]);
This returns an array of Vector3s consisting of all the world position of each vertex.
Now I want to pick one or a few randomly, and then get the amount of adjacent vertices at the same height, maybe in an assigned radius or maybe in a list of neighbors? How do I solve this? I'm a newb with LINQ but Im thinking that might be the way to go?
Thanks in advance!
My other code(Not really important for this question but for the sake of completeness):
plateauDictionary = new Dictionary<int, float>();
for (int j = 0; j < plateaus; j++)
{
var rndSize = plateauSizes[Random.Range(0, plateauSizes.Length)];
var rndHeight = plateauHeights[Random.Range(0, plateauHeights.Length)];
var rndVertex = ((xSize + 2) + (Random.Range(0, xSize - rndSize - 1) * (xSize + 1))) +
Random.Range(0, xSize - rndSize - 1);
plateauVertexArray = new int[rndSize * rndSize];
for (int k = 0, i = 0; k < rndSize; k++)
{
for (int l = 0; l < rndSize; l++, i++)
{
plateauVertexArray[i] = (rndVertex + ((xSize + 1) * k)) + l;
if (!plateauDictionary.ContainsKey(plateauVertexArray[i]))
plateauDictionary.Add(plateauVertexArray[i], rndHeight);
You could compare the remaining worldVectors.y value with your selected selectedWorldVector.y. I have no idea about linq but i think its a good option if you want to avoid if else spaghetti code.
Aside from that you could also instantiate an empty object at each vertex world position, add an empty box collider to each, and then use a Raycast.SphereCastAll() from your selectedWorldVector position. Set your radius in SphereCast to see each close vertex and then filter out ones that are too low/high.

How to calculate/distribute weights from vertices based on a gradient

I have a list of vertices, of N size, and a weight gradient(which can be any length) defined as:
float[] weight_distribution = { 0f, 1f, 0f };
which says that the first and last vertices will have less weight and the middle vertices will have full. Like a black and white gradient with keys defined like the array.
This is based on the Y-axis for a plane of many segments that is to be weighted for procedural rigging based on the gradient.
The list is sorted based on the vertices' Y values, so that the lowest vertices are found at the start of the list and highest last.
I don't know how to calculate the weight for a given vertex with this kind of gradient. Any pointers would be really helpful.
I tried a few different things to find values regarding the current vertex, but I don't know how to extract the weight from the gradient for this position.
This is probably just garbage, but I'll put it here anyway in case it can help.
// find unique Ys
List<float> ys = new List<float>();
for (int i = 0; i < list.Count; i++) {
if (!ys.Contains(list[i].y)) { ys.Add(list[i].y); }
}
float min = ys[0];
float max = ys[ys.Count - 1];
int levels = (ys.Count - 1);
float levelStep = (gradient.Length * 1f / levels * 1f);
float currentY = ys[0];
// set weights here
for (int i = 0; i < list.Count; i++)
{
// find current pos/value based on gradient somehow?
if(list[i].y > currentY ) { currentY = list[i].y; yindex++; }
float pos = ((yindex * levelStep) / levels * 1f);
float lerped = Mathf.Lerp(list[i].y, max, pos);
// ... calculate weight
}

Procedural texture uv error

I am creating a game with procedural generated plane and uv, it works mostly fine, but there is one place where the uv seems distorted in a very confusing way.
Intro: the uv is mapped in a "Global" uv coordinates, instead of a local one, because the texture has to be aligned with adjacent planes. Each texture has a tiling of 2*2, I introduce a uv scale of 2 which means each plane "v" coordinates will be mapped in range [0, 0.5] of the texture vertically, to make sure horizontally "u" coordinates do not go out of range [0, 1]. (horizontally the noise is unpredictable to some extend).
code:
Vector2[] CalculateU(float[] leftXMap, float[] rightXMap) {
int length = leftXMap.Length;
float totalHeight = (length - 1) * 1; // distance between two vertices vertically is 1
float uvheight = totalHeight * uvScale; // uvScale is 2, purpose is to map a plane's v(vertically), into a range of [0, 0.5]..
Vector2[] UMap = new Vector2[length * 2];
for(int i = 0; i < length; i++) {
float left = leftXMap[i];
float right = rightXMap[i];
// make left and right positive.
while (left < 0) {
left += uvheight;
}
while(right < 0) {
right += uvheight;
}
float leftu = (left % uvheight) / uvheight;
float leftv = (i / (float)(length-1)) / uvScale;
float rightu = (right % uvheight) / uvheight;
float rightv = leftv; //(i / (float)length) / 2f;
UMap[i * 2] = new Vector2(leftu, leftv);
UMap[i * 2 + 1] = new Vector2(rightu, rightv);
}
}
explain:
the parameters for the function, are the noise maps for the generated plane. the noise is the x coordinates. while the y coordinates are simple value of 0, 1, 2, .... (length-1).
Distorted texture:
Zoomed in:
I managed to solved the problem, the UV map gets messed where the left noise map values are negative, and right noise map is positive values.
Since I always try to map all negative values to positive values, so left noise map, which is also be my left vertex x coordinates, will be mapped to positive values. but then it will be larger than right noise map (right vertex x coordinates). then it messed up the UV.
I solved it by not mapping at all, and suddenly realize that UV map values can be negative, I don't have to map at all!

Categories