Calculation island error with perlin noise - c#

I'm developing a small project, which is a grid of 100 x 100 hexagons.
In the script below, I paint my hexagons with the perlin noise, but the format I want to island does not go away.
I'll leave my code and 2 examples as my map stays and how I wish it to stay.
My island
My Island
As i need
Im Need
int getColor(float x, float z)
{
xTO = (int)x / terrainWidth - 30;
zTO = (int)z / terrainHeight - 30;
float v = Mathf.PerlinNoise((xTO + x + seed) * freq, (zTO + z) * freq);
// v += 0.001f;
float form = formWorld(x, z);
if (v < 0.25f)
{
//water
return 0;
}
else if (v < 0.5f)
{
//sand
return 1;
}
else if (v < 0.75f)
{
//grass
return 2;
}
else
{
//Trees / Forest
MakeNewTree(new Vector3(xx, 0, z * 7.5f));
return 2;
}
}

If you want your image to look more like the second one, the best option is going to be adding a circular gradient which offsets your Perlin Noise.
The easiest way to do this is to measure the distance from the center and combine that with the perlin noise.
Here's some untested code.
int getColor(float x, float z)
{
xTO = (int)x / terrainWidth - 30;
zTO = (int)z / terrainHeight - 30;
float v = Mathf.PerlinNoise((xTO + x + seed) * freq, (zTO + z) * freq);
// v += 0.001f;
v -= CircleOffset(x,z)/2; //Change the two to make the island bigger.
float form = formWorld(x, z);
if (v < 0.25f)
{
//water
return 0;
}
else if (v < 0.5f)
{
//sand
return 1;
}
else if (v < 0.75f)
{
//grass
return 2;
}
else
{
//Trees / Forest
MakeNewTree(new Vector3(xx, 0, z * 7.5f));
return 2;
}
}
float CircleOffset(float x, float y)
{
Vector2 center = new Vector2(terrainWidth/2,terrainHeight/2);
float distance = Mathf.Sqrt((center.x - x)*(center.x - x) + (center.y - y) * (center.y - y));
return distance/terrainWidth;
}
Hope this helps!

Related

Unity - How to create circular gradient?

