/// <summary> /// Shoot the ray through pixel (x1,y1). /// Returns color of the pixel. /// </summary> /// <param name="x1"></param> /// <param name="y1"></param> /// <returns></returns> public unsafe Color Calculate(double x1, double y1) { Color?pixel = null; Color hitPixel; double htry = 0.5, escal = 1e11, hdid = 0.0, hnext = 0.0; double range = 0.0025 * this.equation.Rdisk / (sizex - 1); double yaw = this.cameraYaw * sizex; double *y = stackalloc double[this.equation.N]; double *dydx = stackalloc double[this.equation.N]; double *yscal = stackalloc double[this.equation.N]; double *ylaststep = stackalloc double[this.equation.N]; int side; int i; double tiltSin = Math.Sin((cameraTilt / 180) * Math.PI); double tiltCos = Math.Cos((cameraTilt / 180) * Math.PI); double xRot = x1 - (sizex + 1) / 2 - yaw; double yRot = y1 - (sizey + 1) / 2; this.equation.SetInitialConditions(y, dydx, (int)(xRot * tiltCos - yRot * tiltSin) * range, (int)(yRot * tiltCos + xRot * tiltSin) * range ); // if tracing on, store the initial point if (this.trace) { this.RayPoints.Clear(); this.RayPoints.Add(new Tuple <double, double, double>(y[0], y[1], y[2])); } int rCount = 0; while (true) { MemHelper.memcpy((IntPtr)ylaststep, (IntPtr)y, this.equation.N * sizeof(double)); this.equation.Function(y, dydx); for (i = 0; i < this.equation.N; i++) { yscal[i] = Math.Abs(y[i]) + Math.Abs(dydx[i] * htry) + 1.0e-3; } if (y[1] > Math.PI / 2) { side = 1; } else if (y[1] < Math.PI / 2) { side = -1; } else { side = 0; } hnext = RungeKuttaEngine.RKIntegrate(this.equation, y, dydx, htry, escal, yscal, out hdid); if ((y[1] - Math.PI / 2) * side < 0) { MemHelper.memcpy((IntPtr)ylaststep, (IntPtr)y, this.equation.N * sizeof(double)); this.IntersectionSearch(y, dydx, hdid); // Ray hits accretion disc? if ((y[0] <= this.equation.Rdisk) && (y[0] >= this.equation.Rmstable)) { // y[0] - radial position // y[2] - phi (horizontal) angular position int r = (int)(450 * (y[0] - this.equation.Rmstable) / (this.equation.Rdisk - this.equation.Rmstable)); int xPos, yPos; // do mapping of texture image this.discMap.Map(y[0], y[1], y[2], out xPos, out yPos); lock (discTexture) { if (pixel != null) { pixel = ColorHelper.AddColor(discTexture.GetPixel(xPos, yPos), pixel.Value); } else { pixel = discTexture.GetPixel(xPos, yPos); } // don't return yet, just remember the color to 'tint' the texture later } } } // Ray hits the event horizon? if ((y[0] < this.equation.Rhor)) { hitPixel = Color.FromArgb(0, 0, 0); // tint the color if (pixel != null) { return(ColorHelper.AddColor(hitPixel, pixel.Value)); } else { return(hitPixel); } } // Ray escaped to infinity? if (y[0] > this.equation.R0) { int xPos, yPos; this.backgroundMap.Map(y[0], y[1], y[2], out xPos, out yPos); lock (this.backgroundTexture) { hitPixel = this.backgroundTexture.GetPixel(xPos, yPos); // tint the color if (pixel != null) { return(ColorHelper.AddColor(hitPixel, pixel.Value)); } else { return(hitPixel); } } } // if tracing on, store the calculated point if (this.trace) { this.RayPoints.Add(new Tuple <double, double, double>(y[0], y[1], y[2])); } htry = hnext; if (rCount++ > 1000000) // failsafe... { Console.WriteLine("Error - solution not converging!"); return(Color.Fuchsia); } } }