Search the source of every open source package.
460 results
using System.Runtime.CompilerServices;
public static class OpenSimplex2S
{
private const long PRIME_X = 0x5205402B9270C86FL;
private const long PRIME_Y = 0x598CD327003817B5L;
private const long PRIME_Z = 0x5BCC226E9FA0BACBL;
private const long PRIME_W = 0x56CC5227E58F554BL;
private const long HASH_MULTIPLIER = 0x53A3F72DEEC546F5L;
private const long SEED_FLIP_3D = -0x52D547B2E96ED629L;
private const double ROOT2OVER2 = 0.7071067811865476;
private const double SKEW_2D = 0.366025403784439;
private const double UNSKEW_2D = -0.21132486540518713;
private const double ROOT3OVER3 = 0.577350269189626;
private const double FALLBACK_ROTATE3 = 2.0 / 3.0;
private const double ROTATE3_ORTHOGONALIZER = UNSKEW_2D;
private const float SKEW_4D = 0.309016994374947f;
private const float UNSKEW_4D = -0.138196601125011f;
private const int N_GRADS_2D_EXPONENT = 7;
private const int N_GRADS_3D_EXPONENT = 8;
private const int N_GRADS_4D_EXPONENT = 9;
private const int N_GRADS_2D = 1 << N_GRADS_2D_EXPONENT;
private const int N_GRADS_3D = 1 << N_GRADS_3D_EXPONENT;
private const int N_GRADS_4D = 1 << N_GRADS_4D_EXPONENT;
private const double NORMALIZER_2D = 0.05481866495625118;
private const double NORMALIZER_3D = 0.2781926117527186;
private const double NORMALIZER_4D = 0.11127401889945551;
private const float RSQUARED_2D = 2.0f / 3.0f;
private const float RSQUARED_3D = 3.0f / 4.0f;
private const float RSQUARED_4D = 4.0f / 5.0f;
/*
* Noise Evaluators
*/
/**
* 2D OpenSimplex2S/SuperSimplex noise, standard lattice orientation.
*/
public static float Noise2( long seed, double x, double y )
{
// Get points for A2* lattice
double s = SKEW_2D * (x + y);
double xs = x + s, ys = y + s;
return Noise2_UnskewedBase( seed, xs, ys );
}
/**
* 2D OpenSimplex2S/SuperSimplex noise, with Y pointing down the main diagonal.
* Might be better for a 2D sandbox style game, where Y is vertical.
* Probably slightly less optimal for heightmaps or continent maps,
* unless your map is centered around an equator. It's a slight
* difference, but the option is here to make it easy.
*/
public static float Noise2_ImproveX( long seed, double x, double y )
{
// Skew transform and rotation baked into one.
double xx = x * ROOT2OVER2;
double yy = y * (ROOT2OVER2 * (1 + 2 * SKEW_2D));
return Noise2_UnskewedBase( seed, yy + xx, yy - xx );
}
/**
* 2D OpenSimplex2S/SuperSimplex noise base.
*/
private static float Noise2_UnskewedBase( long seed, double xs, double ys )
{
// Get base points and offsets.
int xsb = FastFloor( xs ), ysb = FastFloor( ys );
float xi = (float)(xs - xsb), yi = (float)(ys - ysb);
// Prime pre-multiplication for hash.
long xsbp = xsb * PRIME_X, ysbp = ysb * PRIME_Y;
// Unskew.
float t = (xi + yi) * (float)UNSKEW_2D;
float dx0 = xi + t, dy0 = yi + t;
// First vertex.
float a0 = RSQUARED_2D - dx0 * dx0 - dy0 * dy0;
float value = (a0 * a0) * (a0 * a0) * Grad( seed, xsbp, ysbp, dx0, dy0 );
// Second vertex.
float a1 = (float)(2 * (1 + 2 * UNSKEW_2D) * (1 / UNSKEW_2D + 2)) * t + ((float)(-2 * (1 + 2 * UNSKEW_2D) * (1 + 2 * UNSKEW_2D)) + a0);
float dx1 = dx0 - (float)(1 + 2 * UNSKEW_2D);
float dy1 = dy0 - (float)(1 + 2 * UNSKEW_2D);
value += (a1 * a1) * (a1 * a1) * Grad( seed, xsbp + PRIME_X, ysbp + PRIME_Y, dx1, dy1 );
// Third and fourth vertices.
// Nested conditionals were faster than compact bit logic/arithmetic.
float xmyi = xi - yi;
if ( t < UNSKEW_2D )
{
if ( xi + xmyi > 1 )
{
float dx2 = dx0 - (float)(3 * UNSKEW_2D + 2);
float dy2 = dy0 - (float)(3 * UNSKEW_2D + 1);
float a2 = RSQUARED_2D - dx2 * dx2 - dy2 * dy2;
if ( a2 > 0 )
{
value += (a2 * a2) * (a2 * a2) * Grad( seed, xsbp + (PRIME_X << 1), ysbp + PRIME_Y, dx2, dy2 );
}
}
else
{
float dx2 = dx0 - (float)UNSKEW_2D;
float dy2 = dy0 - (float)(UNSKEW_2D + 1);
float a2 = RSQUARED_2D - dx2 * dx2 - dy2 * dy2;
if ( a2 > 0 )
{
value += (a2 * a2) * (a2 * a2) * Grad( seed, xsbp, ysbp + PRIME_Y, dx2, dy2 );
}
}
if ( yi - xmyi > 1 )
{
float dx3 = dx0 - (float)(3 * UNSKEW_2D + 1);
float dy3 = dy0 - (float)(3 * UNSKEW_2D + 2);
float a3 = RSQUARED_2D - dx3 * dx3 - dy3 * dy3;
if ( a3 > 0 )
{
value += (a3 * a3) * (a3 * a3) * Grad( seed, xsbp + PRIME_X, ysbp + (PRIME_Y << 1), dx3, dy3 );
}
}
else
{
float dx3 = dx0 - (float)(UNSKEW_2D + 1);
float dy3 = dy0 - (float)UNSKEW_2D;
float a3 = RSQUARED_2D - dx3 * dx3 - dy3 * dy3;
if ( a3 > 0 )
{
value += (a3 * a3) * (a3 * a3) * Grad( seed, xsbp + PRIME_X, ysbp, dx3, dy3 );
}
}
}
else
{
if ( xi + xmyi < 0 )
{
float dx2 = dx0 + (float)(1 + UNSKEW_2D);
float dy2 = dy0 + (float)UNSKEW_2D;
float a2 = RSQUARED_2D - dx2 * dx2 - dy2 * dy2;
if ( a2 > 0 )
{
value += (a2 * a2) * (a2 * a2) * Grad( seed, xsbp - PRIME_X, ysbp, dx2, dy2 );
}
}
else
{
float dx2 = dx0 - (float)(UNSKEW_2D + 1);
float dy2 = dy0 - (float)UNSKEW_2D;
float a2 = RSQUARED_2D - dx2 * dx2 - dy2 * dy2;
if ( a2 > 0 )
{
value += (a2 * a2) * (a2 * a2) * Grad( seed, xsbp + PRIME_X, ysbp, dx2, dy2 );
}
}
if ( yi < xmyi )
{
float dx2 = dx0 + (float)UNSKEW_2D;
float dy2 = dy0 + (float)(UNSKEW_2D + 1);
float a2 = RSQUARED_2D - dx2 * dx2 - dy2 * dy2;
if ( a2 > 0 )
{
value += (a2 * a2) * (a2 * a2) * Grad( seed, xsbp, ysbp - PRIME_Y, dx2, dy2 );
}
}
else
{
float dx2 = dx0 - (float)UNSKEW_2D;
float dy2 = dy0 - (float)(UNSKEW_2D + 1);
float a2 = RSQUARED_2D - dx2 * dx2 - dy2 * dy2;
if ( a2 > 0 )
{
value += (a2 * a2) * (a2 * a2) * Grad( seed, xsbp, ysbp + PRIME_Y, dx2, dy2 );
}
}
}
return value;
}
/**
* 3D OpenSimplex2S/SuperSimplex noise, with better visual isotropy in (X, Y).
* Recommended for 3D terrain and time-varied animations.
* The Z coordinate should always be the "different" coordinate in whatever your use case is.
* If Y is vertical in world coordinates, call Noise3_ImproveXZ(x, z, Y) or use Noise3_XZBeforeY.
* If Z is vertical in world coordinates, call Noise3_ImproveXZ(x, y, Z).
* For a time varied animation, call Noise3_ImproveXY(x, y, T).
*/
public static float Noise3_ImproveXY( long seed, double x, double y, double z )
{
// Re-orient the cubic lattices without skewing, so Z points up the main lattice diagonal,
// and the planes formed by XY are moved far out of alignment with the cube faces.
// Orthonormal rotation. Not a skew transform.
double xy = x + y;
double s2 = xy * ROTATE3_ORTHOGONALIZER;
double zz = z * ROOT3OVER3;
double xr = x + s2 + zz;
double yr = y + s2 + zz;
double zr = xy * -ROOT3OVER3 + zz;
// Evaluate both lattices to form a BCC lattice.
return Noise3_UnrotatedBase( seed, xr, yr, zr );
}
/**
* 3D OpenSimplex2S/SuperSimplex noise, with better visual isotropy in (X, Z).
* Recommended for 3D terrain and time-varied animations.
* The Y coordinate should always be the "different" coordinate in whatever your use case is.
* If Y is vertical in world coordinates, call Noise3_ImproveXZ(x, Y, z).
* If Z is vertical in world coordinates, call Noise3_ImproveXZ(x, Z, y) or use Noise3_ImproveXY.
* For a time varied animation, call Noise3_ImproveXZ(x, T, y) or use Noise3_ImproveXY.
*/
public static float Noise3_ImproveXZ( long seed, double x, double y, double z )
{
// Re-orient the cubic lattices without skewing, so Y points up the main lattice diagonal,
// and the planes formed by XZ are moved far out of alignment with the cube faces.
// Orthonormal rotation. Not a skew transform.
double xz = x + z;
double s2 = xz * -0.211324865405187;
double yy = y * ROOT3OVER3;
double xr = x + s2 + yy;
double zr = z + s2 + yy;
double yr = xz * -ROOT3OVER3 + yy;
// Evaluate both lattices to form a BCC lattice.
return Noise3_UnrotatedBase( seed, xr, yr, zr );
}
/**
* 3D OpenSimplex2S/SuperSimplex noise, fallback rotation option
* Use Noise3_ImproveXY or Noise3_ImproveXZ instead, wherever appropriate.
* They have less diagonal bias. This function's best use is as a fallback.
*/
public static float Noise3_Fallback( long seed, double x, double y, double z )
{
// Re-orient the cubic lattices via rotation, to produce a familiar look.
// Orthonormal rotation. Not a skew transform.
double r = FALLBACK_ROTATE3 * (x + y + z);
double xr = r - x, yr = r - y, zr = r - z;
// Evaluate both lattices to form a BCC lattice.
return Noise3_UnrotatedBase( seed, xr, yr, zr );
}
/**
* Generate overlapping cubic lattices for 3D Re-oriented BCC noise.
* Lookup table implementation inspired by DigitalShadow.
* It was actually faster to narrow down the points in the loop itself,
* than to build up the index with enough info to isolate 8 points.
*/
private static float Noise3_UnrotatedBase( long seed, double xr, double yr, double zr )
{
// Get base points and offsets.
int xrb = FastFloor( xr ), yrb = FastFloor( yr ), zrb = FastFloor( zr );
float xi = (float)(xr - xrb), yi = (float)(yr - yrb), zi = (float)(zr - zrb);
// Prime pre-multiplication for hash. Also flip seed for second lattice copy.
long xrbp = xrb * PRIME_X, yrbp = yrb * PRIME_Y, zrbp = zrb * PRIME_Z;
long seed2 = seed ^ -0x52D547B2E96ED629L;
// -1 if positive, 0 if negative.
int xNMask = (int)(-0.5f - xi), yNMask = (int)(-0.5f - yi), zNMask = (int)(-0.5f - zi);
// First vertex.
float x0 = xi + xNMask;
float y0 = yi + yNMask;
float z0 = zi + zNMask;
float a0 = RSQUARED_3D - x0 * x0 - y0 * y0 - z0 * z0;
float value = (a0 * a0) * (a0 * a0) * Grad( seed,
xrbp + (xNMask & PRIME_X), yrbp + (yNMask & PRIME_Y), zrbp + (zNMask & PRIME_Z), x0, y0, z0 );
// Second vertex.
float x1 = xi - 0.5f;
float y1 = yi - 0.5f;
float z1 = zi - 0.5f;
float a1 = RSQUARED_3D - x1 * x1 - y1 * y1 - z1 * z1;
value += (a1 * a1) * (a1 * a1) * Grad( seed2,
xrbp + PRIME_X, yrbp + PRIME_Y, zrbp + PRIME_Z, x1, y1, z1 );
// Shortcuts for building the remaining falloffs.
// Derived by subtracting the polynomials with the offsets plugged in.
float xAFlipMask0 = ((xNMask | 1) << 1) * x1;
float yAFlipMask0 = ((yNMask | 1) << 1) * y1;
float zAFlipMask0 = ((zNMask | 1) << 1) * z1;
float xAFlipMask1 = (-2 - (xNMask << 2)) * x1 - 1.0f;
float yAFlipMask1 = (-2 - (yNMask << 2)) * y1 - 1.0f;
float zAFlipMask1 = (-2 - (zNMask << 2)) * z1 - 1.0f;
bool skip5 = false;
float a2 = xAFlipMask0 + a0;
if ( a2 > 0 )
{
float x2 = x0 - (xNMask | 1);
float y2 = y0;
float z2 = z0;
value += (a2 * a2) * (a2 * a2) * Grad( seed,
xrbp + (~xNMask & PRIME_X), yrbp + (yNMask & PRIME_Y), zrbp + (zNMask & PRIME_Z), x2, y2, z2 );
}
else
{
float a3 = yAFlipMask0 + zAFlipMask0 + a0;
if ( a3 > 0 )
{
float x3 = x0;
float y3 = y0 - (yNMask | 1);
float z3 = z0 - (zNMask | 1);
value += (a3 * a3) * (a3 * a3) * Grad( seed,
xrbp + (xNMask & PRIME_X), yrbp + (~yNMask & PRIME_Y), zrbp + (~zNMask & PRIME_Z), x3, y3, z3 );
}
float a4 = xAFlipMask1 + a1;
if ( a4 > 0 )
{
float x4 = (xNMask | 1) + x1;
float y4 = y1;
float z4 = z1;
value += (a4 * a4) * (a4 * a4) * Grad( seed2,
xrbp + (xNMask & unchecked(PRIME_X * 2)), yrbp + PRIME_Y, zrbp + PRIME_Z, x4, y4, z4 );
skip5 = true;
}
}
bool skip9 = false;
float a6 = yAFlipMask0 + a0;
if ( a6 > 0 )
{
float x6 = x0;
float y6 = y0 - (yNMask | 1);
float z6 = z0;
value += (a6 * a6) * (a6 * a6) * Grad( seed,
xrbp + (xNMask & PRIME_X), yrbp + (~yNMask & PRIME_Y), zrbp + (zNMask & PRIME_Z), x6, y6, z6 );
}
else
{
float a7 = xAFlipMask0 + zAFlipMask0 + a0;
if ( a7 > 0 )
{
float x7 = x0 - (xNMask | 1);
float y7 = y0;
float z7 = z0 - (zNMask | 1);
value += (a7 * a7) * (a7 * a7) * Grad( seed,
xrbp + (~xNMask & PRIME_X), yrbp + (yNMask & PRIME_Y), zrbp + (~zNMask & PRIME_Z), x7, y7, z7 );
}
float a8 = yAFlipMask1 + a1;
if ( a8 > 0 )
{
float x8 = x1;
float y8 = (yNMask | 1) + y1;
float z8 = z1;
value += (a8 * a8) * (a8 * a8) * Grad( seed2,
xrbp + PRIME_X, yrbp + (yNMask & (PRIME_Y << 1)), zrbp + PRIME_Z, x8, y8, z8 );
skip9 = true;
}
}
bool skipD = false;
float aA = zAFlipMask0 + a0;
if ( aA > 0 )
{
float xA = x0;
float yA = y0;
float zA = z0 - (zNMask | 1);
value += (aA * aA) * (aA * aA) * Grad( seed,
xrbp + (xNMask & PRIME_X), yrbp + (yNMask & PRIME_Y), zrbp + (~zNMask & PRIME_Z), xA, yA, zA );
}
else
{
float aB = xAFlipMask0 + yAFlipMask0 + a0;
if ( aB > 0 )
{
float xB = x0 - (xNMask | 1);
float yB = y0 - (yNMask | 1);
float zB = z0;
value += (aB * aB) * (aB * aB) * Grad( seed,
xrbp + (~xNMask & PRIME_X), yrbp + (~yNMask & PRIME_Y), zrbp + (zNMask & PRIME_Z), xB, yB, zB );
}
float aC = zAFlipMask1 + a1;
if ( aC > 0 )
{
float xC = x1;
float yC = y1;
float zC = (zNMask | 1) + z1;
value += (aC * aC) * (aC * aC) * Grad( seed2,
xrbp + PRIME_X, yrbp + PRIME_Y, zrbp + (zNMask & (PRIME_Z << 1)), xC, yC, zC );
skipD = true;
}
}
if ( !skip5 )
{
float a5 = yAFlipMask1 + zAFlipMask1 + a1;
if ( a5 > 0 )
{
float x5 = x1;
float y5 = (yNMask | 1) + y1;
float z5 = (zNMask | 1) + z1;
value += (a5 * a5) * (a5 * a5) * Grad( seed2,
xrbp + PRIME_X, yrbp + (yNMask & (PRIME_Y << 1)), zrbp + (zNMask & (PRIME_Z << 1)), x5, y5, z5 );
}
}
if ( !skip9 )
{
float a9 = xAFlipMask1 + zAFlipMask1 + a1;
if ( a9 > 0 )
{
float x9 = (xNMask | 1) + x1;
float y9 = y1;
float z9 = (zNMask | 1) + z1;
value += (a9 * a9) * (a9 * a9) * Grad( seed2,
xrbp + (xNMask & unchecked(PRIME_X * 2)), yrbp + PRIME_Y, zrbp + (zNMask & (PRIME_Z << 1)), x9, y9, z9 );
}
}
if ( !skipD )
{
float aD = xAFlipMask1 + yAFlipMask1 + a1;
if ( aD > 0 )
{
float xD = (xNMask | 1) + x1;
float yD = (yNMask | 1) + y1;
float zD = z1;
value += (aD * aD) * (aD * aD) * Grad( seed2,
xrbp + (xNMask & (PRIME_X << 1)), yrbp + (yNMask & (PRIME_Y << 1)), zrbp + PRIME_Z, xD, yD, zD );
}
}
return value;
}
/**
* 4D SuperSimplex noise, with XYZ oriented like Noise3_ImproveXY
* and W for an extra degree of freedom. W repeats eventually.
* Recommended for time-varied animations which texture a 3D object (W=time)
* in a space where Z is vertical
*/
public static float Noise4_ImproveXYZ_ImproveXY( long seed, double x, double y, double z, double w )
{
double xy = x + y;
double s2 = xy * -0.21132486540518699998;
double zz = z * 0.28867513459481294226;
double ww = w * 1.118033988749894;
double xr = x + (zz + ww + s2), yr = y + (zz + ww + s2);
double zr = xy * -0.57735026918962599998 + (zz + ww);
double wr = z * -0.866025403784439 + ww;
return Noise4_UnskewedBase( seed, xr, yr, zr, wr );
}
/**
* 4D SuperSimplex noise, with XYZ oriented like Noise3_ImproveXZ
* and W for an extra degree of freedom. W repeats eventually.
* Recommended for time-varied animations which texture a 3D object (W=time)
* in a space where Y is vertical
*/
public static float Noise4_ImproveXYZ_ImproveXZ( long seed, double x, double y, double z, double w )
{
double xz = x + z;
double s2 = xz * -0.21132486540518699998;
double yy = y * 0.28867513459481294226;
double ww = w * 1.118033988749894;
double xr = x + (yy + ww + s2), zr = z + (yy + ww + s2);
double yr = xz * -0.57735026918962599998 + (yy + ww);
double wr = y * -0.866025403784439 + ww;
return Noise4_UnskewedBase( seed, xr, yr, zr, wr );
}
/**
* 4D SuperSimplex noise, with XYZ oriented like Noise3_Fallback
* and W for an extra degree of freedom. W repeats eventually.
* Recommended for time-varied animations which texture a 3D object (W=time)
* where there isn't a clear distinction between horizontal and vertical
*/
public static float Noise4_ImproveXYZ( long seed, double x, double y, double z, double w )
{
double xyz = x + y + z;
double ww = w * 1.118033988749894;
double s2 = xyz * -0.16666666666666666 + ww;
double xs = x + s2, ys = y + s2, zs = z + s2, ws = -0.5 * xyz + ww;
return Noise4_UnskewedBase( seed, xs, ys, zs, ws );
}
/**
* 4D SuperSimplex noise, fallback lattice orientation.
*/
public static float Noise4_Fallback( long seed, double x, double y, double z, double w )
{
// Get points for A4 lattice
double s = SKEW_4D * (x + y + z + w);
double xs = x + s, ys = y + s, zs = z + s, ws = w + s;
return Noise4_UnskewedBase( seed, xs, ys, zs, ws );
}
/**
* 4D SuperSimplex noise base.
* Using ultra-simple 4x4x4x4 lookup partitioning.
* This isn't as elegant or SIMD/GPU/etc. portable as other approaches,
* but it competes performance-wise with optimized 2014 OpenSimplex.
*/
private static float Noise4_UnskewedBase( long seed, double xs, double ys, double zs, double ws )
{
// Get base points and offsets
int xsb = FastFloor( xs ), ysb = FastFloor( ys ), zsb = FastFloor( zs ), wsb = FastFloor( ws );
float xsi = (float)(xs - xsb), ysi = (float)(ys - ysb), zsi = (float)(zs - zsb), wsi = (float)(ws - wsb);
// Unskewed offsets
float ssi = (xsi + ysi + zsi + wsi) * UNSKEW_4D;
float xi = xsi + ssi, yi = ysi + ssi, zi = zsi + ssi, wi = wsi + ssi;
// Prime pre-multiplication for hash.
long xsvp = xsb * PRIME_X, ysvp = ysb * PRIME_Y, zsvp = zsb * PRIME_Z, wsvp = wsb * PRIME_W;
// Index into initial table.
int index = ((FastFloor( xs * 4 ) & 3) << 0)
| ((FastFloor( ys * 4 ) & 3) << 2)
| ((FastFloor( zs * 4 ) & 3) << 4)
| ((FastFloor( ws * 4 ) & 3) << 6);
// Point contributions
float value = 0;
(int secondaryIndexStart, int secondaryIndexStop) = LOOKUP_4D_A[index];
for ( int i = secondaryIndexStart; i < secondaryIndexStop; i++ )
{
LatticeVertex4D c = LOOKUP_4D_B[i];
float dx = xi + c.dx, dy = yi + c.dy, dz = zi + c.dz, dw = wi + c.dw;
float a = (dx * dx + dy * dy) + (dz * dz + dw * dw);
if ( a < RSQUARED_4D )
{
a -= RSQUARED_4D;
a *= a;
value += a * a * Grad( seed, xsvp + c.xsvp, ysvp + c.ysvp, zsvp + c.zsvp, wsvp + c.wsvp, dx, dy, dz, dw );
}
}
return value;
}
/*
* Utility
*/
[MethodImpl( MethodImplOptions.AggressiveInlining )]
private static float Grad( long seed, long xsvp, long ysvp, float dx, float dy )
{
long hash = seed ^ xsvp ^ ysvp;
hash *= HASH_MULTIPLIER;
hash ^= hash >> (64 - N_GRADS_2D_EXPONENT + 1);
int gi = (int)hash & ((N_GRADS_2D - 1) << 1);
return GRADIENTS_2D[gi | 0] * dx + GRADIENTS_2D[gi | 1] * dy;
}
[MethodImpl( MethodImplOptions.AggressiveInlining )]
private static float Grad( long seed, long xrvp, long yrvp, long zrvp, float dx, float dy, float dz )
{
long hash = (seed ^ xrvp) ^ (yrvp ^ zrvp);
hash *= HASH_MULTIPLIER;
hash ^= hash >> (64 - N_GRADS_3D_EXPONENT + 2);
int gi = (int)hash & ((N_GRADS_3D - 1) << 2);
return GRADIENTS_3D[gi | 0] * dx + GRADIENTS_3D[gi | 1] * dy + GRADIENTS_3D[gi | 2] * dz;
}
[MethodImpl( MethodImplOptions.AggressiveInlining )]
private static float Grad( long seed, long xsvp, long ysvp, long zsvp, long wsvp, float dx, float dy, float dz, float dw )
{
long hash = seed ^ (xsvp ^ ysvp) ^ (zsvp ^ wsvp);
hash *= HASH_MULTIPLIER;
hash ^= hash >> (64 - N_GRADS_4D_EXPONENT + 2);
int gi = (int)hash & ((N_GRADS_4D - 1) << 2);
return (GRADIENTS_4D[gi | 0] * dx + GRADIENTS_4D[gi | 1] * dy) + (GRADIENTS_4D[gi | 2] * dz + GRADIENTS_4D[gi | 3] * dw);
}
[MethodImpl( MethodImplOptions.AggressiveInlining )]
private static int FastFloor( double x )
{
int xi = (int)x;
return x < xi ? xi - 1 : xi;
}
/*
* Lookup Tables & Gradients
*/
private static readonly float[] GRADIENTS_2D;
private static readonly float[] GRADIENTS_3D;
private static readonly float[] GRADIENTS_4D;
private static readonly (short SecondaryIndexStart, short SecondaryIndexStop)[] LOOKUP_4D_A;
private static readonly LatticeVertex4D[] LOOKUP_4D_B;
static OpenSimplex2S()
{
GRADIENTS_2D = new float[N_GRADS_2D * 2];
float[] grad2 = {
0.38268343236509f, 0.923879532511287f,
0.923879532511287f, 0.38268343236509f,
0.923879532511287f, -0.38268343236509f,
0.38268343236509f, -0.923879532511287f,
-0.38268343236509f, -0.923879532511287f,
-0.923879532511287f, -0.38268343236509f,
-0.923879532511287f, 0.38268343236509f,
-0.38268343236509f, 0.923879532511287f,
//-------------------------------------//
0.130526192220052f, 0.99144486137381f,
0.608761429008721f, 0.793353340291235f,
0.793353340291235f, 0.608761429008721f,
0.99144486137381f, 0.130526192220051f,
0.99144486137381f, -0.130526192220051f,
0.793353340291235f, -0.60876142900872f,
0.608761429008721f, -0.793353340291235f,
0.130526192220052f, -0.99144486137381f,
-0.130526192220052f, -0.99144486137381f,
-0.608761429008721f, -0.793353340291235f,
-0.793353340291235f, -0.608761429008721f,
-0.99144486137381f, -0.130526192220052f,
-0.99144486137381f, 0.130526192220051f,
-0.793353340291235f, 0.608761429008721f,
-0.608761429008721f, 0.793353340291235f,
-0.130526192220052f, 0.99144486137381f,
};
for ( int i = 0; i < grad2.Length; i++ )
{
grad2[i] = (float)(grad2[i] / NORMALIZER_2D);
}
for ( int i = 0, j = 0; i < GRADIENTS_2D.Length; i++, j++ )
{
if ( j == grad2.Length ) j = 0;
GRADIENTS_2D[i] = grad2[j];
}
GRADIENTS_3D = new float[N_GRADS_3D * 4];
float[] grad3 = {
2.22474487139f, 2.22474487139f, -1.0f, 0.0f,
2.22474487139f, 2.22474487139f, 1.0f, 0.0f,
3.0862664687972017f, 1.1721513422464978f, 0.0f, 0.0f,
1.1721513422464978f, 3.0862664687972017f, 0.0f, 0.0f,
-2.22474487139f, 2.22474487139f, -1.0f, 0.0f,
-2.22474487139f, 2.22474487139f, 1.0f, 0.0f,
-1.1721513422464978f, 3.0862664687972017f, 0.0f, 0.0f,
-3.0862664687972017f, 1.1721513422464978f, 0.0f, 0.0f,
-1.0f, -2.22474487139f, -2.22474487139f, 0.0f,
1.0f, -2.22474487139f, -2.22474487139f, 0.0f,
0.0f, -3.0862664687972017f, -1.1721513422464978f, 0.0f,
0.0f, -1.1721513422464978f, -3.0862664687972017f, 0.0f,
-1.0f, -2.22474487139f, 2.22474487139f, 0.0f,
1.0f, -2.22474487139f, 2.22474487139f, 0.0f,
0.0f, -1.1721513422464978f, 3.0862664687972017f, 0.0f,
0.0f, -3.0862664687972017f, 1.1721513422464978f, 0.0f,
//--------------------------------------------------------------------//
-2.22474487139f, -2.22474487139f, -1.0f, 0.0f,
-2.22474487139f, -2.22474487139f, 1.0f, 0.0f,
-3.0862664687972017f, -1.1721513422464978f, 0.0f, 0.0f,
-1.1721513422464978f, -3.0862664687972017f, 0.0f, 0.0f,
-2.22474487139f, -1.0f, -2.22474487139f, 0.0f,
-2.22474487139f, 1.0f, -2.22474487139f, 0.0f,
-1.1721513422464978f, 0.0f, -3.0862664687972017f, 0.0f,
-3.0862664687972017f, 0.0f, -1.1721513422464978f, 0.0f,
-2.22474487139f, -1.0f, 2.22474487139f, 0.0f,
-2.22474487139f, 1.0f, 2.22474487139f, 0.0f,
-3.0862664687972017f, 0.0f, 1.1721513422464978f, 0.0f,
-1.1721513422464978f, 0.0f, 3.0862664687972017f, 0.0f,
-1.0f, 2.22474487139f, -2.22474487139f, 0.0f,
1.0f, 2.22474487139f, -2.22474487139f, 0.0f,
0.0f, 1.1721513422464978f, -3.0862664687972017f, 0.0f,
0.0f, 3.0862664687972017f, -1.1721513422464978f, 0.0f,
-1.0f, 2.22474487139f, 2.22474487139f, 0.0f,
1.0f, 2.22474487139f, 2.22474487139f, 0.0f,
0.0f, 3.0862664687972017f, 1.1721513422464978f, 0.0f,
0.0f, 1.1721513422464978f, 3.0862664687972017f, 0.0f,
2.22474487139f, -2.22474487139f, -1.0f, 0.0f,
2.22474487139f, -2.22474487139f, 1.0f, 0.0f,
1.1721513422464978f, -3.0862664687972017f, 0.0f, 0.0f,
3.0862664687972017f, -1.1721513422464978f, 0.0f, 0.0f,
2.22474487139f, -1.0f, -2.22474487139f, 0.0f,
2.22474487139f, 1.0f, -2.22474487139f, 0.0f,
3.0862664687972017f, 0.0f, -1.1721513422464978f, 0.0f,
1.1721513422464978f, 0.0f, -3.0862664687972017f, 0.0f,
2.22474487139f, -1.0f, 2.22474487139f, 0.0f,
2.22474487139f, 1.0f, 2.22474487139f, 0.0f,
1.1721513422464978f, 0.0f, 3.0862664687972017f, 0.0f,
3.0862664687972017f, 0.0f, 1.1721513422464978f, 0.0f,
};
for ( int i = 0; i < grad3.Length; i++ )
{
grad3[i] = (float)(grad3[i] / NORMALIZER_3D);
}
for ( int i = 0, j = 0; i < GRADIENTS_3D.Length; i++, j++ )
{
if ( j == grad3.Length ) j = 0;
GRADIENTS_3D[i] = grad3[j];
}
GRADIENTS_4D = new float[N_GRADS_4D * 4];
float[] grad4 = {
-0.6740059517812944f, -0.3239847771997537f, -0.3239847771997537f, 0.5794684678643381f,
-0.7504883828755602f, -0.4004672082940195f, 0.15296486218853164f, 0.5029860367700724f,
-0.7504883828755602f, 0.15296486218853164f, -0.4004672082940195f, 0.5029860367700724f,
-0.8828161875373585f, 0.08164729285680945f, 0.08164729285680945f, 0.4553054119602712f,
-0.4553054119602712f, -0.08164729285680945f, -0.08164729285680945f, 0.8828161875373585f,
-0.5029860367700724f, -0.15296486218853164f, 0.4004672082940195f, 0.7504883828755602f,
-0.5029860367700724f, 0.4004672082940195f, -0.15296486218853164f, 0.7504883828755602f,
-0.5794684678643381f, 0.3239847771997537f, 0.3239847771997537f, 0.6740059517812944f,
-0.6740059517812944f, -0.3239847771997537f, 0.5794684678643381f, -0.3239847771997537f,
-0.7504883828755602f, -0.4004672082940195f, 0.5029860367700724f, 0.15296486218853164f,
-0.7504883828755602f, 0.15296486218853164f, 0.5029860367700724f, -0.4004672082940195f,
-0.8828161875373585f, 0.08164729285680945f, 0.4553054119602712f, 0.08164729285680945f,
-0.4553054119602712f, -0.08164729285680945f, 0.8828161875373585f, -0.08164729285680945f,
-0.5029860367700724f, -0.15296486218853164f, 0.7504883828755602f, 0.4004672082940195f,
-0.5029860367700724f, 0.4004672082940195f, 0.7504883828755602f, -0.15296486218853164f,
-0.5794684678643381f, 0.3239847771997537f, 0.6740059517812944f, 0.3239847771997537f,
-0.6740059517812944f, 0.5794684678643381f, -0.3239847771997537f, -0.3239847771997537f,
-0.7504883828755602f, 0.5029860367700724f, -0.4004672082940195f, 0.15296486218853164f,
-0.7504883828755602f, 0.5029860367700724f, 0.15296486218853164f, -0.4004672082940195f,
-0.8828161875373585f, 0.4553054119602712f, 0.08164729285680945f, 0.08164729285680945f,
-0.4553054119602712f, 0.8828161875373585f, -0.08164729285680945f, -0.08164729285680945f,
-0.5029860367700724f, 0.7504883828755602f, -0.15296486218853164f, 0.4004672082940195f,
-0.5029860367700724f, 0.7504883828755602f, 0.4004672082940195f, -0.15296486218853164f,
-0.5794684678643381f, 0.6740059517812944f, 0.3239847771997537f, 0.3239847771997537f,
0.5794684678643381f, -0.6740059517812944f, -0.3239847771997537f, -0.3239847771997537f,
0.5029860367700724f, -0.7504883828755602f, -0.4004672082940195f, 0.15296486218853164f,
0.5029860367700724f, -0.7504883828755602f, 0.15296486218853164f, -0.4004672082940195f,
0.4553054119602712f, -0.8828161875373585f, 0.08164729285680945f, 0.08164729285680945f,
0.8828161875373585f, -0.4553054119602712f, -0.08164729285680945f, -0.08164729285680945f,
0.7504883828755602f, -0.5029860367700724f, -0.15296486218853164f, 0.4004672082940195f,
0.7504883828755602f, -0.5029860367700724f, 0.4004672082940195f, -0.15296486218853164f,
0.6740059517812944f, -0.5794684678643381f, 0.3239847771997537f, 0.3239847771997537f,
//------------------------------------------------------------------------------------------//
-0.753341017856078f, -0.37968289875261624f, -0.37968289875261624f, -0.37968289875261624f,
-0.7821684431180708f, -0.4321472685365301f, -0.4321472685365301f, 0.12128480194602098f,
-0.7821684431180708f, -0.4321472685365301f, 0.12128480194602098f, -0.4321472685365301f,
-0.7821684431180708f, 0.12128480194602098f, -0.4321472685365301f, -0.4321472685365301f,
-0.8586508742123365f, -0.508629699630796f, 0.044802370851755174f, 0.044802370851755174f,
-0.8586508742123365f, 0.044802370851755174f, -0.508629699630796f, 0.044802370851755174f,
-0.8586508742123365f, 0.044802370851755174f, 0.044802370851755174f, -0.508629699630796f,
-0.9982828964265062f, -0.03381941603233842f, -0.03381941603233842f, -0.03381941603233842f,
-0.37968289875261624f, -0.753341017856078f, -0.37968289875261624f, -0.37968289875261624f,
-0.4321472685365301f, -0.7821684431180708f, -0.4321472685365301f, 0.12128480194602098f,
-0.4321472685365301f, -0.7821684431180708f, 0.12128480194602098f, -0.4321472685365301f,
0.12128480194602098f, -0.7821684431180708f, -0.4321472685365301f, -0.4321472685365301f,
-0.508629699630796f, -0.8586508742123365f, 0.044802370851755174f, 0.044802370851755174f,
0.044802370851755174f, -0.8586508742123365f, -0.508629699630796f, 0.044802370851755174f,
0.044802370851755174f, -0.8586508742123365f, 0.044802370851755174f, -0.508629699630796f,
-0.03381941603233842f, -0.9982828964265062f, -0.03381941603233842f, -0.03381941603233842f,
-0.37968289875261624f, -0.37968289875261624f, -0.753341017856078f, -0.37968289875261624f,
-0.4321472685365301f, -0.4321472685365301f, -0.7821684431180708f, 0.12128480194602098f,
-0.4321472685365301f, 0.12128480194602098f, -0.7821684431180708f, -0.4321472685365301f,
0.12128480194602098f, -0.4321472685365301f, -0.7821684431180708f, -0.4321472685365301f,
-0.508629699630796f, 0.044802370851755174f, -0.8586508742123365f, 0.044802370851755174f,
0.044802370851755174f, -0.508629699630796f, -0.8586508742123365f, 0.044802370851755174f,
0.044802370851755174f, 0.044802370851755174f, -0.8586508742123365f, -0.508629699630796f,
-0.03381941603233842f, -0.03381941603233842f, -0.9982828964265062f, -0.03381941603233842f,
-0.37968289875261624f, -0.37968289875261624f, -0.37968289875261624f, -0.753341017856078f,
-0.4321472685365301f, -0.4321472685365301f, 0.12128480194602098f, -0.7821684431180708f,
-0.4321472685365301f, 0.12128480194602098f, -0.4321472685365301f, -0.7821684431180708f,
0.12128480194602098f, -0.4321472685365301f, -0.4321472685365301f, -0.7821684431180708f,
-0.508629699630796f, 0.044802370851755174f, 0.044802370851755174f, -0.8586508742123365f,
0.044802370851755174f, -0.508629699630796f, 0.044802370851755174f, -0.8586508742123365f,
0.044802370851755174f, 0.044802370851755174f, -0.508629699630796f, -0.8586508742123365f,
-0.03381941603233842f, -0.03381941603233842f, -0.03381941603233842f, -0.9982828964265062f,
-0.3239847771997537f, -0.6740059517812944f, -0.3239847771997537f, 0.5794684678643381f,
-0.4004672082940195f, -0.7504883828755602f, 0.15296486218853164f, 0.5029860367700724f,
0.15296486218853164f, -0.7504883828755602f, -0.4004672082940195f, 0.5029860367700724f,
0.08164729285680945f, -0.8828161875373585f, 0.08164729285680945f, 0.4553054119602712f,
-0.08164729285680945f, -0.4553054119602712f, -0.08164729285680945f, 0.8828161875373585f,
-0.15296486218853164f, -0.5029860367700724f, 0.4004672082940195f, 0.7504883828755602f,
0.4004672082940195f, -0.5029860367700724f, -0.15296486218853164f, 0.7504883828755602f,
0.3239847771997537f, -0.5794684678643381f, 0.3239847771997537f, 0.6740059517812944f,
-0.3239847771997537f, -0.3239847771997537f, -0.6740059517812944f, 0.5794684678643381f,
-0.4004672082940195f, 0.15296486218853164f, -0.7504883828755602f, 0.5029860367700724f,
0.15296486218853164f, -0.4004672082940195f, -0.7504883828755602f, 0.5029860367700724f,
0.08164729285680945f, 0.08164729285680945f, -0.8828161875373585f, 0.4553054119602712f,
-0.08164729285680945f, -0.08164729285680945f, -0.4553054119602712f, 0.8828161875373585f,
-0.15296486218853164f, 0.4004672082940195f, -0.5029860367700724f, 0.7504883828755602f,
0.4004672082940195f, -0.15296486218853164f, -0.5029860367700724f, 0.7504883828755602f,
0.3239847771997537f, 0.3239847771997537f, -0.5794684678643381f, 0.6740059517812944f,
-0.3239847771997537f, -0.6740059517812944f, 0.5794684678643381f, -0.3239847771997537f,
-0.4004672082940195f, -0.7504883828755602f, 0.5029860367700724f, 0.15296486218853164f,
0.15296486218853164f, -0.7504883828755602f, 0.5029860367700724f, -0.4004672082940195f,
0.08164729285680945f, -0.8828161875373585f, 0.4553054119602712f, 0.08164729285680945f,
-0.08164729285680945f, -0.4553054119602712f, 0.8828161875373585f, -0.08164729285680945f,
-0.15296486218853164f, -0.5029860367700724f, 0.7504883828755602f, 0.4004672082940195f,
0.4004672082940195f, -0.5029860367700724f, 0.7504883828755602f, -0.15296486218853164f,
0.3239847771997537f, -0.5794684678643381f, 0.6740059517812944f, 0.3239847771997537f,
-0.3239847771997537f, -0.3239847771997537f, 0.5794684678643381f, -0.6740059517812944f,
-0.4004672082940195f, 0.15296486218853164f, 0.5029860367700724f, -0.7504883828755602f,
0.15296486218853164f, -0.4004672082940195f, 0.5029860367700724f, -0.7504883828755602f,
0.08164729285680945f, 0.08164729285680945f, 0.4553054119602712f, -0.8828161875373585f,
-0.08164729285680945f, -0.08164729285680945f, 0.8828161875373585f, -0.4553054119602712f,
-0.15296486218853164f, 0.4004672082940195f, 0.7504883828755602f, -0.5029860367700724f,
0.4004672082940195f, -0.15296486218853164f, 0.7504883828755602f, -0.5029860367700724f,
0.3239847771997537f, 0.3239847771997537f, 0.6740059517812944f, -0.5794684678643381f,
-0.3239847771997537f, 0.5794684678643381f, -0.6740059517812944f, -0.3239847771997537f,
-0.4004672082940195f, 0.5029860367700724f, -0.7504883828755602f, 0.15296486218853164f,
0.15296486218853164f, 0.5029860367700724f, -0.7504883828755602f, -0.4004672082940195f,
0.08164729285680945f, 0.4553054119602712f, -0.8828161875373585f, 0.08164729285680945f,
-0.08164729285680945f, 0.8828161875373585f, -0.4553054119602712f, -0.08164729285680945f,
-0.15296486218853164f, 0.7504883828755602f, -0.5029860367700724f, 0.4004672082940195f,
0.4004672082940195f, 0.7504883828755602f, -0.5029860367700724f, -0.15296486218853164f,
0.3239847771997537f, 0.6740059517812944f, -0.5794684678643381f, 0.3239847771997537f,
-0.3239847771997537f, 0.5794684678643381f, -0.3239847771997537f, -0.6740059517812944f,
-0.4004672082940195f, 0.5029860367700724f, 0.15296486218853164f, -0.7504883828755602f,
0.15296486218853164f, 0.5029860367700724f, -0.4004672082940195f, -0.7504883828755602f,
0.08164729285680945f, 0.4553054119602712f, 0.08164729285680945f, -0.8828161875373585f,
-0.08164729285680945f, 0.8828161875373585f, -0.08164729285680945f, -0.4553054119602712f,
-0.15296486218853164f, 0.7504883828755602f, 0.4004672082940195f, -0.5029860367700724f,
0.4004672082940195f, 0.7504883828755602f, -0.15296486218853164f, -0.5029860367700724f,
0.3239847771997537f, 0.6740059517812944f, 0.3239847771997537f, -0.5794684678643381f,
0.5794684678643381f, -0.3239847771997537f, -0.6740059517812944f, -0.3239847771997537f,
0.5029860367700724f, -0.4004672082940195f, -0.7504883828755602f, 0.15296486218853164f,
0.5029860367700724f, 0.15296486218853164f, -0.7504883828755602f, -0.4004672082940195f,
0.4553054119602712f, 0.08164729285680945f, -0.8828161875373585f, 0.08164729285680945f,
0.8828161875373585f, -0.08164729285680945f, -0.4553054119602712f, -0.08164729285680945f,
0.7504883828755602f, -0.15296486218853164f, -0.5029860367700724f, 0.4004672082940195f,
0.7504883828755602f, 0.4004672082940195f, -0.5029860367700724f, -0.15296486218853164f,
0.6740059517812944f, 0.3239847771997537f, -0.5794684678643381f, 0.3239847771997537f,
0.5794684678643381f, -0.3239847771997537f, -0.3239847771997537f, -0.6740059517812944f,
0.5029860367700724f, -0.4004672082940195f, 0.15296486218853164f, -0.7504883828755602f,
0.5029860367700724f, 0.15296486218853164f, -0.4004672082940195f, -0.7504883828755602f,
0.4553054119602712f, 0.08164729285680945f, 0.08164729285680945f, -0.8828161875373585f,
0.8828161875373585f, -0.08164729285680945f, -0.08164729285680945f, -0.4553054119602712f,
0.7504883828755602f, -0.15296486218853164f, 0.4004672082940195f, -0.5029860367700724f,
0.7504883828755602f, 0.4004672082940195f, -0.15296486218853164f, -0.5029860367700724f,
0.6740059517812944f, 0.3239847771997537f, 0.3239847771997537f, -0.5794684678643381f,
0.03381941603233842f, 0.03381941603233842f, 0.03381941603233842f, 0.9982828964265062f,
-0.044802370851755174f, -0.044802370851755174f, 0.508629699630796f, 0.8586508742123365f,
-0.044802370851755174f, 0.508629699630796f, -0.044802370851755174f, 0.8586508742123365f,
-0.12128480194602098f, 0.4321472685365301f, 0.4321472685365301f, 0.7821684431180708f,
0.508629699630796f, -0.044802370851755174f, -0.044802370851755174f, 0.8586508742123365f,
0.4321472685365301f, -0.12128480194602098f, 0.4321472685365301f, 0.7821684431180708f,
0.4321472685365301f, 0.4321472685365301f, -0.12128480194602098f, 0.7821684431180708f,
0.37968289875261624f, 0.37968289875261624f, 0.37968289875261624f, 0.753341017856078f,
0.03381941603233842f, 0.03381941603233842f, 0.9982828964265062f, 0.03381941603233842f,
-0.044802370851755174f, 0.044802370851755174f, 0.8586508742123365f, 0.508629699630796f,
-0.044802370851755174f, 0.508629699630796f, 0.8586508742123365f, -0.044802370851755174f,
-0.12128480194602098f, 0.4321472685365301f, 0.7821684431180708f, 0.4321472685365301f,
0.508629699630796f, -0.044802370851755174f, 0.8586508742123365f, -0.044802370851755174f,
0.4321472685365301f, -0.12128480194602098f, 0.7821684431180708f, 0.4321472685365301f,
0.4321472685365301f, 0.4321472685365301f, 0.7821684431180708f, -0.12128480194602098f,
0.37968289875261624f, 0.37968289875261624f, 0.753341017856078f, 0.37968289875261624f,
0.03381941603233842f, 0.9982828964265062f, 0.03381941603233842f, 0.03381941603233842f,
-0.044802370851755174f, 0.8586508742123365f, -0.044802370851755174f, 0.508629699630796f,
-0.044802370851755174f, 0.8586508742123365f, 0.508629699630796f, -0.044802370851755174f,
-0.12128480194602098f, 0.7821684431180708f, 0.4321472685365301f, 0.4321472685365301f,
0.508629699630796f, 0.8586508742123365f, -0.044802370851755174f, -0.044802370851755174f,
0.4321472685365301f, 0.7821684431180708f, -0.12128480194602098f, 0.4321472685365301f,
0.4321472685365301f, 0.7821684431180708f, 0.4321472685365301f, -0.12128480194602098f,
0.37968289875261624f, 0.753341017856078f, 0.37968289875261624f, 0.37968289875261624f,
0.9982828964265062f, 0.03381941603233842f, 0.03381941603233842f, 0.03381941603233842f,
0.8586508742123365f, -0.044802370851755174f, -0.044802370851755174f, 0.508629699630796f,
0.8586508742123365f, -0.044802370851755174f, 0.508629699630796f, -0.044802370851755174f,
0.7821684431180708f, -0.12128480194602098f, 0.4321472685365301f, 0.4321472685365301f,
0.8586508742123365f, 0.508629699630796f, -0.044802370851755174f, -0.044802370851755174f,
0.7821684431180708f, 0.4321472685365301f, -0.12128480194602098f, 0.4321472685365301f,
0.7821684431180708f, 0.4321472685365301f, 0.4321472685365301f, -0.12128480194602098f,
0.753341017856078f, 0.37968289875261624f, 0.37968289875261624f, 0.37968289875261624f,
};
for ( int i = 0; i < grad4.Length; i++ )
{
grad4[i] = (float)(grad4[i] / NORMALIZER_4D);
}
for ( int i = 0, j = 0; i < GRADIENTS_4D.Length; i++, j++ )
{
if ( j == grad4.Length ) j = 0;
GRADIENTS_4D[i] = grad4[j];
}
int[][] lookup4DVertexCodes = {
new int[] { 0x15, 0x45, 0x51, 0x54, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x15, 0x45, 0x51, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x6A, 0x95, 0x96, 0x9A, 0xA6, 0xAA },
new int[] { 0x01, 0x05, 0x11, 0x15, 0x41, 0x45, 0x51, 0x55, 0x56, 0x5A, 0x66, 0x6A, 0x96, 0x9A, 0xA6, 0xAA },
new int[] { 0x01, 0x15, 0x16, 0x45, 0x46, 0x51, 0x52, 0x55, 0x56, 0x5A, 0x66, 0x6A, 0x96, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x15, 0x45, 0x54, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x69, 0x6A, 0x95, 0x99, 0x9A, 0xA9, 0xAA },
new int[] { 0x05, 0x15, 0x45, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xAA },
new int[] { 0x05, 0x15, 0x45, 0x55, 0x56, 0x59, 0x5A, 0x66, 0x6A, 0x96, 0x9A, 0xAA },
new int[] { 0x05, 0x15, 0x16, 0x45, 0x46, 0x55, 0x56, 0x59, 0x5A, 0x66, 0x6A, 0x96, 0x9A, 0xAA, 0xAB },
new int[] { 0x04, 0x05, 0x14, 0x15, 0x44, 0x45, 0x54, 0x55, 0x59, 0x5A, 0x69, 0x6A, 0x99, 0x9A, 0xA9, 0xAA },
new int[] { 0x05, 0x15, 0x45, 0x55, 0x56, 0x59, 0x5A, 0x69, 0x6A, 0x99, 0x9A, 0xAA },
new int[] { 0x05, 0x15, 0x45, 0x55, 0x56, 0x59, 0x5A, 0x6A, 0x9A, 0xAA },
new int[] { 0x05, 0x15, 0x16, 0x45, 0x46, 0x55, 0x56, 0x59, 0x5A, 0x5B, 0x6A, 0x9A, 0xAA, 0xAB },
new int[] { 0x04, 0x15, 0x19, 0x45, 0x49, 0x54, 0x55, 0x58, 0x59, 0x5A, 0x69, 0x6A, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x05, 0x15, 0x19, 0x45, 0x49, 0x55, 0x56, 0x59, 0x5A, 0x69, 0x6A, 0x99, 0x9A, 0xAA, 0xAE },
new int[] { 0x05, 0x15, 0x19, 0x45, 0x49, 0x55, 0x56, 0x59, 0x5A, 0x5E, 0x6A, 0x9A, 0xAA, 0xAE },
new int[] { 0x05, 0x15, 0x1A, 0x45, 0x4A, 0x55, 0x56, 0x59, 0x5A, 0x5B, 0x5E, 0x6A, 0x9A, 0xAA, 0xAB, 0xAE, 0xAF },
new int[] { 0x15, 0x51, 0x54, 0x55, 0x56, 0x59, 0x65, 0x66, 0x69, 0x6A, 0x95, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x11, 0x15, 0x51, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0xA5, 0xA6, 0xAA },
new int[] { 0x11, 0x15, 0x51, 0x55, 0x56, 0x5A, 0x65, 0x66, 0x6A, 0x96, 0xA6, 0xAA },
new int[] { 0x11, 0x15, 0x16, 0x51, 0x52, 0x55, 0x56, 0x5A, 0x65, 0x66, 0x6A, 0x96, 0xA6, 0xAA, 0xAB },
new int[] { 0x14, 0x15, 0x54, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x99, 0xA5, 0xA9, 0xAA },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x9A, 0xA6, 0xA9, 0xAA },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x96, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x15, 0x16, 0x55, 0x56, 0x5A, 0x66, 0x6A, 0x6B, 0x96, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x14, 0x15, 0x54, 0x55, 0x59, 0x5A, 0x65, 0x69, 0x6A, 0x99, 0xA9, 0xAA },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x9A, 0xAA },
new int[] { 0x15, 0x16, 0x55, 0x56, 0x59, 0x5A, 0x66, 0x6A, 0x6B, 0x9A, 0xAA, 0xAB },
new int[] { 0x14, 0x15, 0x19, 0x54, 0x55, 0x58, 0x59, 0x5A, 0x65, 0x69, 0x6A, 0x99, 0xA9, 0xAA, 0xAE },
new int[] { 0x15, 0x19, 0x55, 0x59, 0x5A, 0x69, 0x6A, 0x6E, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x15, 0x19, 0x55, 0x56, 0x59, 0x5A, 0x69, 0x6A, 0x6E, 0x9A, 0xAA, 0xAE },
new int[] { 0x15, 0x1A, 0x55, 0x56, 0x59, 0x5A, 0x6A, 0x6B, 0x6E, 0x9A, 0xAA, 0xAB, 0xAE, 0xAF },
new int[] { 0x10, 0x11, 0x14, 0x15, 0x50, 0x51, 0x54, 0x55, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x11, 0x15, 0x51, 0x55, 0x56, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA6, 0xAA },
new int[] { 0x11, 0x15, 0x51, 0x55, 0x56, 0x65, 0x66, 0x6A, 0xA6, 0xAA },
new int[] { 0x11, 0x15, 0x16, 0x51, 0x52, 0x55, 0x56, 0x65, 0x66, 0x67, 0x6A, 0xA6, 0xAA, 0xAB },
new int[] { 0x14, 0x15, 0x54, 0x55, 0x59, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA9, 0xAA },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0xA6, 0xAA },
new int[] { 0x15, 0x16, 0x55, 0x56, 0x5A, 0x65, 0x66, 0x6A, 0x6B, 0xA6, 0xAA, 0xAB },
new int[] { 0x14, 0x15, 0x54, 0x55, 0x59, 0x65, 0x69, 0x6A, 0xA9, 0xAA },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0xA9, 0xAA },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0xAA },
new int[] { 0x15, 0x16, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x6B, 0xAA, 0xAB },
new int[] { 0x14, 0x15, 0x19, 0x54, 0x55, 0x58, 0x59, 0x65, 0x69, 0x6A, 0x6D, 0xA9, 0xAA, 0xAE },
new int[] { 0x15, 0x19, 0x55, 0x59, 0x5A, 0x65, 0x69, 0x6A, 0x6E, 0xA9, 0xAA, 0xAE },
new int[] { 0x15, 0x19, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x6E, 0xAA, 0xAE },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x66, 0x69, 0x6A, 0x6B, 0x6E, 0x9A, 0xAA, 0xAB, 0xAE, 0xAF },
new int[] { 0x10, 0x15, 0x25, 0x51, 0x54, 0x55, 0x61, 0x64, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x11, 0x15, 0x25, 0x51, 0x55, 0x56, 0x61, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA6, 0xAA, 0xBA },
new int[] { 0x11, 0x15, 0x25, 0x51, 0x55, 0x56, 0x61, 0x65, 0x66, 0x6A, 0x76, 0xA6, 0xAA, 0xBA },
new int[] { 0x11, 0x15, 0x26, 0x51, 0x55, 0x56, 0x62, 0x65, 0x66, 0x67, 0x6A, 0x76, 0xA6, 0xAA, 0xAB, 0xBA, 0xBB },
new int[] { 0x14, 0x15, 0x25, 0x54, 0x55, 0x59, 0x64, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA9, 0xAA, 0xBA },
new int[] { 0x15, 0x25, 0x55, 0x65, 0x66, 0x69, 0x6A, 0x7A, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x15, 0x25, 0x55, 0x56, 0x65, 0x66, 0x69, 0x6A, 0x7A, 0xA6, 0xAA, 0xBA },
new int[] { 0x15, 0x26, 0x55, 0x56, 0x65, 0x66, 0x6A, 0x6B, 0x7A, 0xA6, 0xAA, 0xAB, 0xBA, 0xBB },
new int[] { 0x14, 0x15, 0x25, 0x54, 0x55, 0x59, 0x64, 0x65, 0x69, 0x6A, 0x79, 0xA9, 0xAA, 0xBA },
new int[] { 0x15, 0x25, 0x55, 0x59, 0x65, 0x66, 0x69, 0x6A, 0x7A, 0xA9, 0xAA, 0xBA },
new int[] { 0x15, 0x25, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x7A, 0xAA, 0xBA },
new int[] { 0x15, 0x55, 0x56, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x6B, 0x7A, 0xA6, 0xAA, 0xAB, 0xBA, 0xBB },
new int[] { 0x14, 0x15, 0x29, 0x54, 0x55, 0x59, 0x65, 0x68, 0x69, 0x6A, 0x6D, 0x79, 0xA9, 0xAA, 0xAE, 0xBA, 0xBE },
new int[] { 0x15, 0x29, 0x55, 0x59, 0x65, 0x69, 0x6A, 0x6E, 0x7A, 0xA9, 0xAA, 0xAE, 0xBA, 0xBE },
new int[] { 0x15, 0x55, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x6E, 0x7A, 0xA9, 0xAA, 0xAE, 0xBA, 0xBE },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x6B, 0x6E, 0x7A, 0xAA, 0xAB, 0xAE, 0xBA, 0xBF },
new int[] { 0x45, 0x51, 0x54, 0x55, 0x56, 0x59, 0x65, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x41, 0x45, 0x51, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xAA },
new int[] { 0x41, 0x45, 0x51, 0x55, 0x56, 0x5A, 0x66, 0x95, 0x96, 0x9A, 0xA6, 0xAA },
new int[] { 0x41, 0x45, 0x46, 0x51, 0x52, 0x55, 0x56, 0x5A, 0x66, 0x95, 0x96, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x44, 0x45, 0x54, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x69, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA9, 0xAA },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA6, 0xA9, 0xAA },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x66, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x45, 0x46, 0x55, 0x56, 0x5A, 0x66, 0x6A, 0x96, 0x9A, 0x9B, 0xA6, 0xAA, 0xAB },
new int[] { 0x44, 0x45, 0x54, 0x55, 0x59, 0x5A, 0x69, 0x95, 0x99, 0x9A, 0xA9, 0xAA },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xAA },
new int[] { 0x45, 0x46, 0x55, 0x56, 0x59, 0x5A, 0x6A, 0x96, 0x9A, 0x9B, 0xAA, 0xAB },
new int[] { 0x44, 0x45, 0x49, 0x54, 0x55, 0x58, 0x59, 0x5A, 0x69, 0x95, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x45, 0x49, 0x55, 0x59, 0x5A, 0x69, 0x6A, 0x99, 0x9A, 0x9E, 0xA9, 0xAA, 0xAE },
new int[] { 0x45, 0x49, 0x55, 0x56, 0x59, 0x5A, 0x6A, 0x99, 0x9A, 0x9E, 0xAA, 0xAE },
new int[] { 0x45, 0x4A, 0x55, 0x56, 0x59, 0x5A, 0x6A, 0x9A, 0x9B, 0x9E, 0xAA, 0xAB, 0xAE, 0xAF },
new int[] { 0x50, 0x51, 0x54, 0x55, 0x56, 0x59, 0x65, 0x66, 0x69, 0x95, 0x96, 0x99, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x51, 0x55, 0x56, 0x59, 0x65, 0x66, 0x6A, 0x95, 0x96, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x51, 0x55, 0x56, 0x5A, 0x65, 0x66, 0x6A, 0x95, 0x96, 0x9A, 0xA5, 0xA6, 0xAA, 0xAB },
new int[] { 0x51, 0x52, 0x55, 0x56, 0x5A, 0x66, 0x6A, 0x96, 0x9A, 0xA6, 0xA7, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x56, 0x59, 0x65, 0x69, 0x6A, 0x95, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x15, 0x45, 0x51, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x6A, 0x95, 0x96, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x55, 0x56, 0x5A, 0x66, 0x6A, 0x96, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x59, 0x5A, 0x65, 0x69, 0x6A, 0x95, 0x99, 0x9A, 0xA5, 0xA9, 0xAA, 0xAE },
new int[] { 0x15, 0x45, 0x54, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x69, 0x6A, 0x95, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x15, 0x45, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA6, 0xA9, 0xAA, 0xAB, 0xAE },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x66, 0x6A, 0x96, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x58, 0x59, 0x5A, 0x69, 0x6A, 0x99, 0x9A, 0xA9, 0xAA, 0xAD, 0xAE },
new int[] { 0x55, 0x59, 0x5A, 0x69, 0x6A, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x69, 0x6A, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x6A, 0x9A, 0xAA, 0xAB, 0xAE, 0xAF },
new int[] { 0x50, 0x51, 0x54, 0x55, 0x65, 0x66, 0x69, 0x95, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x6A, 0x95, 0x96, 0xA5, 0xA6, 0xAA },
new int[] { 0x51, 0x52, 0x55, 0x56, 0x65, 0x66, 0x6A, 0x96, 0xA6, 0xA7, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x99, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x15, 0x51, 0x54, 0x55, 0x56, 0x59, 0x65, 0x66, 0x69, 0x6A, 0x95, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x15, 0x51, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAB, 0xBA },
new int[] { 0x55, 0x56, 0x5A, 0x65, 0x66, 0x6A, 0x96, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x69, 0x6A, 0x95, 0x99, 0xA5, 0xA9, 0xAA },
new int[] { 0x15, 0x54, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAE, 0xBA },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x9A, 0xA6, 0xA9, 0xAA },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x96, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x58, 0x59, 0x65, 0x69, 0x6A, 0x99, 0xA9, 0xAA, 0xAD, 0xAE },
new int[] { 0x55, 0x59, 0x5A, 0x65, 0x69, 0x6A, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x66, 0x69, 0x6A, 0x9A, 0xAA, 0xAB, 0xAE, 0xAF },
new int[] { 0x50, 0x51, 0x54, 0x55, 0x61, 0x64, 0x65, 0x66, 0x69, 0x95, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x51, 0x55, 0x61, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA6, 0xA9, 0xAA, 0xB6, 0xBA },
new int[] { 0x51, 0x55, 0x56, 0x61, 0x65, 0x66, 0x6A, 0xA5, 0xA6, 0xAA, 0xB6, 0xBA },
new int[] { 0x51, 0x55, 0x56, 0x62, 0x65, 0x66, 0x6A, 0xA6, 0xA7, 0xAA, 0xAB, 0xB6, 0xBA, 0xBB },
new int[] { 0x54, 0x55, 0x64, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA6, 0xA9, 0xAA, 0xB9, 0xBA },
new int[] { 0x55, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x55, 0x56, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x55, 0x56, 0x65, 0x66, 0x6A, 0xA6, 0xAA, 0xAB, 0xBA, 0xBB },
new int[] { 0x54, 0x55, 0x59, 0x64, 0x65, 0x69, 0x6A, 0xA5, 0xA9, 0xAA, 0xB9, 0xBA },
new int[] { 0x55, 0x59, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x15, 0x55, 0x56, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0xA6, 0xAA, 0xAB, 0xBA, 0xBB },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x68, 0x69, 0x6A, 0xA9, 0xAA, 0xAD, 0xAE, 0xB9, 0xBA, 0xBE },
new int[] { 0x55, 0x59, 0x65, 0x69, 0x6A, 0xA9, 0xAA, 0xAE, 0xBA, 0xBE },
new int[] { 0x15, 0x55, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0xA9, 0xAA, 0xAE, 0xBA, 0xBE },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0xAA, 0xAB, 0xAE, 0xBA, 0xBF },
new int[] { 0x40, 0x41, 0x44, 0x45, 0x50, 0x51, 0x54, 0x55, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x41, 0x45, 0x51, 0x55, 0x56, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xAA },
new int[] { 0x41, 0x45, 0x51, 0x55, 0x56, 0x95, 0x96, 0x9A, 0xA6, 0xAA },
new int[] { 0x41, 0x45, 0x46, 0x51, 0x52, 0x55, 0x56, 0x95, 0x96, 0x97, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x44, 0x45, 0x54, 0x55, 0x59, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA9, 0xAA },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0xA6, 0xAA },
new int[] { 0x45, 0x46, 0x55, 0x56, 0x5A, 0x95, 0x96, 0x9A, 0x9B, 0xA6, 0xAA, 0xAB },
new int[] { 0x44, 0x45, 0x54, 0x55, 0x59, 0x95, 0x99, 0x9A, 0xA9, 0xAA },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0xA9, 0xAA },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0xAA },
new int[] { 0x45, 0x46, 0x55, 0x56, 0x59, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0x9B, 0xAA, 0xAB },
new int[] { 0x44, 0x45, 0x49, 0x54, 0x55, 0x58, 0x59, 0x95, 0x99, 0x9A, 0x9D, 0xA9, 0xAA, 0xAE },
new int[] { 0x45, 0x49, 0x55, 0x59, 0x5A, 0x95, 0x99, 0x9A, 0x9E, 0xA9, 0xAA, 0xAE },
new int[] { 0x45, 0x49, 0x55, 0x56, 0x59, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0x9E, 0xAA, 0xAE },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x6A, 0x96, 0x99, 0x9A, 0x9B, 0x9E, 0xAA, 0xAB, 0xAE, 0xAF },
new int[] { 0x50, 0x51, 0x54, 0x55, 0x65, 0x95, 0x96, 0x99, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x95, 0x96, 0x9A, 0xA5, 0xA6, 0xAA },
new int[] { 0x51, 0x52, 0x55, 0x56, 0x66, 0x95, 0x96, 0x9A, 0xA6, 0xA7, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x69, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x45, 0x51, 0x54, 0x55, 0x56, 0x59, 0x65, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x45, 0x51, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAB, 0xEA },
new int[] { 0x55, 0x56, 0x5A, 0x66, 0x6A, 0x95, 0x96, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x69, 0x95, 0x99, 0x9A, 0xA5, 0xA9, 0xAA },
new int[] { 0x45, 0x54, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAE, 0xEA },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA6, 0xA9, 0xAA },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x66, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x58, 0x59, 0x69, 0x95, 0x99, 0x9A, 0xA9, 0xAA, 0xAD, 0xAE },
new int[] { 0x55, 0x59, 0x5A, 0x69, 0x6A, 0x95, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x6A, 0x96, 0x99, 0x9A, 0xAA, 0xAB, 0xAE, 0xAF },
new int[] { 0x50, 0x51, 0x54, 0x55, 0x65, 0x95, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x95, 0x96, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x95, 0x96, 0xA5, 0xA6, 0xAA },
new int[] { 0x51, 0x52, 0x55, 0x56, 0x65, 0x66, 0x95, 0x96, 0xA5, 0xA6, 0xA7, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x69, 0x95, 0x99, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x51, 0x54, 0x55, 0x56, 0x59, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA, 0xEA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x6A, 0x95, 0x96, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x51, 0x55, 0x56, 0x5A, 0x65, 0x66, 0x6A, 0x95, 0x96, 0x9A, 0xA5, 0xA6, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x69, 0x95, 0x99, 0xA5, 0xA9, 0xAA },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x69, 0x6A, 0x95, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x6A, 0x95, 0x96, 0x9A, 0xA6, 0xA9, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x58, 0x59, 0x65, 0x69, 0x95, 0x99, 0xA5, 0xA9, 0xAA, 0xAD, 0xAE },
new int[] { 0x54, 0x55, 0x59, 0x5A, 0x65, 0x69, 0x6A, 0x95, 0x99, 0x9A, 0xA5, 0xA9, 0xAA, 0xAE },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x65, 0x69, 0x6A, 0x95, 0x99, 0x9A, 0xA6, 0xA9, 0xAA, 0xAE },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x66, 0x69, 0x6A, 0x96, 0x99, 0x9A, 0xA6, 0xA9, 0xAA, 0xAB, 0xAE, 0xAF },
new int[] { 0x50, 0x51, 0x54, 0x55, 0x61, 0x64, 0x65, 0x95, 0xA5, 0xA6, 0xA9, 0xAA, 0xB5, 0xBA },
new int[] { 0x51, 0x55, 0x61, 0x65, 0x66, 0x95, 0xA5, 0xA6, 0xA9, 0xAA, 0xB6, 0xBA },
new int[] { 0x51, 0x55, 0x56, 0x61, 0x65, 0x66, 0x95, 0x96, 0xA5, 0xA6, 0xAA, 0xB6, 0xBA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x6A, 0x96, 0xA5, 0xA6, 0xA7, 0xAA, 0xAB, 0xB6, 0xBA, 0xBB },
new int[] { 0x54, 0x55, 0x64, 0x65, 0x69, 0x95, 0xA5, 0xA6, 0xA9, 0xAA, 0xB9, 0xBA },
new int[] { 0x55, 0x65, 0x66, 0x69, 0x6A, 0x95, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x6A, 0x96, 0xA5, 0xA6, 0xAA, 0xAB, 0xBA, 0xBB },
new int[] { 0x54, 0x55, 0x59, 0x64, 0x65, 0x69, 0x95, 0x99, 0xA5, 0xA9, 0xAA, 0xB9, 0xBA },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x99, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x55, 0x56, 0x59, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x55, 0x56, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x96, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAB, 0xBA, 0xBB },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x69, 0x6A, 0x99, 0xA5, 0xA9, 0xAA, 0xAD, 0xAE, 0xB9, 0xBA, 0xBE },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x69, 0x6A, 0x99, 0xA5, 0xA9, 0xAA, 0xAE, 0xBA, 0xBE },
new int[] { 0x55, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAE, 0xBA, 0xBE },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x9A, 0xA6, 0xA9, 0xAA, 0xAB, 0xAE, 0xBA },
new int[] { 0x40, 0x45, 0x51, 0x54, 0x55, 0x85, 0x91, 0x94, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x41, 0x45, 0x51, 0x55, 0x56, 0x85, 0x91, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xAA, 0xEA },
new int[] { 0x41, 0x45, 0x51, 0x55, 0x56, 0x85, 0x91, 0x95, 0x96, 0x9A, 0xA6, 0xAA, 0xD6, 0xEA },
new int[] { 0x41, 0x45, 0x51, 0x55, 0x56, 0x86, 0x92, 0x95, 0x96, 0x97, 0x9A, 0xA6, 0xAA, 0xAB, 0xD6, 0xEA, 0xEB },
new int[] { 0x44, 0x45, 0x54, 0x55, 0x59, 0x85, 0x94, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA9, 0xAA, 0xEA },
new int[] { 0x45, 0x55, 0x85, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xDA, 0xEA },
new int[] { 0x45, 0x55, 0x56, 0x85, 0x95, 0x96, 0x99, 0x9A, 0xA6, 0xAA, 0xDA, 0xEA },
new int[] { 0x45, 0x55, 0x56, 0x86, 0x95, 0x96, 0x9A, 0x9B, 0xA6, 0xAA, 0xAB, 0xDA, 0xEA, 0xEB },
new int[] { 0x44, 0x45, 0x54, 0x55, 0x59, 0x85, 0x94, 0x95, 0x99, 0x9A, 0xA9, 0xAA, 0xD9, 0xEA },
new int[] { 0x45, 0x55, 0x59, 0x85, 0x95, 0x96, 0x99, 0x9A, 0xA9, 0xAA, 0xDA, 0xEA },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x85, 0x95, 0x96, 0x99, 0x9A, 0xAA, 0xDA, 0xEA },
new int[] { 0x45, 0x55, 0x56, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0x9B, 0xA6, 0xAA, 0xAB, 0xDA, 0xEA, 0xEB },
new int[] { 0x44, 0x45, 0x54, 0x55, 0x59, 0x89, 0x95, 0x98, 0x99, 0x9A, 0x9D, 0xA9, 0xAA, 0xAE, 0xD9, 0xEA, 0xEE },
new int[] { 0x45, 0x55, 0x59, 0x89, 0x95, 0x99, 0x9A, 0x9E, 0xA9, 0xAA, 0xAE, 0xDA, 0xEA, 0xEE },
new int[] { 0x45, 0x55, 0x59, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0x9E, 0xA9, 0xAA, 0xAE, 0xDA, 0xEA, 0xEE },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0x9B, 0x9E, 0xAA, 0xAB, 0xAE, 0xDA, 0xEA, 0xEF },
new int[] { 0x50, 0x51, 0x54, 0x55, 0x65, 0x91, 0x94, 0x95, 0x96, 0x99, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x51, 0x55, 0x91, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xE6, 0xEA },
new int[] { 0x51, 0x55, 0x56, 0x91, 0x95, 0x96, 0x9A, 0xA5, 0xA6, 0xAA, 0xE6, 0xEA },
new int[] { 0x51, 0x55, 0x56, 0x92, 0x95, 0x96, 0x9A, 0xA6, 0xA7, 0xAA, 0xAB, 0xE6, 0xEA, 0xEB },
new int[] { 0x54, 0x55, 0x94, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xE9, 0xEA },
new int[] { 0x55, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x55, 0x56, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x55, 0x56, 0x95, 0x96, 0x9A, 0xA6, 0xAA, 0xAB, 0xEA, 0xEB },
new int[] { 0x54, 0x55, 0x59, 0x94, 0x95, 0x99, 0x9A, 0xA5, 0xA9, 0xAA, 0xE9, 0xEA },
new int[] { 0x55, 0x59, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x45, 0x55, 0x56, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0xA6, 0xAA, 0xAB, 0xEA, 0xEB },
new int[] { 0x54, 0x55, 0x59, 0x95, 0x98, 0x99, 0x9A, 0xA9, 0xAA, 0xAD, 0xAE, 0xE9, 0xEA, 0xEE },
new int[] { 0x55, 0x59, 0x95, 0x99, 0x9A, 0xA9, 0xAA, 0xAE, 0xEA, 0xEE },
new int[] { 0x45, 0x55, 0x59, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0xA9, 0xAA, 0xAE, 0xEA, 0xEE },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0xAA, 0xAB, 0xAE, 0xEA, 0xEF },
new int[] { 0x50, 0x51, 0x54, 0x55, 0x65, 0x91, 0x94, 0x95, 0xA5, 0xA6, 0xA9, 0xAA, 0xE5, 0xEA },
new int[] { 0x51, 0x55, 0x65, 0x91, 0x95, 0x96, 0xA5, 0xA6, 0xA9, 0xAA, 0xE6, 0xEA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x91, 0x95, 0x96, 0xA5, 0xA6, 0xAA, 0xE6, 0xEA },
new int[] { 0x51, 0x55, 0x56, 0x66, 0x95, 0x96, 0x9A, 0xA5, 0xA6, 0xA7, 0xAA, 0xAB, 0xE6, 0xEA, 0xEB },
new int[] { 0x54, 0x55, 0x65, 0x94, 0x95, 0x99, 0xA5, 0xA6, 0xA9, 0xAA, 0xE9, 0xEA },
new int[] { 0x55, 0x65, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x51, 0x55, 0x56, 0x66, 0x95, 0x96, 0x9A, 0xA5, 0xA6, 0xAA, 0xAB, 0xEA, 0xEB },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x69, 0x94, 0x95, 0x99, 0xA5, 0xA9, 0xAA, 0xE9, 0xEA },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x69, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x55, 0x56, 0x59, 0x65, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x55, 0x56, 0x5A, 0x66, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAB, 0xEA, 0xEB },
new int[] { 0x54, 0x55, 0x59, 0x69, 0x95, 0x99, 0x9A, 0xA5, 0xA9, 0xAA, 0xAD, 0xAE, 0xE9, 0xEA, 0xEE },
new int[] { 0x54, 0x55, 0x59, 0x69, 0x95, 0x99, 0x9A, 0xA5, 0xA9, 0xAA, 0xAE, 0xEA, 0xEE },
new int[] { 0x55, 0x59, 0x5A, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAE, 0xEA, 0xEE },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA6, 0xA9, 0xAA, 0xAB, 0xAE, 0xEA },
new int[] { 0x50, 0x51, 0x54, 0x55, 0x65, 0x95, 0xA1, 0xA4, 0xA5, 0xA6, 0xA9, 0xAA, 0xB5, 0xBA, 0xE5, 0xEA, 0xFA },
new int[] { 0x51, 0x55, 0x65, 0x95, 0xA1, 0xA5, 0xA6, 0xA9, 0xAA, 0xB6, 0xBA, 0xE6, 0xEA, 0xFA },
new int[] { 0x51, 0x55, 0x65, 0x66, 0x95, 0x96, 0xA5, 0xA6, 0xA9, 0xAA, 0xB6, 0xBA, 0xE6, 0xEA, 0xFA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x95, 0x96, 0xA5, 0xA6, 0xA7, 0xAA, 0xAB, 0xB6, 0xBA, 0xE6, 0xEA, 0xFB },
new int[] { 0x54, 0x55, 0x65, 0x95, 0xA4, 0xA5, 0xA6, 0xA9, 0xAA, 0xB9, 0xBA, 0xE9, 0xEA, 0xFA },
new int[] { 0x55, 0x65, 0x95, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA, 0xEA, 0xFA },
new int[] { 0x51, 0x55, 0x65, 0x66, 0x95, 0x96, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA, 0xEA, 0xFA },
new int[] { 0x55, 0x56, 0x65, 0x66, 0x95, 0x96, 0xA5, 0xA6, 0xAA, 0xAB, 0xBA, 0xEA, 0xFB },
new int[] { 0x54, 0x55, 0x65, 0x69, 0x95, 0x99, 0xA5, 0xA6, 0xA9, 0xAA, 0xB9, 0xBA, 0xE9, 0xEA, 0xFA },
new int[] { 0x54, 0x55, 0x65, 0x69, 0x95, 0x99, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA, 0xEA, 0xFA },
new int[] { 0x55, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA, 0xEA, 0xFA },
new int[] { 0x55, 0x56, 0x65, 0x66, 0x6A, 0x95, 0x96, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAB, 0xBA, 0xEA },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x69, 0x95, 0x99, 0xA5, 0xA9, 0xAA, 0xAD, 0xAE, 0xB9, 0xBA, 0xE9, 0xEA, 0xFE },
new int[] { 0x55, 0x59, 0x65, 0x69, 0x95, 0x99, 0xA5, 0xA9, 0xAA, 0xAE, 0xBA, 0xEA, 0xFE },
new int[] { 0x55, 0x59, 0x65, 0x69, 0x6A, 0x95, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAE, 0xBA, 0xEA },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAB, 0xAE, 0xBA, 0xEA },
};
LatticeVertex4D[] latticeVerticesByCode = new LatticeVertex4D[256];
for ( int i = 0; i < 256; i++ )
{
int cx = ((i >> 0) & 3) - 1;
int cy = ((i >> 2) & 3) - 1;
int cz = ((i >> 4) & 3) - 1;
int cw = ((i >> 6) & 3) - 1;
latticeVerticesByCode[i] = new LatticeVertex4D( cx, cy, cz, cw );
}
int nLatticeVerticesTotal = 0;
for ( int i = 0; i < 256; i++ )
{
nLatticeVerticesTotal += lookup4DVertexCodes[i].Length;
}
LOOKUP_4D_A = new (short SecondaryIndexStart, short SecondaryIndexStop)[256];
LOOKUP_4D_B = new LatticeVertex4D[nLatticeVerticesTotal];
for ( int i = 0, j = 0; i < 256; i++ )
{
LOOKUP_4D_A[i] = ((short)j, (short)(j + lookup4DVertexCodes[i].Length));
for ( int k = 0; k < lookup4DVertexCodes[i].Length; k++ )
{
LOOKUP_4D_B[j++] = latticeVerticesByCode[lookup4DVertexCodes[i][k]];
}
}
}
private class LatticeVertex4D
{
public readonly float dx, dy, dz, dw;
public readonly long xsvp, ysvp, zsvp, wsvp;
public LatticeVertex4D( int xsv, int ysv, int zsv, int wsv )
{
this.xsvp = xsv * PRIME_X; this.ysvp = ysv * PRIME_Y;
this.zsvp = zsv * PRIME_Z; this.wsvp = wsv * PRIME_W;
float ssv = (xsv + ysv + zsv + wsv) * UNSKEW_4D;
this.dx = -xsv - ssv;
this.dy = -ysv - ssv;
this.dz = -zsv - ssv;
this.dw = -wsv - ssv;
}
}
}
using Editor;
using Sandbox;
using System;
namespace Sturnus.TerrainGenerationTool;
public static class Islands
{
public static float Default( int x, int y, int width, int height, long seed, float minHeight, bool warp, float warpSize = 0.1f, float warpStrength = 0.5f )
{
float nx = (x / (float)width) * 2 - 1; // Normalize x to range [-1, 1]
float ny = (y / (float)height) * 2 - 1; // Normalize y to range [-1, 1]
float warpX;
float warpY;
float warpedNx;
float warpedNy;
float noise;
if ( warp )
{
// Generate warp offsets using additional noise
warpX = OpenSimplex2S.Noise2( seed + 10, nx * warpSize, ny * warpSize ) * warpStrength;
warpY = OpenSimplex2S.Noise2( seed + 11, nx * warpSize, ny * warpSize ) * warpStrength;
// Apply domain warping
warpedNx = nx + warpX;
warpedNy = ny + warpY;
}
else
{
warpedNx = nx;
warpedNy = ny;
}
// Radial distance from the center
float distance = (float)Math.Sqrt( nx * nx + ny * ny );
float falloff = 1.0f - Math.Clamp( distance, 0.25f, 1 ); // Smooth taper from center to edge
// Central mountain shape (parabolic for smooth curvature)
float centralMountain = (1.0f - distance * distance) * falloff;
// Beach-style taper near the edges
float beachStart = 0.5f; // Start of the beach region (distance normalized)
float beachEnd = 0.98f; // End of the beach region (ocean level)
float beachFalloff = Math.Clamp( (distance - beachStart) / (beachEnd - beachStart), 0.1f, 1 );
float beachTaper = (1.0f - beachFalloff) * 0.25f; // Smooth transition to flat region
// Add subtle noise for terrain variation
if ( warp )
{
noise = OpenSimplex2S.Noise2( seed, warpedNx * 2, warpedNy * 2 ) * 0.4f;
}
else
{
noise = OpenSimplex2S.Noise2( seed, nx * 6, ny * 6 ) * 0.05f; // Low-frequency noise
}
// Combine components: central mountain, beach taper, and noise
float output = centralMountain * (1.0f - beachFalloff) + beachTaper + noise;
// Combine all effects
float heightValue = output;
// Add a baseline value to ensure no flat zero areas
float baseline = minHeight; // Minimum height
float heightValueCombined = MathF.Max( heightValue, baseline );
// Clamp the final height to valid range
return heightValueCombined;
}
public static float Archipelagos( int x, int y, int width, int height, long seed, float minHeight, bool warp, float warpSize, float warpStrength )
{
Random random = new Random( (int)(seed & 0xFFFFFFFF) );
float nx = (x / (float)width) * 2 - 1;
float ny = (y / (float)height) * 2 - 1;
// Apply domain warping
if ( warp )
{
float warpX = OpenSimplex2S.Noise2( seed + 10, nx * warpSize, ny * warpSize ) * warpStrength;
float warpY = OpenSimplex2S.Noise2( seed + 11, nx * warpSize, ny * warpSize ) * warpStrength;
nx += warpX;
ny += warpY;
}
// Use noise layers to create clusters of small islands
float baseNoise = OpenSimplex2S.Noise2( seed, nx * 1.5f, ny * 1.5f );
float secondaryNoise = OpenSimplex2S.Noise2( seed + 1, nx * 3.0f, ny * 3.0f ) * 0.5f;
float archipelagoHeight = baseNoise + secondaryNoise;
// Apply radial falloff to form rounded island clusters
float distance = MathF.Sqrt( nx * nx + ny * ny );
float falloff = Math.Clamp( 1 - distance * 1.2f, 0, 1 );
float heightValue = Math.Clamp( archipelagoHeight * falloff, 0, 1 );
// Add a baseline value to ensure no flat zero areas
float baseline = minHeight; // Minimum height
float heightValueCombined = MathF.Max( heightValue, baseline );
// Clamp the final height to valid range
return heightValueCombined;
}
public static float Atoll( int x, int y, int width, int height, long seed, float minHeight, bool warp, float warpSize, float warpStrength )
{
Random random = new Random( (int)(seed & 0xFFFFFFFF) );
float nx = (x / (float)width) * 2 - 1; // Normalize x to range [-1, 1]
float ny = (y / (float)height) * 2 - 1; // Normalize y to range [-1, 1]
// Apply domain warping
if ( warp )
{
float warpX = OpenSimplex2S.Noise2( seed + 20, nx * warpSize, ny * warpSize ) * warpStrength;
float warpY = OpenSimplex2S.Noise2( seed + 21, nx * warpSize, ny * warpSize ) * warpStrength;
nx += warpX;
ny += warpY;
}
// Calculate distance from the center
float distance = MathF.Sqrt( nx * nx + ny * ny );
// Define parameters for the single ring
float ringCenter = 0.6f; // Center of the ring
float ringWidth = 0.1f; // Width of the ring
// Create a single ring using a Gaussian-like function
float ring = MathF.Exp( -MathF.Pow( (distance - ringCenter) / ringWidth, 2 ) );
// Add some noise for variation
float baseNoise = OpenSimplex2S.Noise2( seed, nx * 1.0f, ny * 1.0f ) * 0.4f;
// Introduce a beach-like area (reduce noise and height for one side of the map)
float beachEffect = Math.Clamp( (1 - nx) * 0.5f, 0.2f, 1.0f ); // Reduces height on one side of the map
float beachNoise = OpenSimplex2S.Noise2( seed + 30, nx * 2.0f, ny * 2.0f ) * 0.2f;
// Combine the ring, noise, and beach effect
float heightValue = (ring + baseNoise * beachEffect + beachNoise) * beachEffect;
// Add a baseline value to ensure no flat zero areas
float baseline = minHeight; // Minimum height
float heightValueCombined = MathF.Max( heightValue, baseline );
// Clamp the final height to valid range
return heightValueCombined;
}
public static float Islets( int x, int y, int width, int height, long seed, float minHeight, bool warp, float warpSize, float warpStrength )
{
Random random = new Random( (int)(seed & 0xFFFFFFFF) );
float nx = (x / (float)width) * 2 - 1;
float ny = (y / (float)height) * 2 - 1;
// Apply domain warping
if ( warp )
{
float warpX = OpenSimplex2S.Noise2( seed + 30, nx * warpSize, ny * warpSize ) * warpStrength;
float warpY = OpenSimplex2S.Noise2( seed + 31, nx * warpSize, ny * warpSize ) * warpStrength;
nx += warpX;
ny += warpY;
}
// Generate scattered small islands
float scatterNoise = OpenSimplex2S.Noise2( seed, nx * 6.0f, ny * 6.0f );
float baseNoise = OpenSimplex2S.Noise2( seed + 1, nx * 3.0f, ny * 3.0f ) * 0.5f;
float isletHeight = scatterNoise + baseNoise;
// Apply distance falloff to create isolated islets
float distance = MathF.Sqrt( nx * nx + ny * ny );
float falloff = Math.Clamp( 1 - distance * 1.5f, 0, 1 );
float heightValue = Math.Clamp( isletHeight * falloff, 0, 1 );
// Add a baseline value to ensure no flat zero areas
float baseline = minHeight; // Minimum height
float heightValueCombined = MathF.Max( heightValue, baseline );
// Clamp the final height to valid range
return heightValueCombined;
}
public static float Oceanic( int x, int y, int width, int height, long seed, float minHeight, bool warp, float warpSize, float warpStrength )
{
Random random = new Random( (int)(seed & 0xFFFFFFFF) );
float nx = (x / (float)width) * 2 - 1;
float ny = (y / (float)height) * 2 - 1;
// Apply domain warping
if ( warp )
{
float warpX = OpenSimplex2S.Noise2( seed + 40, nx * warpSize, ny * warpSize ) * warpStrength;
float warpY = OpenSimplex2S.Noise2( seed + 41, nx * warpSize, ny * warpSize ) * warpStrength;
nx += warpX;
ny += warpY;
}
// Generate large, continuous landmass with a few scattered features
float baseNoise = OpenSimplex2S.Noise2( seed, nx * 1.0f, ny * 1.0f );
float featureNoise = OpenSimplex2S.Noise2( seed + 1, nx * 2.0f, ny * 2.0f ) * 0.5f;
float oceanicHeight = baseNoise + featureNoise;
// Apply radial falloff for a natural ocean/land mix
float distance = MathF.Sqrt( nx * nx + ny * ny );
float falloff = Math.Clamp( 1 - distance * 1.0f, 0, 1 );
float heightValue = Math.Clamp( oceanicHeight * falloff, 0, 1 );
float baseline = minHeight; // Minimum height
float heightValueCombined = MathF.Max( heightValue, baseline );
// Clamp the final height to valid range
return heightValueCombined;
}
}
using Editor;
using Sandbox;
using System;
using System.Collections.Generic;
namespace Sturnus.TerrainGenerationTool;
public static class Planetary
{
public static float Sharded(
int x,
int y,
int width,
int height,
long seed,
float minHeight,
bool warp, // Apply domain warping
float warpSize, // Warp scale
float warpStrength // Warp strength
)
{
Random random = new Random( (int)(seed & 0xFFFFFFFF) );
float nx = (x / (float)width) * 2 - 1; // Normalize x to range [-1, 1]
float ny = (y / (float)height) * 2 - 1; // Normalize y to range [-1, 1]
float shardHeight = 10f;
float crackDepth = 1f;
float noiseStrength = 0.05f;
int cellCount = 5;
// Apply domain warping for irregularity
if ( warp )
{
float warpX = OpenSimplex2S.Noise2( seed + 10, nx * warpSize, ny * warpSize ) * warpStrength;
float warpY = OpenSimplex2S.Noise2( seed + 11, nx * warpSize, ny * warpSize ) * warpStrength;
nx += warpX;
ny += warpY;
}
// Generate cracks
float minDist = float.MaxValue;
float secondaryDist = float.MaxValue;
float cellSize = 2.0f / cellCount; // Normalize the cell size
for ( int i = 0; i < cellCount; i++ )
{
for ( int j = 0; j < cellCount; j++ )
{
float cellX = -1 + i * cellSize + OpenSimplex2S.Noise2( seed + 20, i, j ) * cellSize * 0.5f;
float cellY = -1 + j * cellSize + OpenSimplex2S.Noise2( seed + 21, i, j ) * cellSize * 0.5f;
float distance = MathF.Sqrt( (nx - cellX) * (nx - cellX) + (ny - cellY) * (ny - cellY) );
if ( distance < minDist )
{
secondaryDist = minDist;
minDist = distance;
}
else if ( distance < secondaryDist )
{
secondaryDist = distance;
}
}
}
// Compute shard height based on the secondary distance
float shardNoise = OpenSimplex2S.Noise2( seed + 30, nx, ny ) * noiseStrength;
float heightValue = MathF.Max( secondaryDist - minDist, 0f ) * shardHeight + shardNoise;
// Apply crack depth at borders between shards
if ( secondaryDist - minDist < 0.03f ) // Control the width of cracks
{
heightValue -= crackDepth;
}
// Add a baseline value to ensure no flat zero areas
//heightValue = MathF.Max( heightValue, minHeight );
// Clamp height to avoid negative values
heightValue = Math.Clamp( heightValue, 0, 1 );
float baseline = minHeight; // Minimum height
float heightValueCombined = MathF.Max( heightValue, baseline );
// Clamp the final height to valid range
return heightValueCombined;
}
public static float Craters(
int x,
int y,
int width,
int height,
long seed,
float minHeight,
bool warp, // Apply domain warping for irregularity
float warpSize, // Warp scale
float warpStrength // Warp strength
)
{
Random random = new Random( (int)(seed & 0xFFFFFFFF) );
float nx = (x / (float)width) * 2 - 1; // Normalize x to range [-1, 1]
float ny = (y / (float)height) * 2 - 1; // Normalize y to range [-1, 1]
int craterCount = 100;
float minCraterSize = 0.1f;
float maxCraterSize = 0.3f;
float craterDepth = 0.05f;
float rimHeight = 0.05f;
float rimWidthRatio = 0.2f;
float noiseStrength = 0.05f;
float largeCraterRatio = 0.2f;
float slopeFalloff = 0.9f; // Controls smoothness of ramps
// Apply domain warping for irregularity
if ( warp )
{
float warpX = OpenSimplex2S.Noise2( seed + 10, nx * warpSize, ny * warpSize ) * warpStrength;
float warpY = OpenSimplex2S.Noise2( seed + 11, nx * warpSize, ny * warpSize ) * warpStrength;
nx += warpX;
ny += warpY;
}
// Base terrain noise
float baseTerrain = OpenSimplex2S.Noise2( seed + 1, nx * 2.0f, ny * 2.0f ) * noiseStrength;
baseTerrain = (baseTerrain + 1) * 0.5f; // Normalize to [0, 1]
float heightValue = baseTerrain;
// Iterate through craters in reverse order to overwrite previous craters
for ( int i = craterCount - 1; i >= 0; i-- )
{
// Randomize crater properties
float craterX = random.Next( -100000, 100000 ) / 50000.0f;
float craterY = random.Next( -100000, 100000 ) / 50000.0f;
float craterRadius = (i < craterCount * largeCraterRatio)
? random.Next( (int)(maxCraterSize * 500), (int)(maxCraterSize * 1000) ) / 1000.0f // Large craters
: random.Next( (int)(minCraterSize * 500), (int)(minCraterSize * 1000) ) / 1000.0f; // Small craters
// Distance from the current point to the crater center
float distance = MathF.Sqrt( (nx - craterX) * (nx - craterX) + (ny - craterY) * (ny - craterY) );
if ( distance < craterRadius )
{
float rimStart = craterRadius * (1f - rimWidthRatio);
float rimEnd = craterRadius;
// Inside the pit
if ( distance < rimStart )
{
float pitFalloff = Math.Clamp( 1f - (distance / rimStart), 0f, 1f );
heightValue = baseTerrain - MathF.Pow( pitFalloff, slopeFalloff ) * craterDepth; // Smooth ramp to the center
}
// Raised rim
else if ( distance >= rimStart && distance < rimEnd )
{
float rimFalloff = Math.Clamp( (distance - rimStart) / (rimEnd - rimStart), 0f, 1f );
heightValue = baseTerrain + MathF.Pow( 1f - rimFalloff, slopeFalloff ) * rimHeight; // Rounded rim
}
// Reset terrain below the rim to prevent intersecting ridges
if ( distance >= rimEnd )
{
heightValue = baseTerrain;
}
// Exit the loop once the current crater is applied
break;
}
}
// Ensure height values are clamped to [0, 1]
heightValue = Math.Clamp( heightValue, 0, 1 );
return heightValue;
}
}
using Editor;
using Sandbox;
using Sandbox.UI;
namespace Panelize;
public class Properties : Widget
{
public static object SelectedObject { get; set; }
private static object currentSelectedObject { get; set; }
Layout editor;
public Properties(MainWindow mainWindow) : base(mainWindow)
{
WindowTitle = "Properties";
Layout = Layout.Column();
editor = Layout.AddRow( 1 );
SelectedObject = null;
currentSelectedObject = null;
}
[EditorEvent.Frame]
public void Frame()
{
//Log.Info( $"Select {SelectedObject}" );
if ( SelectedObject != currentSelectedObject )
{
Select( SelectedObject );
}
}
private void Select(object obj )
{
currentSelectedObject = obj;
editor.Clear(true);
var so = obj.GetSerialized();
Widget inspector = null;
if(obj is Panel p )
{
inspector = new PanelInspector( this, p, PanelEditorSession.Current );
}
else
{
inspector = InspectorWidget.Create( so );
}
if ( inspector.IsValid() )
{
editor.Add( inspector, 1 );
}
else
{
// Try CanEdit still..
// todo: Everything that should be an inspector should be an InspectorWidget
inspector = CanEditAttribute.CreateEditorForObject( obj );
if ( inspector.IsValid() )
{
editor.Add( inspector, 1 );
}
else
{
try
{
var sheet = new ControlSheet();
sheet.AddObject( so );
var scroller = new ScrollArea( this );
scroller.Canvas = new Widget();
scroller.Canvas.Layout = Layout.Column();
scroller.Canvas.VerticalSizeMode = SizeMode.CanGrow;
scroller.Canvas.HorizontalSizeMode = SizeMode.Flexible;
scroller.Canvas.Layout.Add( sheet );
scroller.Canvas.Layout.AddStretchCell();
editor.Add( scroller );
}
catch { }
}
}
}
}
using Sandbox;
using Sandbox.Diagnostics;
using Sandbox.UI;
using System.Linq;
namespace Panelize;
public class PanelBrowser : Widget
{
public float EntrySize { get; set; } = 200f;
public int Columns { get; set; } = 2;
GridLayout grid;
public PanelBrowser()
{
WindowTitle = "Panel List";
Layout = Layout.Column();
grid = Layout.AddLayout(Layout.Grid(), 10);
grid.Spacing = 5f;
BuildList();
}
private void BuildList()
{
grid.Clear( true );
var builderTypes = EditorTypeLibrary.GetTypes<PanelBuilder>();
int x = 0;
int y = 0;
foreach (var type in builderTypes)
{
if ( type.IsAbstract || type.IsGenericType ) continue;
if (x > Columns)
{
x = 0;
y++;
}
PanelPreviewWidget preview = new PanelPreviewWidget( type.Create<PanelBuilder>() )
{
FixedWidth = EntrySize,
FixedHeight = EntrySize
};
grid.AddCell(x, y, preview);
x++;
}
}
}
public class PanelPreviewWidget : Widget
{
PanelBuilder builder;
Color bgColor = Color.Gray;
//Drag drag;
public PanelPreviewWidget( PanelBuilder builder )
{
Assert.NotNull( builder );
this.builder = builder;
IsDraggable = true;
}
protected override void OnPaint()
{
Rect size = LocalRect;
Paint.SetBrushAndPen( bgColor );
Paint.DrawRect( size.Shrink(5f), 1f );
Paint.SetPen( Color.White );
Paint.SetFont( "Roboto", 24f, 800 );
Paint.DrawText( size, builder.Title );
}
bool dragging = false;
protected override void OnDragStart()
{
if ( dragging ) return;
dragging = true;
Log.Info( $"Drag Start!" );
}
protected override void OnMouseReleased( MouseEvent e )
{
if(e.LeftMouseButton && dragging )
{
Vector2 pos = e.WindowPosition;
Log.Info( $"Drag stop!" );
PanelEditorSession.Current.EditorWidget?.OnBuilderDrop( builder, e );
dragging = false;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Panelize;
public class Checkbox : Widget
{
public bool Value { get; set; }
public string IconEnabled { get; set; }
public string IconDisabled { get; set; }
public Action<bool> OnEdited;
public Checkbox( Widget parent = null, bool value = false, string iconEnabled = null, string iconDisabled = null, float? size = null) : base(parent)
{
Value = value;
IconEnabled = iconEnabled;
IconDisabled = iconDisabled;
Cursor = CursorShape.Finger;
MinimumWidth = size ?? ControlWidget.ControlRowHeight;
MinimumHeight = size ?? ControlWidget.ControlRowHeight;
HorizontalSizeMode = SizeMode.CanShrink;
VerticalSizeMode = SizeMode.CanShrink;
}
protected override void OnMouseClick( MouseEvent e )
{
if(e.LeftMouseButton)
{
Value = !Value;
OnEdited?.Invoke(Value);
}
}
protected override void OnPaint()
{
Paint.Antialiasing = true;
Paint.TextAntialiasing = true;
float alpha = (ReadOnly ? 0.5f : 1f);
Rect localRect = LocalRect;
Color color = Theme.Blue;
Rect rect = localRect.Shrink( 2 );
Paint.ClearPen();
Paint.SetBrush( ControlWidget.ControlColor.Lighten( ReadOnly ? 0.5f : 0f ).WithAlphaMultiplied( alpha ) );
Paint.DrawRect( rect, 2f );
if(Value)
{
Paint.SetPen( color.WithAlpha( 0.3f * alpha ), 1f );
Paint.SetBrush( color.WithAlpha( 0.2f * alpha ) );
Paint.DrawRect( rect, 2f );
Paint.SetPen( color.WithAlphaMultiplied( 0.5f ) );
Paint.DrawIcon( rect, IconEnabled ?? "done", 13f );
}
else if ( IconDisabled != null )
{
Paint.SetPen( Theme.Grey.WithAlphaMultiplied( 0.5f ) );
Paint.DrawIcon( rect, IconDisabled, 13f );
}
if ( IsUnderMouse && !ReadOnly )
{
Paint.SetPen( color.WithAlpha(0.5f * alpha), 1);
Paint.ClearBrush();
Paint.DrawRect( in rect, 1f );
}
}
}
namespace RbxlReader.DataTypes;
public class NumberSequenceKeypoint {
public float Time;
public float Value;
public float Envelope;
}
public class NumberSequence {
public NumberSequenceKeypoint[] Keypoints;
public NumberSequence(NumberSequenceKeypoint[] points) {
Keypoints = points;
}
}namespace RbxlReader.DataTypes;
public class UDim {
public float Scale;
public int Offset;
public UDim() {}
public UDim(float scale, int offset) {
Scale = scale;
Offset = offset;
}
}using System;
using System.Threading.Tasks;
using Editor;
using Sandbox;
namespace YAAI;
public sealed class YaaiWindow : Window
{
public Task RunningTask;
private bool IsRunning = false;
public readonly int MinimumSpeed = 10;
public readonly int MaximumSpeed = 30;
private Random random = new Random();
private float RandomSpeed => random.Float( MinimumSpeed, MaximumSpeed );
public YaaiWindow()
{
WindowTitle = "YAAI";
Width = 250;
Height = 200;
HasMaximizeButton = false;
DeleteOnClose = true;
WindowFlags = WindowFlags.WithFlag( WindowFlags.WindowStaysOnTopHint, true );
WindowFlags = WindowFlags.WithFlag( WindowFlags.MinimizeButton, false);
new YaaiWidget(this );
Show();
RunningTask = Move();
}
protected override bool OnClose()
{
// Dispose of Task
IsRunning = false;
RunningTask.Wait();
RunningTask.Dispose();
YaaiManager.OnWindowClose( this );
return base.OnClose();
}
async Task Move()
{
IsRunning = true;
bool moveDown = random.Int( 0,1 ) == 1;
bool moveRight = random.Int( 0,1 ) == 1;
float Speed = RandomSpeed;
while ( IsWindow && IsRunning )
{
float nx = moveRight ? Position.x + Speed/2 : Position.x - Speed/2;
float ny = moveDown ? Position.y + Speed : Position.y - Speed;
bool shouldChangeSpeed = false;
if ( Position.x + Width > ScreenGeometry.Width )
{
shouldChangeSpeed = true;
moveRight = false;
}
else if ( Position.x < 0)
{
shouldChangeSpeed = true;
moveRight = true;
}
if ( Position.y + Height > ScreenGeometry.Height )
{
shouldChangeSpeed = true;
moveDown = false;
}
else if ( Position.y < 0)
{
shouldChangeSpeed = true;
moveDown = true;
}
if ( shouldChangeSpeed )
Speed = RandomSpeed;
Position = Position.WithX( nx ).WithY( ny );
await Task.Delay( 25 );
}
}
}
using Sandbox;
using System;
public class EditorTheme
{
[Group( "Window" )] public Color TabBackground { get; set; } = new Color( 0.231f, 0.231f, 0.231f );
[Group( "Window" )] public Color TabBarBackground { get; set; } = new Color( 0.141f, 0.141f, 0.141f );
[Group( "Window" )] public Color TabInactiveBackground { get; set; } = new Color( 0.141f, 0.141f, 0.141f );
[Group( "Window" )] public Color SurfaceBackground { get; set; } = new Color( 0.231f, 0.231f, 0.231f );
[Group( "Window" )] public Color SurfaceLightBackground { get; set; } = new Color( 0.412f, 0.412f, 0.412f );
[Group( "Window" )] public Color SidebarBackground { get; set; } = new Color( 0.141f, 0.141f, 0.141f );
[Group( "Window" )] public Color WindowBackground { get; set; } = new Color( 0.094f, 0.094f, 0.094f );
[Group( "Window" )] public Color WidgetBackground { get; set; } = new Color( 0.141f, 0.141f, 0.141f );
[Group( "Window" )] public Color ControlBackground { get; set; } = new Color( 0.094f, 0.094f, 0.094f );
[Group( "Window" )] public Color ButtonBackground { get; set; } = new Color( 0.231f, 0.231f, 0.231f );
[Group( "Window" )] public Color SelectedBackground { get; set; } = new Color( 0.502f, 0.502f, 0.502f );
[Group( "Window" )] public Color StatusBarBackground { get; set; } = new Color( 0.141f, 0.141f, 0.141f );
[Group( "Text" )] public Color Text { get; set; } = new Color( 1.0f, 1.0f, 1.0f );
[Group( "Text" )] public Color TextControl { get; set; } = new Color( 1.0f, 1.0f, 1.0f );
[Group( "Text" )] public Color TextLight { get; set; } = new Color( 0.62f, 0.62f, 0.62f );
[Group( "Text" )] public Color TextWidget { get; set; } = new Color( 1.0f, 1.0f, 1.0f );
[Group( "Text" )] public Color TextButton { get; set; } = new Color( 1.0f, 1.0f, 1.0f );
[Group( "Text" )] public Color TextSelected { get; set; } = new Color( 0.4f, 0.639f, 1.0f );
[Group( "Text" )] public Color TextLink { get; set; } = new Color( 0.4f, 0.639f, 1.0f );
[Group( "Text" )] public Color TextHighlight { get; set; } = new Color( 0.4f, 0.639f, 1.0f );
[Group( "Text" )] public Color TextDisabled { get; set; } = new Color( 1.0f, 1.0f, 1.0f, 0.47f );
[Group( "Text" )] public Color TextDark { get; set; } = new Color( 0.0f, 0.0f, 0.0f );
[Group( "Window" )] public Color Border { get; set; } = new Color( 0.322f, 0.322f, 0.322f );
[Group( "Window" )] public Color BorderLight { get; set; } = new Color( 0.412f, 0.412f, 0.412f );
[Group( "Window" )] public Color BorderButton { get; set; } = new Color( 0.412f, 0.412f, 0.412f );
[Group( "Window" )] public Color Shadow { get; set; } = new Color( 0.141f, 0.141f, 0.141f );
[Group( "Window" )] public Color Primary { get; set; } = new Color( 0.353f, 0.553f, 0.922f );
[Group( "Window" )] public Color Overlay { get; set; } = new Color( 0.141f, 0.141f, 0.141f );
[Group( "Window" )] public Color MultipleValues { get; set; } = new Color( 0.502f, 0.502f, 0.502f );
[Group( "Window" )] public Color Highlight { get; set; } = new Color( 0.298f, 0.478f, 0.749f );
[Group( "Checkbox" )] public Color ToggleEnabled { get; set; } = new Color( 0.353f, 0.922f, 0.361f );
[Group( "Checkbox" )] public Color ToggleDisabled { get; set; } = new Color( 0.337f, 0.431f, 0.337f );
[Group( "Window" )] public Color Base { get; set; } = new Color(0.125f, 0.125f, 0.125f);
[Group( "Window" )] public Color BaseAlt { get; set; } = new Color(0.164f,0.164f,0.164f);
[Group( "Colors?" )] public Color Blue { get; set; } = new Color( 0.353f, 0.553f, 0.922f );
[Group( "Colors?" )] public Color Green { get; set; } = new Color( 0.690f, 0.89f, 0.302f );
[Group( "Colors?" )] public Color Red { get; set; } = new Color( 0.984f, 0.353f, 0.353f );
[Group( "Colors?" )] public Color Yellow { get; set; } = new Color( 0.902f, 0.859f, 0.455f );
[Group( "Colors?" )] public Color Pink { get; set; } = new Color( 0.874f, 0.569f, 0.580f );
[Group( "Files" )] public Color Prefab { get; set; } = new Color( 0.353f, 0.553f, 0.922f );
[Group( "Files" )] public Color Folder { get; set; } = new Color( 0.902f, 0.859f, 0.455f );
[Group( "Layout" )] public int RowHeight { get; set; } = 22;
[Group( "Layout" )] public int ControlHeight { get; set; } = 22;
[Group( "Layout" )] public int ControlRadius { get; set; } = 4;
[FontName][Group( "Text" )] public string HeadingFont { get; set; } = "Inter";
[FontName][Group( "Text" )] public string DefaultFont { get; set; } = "Inter";
public EditorTheme()
{ }
public static EditorTheme ShallowCopy( EditorTheme source )
{
if ( source == null )
throw new ArgumentNullException( nameof( source ) );
EditorTheme copy = new EditorTheme();
var properties = typeof( EditorTheme ).GetProperties();
foreach ( var prop in properties )
{
if ( prop.CanRead && prop.CanWrite )
{
var value = prop.GetValue( source, null );
prop.SetValue( copy, value, null );
}
}
return copy;
}
}
using System.Text;
using System.Text.Json.Serialization;
using Sandbox.MovieMaker;
namespace Editor.MovieMaker;
#nullable enable
/// <summary>
/// Describes a translation and scale that can be applied to <see cref="MovieTime"/>s.
/// </summary>
/// <param name="Translation">Time offset to apply.</param>
/// <param name="Scale">Time scale to apply.</param>
public readonly record struct MovieTransform(
[property: JsonIgnore( Condition = JsonIgnoreCondition.WhenWritingDefault )]
MovieTime Translation = default,
[property: JsonIgnore( Condition = JsonIgnoreCondition.WhenWritingDefault )]
MovieTimeScale Scale = default )
{
public static MovieTransform Identity => default;
[JsonIgnore]
public MovieTransform Inverse => new( Scale.Inverse * -Translation, Scale.Inverse );
public static MovieTime operator *( MovieTransform transform, MovieTime time ) =>
time * transform.Scale + transform.Translation;
public static MovieTimeRange operator *( MovieTransform transform, MovieTimeRange timeRange ) =>
(transform * timeRange.Start, transform * timeRange.End);
public static MovieTransform operator *( MovieTransform a, MovieTransform b ) =>
new( a * b.Translation, a.Scale * b.Scale );
public static MovieTransform operator *( MovieTimeScale a, MovieTransform b ) =>
new( a * b.Translation, a * b.Scale );
public static MovieTransform operator +( MovieTransform transform, MovieTime translation ) =>
transform with { Translation = transform.Translation + translation };
public static MovieTransform FromTo( MovieTimeRange from, MovieTimeRange to )
{
var scale = MovieTimeScale.FromDurationChange( from.Duration, to.Duration );
return scale * new MovieTransform( -from.Start ) + to.Start;
}
private bool PrintMembers( StringBuilder builder )
{
if ( this == Identity )
{
builder.Append( "Identity" );
return true;
}
if ( !Translation.IsZero )
{
builder.Append( $"{nameof(Translation)} = {Translation}" );
}
if ( Scale != MovieTimeScale.Identity )
{
if ( !Translation.IsZero )
{
builder.Append( ", " );
}
builder.Append( $"{nameof(Scale)} = {Scale}" );
}
return true;
}
}
namespace Editor.MovieMaker.BlockDisplays;
#nullable enable
public abstract class ThumbnailBlockItem<T> : PropertyBlockItem<T>
{
protected abstract Pixmap? GetThumbnail();
protected override void OnPaint()
{
base.OnPaint();
if ( GetThumbnail() is { } thumb )
{
Paint.Draw( LocalRect.Contain( Height ), thumb, 0.5f );
}
}
}
public sealed class ResourceBlockItem<T> : ThumbnailBlockItem<T>
where T : Resource
{
protected override Pixmap? GetThumbnail() => Block.GetValue( Block.TimeRange.Start ) is { ResourcePath: { } path }
? AssetSystem.FindByPath( path )?.GetAssetThumb()
: null;
}
using System.Linq;
using Sandbox.MovieMaker;
namespace Editor.MovieMaker;
#nullable enable
// [MovieModification( "Stretch", Icon = "width_full" )]
file sealed class StretchModification() : PerTrackModification<StretchOptions>( StretchOptions.Default, true )
{
public override void Start( TimeSelection selection )
{
Options = Options with { SourceDuration = selection.TotalTimeRange.Duration };
}
protected override ITrackModification<TValue> OnCreateModification<TValue>( IPropertyTrack<TValue> track ) =>
new StretchTrackModification<TValue>();
}
file sealed record StretchOptions( MovieTime SourceDuration = default ) : IModificationOptions
{
public static StretchOptions Default => new();
}
file sealed class StretchTrackModification<T> : ITrackModification<T, StretchOptions>
{
public IEnumerable<PropertyBlock<T>> Apply( IReadOnlyList<PropertyBlock<T>> original,
TimeSelection selection, StretchOptions options )
{
return options.SourceDuration > 0 && options.SourceDuration != selection.TotalTimeRange
? original.Select( x => Stretch( x, selection, options ) )
: original;
}
private PropertyBlock<T> Stretch( PropertyBlock<T> original, TimeSelection selection, StretchOptions options )
{
var signal = original.Signal.SlidingStretch( options.SourceDuration, selection );
return new PropertyBlock<T>( signal, original.TimeRange );
}
}
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Json.Serialization;
using Sandbox.MovieMaker.Compiled;
using Sandbox.MovieMaker;
namespace Editor.MovieMaker;
#nullable enable
[JsonDiscriminator( "Source" )]
[method: JsonConstructor]
file sealed record CompiledSignal<T>( ProjectSourceClip Source, int TrackIndex, int BlockIndex,
[property: JsonIgnore( Condition = JsonIgnoreCondition.WhenWritingDefault )] MovieTransform Transform = default,
[property: JsonIgnore( Condition = JsonIgnoreCondition.WhenWritingDefault )] MovieTime SmoothingSize = default ) : PropertySignal<T>
{
private IReadOnlyList<T>? _samples;
private CompiledSampleBlock<T>? _block;
private CompiledSampleBlock<T> Block => _block ??= (CompiledSampleBlock<T>)((CompiledPropertyTrack<T>)Source.Clip.Tracks[TrackIndex]).Blocks[BlockIndex];
private IReadOnlyList<T> Samples => _samples ??= Block.Resample( Block.SampleRate, SmoothingSize, _interpolator );
public CompiledSignal( CompiledSignal<T> copy )
: base( copy )
{
Source = copy.Source;
TrackIndex = copy.TrackIndex;
BlockIndex = copy.BlockIndex;
Transform = copy.Transform;
SmoothingSize = copy.SmoothingSize;
_samples = null;
_block = null;
}
private MovieTime GetLocalTime( MovieTime time ) =>
(Transform.Inverse * time).Clamp( Block.TimeRange ) - Block.TimeRange.Start - Block.Offset;
public override T GetValue( MovieTime time ) =>
Samples.Sample( GetLocalTime( time ), Block.SampleRate, _interpolator );
protected override PropertySignal<T> OnTransform( MovieTransform transform ) =>
this with { Transform = transform * Transform };
protected override PropertySignal<T> OnReduce( MovieTime? start, MovieTime? end )
{
if ( start is { } s && GetLocalTime( s ) >= Block.TimeRange.Duration ) return Block.GetValue( Block.TimeRange.End );
if ( end is { } e && GetLocalTime( e ) <= 0d ) return Block.GetValue( Block.TimeRange.Start );
return this;
}
protected override PropertySignal<T> OnSmooth( MovieTime size ) =>
_interpolator is null ? this : this with { SmoothingSize = size };
public override IEnumerable<MovieTimeRange> GetPaintHints( MovieTimeRange timeRange )
{
if ( timeRange.Intersect( Transform * Block.TimeRange ) is { } intersection )
{
return [intersection];
}
return [];
}
protected override bool PrintMembers( StringBuilder builder )
{
builder.Append( $"Source = {Source}, " );
builder.Append( $"Block = {Block.TimeRange}" );
if ( Transform != MovieTransform.Identity )
{
builder.Append( $", Transform = {Transform}" );
}
if ( SmoothingSize != default )
{
builder.Append( $", SmoothingSize = {SmoothingSize}" );
}
return true;
}
public override int GetHashCode()
{
return HashCode.Combine( Source, TrackIndex, BlockIndex, Transform, SmoothingSize );
}
public bool Equals( CompiledSignal<T>? other )
{
if ( other is null ) return false;
return Source.Equals( other.Source )
&& TrackIndex == other.TrackIndex
&& BlockIndex == other.BlockIndex
&& Transform == other.Transform
&& SmoothingSize == other.SmoothingSize;
}
private static readonly IInterpolator<T>? _interpolator = Interpolator.GetDefault<T>();
}
file sealed class ResampleCache<T>
{
private readonly record struct Key( int SampleRate, MovieTime SmoothingSize );
#pragma warning disable SB3000
[SkipHotload]
private static ConditionalWeakTable<CompiledSampleBlock<T>, Dictionary<Key, WeakReference<T[]>>> Cache { get; } = new();
#pragma warning restore SB3000
public static T[]? Get( CompiledSampleBlock<T> block, int sampleRate, MovieTime smoothingSize )
{
return Cache.TryGetValue( block, out var dict )
&& dict.TryGetValue( new( sampleRate, smoothingSize ), out var weakRef )
&& weakRef.TryGetTarget( out var array ) ? array : null;
}
public static void Set( CompiledSampleBlock<T> block, int sampleRate, MovieTime smoothingSize, T[] array )
{
if ( !Cache.TryGetValue( block, out var dict ) )
{
Cache.TryAdd( block, dict = new Dictionary<Key, WeakReference<T[]>>() );
}
dict[new( sampleRate, smoothingSize )] = new WeakReference<T[]>( array );
}
}
partial class PropertySignalExtensions
{
public static IReadOnlyList<PropertyBlock<T>> AsBlocks<T>( this ProjectSourceClip source, IProjectPropertyTrack track )
{
var (refTrack, propertyPath) = track.GetPath();
if ( source.Clip.GetProperty<T>( refTrack.Id, propertyPath ) is not { } matchingTrack )
{
return [];
}
var trackIndex = source.Clip.Tracks.IndexOf( matchingTrack );
return matchingTrack.Blocks
.Select( (x, i) => new PropertyBlock<T>( x switch
{
CompiledConstantBlock<T> constant => constant.Value,
CompiledSampleBlock<T> => new CompiledSignal<T>( source, trackIndex, i ),
_ => throw new NotImplementedException()
}, x.TimeRange ) )
.ToImmutableArray();
}
public static IReadOnlyList<T> Resample<T>( this CompiledSampleBlock<T> source, int sampleRate,
MovieTime smoothingSize, IInterpolator<T>? interpolator )
{
if ( interpolator is null )
{
smoothingSize = default;
}
if ( sampleRate == source.SampleRate && smoothingSize <= 0d )
{
return source.Samples;
}
if ( ResampleCache<T>.Get( source, sampleRate, smoothingSize ) is { } cached )
{
return cached;
}
var sampleCount = sampleRate == source.SampleRate
? source.Samples.Length
: source.TimeRange.Duration.GetFrameCount( sampleRate );
var samples = new T[sampleCount];
var sourceSamples = source.Samples;
if ( sampleRate == source.SampleRate )
{
sourceSamples.CopyTo( samples );
}
else
{
for ( var i = 0; i < sampleCount; i++ )
{
var t = MovieTime.FromFrames( i, sampleRate );
samples[i] = sourceSamples.Sample( t, sampleRate, interpolator );
}
}
if ( smoothingSize <= 0d || interpolator is null )
{
ResampleCache<T>.Set( source, sampleRate, smoothingSize, samples );
return samples;
}
var smoothingPasses = smoothingSize.GetFrameCount( sampleRate );
T[] back = samples, front = [..samples];
for ( var pass = 0; pass < smoothingPasses; pass++ )
{
for ( var i = 0; i < sampleCount; i++ )
{
var prev = back[Math.Max( 0, i - 1 )];
var curr = back[i];
var next = back[Math.Min( sampleCount - 1, i + 1 )];
var prevCurr = interpolator.Interpolate( prev, curr, 0.5f );
var currNext = interpolator.Interpolate( curr, next, 0.5f );
front[i] = interpolator.Interpolate( prevCurr, currNext, 0.5f );
}
(back, front) = (front, back);
}
ResampleCache<T>.Set( source, sampleRate, smoothingSize, back );
return back;
}
}
using System.Diagnostics.CodeAnalysis;
using Sandbox.MovieMaker;
namespace Editor.MovieMaker.BlockDisplays;
#nullable enable
partial class BlockItem
{
public static BlockItem Create( TimelineTrack parent, ITrackBlock block, MovieTime offset )
{
var blockType = block.GetType();
var propertyType = (block as IPropertyBlock)?.PropertyType;
var inst = (BlockItem)Activator.CreateInstance( GetBlockItemType( blockType, propertyType ) )!;
try
{
inst.Initialize( parent, block, offset );
}
catch ( Exception ex )
{
Log.Error( ex );
BlockItemTypeCache[blockType] = typeof(DefaultBlockItem);
inst = new DefaultBlockItem();
inst.Initialize( parent, block, offset );
}
return inst;
}
[SkipHotload] private static Dictionary<Type, Type> BlockItemTypeCache { get; } = new();
[EditorEvent.Hotload]
private static void OnHotload()
{
BlockItemTypeCache.Clear();
}
private static Type GetBlockItemType( Type targetBlockType, Type? propertyType )
{
if ( BlockItemTypeCache.TryGetValue( targetBlockType, out var blockItemType ) ) return blockItemType;
var bestBlockItemType = typeof(DefaultBlockItem);
var bestScore = int.MaxValue;
foreach ( var typeDesc in EditorTypeLibrary.GetTypes<BlockItem>() )
{
var type = typeDesc.TargetType;
var baseDistance = 0;
if ( type.IsAbstract ) continue;
if ( type.IsGenericType )
{
if ( propertyType is null ) continue;
if ( !TryMakeGenericType( type, propertyType, out var newType ) )
{
continue;
}
type = newType;
baseDistance = 1;
}
var score = baseDistance + GetScore( type, targetBlockType, propertyType );
if ( score > bestScore ) continue;
bestBlockItemType = type;
bestScore = score;
}
BlockItemTypeCache[targetBlockType] = bestBlockItemType;
return bestBlockItemType;
}
private static bool TryMakeGenericType( Type trackPreviewType, Type propertyType,
[NotNullWhen( true )] out Type? newTrackPreviewType )
{
newTrackPreviewType = null;
if ( trackPreviewType.GetGenericArguments().Length != 1 )
{
return false;
}
try
{
newTrackPreviewType = trackPreviewType.MakeGenericType( propertyType );
return true;
}
catch
{
return false;
}
}
private static int GetScore( Type blockItemType, Type targetBlockType, Type? propertyType )
{
var score = int.MaxValue;
foreach ( var iFace in blockItemType.GetInterfaces() )
{
if ( !iFace.IsConstructedGenericType ) continue;
if ( iFace.GetGenericTypeDefinition() == typeof(IBlockItem<>) )
{
var iFaceTargetType = iFace.GetGenericArguments()[0];
score = Math.Min( score, GetDistance( iFaceTargetType, targetBlockType ) );
}
if ( iFace.GetGenericTypeDefinition() == typeof(IPropertyBlockItem<>) && propertyType != null )
{
var iFaceTargetType = iFace.GetGenericArguments()[0];
score = Math.Min( score, GetDistance( iFaceTargetType, propertyType ) );
}
}
return score;
}
private static int GetDistance( Type baseType, Type? derivedType )
{
if ( !baseType.IsAssignableFrom( derivedType ) ) return int.MaxValue;
if ( baseType.IsInterface && !derivedType.IsInterface ) return 1;
var distance = 0;
while ( baseType != derivedType && derivedType != null )
{
derivedType = derivedType.BaseType;
distance++;
}
return distance;
}
}
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Sandbox.MovieMaker;
using Sandbox.MovieMaker.Compiled;
namespace Editor.MovieMaker;
#nullable enable
public partial interface IProjectTrack : ITrack, IComparable<IProjectTrack>
{
Guid Id { get; }
MovieProject Project { get; }
new IProjectTrack? Parent { get; }
IReadOnlyList<IProjectTrack> Children { get; }
bool IsEmpty { get; }
int Order { get; }
void Remove();
IProjectTrack? GetChild( string name );
ICompiledTrack Compile( ICompiledTrack? compiledParent, bool headerOnly );
ITrack? ITrack.Parent => Parent;
IEnumerable<MovieResource> References { get; }
int IComparable<IProjectTrack>.CompareTo( IProjectTrack? other )
{
if ( ReferenceEquals( this, other ) )
{
return 0;
}
if ( other is null )
{
return 1;
}
var orderComparison = Order.CompareTo( other.Order );
if ( orderComparison != 0 ) return orderComparison;
return string.Compare( Name, other.Name, StringComparison.Ordinal );
}
}
internal interface IProjectTrackInternal : IProjectTrack
{
new IProjectTrackInternal? Parent { get; set; }
void AddChild( IProjectTrackInternal child );
void RemoveChild( IProjectTrackInternal child );
}
public abstract partial class ProjectTrack<T>( MovieProject project, Guid id, string name ) : IProjectTrackInternal
{
private readonly List<IProjectTrack> _children = new();
private bool _childrenChanged;
public MovieProject Project { get; } = project;
public Guid Id { get; } = id;
public string Name { get; set; } = name;
public Type TargetType { get; } = typeof(T);
public IProjectTrack? Parent { get; private set; }
public virtual IEnumerable<MovieResource> References => [];
public virtual bool IsEmpty => Children.Count == 0;
public virtual int Order => 0;
public IReadOnlyList<IProjectTrack> Children
{
get
{
UpdateChildren();
return _children;
}
}
public void Remove() => Project.RemoveTrackInternal( this );
public IProjectTrack? GetChild( string name ) => Children.FirstOrDefault( x => x.Name == name );
public abstract ICompiledTrack Compile( ICompiledTrack? compiledParent, bool headerOnly );
IProjectTrackInternal? IProjectTrackInternal.Parent
{
get => (IProjectTrackInternal?)Parent;
set => Parent = value;
}
void IProjectTrackInternal.AddChild( IProjectTrackInternal child )
{
if ( child.Parent != null )
{
throw new ArgumentException( "Track already has a parent!", nameof(child) );
}
child.Parent = this;
_children.Add( child );
_childrenChanged = true;
}
void IProjectTrackInternal.RemoveChild( IProjectTrackInternal child )
{
_children.Remove( child );
_childrenChanged = true;
}
private void UpdateChildren()
{
if ( !_childrenChanged ) return;
_childrenChanged = false;
_children.Sort();
}
}
public partial interface IProjectReferenceTrack : IProjectTrack, IReferenceTrack
{
public static IProjectReferenceTrack Create( MovieProject project, Guid id, string name, Type targetType )
{
var trackType = typeof(ProjectReferenceTrack<>).MakeGenericType( targetType );
return (IProjectReferenceTrack)Activator.CreateInstance( trackType, project, id, name )!;
}
new ProjectReferenceTrack<GameObject>? Parent { get; }
new Guid Id { get; }
new Guid? ReferenceId { get; set; }
IReferenceTrack<GameObject>? IReferenceTrack.Parent => Parent;
IProjectTrack? IProjectTrack.Parent => Parent;
Guid IReferenceTrack.Id => Id;
Guid IProjectTrack.Id => Id;
Guid? IReferenceTrack.ReferenceId => ReferenceId;
}
public partial class ProjectReferenceTrack<T>( MovieProject project, Guid id, string name )
: ProjectTrack<T>( project, id, name ), IProjectReferenceTrack, IReferenceTrack<T>
where T : class, IValid
{
public override int Order => -1000;
public new ProjectReferenceTrack<GameObject>? Parent => (ProjectReferenceTrack<GameObject>?)base.Parent;
public Guid? ReferenceId { get; set; }
public override ICompiledTrack Compile( ICompiledTrack? compiledParent, bool headerOnly ) =>
new CompiledReferenceTrack<T>( Id, Name, (CompiledReferenceTrack<GameObject>)compiledParent!, ReferenceId );
ITrack? ITrack.Parent => Parent;
}
public interface IProjectBlockTrack : IProjectTrack
{
MovieTimeRange TimeRange { get; }
IReadOnlyList<ITrackBlock> Blocks { get; }
}
public partial interface IProjectPropertyTrack : IPropertyTrack, IProjectBlockTrack
{
public static IProjectPropertyTrack Create( MovieProject project, Guid id, string name, Type targetType )
{
var trackType = typeof(ProjectPropertyTrack<>).MakeGenericType( targetType );
return (IProjectPropertyTrack)Activator.CreateInstance( trackType, project, id, name )!;
}
new IProjectTrack? Parent { get; }
new IReadOnlyList<IProjectPropertyBlock> Blocks { get; }
ITrack IPropertyTrack.Parent => Parent!;
/// <summary>
/// Add empty space from the start of <paramref name="timeRange"/>, with
/// the duration of <paramref name="timeRange"/>. Will split any blocks that
/// span the start time.
/// </summary>
bool Insert( MovieTimeRange timeRange );
/// <summary>
/// Remove the given <paramref name="timeRange"/>, then collapse the removed
/// time range so any blocks after the end of the range will start earlier.
/// </summary>
bool Remove( MovieTimeRange timeRange );
/// <summary>
/// Remove any blocks within the <paramref name="timeRange"/>, splitting any
/// blocks that span the start or end. This doesn't shift any blocks, so will
/// leave an empty region of time.
/// </summary>
bool Clear( MovieTimeRange timeRange );
/// <summary>
/// Adds a block, replacing any blocks that overlap its time range.
/// This will split any blocks that partially overlap.
/// </summary>
bool Add( MovieTimeRange timeRange, IPropertySignal signal );
/// <summary>
/// Adds a block, replacing any blocks that overlap its time range.
/// This will split any blocks that partially overlap.
/// </summary>
bool Add( IProjectPropertyBlock block );
bool AddRange( IEnumerable<IProjectPropertyBlock> blocks );
void SetBlocks( IReadOnlyList<IProjectPropertyBlock> blocks );
/// <summary>
/// Copies blocks that overlap the given <paramref name="timeRange"/> and returns
/// the copies.
/// </summary>
IReadOnlyList<IProjectPropertyBlock> Slice( MovieTimeRange timeRange );
IReadOnlyList<IProjectPropertyBlock> CreateSourceBlocks( ProjectSourceClip source );
IReadOnlyList<ITrackBlock> IProjectBlockTrack.Blocks => Blocks;
IProjectTrack? IProjectTrack.Parent => Parent;
ITrack? ITrack.Parent => Parent;
}
public sealed partial class ProjectPropertyTrack<T>( MovieProject project, Guid id, string name )
: ProjectTrack<T>( project, id, name ), IProjectPropertyTrack, IPropertyTrack<T>
{
private readonly List<PropertyBlock<T>> _blocks = new();
private bool _blocksChanged;
public MovieTimeRange TimeRange => (0d, Blocks.Select( x => x.TimeRange.End )
.DefaultIfEmpty()
.Max());
public IReadOnlyList<PropertyBlock<T>> Blocks
{
get
{
UpdateBlocks();
return _blocks;
}
}
public override bool IsEmpty => base.IsEmpty && Blocks.Count == 0;
IReadOnlyList<IProjectPropertyBlock> IProjectPropertyTrack.Blocks => Blocks;
public override ICompiledTrack Compile( ICompiledTrack? compiledParent, bool headerOnly )
{
var compiled = new CompiledPropertyTrack<T>( Name, compiledParent!, [] );
if ( headerOnly ) return compiled;
return compiled with { Blocks = [..Blocks.Select( x => x.Compile( this ) )] };
}
public bool TryGetValue( MovieTime time, [MaybeNullWhen( false )] out T value )
{
if ( Blocks.GetBlock( time ) is { } block )
{
value = block.GetValue( time );
return true;
}
value = default;
return false;
}
bool IPropertyTrack.TryGetValue( MovieTime time, out object? value )
{
if ( TryGetValue( time, out var val ) )
{
value = val;
return true;
}
value = null;
return false;
}
public bool Insert( MovieTimeRange timeRange )
{
return Clear( timeRange.Start )
| Shift( timeRange.Start, timeRange.Duration );
}
public bool Remove( MovieTimeRange timeRange )
{
return Clear( timeRange )
| Shift( timeRange.End, -timeRange.Duration );
}
private bool Shift( MovieTime from, MovieTime offset )
{
var changed = false;
for ( var i = 0; i < _blocks.Count; ++i )
{
var block = _blocks[i];
if ( block.TimeRange.Start >= from )
{
_blocks[i] += offset;
_blocksChanged = changed = true;
}
}
return changed;
}
public bool Clear( MovieTimeRange timeRange )
{
var overlaps = this.GetBlocks( timeRange ).ToArray();
if ( overlaps.Length == 0 ) return false;
foreach ( var overlap in overlaps )
{
_blocks.Remove( overlap );
if ( overlap.TimeRange.Start < timeRange.Start )
{
_blocks.Add( overlap.Slice( (overlap.TimeRange.Start, timeRange.Start) )! );
}
if ( overlap.TimeRange.End > timeRange.End )
{
_blocks.Add( overlap.Slice( (timeRange.End, overlap.TimeRange.End) )! );
}
}
_blocksChanged = true;
return true;
}
public bool Add( MovieTimeRange timeRange, PropertySignal<T> signal )
{
if ( timeRange.End < 0 ) return false;
timeRange = timeRange.ClampStart( 0 );
// Remove any overlaps
Clear( timeRange );
// Add to the end of _blocks, it'll get sorted later
_blocksChanged = true;
_blocks.AddRange( signal.AsBlocks( timeRange ) );
return true;
}
public bool Add( PropertyBlock<T> block )
{
if ( block.TimeRange.Start < 0 )
{
throw new ArgumentException( "Block can't have negative start time." );
}
if ( _blocks.Any( x => x.TimeRange.Contains( block.TimeRange ) && x.Signal.Equals( block.Signal ) ) )
{
return false;
}
Clear( block.TimeRange );
_blocksChanged = true;
_blocks.Add( block );
return true;
}
public bool AddRange( IEnumerable<PropertyBlock<T>> blocks )
{
var changed = false;
foreach ( var block in blocks )
{
changed |= Add( block );
}
return changed;
}
bool IProjectPropertyTrack.Add( MovieTimeRange timeRange, IPropertySignal signal ) =>
Add( timeRange, (PropertySignal<T>)signal );
bool IProjectPropertyTrack.Add( IProjectPropertyBlock block ) => Add( (PropertyBlock<T>)block );
bool IProjectPropertyTrack.AddRange( IEnumerable<IProjectPropertyBlock> blocks ) =>
AddRange( blocks.Cast<PropertyBlock<T>>() );
public void SetBlocks( IReadOnlyList<IProjectPropertyBlock> blocks )
{
_blocksChanged = true;
_blocks.Clear();
_blocks.AddRange( blocks.Cast<PropertyBlock<T>>() );
}
public IReadOnlyList<PropertyBlock<T>> Slice( MovieTimeRange timeRange )
{
return Blocks
.Where( x => x.TimeRange.Intersect( timeRange ) is { } intersection && (!intersection.IsEmpty || timeRange.IsEmpty) )
.Select( x => x.Slice( timeRange ) )
.OfType<PropertyBlock<T>>()
.ToImmutableArray();
}
IReadOnlyList<IProjectPropertyBlock> IProjectPropertyTrack.Slice( MovieTimeRange timeRange ) => Slice( timeRange );
IReadOnlyList<IProjectPropertyBlock> IProjectPropertyTrack.CreateSourceBlocks( ProjectSourceClip source ) =>
source.AsBlocks<T>( this );
private void UpdateBlocks()
{
if ( !_blocksChanged ) return;
_blocksChanged = false;
// Sort by time
_blocks.Sort( ( a, b ) => a.TimeRange.Start.CompareTo( b.TimeRange.Start ) );
// Merge touching blocks that have identical values at their interface
var comparer = EqualityComparer<T>.Default;
for ( var i = _blocks.Count - 2; i >= 0; --i )
{
var prev = _blocks[i];
var next = _blocks[i + 1];
if ( prev.TimeRange.End != next.TimeRange.Start ) continue;
var prevValue = prev.GetValue( prev.TimeRange.End );
var nextValue = next.GetValue( next.TimeRange.Start );
if ( !comparer.Equals( prevValue, nextValue ) )
{
continue;
}
var combinedTimeRange = prev.TimeRange.Union( next.TimeRange );
var combinedSignal = prev.Signal.HardCut( next.Signal, prev.TimeRange.End ).Reduce( combinedTimeRange );
_blocks[i] = new PropertyBlock<T>( combinedSignal, combinedTimeRange );
_blocks.RemoveAt( i + 1 );
}
}
}
using System.Linq;
using System.Text.Json.Serialization;
using Sandbox.MovieMaker;
namespace Editor.MovieMaker;
#nullable enable
public abstract partial record PropertySignal : IPropertySignal
{
[JsonIgnore]
public abstract Type PropertyType { get; }
protected PropertySignal( PropertySignal copy )
{
// Empty so any lazily computed fields aren't copied
}
object? IPropertySignal.GetValue( MovieTime time ) => OnGetValue( time );
protected abstract object? OnGetValue( MovieTime time );
/// <summary>
/// Gets time ranges within the given <paramref name="timeRange"/> that have changing values.
/// For painting in the timeline.
/// </summary>
public virtual IEnumerable<MovieTimeRange> GetPaintHints( MovieTimeRange timeRange ) => [timeRange];
}
/// <summary>
/// A <see cref="IPropertySignal{T}"/> that can be composed with <see cref="PropertyOperation{T}"/>s,
/// and stored in a <see cref="IPropertyBlock{T}"/>.
/// </summary>
public abstract partial record PropertySignal<T>() : PropertySignal, IPropertySignal<T>
{
[JsonIgnore]
public sealed override Type PropertyType => typeof(T);
protected PropertySignal( PropertySignal<T> copy )
: base( copy )
{
// Empty so any lazily computed fields aren't copied
}
public abstract T GetValue( MovieTime time );
protected sealed override object? OnGetValue( MovieTime time ) => GetValue( time );
/// <summary>
/// Try to make a more minimal composition for this signal, optionally within a time range.
/// </summary>
/// <param name="start">Optional start time, we can discard any features before this if given.</param>
/// <param name="end">Optional end time, we can discard any features after this if given.</param>
public PropertySignal<T> Reduce( MovieTime? start = null, MovieTime? end = null )
{
return start >= end && !GetKeyframes( start.Value ).Any() ? GetValue( start.Value ) : OnReduce( start, end );
}
public PropertySignal<T> Reduce( MovieTimeRange timeRange ) =>
Reduce( timeRange.Start, timeRange.End );
public T[] Sample( MovieTimeRange timeRange, int sampleRate )
{
var sampleCount = timeRange.Duration.GetFrameCount( sampleRate );
var samples = new T[sampleCount];
for ( var i = 0; i < sampleCount; i++ )
{
var time = timeRange.Start + MovieTime.FromFrames( i, sampleRate );
samples[i] = GetValue( time );
}
return samples;
}
protected abstract PropertySignal<T> OnReduce( MovieTime? start, MovieTime? end );
public PropertySignal<T> Smooth( MovieTime size ) => size <= 0d ? this : OnSmooth( size );
protected virtual PropertySignal<T> OnSmooth( MovieTime size ) => this;
}
/// <summary>
/// Extension methods for creating and composing <see cref="IPropertySignal"/>s.
/// </summary>
// ReSharper disable once UnusedMember.Global
public static partial class PropertySignalExtensions;
using Editor;
using Editor.TerrainEditor;
namespace Sandbox;
public class ProjectSettingsWindow : WidgetWindow
{
public ProjectSettingsWindow( Widget parent ) : base( parent, "Project Settings" )
{
Parent = parent;
DeleteOnClose = true;
Size = new Vector2( 1580, 720 );
WindowTitle = "Project Settings";
Float();
WindowFlags = WindowFlags.Dialog | WindowFlags.Customized | WindowFlags.CloseButton | WindowFlags.WindowSystemMenuHint | WindowFlags.WindowTitle | WindowFlags.MaximizeButton;
SetWindowIcon( "electrical_services" );
Layout = Layout.Column();
var inspectorWidget = InspectorWidget.Create( Project.Current.GetSerialized() );
Layout.Add( inspectorWidget );
}
}
using Sandbox;
using System.Linq;
using static Sandbox.ResourceLibrary;
namespace Editor;
public class MotivationNotice : NoticeWidget
{
public string Portrait { get; init; }
public string Message { get; init; }
public string Bubble => FileSystem.Mounted.GetFullPath( "portraits/bubble.png" );
protected override Vector2 SizeHint() => new( 512, 384 );
public MotivationNotice()
{
var personality = Game.Random.FromArray( GetAll<MotivationResource>().ToArray() );
Portrait = personality.GetPortrait();
Message = personality.GetMessage();
}
protected override void OnPaint()
{
Paint.SetPen( Theme.TextDark );
Paint.SetDefaultFont( 16 );
var rect = LocalRect.Align( 350, TextFlag.LeftBottom );
Paint.Draw( rect, Portrait );
rect = LocalRect.Align( 250, TextFlag.RightTop );
Paint.Draw( rect, Bubble );
rect = rect.Shrink( 0, 0, 0, 55 );
Paint.DrawText( rect, Message );
}
}
namespace AltCurves;
/// <summary>
/// User options for snapping values to a grid
/// </summary>
public enum ValueSnapOptions
{
[Title( "0.1" )]
Tenth,
[Title( "0.5" )]
Half,
[Title( "1" )]
One,
[Title( "2" )]
Two,
[Title( "5" )]
Five,
[Title( "10" )]
Ten,
[Title( "50" )]
Fifty,
[Title( "100" )]
Hundred,
[Title( "Snap-To-Gridline" )]
Gridlines,
[Title( "User-provided amount" )]
Custom,
};
using Editor;
using System;
namespace AltCurves.GraphicsItems;
public partial class EditableAltCurve : GraphicsItem
{
/// <summary>
/// The draggable, invisible handle that represents a keyframe on the curve
/// The actual visible keyframe position can vary with snapping
/// </summary>
public class DragHandle : GraphicsItem
{
/// <summary>
/// Underlying keyframe that we're positioned for, updated on dragging
/// </summary>
public AltCurve.Keyframe Keyframe
{
get => _keyframe;
set
{
_keyframe = value;
StartDragKeyframe = _keyframe;
Position = _transform.CurveToWidgetPosition( new( _keyframe.Time, _keyframe.Value ) );
Update();
}
}
private AltCurve.Keyframe _keyframe;
/// <summary>
/// The keyframe when we started the last dragging operation
/// </summary>
public AltCurve.Keyframe StartDragKeyframe { get; private set; }
public CurveWidgetTransform Transform
{
get => _transform;
set
{
_transform = value;
Position = _transform.CurveToWidgetPosition( new( Keyframe.Time, Keyframe.Value ) );
Update();
}
}
private CurveWidgetTransform _transform;
/// <summary>
/// Called when we're left-clicked for selection logic
/// Params are index, isShift, isCtrl, isAlt
/// </summary>
public Action<int, bool, bool, bool> OnMouseDown { get; set; } = null;
public Action<int, bool, bool, bool> OnMouseUp { get; set; } = null;
/// <summary>
/// Called during drag operations, after the Keyframe has been updated for our new position
/// Parameter is the index and the pre-drag keyframe.
/// </summary>
public Action<int> OnDragging { get; set; } = null;
/// <summary>
/// Called when releasing the mouse, the drag has completed
/// </summary>
public Action<int> OnDragComplete { get; set; } = null;
private readonly int _index;
private Vector2 _mouseDownPos;
public DragHandle( int index, CurveWidgetTransform transform, AltCurve.Keyframe keyframe, GraphicsItem parent ) : base( parent )
{
_index = index;
_keyframe = keyframe;
StartDragKeyframe = keyframe;
HandlePosition = new( 0.5f );
Cursor = CursorShape.Finger;
Movable = true;
Clip = true;
HoverEvents = true;
Size = new( 14.0f );
_transform = transform;
Position = _transform.CurveToWidgetPosition( new( Keyframe.Time, Keyframe.Value ) );
}
protected override void OnMoved()
{
base.OnMoved();
_keyframe = _keyframe with { Time = _transform.WidgetToCurveX( Position.x ), Value = _transform.WidgetToCurveY( Position.y ) };
OnDragging?.Invoke( _index );
}
protected override void OnMousePressed( GraphicsMouseEvent e )
{
if ( e.LeftMouseButton )
{
StartDragKeyframe = Keyframe;
_mouseDownPos = e.ScreenPosition;
OnMouseDown?.Invoke( _index, e.HasShift, e.HasCtrl, e.HasAlt );
}
// Begin drag
base.OnMousePressed( e );
}
protected override void OnMouseReleased( GraphicsMouseEvent e )
{
base.OnMouseReleased( e );
// Either the user moved us from the starting location and we're completing a drag,
// or they just clicked us normally and the mouse up action should trigger
if ( e.LeftMouseButton )
{
if ( e.ScreenPosition != _mouseDownPos )
{
OnDragComplete?.Invoke( _index );
}
else
{
OnMouseUp?.Invoke( _index, e.HasShift, e.HasCtrl, e.HasAlt );
}
}
}
}
}
using Editor;
public static class MyEditorMenu
{
[Menu( "Editor", "Crosshair Maker/My Menu Option" )]
public static void OpenMyMenu()
{
EditorUtility.DisplayDialog( "It worked!", "This is being called from your library's editor code!" );
}
}
using Sandbox;
using Editor;
using System;
using System.Linq;
namespace ChitChat.Editor;
public class DialogueSelector : Widget
{
public Action<SerializedProperty> onDialogueActionSelected;
public Action onSelectedDialogueRemoved;
private ScrollArea _dialogueArea;
private ListControlView _listView;
private Menu _menu;
private SerializedObject _serializedObject;
private SerializedCollection _serializedDialogueDatasArray;
private int _lastSelectedIndex = -1;
public DialogueSelector(Widget parent, DialogueData data) : base(parent)
{
WindowTitle = "Dialogue Selector";
Name = "Dialogue Selector";
Size = new Vector2(ChitChatEditorWindow.WINDOW_SIZE_X * 0.5f, ChitChatEditorWindow.WINDOW_SIZE_Y * 0.5f);
Layout = Layout.Column();
SetWindowIcon("list_alt");
_serializedObject = data.GetSerialized();
Rebuild();
}
public void SetData(DialogueData data) => _serializedObject = data.GetSerialized();
public void SelectLastest()
{
_listView.SelectItem(_lastSelectedIndex);
}
//TODO: Use this to see if something changed and update asset
public override void ChildValuesChanged(Widget source)
{
base.ChildValuesChanged(source);
}
public void Rebuild()
{
//Needs to save last selected index to reload it at that index
if(_listView.IsValid())
_lastSelectedIndex = _listView.SelectedIndex;
Layout.Clear(true);
SerializedProperty prop = _serializedObject.GetProperty(nameof(DialogueData.DialogueDatas));
//Scroll area
_dialogueArea = new ScrollArea(this);
_dialogueArea.Layout = Layout.Column();
_dialogueArea.Canvas = new Widget(_dialogueArea);
_dialogueArea.Canvas.Layout = Layout.Column();
_dialogueArea.Canvas.Layout.Alignment = TextFlag.LeftTop;
_dialogueArea.Canvas.Parent.Update();
if (prop.TryGetAsObject(out var obj))
{
//Dialogue list
_serializedDialogueDatasArray = (SerializedCollection)obj;
_listView = new ListControlView(prop, _serializedDialogueDatasArray, OnCreateUIListItem, false);
_listView.onItemSelected += OnItemSelected;
_listView.onItemRemoved += OnItemMovedOrRemoved;
_listView.onItemMoved += OnItemMovedOrRemoved;
_listView.onItemRightClicked += CreateDialogueActionMenuWithIndex;
_dialogueArea.Canvas.Layout.Add(_listView);
}
//Add dialogue action button
Widget addButtonContainer = new Widget(_dialogueArea.Canvas);
addButtonContainer.Layout = Layout.Row();
addButtonContainer.Layout.Alignment = TextFlag.Left;
addButtonContainer.Layout.AddSpacingCell(24);
IconButton addDialogueActionButton = new IconButton("add", CreateDialogueActionMenu);
addDialogueActionButton.Background = Theme.ControlBackground;
addButtonContainer.Layout.Add(addDialogueActionButton);
_dialogueArea.Canvas.Layout.Add(addButtonContainer);
_dialogueArea.Canvas.Layout.AddStretchCell();
Layout.Add(_dialogueArea);
}
private ControlWidget OnCreateUIListItem(SerializedProperty prop)
{
DialogueActionBase baseAction = prop.GetValue<DialogueActionBase>();
if (baseAction is SpeakAction)
{
return new SpeakActionControlWidget(prop);
}
else if (baseAction is EventAction)
{
return new EventActionControlWidget(prop);
}
else if (baseAction is ChoiceAction)
{
return new ChoiceActionControlWidget(prop);
}
return ControlWidget.Create(prop);
}
private void CreateDialogueActionMenu()
{
if(_menu.IsValid())
return;
CreateDialogueActionMenuWithIndex(_serializedDialogueDatasArray.Count() - 1);
}
private void CreateDialogueActionMenuWithIndex(int index)
{
if (_menu.IsValid())
return;
_menu = new ContextMenu();
_menu.AddHeading("Dialogue Actions");
_menu.AddOption("Speak Action", "record_voice_over", () => _serializedDialogueDatasArray.Add(index + 1, new SpeakAction(TemplateWindow.s_SpeakActionTemplate)));
_menu.AddSeparator();
_menu.AddOption("Event Action", "event", () => _serializedDialogueDatasArray.Add(index + 1, new EventAction(TemplateWindow.s_EventActionTemplate)));
_menu.AddSeparator();
_menu.AddOption("Choice Action", "alt_route", () => _serializedDialogueDatasArray.Add(index + 1, new ChoiceAction(TemplateWindow.s_ChoiceActionTemplate)));
_menu.OpenAtCursor(true);
_menu.MinimumWidth = ScreenRect.Width;
}
private void OnItemMovedOrRemoved(int index)
{
if(index == _listView.SelectedIndex)
onSelectedDialogueRemoved?.Invoke();
}
private void OnItemSelected(SerializedProperty prop)
{
onDialogueActionSelected?.Invoke(prop);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Editor;
using Sandbox;
namespace Sandbox.AssetBrowserAddon;
/// <summary>
/// Dialog that captures the settings for a new custom Asset Browser location.
/// </summary>
public sealed class LocationEditor : Dialog
{
private readonly Action<CustomLocationDefinition> _onConfirm;
private readonly CustomLocationDefinition _initialDefinition;
private readonly LineEdit _nameInput;
private readonly LineEdit _iconInput;
private readonly LineEdit _includeInput;
private readonly LineEdit _excludeInput;
private readonly AssetTypeSelector _assetTypeSelector;
private readonly ToggleSwitch _projectOnlyToggle;
public LocationEditor(Action<CustomLocationDefinition> onConfirm, CustomLocationDefinition initialDefinition = null)
{
_onConfirm = onConfirm;
_initialDefinition = initialDefinition;
Window.Title = initialDefinition is null ? "Add Bookmark" : "Edit Bookmark";
Window.Size = new Vector2(500, 750);
Layout = Layout.Column();
Layout.Margin = 16f;
Layout.Spacing = 12f;
_nameInput = AddTextRow("Title", "My Bookmark");
_iconInput = AddIconRow("Icon", "bookmark");
Layout.Add( new Label( this ) { Text = "Asset Types" } );
_assetTypeSelector = Layout.Add( new AssetTypeSelector( this, StyleInput ) );
_projectOnlyToggle = Layout.Add( new ToggleSwitch( "Only include assets from this project", this ) );
_projectOnlyToggle.MinimumHeight = Theme.RowHeight;
_projectOnlyToggle.Value = true;
_includeInput = AddTextArea("Include Folders", "Separate folders with ;");
_excludeInput = AddTextArea("Exclude Folders", "Separate folders with ;");
if ( _initialDefinition is not null )
{
_nameInput.Text = _initialDefinition.Name;
_iconInput.Text = _initialDefinition.Icon;
_assetTypeSelector.SetSelected( _initialDefinition.AssetTypes );
_includeInput.Text = string.Join( ';', _initialDefinition.IncludeFolders ?? new List<string>() );
_excludeInput.Text = string.Join( ';', _initialDefinition.ExcludeFolders ?? new List<string>() );
_projectOnlyToggle.Value = _initialDefinition.ProjectAssetsOnly;
}
var buttonRow = Layout.AddRow();
buttonRow.Spacing = 8f;
buttonRow.AddStretchCell();
buttonRow.Add( new Button( "Cancel", this ) { Clicked = Close } );
var buttonLabel = _initialDefinition is null ? "Add Bookmark" : "Save Bookmark";
var buttonIcon = _initialDefinition is null ? "add" : "save";
buttonRow.Add( new Button.Primary( buttonLabel, buttonIcon, this ) { Clicked = Submit } );
}
private LineEdit AddTextRow(string label, string placeholder)
{
var row = Layout.AddRow();
row.Spacing = 8f;
row.Add( new Label( this ) { Text = label, FixedWidth = 130 } );
var input = row.Add( new LineEdit( this ) );
input.PlaceholderText = placeholder;
StyleInput( input );
return input;
}
private LineEdit AddIconRow(string label, string placeholder)
{
var row = Layout.AddRow();
row.Spacing = 8f;
row.Add( new Label( this ) { Text = label, FixedWidth = 130 } );
var input = row.Add( new LineEdit( this ) );
input.PlaceholderText = placeholder;
StyleInput( input );
var button = row.Add( new IconButton( "search", () => ShowIconPicker( input ), this ) );
button.ToolTip = "Browse material icons";
button.MinimumWidth = Theme.RowHeight;
return input;
}
private LineEdit AddTextArea(string label, string placeholder)
{
var column = Layout.Add( Layout.Column() );
column.Spacing = 4f;
column.Add( new Label( this ) { Text = label } );
var input = column.Add( new LineEdit( this ) );
input.PlaceholderText = placeholder;
StyleInput( input );
return input;
}
private void Submit()
{
var name = _nameInput.Text?.Trim();
if ( string.IsNullOrWhiteSpace( name ) )
{
EditorUtility.DisplayDialog( "Missing Title", "Please enter a title for the bookmark." );
return;
}
var icon = string.IsNullOrWhiteSpace( _iconInput.Text ) ? "extension" : _iconInput.Text.Trim();
var definition = _initialDefinition is null
? new CustomLocationDefinition()
: new CustomLocationDefinition { Id = _initialDefinition.Id };
definition.Name = name;
definition.Icon = icon;
definition.AssetTypes = _assetTypeSelector.SelectedTags.Select( NormalizeExtension ).Where( x => !string.IsNullOrWhiteSpace( x ) ).ToList();
definition.IncludeFolders = SplitToList( _includeInput.Text );
definition.ExcludeFolders = SplitToList( _excludeInput.Text );
definition.ProjectAssetsOnly = _projectOnlyToggle.Value;
_onConfirm?.Invoke( definition );
Close();
}
private void ShowIconPicker( LineEdit target )
{
var pickerType = AppDomain.CurrentDomain.GetAssemblies()
.Select( asm => asm.GetType( "Editor.IconPickerWidget", false ) )
.FirstOrDefault( t => t is not null );
var openPopup = pickerType?.GetMethod( "OpenPopup", BindingFlags.Public | BindingFlags.Static );
if ( openPopup is null )
{
EditorUtility.DisplayDialog( "Icon Picker", "Unable to locate the icon picker widget." );
return;
}
openPopup.Invoke( null, new object[]
{
this,
target.Text ?? string.Empty,
(Action<string>)(value => target.Text = value)
} );
}
private static List<string> SplitToList(string raw)
{
if ( string.IsNullOrWhiteSpace( raw ) )
return new List<string>();
return raw
.Split( new[] { ';', ',', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries )
.Select( part => part.Trim() )
.Where( part => part.Length > 0 )
.ToList();
}
private static string NormalizeExtension( string value )
{
if ( string.IsNullOrWhiteSpace( value ) )
return string.Empty;
return value.Trim().TrimStart( '.' ).ToLowerInvariant();
}
private static void StyleInput( LineEdit input )
{
var background = Theme.ControlBackground.Darken( 0.25f ).Hex;
var border = Theme.Border.Hex;
input.SetStyles( $"background-color: {background}; border-color: {border};" );
}
}
using Sandbox.MovieMaker;
namespace Editor.MovieMaker.BlockDisplays;
#nullable enable
public abstract partial class BlockItem : GraphicsItem
{
private ITrackBlock? _block;
public new TimelineTrack Parent { get; private set; } = null!;
public ITrackBlock Block
{
get => _block ?? throw new InvalidOperationException();
set
{
if ( ReferenceEquals( _block, value ) ) return;
if ( _block is IDynamicBlock oldBlock )
{
oldBlock.Changed -= Block_Changed;
}
_block = value;
if ( _block is IDynamicBlock newBlock )
{
newBlock.Changed += Block_Changed;
}
}
}
public MovieTime Offset { get; set; }
protected IProjectTrack Track => Parent.View.Track;
protected MovieTimeRange TimeRange => Block.TimeRange + Offset;
protected int DataHash => HashCode.Combine( Block, TimeRange.Duration, Width );
private void Initialize( TimelineTrack parent, ITrackBlock block, MovieTime offset )
{
base.Parent = Parent = parent;
Block = block;
Offset = offset;
}
private void Block_Changed() => Layout();
protected override void OnDestroy()
{
// To remove Changed event
Block = null!;
}
public void Layout()
{
var session = Parent.Session;
PrepareGeometryChange();
Position = new Vector2( session.TimeToPixels( TimeRange.Start ), 1f );
Size = new Vector2( session.TimeToPixels( TimeRange.Duration ), Parent.Height - 2f );
Update();
}
protected override void OnPaint()
{
Paint.SetBrushAndPen( Timeline.Colors.ChannelBackground.Lighten( Parent.View.IsLocked ? 0.2f : 0f ).WithAlpha( 0.75f ) );
Paint.DrawRect( LocalRect );
if ( Parent.View.IsLocked ) return;
Paint.ClearBrush();
Paint.SetPen( Color.White.WithAlpha( 0.1f ) );
Paint.DrawLine( LocalRect.BottomLeft, LocalRect.TopLeft );
Paint.DrawLine( LocalRect.BottomRight, LocalRect.TopRight );
}
}
public interface IBlockItem<T>;
public abstract class BlockItem<T> : BlockItem, IBlockItem<T>
where T : ITrackBlock
{
public new T Block => (T)base.Block;
}
public interface IPropertyBlockItem<T>;
public abstract class PropertyBlockItem<T> : BlockItem<IPropertyBlock<T>>, IPropertyBlockItem<T>;
using Sandbox.MovieMaker;
namespace Editor.MovieMaker;
#nullable enable
public sealed partial class Session
{
private bool _isPlaying;
private bool _isLooping = true;
private float _timeScale = 1f;
private bool _syncPlayback = true;
private MovieTime? _lastPlayerPosition;
private bool _applyNextFrame;
private MovieTime _lastAppliedTime;
public bool IsOpenInEditor => Editor.Session == this;
public bool IsPlaying
{
get => IsEditorScene ? _isPlaying : Player.IsPlaying;
set
{
if ( IsEditorScene ) _isPlaying = value;
else Player.IsPlaying = value;
}
}
public bool IsLooping
{
get => IsEditorScene ? _isLooping : Player.IsLooping;
set
{
if ( IsEditorScene ) _isLooping = Cookies.IsLooping = value;
else Player.IsLooping = value;
}
}
public bool SyncPlayback
{
get => _syncPlayback;
set => _syncPlayback = Cookies.SyncPlayback = value;
}
public float TimeScale
{
get => IsEditorScene ? _timeScale : Player.TimeScale;
set
{
if ( IsEditorScene ) _timeScale = Cookies.TimeScale = value;
else Player.TimeScale = value;
}
}
public void ApplyFrame( MovieTime time )
{
_applyNextFrame = false;
if ( IsOpenInEditor && SyncPlayback )
{
foreach ( var player in Player.Scene.GetAllComponents<MoviePlayer>() )
{
if ( player == Player ) continue;
player.Position = time;
}
}
ApplyFrameCore( time );
}
private void ApplyFrameCore( MovieTime time )
{
Parent?.ApplyFrameCore( SequenceTransform * time );
foreach ( var view in TrackList.AllTracks )
{
view.ApplyFrame( time );
}
AdvanceAnimations( time - _lastAppliedTime );
_lastAppliedTime = time;
}
public void RefreshNextFrame()
{
_applyNextFrame = true;
}
private void PlaybackFrame()
{
if ( IsPlaying && IsEditorScene )
{
var targetTime = PlayheadTime + MovieTime.FromSeconds( RealTime.Delta * TimeScale );
// Handle looping / reaching end
if ( !IsRecording )
{
if ( LoopTimeRange is { } loopRange )
{
if ( targetTime <= loopRange.Start || targetTime >= loopRange.End )
{
targetTime = loopRange.Start;
}
}
else if ( targetTime >= Project.Duration && Project.Duration.IsPositive )
{
if ( IsLooping )
{
targetTime = MovieTime.Zero;
}
else
{
targetTime = Project.Duration;
IsPlaying = false;
}
}
}
PlayheadTime = targetTime;
}
else if ( _lastPlayerPosition is { } lastPlayerPosition && lastPlayerPosition != Player.Position )
{
// We're setting the backing field here because we don't want to call ApplyFrame / set the Player position,
// since we're reacting to the Player advancing time.
_playheadTime = lastPlayerPosition;
PlayheadChanged?.Invoke( PlayheadTime );
}
_lastPlayerPosition = Player.Position;
}
}
using Sandbox.MovieMaker;
using System.Linq;
using System.Reflection;
namespace Editor.MovieMaker;
#nullable enable
/// <summary>
/// Panel containing the track list.
/// </summary>
public sealed class ListPanel : MovieEditorPanel
{
public TrackListWidget TrackList { get; }
public ListPanel( MovieEditor parent, Session session )
: base( parent )
{
TrackList = new TrackListWidget( this, session );
Layout.Add( TrackList );
MinimumWidth = 300;
// File menu
var fileGroup = ToolBar.AddGroup( true );
var resourceIcon = typeof( MovieResource ).GetCustomAttribute<GameResourceAttribute>()!.Icon;
var fileDisplay = new ToolBarItemDisplay( "File", "folder", "Actions for saving / loading / importing movies, or switching player components." );
var fileAction = fileGroup.AddAction( fileDisplay, () =>
{
var menu = new Menu();
menu.AddHeading( "File" );
menu.AddOption( "New Movie", "note_add", Editor.SwitchToNewEmbedded );
menu.AddSeparator();
var openMenu = menu.AddMenu( "Open Movie", "file_open" );
var movies = ResourceLibrary.GetAll<MovieResource>().ToArray();
openMenu.AddOptions( movies, x => $"{x.ResourcePath}:{resourceIcon}", Editor.SwitchResource );
session.CreateImportMenu( menu );
menu.AddSeparator();
menu.AddOption( $"Save Movie", "save", parent.OnSave, shortcut: "CTRL+S" );
var saveAsMenu = menu.AddMenu( $"Save Movie As..", "save_as" );
var embed = saveAsMenu.AddOption( "Embedded", "attach_file", parent.SwitchToEmbedded );
embed.Checkable = true;
embed.Checked = session.Resource is EmbeddedMovieResource;
embed.ToolTip = "Store the movie inside this Movie Player component, embedded in the current scene or prefab.";
saveAsMenu.AddOption( "New Movie Resource", resourceIcon, parent.SaveFileAs );
menu.OpenAt( fileGroup.ScreenRect.BottomLeft );
} );
fileAction.ToolTip = "File menu for opening, importing, or saving movie projects.";
// MoviePlayer selection
var playerGroup = ToolBar.AddGroup( true );
var playerCombo = playerGroup.Layout.Add( new PlayerComboBox( session ) );
playerCombo.HorizontalSizeMode = SizeMode.CanGrow | SizeMode.Expand;
playerCombo.Bind( "Value" ).From(
() => session.Player,
value => session.Editor.Switch( value ) );
}
protected override void OnPaint()
{
Paint.SetBrushAndPen( Theme.TabBackground );
Paint.DrawRect( LocalRect );
}
}
file class PlayerComboBox : IconComboBox<MoviePlayer?>
{
private readonly Session _session;
public PlayerComboBox( Session session )
{
_session = session;
IconAspect = null;
}
protected override IEnumerable<MoviePlayer?> OnGetOptions() =>
_session.Player.Scene.IsValid ? _session.Player.Scene.GetAllComponents<MoviePlayer>() : [];
protected override string OnGetOptionTitle( MoviePlayer? option ) => option?.GameObject.Name ?? "None";
protected override void OnPaintOptionIcon( MoviePlayer? option, Rect rect )
{
Paint.DrawText( rect, OnGetOptionTitle( option ) );
}
protected override void OnCreateMenu( Menu menu )
{
base.OnCreateMenu( menu );
menu.AddSeparator();
menu.AddOption( "Create New Movie Player", "live_tv", _session.Editor.CreateNewPlayer );
}
}
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace EditorAllChat;
public class MessagesRequest
{
[JsonPropertyName( "success" )]
public bool Success { get; set; }
[JsonPropertyName( "messages" )]
public List<MessageObject> Messages { get; set; }
[JsonPropertyName( "count" )]
public int Count { get; set; }
[JsonPropertyName( "total_stored" )]
public int TotalStored { get; set; }
}
using System.Text.Json.Serialization;
namespace EditorAllChat;
public class MessageResponse
{
[JsonPropertyName( "success" )]
public bool Success { get; set; }
[JsonPropertyName( "message" )]
public string Message { get; set; }
[JsonPropertyName( "data" )]
public MessageObject Data { get; set; }
}
using System;
using System.Collections.Generic;
using System.Linq;
using Editor;
using Sandbox;
namespace MANIFOLD.BHLib.Editor {
public class TimelineWidget : GraphicsView {
public class EventMarker : GraphicsItem {
public float time;
public List<AttackEvent> events;
public bool selected;
public int selectedIndex;
public Action<EventMarker> onSelected;
private TimelineWidget timeline;
public EventMarker(TimelineWidget widget) {
timeline = widget;
events = new List<AttackEvent>();
ZIndex = 0;
HoverEvents = true;
}
protected override void OnMousePressed(GraphicsMouseEvent e) {
if (e.LeftMouseButton) {
if (!selected) selectedIndex = 0;
else {
selectedIndex++;
if (selectedIndex >= events.Count) selectedIndex = 0;
}
selected = true;
Update();
onSelected?.Invoke(this);
e.Accepted = true;
}
}
protected override void OnPaint() {
base.OnPaint();
Paint.Antialiasing = false;
Color unselectedColor = Color.White.Darken(0.05f);
Color color;
if (selected) color = Color.Orange;
else if (Paint.HasMouseOver) color = Gizmo.Colors.Hovered;
else color = unselectedColor;
Paint.SetPen(color, 2);
var rect = LocalRect.Shrink(2);
Paint.DrawLine(new Vector2(rect.Left, rect.Bottom), new Vector2(rect.Left, rect.Top));
// Paint.DrawText(rect.Shrink(4, 0, 0, 6), string.Join("\n", events.Select(x => x.Name)), TextFlag.LeftBottom);
rect = rect.Shrink(4, 0, 0, 6);
for (int i = 0; i < events.Count; i++) {
Color textColor;
if (selected) {
textColor = i == selectedIndex ? color : unselectedColor;
} else {
textColor = color;
}
Paint.SetPen(textColor);
Paint.DrawText(rect, events[i].Name, TextFlag.LeftBottom);
rect = rect.Shrink(0, 0, 0, 9);
}
}
}
public class TimeAxis : GraphicsItem {
private TimelineWidget timeline;
public TimeAxis(TimelineWidget widget) {
timeline = widget;
ZIndex = 10;
HoverEvents = true;
}
protected override void OnPaint() {
base.OnPaint();
Paint.Antialiasing = false;
Paint.ClearPen();
Paint.SetBrush(Theme.ControlBackground);
Paint.DrawRect(LocalRect);
Paint.SetDefaultFont(7);
var rect = LocalRect.Shrink(1);
var zoom = timeline.ZoomFactor;
var spacing = 100 * zoom;
var lines = rect.Width / spacing;
var w = spacing;
var subdivisions = (int)(3 * zoom);
var subLineSpacing = w / subdivisions;
for (int i = 0; i < lines; i++) {
float xPos = rect.Left + w * i;
Paint.SetPen(Theme.Text.WithAlpha(0.5f));
Paint.DrawLine(new Vector2(xPos, rect.Bottom), new Vector2(xPos, rect.Bottom - 8));
Paint.DrawText(new Vector2(xPos, rect.Top), $"{i}");
Paint.SetPen(Theme.Text.WithAlpha(0.2f));
for (int j = 0; j < subdivisions; j++) {
var sublineX = w * i + subLineSpacing * j;
Paint.DrawLine(new Vector2(rect.Left + sublineX, rect.Bottom), new Vector2(rect.Left + sublineX, rect.Bottom - 4));
}
}
}
protected override void OnMousePressed(GraphicsMouseEvent e) {
base.OnMousePressed(e);
if (e.LeftMouseButton) {
timeline.ScrubTo(timeline.TimeFromPosition(e.LocalPosition.x));
}
}
}
public class Scrubber : GraphicsItem {
private TimelineWidget timeline;
public Scrubber(TimelineWidget widget) {
timeline = widget;
ZIndex = 20;
HoverEvents = true;
Cursor = CursorShape.SizeH;
Movable = true;
Selectable = true;
}
protected override void OnPaint() {
base.OnPaint();
Paint.Antialiasing = false;
Paint.ClearPen();
Paint.SetBrush(Theme.Green.WithAlpha(0.7f));
Paint.DrawRect(new Rect(0, new Vector2(LocalRect.Width, Theme.RowHeight + 1)));
Paint.SetPen(Theme.Green.WithAlpha(0.7f));
Paint.DrawLine(new Vector2(4, Theme.RowHeight + 1), new Vector2(4, LocalRect.Bottom));
}
protected override void OnMoved() {
base.OnMoved();
timeline.ScrubTo(timeline.TimeFromPosition(Position.x));
Position = Position.WithY(0);
Position = Position.WithX(MathF.Max(-4, Position.x));
}
}
private ITimeline timeline;
private float time;
private float zoomFactor;
private bool showHidden;
private TimeAxis timeAxis;
private Scrubber scrubber;
private List<EventMarker> markers;
private Dictionary<AttackEvent, EventMarker> eventToMarker;
private EventMarker selectedMarker;
public ITimeline Timeline {
get => timeline;
set {
timeline = value;
RebuildMarkers();
DoLayout();
}
}
public float Range { get; set; }
public float ZoomFactor {
get => zoomFactor;
set {
zoomFactor = value;
DoLayout();
timeAxis.Update();
scrubber.Update();
}
}
public float Time {
get => time;
set {
time = value;
DoLayout();
}
}
public bool ShowHidden {
get => showHidden;
set {
showHidden = value;
RebuildMarkers();
}
}
public AttackEvent SelectedEvent => selectedMarker?.events[selectedMarker.selectedIndex];
public Action<AttackEvent> OnEventSelected { get; set; }
public Action OnTimeScrubbed { get; set; }
public TimelineWidget(Widget parent = null) : base(parent) {
Antialiasing = false;
BilinearFiltering = false;
SceneRect = new Rect(0, Size);
HorizontalScrollbar = ScrollbarMode.Auto;
VerticalScrollbar = ScrollbarMode.Off;
MouseTracking = true;
Scale = 1;
zoomFactor = 1;
timeAxis = new TimeAxis(this);
Add(timeAxis);
scrubber = new Scrubber(this);
Add(scrubber);
markers = new List<EventMarker>();
eventToMarker = new Dictionary<AttackEvent, EventMarker>();
}
protected override void DoLayout() {
base.DoLayout();
var size = Size;
size.x = MathF.Max(size.x, PositionFromTime(Range + 3));
SceneRect = new Rect(0, size);
timeAxis.Size = new Vector2(size.x, Theme.RowHeight);
scrubber.Size = new Vector2(9, size.y);
var rect = SceneRect;
rect.Top = timeAxis.SceneRect.Bottom;
scrubber.Position = scrubber.Position.WithX(PositionFromTime(Time) - 3).SnapToGrid(1f);
foreach (var marker in markers) {
var markerRect = rect;
markerRect.Left = PositionFromTime(marker.time);
markerRect.Width = 80;
marker.SceneRect = markerRect;
}
}
protected override void OnWheel(WheelEvent e) {
e.Accept();
}
public void RebuildMarkers() {
var previousSelectedEvent = SelectedEvent;
foreach (var marker in markers) {
marker.Destroy();
}
markers.Clear();
eventToMarker.Clear();
if (timeline != null) {
foreach (var evt in Timeline.Events) {
if (!ShowHidden && evt.Hidden) continue;
AddEvent(evt);
}
}
DoLayout();
if (previousSelectedEvent != null) {
if (eventToMarker.TryGetValue(previousSelectedEvent, out EventMarker marker)) {
marker.selected = true;
marker.selectedIndex = marker.events.IndexOf(previousSelectedEvent);
try {
marker.Update();
} catch {
// ignored
}
}
}
}
public void ScrubTo(float time) {
Time = time;
OnTimeScrubbed?.Invoke();
}
public float PositionFromTime(float time) {
return 100 * ZoomFactor * time;
}
public float TimeFromPosition(float position) {
return (ZoomFactor / 100) * position;
}
private void AddEvent(AttackEvent evt) {
var existing = markers.FirstOrDefault(x => x.time.AlmostEqual(evt.Time));
if (existing != null) {
existing.events.Add(evt);
eventToMarker.Add(evt, existing);
} else {
EventMarker marker = new EventMarker(this);
marker.time = evt.Time;
marker.events.Add(evt);
marker.onSelected = OnMarkerSelected;
Add(marker);
markers.Add(marker);
eventToMarker.Add(evt, marker);
}
}
private void OnMarkerSelected(EventMarker marker) {
if (marker != selectedMarker && selectedMarker != null) {
selectedMarker.selected = false;
try {
selectedMarker.Update();
} catch {
// ignored
}
}
selectedMarker = marker;
OnEventSelected?.Invoke(SelectedEvent);
}
}
}
namespace Braxnet;
public class CompletionsCapability
{
}
using System.Text.Json.Serialization;
namespace Braxnet;
public class PromptsCapability
{
[JsonPropertyName( "listChanged" )] public bool ListChanged { get; set; }
}
using System.Text.Json.Serialization;
namespace Braxnet;
public class RootsCapability
{
[JsonPropertyName( "listChanged" )] public bool ListChanged { get; set; }
}
using System.Threading.Tasks;
using System.Text.Json;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System;
using Sandbox;
using Editor;
using FileSystem = Editor.FileSystem;
namespace Braxnet.Commands;
[MCPCommand( "resources/list" )]
public class ListResourcesCommand : IMCPCommand
{
public string Name => "resources/list";
public async Task<object> ExecuteAsync( JsonRpcRequest request, string sessionId, string protocolVersion )
{
var resources = new List<Resource>();
try
{
var projectDir = Path.Combine( FileSystem.Mounted.GetFullPath( "." ), ".." );
projectDir = Path.GetFullPath( projectDir );
var files = Directory.GetFiles( projectDir, "*.*", SearchOption.AllDirectories )
.Where( f => !f.Contains( ".git" ) && !f.Contains( ".vscode" ) && !f.Contains( ".idea" ) )
.Take( 100 );
foreach ( var file in files )
{
var relativePath = Path.GetRelativePath( projectDir, file );
var fileInfo = new FileInfo( file );
resources.Add( new Resource
{
Uri = $"file://{file.Replace( '\\', '/' )}",
Name = relativePath.Replace( '\\', '/' ),
Title = Path.GetFileName( file ),
Description = $"Project file: {relativePath}",
MimeType = MCPServer.GetMimeType( file ),
Size = fileInfo.Length
} );
}
}
catch ( Exception ex )
{
Log.Error( $"Error listing resources: {ex.Message}" );
}
return new ListResourcesResult { Resources = resources };
}
}
[MCPCommand( "resources/read" )]
public class ReadResourceCommand : IMCPCommand
{
public string Name => "resources/read";
public async Task<object> ExecuteAsync( JsonRpcRequest request, string sessionId, string protocolVersion )
{
var contents = new List<ResourceContents>();
if ( request.Params.HasValue )
{
var paramsObj = JsonSerializer.Deserialize<Dictionary<string, object>>(
request.Params.Value.GetRawText(), MCPServer.JsonOptions );
if ( paramsObj?.ContainsKey( "uri" ) == true )
{
var uriStr = paramsObj["uri"]?.ToString();
if ( !string.IsNullOrEmpty( uriStr ) && uriStr.StartsWith( "file://" ) )
{
var filePath = uriStr.Substring( 7 );
try
{
if ( File.Exists( filePath ) )
{
var text = await File.ReadAllTextAsync( filePath );
contents.Add( new ResourceContents
{
Uri = uriStr, Text = text, MimeType = MCPServer.GetMimeType( filePath )
} );
}
}
catch ( Exception ex )
{
Log.Error( $"Error reading file {filePath}: {ex.Message}" );
throw new Exception( $"Could not read file: {ex.Message}" );
}
}
}
}
return new ReadResourceResult { Contents = contents };
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Sandbox;
namespace Braxnet.Commands.Tools;
[MCPTool( "get_children", "Get Children Tool",
"Get the children of a GameObject in the current scene" )]
public class GetChildrenTool : IMCPTool
{
public string Name => "get_children";
public string Title => "Get Children Tool";
public string Description => "Get the children of a GameObject in the current scene";
public JsonElement InputSchema => JsonSerializer.SerializeToElement( new
{
type = "object",
properties =
new { id = new { type = "string", description = "The ID of the GameObject to get children for" } },
required = new[] { "id" }
} );
public JsonElement OutputSchema => default;
public async Task<CallToolResult> ExecuteAsync( Dictionary<string, object> arguments, string sessionId )
{
var result = new CallToolResult();
if ( !arguments.TryGetValue( "id", out var idObj ) || idObj is not string id )
{
result.IsError = true;
result.Content.Add( new TextContent { Text = "Invalid or missing 'id' argument." } );
return result;
}
if ( string.IsNullOrEmpty( id ) )
{
result.IsError = true;
result.Content.Add( new TextContent { Text = "GameObject ID cannot be empty." } );
return result;
}
if ( !Guid.TryParse( id, out var guid ) )
{
result.IsError = true;
result.Content.Add( new TextContent { Text = "Invalid GameObject ID format." } );
return result;
}
var gameObject = Game.ActiveScene.Directory.FindByGuid( guid );
if ( gameObject == null )
{
result.IsError = true;
result.Content.Add( new TextContent { Text = $"GameObject with ID '{id}' not found." } );
return result;
}
await GameTask.MainThread(); // Ensure this runs on the main thread
var childrenList = gameObject.Children.Select( child => new
{
Id = child.Id,
Name = child.Name,
LocalPosition = child.LocalPosition,
LocalRotation = child.LocalRotation,
LocalScale = child.LocalScale,
Components = child.Components.Count,
ChildrenCount = child.Children.Count,
Enabled = child.Enabled,
Tags = child.Tags.ToList(),
} ).ToList();
result.StructuredContent = new { Children = childrenList };
return result;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Editor;
using Sandbox;
namespace Braxnet.Commands.Tools;
[MCPTool( "get_game_objects", "List Game Objects",
"List all GameObjects in the current scene" )]
public class ListGameObjectsTool : IMCPTool
{
public string Name => "get_game_objects";
public string Title => "List Game Objects";
public string Description => "List all GameObjects in the current scene";
public JsonElement InputSchema => JsonSerializer.SerializeToElement( new
{
type = "object", properties = new { }, required = Array.Empty<string>()
} );
public JsonElement OutputSchema => JsonSerializer.SerializeToElement( new
{
type = "object",
properties = new
{
SceneFile = new { type = "string", description = "Path to the scene file" },
GameObjects = new
{
type = "array",
items = new
{
type = "object",
properties = new
{
Id = new { type = "string", description = "GameObject ID" },
Name = new { type = "string", description = "GameObject name" },
Position =
new
{
type = "object",
properties =
new
{
x = new { type = "number" },
y = new { type = "number" },
z = new { type = "number" }
}
},
Rotation =
new
{
type = "object",
properties =
new
{
pitch = new { type = "number" },
yaw = new { type = "number" },
roll = new { type = "number" }
}
},
Components = new { type = "integer", description = "Number of components" },
ChildrenCount =
new { type = "integer", description = "Number of child GameObjects" },
Enabled =
new { type = "boolean", description = "Is the GameObject enabled?" },
Tags = new
{
type = "array",
items = new { type = "string" },
description = "List of tags assigned to the GameObject"
}
},
required = new[]
{
"Id", "Name", "Position", "Rotation", "Components", "ChildrenCount", "Enabled", "Tags"
}
}
}
},
required = new[] { "SceneFile", "GameObjects" }
} );
public async Task<CallToolResult> ExecuteAsync( Dictionary<string, object> arguments, string sessionId )
{
var result = new CallToolResult();
try
{
var rootGameObjects = SceneEditorSession.Active.Scene.Children;
if ( rootGameObjects == null || rootGameObjects.Count == 0 )
{
result.Content.Add( new TextContent { Text = "No GameObjects found in the scene." } );
return result;
}
result.Content.Add( new TextContent
{
Text = $"Found {rootGameObjects.Count} root GameObjects in the scene."
} );
var gameObjectsList = new List<GameObjectInfo>();
foreach ( var gameObject in rootGameObjects )
{
gameObjectsList.Add( new GameObjectInfo
{
Id = gameObject.Id,
Name = gameObject.Name,
Position = gameObject.WorldPosition,
Rotation = gameObject.WorldRotation,
Components = gameObject.Components.Count,
ChildrenCount = gameObject.Children.Count,
Enabled = gameObject.Enabled,
Tags = gameObject.Tags.ToList(),
} );
}
result.StructuredContent = new
{
SceneFile = SceneEditorSession.Active.Scene.Source.ResourcePath, GameObjects = gameObjectsList
};
}
catch ( Exception ex )
{
result.IsError = true;
result.Content.Add( new TextContent { Text = $"Error listing GameObjects: {ex.Message}" } );
}
return result;
}
public class GameObjectInfo
{
public Guid Id { get; set; }
public string Name { get; set; }
public Vector3 Position { get; set; }
public Angles Rotation { get; set; }
public int Components { get; set; }
public int ChildrenCount { get; set; }
public bool Enabled { get; set; }
public List<string> Tags { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading.Tasks;
using Editor;
using Sandbox;
namespace Braxnet.Commands.Tools;
[MCPTool( "duplicate_game_object", "Duplicate Game Object",
"Duplicate a GameObject in the current scene" )]
public class DuplicateGameObjectTool : IMCPTool
{
public string Name => "duplicate_game_object";
public string Title => "Duplicate Game Object";
public string Description => "Duplicate a GameObject in the current scene";
public JsonElement InputSchema => JsonSerializer.SerializeToElement( new
{
type = "object",
properties = new
{
id = new { type = "string", description = "The ID of the GameObject to duplicate" },
position =
new
{
type = "object",
properties =
new
{
x = new { type = "number", description = "X position" },
y = new { type = "number", description = "Y position" },
z = new { type = "number", description = "Z position" }
},
description = "Position to place the duplicated GameObject"
},
rotation = new
{
type = "object",
properties =
new
{
pitch = new { type = "number", description = "Pitch rotation" },
yaw = new { type = "number", description = "Yaw rotation" },
roll = new { type = "number", description = "Roll rotation" }
},
description = "Rotation of the duplicated GameObject"
},
parentId = new
{
type = "string",
description = "Optional parent GameObject ID to set the duplicated GameObject's parent"
}
},
required = new[] { "id" }
} );
public JsonElement OutputSchema => default;
public async Task<CallToolResult> ExecuteAsync( Dictionary<string, object> arguments, string sessionId )
{
var result = new CallToolResult();
if ( !arguments.TryGetValue( "id", out var idObj ) || idObj is not string id )
{
result.IsError = true;
result.Content.Add( new TextContent { Text = "Invalid or missing 'id' argument." } );
return result;
}
if ( string.IsNullOrEmpty( id ) )
{
result.IsError = true;
result.Content.Add( new TextContent { Text = "GameObject ID cannot be empty." } );
return result;
}
if ( !Guid.TryParse( id, out var guid ) )
{
result.IsError = true;
result.Content.Add( new TextContent { Text = "Invalid GameObject ID format." } );
return result;
}
var gameObject = SceneEditorSession.Active.Scene.Directory.FindByGuid( guid );
if ( gameObject == null )
{
result.IsError = true;
result.Content.Add( new TextContent { Text = $"GameObject with ID '{id}' not found." } );
return result;
}
await GameTask.MainThread(); // Ensure this runs on the main thread
GameObject duplicate = null;
using ( SceneEditorSession.Active.UndoScope( "Duplicate GameObject" )
.WithGameObjectCreations().Push() )
{
var position = arguments.GetValueOrDefault( "position" ) as JsonElement?;
var rotation = arguments.GetValueOrDefault( "rotation" ) as JsonElement?;
var parentId = arguments.GetValueOrDefault( "parentId" ) as string;
duplicate = gameObject.Clone();
if ( position.HasValue && position.Value.ValueKind == JsonValueKind.Object )
{
var posX = position.Value.GetProperty( "x" ).GetSingle();
var posY = position.Value.GetProperty( "y" ).GetSingle();
var posZ = position.Value.GetProperty( "z" ).GetSingle();
duplicate.WorldPosition = new Vector3( posX, posY, posZ );
}
if ( rotation.HasValue && rotation.Value.ValueKind == JsonValueKind.Object )
{
var pitch = rotation.Value.GetProperty( "pitch" ).GetSingle();
var yaw = rotation.Value.GetProperty( "yaw" ).GetSingle();
var roll = rotation.Value.GetProperty( "roll" ).GetSingle();
duplicate.WorldRotation = new Angles( pitch, yaw, roll );
}
if ( !string.IsNullOrEmpty( parentId ) && Guid.TryParse( parentId, out var parentGuid ) )
{
var parentObject = SceneEditorSession.Active.Scene.Directory.FindByGuid( parentGuid );
if ( parentObject != null )
{
duplicate.SetParent( parentObject );
}
else
{
result.IsError = true;
result.Content.Add(
new TextContent { Text = $"Parent GameObject with ID '{parentId}' not found." } );
return result;
}
}
}
result.Content.Add( new TextContent { Text = $"Successfully duplicated GameObject: {gameObject.Name}" } );
result.StructuredContent = new
{
duplicatedGameObjectId = duplicate.Id,
duplicatedGameObjectName = duplicate.Name,
duplicatedGameObjectPosition =
new[] { duplicate.WorldPosition.x, duplicate.WorldPosition.y, duplicate.WorldPosition.z },
duplicatedGameObjectRotation = new[]
{
duplicate.WorldRotation.Pitch(), duplicate.WorldRotation.Yaw(), duplicate.WorldRotation.Roll()
}
};
return result;
}
}
using System;
using System.Numerics;
using Editor;
using IconRenderer;
using Sandbox;
internal class IconRendering : SceneRenderingWidget
{
public readonly CameraComponent camera;
public readonly SkinnedModelRenderer model;
public readonly DirectionalLight light;
public readonly SpriteRenderer mockBackground;
private float hoverTime;
public IconRendering( string modelName, string background ) : base( null )
{
Size = 512;
Scene = Scene.CreateEditorScene();
using ( Scene.Push() )
{
{
camera = new GameObject( true, "camera" ).GetOrAddComponent<CameraComponent>( false );
camera.BackgroundColor = Color.Black;
camera.Enabled = true;
}
{
light = new GameObject( true, "light" ).GetOrAddComponent<DirectionalLight>( false );
light.LightColor = Color.White;
light.Enabled = true;
}
{
model = new GameObject( true, "model" ).GetOrAddComponent<SkinnedModelRenderer>( false );
model.Model = Model.Load( modelName ?? "models/error.vmdl" );
model.Enabled = true;
}
{
mockBackground = new GameObject( true, "mockBackground" ).GetOrAddComponent<SpriteRenderer>( false );
mockBackground.Texture = Texture.Load( background );
mockBackground.Size = new Vector2( 512, 512 );
mockBackground.LocalPosition = new Vector3( -256, 0, 0 );
mockBackground.Enabled = true;
}
}
}
public override void OnDestroyed()
{
base.OnDestroyed();
Scene?.Destroy();
Scene = null;
}
public override void PreFrame()
{
Scene.EditorTick( RealTime.Now, RealTime.Delta );
}
}
using Editor;
using Sandbox;
using Todo.Dialogs;
namespace Todo.Widgets;
public sealed class ToolsControls : Widget
{
public ToolsControls( Widget parent ) : base( parent )
{
Layout = Layout.Row();
Layout.Spacing = 2f;
ToolButton refreshButton = Layout.Add( new ToolButton( "", "refresh", this ) );
refreshButton.MouseClick = TodoDock.Instance.RefreshItems;
refreshButton.ToolTip = "Refresh All";
ToolButton showVisibilityButton = Layout.Add( new ToolButton( "", "visibility", this ) );
showVisibilityButton.MouseClick = OpenVisibilityMenu;
showVisibilityButton.ToolTip = "Change Visibility";
showVisibilityButton.OnPaintOverride = () =>
{
OnVisibilityPaint( showVisibilityButton.LocalRect, showVisibilityButton );
return true;
};
ToolButton moreButton = Layout.Add( new ToolButton( "", "more_vert", this ) );
moreButton.MouseClick = OpenMoreMenu;
moreButton.ToolTip = "More";
}
protected override void OnPaint()
{
Paint.ClearPen();
Paint.SetBrush( Theme.ControlBackground );
Paint.DrawRect( LocalRect, 4 );
}
// Edited version of ToolButton's OnPaint method
private void OnVisibilityPaint( Rect rect, ToolButton button )
{
Color color = Color.White;
bool showManual = TodoDock.Cookies.ShowManualEntries;
bool showCode = TodoDock.Cookies.ShowCodeEntries;
if ( showManual && showCode )
{
color = Theme.Blue;
}
else if ( showCode )
{
color = Theme.Green;
}
else if ( showManual is false && showCode is false )
{
color = Theme.TextDisabled;
}
Paint.ClearPen();
if ( Paint.HasMouseOver )
Paint.SetBrush( Theme.SurfaceBackground );
else
Paint.SetBrush( Theme.ControlBackground );
Paint.DrawRect( rect.Shrink( 1.0f ), Theme.ControlRadius );
Paint.SetPen( color );
Paint.DrawIcon( rect, button.Icon, 14, TextFlag.Center );
Update();
}
private void OpenVisibilityMenu()
{
var menu = new Menu( this );
{
var option = menu.AddOption( new Option( this, "Show Manual Entries", "checklist" ) );
option.Checkable = true;
option.Checked = TodoDock.Cookies.ShowManualEntries;
option.Toggled = SetManual;
}
{
var option = menu.AddOption( new Option( this, "Show Code Entries", "code" ) );
option.Checkable = true;
option.Checked = TodoDock.Cookies.ShowCodeEntries;
option.Toggled = SetCode;
}
menu.DeleteOnClose = true;
menu.OpenAtCursor( true );
}
[Shortcut( "todo.toggle-manual-entries", "CTRL+1", typeof( TodoDock ), ShortcutType.Application )]
private void ToggleManual()
{
SetManual( !TodoDock.Cookies.ShowManualEntries );
}
[Shortcut( "todo.toggle-code-entries", "CTRL+2", typeof( TodoDock ), ShortcutType.Application )]
private void ToggleCode()
{
SetCode( !TodoDock.Cookies.ShowCodeEntries );
}
private void SetManual( bool state )
{
TodoDock.Cookies.ShowManualEntries = state;
TodoDock.Instance.SaveAndRefresh();
}
private void SetCode( bool state )
{
TodoDock.Cookies.ShowCodeEntries = state;
TodoDock.Instance.SaveAndRefresh();
}
private void OpenMoreMenu()
{
var menu = new Menu( this );
{
var option = menu.AddOption( new Option( this, "Import Entries", "download" ) );
option.Triggered = TodoDock.Instance.Import;
}
{
var option = menu.AddOption( new Option( this, "Export Entries", "upload" ) );
option.Triggered = TodoDock.Instance.Export;
}
menu.AddSeparator();
{
var option = menu.AddOption( new Option( this, "Settings", "settings" ) );
option.Triggered = OpenSettingsWidget;
}
{
var option = menu.AddOption( new Option( this, "Help", "question_mark" ) );
option.Triggered = OpenHelpWidget;
}
menu.DeleteOnClose = true;
menu.OpenAtCursor( true );
}
private void OpenHelpWidget()
{
var widget = new HelpDialog( null );
widget.Show();
}
private void OpenSettingsWidget()
{
var widget = new SettingsDialog( null );
widget.Show();
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Editor;
using Sandbox;
using Sandbox.UI;
using Sandbox.VR;
using Label = Editor.Label;
namespace DataTablesEditor;
/// <summary>
/// I am absolutely not proud of this at all.
/// </summary>
internal class TableView : Widget
{
public ListView ListView { get; set; }
public List<Column> Columns { get; set; } = new();
TableHeader Header;
public Action<object> ItemClicked;
public Dictionary<VirtualWidget, int> WidgetIndexMap = new();
public TableView( Widget parent ) : base( parent )
{
ListView = new( this );
ListView.ItemPaint = widget => PaintRow( widget );
ListView.ItemSize = new Vector2( 0, 24 );
ListView.ItemSpacing = 0;
ListView.ItemClicked = o =>
{
if ( ItemClicked is not null )
ItemClicked( o );
};
ListView.Margin = new Margin( 0, 4, 0, 0 );
ListView.MultiSelect = true;
Layout = Layout.Column();
Layout.Add( Header = new TableHeader( this ) );
Layout.Add( ListView );
}
public Column AddColumn( string name = null, int? width = null, Func<object, string> value = null )
{
var col = new Column();
col.Name = name;
col.Width = width ?? 50;
col.Value = value;
Columns.Add( col );
return col;
}
protected override void DoLayout()
{
base.DoLayout();
int i = 0;
foreach ( var lbl in Header.Labels )
{
Columns[i].Width = (int)lbl.Width;
i++;
}
}
public void FillHeader()
{
foreach ( var column in Columns )
{
Header.AddColumn( column );
}
}
public void SetItems<T>( IEnumerable<T> items )
{
foreach ( var elem in items.ToList().Cast<object>() )
{
ListView.AddItem( elem );
}
}
public void AddItem( object item )
{
ListView.AddItem( item );
}
private void PaintRow( VirtualWidget widget )
{
/*if ( widget.Object is not T t )
return;*/
var isAlt = widget.Row % 2 == 0;
var backgroundColor = widget.Selected
? Theme.Selection
: (widget.Hovered ? Theme.ButtonDefault.WithAlpha( 0.7f ) :
isAlt ? Color.Parse( "#262627" ).GetValueOrDefault() : Color.Parse( "#313131" ).GetValueOrDefault());
Paint.ClearPen();
Paint.SetBrush( backgroundColor );
Paint.DrawRect( widget.Rect );
Rect rect = widget.Rect;
Paint.SetDefaultFont();
foreach ( var column in Columns )
{
Paint.SetPen( widget.Selected ? Color.White : column.TextColor );
var width = column.Width + 4;
rect.Width = width;
Paint.DrawText( rect.Shrink( 8, 0 ), column.Value(widget.Object), column.TextFlag | TextFlag.CenterVertically | TextFlag.SingleLine );
rect.Left += width;
Paint.SetPen( Color.Parse("#414141").GetValueOrDefault() );
Paint.DrawLine( rect.TopLeft.WithX( rect.TopLeft.x - 1 ), rect.BottomLeft.WithX( rect.BottomLeft.x - 1 ) );
Paint.SetPen( widget.Selected ? Color.White : Theme.ControlText );
}
Paint.SetPen( Color.Parse("#414141").GetValueOrDefault() );
Paint.DrawLine( widget.Rect.BottomLeft, widget.Rect.BottomRight );
}
protected override void OnPaint()
{
base.OnPaint();
Paint.ClearPen();
Paint.SetBrush( Theme.WidgetBackground.WithAlpha( 0.5f ) );
Paint.DrawRect( LocalRect );
Paint.ClearPen();
Paint.SetBrush( Theme.WindowBackground );
Paint.DrawRect( LocalRect.Shrink( 0, 0, 0, LocalRect.Height - 30 ) );
}
}
internal class Column
{
private string _name;
public string Name
{
get
{
return _name;
}
set
{
if ( value is null )
_name = null;
else
{
StringBuilder result = new StringBuilder();
result.Append( value[0] );
for (int i = 1; i < value.Length; i++)
{
if ( char.IsUpper( value[i] ) && !char.IsUpper( value[i - 1] ) )
result.Append( ' ' );
result.Append( value[i] );
}
_name = result.ToString();
}
}
}
public int Width;
public Func<object, string> Value;
public TextFlag TextFlag = TextFlag.Left;
public Label Label;
public Color TextColor = Theme.ControlText;
}
internal class TableHeader : Widget
{
readonly TableView Table;
public List<Label> Labels = new();
public HeaderSplitter _splitter;
private static List<TableHeader> _headers;
static TableHeader()
{
_headers = new();
}
[EditorEvent.Frame]
public static void Frame()
{
foreach ( var Header in _headers )
{
if ( Header is null || !Header.IsValid )
continue;
var list = Header._splitter.Labels;
for ( int i = 0; i < list.Count; i++ )
{
Header.Table.Columns[i].Width = (int)list[i].Width;
Header.Table.ListView.Update();
}
}
}
public TableHeader( TableView parent ) : base( parent )
{
Table = parent;
MinimumHeight = 25;
Layout = Layout.Row();
_splitter = new(this);
_splitter.IsHorizontal = true;
_headers.Add( this );
Layout.Add( _splitter );
}
public override void Close()
{
base.Close();
_headers.Remove( this );
}
public void AddColumn( Column column )
{
if ( _splitter.IsValid() )
Labels.Add( _splitter.AddColumn( column ) );
}
}
internal class HeaderSplitter : Splitter
{
public List<Label> Labels = new();
public HeaderSplitter(Widget parent) : base(parent)
{
}
private int splitterCount = 0;
public Label AddColumn( Column column )
{
var lbl = new Label( column.Name );
lbl.ContentMargins = new Margin( 8, 0, 0, 0 );
lbl.SetStyles( "font-weight: bold; font-size: 12px;" );
lbl.OnPaintOverride = () =>
{
Paint.SetBrush( ControlWidget.ControlColor );
Paint.SetPen( Color.Transparent );
Paint.DrawRect( lbl.LocalRect );
return false;
};
column.Label = lbl;
Labels.Add( lbl );
AddWidget( lbl );
SetCollapsible( splitterCount++, false );
return lbl;
}
}
using System;
using System.Collections.Generic;
using Andicraft.SecondOrderDynamics;
using Editor;
using Sandbox;
public class DynamicsGraphWidget : Widget
{
private SerializedObject _targetObject;
public DynamicsGraphWidget(Widget parent, SerializedObject baseProp) : base(parent, false)
{
// Create a Column Layout
Layout = Layout.Column();
// Give it some Margins/Spacing
Layout.Margin = 4;
Layout.Spacing = 4;
// Apply some CSS styling
ToolTip = "Five second preview of the current dynamics settings";
var f = new Frame( this ) { HorizontalSizeMode = SizeMode.Expand, Size = new Vector2( 100 ) };
Layout.Add(f);
_targetObject = baseProp;
}
protected override void OnPaint()
{
Paint.SetBrushAndPen( new Color( 0, 0, 0, .2f ), new Color( 0, 0, 0, .2f ) );
var borderRect = ContentRect;
borderRect.Left += 1;
Paint.DrawRect( borderRect );
_targetObject.TryGetProperty( "Frequency", out var f );
_targetObject.TryGetProperty( "Damping", out var d );
_targetObject.TryGetProperty( "Response", out var r );
var fd = new FloatDynamics( f.GetValue<float>(), d.GetValue<float>(), r.GetValue<float>(), 1);
Paint.SetPen( Color.Red );
Paint.ClearBrush();
var linePts = new List<Vector2>();
var baseY = ContentRect.Center.y;
var waveHeight = ContentRect.Height * .5f;
for ( int i = 0; i < 60*5; i++ )
{
var x = (ContentRect.Width / 60f*5f) * (i / 60f * 5f);
var y = Math.Clamp(baseY + fd.Update( 1 / 60f, 0 ) * waveHeight, ContentRect.Top, ContentRect.Bottom);
linePts.Add( new Vector2( x, y ) );
}
Paint.DrawLine(linePts);
}
}
using RbxlReader.DataTypes;
namespace RbxlReader.Instances;
public class InstanceProperty {
public PropertyType Type {get;}
public object Value {get; set;}
public byte[] RawBuffer = Array.Empty<byte>();
public InstanceProperty(PropertyType type, object value) {
this.Type = type;
Value = value;
}
}namespace RbxlReader.DataTypes;
public class Vector3int16 {
public short X,Y,Z;
public override string ToString() => $"{X}, {Y}, {Z}";
}using Editor;
using Sandbox;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace QuickAsset;
public class QuickAssetMenu : Menu
{
public static QuickAssetMenu Current { get; set; }
public string Filter { get; set; }
private LineEdit _searchInput;
private ListView _list;
private bool UseCloud
{
get => EditorCookie.Get( "qam.UseCloud", false );
set => EditorCookie.Set( "qam.UseCloud", value );
}
private IReadOnlyList<string> RecentPackages
{
get => EditorCookie.Get<List<string>>( "qam.RecentPackages", [] );
set => EditorCookie.Set( "qam.RecentPackages", value );
}
private IReadOnlyList<string> RecentAssets
{
get => EditorCookie.Get<List<string>>( "qam.RecentAssets", [] );
set => EditorCookie.Set( "qam.RecentAssets", value );
}
public QuickAssetMenu() : base( null )
{
IsWindow = false;
Current = this;
Layout = Layout.Column();
FixedWidth = 300;
FixedHeight = 410;
Layout.Margin = 8;
Layout.Spacing = 8;
var row = Layout.AddRow();
_searchInput = row.Add( new LineEdit(), 1 );
_searchInput.TextChanged += v =>
{
UpdateList();
};
_searchInput.PlaceholderText = "Search for assets...";
_searchInput.FocusMode = FocusMode.Click;
_searchInput.Focus();
var cloudToggle = row.Add( new IconButton( "cloud_off" ) );
void SetCloudToggle()
{
cloudToggle.Icon = UseCloud ? "cloud" : "cloud_off";
cloudToggle.ToolTip = UseCloud ? "Cloud Enabled" : "Cloud Disabled";
}
SetCloudToggle();
cloudToggle.OnClick = () =>
{
UseCloud = !UseCloud;
SetCloudToggle();
UpdateList();
};
Bind( nameof( Filter ) ).ReadOnly().From( _searchInput, x => x.Text );
Layout.Add( BuildAssets(), 1 );
}
// fucking shite x2
private bool IsSpawnable( AssetType type )
{
if ( type == null )
return false;
if ( type.IsSimpleAsset )
return false;
if ( type.FileExtension == "vmdl" )
return true;
if ( type.FileExtension == "prefab" )
return true;
if ( type.FileExtension == "sound" )
return true;
if ( type.FileExtension == "vpcf" )
return true;
return false;
}
private void UpdateList()
{
if ( UseCloud )
_ = SetListCloud();
else
SetListAssets();
}
private void SetListAssets()
{
List<object> options = new();
if ( string.IsNullOrEmpty( Filter ) )
{
foreach ( var recent in RecentAssets )
{
var asset = AssetSystem.FindByPath( recent );
if ( asset == null )
continue;
if ( !IsSpawnable( asset.AssetType ) )
continue;
options.Add( asset );
}
}
else
{
var assets = AssetSystem.All
.Where( x => x.Path.Contains( Filter, StringComparison.OrdinalIgnoreCase ) )
.Where( x => IsSpawnable( x.AssetType ) )
.OrderByDescending( x => x.LastOpened )
.OrderByDescending( x => RecentAssets.Contains( x.Path ) );
options.AddRange( assets );
}
_list.SetItems( options );
}
private async Task SetListCloud()
{
List<object> options = new();
if ( string.IsNullOrEmpty( Filter ) )
{
foreach ( var ident in RecentPackages )
{
var package = await Package.Fetch( ident, false );
if ( package == null )
continue;
if ( !IsSpawnable( GetAssetType( package.TypeName ) ) )
continue;
options.Add( package );
}
}
else
{
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
var found = await Package.FindAsync( Filter, 200, 0, token );
if ( found.Packages.Length == 0 )
return;
//
// Add them all to the list
//
var packages = found.Packages.Where( x => IsSpawnable( GetAssetType( x.TypeName ) ) )
.OrderByDescending( x => x.Updated )
.OrderByDescending( x => RecentPackages.Contains( x.FullIdent ) );
options.AddRange( packages );
}
_list.SetItems( options );
}
private Widget BuildAssets()
{
var canvas = new Widget( null );
canvas.Layout = Layout.Row();
_list = new ListView( canvas );
UpdateList();
_list.Margin = 0;
_list.ItemSize = new Vector2( 0, 40 );
_list.ItemPaint = PaintListMode;
_list.ItemSpacing = 0;
_list.ItemClicked = o =>
{
if ( o is Asset a )
_ = OnSelected( a );
if ( o is Package p )
_ = OnSelected( p );
};
_list.OnPaintOverride = PaintList;
canvas.Layout.Add( _list );
return canvas;
}
public static AssetType GetAssetType( string typeName ) => typeName switch
{
"map" => AssetType.MapFile,
"model" => AssetType.Model,
"material" => AssetType.Material,
"sound" => AssetType.SoundFile,
"shader" => AssetType.Shader,
_ => null
};
private void PaintListMode( VirtualWidget item )
{
string title = "";
string subtitle = "";
Color color = Color.White;
bool isRecent = false;
{
if ( item.Object is Asset asset )
{
title = asset.Name;
subtitle = asset.AssetType.FriendlyName;
color = asset.AssetType.Color;
isRecent = RecentAssets.Contains( asset.Path );
}
else if ( item.Object is Package package )
{
var type = GetAssetType( package.TypeName );
title = package.Title;
subtitle = package.Org.Title;
color = type.Color;
isRecent = RecentPackages.Contains( package.FullIdent );
}
else
{
Log.Warning( "Can't paint type" );
return;
}
}
if ( Paint.HasSelected || Paint.HasPressed )
{
Paint.ClearPen();
Paint.SetBrush( Paint.HasPressed ? Theme.Primary.WithAlpha( 0.4f ) : Theme.Primary.WithAlpha( 0.2f ) );
Paint.DrawRect( item.Rect.Shrink( 0 ), 3 );
Paint.SetPen( Theme.White );
}
else if ( Paint.HasMouseOver )
{
Paint.ClearPen();
Paint.SetBrush( Theme.Blue.Darken( 0.7f ).Desaturate( 0.3f ).WithAlpha( 0.5f ) );
Paint.DrawRect( item.Rect );
Paint.SetPen( Theme.White );
}
var itemRect = item.Rect.Shrink( 1 );
itemRect = itemRect.Shrink( 0, 0, 8, 0 );
var rect = itemRect;
var textRect = rect.Shrink( 40 + 8, 4, 0, 4 );
{
Paint.SetPen( Theme.ControlText );
Paint.SetDefaultFont();
Paint.DrawText( textRect, title, TextFlag.LeftTop | TextFlag.SingleLine );
}
{
Paint.SetDefaultFont();
Paint.SetPen( Theme.ControlText.WithAlpha( 0.5f ) );
Paint.DrawText( textRect, subtitle, TextFlag.LeftBottom | TextFlag.SingleLine );
}
if ( isRecent )
{
Paint.SetDefaultFont();
Paint.SetPen( Theme.ControlText.WithAlpha( 0.5f ) );
Paint.DrawIcon( textRect, "history", 13.0f, TextFlag.RightCenter );
}
Paint.ClearPen();
var ir = itemRect;
ir.Size = new Vector2( itemRect.Height, itemRect.Height );
{
var aPos = rect.TopLeft;
var bPos = rect.BottomLeft;
var aColor = color.WithAlpha( 0 );
var bColor = color.WithAlpha( 0.5f );
Paint.SetBrushLinear( aPos, bPos, aColor, bColor );
Paint.DrawRect( ir );
if ( item.Object is Asset asset )
{
Paint.Draw( ir, asset.GetAssetThumb() ?? asset.AssetType.Icon64 );
}
else if ( item.Object is Package package )
{
Paint.Draw( ir, package.Thumb );
}
}
ir.Top = ir.Bottom - 4;
Paint.ClearPen();
Paint.SetBrush( color );
Paint.DrawRect( ir );
}
public override void Hide()
{
base.Hide();
WindowOpacity = 0;
}
protected override void OnKeyPress( KeyEvent e )
{
if ( e.KeyboardModifiers.HasFlag( KeyboardModifiers.Ctrl ) && e.Key is KeyCode.K )
_searchInput.Focus();
}
// fucking shite
private Ray GetRay( Vector2 cursorPosition, Vector2 screenSize )
{
var state = SceneViewportWidget.LastSelected.State;
var fov = 80.0f;
var aspect = screenSize.x / screenSize.y;
var posNormalized = new Vector2( (2.0f * cursorPosition.x / screenSize.x) - 1, (2.0f * cursorPosition.y / screenSize.y) - 1 ) * -1.0f;
float halfWidth = MathF.Tan( fov * MathF.PI / 360.0f );
float halfHeight = halfWidth / aspect;
var ray = new Vector3( 1.0f, posNormalized.x / (1.0f / halfWidth), posNormalized.y / (1.0f / halfHeight) ) * state.CameraRotation;
return new Ray( state.CameraPosition, ray.Normal );
}
private async Task CreateGameObject( string path )
{
using var a = SceneEditorSession.Scope();
using var b = SceneEditorSession.Active.UndoScope( "Quick Add Asset" ).Push();
var drop = await BaseDropObject.CreateDropFor( path );
var rect = SceneViewportWidget.LastSelected.ScreenRect;
var cursorPos = CursorPosition - rect.TopLeft;
var state = SceneViewportWidget.LastSelected.State;
var ray = GetRay( cursorPos, rect.Size );
var tr = SceneEditorSession.Active.Scene.Trace
.WithoutTags( "trigger" )
.Ray( ray, 1000f )
.Run();
if ( drop is not null )
{
await drop.StartInitialize( path );
drop.UpdateDrag( tr, EditorScene.GizmoSettings );
using ( var sc = SceneEditorSession.Active.Scene.Push() )
{
await drop.OnDrop();
var go = drop.GameObject;
if ( go.IsValid() )
{
EditorScene.Selection.Add( go );
}
}
drop.Delete();
}
}
private async Task OnSelected( Package package )
{
Close();
// Remove existing, append to back
RecentPackages = [package.FullIdent, .. RecentPackages.Where( x => x != package.FullIdent ).Take( 8 )];
await CreateGameObject( package.Url );
}
private async Task OnSelected( Asset asset )
{
Close();
// Remove existing, append to back
RecentAssets = [asset.Path, .. RecentAssets.Where( x => x != asset.Path ).Take( 8 )];
await CreateGameObject( asset.Path );
}
private bool PaintList()
{
if ( _list.Items.Any() )
return false;
Paint.ClearBrush();
Paint.ClearPen();
Paint.SetPen( Theme.ControlText.WithAlpha( 0.5f ) );
Paint.SetDefaultFont();
if ( string.IsNullOrEmpty( Filter ) )
{
Paint.DrawText( _list.ContentRect, "Type to start searching for assets" );
}
else
{
Paint.DrawText( _list.ContentRect, "Nothing found :(" );
}
return true;
}
private Vector2 CursorPosition;
[Shortcut( "quick-asset.toggle", "SHIFT+A", ShortcutType.Widget )]
public static void ShowQuickAsset()
{
var rect = SceneViewWidget.Current.ScreenRect;
var cursorPos = Editor.Application.CursorPosition;
if ( !rect.IsInside( cursorPos ) )
return;
Current?.Destroy();
Current = new QuickAssetMenu();
Current.Show();
Current.CursorPosition = cursorPos;
Current.Position = cursorPos + 16;
}
}
using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.Win32;
namespace Editor.CodeEditors;
[Title( "Cursor" )]
public class Cursor : ICodeEditor
{
public void OpenFile( string path, int? line, int? column )
{
var codeWorkspace = $"{Environment.CurrentDirectory}/s&box.code-workspace";
CreateWorkspace( codeWorkspace );
Launch( $"\"{codeWorkspace}\" -g \"{path}:{line}:{column}\"" );
}
public void OpenSolution()
{
var codeWorkspace = $"{Environment.CurrentDirectory}/s&box.code-workspace";
CreateWorkspace( codeWorkspace );
// Need to wrap the code workspace in quotes, but CreateWorkspace doesn't need that
Launch( $"\"{codeWorkspace}\"" );
}
public void OpenAddon( Project addon )
{
var projectPath = (addon != null) ? addon.GetRootPath() : "";
Launch( $"\"{projectPath}\"" );
}
public bool IsInstalled() => !string.IsNullOrEmpty( GetLocation() );
private static void Launch( string arguments )
{
var startInfo = new System.Diagnostics.ProcessStartInfo
{
FileName = GetLocation(),
Arguments = arguments,
CreateNoWindow = true,
};
System.Diagnostics.Process.Start( startInfo );
}
private static void CreateWorkspace( string path )
{
StringBuilder builder = new();
builder.AppendLine( "{" );
builder.AppendLine( " \"folders\": [" );
foreach ( var addon in EditorUtility.Projects.GetAll() )
{
if ( !addon.Active ) continue;
builder.AppendLine( " {" );
builder.AppendLine( $" \"name\": \"{addon.Config.Ident}\"," );
builder.AppendLine( $" \"path\": \"{addon.GetRootPath().Replace( @"\", @"\\" )}\"," );
builder.AppendLine( " }," );
}
builder.AppendLine( " ]" );
// You need the C# extension to do anything
// builder.AppendLine( " \"extensions\": {" );
// builder.AppendLine( " \"recommendations\": [" );
// builder.AppendLine( " \"ms-dotnettools.csharp\"" );
// builder.AppendLine( " ]," );
// builder.AppendLine( " }" );
// Settings: make sure we're using .net 6 and that roslyn analyzers are on (they never fucking are)
// builder.AppendLine( " \"settings\": {" );
// builder.AppendLine( " \"omnisharp.useModernNet\": true," );
// builder.AppendLine( " \"omnisharp.enableRoslynAnalyzers\": true" );
// builder.AppendLine( " }" );
builder.AppendLine( "}" );
File.WriteAllText( path, builder.ToString() );
}
static string Location;
[System.Diagnostics.CodeAnalysis.SuppressMessage( "Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>" )]
private static string GetLocation()
{
if ( Location != null )
{
return Location;
}
string value = null;
using ( var key = Registry.ClassesRoot.OpenSubKey( @"Applications\\Cursor.exe\\shell\\open\\command" ) )
{
value = key?.GetValue( "" ) as string;
}
if ( value == null )
{
return null;
}
// Given `"C:\Users\<user>\AppData\Local\Programs\cursor\Cursor.exe" "%1"` grab the first bit
Regex rgx = new Regex( "\"(.*)\" \".*\"", RegexOptions.IgnoreCase );
var matches = rgx.Matches( value );
if ( matches.Count == 0 || matches[0].Groups.Count < 2 )
{
return null;
}
Location = matches[0].Groups[1].Value;
return Location;
}
}
using Editor;
public static class MyEditorMenu
{
[Menu( "Editor", "TestLibrary1/My Menu Option" )]
public static void OpenMyMenu()
{
EditorUtility.DisplayDialog( "It worked!", "This is being called from your library's editor code!" );
}
}
namespace Editor.rectedittemplateexporter;
public enum DragState
{
None,
WaitingForMovement,
Dragging,
}
enum GridSnapMode
{
Nearest,
RoundDown,
RoundUp,
}
public class RectView : Widget
{
private readonly Window Session;
private Document Document => Session.Document;
private Rect DrawRect;
private Pixmap SourceImage;
private Pixmap ScaledImage;
private DragState DragState;
private Vector2 DragStartPos;
private Rect NewRect;
private List<Document.Rectangle> RectanglesUnderCursor;
public RectView( Window session ) : base( session )
{
Session = session;
Name = "Rect View";
WindowTitle = "Rect View";
SetWindowIcon( "space_dashboard" );
MouseTracking = true;
FocusMode = FocusMode.Click;
DrawRect = GetDrawRect();
}
private Vector2 SnapUVToGrid( Vector2 uv )
{
var gridCountX = GetGridCountX();
var gridCountY = GetGridCountY();
var x = (int)(gridCountX * uv.x + 0.5f);
var y = (int)(gridCountY * uv.y + 0.5f);
return new Vector2( x / (float)gridCountX, y / (float)gridCountY );
}
private Vector2 PixelToUV_OnGrid( Vector2 vPixel )
{
return SnapUVToGrid( PixelToUV( vPixel ) );
}
private void DragUpdate( Vector2 mousePos )
{
var minStart = PixelToUV_OnGrid( DragStartPos );
var maxStart = PixelToUV_OnGrid( DragStartPos );
var current = PixelToUV_OnGrid( mousePos );
var min = Vector2.Min( current, minStart );
var max = Vector2.Max( current, maxStart );
NewRect = new Rect( min, max - min );
Update();
}
[EditorEvent.Frame]
protected void OnFrame()
{
FindRectanglesUnderCursor( FromScreen( Application.CursorPosition ) );
}
protected override void OnMouseMove( MouseEvent e )
{
base.OnMouseMove( e );
if ( DragState == DragState.WaitingForMovement && (NewRect.Width > 0.0f || NewRect.Height > 0.0f) )
{
DragState = DragState.Dragging;
}
if ( DragState != DragState.None )
{
DragUpdate( e.LocalPosition );
}
}
protected override void OnMousePress( MouseEvent e )
{
base.OnMousePress( e );
if ( e.Button == MouseButtons.Left )
{
DragState = DragState.WaitingForMovement;
DragStartPos = e.LocalPosition;
}
}
protected override void OnMouseReleased( MouseEvent e )
{
base.OnMouseReleased( e );
if ( e.Button == MouseButtons.Left )
{
if ( DragState == DragState.Dragging )
{
if ( Document is not null && NewRect.Width > 0.0f && NewRect.Height > 0.0f )
{
Document.SelectRectangle( Document.AddRectangle( NewRect ), SelectionOperation.Set );
Session.Snapshot( "Create Rectangle" );
}
}
else
{
Document.SelectRectangle( GetFirstRectangleUnderCursor(), SelectionOperation.Set );
Session.Snapshot( "Select Rectangle" );
}
DragState = DragState.None;
NewRect = default;
}
}
public void SetMaterial( Material material )
{
SourceImage = null;
ScaledImage = null;
if ( material is null )
return;
var texture = material.FirstTexture;
if ( texture is null )
return;
SourceImage = Pixmap.FromTexture( texture, false );
if ( SourceImage is null )
return;
UpdateScaledBackgroundImage();
}
private void UpdateScaledBackgroundImage()
{
ScaledImage = SourceImage?.Resize( DrawRect.Size );
}
protected override void OnResize()
{
base.OnResize();
DrawRect = GetDrawRect();
UpdateScaledBackgroundImage();
}
protected override void OnPaint()
{
Paint.ClearPen();
Paint.SetBrush( Theme.ControlBackground );
Paint.DrawRect( LocalRect );
Paint.SetBrush( Color.Gray );
Paint.DrawRect( DrawRect );
if ( ScaledImage is not null )
{
Paint.Draw( DrawRect, ScaledImage );
}
if ( Session.GridEnabled )
{
DrawGrid();
}
DrawRectangleSet( Document?.Rectangles );
if ( DragState == DragState.Dragging )
{
var topLeft = UVToPixel( NewRect.TopLeft );
var bottomRight = UVToPixel( NewRect.BottomRight );
var newRect = new Rect( topLeft, bottomRight - topLeft );
Paint.ClearBrush();
Paint.SetPen( Color.Yellow, 3 );
Paint.DrawRect( newRect );
}
}
private Vector2 UVToPixel( Vector2 uv )
{
return new Vector2( (int)((uv.x * DrawRect.Width) + DrawRect.Left), (int)((uv.y * DrawRect.Height) + DrawRect.Top) );
}
private Vector2 PixelToUV( Vector2 pixel )
{
return new Vector2( (pixel.x - DrawRect.Left) / DrawRect.Width, (pixel.y - DrawRect.Top) / DrawRect.Height );
}
private int GetGridPower()
{
return Session.GridEnabled ? Session.GridPower : 10;
}
private int GetGridCountX()
{
var width = SourceImage is null ? 512 : System.Math.Max( (int)SourceImage.Width, 1 );
var height = SourceImage is null ? 512 : System.Math.Max( (int)SourceImage.Height, 1 );
var gridPower = GetGridPower();
if ( width >= height )
{
return 1 << gridPower;
}
else
{
return (1 << gridPower) * width / height;
}
}
private int GetGridCountY()
{
var width = SourceImage is null ? 512 : System.Math.Max( (int)SourceImage.Width, 1 );
var height = SourceImage is null ? 512 : System.Math.Max( (int)SourceImage.Height, 1 );
var gridPower = GetGridPower();
if ( height >= width )
{
return 1 << gridPower;
}
else
{
return (1 << gridPower) * height / width;
}
}
public Document.Rectangle GetFirstRectangleUnderCursor()
{
return RectanglesUnderCursor?.FirstOrDefault();
}
public void FindRectanglesUnderCursor( Vector2 mousePos )
{
RectanglesUnderCursor = FindRectanglesContainingPoint( PixelToUV( mousePos ) );
Update();
}
public List<Document.Rectangle> FindRectanglesContainingPoint( Vector2 vPoint )
{
return Document.Rectangles
.Where( rectangle => rectangle.IsPointInRectangle( vPoint ) )
.Select( rectangle => new { Rectangle = rectangle, Distance = rectangle.DistanceFromPointToCenter( vPoint ) } )
.OrderBy( item => item.Distance )
.Select( item => item.Rectangle )
.ToList();
}
private void DrawRectangleSet( IEnumerable<Document.Rectangle> rectangles )
{
if ( rectangles is null )
return;
foreach ( var rectangle in rectangles.Where( x => !Document.IsRectangleSelected( x ) ) )
{
Paint.SetBrush( rectangle.Color.WithAlpha( 0.5f ) );
Paint.SetPen( Color.Black.WithAlpha( 192 / 255.0f ), 1 );
DrawRectangle( rectangle );
}
foreach ( var rectangle in Document.SelectedRectangles )
{
Paint.SetBrush( new Color32( 255, 255, 0, 64 ) );
Paint.SetPen( new Color32( 255, 255, 0 ), 1 );
DrawRectangle( rectangle );
}
var rectangleUnderCursor = GetFirstRectangleUnderCursor();
if ( rectangleUnderCursor is not null && !Document.IsRectangleSelected( rectangleUnderCursor ) )
{
Paint.SetBrush( new Color32( 0, 255, 0, 64 ) );
Paint.SetPen( Color.Green );
DrawRectangle( rectangleUnderCursor );
}
}
private void DrawRectangle( Document.Rectangle rectangle, int nMinInset = 0, int nMaxInset = 0 )
{
if ( rectangle is null )
return;
var minPoint = UVToPixel( rectangle.Min );
var maxPoint = UVToPixel( rectangle.Max );
minPoint += new Vector2( nMinInset, nMinInset );
maxPoint -= new Vector2( nMaxInset + 1, nMaxInset + 1 );
Paint.DrawRect( new Rect( minPoint, maxPoint - minPoint ) );
}
private void DrawGrid()
{
const float gridOpacity = 64 / 255.0f;
var gridCountX = GetGridCountX();
var gridCountY = GetGridCountY();
var stepX = 1.0f / gridCountX;
var stepY = 1.0f / gridCountY;
var rect = DrawRect;
Paint.ClearBrush();
for ( int ix = 0; ix <= gridCountX; ++ix )
{
var u = ix * stepX;
var gx = UVToPixel( new Vector2( u, 0 ) ).x;
if ( gx > rect.Left )
{
Paint.SetPen( new Color( 1, 1, 1, gridOpacity ) );
Paint.DrawLine( new Vector2( gx - 1, rect.Top + 1 ), new Vector2( gx - 1, rect.Height + rect.Top - 2 ) );
}
if ( gx < (rect.Left + rect.Width) )
{
Paint.SetPen( new Color( 0, 0, 0, gridOpacity ) );
Paint.DrawLine( new Vector2( gx, rect.Top + 1 ), new Vector2( gx, rect.Height + rect.Top - 2 ) );
}
}
for ( int iy = 0; iy <= gridCountY; ++iy )
{
var v = iy * stepY;
var gy = UVToPixel( new Vector2( 0, v ) ).y;
if ( gy > rect.Top )
{
Paint.SetPen( new Color( 1, 1, 1, gridOpacity ) );
if ( gy == (rect.Top + rect.Height) )
{
Paint.DrawLine( new Vector2( rect.Left, gy - 1 ), new Vector2( rect.Width + rect.Left - 1, gy - 1 ) );
}
else
{
Paint.DrawLine( new Vector2( rect.Left + 1, gy - 1 ), new Vector2( rect.Width + rect.Left - 2, gy - 1 ) );
}
}
if ( gy < (rect.Top + rect.Height) )
{
Paint.SetPen( new Color( 0, 0, 0, gridOpacity ) );
if ( gy == rect.Top )
{
Paint.DrawLine( new Vector2( rect.Left, gy ), new Vector2( rect.Width + rect.Left - 1, gy ) );
}
else
{
Paint.DrawLine( new Vector2( rect.Left + 1, gy ), new Vector2( rect.Width + rect.Left - 2, gy ) );
}
}
}
}
private Rect GetDrawRect()
{
const int marigin = 16;
const int drawSnapSize = 4;
var imageSize = SourceImage is null ? 0 : SourceImage.Size;
var widgetWidth = System.Math.Max( (int)Width - (marigin * 2), 128 );
var widgetHeight = System.Math.Max( (int)Height - (marigin * 2), 128 );
var imageWidth = System.Math.Max( (int)imageSize.x, 1 );
var imageHeight = System.Math.Max( (int)imageSize.y, 1 );
int drawWidth;
int drawHeight;
if ( (imageWidth > 0) && (imageHeight > 0) )
{
var aspect = imageWidth / (float)imageHeight;
var relativeWidth = (int)(widgetWidth / System.MathF.Max( aspect, 1.0f ));
var relativeHeight = (int)(widgetHeight * System.MathF.Min( aspect, 1.0f ));
if ( relativeWidth <= relativeHeight )
{
drawWidth = widgetWidth;
drawHeight = widgetWidth * imageHeight / imageWidth;
}
else
{
drawHeight = widgetHeight;
drawWidth = widgetHeight * imageWidth / imageHeight;
}
}
else
{
var drawSize = System.Math.Min( widgetWidth, widgetHeight );
drawHeight = drawSize;
drawWidth = drawSize;
}
drawWidth = drawWidth / drawSnapSize * drawSnapSize;
drawHeight = drawHeight / drawSnapSize * drawSnapSize;
return new Rect( marigin, marigin, drawWidth, drawHeight );
}
}
using Sandbox;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Panelize;
public abstract class EnumControl<T> : Widget where T : struct, Enum
{
public Dictionary<T, DisplayInfo> DisplayOverrides { get; set; } = new();
/// <summary>
/// Display items in this order.
/// </summary>
public List<T> ValueOverrides { get; set; } = new();
public T Value { get; protected set; }
public Action<T> OnValueChanged { get; set; }
public EnumControl(T defaultValue = default)
{
Value = default;
}
public virtual void SetValue(T value)
{
Value = value;
OnValueChanged?.Invoke( value );
}
public void SetDisplay( T value, string icon = "", string name = "", string description = "", string group = "" )
{
bool iconChanged = !string.IsNullOrEmpty( icon );
bool nameChanged = !string.IsNullOrEmpty( name );
bool descriptionChanged = !string.IsNullOrEmpty( description );
bool groupChanged = !string.IsNullOrEmpty( group );
if ( DisplayOverrides.TryGetValue( value, out var o ) )
{
if(iconChanged)
o.Icon = icon;
if ( nameChanged )
o.Name = name;
if(descriptionChanged)
o.Description = description;
if ( groupChanged )
o.Group = group;
DisplayOverrides[value] = o;
}
else
{
DisplayInfo info = new();
DisplayInfo existing = DisplayInfo.ForEnumValues<T>()
.Where(e => e.value.Equals(value)).FirstOrDefault().info;
info.Icon = iconChanged ? icon : existing.Icon;
info.Name = nameChanged ? name : existing.Name;
info.Description = descriptionChanged ? description : existing.Description;
info.Group = groupChanged ? group : existing.Group;
info.Browsable = true;
DisplayOverrides.Add( value, info );
}
}
/// <summary>
/// Set order of values to be displayed.
/// Values not in list are not displayed.
/// </summary>
/// <param name="values"></param>
public void SetOrder( params T[] values )
{
ValueOverrides = values.ToList();
}
public (T, DisplayInfo)[] GetValueDisplays()
{
var valueDisplays = DisplayInfo.ForEnumValues<T>();
if ( ValueOverrides != null && ValueOverrides.Count > 0 )
{
valueDisplays = valueDisplays
.IntersectBy( ValueOverrides, kv => kv.value )
.OrderBy( kv => ValueOverrides.IndexOf( kv.value ) )
.ToArray();
}
if(DisplayOverrides.Count > 0)
{
List<(T value, DisplayInfo info)> newDisplays = new();
foreach((T value, DisplayInfo info) in valueDisplays)
{
if(DisplayOverrides.ContainsKey(value))
{
DisplayInfo o = DisplayOverrides[value];
newDisplays.Add( (value, o) );
}
else
{
newDisplays.Add( (value, info) );
}
}
valueDisplays = newDisplays.ToArray();
}
return valueDisplays;
}
public DisplayInfo GetValueDisplay(T value)
{
if(DisplayOverrides.ContainsKey(value))
{
return DisplayOverrides[value];
}
return DisplayInfo.ForEnumValues<T>()
.Where( e => e.value.Equals( value ) ).FirstOrDefault().info;
}
}
using Sandbox.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Panelize;
public class BoxSizeControl : Widget
{
LengthControl topControl;
LengthControl leftControl;
LengthControl rightControl;
LengthControl bottomControl;
GridLayout editor;
public BoxSizeControl()
{
Layout = Layout.Column();
editor = Layout.Grid();
editor.Spacing = 16f;
Layout.Add(editor);
SetSizeMode( SizeMode.CanShrink, SizeMode.Default );
topControl = CreateControl();
leftControl = CreateControl();
rightControl = CreateControl();
bottomControl = CreateControl();
editor.AddCell( 1, 0, topControl );
editor.AddCell( 0, 1, leftControl );
editor.AddCell( 2, 1, rightControl );
editor.AddCell( 1, 2, bottomControl );
}
public void Bind(SerializedProperty topProperty, SerializedProperty leftProperty, SerializedProperty rightProperty, SerializedProperty bottomProperty )
{
topControl.Bind( topProperty );
leftControl.Bind( leftProperty );
rightControl.Bind( rightProperty );
bottomControl.Bind( bottomProperty );
}
private LengthControl CreateControl()
{
LengthControl control = new( this, true )
{
MaximumWidth = 110f,
//AmountControlWidth = 110f,
//AmountSliderControlWidth = 40f,
AmountSliderWidth = 70f,
//UnitControlWidth = 110f
};
control.SetUnit( LengthUnit.Auto );
return control;
}
protected override void OnPaint()
{
base.OnPaint();
var rect = editor.GetCellRect( 1, 1 ).Shrink(24f, 8f);
Paint.SetPen( Theme.Grey );
Paint.DrawRect( rect );
Paint.SetPen( Theme.White );
var topRect = editor.GetCellRect( 0, 1 );
topRect.Top += topRect.Height + 16f;
Paint.DrawText( topRect, "Top" );
var leftRect = editor.GetCellRect( 1, 0 );
leftRect.Left += leftRect.Width + 32f;
Paint.DrawText( leftRect, "Left" );
var rightRect = editor.GetCellRect( 1, 2 );
rightRect.Left -= rightRect.Width + 32f;
Paint.DrawText( rightRect, "Right" );
var bottomRect = editor.GetCellRect( 2, 1 );
bottomRect.Top -= bottomRect.Height + 16f;
Paint.DrawText( bottomRect, "Bottom" );
}
}
using System;
using System.Linq;
using System.Reflection;
using Sandbox;
using Sandbox.UI;
namespace Panelize;
public class PropertySheetRow : Widget
{
public Widget Control { get; private set; }
public static PropertySheetRow Create( SerializedProperty property )
{
Widget editor = default;
if(EditorTypeLibrary.TryGetType(property.PropertyType, out _))
{
try
{
editor = ControlWidget.Create( property );
}
catch ( Exception e )
{
Log.Warning( e, $"Error creating controlwidget for {property.Name}" );
}
}
if ( !editor.IsValid() )
{
editor = CanEditAttribute.CreateEditorFor( property.PropertyType );
}
if ( !editor.IsValid() ) return null;
var row = new PropertySheetRow( property );
row.Build( editor );
return row;
}
public static PropertySheetRow Create( PropertyInfo info, object target )
{
CustomSerializedProperty property = new( info, target );
return Create( property );
}
public SerializedProperty Property { get; private set; }
public PropertySheetRow( SerializedProperty property )
{
Property = property;
FocusMode = FocusMode.Click;
}
public void Build( Widget controlWidget, bool hasLabel = true, bool isExpanded = false )
{
if ( Property is null )
return;
var gridLayout = Layout.Grid();
gridLayout.HorizontalSpacing = 0;
gridLayout.Margin = new Margin( 0, 0, 4, 0 );
Layout = gridLayout;
Control = controlWidget;
ToolTip = $"<font>{Property.Description ?? Property.DisplayName}</font>";
HorizontalSizeMode = SizeMode.CanShrink;
Control.HorizontalSizeMode = SizeMode.Flexible;
/*
if ( EditorUtility.Prefabs.GetVariables( property.Parent ) is not null )
{
gridLayout.AddCell( 0, 1, new VariableButton( property, controlWidget ), 1, 1, TextFlag.LeftTop );
}
if ( property.IsNullable )
{
gridLayout.AddCell( 1, 1, new PropertyButton( property, controlWidget ), 1, 1, TextFlag.LeftTop );
controlWidget.Enabled = !property.IsNull;
}
*/
if ( hasLabel )
{
var label = new PropertySheetPropertyLabel( Property );
label.ContentMargins = isExpanded ? new( 0, 0, 0, 4 ) : new( 0, 0, 4, 0 );
gridLayout.AddCell( 2, 1, label, xSpan: (isExpanded ? 2 : 1), alignment: TextFlag.LeftTop );
gridLayout.AddCell( 3 - (isExpanded ? 1 : 0), 1 + (isExpanded ? 1 : 0), controlWidget, alignment: TextFlag.LeftTop );
}
else
{
gridLayout.AddCell( 2, 1, controlWidget, xSpan: 2, alignment: TextFlag.LeftTop );
}
gridLayout.SetColumnStretch( 0, 0, 0, 1 );
gridLayout.SetMinimumColumnWidth( 0, 0 );
gridLayout.SetMinimumColumnWidth( 1, 0 );
gridLayout.SetMinimumColumnWidth( 2, 140 );
}
}
file class PropertySheetPropertyLabel : PropertySheetLabel
{
private SerializedProperty Property { get; }
private Drag _drag;
public PropertySheetPropertyLabel( SerializedProperty property )
{
Property = property;
Text = Property.DisplayName;
IsDraggable = false;
}
protected override void OnDragStart()
{
base.OnDragStart();
_drag = new Drag( this )
{
Data = { Object = Property, Text = Property.As.String }
};
_drag.Execute();
}
protected override void OnPaint()
{
base.OnPaint();
Paint.Pen = Theme.ControlText.WithAlpha( Paint.HasMouseOver ? 1.0f : 0.6f );
Paint.DrawText( LocalRect.Shrink( 8, 4 ), Property.DisplayName, TextFlag.LeftTop );
if ( !IsDraggable ) return;
var isDragging = _drag.IsValid();
if ( isDragging )
{
Paint.ClearPen();
Paint.SetBrush( Theme.Pink.WithAlpha( 0.3f ) );
Paint.DrawRect( ContentRect, 3f );
}
}
}