I need a small help, I am trying to create an Island generator using Unity.
However I do not know how to make circular fall Off map.But instead I manage to create an island that is more of a box shaped island.
This is what I am doing:
I am generating Height map using Perlin noise
I will generate FallOff map and substract it from Perlin noise
The Island getsgenerated, but instead of having circular shape the
island hasbox-like shape
I want to create this
This is my Perlin noise function
public float[,] GenerateNoise(int mapSize,int octaves, string seed, float noiseScale, float persistence, float lacunarity, Vector2 offset)
{
if (noiseScale <= 0)
{
noiseScale = 0.0001f;
}
float halfWidth = mapSize / 2f;
float halfHeight = mapSize / 2f;
float[,] noiseMap = new float[mapSize + 1, mapSize + 1];
System.Random rand = new System.Random(seed.GetHashCode());
//Octaves offset
Vector2[] octavesOffset = new Vector2[octaves];
for (int i = 0; i < octaves; i++)
{
float offset_X = rand.Next(-100000, 100000) + offset.x;
float offset_Y = rand.Next(-100000, 100000) + offset.y;
octavesOffset[i] = new Vector2(offset_X / mapSize, offset_Y / mapSize);
}
for (int x = 0; x < mapSize; x++)
{
for (int y = 0; y < mapSize; y++)
{
float amplitude = 1;
float frequency = 1;
float noiseHeight = 0;
float superpositionCompensation = 0;
for (int i = 0; i < octaves; i++)
{
float sampleX = (x - halfWidth) / noiseScale * frequency + octavesOffset[i].x;
float sampleY = (y - halfHeight) / noiseScale * frequency + octavesOffset[i].y;
float perlinValue = Mathf.PerlinNoise(sampleX, sampleY);
noiseHeight += perlinValue * amplitude;
noiseHeight -= superpositionCompensation;
amplitude *= persistence;
frequency *= lacunarity;
superpositionCompensation = amplitude / 2;
}
noiseMap[x, y] = Mathf.Clamp01(noiseHeight);
}
}
return noiseMap;
}
And this is my FallOff map function
public float[,] GenerateFallOffMap(int mapSize)
{
float[,] fallOffMap = new float[mapSize , mapSize];
for (int x = 0; x < mapSize; x++)
{
for (int y = 0; y < mapSize; y++)
{
int index = x * mapSize + y;
float fallOff_A = x / (float)mapSize * 2 - 1;
float fallOff_B = y / (float)mapSize * 2 - 1;
float value = Mathf.Max(Mathf.Abs(fallOff_A), Mathf.Abs(fallOff_B));
fallOffMap[x,y] = Evaluate(value);
}
}
return fallOffMap;
}
static float Evaluate(float value)
{
float a = 3;
float b = 2.2f;
return Mathf.Pow(value, a) / (Mathf.Pow(value, a) + Mathf.Pow(b - b * value, a));
}
Use the distance from the center of the grid as the parameter to calculate the falloff value.
/// value - The calculated value to process
/// radius - The distance from center to calculate falloff distance
/// x - The x-coordinate of the value position
/// y - The y-coordinate of the value position
/// cx - The x-coordinate of the center position
/// cy - The y-coordinate of the center position
public float RadialFallOff(float value, float radius, int x, int y, float cx, float cy)
{
float dx = cx - x;
float dy = cy - y;
float distSqr = dx * dx + dy * dy;
float radSqr = radius * radius;
if (distSqr > radSqr) return 0f;
return value;
}
This will result in a hard cutoff at radius. If you want a softer transition along the edges, you can use an innerRadius and an outerRadius to produce a feathered effect:
/// value - The calculated value to process
/// innerRadius - The distance from center to start feathering
/// outerRadius - The distance from center to fully fall off
/// x - The x-coordinate of the value position
/// y - The y-coordinate of the value position
/// cx - The x-coordinate of the center position
/// cy - The y-coordinate of the center position
public float FeatheredRadialFallOff(float value, float innerRadius, float outerRadius, int x, int y, float cx, float cy)
{
float dx = cx - x;
float dy = cy - y;
float distSqr = dx * dx + dy * dy;
float iRadSqr = innerRadius * innerRadius;
float oRadSqr = outerRadius * outerRadius;
if (distSqr >= oRadSqr) return 0f;
if (distSqr <= iRadSqr) return value;
float dist = Mathf.Sqr(distSqr);
float t = Mathf.InverseLerp(innerRadius, outerRadius, dist);
// Use t with whatever easing you want here, or leave it as is for linear easing
return value * t;
}
You can use it like such:
float value = Mathf.Max(Mathf.Abs(fallOff_A), Mathf.Abs(fallOff_B));
value = Evaluate(value)
fallOffMap[x,y] = RadialFalloff(value, someRadius, x, y, mapSize / 2f, mapSize / 2f);
// or
fallOffMap[x,y] = FeatheredRadialFalloff(value, someInnerRadius, someOuterRadius, x, y, mapSize / 2f, mapSize / 2f);

Get all points within a triangle is causing an overflow

