/// <summary> /// CDF-based sampling. /// </summary> /// <param name="random">[0,1] uniform random value.</param> /// <param name="rnd">Optional random generator instance. If provided, internal randomization is possible.</param> public virtual void GetSample(out double x, out double y, double random, RandomJames rnd = null) { if (cdf == null) { CollectCdf(); } // CDF-based importance sampling: int ia = 0; int ib = cdfRes - 1; double a = 0.0; double b = 1.0; do { int ip = (ia + ib) >> 1; double p = cdf[ip]; if (p < random) { ia = ip; a = p; } else { ib = ip; b = p; } }while (ia + 1 < ib); int ix = ia / resolution; int iy = ia % resolution; x = (ix + (rnd == null ? 0.5 : rnd.UniformNumber())) * pixel; y = (iy + (rnd == null ? 0.5 : rnd.UniformNumber())) * pixel; }
//--- Public methods --- public FluidSimulator(Progress prog, RandomJames _rnd = null) { // particles: particles = null; activeParticles = 0; TotalSpawned = 0L; // boundaries: walls = null; xMin = 0.0; xMax = 3.0; yMin = 0.0; yMax = 1.0; InitWalls(); progress = prog; // random generator: rnd = _rnd; if (rnd == null) { rnd = new RandomJames(); rnd.Randomize(); } }
/// <summary> /// Allocate large enough colormap.. /// </summary> public static void AssertColors(int num) { int i; if (debugColors == null || debugColors.Count < num) { debugColors = new List <Color>(num); RandomJames rnd = new RandomJames(); for (i = 0; i < num; i++) { int R = rnd.RandomInteger(0, 255); int G = rnd.RandomInteger(0, 255); int B = rnd.RandomInteger(0, 255); if (R >= 128 && G >= 128 && B >= 128) { if (rnd.UniformNumber() > 0.5) { R -= 128; } else { G -= 128; } } debugColors.Add(Color.FromArgb(R, G, B)); } } }
public SamplingState(RectangleLightSource src, RandomJames _rnd) { source = src; rnd = _rnd; permU = new RandomJames.Permutation(); permV = new RandomJames.Permutation(); rank = total = 0; }
// Put more TLS data here.. /// <summary> /// Cold init of the thread data. /// </summary> public static void InitThreadData() { if (rnd == null) { rnd = new RandomJames(System.Threading.Thread.CurrentThread.GetHashCode() ^ DateTime.Now.Ticks); } // Put TLS data init here.. }
public MarblesWorld(string param) { rnd = new RandomJames(144); MyMarbles = new List <SphereObject>(); Frames = 0; Time = 0.0; Running = false; }
static public void Generate() { wasGenerated = true; // !!!{{ TODO - generate and draw maze in SVG format string fileName = CmdOptions.options.outputFileName; if (string.IsNullOrEmpty(fileName)) { fileName = CmdOptions.options.html ? "out.html" : "out.svg"; } string outFn = Path.Combine(CmdOptions.options.outDir, fileName); // SVG output: using (StreamWriter wri = new StreamWriter(outFn)) { if (CmdOptions.options.html) { wri.WriteLine("<!DOCTYPE html>"); wri.WriteLine("<meta charset=\"utf-8\">"); wri.WriteLine($"<title>SVG test ({CmdOptions.options.name})</title>"); wri.WriteLine(string.Format(CultureInfo.InvariantCulture, "<svg width=\"{0:f0}\" height=\"{1:f0}\">", CmdOptions.options.width, CmdOptions.options.height)); } else { wri.WriteLine(string.Format(CultureInfo.InvariantCulture, "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{0:f0}\" height=\"{1:f0}\">", CmdOptions.options.width, CmdOptions.options.height)); } List <Vector2> workList = new List <Vector2>(); RandomJames rnd = new RandomJames(); if (CmdOptions.options.seed > 0L) { rnd.Reset(CmdOptions.options.seed); } else { rnd.Randomize(); } for (int i = 0; i < CmdOptions.options.columns; i++) { workList.Add(new Vector2(rnd.RandomFloat(0.0f, (float)CmdOptions.options.width), rnd.RandomFloat(0.0f, (float)CmdOptions.options.height))); } drawCurve(wri, workList, 0, 0, string.Format("#{0:X2}{0:X2}{0:X2}", 0)); wri.WriteLine("</svg>"); // !!!}} } }
/// <summary> /// Renders the single pixel of an image. /// </summary> /// <param name="x">Horizontal coordinate.</param> /// <param name="y">Vertical coordinate.</param> /// <param name="color">Computed pixel color.</param> /// <param name="rnd">Shared random generator.</param> public override void RenderPixel(int x, int y, double[] color, RandomJames rnd) { Debug.Assert(color != null); Debug.Assert(rnd != null); int bands = color.Length; int b; Array.Clear(color, 0, bands); double[] tmp = new double[bands]; int i, j, ord; double step = 1.0 / superXY; double amplitude = Jittering * step; double origin = 0.5 * (step - amplitude); double x0, y0; for (j = ord = 0, y0 = y + origin; j++ < superXY; y0 += step) { for (i = 0, x0 = x + origin; i++ < superXY; x0 += step) { ImageFunction.GetSample(x0 + amplitude * rnd.UniformNumber(), y0 + amplitude * rnd.UniformNumber(), ord++, Supersampling, rnd, tmp); for (b = 0; b < bands; b++) { color[b] += tmp[b]; } } } double mul = step / superXY; if (Gamma > 0.001) { // gamma-encoding and clamping double g = 1.0 / Gamma; for (b = 0; b < bands; b++) { color[b] = Arith.Clamp(Math.Pow(color[b] * mul, g), 0.0, 1.0); } } else // no gamma, no clamping (for HDRI) { for (b = 0; b < bands; b++) { color[b] *= mul; } } }
/// <summary> /// Renders the single pixel of an image. /// </summary> /// <param name="x">Horizontal coordinate.</param> /// <param name="y">Vertical coordinate.</param> /// <param name="color">Computed pixel color.</param> /// <param name="rnd">Shared random generator.</param> public virtual void RenderPixel(int x, int y, double[] color, RandomJames rnd) { ImageFunction.GetSample(x + 0.5, y + 0.5, color); // gamma-encoding: if (Gamma > 0.001) { // gamma-encoding and clamping double g = 1.0 / Gamma; for (int b = 0; b < color.Length; b++) { color[b] = Arith.Clamp(Math.Pow(color[b], g), 0.0, 1.0); } } // else: no gamma, no clamping (for HDRI) }
/// <summary> /// Computes one image sample. Internal integration support. /// </summary> /// <param name="x">Horizontal coordinate.</param> /// <param name="y">Vertical coordinate.</param> /// <param name="rank">Rank of this sample, 0 <= rank < total (for integration).</param> /// <param name="total">Total number of samples (for integration).</param> /// <param name="rnd">Global (per-thread) instance of the random generator.</param> /// <param name="color">Computed sample color.</param> /// <returns>Hash-value used for adaptive subsampling.</returns> public override long GetSample(double x, double y, int rank, int total, RandomJames rnd, double[] color) { // initial color = black Array.Clear(color, 0, color.Length); Vector3d p0, p1; if (!scene.Camera.GetRay(x, y, rank, total, rnd, out p0, out p1)) { return(11L); } long hash = shade(0, 1.0, ref p0, ref p1, rank, total, rnd, color); return(hash); }
public MarblesWorld(string param) { // {{ Parse 'param' if you need.. // }} rnd = new RandomJames(144); centers = new List <Vector3>(); radii = new List <float>(); velocities = new List <Vector3>(); colors = new List <Color>(); Frames = 0; Time = 0.0; Running = false; }
/// <summary> /// CDF-based sampling. /// </summary> /// <param name="x">Output horizontal coordinate from the [0.0,width) range.</param> /// <param name="y">Output vertical coordinate from the [0.0,height) range.</param> /// <param name="random">[0,1] uniform random value.</param> /// <param name="rnd">Optional random generator instance. If provided, internal randomization is possible.</param> public void GetSample(out double x, out double y, double random, RandomJames rnd = null) { if (cdf == null) { PrepareCdf(); } // CDF-based importance sampling: int ia = 0; int ib = width * height; double a = 0.0; double b = 1.0; do { int ip = (ia + ib) >> 1; double p = cdf[ip]; if (p < random) { ia = ip; a = p; } else { ib = ip; b = p; } }while (ia + 1 < ib); y = ia / width; x = ia % width; if (rnd != null) { x += rnd.UniformNumber(); y += rnd.UniformNumber(); } else { x += 0.5; y += 0.5; } }
/// <summary> /// Recursive shading function - computes color contribution of the given ray (shot from the /// origin 'p0' into direction vector 'p1''). Recursion is stopped /// by a hybrid method: 'importance' and 'level' are checked. /// Internal integration support. /// </summary> /// <param name="level">Current recursion depth.</param> /// <param name="importance">Importance of the current ray.</param> /// <param name="p0">Ray origin.</param> /// <param name="p1">Ray direction vector.</param> /// <param name="rank">Rank of this sample, 0 <= rank < total (for integration).</param> /// <param name="total">Total number of samples (for integration).</param> /// <param name="rnd">Global (per-thread) instance of the random generator.</param> /// <param name="color">Result color.</param> /// <returns>Hash-value (ray sub-signature) used for adaptive subsampling.</returns> protected virtual long shade(int level, double importance, ref Vector3d p0, ref Vector3d p1, int rank, int total, RandomJames rnd, double[] color) { int bands = color.Length; LinkedList <Intersection> intersections = scene.Intersectable.Intersect(p0, p1); Intersection.countRays++; Intersection i = Intersection.FirstIntersection(intersections, ref p1); int b; if (i == null) // no intersection -> background color { Array.Copy(scene.BackgroundColor, color, bands); return(1L); } // there was at least one intersection i.Complete(); // hash code for adaptive supersampling: long hash = i.Solid.GetHashCode(); // apply all the textures fist.. if (i.Textures != null) { foreach (ITexture tex in i.Textures) { hash = hash * HASH_TEXTURE + tex.Apply(i, rank, total, rnd); } } p1 = -p1; // viewing vector p1.Normalize(); if (scene.Sources == null || scene.Sources.Count < 1) { // no light sources at all Array.Copy(i.SurfaceColor, color, bands); } else { // apply the reflectance model for each source i.Material = (IMaterial)i.Material.Clone(); i.Material.Color = i.SurfaceColor; Array.Clear(color, 0, bands); foreach (ILightSource source in scene.Sources) { Vector3d dir; double[] intensity = source.GetIntensity(i, rank, total, rnd, out dir); if (intensity != null) { if (DoShadows && !dir.Equals(Vector3d.Zero)) { intersections = scene.Intersectable.Intersect(i.CoordWorld, dir); Intersection.countRays++; Intersection si = Intersection.FirstIntersection(intersections, ref dir); // Better shadow testing: intersection between 0.0 and 1.0 kills the lighting if (si != null && !si.Far(1.0, ref dir)) { continue; } } double[] reflection = i.ReflectanceModel.ColorReflection(i, dir, p1, ReflectionComponent.ALL); if (reflection != null) { for (b = 0; b < bands; b++) { color[b] += intensity[b] * reflection[b]; } hash = hash * HASH_LIGHT + source.GetHashCode(); } } } } // check the recursion depth: if (level++ >= MaxLevel || !DoReflections && !DoRefractions) { return(hash); // no further recursion } Vector3d r; double maxK; double[] comp = new double[bands]; double newImportance; if (DoReflections) // trying to shoot a reflected ray.. { Geometry.SpecularReflection(ref i.Normal, ref p1, out r); double[] ks = i.ReflectanceModel.ColorReflection(i, p1, r, ReflectionComponent.SPECULAR_REFLECTION); if (ks != null) { maxK = ks[0]; for (b = 1; b < bands; b++) { if (ks[b] > maxK) { maxK = ks[b]; } } newImportance = importance * maxK; if (newImportance >= MinImportance) // do compute the reflected ray { hash += HASH_REFLECT * shade(level, newImportance, ref i.CoordWorld, ref r, rank, total, rnd, comp); for (b = 0; b < bands; b++) { color[b] += ks[b] * comp[b]; } } } } if (DoRefractions) // trying to shoot a refracted ray.. { maxK = i.Material.Kt; // simple solution, no influence of reflectance model yet newImportance = importance * maxK; if (newImportance < MinImportance) { return(hash); } // refracted ray: if ((r = Geometry.SpecularRefraction(i.Normal, i.Material.n, p1)) == null) { return(hash); } hash += HASH_REFRACT * shade(level, newImportance, ref i.CoordWorld, ref r, rank, total, rnd, comp); for (b = 0; b < bands; b++) { color[b] += maxK * comp[b]; } } return(hash); }
/// <summary> /// Returns intensity (incl. color) of the source contribution to the given scene point. /// Internal integration support. /// </summary> /// <param name="intersection">Scene point (only world coordinates and normal vector are needed).</param> /// <param name="rank">Rank of this sample, 0 <= rank < total (for integration).</param> /// <param name="total">Total number of samples (for integration).</param> /// <param name="rnd">Global (per-thread) instance of the random generator.</param> /// <param name="dir">Direction to the source is set here (zero vector for omnidirectional source). Not normalized!</param> /// <returns>Intensity vector in current color space or null if the point is not lit.</returns> public override double[] GetIntensity(Intersection intersection, int rank, int total, RandomJames rnd, out Vector3d dir) { if (rnd == null) { return(GetIntensity(intersection, out dir)); } SamplingState ss; lock ( states ) { if (!states.TryGetValue(rnd.GetHashCode(), out ss)) { ss = new SamplingState(this, rnd); states.Add(rnd.GetHashCode(), ss); } } // generate a [new] sample: ss.generateSample(rank, total); dir = ss.sample - intersection.CoordWorld; if (Vector3d.Dot(dir, intersection.Normal) <= 0.0) { return(null); } if (Dim == null || Dim.Length < 3) { return(intensity); } double dist = dir.Length; double dimCoef = 1.0 / (Dim[0] + dist * (Dim[1] + dist * Dim[2])); int bands = intensity.Length; double[] result = new double[bands]; for (int i = 0; i < bands; i++) { result[i] = intensity[i] * dimCoef; } return(result); }
/// <summary> /// Converts the given image into B/W (1bpp) output suitable for high-resolution printer. /// </summary> /// <param name="input">Input image.</param> /// <param name="output">Output (1bpp) image.</param> /// <param name="oWidth">Default output image width in pixels.</param> /// <param name="oHeight">Default output image height in pixels.</param> /// <param name="param">Set of optional text parameters.</param> /// <returns>Number of dots printed.</returns> public static long TransformImage(Bitmap input, out Bitmap output, int oWidth, int oHeight, string param) { // !!!{{ TODO: write your own image dithering code here int iWidth = input.Width; int iHeight = input.Height; long dots = 0L; // custom parameters from the text-field: double randomness = 0.0; double dot = 0.0; double gamma = 0.0; bool sampling = false; Dictionary <string, string> p = Util.ParseKeyValueList(param); if (p.Count > 0) { double scale = 0.0; // scale=<float-number> if (Util.TryParse(p, "scale", ref scale) && scale > 0.01) { oWidth = (int)(iWidth * scale); oHeight = (int)(iHeight * scale); } // rnd=<float-number> if (Util.TryParse(p, "rnd", ref randomness)) { randomness = Arith.Clamp(randomness, 0.0, 1.0); } // dot=<float-number> if (Util.TryParse(p, "dot", ref dot)) { dot = Math.Max(dot, 0.0); } // gamma=<float-number> if (Util.TryParse(p, "gamma", ref gamma)) { gamma = Math.Max(gamma, 0.0); } // sampling=<bool> Util.TryParse(p, "sampling", ref sampling); } // create output 1bpp Bitmap output = new Bitmap(oWidth, oHeight, PixelFormat.Format1bppIndexed); float dx = (iWidth - 1.0f) / (oWidth - 1.0f); float dy = (iHeight - 1.0f) / (oHeight - 1.0f); // set the B/W palette (0 .. black, 1 .. white): ColorPalette pal = output.Palette; pal.Entries[0] = Color.Black; pal.Entries[1] = Color.White; output.Palette = pal; int x, y; float fx, fy; RandomJames rnd = new RandomJames(); // convert pixel data (fast memory-mapped code): PixelFormat iFormat = input.PixelFormat; if (!PixelFormat.Format24bppRgb.Equals(iFormat) && !PixelFormat.Format32bppArgb.Equals(iFormat) && !PixelFormat.Format32bppPArgb.Equals(iFormat) && !PixelFormat.Format32bppRgb.Equals(iFormat)) { iFormat = PixelFormat.Format24bppRgb; } BitmapData dataOut = output.LockBits(new Rectangle(0, 0, oWidth, oHeight), ImageLockMode.WriteOnly, output.PixelFormat); unsafe { byte *optr; // A. placing reasonable number of random dots on the paper if (sampling) { dot = Math.Max(dot, 1.0); // clear output image: optr = (byte *)dataOut.Scan0; for (x = 0; x++ < oHeight * dataOut.Stride;) { *optr++ = 255; } // create grayscale image able to sample points from itself: FloatImage fi = new FloatImage(input); fi = fi.GrayImage(true, gamma); fi.PrepareCdf(); // sample 'dots' random dots: dots = (long)(1.2 * oWidth * oHeight / (dot * dot)); double xx, yy; for (long i = 0; i++ < dots;) { fi.GetSample(out xx, out yy, rnd.UniformNumber(), rnd); xx = oWidth * (xx / iWidth); yy = oHeight * (yy / iHeight); Dot1bpp((int)xx, (int)yy, dot, dataOut); } } else { BitmapData dataIn = input.LockBits(new Rectangle(0, 0, iWidth, iHeight), ImageLockMode.ReadOnly, iFormat); // B. random screen using dots bigger than 1px if (dot > 0.0) { // clear output image: optr = (byte *)dataOut.Scan0; for (x = 0; x++ < oHeight * dataOut.Stride;) { *optr++ = 255; } int dI = Image.GetPixelFormatSize(iFormat) / 8; for (y = 0, fy = 0.0f; y < oHeight; y++, fy += dy) { if (!Form1.cont) { break; } for (x = 0, fx = 0.0f; x < oWidth; x++, fx += dx) { float gray = GetGray(fx, fy, dataIn, dI); if (gamma > 0.0) { gray = (float)Math.Pow(gray, gamma); } float threshold = (float)(0.5 - randomness * (rnd.UniformNumber() - 0.5)); if (gray < threshold) { dots++; Dot1bpp(x, y, dot, dataOut); } } } } else // C. random screen using individual pixels { int buffer; int dI = Image.GetPixelFormatSize(iFormat) / 8; for (y = 0, fy = 0.0f; y < oHeight; y++, fy += dy) { if (!Form1.cont) { break; } optr = (byte *)dataOut.Scan0 + y * dataOut.Stride; buffer = 0; for (x = 0, fx = 0.0f; x < oWidth; fx += dx) { float gray = GetGray(fx, fy, dataIn, dI); if (gamma > 0.0) { gray = (float)Math.Pow(gray, gamma); } float threshold = (float)(0.5 - randomness * (rnd.UniformNumber() - 0.5)); buffer += buffer; if (gray >= threshold) { buffer++; } else { dots++; } if ((++x & 7) == 0) { *optr++ = (byte)buffer; buffer = 0; } } // finish the last byte of the scanline: if ((x & 7) != 0) { while ((x++ & 7) != 0) { buffer += buffer; } *optr = (byte)buffer; } } } input.UnlockBits(dataIn); } output.UnlockBits(dataOut); } return(dots); // !!!}} }
/// <summary> /// Returns intensity (incl. color) of the source contribution to the given scene point. /// Internal integration support. /// </summary> /// <param name="intersection">Scene point (only world coordinates and normal vector are needed).</param> /// <param name="rank">Rank of this sample, 0 <= rank < total (for integration).</param> /// <param name="total">Total number of samples (for integration).</param> /// <param name="rnd">Global (per-thread) instance of the random generator.</param> /// <param name="dir">Direction to the source is set here (zero vector for omnidirectional source).</param> /// <returns>Intensity vector in current color space or null if the point is not lit.</returns> public double[] GetIntensity(Intersection intersection, int rank, int total, RandomJames rnd, out Vector3d dir) { return(GetIntensity(intersection, out dir)); }
/// <summary> /// Ray-generator. Internal integration support. /// </summary> /// <param name="x">Origin position within a viewport (horizontal coordinate).</param> /// <param name="y">Origin position within a viewport (vertical coordinate).</param> /// <param name="rank">Rank of this ray, 0 <= rank < total (for integration).</param> /// <param name="total">Total number of rays (for integration).</param> /// <param name="rnd">Global (per-thread) instance of the random generator.</param> /// <param name="p0">Ray origin.</param> /// <param name="p1">Ray direction vector.</param> /// <returns>True if the ray (viewport position) is valid.</returns> public bool GetRay(double x, double y, int rank, int total, RandomJames rnd, out Vector3d p0, out Vector3d p1) { return(GetRay(x, y, out p0, out p1)); }
public static void Draw(Canvas c, string param) { int depthOfRecursion = 6; int borderWidth = ((c.Height + c.Width) / (20 * depthOfRecursion) == 0) ? 1 : (c.Height + c.Width) / (20 * depthOfRecursion); int numOfCircles = 1000; RandomJames rnd = new RandomJames(); long seed; if (long.TryParse(param, NumberStyles.Number, CultureInfo.InvariantCulture, out seed)) { rnd.Reset(seed); } // naming parameters like RandomJames.Reset(long ijkl), i love it :D RecursiveMondrian(rnd, c, new Rect() { x0 = 0, x1 = c.Width, y0 = 0, y1 = c.Height }, borderWidth, 0, depthOfRecursion, numOfCircles); // {{ TODO: put your drawing code here // int wq = c.Width / 4; // int hq = c.Height / 4; // int minq = Math.Min(wq, hq); // double t; // int i, j; // double x, y, r; // RandomJames rnd = new RandomJames(); // c.Clear(Color.Black); // // Example of even simpler passing of a numeric value through string param. // long seed; // if (long.TryParse(param, NumberStyles.Number, CultureInfo.InvariantCulture, out seed)) // rnd.Reset(seed); // // 1st quadrant - anti-aliased disks in a spiral. // c.SetAntiAlias(true); // const int MAX_DISK = 30; // for (i = 0, t = 0.0; i < MAX_DISK; i++, t += 0.65) // { // r = 5.0 + i * (minq * 0.7 - 5.0) / MAX_DISK; // c.SetColor(Color.FromArgb(i * 255 / MAX_DISK, 255, 255 - i * 255 / MAX_DISK)); // c.FillDisc((float)(wq + r * Math.Sin(t)), (float)(hq + r * Math.Cos(t)), (float)(r * 0.3)); // } // // 2nd quadrant - anti-aliased random dots in a heart shape.. // const int MAX_RND_DOTS = 1000; // double xx, yy, tmp; // for (i = 0; i < MAX_RND_DOTS; i++) // { // // This is called "Rejection Sampling" // do // { // x = rnd.RandomDouble(-1.5, 1.5); // y = rnd.RandomDouble(-1.0, 1.5); // xx = x * x; // yy = y * y; // tmp = xx + yy - 1.0; // } while (tmp * tmp * tmp - xx * yy * y > 0.0); // c.SetColor(Color.FromArgb(rnd.RandomInteger(200, 255), // rnd.RandomInteger(120, 220), // rnd.RandomInteger(120, 220))); // c.FillDisc(3.1f * wq + 0.8f * minq * (float)x, // 1.2f * hq - 0.8f * minq * (float)y, // rnd.RandomFloat(1.0f, minq * 0.03f)); // } // // 4th quadrant - CGG logo. // c.SetColor(COLORS[0]); // for (i = 0; i < DISC_DATA.Length / 3; i++) // { // x = DISC_DATA[i, 0]; // y = DISC_DATA[i, 1]; // r = DISC_DATA[i, 2]; // if (i == FIRST_COLOR) // c.SetColor(COLORS[1]); // c.FillDisc(3.0f * wq + (float)((x - 85.0) * 0.018 * minq), // 3.0f * hq + (float)((y - 65.0) * 0.018 * minq), // (float)(r * 0.018 * minq)); // } // // 3rd quadrant - disk grid. // const int DISKS = 12; // for (j = 0; j < DISKS; j++) // for (i = 0; i < DISKS; i++) // { // c.SetColor(((i ^ j) & 1) == 0 ? Color.White : Color.Blue); // c.FillDisc(wq + (i - DISKS / 2) * (wq * 1.8f / DISKS), // 3 * hq + (j - DISKS / 2) * (hq * 1.7f / DISKS), // (((i ^ j) & 15) + 1.0f) / DISKS * minq * 0.08f); // } // // }} }
/// <summary> /// Renders the given rectangle into the given raster image. /// Has to be re-entrant since this code is started in multiple parallel threads. /// </summary> /// <param name="image">Pre-initialized raster image.</param> /// <param name="sel">Selector for this working thread.</param> /// <param name="rnd">Thread-specific random generator.</param> public virtual void RenderRectangle(Bitmap image, int x1, int y1, int x2, int y2, ThreadSelector sel, RandomJames rnd) { bool lead = sel(0L); if (lead && ProgressData != null) { lock ( ProgressData ) { ProgressData.Finished = 0.0f; ProgressData.Message = ""; } } double[] color = new double[3]; // pixel color // run several phases of image rendering: int cell = 32; // cell size while (cell > 1 && cell > Adaptive) { cell >>= 1; } int initCell = cell; int x, y; bool xParity, yParity; float total = (x2 - x1) * (y2 - y1); long counter = 0L; long units = 0; do // do one phase { for (y = y1, yParity = false; y < y2; // one image row y += cell, yParity = !yParity) { for (x = x1, xParity = false; x < x2; // one image cell x += cell, xParity = !xParity) { if (cell == initCell || xParity || yParity) // process the cell { if (!sel(counter++)) { continue; } // determine sample color .. RenderPixel(x, y, color, rnd); if (Gamma <= 0.001) { for (int b = 0; b < color.Length; b++) { color[b] = Arith.Clamp(color[b], 0.0, 1.0); } } // .. and render it: Color c = Color.FromArgb((int)(color[0] * 255.0), (int)(color[1] * 255.0), (int)(color[2] * 255.0)); lock ( image ) { if (cell == 1) { image.SetPixel(x, y, c); } else { int xMax = x + cell; if (xMax > x2) { xMax = x2; } int yMax = y + cell; if (yMax > y2) { yMax = y2; } for (int iy = y; iy < yMax; iy++) { for (int ix = x; ix < xMax; ix++) { image.SetPixel(ix, iy, c); } } } } if ((++units & 63L) == 0L && ProgressData != null) { lock ( ProgressData ) { if (!ProgressData.Continue) { return; } if (lead) { ProgressData.Finished = counter / total; ProgressData.Sync(image); } } } } } } }while ((cell >>= 1) > 0); // do one phase }
/// <summary> /// Renders the given rectangle into the given raster image. /// </summary> /// <param name="image">Pre-initialized raster image.</param> /// <param name="rnd">Shared random generator.</param> public virtual void RenderRectangle(Bitmap image, int x1, int y1, int x2, int y2, RandomJames rnd) { RenderRectangle(image, x1, y1, x2, y2, (n) => true, rnd); }
/// <summary> /// Apply the relevant value-modulation in the given Intersection instance. /// Internal integration support. /// </summary> /// <param name="inter">Data object to modify.</param> /// <param name="rank">Rank of this sample, 0 <= rank < total (for integration).</param> /// <param name="total">Total number of samples (for integration).</param> /// <param name="rnd">Global (per-thread) instance of the random generator.</param> /// <returns>Hash value (texture signature) for adaptive subsampling.</returns> public long Apply(Intersection inter, int rank, int total, RandomJames rnd) { return(Apply(inter)); }
static void RecursiveMondrian(RandomJames rnd, Canvas c, Rect rect, int borderWidth, int depthOfRecursion, int maxDepth, int maxDots) { if (depthOfRecursion >= maxDepth) { goto FILL; } int divisionLine; Rect DivisionLine; Rect Splitted1; Rect Splitted2; if (depthOfRecursion % 2 == 0) { // Vertical split if (rect.x1 - rect.x0 <= borderWidth * 4) { goto FILL; } divisionLine = rnd.RandomInteger(rect.x0 + borderWidth * 2, rect.x1 - borderWidth * 2); DivisionLine = new Rect() { x0 = divisionLine - (borderWidth / 2), x1 = divisionLine + (borderWidth / 2), y0 = rect.y0, y1 = rect.y1 }; Splitted1 = new Rect() { x0 = rect.x0, x1 = DivisionLine.x0 - 1, y0 = rect.y0, y1 = rect.y1 }; Splitted2 = new Rect() { x0 = DivisionLine.x1 + 1, x1 = rect.x1, y0 = rect.y0, y1 = rect.y1 }; } else { // Horizontal split if (rect.y1 - rect.y0 <= borderWidth * 4) { goto FILL; } divisionLine = rnd.RandomInteger(rect.y0 + borderWidth * 2, rect.y1 - borderWidth * 2); DivisionLine = new Rect() { x0 = rect.x0, x1 = rect.x1, y0 = divisionLine - (borderWidth / 2), y1 = divisionLine + (borderWidth / 2) }; Splitted1 = new Rect() { x0 = rect.x0, x1 = rect.x1, y0 = rect.y0, y1 = DivisionLine.y0 - 1 }; Splitted2 = new Rect() { x0 = rect.x0, x1 = rect.x1, y0 = DivisionLine.y1 + 1, y1 = rect.y1 }; } // Filling Division line for (int i = 0; i < maxDots; i++) { int x = rnd.RandomInteger(DivisionLine.x0, DivisionLine.x1); int y = rnd.RandomInteger(DivisionLine.y0, DivisionLine.y1); c.SetColor(Color.Black); c.FillDisc((float)x, (float)y, (float)rnd.RandomInteger(1, borderWidth / 4)); } RecursiveMondrian(rnd, c, Splitted1, borderWidth, depthOfRecursion + 1, maxDepth, maxDots); RecursiveMondrian(rnd, c, Splitted2, borderWidth, depthOfRecursion + 1, maxDepth, maxDots); return; FILL: int area = (rect.x1 - rect.x0) * (rect.y1 - rect.y0); var maxCircleRadius = Math.Sqrt((double)area / (Math.PI * Math.Log(area) * 8)); //fill that rectangle with random color List <Color> colors = new List <Color>() { Color.White, Color.White, Color.White, Color.Red, Color.Blue, Color.Yellow }; Color randomColor = colors[rnd.RandomInteger(0, colors.Count - 1)]; for (int i = 0; i < maxDots; i++) { int x = rnd.RandomInteger(rect.x0, rect.x1); int y = rnd.RandomInteger(rect.y0, rect.y1); c.SetColor(randomColor); c.FillDisc((float)x, (float)y, (float)rnd.RandomDouble((maxCircleRadius / 2) * 0.25, maxCircleRadius / 2)); } }
/// <summary> /// Draw the image into the initialized Canvas object. /// </summary> /// <param name="c">Canvas ready for your drawing.</param> /// <param name="param">Optional string parameter from the form.</param> public static void Draw(Canvas c, string param) { // {{ TODO: put your drawing code here int wq = c.Width / 4; int hq = c.Height / 4; int minq = Math.Min(wq, hq); double t; int i, j; double x, y, r; RandomJames rnd = new RandomJames(); c.Clear(Color.Black); // Example of even simpler passing of a numeric value through string param. long seed; if (long.TryParse(param, NumberStyles.Number, CultureInfo.InvariantCulture, out seed)) { rnd.Reset(seed); } // 1st quadrant - anti-aliased disks in a spiral. c.SetAntiAlias(true); const int MAX_DISK = 30; for (i = 0, t = 0.0; i < MAX_DISK; i++, t += 0.65) { r = 5.0 + i * (minq * 0.7 - 5.0) / MAX_DISK; c.SetColor(Color.FromArgb(i * 255 / MAX_DISK, 255, 255 - i * 255 / MAX_DISK)); c.FillDisc((float)(wq + r * Math.Sin(t)), (float)(hq + r * Math.Cos(t)), (float)(r * 0.3)); } // 2nd quadrant - anti-aliased random dots in a heart shape.. const int MAX_RND_DOTS = 1000; double xx, yy, tmp; for (i = 0; i < MAX_RND_DOTS; i++) { // This is called "Rejection Sampling" do { x = rnd.RandomDouble(-1.5, 1.5); y = rnd.RandomDouble(-1.0, 1.5); xx = x * x; yy = y * y; tmp = xx + yy - 1.0; } while (tmp * tmp * tmp - xx * yy * y > 0.0); c.SetColor(Color.FromArgb(rnd.RandomInteger(200, 255), rnd.RandomInteger(120, 220), rnd.RandomInteger(120, 220))); c.FillDisc(3.1f * wq + 0.8f * minq * (float)x, 1.2f * hq - 0.8f * minq * (float)y, rnd.RandomFloat(1.0f, minq * 0.03f)); } // 4th quadrant - CGG logo. c.SetColor(COLORS[0]); for (i = 0; i < DISC_DATA.Length / 3; i++) { x = DISC_DATA[i, 0]; y = DISC_DATA[i, 1]; r = DISC_DATA[i, 2]; if (i == FIRST_COLOR) { c.SetColor(COLORS[1]); } c.FillDisc(3.0f * wq + (float)((x - 85.0) * 0.018 * minq), 3.0f * hq + (float)((y - 65.0) * 0.018 * minq), (float)(r * 0.018 * minq)); } // 3rd quadrant - disk grid. const int DISKS = 12; for (j = 0; j < DISKS; j++) { for (i = 0; i < DISKS; i++) { c.SetColor(((i ^ j) & 1) == 0 ? Color.White : Color.Blue); c.FillDisc(wq + (i - DISKS / 2) * (wq * 1.8f / DISKS), 3 * hq + (j - DISKS / 2) * (hq * 1.7f / DISKS), (((i ^ j) & 15) + 1.0f) / DISKS * minq * 0.08f); } } // }} }
/// <summary> /// Computes one image sample. Internal integration support. /// </summary> /// <param name="x">Horizontal coordinate.</param> /// <param name="y">Vertical coordinate.</param> /// <param name="rank">Rank of this sample, 0 <= rank < total (for integration).</param> /// <param name="total">Total number of samples (for integration).</param> /// <param name="rnd">Global (per-thread) instance of the random generator.</param> /// <param name="color">Computed sample color.</param> /// <returns>Hash-value used for adaptive subsampling.</returns> public virtual long GetSample(double x, double y, int rank, int total, RandomJames rnd, double[] color) { Vector3d p0, p1; int bands = color.Length; if (!scene.Camera.GetRay(x, y, rank, total, rnd, out p0, out p1)) { Array.Clear(color, 0, bands); // invalid ray -> black color return(1L); } LinkedList <Intersection> intersections = scene.Intersectable.Intersect(p0, p1); Intersection.countRays++; Intersection i = Intersection.FirstIntersection(intersections, ref p1); if (i == null) // no intersection -> background color { Array.Copy(scene.BackgroundColor, color, bands); return(0L); } // there was at least one intersection i.Complete(); // hash code for adaptive supersampling: long hash = i.Solid.GetHashCode(); // apply all the textures fist.. if (i.Textures != null) { foreach (ITexture tex in i.Textures) { hash = hash * HASH_TEXTURE + tex.Apply(i, rank, total, rnd); } } // terminate if light sources are missing if (scene.Sources == null || scene.Sources.Count < 1) { Array.Copy(i.SurfaceColor, color, bands); return(hash); } // .. else apply the reflectance model for each source p1 = -p1; p1.Normalize(); i.Material = (IMaterial)i.Material.Clone(); i.Material.Color = i.SurfaceColor; Array.Clear(color, 0, bands); foreach (ILightSource source in scene.Sources) { Vector3d dir; double[] intensity = source.GetIntensity(i, rank, total, rnd, out dir); if (intensity != null) { double[] reflection = i.ReflectanceModel.ColorReflection(i, dir, p1, ReflectionComponent.ALL); if (reflection != null) { for (int b = 0; b < bands; b++) { color[b] += intensity[b] * reflection[b]; } hash = hash * HASH_LIGHT + source.GetHashCode(); } } } return(hash); }
/// <summary> /// Draw single animation frame. /// </summary> /// <param name="c">Canvas to draw to.</param> /// <param name="time">Current time in seconds.</param> /// <param name="start">Start time (t0)</param> /// <param name="end">End time (for animation length normalization).</param> /// <param name="param">Optional string parameter from the form.</param> public static void DrawFrame(Canvas c, double time, double start, double end, string param) { // {{ TODO: put your drawing code here int objects = 1200; long seed = 144; double speed = 1.0; // Parse parameters. Dictionary <string, string> p = Util.ParseKeyValueList(param); if (p.Count > 0) { // objects=<int> if (Util.TryParse(p, "objects", ref objects)) { if (objects < 10) { objects = 10; } } // seed=<long> Util.TryParse(p, "seed", ref seed); // speed=<double> Util.TryParse(p, "speed", ref speed); } int wq = c.Width / 4; int hq = c.Height / 4; int minq = Math.Min(wq, hq); double t; int i, j; double x, y, r; c.Clear(Color.Black); c.SetAntiAlias(true); // 1st quadrant - anti-aliased disks in a spiral. const int MAX_DISK = 30; for (i = 0, t = 0.0; i < MAX_DISK; i++, t += 0.65) { r = 5.0 + i * (minq * 0.7 - 5.0) / MAX_DISK; c.SetColor(Color.FromArgb((i * 255) / MAX_DISK, 255, 255 - (i * 255) / MAX_DISK)); c.FillDisc((float)(wq + r * Math.Sin(t)), (float)(hq + r * Math.Cos(t)), (float)(r * 0.3)); } // 2nd quadrant - anti-aliased random dots in a heart shape.. RandomJames rnd = new RandomJames(seed + (long)((time - start) * 5)); double xx, yy, tmp; for (i = 0; i < objects; i++) { // This is called "Rejection Sampling" do { x = rnd.RandomDouble(-1.5, 1.5); y = rnd.RandomDouble(-1.0, 1.5); xx = x * x; yy = y * y; tmp = xx + yy - 1.0; } while (tmp * tmp * tmp - xx * yy * y > 0.0); c.SetColor(Color.FromArgb(rnd.RandomInteger(200, 255), rnd.RandomInteger(120, 220), rnd.RandomInteger(120, 220))); c.FillDisc(3.1f * wq + 0.8f * minq * (float)x, 1.2f * hq - 0.8f * minq * (float)y, rnd.RandomFloat(1.0f, minq * 0.03f)); } // 4th quadrant - CGG logo. c.SetColor(COLORS[0]); for (i = 0; i < DISC_DATA.Length / 3; i++) { x = DISC_DATA[i, 0] - 65.0; y = DISC_DATA[i, 1] - 65.0; r = DISC_DATA[i, 2]; if (i == FIRST_COLOR) { c.SetColor(COLORS[1]); } t = 4.0 * speed * Math.PI * (time - start) / (end - start); double sina = Math.Sin(t); double cosa = Math.Cos(t); double nx = cosa * x + sina * y; double ny = -sina * x + cosa * y; c.FillDisc(3.0f * wq + (float)((nx - 20.0) * 0.018 * minq), 3.0f * hq + (float)(ny * 0.018 * minq), (float)(r * 0.018 * minq)); } // 3rd quadrant - jaggy disks. const int DISKS = 12; for (j = 0; j < DISKS; j++) { for (i = 0; i < DISKS; i++) { c.SetColor(((i ^ j) & 1) == 0 ? Color.White : Color.Blue); c.FillDisc(wq + (i - DISKS / 2) * (wq * 1.8f / DISKS), 3 * hq + (j - DISKS / 2) * (hq * 1.7f / DISKS), (((i ^ j) & 15) + 1.0f) / DISKS * minq * 0.08f); } } // }} }