1: /// <summary>
2: /// Does fluvial erosion on black and white images
3: /// </summary>
4: /// <param name="Image">Image to manipulate</param>
5: /// <param name="X">X position to drop water</param>
6: /// <param name="Y">Y position to drop water</param>
7: /// <param name="WaterPercent">Percent that the space is filled with water (1.0=100%, 0.0=0%)</param>
8: /// <param name="DepositRate">When the water moves, this is the amount that is deposited in the new spot (1.0=all, 0.0=none)</param>
9: /// <param name="PickupRate">The amount of "land" that the water picks up when it gets in a new spot</param>
10: /// <param name="Iterations">Number of steps that the water should take before stopping</param>
11: /// <returns>Returns the resulting Bitmap</returns>
12: public static Bitmap FluvialErosion(Bitmap Image, int X, int Y,
13: float WaterPercent, float DepositRate, float PickupRate, int Iterations)
14: {
15: Bitmap ReturnValue=new Bitmap(Image.Width,Image.Height);
16: BitmapData ImageData = LockImage(Image);
17: int ImagePixelSize = GetPixelSize(ImageData);
18: BitmapData ReturnData = LockImage(ReturnValue);
19: int ReturnPixelSize = GetPixelSize(ReturnData);
20: float[,] SedimentValues = new float[Image.Width, Image.Height];
21: float[,] WaterValues = new float[Image.Width, Image.Height];
22: float[,] Data = new float[Image.Width, Image.Height];
23: for (int x = 0; x < Image.Width; ++x)
24: {
25: for (int y = 0; y < Image.Height; ++y)
26: {
27: Data[x, y] = GetHeight(GetPixel(ImageData, x, y, ImagePixelSize));
28: }
29: }
30: WaterValues[X, Y] = WaterPercent;
31: if (Data[X, Y] > PickupRate)
32: {
33: SedimentValues[X, Y] = PickupRate;
34: Data[X, Y] -= PickupRate;
35: Data[X, Y] = Math.MathHelper.Clamp(Data[X, Y], 1.0f, 0.0f);
36: }
37: else
38: {
39: SedimentValues[X, Y] = Data[X, Y];
40: Data[X, Y] -= PickupRate;
41: Data[X, Y] = Math.MathHelper.Clamp(Data[X, Y], 1.0f, 0.0f);
42: }
43: for (int i = 0; i < Iterations; ++i)
44: {
45: for (int x = 0; x < Image.Width; ++x)
46: {
47: for (int y = 0; y < Image.Height; ++y)
48: {
49: if (WaterValues[x, y] > 0.0f)
50: {
51: List<int> MinX = new List<int>();
52: List<int> MinY = new List<int>();
53: float MinValue = Data[x, y];
54: for (int BoxX = Math.MathHelper.Clamp(x - 1, Image.Width - 1, 0); BoxX <= Math.MathHelper.Clamp(x + 1, Image.Width - 1, 0); ++BoxX)
55: {
56: for (int BoxY = Math.MathHelper.Clamp(y - 1, Image.Height - 1, 0); BoxY <= Math.MathHelper.Clamp(y + 1, Image.Height - 1, 0); ++BoxY)
57: {
58: if (BoxX != x || BoxY != y)
59: {
60: if (Data[BoxX, BoxY] < MinValue)
61: {
62: MinValue = Data[BoxX, BoxY];
63: MinX.Clear();
64: MinX.Add(BoxX);
65: MinY.Clear();
66: MinY.Add(BoxY);
67: }
68: else if (Data[BoxX, BoxY] == MinValue)
69: {
70: MinX.Add(BoxX);
71: MinY.Add(BoxY);
72: }
73: }
74: }
75: }
76: float WaterChange = WaterValues[x, y];
77: for (int z = 0; z < MinX.Count; ++z)
78: {
79: if (WaterChange > ((WaterValues[x, y] + Data[x, y]) - (WaterValues[MinX[z], MinY[z]] + Data[MinX[z], MinY[z]])))
80: {
81: WaterChange = ((WaterValues[x, y] + Data[x, y]) - (WaterValues[MinX[z], MinY[z]] + Data[MinX[z], MinY[z]]));
82: }
83: }
84: if (WaterChange <= 0.0f)
85: {
86: Data[x, y] += (DepositRate * SedimentValues[x, y]);
87: SedimentValues[x, y] *= (1.0f - DepositRate);
88: }
89: else
90: {
91: float SedimentCapacity = WaterValues[x, y] * PickupRate;
92: for (int z = 0; z < MinX.Count; ++z)
93: {
94: WaterValues[MinX[z], MinY[z]] += (WaterChange / (float)MinX.Count);
95: if (SedimentValues[x, y] >= SedimentCapacity)
96: {
97: SedimentValues[MinX[z], MinY[z]] += (SedimentCapacity / (float)MinX.Count);
98: }
99: else
100: {
101: SedimentValues[MinX[z], MinY[z]] += SedimentValues[x, y] + (PickupRate * ((SedimentCapacity - SedimentValues[x, y]) / (float)MinX.Count));
102: }
103: }
104: if (SedimentValues[x, y] >= SedimentCapacity)
105: {
106: Data[x, y] += DepositRate * (SedimentValues[x, y] - SedimentCapacity);
107: SedimentValues[x, y] = (1.0f - DepositRate) * (SedimentValues[x, y] - SedimentCapacity);
108: }
109: else
110: {
111: Data[x, y] += DepositRate * (SedimentCapacity - SedimentValues[x, y]);
112: SedimentValues[x, y] = 0.0f;
113: }
114: WaterValues[x, y] -= WaterChange;
115: }
116: }
117: }
118: }
119: }
120: for (int x = 0; x < Image.Width; ++x)
121: {
122: for (int y = 0; y < Image.Height; ++y)
123: {
124: Data[x, y] += SedimentValues[x, y];
125: Data[x, y] = Math.MathHelper.Clamp(Data[x, y], 1.0f, 0.0f);
126: Data[x,y]*=255.0f;
127: SetPixel(ReturnData, x, y, Color.FromArgb((int)Data[x, y], (int)Data[x, y], (int)Data[x, y]), ReturnPixelSize);
128: }
129: }
130: UnlockImage(ReturnValue, ReturnData);
131: UnlockImage(Image, ImageData);
132: return ReturnValue;
133: }