Well I'm lacking GraphicsPath in Unity (to fills polygon, draw them with and outline and utilities with shapes in general), so I'm doing my own implementation of it. Well, we could debate also which is the best option, but actually, I prefer this because I'm learning a lot.
The idea is the following, given a polygon, we do an offset polygon (inwards and outwards) with ClipperLib, and later with LibTessDotNet we triangulate it, outputing this:
Green, blue and yellow pixels are the sides of every triangle. LibTessDotNet output like 501 triangles for this shape.
So, thanks to #SimpleVar I done this:
public static IEnumerable<T> PointsInTriangle<T>(T pt1, T pt2, T pt3)
where T : IPoint
{
/*
// https://www.geeksforgeeks.org/check-whether-triangle-valid-not-sides-given/
a + b > c
a + c > b
b + c > a
*/
float a = Vector2.Distance(new Vector2(pt1.x, pt1.y), new Vector2(pt2.x, pt2.y)),
b = Vector2.Distance(new Vector2(pt2.x, pt2.y), new Vector2(pt3.x, pt3.y)),
c = Vector2.Distance(new Vector2(pt3.x, pt3.y), new Vector2(pt1.x, pt1.y));
// (new[] { pt1, pt2, pt3 }).Distinct(new PointComparer()).Count() == 0
if (a + b <= c || a + c <= b || b + c <= a)
{
Debug.LogWarning($"The given points must form a triangle. {{{pt1}, {pt2}, {pt3}}}");
yield break;
}
T tmp;
if (pt2.x < pt1.x)
{
tmp = pt1;
pt1 = pt2;
pt2 = tmp;
}
if (pt3.x < pt2.x)
{
tmp = pt2;
pt2 = pt3;
pt3 = tmp;
if (pt2.x < pt1.x)
{
tmp = pt1;
pt1 = pt2;
pt2 = tmp;
}
}
var baseFunc = CreateFunc(pt1, pt3);
var line1Func = pt1.x == pt2.x ? (x => pt2.y) : CreateFunc(pt1, pt2);
for (var x = pt1.x; x < pt2.x; ++x)
{
int maxY;
int minY = GetRange(line1Func(x), baseFunc(x), out maxY);
for (int y = minY; y <= maxY; ++y)
yield return (T)Activator.CreateInstance(typeof(T), x, y);
}
var line2Func = pt2.x == pt3.x ? (x => pt2.y) : CreateFunc(pt2, pt3);
for (var x = pt2.x; x <= pt3.x; ++x)
{
int maxY;
int minY = GetRange(line2Func(x), baseFunc(x), out maxY);
for (int y = minY; y <= maxY; ++y)
yield return (T)Activator.CreateInstance(typeof(T), x, y);
}
}
private static int GetRange(float y1, float y2, out int maxY)
{
if (y1 < y2)
{
maxY = Mathf.FloorToInt(y2);
return Mathf.CeilToInt(y1);
}
maxY = Mathf.FloorToInt(y1);
return Mathf.CeilToInt(y2);
}
private static Func<int, float> CreateFunc<T>(T pt1, T pt2)
where T : IPoint
{
var y0 = pt1.y;
if (y0 == pt2.y)
return x => y0;
float m = (float)(pt2.y - y0) / (pt2.x - pt1.x);
return x => m * (x - pt1.x) + y0;
}
Actually it works, but not too fine. Because it causes overflows (I have to kill Unity process with Process Explorer due to the big amount of RAM used by this code).
I have debugged this thing with breakpoints, but I can't figure where is the problem actually.
I think the problem are in for (var x = pt1.x; x < pt2.x; ++x) or for (int y = minY; y <= maxY; ++y) or in the next block... But as I said I can't debug like I'm get used to in WinForms. When the overflow is reached Visual Studio stops debugging and Unity crashes, so I'm a little bit stucked.
I tried to do a DotNetFiddle doing an overflow but I can't figure out nothing here... So... I don't know what can I do to improve the code.
Explain me everything you find is unoptimized, as well as, approaches I could do to improve my main goal.
Ok, I solved it the problem was that triangles with an area equal or less to 1 was doing the overflow. Checking this with the Heron's formula I solved it:
public static float TriangleArea(Point p1, Point p2, Point p3)
{
float a, b, c;
if (!CheckIfValidTriangle(p1, p2, p3, out a, out b, out c))
return 0;
return TriangleArea(a, b, c);
}
public static float TriangleArea(float a, float b, float c)
{
// Thanks to: http://james-ramsden.com/area-of-a-triangle-in-3d-c-code/
float s = (a + b + c) / 2.0f;
return Mathf.Sqrt(s * (s - a) * (s - b) * (s - c));
}
And then:
if (TriangleArea(pt1, pt2, pt3) <= 1)
return;
Maybe (I didn't tested) but it could be caused by generics.
In any case, I posted on Gist Github this nice TriangleUtils. Given a list of triangles tesselated with LibTessDotNet we can rasterize it: https://gist.github.com/z3nth10n/7d60f22c7e906f645d53c9622507c23b
I uploaded the following video showing what I achieved: https://youtu.be/7yY3MIyRtPw

How to create collider between two circle in 2D

I have created a mesh of first image and i want the collider between the pink shaded region
After writing the following Script i got collider in second image
void InnerOuterCircle () {
vertice = new List<Vector3> ();
triangle = new List<int> ();
for (int x = 0; x <= angle; x ++) {
vertice.Add(new Vector3(Mathf.Cos(x * Mathf.Deg2Rad) * innerRadius,Mathf.Sin(x * Mathf.Deg2Rad) * innerRadius));
vertice.Add(new Vector3(Mathf.Cos(x * Mathf.Deg2Rad) * outerRadius,Mathf.Sin(x * Mathf.Deg2Rad) * outerRadius));
}
for (int x = 0; x < vertice.Count - 2; x += 2) {
triangle.Add (x + 0);
triangle.Add (x + 2);
triangle.Add (x + 1);
triangle.Add (x + 2);
triangle.Add (x + 3);
triangle.Add (x + 1);
}
Mesh mesh = new Mesh ();
MeshFilter filter = GetComponent<MeshFilter> ();
filter.mesh = mesh;
mesh.vertices = vertice.ToArray ();
mesh.triangles = triangle.ToArray ();
PolygonCollider2D collider = gameObject.AddComponent<PolygonCollider2D> ();
Vector2[] edgePoints = new Vector2[vertice.Count];
for (int i = 0; i < vertice.Count; i++) {
edgePoints [i] = vertice [i];
}
collider.points = edgePoints;
}
Assuming it's possible to do custom collision detection logic in Unity, collision detection between circles is rather simple; just use the Pythagorean theorem on the vector between their centers:
bool TestCirclesCollision(double x1, double y1, double r1, double x2, double y2, double r2)
{
// Pythagorean theorem to compute distance between two points
var dx = x2 - x1;
var dy = y2 - y1;
var distance = Math.Sqrt(dx * dx + dy * dy);
// compare to combined radii of two circles
// return true if collision, otherwise false
return distance <= r1 + r2;
}
If the circles have holes in them at their centers, you would add an additional test to make sure they're not too close, same as the existing radius test but with a >= instead of a <= on the inner:
return distance <= r1outer + r2outer && distance >= r1inner + r2inner;

Translating concave hull algorithm to c#

So I am trying to translate the algorith found here for concave hulls: http://repositorium.sdum.uminho.pt/bitstream/1822/6429/1/ConcaveHull_ACM_MYS.pdf
(Page 65)
Ive read through the entire thing but I cant figure out how to implement sortByAngle and angle, im not to sure what method I should do inside of them. This is what I have so far:
//Main method
public static Vertex[] ConcaveHull(Vertex[] points, int k = 3)
{
if (k < 3)
throw new ArgumentException("K is required to be 3 or more", "k");
List<Vertex> hull = new List<Vertex>();
//Clean first, may have lots of duplicates
Vertex[] clean = RemoveDuplicates(points);
if (clean.Length < 3)
throw new ArgumentException("At least 3 dissimilar points reqired", "points");
if (clean.Length == 3)//This is the hull, its already as small as it can be.
return clean;
if (clean.Length < k)
throw new ArgumentException("K must be equal to or smaller then the amount of dissimilar points", "points");
Vertex firstPoint = clean[0]; //TODO find mid point
hull.Add(firstPoint);
Vertex currentPoint = firstPoint;
Vertex[] dataset = RemoveIndex(clean, 0);
double previousAngle = 0;
int step = 2;
int i;
while (((currentPoint != firstPoint) || (step == 2)) && (dataset.Length > 0))
{
if (step == 5)
dataset = Add(dataset, firstPoint);
Vertex[] kNearestPoints = nearestPoints(dataset, currentPoint, k);
Vertex[] cPoints = sortByAngle(kNearestPoints, currentPoint, previousAngle);
bool its = true;
i = 0;
while ((its) && (i < cPoints.Length))
{
i++;
int lastPoint = 0;
if (cPoints[0] == firstPoint)
lastPoint = 1;
int j = 2;
its = false;
while ((!its) && (j < hull.Count - lastPoint))
{
its = intersectsQ(hull[step - 1 - 1], cPoints[0], hull[step - i - j - 1], hull[step - j - 1]);
j++;
}
}
if (its)
{
return ConcaveHull(points, k + 1);
}
currentPoint = cPoints[0];
hull.Add(currentPoint);
previousAngle = angle(hull[step - 1], hull[step - 2]);
dataset = RemoveIndex(dataset, 0);
step++;
}
bool allInside = true;
i = dataset.Length;
while (allInside && i > 0)
{
allInside = new Polygon(dataset).Contains(currentPoint); //TODO havent finished ray casting yet.
i--;
}
if (!allInside)
return ConcaveHull(points, k + 1);
return hull.ToArray();
}
private static Vertex[] Add(Vertex[] vs, Vertex v)
{
List<Vertex> n = new List<Vertex>(vs);
n.Add(v);
return n.ToArray();
}
private static Vertex[] RemoveIndex(Vertex[] vs, int index)
{
List<Vertex> removed = new List<Vertex>();
for (int i = 0; i < vs.Length; i++)
if (i != index)
removed.Add(vs[i]);
return removed.ToArray();
}
private static Vertex[] RemoveDuplicates(Vertex[] vs)
{
List<Vertex> clean = new List<Vertex>();
VertexComparer vc = new VertexComparer();
foreach (Vertex v in vs)
{
if (!clean.Contains(v, vc))
clean.Add(v);
}
return clean.ToArray();
}
private static Vertex[] nearestPoints(Vertex[] vs, Vertex v, int k)
{
Dictionary<double, Vertex> lengths = new Dictionary<double, Vertex>();
List<Vertex> n = new List<Vertex>();
double[] sorted = lengths.Keys.OrderBy(d => d).ToArray();
for (int i = 0; i < k; i++)
{
n.Add(lengths[sorted[i]]);
}
return n.ToArray();
}
private static Vertex[] sortByAngle(Vertex[] vs, Vertex v, double angle)
{
//TODO
return new Vertex[]{};
}
private static bool intersectsQ(Vertex v1, Vertex v2, Vertex v3, Vertex v4)
{
return intersectsQ(new Edge(v1, v2), new Edge(v3, v4));
}
private static bool intersectsQ(Edge e1, Edge e2)
{
double x1 = e1.A.X;
double x2 = e1.B.X;
double x3 = e2.A.X;
double x4 = e2.B.X;
double y1 = e1.A.Y;
double y2 = e1.B.Y;
double y3 = e2.A.Y;
double y4 = e2.B.Y;
var x = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4));
var y = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4));
if (double.IsNaN(x) || double.IsNaN(y))
{
return false;
}
else
{
if (x1 >= x2)
{
if (!(x2 <= x && x <= x1)) { return false; }
}
else
{
if (!(x1 <= x && x <= x2)) { return false; }
}
if (y1 >= y2)
{
if (!(y2 <= y && y <= y1)) { return false; }
}
else
{
if (!(y1 <= y && y <= y2)) { return false; }
}
if (x3 >= x4)
{
if (!(x4 <= x && x <= x3)) { return false; }
}
else
{
if (!(x3 <= x && x <= x4)) { return false; }
}
if (y3 >= y4)
{
if (!(y4 <= y && y <= y3)) { return false; }
}
else
{
if (!(y3 <= y && y <= y4)) { return false; }
}
}
return true;
}
private static double angle(Vertex v1, Vertex v2)
{
// TODO fix
Vertex v3 = new Vertex(v1.X, 0);
if (Orientation(v3, v1, v2) == 0)
return 180;
double b = EuclideanDistance(v3, v1);
double a = EuclideanDistance(v1, v2);
double c = EuclideanDistance(v3, v2);
double angle = Math.Acos((Math.Pow(a, 2) + Math.Pow(b, 2) - Math.Pow(c, 2)) / (2 * a * b));
if (Orientation(v3, v1, v2) < 0)
angle = 360 - angle;
return angle;
}
private static double EuclideanDistance(Vertex v1, Vertex v2)
{
return Math.Sqrt(Math.Pow((v1.X - v2.X), 2) + Math.Pow((v1.Y - v2.Y), 2));
}
public static double Orientation(Vertex p1, Vertex p2, Vertex p)
{
double Orin = (p2.X - p1.X) * (p.Y - p1.Y) - (p.X - p1.X) * (p2.Y - p1.Y);
if (Orin > 0)
return -1;//Left
if (Orin < 0)
return 1;//Right
return 0;//Colinier
}
I know that there is a load of code here. But im not sure if I can show the context and what I have without it.
Other classes:
public class Polygon
{
private Vertex[] vs;
public Polygon(Vertex[] Vertexes)
{
vs = Vertexes;
}
public Polygon(Bounds bounds)
{
vs = bounds.ToArray();
}
public Vertex[] ToArray()
{
return vs;
}
public IEnumerable<Edge> Edges()
{
if (vs.Length > 1)
{
Vertex P = vs[0];
for (int i = 1; i < vs.Length; i++)
{
yield return new Edge(P, vs[i]);
P = vs[i];
}
yield return new Edge(P, vs[0]);
}
}
public bool Contains(Vertex v)
{
return RayCasting.RayCast(this, v);
}
}
public class Edge
{
public Vertex A = new Vertex(0, 0);
public Vertex B = new Vertex(0, 0);
public Edge() { }
public Edge(Vertex a, Vertex b)
{
A = a;
B = b;
}
public Edge(double ax, double ay, double bx, double by)
{
A = new Vertex(ax, ay);
B = new Vertex(bx, by);
}
}
public class Bounds
{
public Vertex TopLeft;
public Vertex TopRight;
public Vertex BottomLeft;
public Vertex BottomRight;
public Bounds() { }
public Bounds(Vertex TL, Vertex TR, Vertex BL, Vertex BR)
{
TopLeft = TL;
TopRight = TR;
BottomLeft = BL;
BottomRight = BR;
}
public Vertex[] ToArray()
{
return new Vertex[] { TopLeft, TopRight, BottomRight, BottomLeft };
}
}
public class Vertex
{
public double X = 0;
public double Y = 0;
public Vertex() { }
public Vertex(double x, double y)
{
X = x;
Y = y;
}
public static Vertex[] Convert(string vs)
{
vs = vs.Replace("[", "");
vs = vs.Replace("]", "");
string[] spl = vs.Split(';');
List<Vertex> nvs = new List<Vertex>();
foreach (string s in spl)
{
try
{
nvs.Add(new Vertex(s));
}
catch
{
}
}
return nvs.ToArray();
}
public static string Stringify(Vertex[] vs)
{
string res = "[";
foreach (Vertex v in vs)
{
res += v.ToString();
res += ";";
}
res = res.RemoveLastCharacter();
res += "]";
return res;
}
public static string ToString(Vertex[] array)
{
string res = "[";
foreach (Vertex v in array)
res += v.ToString() + ",";
return res.RemoveLastCharacter() + "]";
}
/*
//When x < y return -1
//When x == y return 0
//When x > y return 1
public static int Compare(Vertex x, Vertex y)
{
//To find lowest
if (x.X < y.X)
{
return -1;
}
else if (x.X == y.X)
{
if (x.Y < y.Y)
{
return -1;
}
else if (x.Y == y.Y)
{
return 0;
}
else
{
return 1;
}
}
else
{
return 1;
}
}
*/
public static int CompareY(Vertex a, Vertex b)
{
if (a.Y < b.Y)
return -1;
if (a.Y == b.Y)
return 0;
return 1;
}
public static int CompareX(Vertex a, Vertex b)
{
if (a.X < b.X)
return -1;
if (a.X == b.X)
return 0;
return 1;
}
public double distance (Vertex b){
double dX = b.X - this.X;
double dY = b.Y - this.Y;
return Math.Sqrt((dX*dX) + (dY*dY));
}
public double slope (Vertex b){
double dX = b.X - this.X;
double dY = b.Y - this.Y;
return dY / dX;
}
public static int Compare(Vertex u, Vertex a, Vertex b)
{
if (a.X == b.X && a.Y == b.Y) return 0;
Vertex upper = new Vertex();
Vertex p1 = new Vertex();
Vertex p2 = new Vertex();
upper.X = (u.X + 180) * 360;
upper.Y = (u.Y + 90) * 180;
p1.X = (a.X + 180) * 360;
p1.Y = (a.Y + 90) * 180;
p2.X = (b.X + 180) * 360;
p2.Y = (b.Y + 90) * 180;
if(p1 == upper) return -1;
if(p2 == upper) return 1;
double m1 = upper.slope(p1);
double m2 = upper.slope(p2);
if (m1 == m2)
{
return p1.distance(upper) < p2.distance(upper) ? -1 : 1;
}
if (m1 <= 0 && m2 > 0) return -1;
if (m1 > 0 && m2 <= 0) return -1;
return m1 > m2 ? -1 : 1;
}
public static Vertex UpperLeft(Vertex[] vs)
{
Vertex top = vs[0];
for (int i = 1; i < vs.Length; i++)
{
Vertex temp = vs[i];
if (temp.Y > top.Y || (temp.Y == top.Y && temp.X < top.X))
{
top = temp;
}
}
return top;
}
}
Just a note on convention: you should start function names with upper case, and variables with lower case. In the function sortByAngle, you have a reference to the parameter angle and the function angle simultaneously.
Assuming Angle(...) is simply meant to calculate the angle between two points:
private static double Angle(Vertex v1, Vertex v2)
{
return Math.Atan2(v2.Y - v1.Y, v2.X - v1.X);
}
will give you the angle from v1 to v2, in radians between -pi and +pi. Do not mix degrees and radians. My suggestion is to always use radians, and only convert to degrees if necessary for human-readable output.
private static Vertex[] SortByAngle(Vertex[] vs, Vertex v, double angle)
{
List<Vertex> vertList = new List<Vertex>(vs);
vertList.Sort((v1, v2) => AngleDifference(angle, Angle(v, v1)).CompareTo(AngleDifference(angle, Angle(v, v2))));
return vertList.ToArray();
}
uses List.Sort to sort the vertices from greatest to least angle difference between the vertices point and itself, and angle. The order of v1 and v2 are swapped in the input tuple to sort descending, that is, greatest difference first. The difference between angles is calculated like so:
private static double AngleDifference(double a, double b)
{
while (a < b - Math.PI) a += Math.PI * 2;
while (b < a - Math.PI) b += Math.PI * 2;
return Math.Abs(a - b);
}
The first two lines ensure that the angles are not more than 180 degrees apart.
You have error in
private static Vertex[] nearestPoints(Vertex[] vs, Vertex v, int k)
{
Dictionary<double, Vertex> lengths = new Dictionary<double, Vertex>();
List<Vertex> n = new List<Vertex>();
double[] sorted = lengths.Keys.OrderBy(d => d).ToArray();
for (int i = 0; i < k; i++)
{
n.Add(lengths[sorted[i]]);
}
return n.ToArray();
}
according to code if you have several vertexes at the same distance, function returns only one. Since Dictionary uses unique keys.
BTW, did anyone finish this?
I don't have the time right now to read the paper, but I assume from my knowledge of conVEX hull algorithms that you're going around the points in a particular direction looking for the next point to link to.
If that's the case, "angle" would be the angle of the most recent line segment of the hull, and you want to sort the points by their angle from that line. Therefore you want to calculate the angles between a line (on the hull) and a set of lines (from the current point to each other point being considered). Whether the angles calculated are positive or negative depends upon whether you're going clockwise or anticlockwise. To calculate the angles, look at something like this:
Calculating the angle between two lines without having to calculate the slope? (Java)
Then just sort by the angles.
What about that?
private List<Vector> sortClockwiseFromCentroid(List<Vector> points, Vector center)
{
points = points.OrderBy(x => Math.Atan2(x.X - center.X, x.Y - center.Y)).ToList();
return points;
}

