private static int[] GetFieldSphere(Point3D point, double radius, FluidField3D field) { // Get the box that contains the return circle var center = GetFieldPoint_XYZ(point, field); var min = GetFieldPoint_XYZ(new Point3D(point.X - radius, point.Y - radius, point.Z - radius), field); var max = GetFieldPoint_XYZ(new Point3D(point.X + radius, point.Y + radius, point.Z + radius), field); // Get points that are inside the circle List <int> retVal = new List <int>(); double maxDistance = radius * field.Size; double maxDistanceSquared = maxDistance * maxDistance; for (int x = min.Item1; x <= max.Item1; x++) { for (int y = min.Item2; y <= max.Item2; y++) { for (int z = min.Item3; z <= max.Item3; z++) { double dx = x - center.Item1; double dy = y - center.Item2; double dz = z - center.Item3; double distanceSquared = (dx * dx) + (dy * dy) + (dz * dz); if (distanceSquared <= maxDistanceSquared) { retVal.Add(field.Get1DIndex(x, y, z)); } } } } // Exit Function return(retVal.ToArray()); }
private void Window_Loaded(object sender, RoutedEventArgs e) { try { // Field _field = new FluidField3D(40); _field.Diffusion = 0; _field.Damping = 0; // Sliders PropertyInfo[] propsOptions = typeof(FluidField3D).GetProperties(); _propLinks.Add(new SliderShowValues.PropSync(trkDiffusion, propsOptions.Where(o => o.Name == "Diffusion").First(), _field, 0, .03)); _propLinks.Add(new SliderShowValues.PropSync(trkDamping, propsOptions.Where(o => o.Name == "Damping").First(), _field, 0, .1)); _propLinks.Add(new SliderShowValues.PropSync(trkTimestep, propsOptions.Where(o => o.Name == "TimeStep").First(), _field, 0, 100)); _propLinks.Add(new SliderShowValues.PropSync(trkIterations, propsOptions.Where(o => o.Name == "Iterations").First(), _field, 0, 20)); trkBrushSize.Minimum = 0; trkBrushSize.Maximum = .5; trkBrushSize.Value = .15; trkVelocityMultiplier.Minimum = .1; trkVelocityMultiplier.Maximum = 20; trkVelocityMultiplier.Value = 3; trkBrushDepth.Minimum = 0; trkBrushDepth.Maximum = 1; trkBrushDepth.Value = .5; // Create a new image Image img = new Image(); RenderOptions.SetBitmapScalingMode(img, BitmapScalingMode.NearestNeighbor); RenderOptions.SetEdgeMode(img, EdgeMode.Aliased); // Add this image to the canvas grdFluid.Children.Add(img); // Create the bitmap, and set _bitmap = new WriteableBitmap(_field.Size, _field.Size, UtilityWPF.DPI, UtilityWPF.DPI, PixelFormats.Bgra32, null); img.Source = _bitmap; img.Stretch = Stretch.Fill; // Timer _timer = new DispatcherTimer(); _timer.Interval = TimeSpan.FromMilliseconds(1); _timer.Tick += Timer_Tick; _timer.IsEnabled = true; // Need to manually set the scrollviewer's height (a binding can't be conditional - see Window_SizeChanged) expanderScrollViewer.MaxHeight = expanderRow.ActualHeight; _isInitialized = true; } catch (Exception ex) { MessageBox.Show(ex.ToString(), this.Title, MessageBoxButton.OK, MessageBoxImage.Error); } }
private static Tuple <int, int, int> GetFieldPoint_XYZ(Point3D point, FluidField3D field) { int x = Convert.ToInt32(Math.Round(point.X * field.Size)); if (x < 0) { x = 0; } else if (x >= field.Size) { x = field.Size - 1; } int y = Convert.ToInt32(Math.Round(point.Y * field.Size)); if (y < 0) { y = 0; } else if (y >= field.Size) { y = field.Size - 1; } int z = Convert.ToInt32(Math.Round(point.Z * field.Size)); if (z < 0) { z = 0; } else if (z >= field.Size) { z = field.Size - 1; } return(Tuple.Create(x, y, z)); }
private static void DrawFieldSprtDoIt(byte[] pixels, double[] ink, bool[] blocked, int size, byte[] colorZFront, byte[] colorZBack, AxisFor pixelX, AxisFor pixelY, AxisFor pixelZ) { List <Mapping_2D_1D> flattened = new List <Mapping_2D_1D>(); // Setup the pixel array //for (int y2D = pixelY.Start; pixelY.IsPos ? y2D <= pixelY.Stop : y2D >= pixelY.Stop; y2D += pixelY.Increment) foreach (int y2D in pixelY.Iterate()) { int offsetY = pixelY.GetValueForOffset(y2D) * size; //for (int x2D = pixelX.Start; pixelX.IsPos ? x2D <= pixelX.Stop : x2D >= pixelX.Stop; x2D += pixelX.Increment) foreach (int x2D in pixelX.Iterate()) { int offset = offsetY + pixelX.GetValueForOffset(x2D); flattened.Add(new Mapping_2D_1D(x2D, y2D, offset)); } } // Each pixel of the output bitmap can be added up independently, so employ some threading flattened.AsParallel().ForAll(o => { //TODO: Color is 6.5 times slower than byte array List <byte[]> colorColumn = new List <byte[]>(); for (int z2D = pixelZ.Start; pixelZ.IsPos?z2D <= pixelZ.Stop : z2D >= pixelZ.Stop; z2D += pixelZ.Increment) { int x = -1, y = -1, z = -1; pixelX.Set3DIndex(ref x, ref y, ref z, o.X); pixelY.Set3DIndex(ref x, ref y, ref z, o.Y); pixelZ.Set3DIndex(ref x, ref y, ref z, z2D); int index = FluidField3D.Get1DIndex(x, y, z, size); if (blocked[index]) { // Blocked cells are all white, so save the overlay method a bit of work, and throw out everything behind this colorColumn.Clear(); colorColumn.Add(new byte[] { 255, 255, 255, 255 }); continue; } double inkCell = ink[index]; if (Math1D.IsNearZero(inkCell)) { continue; } byte[] depthColor = UtilityWPF.AlphaBlend(colorZBack, colorZFront, UtilityCore.GetScaledValue_Capped(0, 1, 0, size - 1, z)); int alpha = Convert.ToInt32(Math.Round(inkCell * 255)); if (alpha < 0) { alpha = 0; } else if (alpha > 255) { alpha = 255; } colorColumn.Add(new byte[] { Convert.ToByte(alpha), depthColor[1], depthColor[2], depthColor[3] }); } byte[] color = colorColumn.Count > 0 ? UtilityWPF.OverlayColors(colorColumn) : new byte[] { 0, 0, 0, 0 }; pixels[o.Offset1D * 4 + 0] = color[3]; // Blue pixels[o.Offset1D * 4 + 1] = color[2]; // Green pixels[o.Offset1D * 4 + 2] = color[1]; // Red pixels[o.Offset1D * 4 + 3] = color[0]; // Alpha }); }
private static void DrawField(WriteableBitmap bitmap, FluidField3D field, ViewDirection viewDirection, byte[] colorZFront, byte[] colorZBack) { Int32Rect rect = new Int32Rect(0, 0, field.Size, field.Size); int size = rect.Width * rect.Height * 4; byte[] pixels = new byte[size]; double[] ink = field.Ink; bool[] blocked = field.Blocked; switch (viewDirection) { case ViewDirection.Front: #region Front DrawFieldSprtDoIt(pixels, ink, blocked, field.Size, colorZFront, colorZBack, new AxisFor(Axis.X, 0, field.Size - 1), new AxisFor(Axis.Y, 0, field.Size - 1), new AxisFor(Axis.Z, field.Size - 1, 0)); // pixel z needs to start at the back, because the colors are overlaid #endregion break; case ViewDirection.Right: #region Right DrawFieldSprtDoIt(pixels, ink, blocked, field.Size, colorZFront, colorZBack, new AxisFor(Axis.Z, 0, field.Size - 1), new AxisFor(Axis.Y, 0, field.Size - 1), new AxisFor(Axis.X, 0, field.Size - 1)); #endregion break; case ViewDirection.Left: #region Left DrawFieldSprtDoIt(pixels, ink, blocked, field.Size, colorZFront, colorZBack, new AxisFor(Axis.Z, field.Size - 1, 0), new AxisFor(Axis.Y, 0, field.Size - 1), new AxisFor(Axis.X, field.Size - 1, 0)); #endregion break; case ViewDirection.Top: #region Top DrawFieldSprtDoIt(pixels, ink, blocked, field.Size, colorZFront, colorZBack, new AxisFor(Axis.X, 0, field.Size - 1), new AxisFor(Axis.Z, field.Size - 1, 0), new AxisFor(Axis.Y, field.Size - 1, 0)); #endregion break; case ViewDirection.Bottom: #region Bottom DrawFieldSprtDoIt(pixels, ink, blocked, field.Size, colorZFront, colorZBack, new AxisFor(Axis.X, 0, field.Size - 1), new AxisFor(Axis.Z, 0, field.Size - 1), new AxisFor(Axis.Y, 0, field.Size - 1)); #endregion break; case ViewDirection.Back: #region Back //NOTE: This one is up for interpretation. I will rotate left to right, because I think that is more intuitive. If rotating //top to bottom, it would be upside down from the way I am presenting DrawFieldSprtDoIt(pixels, ink, blocked, field.Size, colorZFront, colorZBack, new AxisFor(Axis.X, field.Size - 1, 0), new AxisFor(Axis.Y, 0, field.Size - 1), new AxisFor(Axis.Z, 0, field.Size - 1)); // The alternate //DrawField_DoIt(pixels, ink, blocked, field.Size, colorZFront, colorZBack, // new AxisFor(Axis.X, 0, field.Size - 1), // new AxisFor(Axis.Y, field.Size - 1, 0), // new AxisFor(Axis.Z, 0, field.Size - 1)); #endregion break; default: throw new ApplicationException("Unknown ViewDirection: " + viewDirection.ToString()); } bitmap.WritePixels(rect, pixels, rect.Width * 4, 0); }