Эффект воды - Visual Basic .NET
Формулировка задачи:
Решение задачи: «Эффект воды»
textual
Листинг программы
using System; using System.Collections; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; using System.Windows.Forms; namespace WaterEffectDemo { /// <summary> /// /// </summary> public class WaterEffectControl : System.Windows.Forms.Panel { private System.Windows.Forms.Timer effectTimer; private System.ComponentModel.IContainer components; private Bitmap _bmp; private short[,,] _waves; private int _waveWidth; private int _waveHeight; private int _activeBuffer = 0; private bool _weHaveWaves; private int _bmpHeight,_bmpWidth; private byte[] _bmpBytes; private BitmapData _bmpBitmapData; private int _scale; private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.effectTimer = new System.Windows.Forms.Timer(this.components); // // effectTimer // this.effectTimer.Tick += new System.EventHandler(this.effectTimer_Tick); // // WaterEffectControl // this.Paint += new System.Windows.Forms.PaintEventHandler(this.WaterEffectControl_Paint); this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.WaterEffectControl_MouseMove); } public WaterEffectControl() { InitializeComponent(); effectTimer.Enabled = true; effectTimer.Interval = 50; SetStyle(ControlStyles.UserPaint, true); SetStyle(ControlStyles.AllPaintingInWmPaint, true); SetStyle(ControlStyles.DoubleBuffer, true); this.BackColor = Color.White; _weHaveWaves = false; _scale = 1; } public WaterEffectControl(Bitmap bmp) : this() { this.ImageBitmap = bmp; } protected override void Dispose( bool disposing ) { _bmp.UnlockBits(_bmpBitmapData); if( disposing ) { if (components != null) { components.Dispose(); } } base.Dispose( disposing ); } /// <summary> /// Timer handler /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void effectTimer_Tick(object sender, System.EventArgs e) { if(_weHaveWaves) { Invalidate(); ProcessWaves(); } } /// <summary> /// Paint handler /// /// Calculates the final effect-image out of /// </summary> /// <param name="sender"></param> /// <param name="e"></param> public void WaterEffectControl_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { using(Bitmap tmp = (Bitmap)_bmp.Clone()) { int xOffset, yOffset; byte alpha; if(_weHaveWaves) { BitmapData tmpData = tmp.LockBits(new Rectangle(0,0,_bmpWidth,_bmpHeight), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); byte[] tmpBytes = new Byte[_bmpWidth*_bmpHeight*4]; Marshal.Copy(tmpData.Scan0,tmpBytes,0,_bmpWidth*_bmpHeight*4); for(int x=1; x<_bmpWidth -1; x++) { for(int y=1; y<_bmpHeight -1; y++) { int waveX = (int)x >> _scale; int waveY = (int)y >> _scale; //check bounds if(waveX <= 0) waveX = 1; if(waveY <= 0) waveY = 1; if(waveX >= _waveWidth-1) waveX = _waveWidth-2; if(waveY >= _waveHeight-1) waveY = _waveHeight-2; //this gives us the effect of water breaking the light xOffset = (_waves[waveX-1,waveY,_activeBuffer] -_waves[waveX+1,waveY,_activeBuffer]) >> 3; yOffset = (_waves[waveX,waveY-1,_activeBuffer] -_waves[waveX,waveY+1,_activeBuffer]) >> 3; if((xOffset != 0) || (yOffset != 0)) { //check bounds if(x+xOffset >= _bmpWidth-1) xOffset = _bmpWidth - x - 1; if(y+yOffset >= _bmpHeight-1) yOffset = _bmpHeight - y - 1; if(x+xOffset < 0) xOffset = -x; if(y+yOffset < 0) yOffset = -y; //generate alpha alpha = (byte)(200-xOffset); if(alpha < 0) alpha = 0; if(alpha > 255) alpha = 254; //set colors tmpBytes[4*(x + y*_bmpWidth)] = _bmpBytes[4*(x+xOffset + (y+yOffset)*_bmpWidth)]; tmpBytes[4*(x + y*_bmpWidth) + 1] = _bmpBytes[4*(x+xOffset + (y+yOffset)*_bmpWidth) + 1]; tmpBytes[4*(x + y*_bmpWidth) + 2] = _bmpBytes[4*(x+xOffset + (y+yOffset)*_bmpWidth) + 2]; tmpBytes[4*(x + y*_bmpWidth) + 3] = alpha; } } } //copy data back Marshal.Copy(tmpBytes, 0, tmpData.Scan0, _bmpWidth*_bmpHeight*4); tmp.UnlockBits(tmpData); } e.Graphics.DrawImage(tmp,0,0,this.ClientRectangle.Width, this.ClientRectangle.Height); } } /// <summary> /// This is the method that actually does move the waves around and simulates the /// behaviour of water. /// </summary> private void ProcessWaves() { int newBuffer = (_activeBuffer == 0) ? 1 : 0; bool wavesFound = false; for(int x=1; x<_waveWidth -1; x++) { for(int y=1; y<_waveHeight -1; y++) { _waves[x,y,newBuffer] = (short)( ((_waves[x-1,y-1,_activeBuffer] + _waves[x,y-1,_activeBuffer] + _waves[x+1,y-1,_activeBuffer] + _waves[x-1,y,_activeBuffer] + _waves[x+1,y,_activeBuffer] + _waves[x-1,y+1,_activeBuffer] + _waves[x,y+1,_activeBuffer] + _waves[x+1,y+1,_activeBuffer]) >> 2) - _waves[x,y,newBuffer]); //damping if(_waves[x,y,newBuffer] != 0) { _waves[x,y,newBuffer] -= (short)(_waves[x,y,newBuffer] >> 4); wavesFound = true; } } } _weHaveWaves = wavesFound; _activeBuffer = newBuffer; } /// <summary> /// This function is used to start a wave by simulating a round drop /// </summary> /// <param name="x">x position of the drop</param> /// <param name="y">y position of the drop</param> /// <param name="height">Height position of the drop</param> private void PutDrop(int x, int y, short height) { _weHaveWaves = true; int radius = 20; double dist; for(int i = -radius; i<=radius; i++) { for(int j = -radius; j<=radius; j++) { if(((x+i>=0) && (x+i<_waveWidth-1)) && ((y+j>=0) && (y+j<_waveHeight-1))) { dist = Math.Sqrt(i*i +j*j); if(dist<radius) _waves[x+i,y+j,_activeBuffer] = (short)(Math.Cos(dist*Math.PI / radius) * height); } } } } /// <summary> /// The MouseMove handler. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void WaterEffectControl_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) { if(e.Button == MouseButtons.Left) { int realX = (int)((e.X / (double)this.ClientRectangle.Width)*_waveWidth); int realY = (int)((e.Y / (double)this.ClientRectangle.Height)*_waveHeight); PutDrop(realX,realY,200); } } #region Properties /// <summary> /// Our background image /// </summary> public Bitmap ImageBitmap { get { return _bmp; } set { _bmp = value; _bmpHeight = _bmp.Height; _bmpWidth = _bmp.Width; _waveWidth = _bmpWidth >> _scale; _waveHeight = _bmpHeight >> _scale; _waves = new Int16[_waveWidth,_waveHeight, 2]; _bmpBytes = new Byte[_bmpWidth*_bmpHeight*4]; _bmpBitmapData = _bmp.LockBits(new Rectangle(0,0,_bmpWidth,_bmpHeight), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); Marshal.Copy(_bmpBitmapData.Scan0,_bmpBytes,0,_bmpWidth*_bmpHeight*4); } } /// <summary> /// The scale of the wave matrix compared to the size of the image. /// Use it for large images to reduce processor load. /// /// 0 : wave resolution is the same than image resolution /// 1 : wave resolution is half the image resolution /// ...and so on /// </summary> public int Scale { get { return _scale; } set { _scale = value; } } #endregion } }
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д