Optimizing a formula to create a triangle from arbitrary points

I think a little background will help before I get into my question. What I'm doing is creating my own small 2D physics library in xna, for fun nonetheless. This is also my first independent xna project, and my first time working with the 3D tools, so I may be doing things all wacky. Anyway, I'm currently making a triangle class in which the constructor takes three arbitrary points in the form of Vector2s. In the constructor I have to put these points in clockwise order (so they're not culled) and then find the texture coordinates they should correspond to (since I'm using VertexPositionTextures as my vertices). What I've got works, but it seems very long and complicated. I'm looking for any ways to shorten/simplify the code, which is this:
public PTriangle(Vector2 a, Vector2 b, Vector2 c)
: base()
{
//set up vertices
VertexPositionTexture[] vertices = new VertexPositionTexture[3];
//center vertices around origin
Vector2 center = new Vector2((a.X + b.X + c.X) / 3, (a.Y + b.Y + c.Y) / 3);
Vector2 newA = a - center;
Vector2 newB = b - center;
Vector2 newC = c - center;
//get angle of each vertex (clockwise from -x axis)
double angleA = MathHelper.Pi - Math.Atan((double)(newA.Y / newA.X));
double angleB = MathHelper.Pi - Math.Atan((double)(newB.Y / newB.X));
double angleC = MathHelper.Pi - Math.Atan((double)(newC.Y / newC.X));
if (newA.X < 0)
{
if (newA.Y < 0)
{
angleA += MathHelper.Pi;
}
else
{
angleA -= MathHelper.Pi;
}
}
if (newB.X < 0)
{
if (newB.Y < 0)
{
angleB += MathHelper.Pi;
}
else
{
angleB -= MathHelper.Pi;
}
}
if (newC.X < 0)
{
if (newC.Y < 0)
{
angleC += MathHelper.Pi;
}
else
{
angleC -= MathHelper.Pi;
}
}
//order vertices by angle
Vector2[] newVertices = new Vector2[3];
if (angleA < angleB && angleA < angleC)
{
newVertices[0] = newA;
if (angleB < angleC)
{
newVertices[1] = newB;
newVertices[2] = newC;
}
else
{
newVertices[1] = newC;
newVertices[2] = newB;
}
}
else if (angleB < angleA && angleB < angleC)
{
newVertices[0] = newB;
if (angleA < angleC)
{
newVertices[1] = newA;
newVertices[2] = newC;
}
else
{
newVertices[1] = newC;
newVertices[2] = newA;
}
}
else
{
newVertices[0] = newC;
if (angleA < angleB)
{
newVertices[1] = newA;
newVertices[2] = newB;
}
else
{
newVertices[1] = newB;
newVertices[2] = newA;
}
}
//set positions of vertices
vertices[0].Position = new Vector3(newVertices[0] + center, 0);
vertices[1].Position = new Vector3(newVertices[1] + center, 0);
vertices[2].Position = new Vector3(newVertices[2] + center, 0);
//get width and height of triangle
float minX = 0;
float minY = 0;
float maxX = 0;
float maxY = 0;
foreach (Vector2 vertex in newVertices)
{
if (vertex.X < minX)
{
minX = vertex.X;
}
else if (vertex.X > maxX)
{
maxX = vertex.X;
}
if (vertex.Y < minY)
{
minY = vertex.Y;
}
else if (vertex.Y > maxY)
{
maxY = vertex.Y;
}
}
float width = maxX - minX;
float height = maxY - minY;
//shift triangle so fits in quadrant IV, and set texture coordinates
for (int index = 0; index < newVertices.Length; ++index)
{
newVertices[index].X -= minX;
newVertices[index].Y -= minY;
vertices[index].TextureCoordinate = new Vector2(
newVertices[index].X / width,
1 - (newVertices[index].Y / height));
}
this.Vertices = vertices;
//set up indices
this.Indices = new short[] { 0, 1, 2 };
}
To put the 3 points in clockwise order, you can use counter-clockwise test (or left-turn test) to check the direction of the 3 points.
Pseudocode from Wikipedia
# Three points are a counter-clockwise turn if ccw > 0, clockwise if
# ccw < 0, and collinear if ccw = 0 because ccw is a determinant that
# gives the signed area of the triangle formed by p1, p2 and p3.
function ccw(p1, p2, p3):
return (p2.x - p1.x)*(p3.y - p1.y) - (p2.y - p1.y)*(p3.x - p1.x)
If the 3 points are counter-clockwise, you can just swap the last 2 points to make the 3 points clockwise order.

Categories