Get all points within a Triangle - c#
I have three points, for example:
Start 194 171
Right 216 131
Left 216 203
I want to get all the points within that triangle. How would I do that efficiently?
see z3nth10n's answer for better input validation
Introduction:
The general idea was to get the triangle's edges (y-Wise) for every x in it's range, and then you have all the y's that exist within the triangle for every single x, which with simple conversion turns into all points within the triangle.
You can look at it as if you cut the triangle into stripes, each being of width 1.
So for X=0, on the line between A and B, the Y is 6, and on the line between A and C, the Y is -2, so you can see that the stripe of X=0 is between -2 and 6. Therefore, you can tell that (0, -2) (0, -1) (0, 0) ... (0, 5) (0, 6) are all in the triangle. Doing that for X's between the smallest and the largest within the triangle, and you have all the points in the triangle!
Speed:
For the triangle (0, 0) (1, 8) (4, 6) - found 16 points.
Done 1,000,000 times in 3.68 seconds.
Implementation:
public IEnumerable<Point> PointsInTriangle(Point pt1, Point pt2, Point pt3)
{
if (pt1.Y == pt2.Y && pt1.Y == pt3.Y)
{
throw new ArgumentException("The given points must form a triangle.");
}
Point 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 (var y = minY; y <= maxY; y++)
{
yield return new Point(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 (var y = minY; y <= maxY; y++)
{
yield return new Point(x, y);
}
}
}
private int GetRange(double y1, double y2, out int maxY)
{
if (y1 < y2)
{
maxY = (int)Math.Floor(y2);
return (int)Math.Ceiling(y1);
}
maxY = (int)Math.Floor(y1);
return (int)Math.Ceiling(y2);
}
private Func<int, double> CreateFunc(Point pt1, Point pt2)
{
var y0 = pt1.Y;
if (y0 == pt2.Y)
{
return x => y0;
}
var m = (double)(pt2.Y - y0) / (pt2.X - pt1.X);
return x => m * (x - pt1.X) + y0;
}
#SimpleVar answer is well, but it lacks a good check for valid triangles. (This can cause an overflow problem).
So I do my own implementation for Unity3D:
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));
if (a + b <= c || a + c <= b || b + c <= a)
{
Debug.LogWarning($"The given points must form a triangle. {{{pt1}, {pt2}, {pt3}}}");
yield break;
}
if (TriangleArea(pt1, pt2, pt3) <= 1)
{
Point center = GetTriangleCenter(pt1, pt2, pt3);
yield return (T)Activator.CreateInstance(typeof(T), center.x, center.y);
return;
}
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;
}
public static float TriangleArea<T>(T p1, T p2, T p3)
where T : IPoint
{
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));
}
public static Point GetTriangleCenter<T>(T p0, T p1, T p2)
where T : IPoint
{
// Thanks to: https://stackoverflow.com/questions/524755/finding-center-of-2d-triangle
return new Point(p0.x + p1.x + p2.x / 3, p0.y + p1.y + p2.y / 3);
}
public static bool CheckIfValidTriangle<T>(T v1, T v2, T v3, out float a, out float b, out float c)
where T : IPoint
{
a = Vector2.Distance(new Vector2(v1.x, v1.y), new Vector2(v2.x, v2.y));
b = Vector2.Distance(new Vector2(v2.x, v2.y), new Vector2(v3.x, v3.y));
c = Vector2.Distance(new Vector2(v3.x, v3.y), new Vector2(v1.x, v1.y));
if (a + b <= c || a + c <= b || b + c <= a)
return false;
return true;
}
IPoint interface could be a good point for own Point implementations (for libs like Clipper, TessDotNet or Poly2Tri). You can change it at any time (two UnityEngine.Vector2 or System.Drawing.Point).
Hope this helps!
EDIT: I solved all bugs here:
Also I answered my own question asking this: https://stackoverflow.com/a/53734816/3286975
Related
Meteorit generator with simplex noise
I try to make a meteor like this video, but I can only get like this: This is my simplex noise: public class PerlinNoise{ int B = 256; int[] m_perm = new int[B+B]; Texture2D m_permTex; public int octava; public float frequencia, amplitud; public PerlinNoise(int seed, float frec, float amp, int oct) { octava = oct; amplitud = amp; frequencia = frec; UnityEngine.Random.seed = seed; int i, j, k; for (i = 0 ; i < B ; i++) { m_perm[i] = i; } while (--i != 0) { k = m_perm[i]; j = UnityEngine.Random.Range(0, B); m_perm[i] = m_perm[j]; m_perm[j] = k; } for (i = 0 ; i < B; i++) { m_perm[B + i] = m_perm[i]; } } float FADE(float t) { return t * t * t * ( t * ( t * 6.0f - 15.0f ) + 10.0f ); } float LERP(float t, float a, float b) { return (a) + (t)*((b)-(a)); } float GRAD1(int hash, float x ) { int h = hash & 15; float grad = 1.0f + (h & 7); if ((h&8) != 0) grad = -grad; return ( grad * x ); } float GRAD2(int hash, float x, float y) { int h = hash & 7; float u = h<4 ? x : y; float v = h<4 ? y : x; return (((h&1) != 0)? -u : u) + (((h&2) != 0) ? -2.0f*v : 2.0f*v); } float GRAD3(int hash, float x, float y , float z) { int h = hash & 15; float u = h<8 ? x : y; float v = (h<4) ? y : (h==12 || h==14) ? x : z; return (((h&1) != 0)? -u : u) + (((h&2) != 0)? -v : v); } float Noise1D( float x ) { //returns a noise value between -0.5 and 0.5 int ix0, ix1; float fx0, fx1; float s, n0, n1; ix0 = (int)Mathf.Floor(x); // Integer part of x fx0 = x - ix0; // Fractional part of x fx1 = fx0 - 1.0f; ix1 = ( ix0+1 ) & 0xff; ix0 = ix0 & 0xff; // Wrap to 0..255 s = FADE(fx0); n0 = GRAD1(m_perm[ix0], fx0); n1 = GRAD1(m_perm[ix1], fx1); return 0.188f * LERP( s, n0, n1); } public float Noise2D( float x, float y ) { int ix0, iy0, ix1, iy1; float fx0, fy0, fx1, fy1, s, t, nx0, nx1, n0, n1; ix0 = (int)Mathf.Floor(x); iy0 = (int)Mathf.Floor(y); fx0 = x - ix0; fy0 = y - iy0; fx1 = fx0 - 1.0f; fy1 = fy0 - 1.0f; ix1 = (ix0 + 1) & 0xff; // Wrap to 0..255 iy1 = (iy0 + 1) & 0xff; ix0 = ix0 & 0xff; iy0 = iy0 & 0xff; t = FADE( fy0 ); s = FADE( fx0 ); nx0 = GRAD2(m_perm[ix0 + m_perm[iy0]], fx0, fy0); nx1 = GRAD2(m_perm[ix0 + m_perm[iy1]], fx0, fy1); n0 = LERP( t, nx0, nx1 ); nx0 = GRAD2(m_perm[ix1 + m_perm[iy0]], fx1, fy0); nx1 = GRAD2(m_perm[ix1 + m_perm[iy1]], fx1, fy1); n1 = LERP(t, nx0, nx1); return 0.507f * LERP( s, n0, n1 ); } float Noise3D( float x, float y, float z ) { //returns a noise value between -1.5 and 1.5 int ix0, iy0, ix1, iy1, iz0, iz1; float fx0, fy0, fz0, fx1, fy1, fz1; float s, t, r; float nxy0, nxy1, nx0, nx1, n0, n1; ix0 = (int)Mathf.Floor( x ); // Integer part of x iy0 = (int)Mathf.Floor( y ); // Integer part of y iz0 = (int)Mathf.Floor( z ); // Integer part of z fx0 = x - ix0; // Fractional part of x fy0 = y - iy0; // Fractional part of y fz0 = z - iz0; // Fractional part of z fx1 = fx0 - 1.0f; fy1 = fy0 - 1.0f; fz1 = fz0 - 1.0f; ix1 = ( ix0 + 1 ) & 0xff; // Wrap to 0..255 iy1 = ( iy0 + 1 ) & 0xff; iz1 = ( iz0 + 1 ) & 0xff; ix0 = ix0 & 0xff; iy0 = iy0 & 0xff; iz0 = iz0 & 0xff; r = FADE( fz0 ); t = FADE( fy0 ); s = FADE( fx0 ); nxy0 = GRAD3(m_perm[ix0 + m_perm[iy0 + m_perm[iz0]]], fx0, fy0, fz0); nxy1 = GRAD3(m_perm[ix0 + m_perm[iy0 + m_perm[iz1]]], fx0, fy0, fz1); nx0 = LERP( r, nxy0, nxy1 ); nxy0 = GRAD3(m_perm[ix0 + m_perm[iy1 + m_perm[iz0]]], fx0, fy1, fz0); nxy1 = GRAD3(m_perm[ix0 + m_perm[iy1 + m_perm[iz1]]], fx0, fy1, fz1); nx1 = LERP( r, nxy0, nxy1 ); n0 = LERP( t, nx0, nx1 ); nxy0 = GRAD3(m_perm[ix1 + m_perm[iy0 + m_perm[iz0]]], fx1, fy0, fz0); nxy1 = GRAD3(m_perm[ix1 + m_perm[iy0 + m_perm[iz1]]], fx1, fy0, fz1); nx0 = LERP( r, nxy0, nxy1 ); nxy0 = GRAD3(m_perm[ix1 + m_perm[iy1 + m_perm[iz0]]], fx1, fy1, fz0); nxy1 = GRAD3(m_perm[ix1 + m_perm[iy1 + m_perm[iz1]]], fx1, fy1, fz1); nx1 = LERP( r, nxy0, nxy1 ); n1 = LERP( t, nx0, nx1 ); return 0.936f * LERP( s, n0, n1 ); } public float FractalNoise1D(float x, int octNum, float frq, float amp) { float gain = 1.0f; float sum = 0.0f; for(int i = 0; i < octNum; i++) { sum += Noise1D(x*gain/frq) * amp/gain; gain *= 2.0f; } return sum; } public float FractalNoise2D(float x, float y, int octNum, float frq, float amp) { float gain = 1.0f; float sum = 0.0f; for(int i = 0; i < octNum; i++) { sum += Noise2D(x*gain/frq, y*gain/frq) * amp/gain; gain *= 2.0f; } return sum; } public int Noise2D(Vector3Int v3) { return Mathf.RoundToInt(FractalNoise2D(v3.x,v3.z,octava,frequencia,amplitud)); } public int Noise2D(int x, int z) { return Mathf.RoundToInt(FractalNoise2D(x, z, octava, frequencia, amplitud)); } public float FractalNoise3D(float x, float y, float z, int octNum, float frq, float amp) { float gain = 1.0f; float sum = 0.0f; for(int i = 0; i < octNum; i++) { sum += Noise3D(x*gain/frq, y*gain/frq, z*gain/frq) * amp/gain; gain *= 2.0f; } return sum; } public void LoadPermTableIntoTexture() { m_permTex = new Texture2D(256, 1, TextureFormat.Alpha8, false); m_permTex.filterMode = FilterMode.Point; m_permTex.wrapMode = TextureWrapMode.Clamp; for(int i = 0; i < 256; i++) { float v = (float)m_perm[i] / 255.0f; m_permTex.SetPixel(i, 0, new Color(0,0,0,v)); } m_permTex.Apply(); } } This is my implementation: public void Resimulate() { PerlinNoise per = new PerlinNoise(seed, frec, amp, octa); for (int x = 0; x < TamX; x++) { for (int y = 0; y < TamY; y++) { for (int z = 0; z < TamZ; z++) { if (per.FractalNoise3D(x, y, z, octa, frec, amp)>0) { lista.Add(new Bloque(new Vector3Int(x, y, z))); } } } } } I found information on various pages, blogs and here but found nothing that worked for me. If anyone has information I would greatly appreciate your help. Thank you for helping me.
First, it's important to note that the code you have is not Simplex noise! It's the older "Perlin noise" algorithm that tends to exhibit visually significant directional artifacts. I would suggest avoiding both Perlin noise and Simplex noise altogether. Perlin because of the directional artifacts, and Simplex because you run into patent issues with the 3D implementation. There's an algorithm I've designed recently to get around both of those issues, that's starting to be adopted by a fair number of game developers, called OpenSimplex noise. Code: https://gist.github.com/KdotJPG/b1270127455a94ac5d19 (EDIT: Here's a C# port: https://gist.github.com/omgwtfgames/601497972e4e30fd9c5f) Do something like this: const double S = 24; //Sparsity of noise features public void Resimulate() { //To get fractal noise, ideally you want multiple instances of noise so you don't get odd behavior near (0,0,0) OpenSimplexNoise n1 = new OpenSimplexNoise(seed); OpenSimplexNoise n2 = new OpenSimplexNoise(seed+1); OpenSimplexNoise n3 = new OpenSimplexNoise(seed+2); for (int x = 0; x < TamX; x++) { for (int y = 0; y < Tamy; z++) { for (int z = 0; z < TamZ; z++) { double n = (n1.eval(x / S, y / S, z / S) + n2.eval(x / S / 2, y / S / 2, z / S / 2) * .5 + n3.eval(x / S / 4, y / S / 4, z / S / 4) * .25) / (1 + .5 + .25); double dx = (TamX / 2.0 - x); double dy = (TamY / 2.0 - y); double dz = (TamZ / 2.0 - z); double d = dx*dx + dy*dy + dz*dz; //d is now your squared distance from the center. //n is your fractal noise value //you want to combine d and n to form some threshold that //determines whether or not there is a block. //threshold = d / C1 + n + C2 > 0 where C1 and C2 are constants you choose } } } } EDIT: Here's a visual difference between Perlin and OpenSimplex: Left is noise(x, y, 0) grayscale Next is noise(x, y, 0) > 0 ? white : black Next is |noise(x, y, 0)| > 0.1 ? white : black Next is noise(x, y, 0.5) grayscale
C# polygon to mask?
I have x, y coordinates of all vertices of a polygon, say (1,1) (20,10) (10,30), how do I generate a mask with all pixels inside the polygon being 1 whereas outside being 0? I know there is a function FillPolygon() in C# that looks pretty much doing the job, but it seems to me that it does not return a mask in any way.
Bitmap b = new Bitmap(30, 30); using (Graphics g = Graphics.FromImage(b)) { g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None; g.Clear(Color.Black); g.FillPolygon(Brushes.White, new Point[] { new Point(5,5), new Point(20,20), new Point(2,15)}); } byte[,] mask = new byte[b.Width, b.Height]; for (int y = 0; y < b.Height; y++) for (int x = 0; x < b.Width; x++) { mask[x, y] = b.GetPixel(x, y).R > 0 ? 1 : 0; } This will of course be more performant if you use direct pixel access using LockBits instead of GetPixel.
I know this is rather old question but in case someone comes around and looks for something similar ... If you just want to get the mask, there are much better approaches than referencing System.Drawing and actually drawing onto image in memory ... struct Point { public readonly int X; public readonly int Y; public Point(int x, int y) { this.X = x; this.Y = y; } } bool PointInPolygon(Point[] polygon, int x, int y) { if(polygon == null || polygon.Length < 3) return false; int counter = 0; double intersections; Point p1 = polygon[0]; Point p2; for (int i = 1; i <= polygon.Length; i++) { p2 = polygon[i % polygon.Length]; if ((y > (p1.Y < p2.Y ? p1.Y : p2.Y)) && (y <= (p1.Y > p2.Y ? p1.Y : p2.Y)) && (x <= (p1.X > p2.X ? p1.X : p2.X)) && (p1.Y != p2.Y)) { intersections = (y - p1.Y) * (p2.X - p1.X) / (p2.Y - p1.Y) + p1.X; if (p1.X == p2.X || x <= intersections) counter++; } p1 = p2; } return counter % 2 != 0; } void Main() { Point[] polygon = new Point[] { new Point(1,1), new Point(20,10), new Point(10,30) }; bool[,] mask = new bool[30,30]; for(int i=0;i<30;i++) { for(int j=0;j<30;j++) { mask[i,j] = PointInPolygon(polygon, i, j); Console.Write(mask[i,j]?"*":"."); } Console.WriteLine(); } } Which will output like this: .............................. .............................. .............................. ..***......................... ...*****...................... ...********................... ....**********................ ....**************............ .....****************......... .....*******************...... ......*********************... ......************************ .......*********************.. .......*******************.... ........****************...... ........**************........ .........***********.......... .........*********............ ..........******.............. ..........****................ ..........**.................. .............................. .............................. .............................. .............................. .............................. .............................. .............................. .............................. ..............................
For playng with masking, you can use a Region.
2D Perlin Noise
I have fully mastered the art of Perlin Noise in 3D, and now I'm trying to use my same implementation for a 2D algorithm. The problem seems to be in picking my gradient directions. In 3D I use 16 gradients in evenly distributed directions and this works great. In 2D I figured I'd use 8 gradients. up, down, left, right, and the four diagonal directions. Here is what I get: The general look of the noise is always correct, but the edges of the squares don't quite match up. I have also tried using other gradients or fewer gradients but get similar results. Here in another example you can see that the edges do match up sometimes and the results are fine in that area - When I don't use gradients and instead just interpolate between a value picked randomly at each of the 4 corners I get the right results, which is what makes me think it is the gradient part that is messing it up. Here is my code: //8 different gradient directions private Point[] grads = new Point[] { new Point(0, 1), new Point(1, 1), new Point(1, 0), new Point(1, -1), new Point(0, -1), new Point(-1, -1), new Point(-1, 0), new Point(-1, 1),}; //takes the dot product of a gradient and (x, y) private float dot2D(int i, float x, float y) { return grads[i].X * x + grads[i].Y * y; } public float Noise2D(float x, float y) { int ix = (int)(x), iy = (int)(y); x = x - ix; y = y - iy; float fx = fade(x), fy = fade(y); ix &= 255; iy &= 255; // here is where i get the index to look up in the list of // different gradients. // hashTable is my array of 0-255 in random order int g00 = hashTable[ix + hashTable[iy ]], g10 = hashTable[ix + 1 + hashTable[iy ]], g01 = hashTable[ix + hashTable[iy + 1]], g11 = hashTable[ix + 1 + hashTable[iy + 1]]; // this takes the dot product to find the values to interpolate between float n00 = dot2D(g00 & 7, x, y), n10 = dot2D(g10 & 7, x, y), n01 = dot2D(g01 & 7, x, y), n11 = dot2D(g11 & 7, x, y); // lerp() is just normal linear interpolation float y1 = lerp(fx, n00, n10), y2 = lerp(fx, n01, n11); return lerp(fy, y1, y2); }
I'm in a bit of a rush, but this might be helpful. I adapted Perlin's reference implementation to C#. For 2D, just use the 3D Noise() function with a fixed z parameter. (public static float Noise(float x, float y, float z) towards the end of the class.) using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework; using System.Diagnostics; namespace GoEngine.Content.Entities { public class NoiseMaker { /// adapted from http://cs.nyu.edu/~perlin/noise/ // JAVA REFERENCE IMPLEMENTATION OF IMPROVED NOISE - COPYRIGHT 2002 KEN PERLIN. private static int[] p = new int[512]; private static int[] permutation = { 151,160,137,91,90,15, 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180 }; static NoiseMaker() { CalculateP(); } private static int _octaves; private static int _halfLength = 256; public static void SetOctaves(int octaves) { _octaves = octaves; var len = (int)Math.Pow(2, octaves); permutation = new int[len]; Reseed(); } private static void CalculateP() { p = new int[permutation.Length * 2]; _halfLength = permutation.Length; for (int i = 0; i < permutation.Length; i++) p[permutation.Length + i] = p[i] = permutation[i]; } public static void Reseed() { var random = new Random(); var perm = Enumerable.Range(0, permutation.Length).ToArray(); for (var i = 0; i < perm.Length; i++) { var swapIndex = random.Next(perm.Length); var t = perm[i]; perm[i] = perm[swapIndex]; perm[swapIndex] = t; } permutation = perm; CalculateP(); } public static float Noise(Vector3 position, int octaves, ref float min, ref float max) { return Noise(position.X, position.Y, position.Z, octaves, ref min, ref max); } public static float Noise(float x, float y, float z, int octaves, ref float min, ref float max) { var perlin = 0f; var octave = 1; for (var i = 0; i < octaves; i++) { var noise = Noise(x * octave, y * octave, z * octave); perlin += noise / octave; octave *= 2; } perlin = Math.Abs((float)Math.Pow(perlin,2)); max = Math.Max(perlin, max); min = Math.Min(perlin, min); //perlin = 1f - 2 * perlin; return perlin; } public static float Noise(float x, float y, float z) { int X = (int)Math.Floor(x) % _halfLength; int Y = (int)Math.Floor(y) % _halfLength; int Z = (int)Math.Floor(z) % _halfLength; if (X < 0) X += _halfLength; if (Y < 0) Y += _halfLength; if (Z < 0) Z += _halfLength; x -= (int)Math.Floor(x); y -= (int)Math.Floor(y); z -= (int)Math.Floor(z); var u = Fade(x); var v = Fade(y); var w = Fade(z); int A = p[X] + Y, AA = p[A] + Z, AB = p[A + 1] + Z, // HASH COORDINATES OF B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z; // THE 8 CUBE CORNERS, return MathHelper.Lerp( MathHelper.Lerp( MathHelper.Lerp( Grad(p[AA], x, y, z) // AND ADD , Grad(p[BA], x - 1, y, z) // BLENDED , u ) , MathHelper.Lerp( Grad(p[AB], x, y - 1, z) // RESULTS , Grad(p[BB], x - 1, y - 1, z) , u ) , v ) , MathHelper.Lerp( MathHelper.Lerp( Grad(p[AA + 1], x, y, z - 1) // CORNERS , Grad(p[BA + 1], x - 1, y, z - 1) // OF CUBE , u ) , MathHelper.Lerp( Grad(p[AB + 1], x, y - 1, z - 1) , Grad(p[BB + 1], x - 1, y - 1, z - 1) , u ) , v ) , w ); } static float Fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); } static float Grad(int hash, float x, float y, float z) { int h = hash & 15; // CONVERT LO 4 BITS OF HASH CODE float u = h < 8 ? x : y, // INTO 12 GRADIENT DIRECTIONS. v = h < 4 ? y : h == 12 || h == 14 ? x : z; return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); } } } Update Okay, I managed to create a working 2D version. Here's the class: /// implements improved Perlin noise in 2D. /// Transcribed from http://www.siafoo.net/snippet/144?nolinenos#perlin2003 /// </summary> public static class Noise2d { private static Random _random = new Random(); private static int[] _permutation; private static Vector2[] _gradients; static Noise2d() { CalculatePermutation(out _permutation); CalculateGradients(out _gradients); } private static void CalculatePermutation(out int[] p) { p = Enumerable.Range(0, 256).ToArray(); /// shuffle the array for (var i = 0; i < p.Length; i++) { var source = _random.Next(p.Length); var t = p[i]; p[i] = p[source]; p[source] = t; } } /// <summary> /// generate a new permutation. /// </summary> public static void Reseed() { CalculatePermutation(out _permutation); } private static void CalculateGradients(out Vector2[] grad) { grad = new Vector2[256]; for (var i = 0; i < grad.Length; i++) { Vector2 gradient; do { gradient = new Vector2((float)(_random.NextDouble() * 2 - 1), (float)(_random.NextDouble() * 2 - 1)); } while (gradient.LengthSquared() >= 1); gradient.Normalize(); grad[i] = gradient; } } private static float Drop(float t) { t = Math.Abs(t); return 1f - t * t * t * (t * (t * 6 - 15) + 10); } private static float Q(float u, float v) { return Drop(u) * Drop(v); } public static float Noise(float x, float y) { var cell = new Vector2((float)Math.Floor(x), (float)Math.Floor(y)); var total = 0f; var corners = new[] { new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 0), new Vector2(1, 1) }; foreach (var n in corners) { var ij = cell + n; var uv = new Vector2(x - ij.X, y - ij.Y); var index = _permutation[(int)ij.X % _permutation.Length]; index = _permutation[(index + (int)ij.Y) % _permutation.Length]; var grad = _gradients[index % _gradients.Length]; total += Q(uv.X, uv.Y) * Vector2.Dot(grad, uv); } return Math.Max(Math.Min(total, 1f), -1f); } } Call it like this: private void GenerateNoiseMap(int width, int height, ref Texture2D noiseTexture, int octaves) { var data = new float[width * height]; /// track min and max noise value. Used to normalize the result to the 0 to 1.0 range. var min = float.MaxValue; var max = float.MinValue; /// rebuild the permutation table to get a different noise pattern. /// Leave this out if you want to play with changing the number of octaves while /// maintaining the same overall pattern. Noise2d.Reseed(); var frequency = 0.5f; var amplitude = 1f; var persistence = 0.25f; for (var octave = 0; octave < octaves; octave++) { /// parallel loop - easy and fast. Parallel.For(0 , width * height , (offset) => { var i = offset % width; var j = offset / width; var noise = Noise2d.Noise(i*frequency*1f/width, j*frequency*1f/height); noise = data[j * width + i] += noise * amplitude; min = Math.Min(min, noise); max = Math.Max(max, noise); } ); frequency *= 2; amplitude /= 2; } if (noiseTexture != null && (noiseTexture.Width != width || noiseTexture.Height != height)) { noiseTexture.Dispose(); noiseTexture = null; } if (noiseTexture==null) { noiseTexture = new Texture2D(Device, width, height, false, SurfaceFormat.Color); } var colors = data.Select( (f) => { var norm = (f - min) / (max - min); return new Color(norm, norm, norm, 1); } ).ToArray(); noiseTexture.SetData(colors); } Note that I've used a couple of XNA structures (Vector2 and Texture2D), but it should be pretty clear what they do. If you want higher frequency (more "noisy") content with fewer octaves, increase the initial frequency value that used in the octave loop. This implementation uses "improved" Perlin noise, which should be a bit faster than the standard version. You might also have a look at Simplex noise, which is quite a bit faster at higher dimensions.
I had to change this: n00 = dot2D(g00 & 7, x, y), n10 = dot2D(g10 & 7, x, y), n01 = dot2D(g01 & 7, x, y), n11 = dot2D(g11 & 7, x, y); to this: n00 = dot2D(g00 & 7, x , y ), n10 = dot2D(g10 & 7, x - 1, y ), n01 = dot2D(g01 & 7, x , y - 1), n11 = dot2D(g11 & 7, x - 1, y - 1); Basically just subtracting 1 from the x and y where needed.
If you plug in a zero value for z into your 3D equation and simply follow the math through, removing terms, you'll see that you end up with a simpler equation in the end. Your implementation looks kind of different to the one I'm using though. Here's a comparison of a 3D and 2D function I'm using (in JavaScript): noise3d: function(x, y, z) { // Find unit cube that contains point. var X = Math.floor(x) & 255, Y = Math.floor(y) & 255, Z = Math.floor(z) & 255; // Find relative x,y,z of point in cube. x -= Math.floor(x); y -= Math.floor(y); z -= Math.floor(z); // Compute fade curves for each of x,y,z. var u = fade(x), v = fade(y), w = fade(z); // Hash coordinates of the corners. var A = p[X ] + Y, AA = p[A] + Z, AB = p[A + 1] + Z, B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z; // Add blended results from 8 corners of cube. return scale( lerp( w, lerp( v, lerp( u, grad(p[AA], x, y, z), grad(p[BA], x - 1, y, z) ), lerp( u, grad(p[AB], x, y - 1, z), grad(p[BB], x - 1, y - 1, z) ) ), lerp( v, lerp( u, grad(p[AA + 1], x, y, z - 1), grad(p[BA + 1], x - 1, y, z - 1) ), lerp( u, grad(p[AB + 1], x, y - 1, z - 1), grad(p[BB + 1], x - 1, y - 1, z - 1) ) ) ) ); } The 2D version involves fewer computations. noise2d: function(x, y) { // Find unit square that contains point. var X = Math.floor(x) & 255, Y = Math.floor(y) & 255; // Find relative x,y of point in square. x -= Math.floor(x); y -= Math.floor(y); // Compute fade curves for each of x,y. var u = fade(x), v = fade(y); // Hash coordinates of the corners. var A = p[X ] + Y, AA = p[A], AB = p[A + 1], B = p[X + 1] + Y, BA = p[B], BB = p[B + 1]; // Add blended results from the corners. return scale( lerp( v, lerp( u, grad(p[AA], x, y, 0), grad(p[BA], x - 1, y, 0) ), lerp( u, grad(p[AB], x, y - 1, 0), grad(p[BB], x - 1, y - 1, 0) ) ) ); }
XNA 2D TileEngine Mouse clicking issue
My problem is that clicks only get registers on the lower right corner and in some cases not even there it seems to get worse the longer you stray from the 0.0 and the likes the worse it gets. public void Render(SpriteBatch B, Camera C) { Vector2 firstSquare = new Vector2(C.Position.X / 32, C.Position.Y / 32); int firstX = (int)firstSquare.X; int firstY = (int)firstSquare.Y; Vector2 squareOffset = new Vector2(C.Position.X % 32, C.Position.Y % 32); int offsetX = (int)squareOffset.X; int offsetY = (int)squareOffset.Y; for (int y = 0; y < 16; y++) { for (int x = 0; x < 26; x++) { Tile T = GetTile(x + firstX, y + firstY); if (T == null) { continue; } T.RenderWithCamera(B,new Vector2((x*32)-offsetX,(y*32)-offsetY)); } } public void CheckClick(float mx, float my,Camera C) { Vector2 firstSquare = new Vector2(C.Position.X / 32, C.Position.Y / 32); int x = (int)firstSquare.X; int y = (int)firstSquare.Y; Vector2 squareOffset = new Vector2(C.Position.X % 32, C.Position.Y % 32); int offsetX = (int)squareOffset.X; int offsetY = (int)squareOffset.Y; int vx = (int)mx / 32; int vy = (int)my / 32; float x1 = vx + x; float y1 = vy + y; int maxX, maxY; maxX = C.Width / 32; maxY = C.Height / 32; Console.WriteLine("MAX_X:" + maxX + "MAX_Y:" + maxY); Tile T = GetTile(x1, y1); Rectangle A = new Rectangle((int)mx, (int)my, 1, 1); if (T == null) { Console.WriteLine("No Tile found"); return; } if (T.IsInside(A)) { Console.WriteLine("Not inside?"); Tile S = null; S = new Wall((int)x1, (int)y1, 0); if (S != null) { tiles.Add(S); tiles2[(int)T.pos.X, (int)T.pos.Y] = S; } } Console.WriteLine("Clicked Tile at X:" + T.pos.X + "Y:" + T.pos.Y); } public bool IsInside(Rectangle B) // TILE { Rectangle rectA = new Rectangle((int)Last_pos.X, (int)Last_pos.Y, icon.Width, icon.Height); Console.WriteLine("A:" + rectA.X + "A.y:" + rectA.Y + "B.X:" + B.X + "B.Y:" + B.Y); if(rectA.Intersects(B)) { return true; } else return false; }
Here's how I like to handle clicking a tilemap. int xTile = Math.floor((Mouse.X + CameraBounds.left) / Tile.width); int yTile = Math.floor((Mouse.Y + CameraBounds.top) / Tile.height);
Algorithm for intersection of 2 lines?
I have 2 lines. Both lines containing their 2 points of X and Y. This means they both have length. I see 2 formulas, one using determinants and one using normal algebra. Which would be the most efficient to calculate and what does the formula looks like? I'm having a hard time using matrices in code. This is what I have so far, can it be more efficient? public static Vector3 Intersect(Vector3 line1V1, Vector3 line1V2, Vector3 line2V1, Vector3 line2V2) { //Line1 float A1 = line1V2.Y - line1V1.Y; float B1 = line1V1.X - line1V2.X; float C1 = A1*line1V1.X + B1*line1V1.Y; //Line2 float A2 = line2V2.Y - line2V1.Y; float B2 = line2V1.X - line2V2.X; float C2 = A2 * line2V1.X + B2 * line2V1.Y; float det = A1*B2 - A2*B1; if (det == 0) { return null;//parallel lines } else { float x = (B2*C1 - B1*C2)/det; float y = (A1 * C2 - A2 * C1) / det; return new Vector3(x,y,0); } }
Assuming you have two lines of the form Ax + By = C, you can find it pretty easily: float delta = A1 * B2 - A2 * B1; if (delta == 0) throw new ArgumentException("Lines are parallel"); float x = (B2 * C1 - B1 * C2) / delta; float y = (A1 * C2 - A2 * C1) / delta; Pulled from here
I recently went back on paper to find a solution to this problem using basic algebra. We just need to solve the equations formed by the two lines and if a valid solution exist then there is an intersection. You can check my Github repository for extended implementation handling potential precision issue with double and tests. public struct Line { public double x1 { get; set; } public double y1 { get; set; } public double x2 { get; set; } public double y2 { get; set; } } public struct Point { public double x { get; set; } public double y { get; set; } } public class LineIntersection { // Returns Point of intersection if do intersect otherwise default Point (null) public static Point FindIntersection(Line lineA, Line lineB, double tolerance = 0.001) { double x1 = lineA.x1, y1 = lineA.y1; double x2 = lineA.x2, y2 = lineA.y2; double x3 = lineB.x1, y3 = lineB.y1; double x4 = lineB.x2, y4 = lineB.y2; // equations of the form x=c (two vertical lines) with overlapping if (Math.Abs(x1 - x2) < tolerance && Math.Abs(x3 - x4) < tolerance && Math.Abs(x1 - x3) < tolerance) { throw new Exception("Both lines overlap vertically, ambiguous intersection points."); } //equations of the form y=c (two horizontal lines) with overlapping if (Math.Abs(y1 - y2) < tolerance && Math.Abs(y3 - y4) < tolerance && Math.Abs(y1 - y3) < tolerance) { throw new Exception("Both lines overlap horizontally, ambiguous intersection points."); } //equations of the form x=c (two vertical parallel lines) if (Math.Abs(x1 - x2) < tolerance && Math.Abs(x3 - x4) < tolerance) { //return default (no intersection) return default(Point); } //equations of the form y=c (two horizontal parallel lines) if (Math.Abs(y1 - y2) < tolerance && Math.Abs(y3 - y4) < tolerance) { //return default (no intersection) return default(Point); } //general equation of line is y = mx + c where m is the slope //assume equation of line 1 as y1 = m1x1 + c1 //=> -m1x1 + y1 = c1 ----(1) //assume equation of line 2 as y2 = m2x2 + c2 //=> -m2x2 + y2 = c2 -----(2) //if line 1 and 2 intersect then x1=x2=x & y1=y2=y where (x,y) is the intersection point //so we will get below two equations //-m1x + y = c1 --------(3) //-m2x + y = c2 --------(4) double x, y; //lineA is vertical x1 = x2 //slope will be infinity //so lets derive another solution if (Math.Abs(x1 - x2) < tolerance) { //compute slope of line 2 (m2) and c2 double m2 = (y4 - y3) / (x4 - x3); double c2 = -m2 * x3 + y3; //equation of vertical line is x = c //if line 1 and 2 intersect then x1=c1=x //subsitute x=x1 in (4) => -m2x1 + y = c2 // => y = c2 + m2x1 x = x1; y = c2 + m2 * x1; } //lineB is vertical x3 = x4 //slope will be infinity //so lets derive another solution else if (Math.Abs(x3 - x4) < tolerance) { //compute slope of line 1 (m1) and c2 double m1 = (y2 - y1) / (x2 - x1); double c1 = -m1 * x1 + y1; //equation of vertical line is x = c //if line 1 and 2 intersect then x3=c3=x //subsitute x=x3 in (3) => -m1x3 + y = c1 // => y = c1 + m1x3 x = x3; y = c1 + m1 * x3; } //lineA & lineB are not vertical //(could be horizontal we can handle it with slope = 0) else { //compute slope of line 1 (m1) and c2 double m1 = (y2 - y1) / (x2 - x1); double c1 = -m1 * x1 + y1; //compute slope of line 2 (m2) and c2 double m2 = (y4 - y3) / (x4 - x3); double c2 = -m2 * x3 + y3; //solving equations (3) & (4) => x = (c1-c2)/(m2-m1) //plugging x value in equation (4) => y = c2 + m2 * x x = (c1 - c2) / (m2 - m1); y = c2 + m2 * x; //verify by plugging intersection point (x, y) //in orginal equations (1) & (2) to see if they intersect //otherwise x,y values will not be finite and will fail this check if (!(Math.Abs(-m1 * x + y - c1) < tolerance && Math.Abs(-m2 * x + y - c2) < tolerance)) { //return default (no intersection) return default(Point); } } //x,y can intersect outside the line segment since line is infinitely long //so finally check if x, y is within both the line segments if (IsInsideLine(lineA, x, y) && IsInsideLine(lineB, x, y)) { return new Point { x = x, y = y }; } //return default (no intersection) return default(Point); } // Returns true if given point(x,y) is inside the given line segment private static bool IsInsideLine(Line line, double x, double y) { return (x >= line.x1 && x <= line.x2 || x >= line.x2 && x <= line.x1) && (y >= line.y1 && y <= line.y2 || y >= line.y2 && y <= line.y1); } }
How to find intersection of two lines/segments/ray with rectangle public class LineEquation{ public LineEquation(Point start, Point end){ Start = start; End = end; IsVertical = Math.Abs(End.X - start.X) < 0.00001f; M = (End.Y - Start.Y)/(End.X - Start.X); A = -M; B = 1; C = Start.Y - M*Start.X; } public bool IsVertical { get; private set; } public double M { get; private set; } public Point Start { get; private set; } public Point End { get; private set; } public double A { get; private set; } public double B { get; private set; } public double C { get; private set; } public bool IntersectsWithLine(LineEquation otherLine, out Point intersectionPoint){ intersectionPoint = new Point(0, 0); if (IsVertical && otherLine.IsVertical) return false; if (IsVertical || otherLine.IsVertical){ intersectionPoint = GetIntersectionPointIfOneIsVertical(otherLine, this); return true; } double delta = A*otherLine.B - otherLine.A*B; bool hasIntersection = Math.Abs(delta - 0) > 0.0001f; if (hasIntersection){ double x = (otherLine.B*C - B*otherLine.C)/delta; double y = (A*otherLine.C - otherLine.A*C)/delta; intersectionPoint = new Point(x, y); } return hasIntersection; } private static Point GetIntersectionPointIfOneIsVertical(LineEquation line1, LineEquation line2){ LineEquation verticalLine = line2.IsVertical ? line2 : line1; LineEquation nonVerticalLine = line2.IsVertical ? line1 : line2; double y = (verticalLine.Start.X - nonVerticalLine.Start.X)* (nonVerticalLine.End.Y - nonVerticalLine.Start.Y)/ ((nonVerticalLine.End.X - nonVerticalLine.Start.X)) + nonVerticalLine.Start.Y; double x = line1.IsVertical ? line1.Start.X : line2.Start.X; return new Point(x, y); } public bool IntersectWithSegementOfLine(LineEquation otherLine, out Point intersectionPoint){ bool hasIntersection = IntersectsWithLine(otherLine, out intersectionPoint); if (hasIntersection) return intersectionPoint.IsBetweenTwoPoints(otherLine.Start, otherLine.End); return false; } public bool GetIntersectionLineForRay(Rect rectangle, out LineEquation intersectionLine){ if (Start == End){ intersectionLine = null; return false; } IEnumerable<LineEquation> lines = rectangle.GetLinesForRectangle(); intersectionLine = new LineEquation(new Point(0, 0), new Point(0, 0)); var intersections = new Dictionary<LineEquation, Point>(); foreach (LineEquation equation in lines){ Point point; if (IntersectWithSegementOfLine(equation, out point)) intersections[equation] = point; } if (!intersections.Any()) return false; var intersectionPoints = new SortedDictionary<double, Point>(); foreach (var intersection in intersections){ if (End.IsBetweenTwoPoints(Start, intersection.Value) || intersection.Value.IsBetweenTwoPoints(Start, End)){ double distanceToPoint = Start.DistanceToPoint(intersection.Value); intersectionPoints[distanceToPoint] = intersection.Value; } } if (intersectionPoints.Count == 1){ Point endPoint = intersectionPoints.First().Value; intersectionLine = new LineEquation(Start, endPoint); return true; } if (intersectionPoints.Count == 2){ Point start = intersectionPoints.First().Value; Point end = intersectionPoints.Last().Value; intersectionLine = new LineEquation(start, end); return true; } return false; } public override string ToString(){ return "[" + Start + "], [" + End + "]"; } } full sample is described [here][1]