private static void test01() //****************************************************************************80 // // Purpose: // // TEST01 tests the coordinate conversion routines. // // Licensing: // // This code is distributed under the GNU LGPL license. // // Modified: // // 15 December 2013 // // Author: // // John Burkardt // { int m; Console.WriteLine(""); Console.WriteLine("TEST01"); Console.WriteLine(" Test the coordinate conversion routines:"); Console.WriteLine(" CARTESIAN_TO_HYPERSPHERE: X -> R,Theta"); Console.WriteLine(" HYPERSPHERE_TO_CARTESIAN: R,Theta -> X."); Console.WriteLine(""); Console.WriteLine(" Pick a random X, and compute X2 by converting X"); Console.WriteLine(" to hypersphere and back. Consider norm of difference."); Console.WriteLine(""); Console.WriteLine(" M || X - X2 ||"); int seed = 123456789; const int n = 1; double[] r = new double[n]; for (m = 1; m <= 5; m++) { Console.WriteLine(""); double[] theta = new double[(m - 1) * n]; int test; for (test = 1; test <= 5; test++) { double[] x = UniformRNG.r8mat_uniform_01_new(m, n, ref seed); double[] c = UniformRNG.r8vec_uniform_01_new(m, ref seed); Hypersphere.cartesian_to_hypersphere(m, n, c, x, ref r, ref theta); double[] x2 = Hypersphere.hypersphere_to_cartesian(m, n, c, r, theta); double err = typeMethods.r8mat_norm_fro_affine(m, n, x, x2); Console.WriteLine(" " + m.ToString(CultureInfo.InvariantCulture).PadLeft(2) + " " + err.ToString(CultureInfo.InvariantCulture).PadLeft(14) + ""); } } }
public static double ellipsoid_volume(int m, double[] a, double[] v, double r) //****************************************************************************80 // // Purpose: // // ELLIPSOID_VOLUME returns the volume of an ellipsoid. // // Discussion: // // The points X in the ellipsoid are described by an M by M // positive definite symmetric matrix A, an M-dimensional point V, // and a "radius" R, such that // (X-V)' * A * (X-V) <= R * R // // Licensing: // // This code is distributed under the GNU LGPL license. // // Modified: // // 14 August 2014 // // Author: // // John Burkardt // // Parameters: // // Input, int M, the spatial dimension. // // Input, double A[M*M], the matrix that describes // the ellipsoid. A must be symmetric and positive definite. // // Input, double V[M], the "center" of the ellipse. // The value of V is not actually needed by this function. // // Input, double R, the "radius" of the ellipse. // // Output, double ELLIPSOID_VOLUME, the volume of the ellipsoid. // { int i; double[] u = typeMethods.r8po_fa(m, a); double sqrt_det = 1.0; for (i = 0; i < m; i++) { sqrt_det *= u[i + i * m]; } double volume = Math.Pow(r, m) * Hypersphere.hypersphere_unit_volume(m) / sqrt_det; return(volume); }
private static void test06() //****************************************************************************80 // // Purpose: // // TEST06 tests the stereographic mapping. // // Licensing: // // This code is distributed under the GNU LGPL license. // // Modified: // // 15 December 2013 // // Author: // // John Burkardt // { int m; Console.WriteLine(""); Console.WriteLine("TEST06"); Console.WriteLine(" Test the stereographic mapping:"); Console.WriteLine(" HYPERSPHERE_STEREOGRAPH maps hypersphere points to the plane."); Console.WriteLine(" HYPERSPHERE_STEREOGRAPH_INVERSE inverts the mapping."); Console.WriteLine(""); Console.WriteLine(" Pick a random X1 on the hypersphere."); Console.WriteLine(" Map it to a point X2 on the plane."); Console.WriteLine(" Map it back to a point X3 on the hypersphere."); Console.WriteLine(" Consider norm of difference."); Console.WriteLine(""); Console.WriteLine(" M || X1 - X3 ||"); int seed = 123456789; typeMethods.r8vecNormalData data = new(); const int n = 1; for (m = 2; m <= 5; m++) { Console.WriteLine(""); int test; for (test = 1; test <= 5; test++) { double[] x1 = Hypersphere.hypersphere_01_surface_uniform(m, n, ref data, ref seed); double[] x2 = Hypersphere.hypersphere_stereograph(m, n, x1); double[] x3 = Hypersphere.hypersphere_stereograph_inverse(m, n, x2); double err = typeMethods.r8mat_norm_fro_affine(m, n, x1, x3); Console.WriteLine(" " + m.ToString(CultureInfo.InvariantCulture).PadLeft(2) + " " + err.ToString(CultureInfo.InvariantCulture).PadLeft(14) + ""); } } }
private static void test04() //****************************************************************************80 // // Purpose: // // TEST04 tests HYPERSPHERE_01_VOLUME. // // Licensing: // // This code is distributed under the GNU LGPL license. // // Modified: // // 15 December 2013 // // Author: // // John Burkardt // { int m = 0; double volume = 0; Console.WriteLine(""); Console.WriteLine("TEST04:"); Console.WriteLine(" HYPERSPHERE_01_VOLUME evaluates the area of the unit"); Console.WriteLine(" hypersphere in M dimensions."); Console.WriteLine(" HYPERSPHERE_01_VOLUME_VALUES returns some test values."); Console.WriteLine(""); Console.WriteLine(" M Exact Computed"); Console.WriteLine(" Volume Volume"); Console.WriteLine(""); int n_data = 0; for (;;) { Hypersphere.hypersphere_01_volume_values(ref n_data, ref m, ref volume); if (n_data == 0) { break; } double volume2 = Hypersphere.hypersphere_01_volume(m); Console.WriteLine(" " + m.ToString(CultureInfo.InvariantCulture).PadLeft(6) + " " + volume.ToString(CultureInfo.InvariantCulture).PadLeft(10) + " " + volume2.ToString(CultureInfo.InvariantCulture).PadLeft(10) + ""); } }
private static void test03() //****************************************************************************80 // // Purpose: // // TEST03 tests HYPERSPHERE_01_AREA. // // Licensing: // // This code is distributed under the GNU LGPL license. // // Modified: // // 15 December 2013 // // Author: // // John Burkardt // { double area = 0; int m = 0; Console.WriteLine(""); Console.WriteLine("TEST03:"); Console.WriteLine(" HYPERSPHERE_01_AREA evaluates the area of the unit"); Console.WriteLine(" hypersphere in M dimensions."); Console.WriteLine(""); Console.WriteLine(" M Exact Computed"); Console.WriteLine(" Area Area"); Console.WriteLine(""); int n_data = 0; for (;;) { Hypersphere.hypersphere_01_area_values(ref n_data, ref m, ref area); if (n_data == 0) { break; } double area2 = Hypersphere.hypersphere_01_area(m); Console.WriteLine(" " + m.ToString(CultureInfo.InvariantCulture).PadLeft(6) + " " + area.ToString(CultureInfo.InvariantCulture).PadLeft(10) + " " + area2.ToString(CultureInfo.InvariantCulture).PadLeft(10) + ""); } }
private static void test05() //****************************************************************************80 // // Purpose: // // TEST05 tests HYPERSPHERE_AREA, HYPERSPHERE_VOLUME. // // Licensing: // // This code is distributed under the GNU LGPL license. // // Modified: // // 15 May 2013 // // Author: // // John Burkardt // { int m; const double r = 1.5; Console.WriteLine(""); Console.WriteLine("TEST05"); Console.WriteLine(" For a hypersphere in M dimensions:"); Console.WriteLine(" HYPERSPHERE_AREA computes the area"); Console.WriteLine(" HYPERSPHERE_VOLUME computes the volume."); Console.WriteLine(""); Console.WriteLine(" Notice that both quantities eventually decrease"); Console.WriteLine(""); Console.WriteLine(" We use a radius of R = " + r + ""); Console.WriteLine(""); Console.WriteLine(" M Area Volume Area / Volume "); Console.WriteLine(""); for (m = 1; m <= 20; m++) { double area = Hypersphere.hypersphere_area(m, r); double volume = Hypersphere.hypersphere_volume(m, r); Console.WriteLine(" " + m.ToString(CultureInfo.InvariantCulture).PadLeft(3) + " " + area.ToString(CultureInfo.InvariantCulture).PadLeft(14) + " " + volume.ToString(CultureInfo.InvariantCulture).PadLeft(14) + " " + (area / volume).ToString(CultureInfo.InvariantCulture).PadLeft(14) + ""); } }
private static void test02() //****************************************************************************80 // // Purpose: // // TEST02 tests HYPERSPHERE_01_SURFACE_UNIFORM. // // Licensing: // // This code is distributed under the GNU LGPL license. // // Modified: // // 15 December 2013 // // Author: // // John Burkardt // { int m; Console.WriteLine(""); Console.WriteLine("TEST02"); Console.WriteLine(" HYPERSPHERE_01_SURFACE_UNIFORM samples uniformly from the"); Console.WriteLine(" surface of the unit hypersphere"); int seed = 123456789; typeMethods.r8vecNormalData data = new(); const int n = 1; for (m = 1; m <= 5; m++) { int test; for (test = 1; test <= 3; test++) { double[] x = Hypersphere.hypersphere_01_surface_uniform(m, n, ref data, ref seed); typeMethods.r8vec_transpose_print(m, x, " Random hypersphere point:"); } } }
static async Task Main(string[] args) { var surfaceSize = new Size(640, 400); var pixelScaling = 3; var window = new SdlWindow(surfaceSize * pixelScaling); var controls = new Controls(window); var cameraPos = Vector4.Zero; var cameraRot = Matrix4.Identity; var focalLength = window.Height / 2.0f / pixelScaling; var circles = new Hypersphere[8]; var rnd = new Random(1); for (int i = 0; i < circles.Length; i++) { var w = rnd.Next(-5.0f, 5.0f); var x = rnd.Next(-12.0f, 12.0f); var y = rnd.Next(-12.0f, 12.0f); var z = rnd.Next(-12.0f, 12.0f); var position = Vector4.Forward * 16 + new Vector4(w, x, y, z); var radius = rnd.Next(4.0f, 6.0f); circles[i] = new Hypersphere(position, radius); } window.Update += (delta) => { var mouseSensitivity = 0.2f; var moveSpeed = 5.0f * (float)delta.TotalSeconds; if (controls.FourDimensionalMove) { var roll = Deg2Rad(controls.MouseMotion.X) * mouseSensitivity; var w = Deg2Rad(controls.MouseMotion.Y) * mouseSensitivity; var rollRot = new Matrix4( 1, 0, 0, 0, 0, Cos(roll), 0, -Sin(roll), 0, 0, 1, 0, 0, Sin(roll), 0, Cos(roll)); var wRot = new Matrix4( Cos(w), 0, -Sin(w), 0, 0, 1, 0, 0, Sin(w), 0, Cos(w), 0, 0, 0, 0, 1); cameraRot = cameraRot * rollRot * wRot; if (controls.Up) { cameraPos += cameraRot * Vector4.UnitW * moveSpeed; } if (controls.Down) { cameraPos += cameraRot * -Vector4.UnitW * moveSpeed; } } if (controls.TraditionalMove) { var yaw = Deg2Rad(controls.MouseMotion.X) * mouseSensitivity; var pitch = -Deg2Rad(controls.MouseMotion.Y) * mouseSensitivity; var yawRot = new Matrix4( 1, 0, 0, 0, 0, Cos(yaw), -Sin(yaw), 0, 0, Sin(yaw), Cos(yaw), 0, 0, 0, 0, 1); var pitchRot = new Matrix4( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, Cos(pitch), -Sin(pitch), 0, 0, Sin(pitch), Cos(pitch)); cameraRot = cameraRot * pitchRot * yawRot; if (controls.Forward) { cameraPos += cameraRot * Vector4.Forward * moveSpeed; } if (controls.Back) { cameraPos += cameraRot * Vector4.Back * moveSpeed; } if (controls.Right) { cameraPos += cameraRot * Vector4.Right * moveSpeed; } if (controls.Left) { cameraPos += cameraRot * Vector4.Left * moveSpeed; } if (controls.Up) { cameraPos += cameraRot * Vector4.Up * moveSpeed; } if (controls.Down) { cameraPos += cameraRot * Vector4.Down * moveSpeed; } } controls.ResetMouseMotion(); return(Task.CompletedTask); }; window.Render += async(delta) => { var forward = cameraRot * Vector4.Forward; var stepRight = cameraRot * Vector4.Right; var stepDown = cameraRot * Vector4.Down; var width = window.Width / pixelScaling; var height = window.Height / pixelScaling; IEnumerable <Rectangle> SplitScreen(int size) { for (var x = 0; x < width; x += size) { for (var y = 0; y < height; y += size) { yield return(new Rectangle(x, y, Math.Min(width, x + size), Math.Min(height, y + size))); } } } await Task.WhenAll(SplitScreen(32).Select(rect => Task.Run(() => { var rayTmp = default(Ray); var ray = new Ray(cameraPos, forward *focalLength - stepRight * (width / 2 - rect.Left) - stepDown * (height / 2 - rect.Top)); for (var x = rect.Left; x < rect.Right; x++) { Vector4.Add(ref ray.Direction, stepRight); rayTmp = ray; for (var y = rect.Top; y < rect.Bottom; y++) { Vector4.Add(ref ray.Direction, stepDown); float?tMin = null; int foundIndex = -1; for (var i = 0; i < circles.Length; i++) { if ((circles[i].Intersect(ray) is float tCur) && !(tCur > tMin)) { tMin = tCur; foundIndex = i; } } if (tMin is float t) { var hit = ray.Origin + ray.Direction *t; var normal = circles[foundIndex].CalculateNormal(hit); var color = new Color(Math.Abs(normal.X), Math.Abs(normal.Y), Math.Abs(normal.Z)); for (var xx = 0; xx < pixelScaling; xx++) { for (var yy = 0; yy < pixelScaling; yy++) { window.Surface[x *pixelScaling + xx, y *pixelScaling + yy] = color; } } } } ray = rayTmp; } }))); }; await window.Run(); }