(61d00a474) v0.9.7.1

This commit is contained in:
Regalis
2020-03-04 13:04:10 +01:00
parent 3c50efa5c9
commit 3c09ebe02f
5086 changed files with 786063 additions and 295871 deletions
@@ -0,0 +1,183 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Text;
namespace Barotrauma
{
//By Adrian Biagioli (Flafla2)
//under a Creative Commons Attribution 4.0 International License.
public static class PerlinNoise
{
public static double OctavePerlin(double x, double y, double z, double frequency, int octaves, double persistence)
{
double total = 0;
double amplitude = 3;
for (int i = 0; i < octaves; i++)
{
total += CalculatePerlin(x * frequency, y * frequency, z * frequency) * amplitude;
amplitude *= persistence;
frequency *= 2;
}
return total;
}
// Hash lookup table as defined by Ken Perlin. This is a randomly
// arranged array of all numbers from 0-255 inclusive.
private static readonly 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
};
private static readonly int[] p; // Doubled permutation to avoid overflow
private static readonly float[] cachedNoise;
private const int CacheResolution = 256;
static PerlinNoise()
{
p = new int[512];
for (int x = 0; x < 512; x++)
{
p[x] = permutation[x % 256];
}
float minValue = float.MaxValue;
float maxValue = float.MinValue;
cachedNoise = new float[CacheResolution * CacheResolution];
for (int x = 0; x < CacheResolution; x++)
{
for (int y = 0; y < CacheResolution; y++)
{
cachedNoise[x + CacheResolution * y] = (float)OctavePerlin(x / (double)CacheResolution, y / (double)CacheResolution, 0.5, 10, 4, 0.5f);
}
}
for (int i = 0; i < CacheResolution * CacheResolution; i++)
{
minValue = Math.Min(cachedNoise[i], minValue);
maxValue = Math.Max(cachedNoise[i], maxValue);
}
//normalize to 0-1 range
for (int i = 0; i < CacheResolution * CacheResolution; i++)
{
cachedNoise[i] = (cachedNoise[i] - minValue) / (maxValue - minValue);
}
}
/// <summary>
/// Sample a pre-generated perlin noise map. Faster than calculating the noise on the fly.
/// </summary>
/// <param name="x">Normalized x position. The noise map starts repeating after x > 1</param>
/// <param name="y">Normalized y position. The noise map starts repeating after y > 1</param>
/// <returns>A noise value between 0.0f and 1.0f</returns>
public static float GetPerlin(float x, float y)
{
x = Math.Abs(x) % 1.0f;
y = Math.Abs(y) % 1.0f;
float xIndex = x < 0.5f ? (x * 2.0f * CacheResolution) : CacheResolution - ((x - 0.5f) * 2.0f * CacheResolution);
xIndex = Math.Min(xIndex, CacheResolution - 1);
float yIndex = y < 0.5f ? (y * 2.0f * CacheResolution) : CacheResolution - ((y - 0.5f) * 2.0f * CacheResolution);
yIndex = Math.Min(yIndex, CacheResolution - 1);
int minX = (int)xIndex, maxX = (int)Math.Ceiling(xIndex);
int minY = (int)yIndex, maxY = (int)Math.Ceiling(yIndex);
return MathHelper.Lerp(
MathHelper.Lerp(cachedNoise[minX + minY * CacheResolution], cachedNoise[maxX + minY * CacheResolution], xIndex % 1.0f),
MathHelper.Lerp(cachedNoise[minX + maxY * CacheResolution], cachedNoise[maxX + maxY * CacheResolution], xIndex % 1.0f),
yIndex % 1.0f);
}
public static double CalculatePerlin(double x, double y, double z)
{
int xi = (int)x & 255; // Calculate the "unit cube" that the point asked will be located in
int yi = (int)y & 255; // The left bound is ( |_x_|,|_y_|,|_z_| ) and the right bound is that
int zi = (int)z & 255; // plus 1. Next we calculate the location (from 0.0 to 1.0) in that cube.
double xf = x - (int)x; // We also fade the location to smooth the result.
double yf = y - (int)y;
double zf = z - (int)z;
double u = Fade(xf);
double v = Fade(yf);
double w = Fade(zf);
int a = p[xi] + yi; // This here is Perlin's hash function. We take our x value (remember,
int aa = p[a] + zi; // between 0 and 255) and get a random value (from our p[] array above) between
int ab = p[a + 1] + zi; // 0 and 255. We then add y to it and plug that into p[], and add z to that.
int b = p[xi + 1] + yi; // Then, we get another random value by adding 1 to that and putting it into p[]
int ba = p[b] + zi; // and add z to it. We do the whole thing over again starting with x+1. Later
int bb = p[b + 1] + zi; // we plug aa, ab, ba, and bb back into p[] along with their +1's to get another set.
// in the end we have 8 values between 0 and 255 - one for each vertex on the unit cube.
// These are all interpolated together using u, v, and w below.
double x1, x2, y1, y2;
x1 = Lerp(Grad(p[aa], xf, yf, zf), // This is where the "magic" happens. We calculate a new set of p[] values and use that to get
Grad(p[ba], xf - 1, yf, zf), // our final gradient values. Then, we interpolate between those gradients with the u value to get
u); // 4 x-values. Next, we interpolate between the 4 x-values with v to get 2 y-values. Finally,
x2 = Lerp(Grad(p[ab], xf, yf - 1, zf), // we interpolate between the y-values to get a z-value.
Grad(p[bb], xf - 1, yf - 1, zf),
u); // When calculating the p[] values, remember that above, p[a+1] expands to p[xi]+yi+1 -- so you are
y1 = Lerp(x1, x2, v); // essentially adding 1 to yi. Likewise, p[ab+1] expands to p[p[xi]+yi+1]+zi+1] -- so you are adding
// to zi. The other 3 parameters are your possible return values (see grad()), which are actually
x1 = Lerp(Grad(p[aa + 1], xf, yf, zf - 1), // the vectors from the edges of the unit cube to the point in the unit cube itself.
Grad(p[ba + 1], xf - 1, yf, zf - 1),
u);
x2 = Lerp(Grad(p[ab + 1], xf, yf - 1, zf - 1),
Grad(p[bb + 1], xf - 1, yf - 1, zf - 1),
u);
y2 = Lerp(x1, x2, v);
return (Lerp(y1, y2, w) + 1) / 2; // For convenience we bound it to 0 - 1 (theoretical min/max before is -1 - 1)
}
public static double Grad(int hash, double x, double y, double z)
{
int h = hash & 15; // Take the hashed value and take the first 4 bits of it (15 == 0b1111)
double u = h < 8 /* 0b1000 */ ? x : y; // If the most signifigant bit (MSB) of the hash is 0 then set u = x. Otherwise y.
double v; // In Ken Perlin's original implementation this was another conditional operator (?:). I
// expanded it for readability.
if (h < 4 /* 0b0100 */) // If the first and second signifigant bits are 0 set v = y
v = y;
else if (h == 12 /* 0b1100 */ || h == 14 /* 0b1110*/)// If the first and second signifigant bits are 1 set v = x
v = x;
else // If the first and second signifigant bits are not equal (0/1, 1/0) set v = z
v = z;
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); // Use the last 2 bits to decide if u and v are positive or negative. Then return their addition.
}
public static double Fade(double t)
{
// Fade function as defined by Ken Perlin. This eases coordinate values
// so that they will "ease" towards integral values. This ends up smoothing
// the final output.
return t * t * t * (t * (t * 6 - 15) + 10); // 6t^5 - 15t^4 + 10t^3
}
public static double Lerp(double a, double b, double x)
{
return a + x * (b - a);
}
}
}
@@ -0,0 +1,991 @@
/*
* Created by SharpDevelop.
* User: Burhan
* Date: 17/06/2014
* Time: 11:30 م
*
* To change this template use Tools | Options | Coding | Edit Standard Headers.
*/
/*
* The author of this software is Steven Fortune. Copyright (c) 1994 by AT&T
* Bell Laboratories.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
/*
* This code was originally written by Stephan Fortune in C code. I, Shane O'Sullivan,
* have since modified it, encapsulating it in a C++ class and, fixing memory leaks and
* adding accessors to the Voronoi Edges.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
/*
* Java Version by Zhenyu Pan
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
/*
* C# Version by Burhan Joukhadar
*
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
namespace Voronoi2
{
/// <summary>
/// Description of Voronoi.
/// </summary>
public class Voronoi
{
// ************* Private members ******************
double borderMinX, borderMaxX, borderMinY, borderMaxY;
int siteidx;
double xmin, xmax, ymin, ymax, deltax, deltay;
int nvertices;
int nedges;
int nsites;
Site[] sites;
Site bottomsite;
int sqrt_nsites;
double minDistanceBetweenSites;
int PQcount;
int PQmin;
int PQhashsize;
Halfedge[] PQhash;
const int LE = 0;
const int RE = 1;
int ELhashsize;
Halfedge[] ELhash;
Halfedge ELleftend, ELrightend;
List<GraphEdge> allEdges;
// ************* Public methods ******************
// ******************************************
// constructor
public Voronoi ( double minDistanceBetweenSites )
{
siteidx = 0;
sites = null;
allEdges = null;
this.minDistanceBetweenSites = minDistanceBetweenSites;
}
/**
*
* @param xValuesIn Array of X values for each site.
* @param yValuesIn Array of Y values for each site. Must be identical length to yValuesIn
* @param minX The minimum X of the bounding box around the voronoi
* @param maxX The maximum X of the bounding box around the voronoi
* @param minY The minimum Y of the bounding box around the voronoi
* @param maxY The maximum Y of the bounding box around the voronoi
* @return
*/
// تستدعى هذه العملية لإنشاء مخطط فورونوي
public List<GraphEdge> generateVoronoi ( double[] xValuesIn, double[] yValuesIn, double minX, double maxX, double minY, double maxY )
{
sort(xValuesIn, yValuesIn, xValuesIn.Length);
// Check bounding box inputs - if mins are bigger than maxes, swap them
double temp = 0;
if ( minX > maxX )
{
temp = minX;
minX = maxX;
maxX = temp;
}
if ( minY > maxY )
{
temp = minY;
minY = maxY;
maxY = temp;
}
borderMinX = minX;
borderMinY = minY;
borderMaxX = maxX;
borderMaxY = maxY;
siteidx = 0;
voronoi_bd ();
return allEdges;
}
/*********************************************************
* Private methods - implementation details
********************************************************/
private void sort ( double[] xValuesIn, double[] yValuesIn, int count )
{
sites = null;
allEdges = new List<GraphEdge>();
nsites = count;
nvertices = 0;
nedges = 0;
double sn = (double)nsites + 4;
sqrt_nsites = (int) Math.Sqrt ( sn );
// Copy the inputs so we don't modify the originals
double[] xValues = new double[count];
double[] yValues = new double[count];
for (int i = 0; i < count; i++)
{
xValues[i] = xValuesIn[i];
yValues[i] = yValuesIn[i];
}
sortNode ( xValues, yValues, count );
}
private void qsort ( Site[] sites )
{
List<Site> listSites = new List<Site>( sites.Length );
for ( int i = 0; i < sites.Length; i++ )
{
listSites.Add ( sites[i] );
}
listSites.Sort ( new SiteSorterYX () );
// Copy back into the array
for (int i=0; i < sites.Length; i++)
{
sites[i] = listSites[i];
}
}
private void sortNode ( double[] xValues, double[] yValues, int numPoints )
{
nsites = numPoints;
sites = new Site[nsites];
xmin = xValues[0];
ymin = yValues[0];
xmax = xValues[0];
ymax = yValues[0];
for ( int i = 0; i < nsites; i++ )
{
sites[i] = new Site();
sites[i].Coord.SetPoint ( xValues[i], yValues[i] );
sites[i].SiteNbr = i;
if ( xValues[i] < xmin )
xmin = xValues[i];
else if ( xValues[i] > xmax )
xmax = xValues[i];
if ( yValues[i] < ymin )
ymin = yValues[i];
else if ( yValues[i] > ymax )
ymax = yValues[i];
}
qsort ( sites );
deltax = xmax - xmin;
deltay = ymax - ymin;
}
private Site nextone ()
{
Site s;
if ( siteidx < nsites )
{
s = sites[siteidx];
siteidx++;
return s;
}
return null;
}
private Edge bisect ( Site s1, Site s2 )
{
double dx, dy, adx, ady;
Edge newedge;
newedge = new Edge();
newedge.reg[0] = s1;
newedge.reg[1] = s2;
newedge.ep [0] = null;
newedge.ep[1] = null;
dx = s2.Coord.X - s1.Coord.X;
dy = s2.Coord.Y - s1.Coord.Y;
adx = dx > 0 ? dx : -dx;
ady = dy > 0 ? dy : -dy;
newedge.c = (double)(s1.Coord.X * dx + s1.Coord.Y * dy + (dx * dx + dy* dy) * 0.5);
if ( adx > ady )
{
newedge.a = 1.0;
newedge.b = dy / dx;
newedge.c /= dx;
}
else
{
newedge.a = dx / dy;
newedge.b = 1.0;
newedge.c /= dy;
}
newedge.edgenbr = nedges;
nedges++;
return newedge;
}
private void makevertex ( Site v )
{
v.SiteNbr = nvertices;
nvertices++;
}
private bool PQinitialize ()
{
PQcount = 0;
PQmin = 0;
PQhashsize = 4 * sqrt_nsites;
PQhash = new Halfedge[ PQhashsize ];
for ( int i = 0; i < PQhashsize; i++ )
{
PQhash [i] = new Halfedge();
}
return true;
}
private int PQbucket ( Halfedge he )
{
int bucket;
bucket = (int) ((he.ystar - ymin) / deltay * PQhashsize);
if ( bucket < 0 )
bucket = 0;
if ( bucket >= PQhashsize )
bucket = PQhashsize - 1;
if ( bucket < PQmin )
PQmin = bucket;
return bucket;
}
// push the HalfEdge into the ordered linked list of vertices
private void PQinsert ( Halfedge he, Site v, double offset )
{
Halfedge last, next;
he.vertex = v;
he.ystar = (double)(v.Coord.Y + offset);
last = PQhash [ PQbucket (he) ];
while
(
(next = last.PQnext) != null
&&
(he.ystar > next.ystar || (he.ystar == next.ystar && v.Coord.X > next.vertex.Coord.X))
)
{
last = next;
}
he.PQnext = last.PQnext;
last.PQnext = he;
PQcount++;
}
// remove the HalfEdge from the list of vertices
private void PQdelete ( Halfedge he )
{
Halfedge last;
if (he.vertex != null)
{
last = PQhash [ PQbucket (he) ];
while ( last.PQnext != he )
{
last = last.PQnext;
}
last.PQnext = he.PQnext;
PQcount--;
he.vertex = null;
}
}
private bool PQempty ()
{
return ( PQcount == 0 );
}
private DoubleVector2 PQ_min ()
{
DoubleVector2 answer = new DoubleVector2 ();
while ( PQhash[PQmin].PQnext == null )
{
PQmin++;
}
answer.X = PQhash[PQmin].PQnext.vertex.Coord.X;
answer.Y = PQhash[PQmin].PQnext.ystar;
return answer;
}
private Halfedge PQextractmin ()
{
Halfedge curr;
curr = PQhash[PQmin].PQnext;
PQhash[PQmin].PQnext = curr.PQnext;
PQcount--;
return curr;
}
private Halfedge HEcreate(Edge e, int pm)
{
Halfedge answer = new Halfedge();
answer.ELedge = e;
answer.ELpm = pm;
answer.PQnext = null;
answer.vertex = null;
return answer;
}
private bool ELinitialize()
{
ELhashsize = 2 * sqrt_nsites;
ELhash = new Halfedge[ELhashsize];
for (int i = 0; i < ELhashsize; i++)
{
ELhash[i] = null;
}
ELleftend = HEcreate ( null, 0 );
ELrightend = HEcreate ( null, 0 );
ELleftend.ELleft = null;
ELleftend.ELright = ELrightend;
ELrightend.ELleft = ELleftend;
ELrightend.ELright = null;
ELhash[0] = ELleftend;
ELhash[ELhashsize - 1] = ELrightend;
return true;
}
private Halfedge ELright( Halfedge he )
{
return he.ELright;
}
private Halfedge ELleft( Halfedge he )
{
return he.ELleft;
}
private Site leftreg( Halfedge he )
{
if (he.ELedge == null)
{
return bottomsite;
}
return (he.ELpm == LE ? he.ELedge.reg[LE] : he.ELedge.reg[RE]);
}
private void ELinsert( Halfedge lb, Halfedge newHe )
{
newHe.ELleft = lb;
newHe.ELright = lb.ELright;
(lb.ELright).ELleft = newHe;
lb.ELright = newHe;
}
/*
* This delete routine can't reclaim node, since pointers from hash table
* may be present.
*/
private void ELdelete( Halfedge he )
{
(he.ELleft).ELright = he.ELright;
(he.ELright).ELleft = he.ELleft;
he.deleted = true;
}
/* Get entry from hash table, pruning any deleted nodes */
private Halfedge ELgethash( int b )
{
Halfedge he;
if (b < 0 || b >= ELhashsize)
return null;
he = ELhash[b];
if (he == null || !he.deleted )
return he;
/* Hash table points to deleted half edge. Patch as necessary. */
ELhash[b] = null;
return null;
}
private Halfedge ELleftbnd( DoubleVector2 p )
{
int bucket;
Halfedge he;
/* Use hash table to get close to desired halfedge */
// use the hash function to find the place in the hash map that this
// HalfEdge should be
bucket = (int) ((p.X - xmin) / deltax * ELhashsize);
// make sure that the bucket position is within the range of the hash
// array
if ( bucket < 0 ) bucket = 0;
if ( bucket >= ELhashsize ) bucket = ELhashsize - 1;
he = ELgethash ( bucket );
// if the HE isn't found, search backwards and forwards in the hash map
// for the first non-null entry
if ( he == null )
{
for ( int i = 1; i < ELhashsize; i++ )
{
if ( (he = ELgethash ( bucket - i ) ) != null )
break;
if ( (he = ELgethash ( bucket + i ) ) != null )
break;
}
}
/* Now search linear list of halfedges for the correct one */
if ( he == ELleftend || ( he != ELrightend && right_of (he, p) ) )
{
// keep going right on the list until either the end is reached, or
// you find the 1st edge which the point isn't to the right of
do
{
he = he.ELright;
}
while ( he != ELrightend && right_of(he, p) );
he = he.ELleft;
}
else
// if the point is to the left of the HalfEdge, then search left for
// the HE just to the left of the point
{
do
{
he = he.ELleft;
}
while ( he != ELleftend && !right_of(he, p) );
}
/* Update hash table and reference counts */
if ( bucket > 0 && bucket < ELhashsize - 1)
{
ELhash[bucket] = he;
}
return he;
}
private void pushGraphEdge( Site leftSite, Site rightSite, Vector2 point1, Vector2 point2 )
{
GraphEdge newEdge = new GraphEdge(point1, point2);
allEdges.Add ( newEdge );
newEdge.Site1 = leftSite;
newEdge.Site2 = rightSite;
}
private void clip_line( Edge e )
{
double pxmin, pxmax, pymin, pymax;
Site s1, s2;
double x1 = e.reg[0].Coord.X;
double y1 = e.reg[0].Coord.Y;
double x2 = e.reg[1].Coord.X;
double y2 = e.reg[1].Coord.Y;
double x = x2- x1;
double y = y2 - y1;
// if the distance between the two points this line was created from is
// less than the square root of 2 عن جد؟, then ignore it
if ( Math.Sqrt ( (x*x) + (y*y) ) < minDistanceBetweenSites )
{
return;
}
pxmin = borderMinX;
pymin = borderMinY;
pxmax = borderMaxX;
pymax = borderMaxY;
if ( e.a == 1.0 && e.b >= 0.0 )
{
s1 = e.ep[1];
s2 = e.ep[0];
}
else
{
s1 = e.ep[0];
s2 = e.ep[1];
}
if ( e.a == 1.0 )
{
y1 = pymin;
if ( s1 != null && s1.Coord.Y > pymin )
y1 = s1.Coord.Y;
if ( y1 > pymax )
y1 = pymax;
x1 = e.c - e.b * y1;
y2 = pymax;
if ( s2 != null && s2.Coord.Y < pymax )
y2 = s2.Coord.Y;
if ( y2 < pymin )
y2 = pymin;
x2 = e.c - e.b * y2;
if ( ( (x1 > pxmax) & (x2 > pxmax) ) | ( (x1 < pxmin) & (x2 < pxmin) ) )
return;
if ( x1 > pxmax )
{
x1 = pxmax;
y1 = ( e.c - x1 ) / e.b;
}
if ( x1 < pxmin )
{
x1 = pxmin;
y1 = ( e.c - x1 ) / e.b;
}
if ( x2 > pxmax )
{
x2 = pxmax;
y2 = ( e.c - x2 ) / e.b;
}
if ( x2 < pxmin )
{
x2 = pxmin;
y2 = ( e.c - x2 ) / e.b;
}
}
else
{
x1 = pxmin;
if ( s1 != null && s1.Coord.X > pxmin )
x1 = s1.Coord.X;
if ( x1 > pxmax )
x1 = pxmax;
y1 = e.c - e.a * x1;
x2 = pxmax;
if ( s2 != null && s2.Coord.X < pxmax )
x2 = s2.Coord.X;
if ( x2 < pxmin )
x2 = pxmin;
y2 = e.c - e.a * x2;
if (((y1 > pymax) & (y2 > pymax)) | ((y1 < pymin) & (y2 < pymin)))
return;
if ( y1 > pymax )
{
y1 = pymax;
x1 = ( e.c - y1 ) / e.a;
}
if ( y1 < pymin )
{
y1 = pymin;
x1 = ( e.c - y1 ) / e.a;
}
if ( y2 > pymax )
{
y2 = pymax;
x2 = ( e.c - y2 ) / e.a;
}
if ( y2 < pymin )
{
y2 = pymin;
x2 = ( e.c - y2 ) / e.a;
}
}
pushGraphEdge(e.reg[0], e.reg[1], new Vector2((float)x1, (float)y1), new Vector2((float)x2, (float)y2));
}
private void endpoint( Edge e, int lr, Site s )
{
e.ep[lr] = s;
if ( e.ep[RE - lr] == null )
return;
clip_line ( e );
}
/* returns true if p is to right of halfedge e */
private bool right_of(Halfedge el, DoubleVector2 p)
{
Edge e;
Site topsite;
bool right_of_site;
bool above, fast;
double dxp, dyp, dxs, t1, t2, t3, yl;
e = el.ELedge;
topsite = e.reg[1];
if ( p.X > topsite.Coord.X )
right_of_site = true;
else
right_of_site = false;
if ( right_of_site && el.ELpm == LE )
return true;
if (!right_of_site && el.ELpm == RE )
return false;
if ( e.a == 1.0 )
{
dxp = p.X - topsite.Coord.X;
dyp = p.Y - topsite.Coord.Y;
fast = false;
if ( (!right_of_site & (e.b < 0.0)) | (right_of_site & (e.b >= 0.0)) )
{
above = dyp >= e.b * dxp;
fast = above;
}
else
{
above = p.X + p.Y * e.b > e.c;
if ( e.b < 0.0 )
above = !above;
if ( !above )
fast = true;
}
if ( !fast )
{
dxs = topsite.Coord.X - ( e.reg[0] ).Coord.X;
above = e.b * (dxp * dxp - dyp * dyp)
< dxs * dyp * (1.0 + 2.0 * dxp / dxs + e.b * e.b);
if ( e.b < 0 )
above = !above;
}
}
else // e.b == 1.0
{
yl = e.c - e.a * p.X;
t1 = p.Y - yl;
t2 = p.X - topsite.Coord.X;
t3 = yl - topsite.Coord.Y;
above = t1 * t1 > t2 * t2 + t3 * t3;
}
return ( el.ELpm == LE ? above : !above );
}
private Site rightreg(Halfedge he)
{
if (he.ELedge == (Edge) null)
// if this halfedge has no edge, return the bottom site (whatever
// that is)
{
return (bottomsite);
}
// if the ELpm field is zero, return the site 0 that this edge bisects,
// otherwise return site number 1
return (he.ELpm == LE ? he.ELedge.reg[RE] : he.ELedge.reg[LE]);
}
private double dist( Site s, Site t )
{
double dx, dy;
dx = s.Coord.X - t.Coord.X;
dy = s.Coord.Y - t.Coord.Y;
return Math.Sqrt ( dx * dx + dy * dy );
}
// create a new site where the HalfEdges el1 and el2 intersect - note that
// the Point in the argument list is not used, don't know why it's there
private Site intersect( Halfedge el1, Halfedge el2 )
{
Edge e1, e2, e;
Halfedge el;
double d, xint, yint;
bool right_of_site;
Site v; // vertex
e1 = el1.ELedge;
e2 = el2.ELedge;
if ( e1 == null || e2 == null )
return null;
// if the two edges bisect the same parent, return null
if ( e1.reg[1] == e2.reg[1] )
return null;
d = e1.a * e2.b - e1.b * e2.a;
if ( -1.0e-10 < d && d < 1.0e-10 )
return null;
xint = ( e1.c * e2.b - e2.c * e1.b ) / d;
yint = ( e2.c * e1.a - e1.c * e2.a ) / d;
if ( (e1.reg[1].Coord.Y < e2.reg[1].Coord.Y)
|| (e1.reg[1].Coord.Y == e2.reg[1].Coord.Y && e1.reg[1].Coord.X < e2.reg[1].Coord.X) )
{
el = el1;
e = e1;
}
else
{
el = el2;
e = e2;
}
right_of_site = xint >= e.reg[1].Coord.X;
if ((right_of_site && el.ELpm == LE)
|| (!right_of_site && el.ELpm == RE))
return null;
// create a new site at the point of intersection - this is a new vector
// event waiting to happen
v = new Site();
v.Coord.X = xint;
v.Coord.Y = yint;
return v;
}
/*
* implicit parameters: nsites, sqrt_nsites, xmin, xmax, ymin, ymax, deltax,
* deltay (can all be estimates). Performance suffers if they are wrong;
* better to make nsites, deltax, and deltay too big than too small. (?)
*/
private bool voronoi_bd()
{
Site newsite, bot, top, temp, p;
Site v;
DoubleVector2 newintstar = null;
int pm;
Halfedge lbnd, rbnd, llbnd, rrbnd, bisector;
Edge e;
PQinitialize();
ELinitialize();
bottomsite = nextone();
newsite = nextone();
while (true)
{
if (!PQempty())
{
newintstar = PQ_min();
}
// if the lowest site has a smaller y value than the lowest vector
// intersection,
// process the site otherwise process the vector intersection
if (newsite != null && (PQempty()
|| newsite.Coord.Y < newintstar.Y
|| (newsite.Coord.Y == newintstar.Y
&& newsite.Coord.X < newintstar.X)))
{
/* new site is smallest -this is a site event */
// get the first HalfEdge to the LEFT of the new site
lbnd = ELleftbnd((newsite.Coord));
// get the first HalfEdge to the RIGHT of the new site
rbnd = ELright(lbnd);
// if this halfedge has no edge,bot =bottom site (whatever that
// is)
bot = rightreg(lbnd);
// create a new edge that bisects
e = bisect(bot, newsite);
// create a new HalfEdge, setting its ELpm field to 0
bisector = HEcreate(e, LE);
// insert this new bisector edge between the left and right
// vectors in a linked list
ELinsert(lbnd, bisector);
// if the new bisector intersects with the left edge,
// remove the left edge's vertex, and put in the new one
if ((p = intersect(lbnd, bisector)) != null)
{
PQdelete(lbnd);
PQinsert(lbnd, p, dist(p, newsite));
}
lbnd = bisector;
// create a new HalfEdge, setting its ELpm field to 1
bisector = HEcreate(e, RE);
// insert the new HE to the right of the original bisector
// earlier in the IF stmt
ELinsert(lbnd, bisector);
// if this new bisector intersects with the new HalfEdge
if ((p = intersect(bisector, rbnd)) != null)
{
// push the HE into the ordered linked list of vertices
PQinsert(bisector, p, dist(p, newsite));
}
newsite = nextone();
} else if (!PQempty())
/* intersection is smallest - this is a vector event */
{
// pop the HalfEdge with the lowest vector off the ordered list
// of vectors
lbnd = PQextractmin();
// get the HalfEdge to the left of the above HE
llbnd = ELleft(lbnd);
// get the HalfEdge to the right of the above HE
rbnd = ELright(lbnd);
// get the HalfEdge to the right of the HE to the right of the
// lowest HE
rrbnd = ELright(rbnd);
// get the Site to the left of the left HE which it bisects
bot = leftreg(lbnd);
// get the Site to the right of the right HE which it bisects
top = rightreg(rbnd);
v = lbnd.vertex; // get the vertex that caused this event
makevertex(v); // set the vertex number - couldn't do this
// earlier since we didn't know when it would be processed
endpoint(lbnd.ELedge, lbnd.ELpm, v);
// set the endpoint of
// the left HalfEdge to be this vector
endpoint(rbnd.ELedge, rbnd.ELpm, v);
// set the endpoint of the right HalfEdge to
// be this vector
ELdelete(lbnd); // mark the lowest HE for
// deletion - can't delete yet because there might be pointers
// to it in Hash Map
PQdelete(rbnd);
// remove all vertex events to do with the right HE
ELdelete(rbnd); // mark the right HE for
// deletion - can't delete yet because there might be pointers
// to it in Hash Map
pm = LE; // set the pm variable to zero
if (bot.Coord.Y > top.Coord.Y)
// if the site to the left of the event is higher than the
// Site
{ // to the right of it, then swap them and set the 'pm'
// variable to 1
temp = bot;
bot = top;
top = temp;
pm = RE;
}
e = bisect(bot, top); // create an Edge (or line)
// that is between the two Sites. This creates the formula of
// the line, and assigns a line number to it
bisector = HEcreate(e, pm); // create a HE from the Edge 'e',
// and make it point to that edge
// with its ELedge field
ELinsert(llbnd, bisector); // insert the new bisector to the
// right of the left HE
endpoint(e, RE - pm, v); // set one endpoint to the new edge
// to be the vector point 'v'.
// If the site to the left of this bisector is higher than the
// right Site, then this endpoint
// is put in position 0; otherwise in pos 1
// if left HE and the new bisector intersect, then delete
// the left HE, and reinsert it
if ((p = intersect(llbnd, bisector)) != null)
{
PQdelete(llbnd);
PQinsert(llbnd, p, dist(p, bot));
}
// if right HE and the new bisector intersect, then
// reinsert it
if ((p = intersect(bisector, rrbnd)) != null)
{
PQinsert(bisector, p, dist(p, bot));
}
} else
{
break;
}
}
for (lbnd = ELright(ELleftend); lbnd != ELrightend; lbnd = ELright(lbnd))
{
e = lbnd.ELedge;
clip_line(e);
}
return true;
}
public List<GraphEdge> MakeVoronoiGraph(List<Vector2> sites, int width, int height)
{
double[] xVal = new double[sites.Count];
double[] yVal = new double[sites.Count];
for (int i = 0; i < sites.Count; i++)
{
xVal[i] = sites[i].X;
yVal[i] = sites[i].Y;
}
return generateVoronoi(xVal, yVal, 0, width, 0, height);
}
public List<GraphEdge> MakeVoronoiGraph(double[] xVal, double[] yVal, int width, int height)
{
return generateVoronoi(xVal, yVal, 0, width, 0, height);
}
} // Voronoi Class End
} // namespace Voronoi2 End
@@ -0,0 +1,277 @@
/*
* Created by SharpDevelop.
* User: Burhan
* Date: 17/06/2014
* Time: 09:29 م
*
* To change this template use Tools | Options | Coding | Edit Standard Headers.
*/
/*
Copyright 2011 James Humphreys. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY James Humphreys ``AS IS\" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those of the
authors and should not be interpreted as representing official policies, either expressed
or implied, of James Humphreys.
*/
/*
* C# Version by Burhan Joukhadar
*
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
using Barotrauma;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
namespace Voronoi2
{
public class DoubleVector2
{
public double X, Y;
public DoubleVector2()
{
}
public DoubleVector2(double x, double y)
{
this.X = x;
this.Y = y;
}
public void SetPoint(double x, double y)
{
this.X = x;
this.Y = y;
}
public void Normalize()
{
double length = System.Math.Sqrt(X * X + Y * Y);
X /= length;
Y /= length;
}
}
// use for sites and vertecies
public class Site
{
public DoubleVector2 Coord;
public int SiteNbr;
public void SetPoint(Vector2 point)
{
Coord.SetPoint(point.X, point.Y);
}
public Site ()
{
Coord = new DoubleVector2();
}
}
public class Edge
{
public double a = 0, b = 0, c = 0;
public Site[] ep;
public Site[] reg;
public int edgenbr;
public Edge ()
{
ep = new Site[2];
reg = new Site[2];
}
}
public class Halfedge
{
public Halfedge ELleft, ELright;
public Edge ELedge;
public bool deleted;
public int ELpm;
public Site vertex;
public double ystar;
public Halfedge PQnext;
public Halfedge ()
{
PQnext = null;
}
}
public enum CellType
{
Solid, Empty, Edge, Path, Removed
}
public class VoronoiCell
{
public List<GraphEdge> Edges;
public Site Site;
public List<Vector2> BodyVertices;
public Body Body;
public CellType CellType;
public Vector2 Translation;
public Vector2 Center
{
get { return new Vector2((float)Site.Coord.X, (float)Site.Coord.Y) + Translation; }
}
public VoronoiCell(Vector2[] vertices)
{
Edges = new List<GraphEdge>();
BodyVertices = new List<Vector2>();
Vector2 midPoint = Vector2.Zero;
foreach (Vector2 vertex in vertices)
{
midPoint += vertex;
}
midPoint /= vertices.Length;
for (int i = 1; i < vertices.Length; i++ )
{
GraphEdge ge = new GraphEdge(vertices[i-1], vertices[i]);
System.Diagnostics.Debug.Assert(ge.Point1 != ge.Point2);
Edges.Add(ge);
}
GraphEdge lastEdge = new GraphEdge(vertices[0], vertices[vertices.Length-1]);
Edges.Add(lastEdge);
Site = new Site();
Site.SetPoint(midPoint);
}
public VoronoiCell(Site site)
{
Edges = new List<GraphEdge>();
BodyVertices = new List<Vector2>();
//bodies = new List<Body>();
this.Site = site;
}
public bool IsPointInside(Vector2 point)
{
foreach (GraphEdge edge in Edges)
{
if (MathUtils.LinesIntersect(point, Center, edge.Point1 + Translation, edge.Point2 + Translation)) return false;
}
return true;
}
}
public class GraphEdge
{
public Vector2 Point1, Point2;
public Site Site1, Site2;
public VoronoiCell Cell1, Cell2;
public bool IsSolid;
public bool OutsideLevel;
public Vector2 Center
{
get { return (Point1 + Point2) / 2.0f; }
}
public GraphEdge(Vector2 point1, Vector2 point2)
{
this.Point1 = point1;
this.Point2 = point2;
}
public VoronoiCell AdjacentCell(VoronoiCell cell)
{
if (Cell1 == cell)
{
return Cell2;
}
else if (Cell2 == cell)
{
return Cell1;
}
return null;
}
/// <summary>
/// Returns the normal of the edge that points outwards from the specified cell
/// </summary>
public Vector2 GetNormal(VoronoiCell cell)
{
Vector2 dir = Vector2.Normalize(Point1 - Point2);
Vector2 normal = new Vector2(dir.Y, -dir.X);
if (cell != null && Vector2.Dot(normal, Vector2.Normalize(Center - cell.Center)) < 0)
{
normal = -normal;
}
return normal;
}
public override string ToString()
{
return "GraphEdge (" + Point1.ToString() + ", " + Point2.ToString() + ")";
}
}
// للترتيب
public class SiteSorterYX : IComparer<Site>
{
public int Compare ( Site p1, Site p2 )
{
DoubleVector2 s1 = p1.Coord;
DoubleVector2 s2 = p2.Coord;
if ( s1.Y < s2.Y ) return -1;
if ( s1.Y > s2.Y ) return 1;
if ( s1.X < s2.X ) return -1;
if ( s1.X > s2.X ) return 1;
return 0;
}
}
}