using System; using System.Drawing; using System.Drawing.Imaging; using System.Windows.Forms; using System.Threading; namespace Fractals { public class FractalSet : IDisposable { protected int _MaxIter = 512; protected int _MaxColors; protected int _MaxMagSquared = 4; //protected int[] _Color = {Color.Black.ToArgb(),Color.Chocolate.ToArgb(),Color.Beige.ToArgb(),Color.Cyan.ToArgb(),Color.DarkCyan.ToArgb(),Color.Aqua.ToArgb(),Color.Blue.ToArgb(),Color.DarkBlue.ToArgb(),Color.Red.ToArgb(),Color.DarkRed.ToArgb(),Color.Green.ToArgb(),Color.DarkGreen.ToArgb(),Color.Violet.ToArgb(),Color.DarkViolet.ToArgb(),Color.Yellow.ToArgb(),Color.White.ToArgb()}; Rectangle _ZoomRect; Point _LastPoint; bool _MouseDown; public delegate void UpdateReadoutsDelegate( double realCenter, double imgCenter, double scale ); public UpdateReadoutsDelegate ReadoutsDelegate; protected bool _WorkStopped = false; protected object _Lock = new object(); protected ManualResetEvent _ResetEvent = new ManualResetEvent(false); protected delegate void UpdateImageDelegate( Bitmap ColBmp, int Col ); protected Thread _Thread; protected Bitmap _bmp; protected PictureBox _PictureBox; protected int _width = 0; protected int _height = 0; protected double _realCenter = 0.0; protected double _imgCenter = 0.0; protected double _realMin = -2.0; protected double _imgMin = -2.0; protected double _deltaReal; protected double _deltaImg; public FractalSet( ) { _MaxColors = _Color.Length; } protected void Draw( ) { int band = 5; Bitmap bm = new Bitmap( band, _PictureBox.Height ); UpdateImageDelegate UpdateDelegate = new UpdateImageDelegate( UpdateImage ); int Col = 0; object[] args = new object[] {bm, Col}; for( int i = 0 ; i < _width && WorkStopped == false ; i+=band ) { for( int k = 0 ; k < band && WorkStopped == false ; k++ ) { double RealC = _realMin + _deltaReal * (i + k); for( int j = 0 ; j < _height && WorkStopped == false ; j++ ) { double ImaginaryC = _imgMin + _deltaImg * j; bm.SetPixel( k, j, GetColorVal( RealC, ImaginaryC ) ); } } //Copy a column to the large bitmap args[1] = i; IAsyncResult ares = _PictureBox.BeginInvoke( UpdateDelegate, args ); if( WorkStopped == false ) { WaitHandle[] arr = new WaitHandle[2]; arr[0] = _ResetEvent; arr[1] = ares.AsyncWaitHandle; WaitHandle.WaitAny( arr ); } } } protected virtual Color GetColorVal( double r, double i ) { return( Color.Black ); } public void Start( PictureBox pb ) { DisableMouseHandlers(); _PictureBox = pb; _PictureBox.MouseDown += new MouseEventHandler(_PictureBox_MouseDown); _PictureBox.MouseMove += new MouseEventHandler(_PictureBox_MouseMove); _PictureBox.MouseUp += new MouseEventHandler(_PictureBox_MouseUp); Start(); } public void DisableMouseHandlers( ) { if( _PictureBox != null ) { _PictureBox.MouseDown -= new MouseEventHandler(_PictureBox_MouseDown); _PictureBox.MouseMove -= new MouseEventHandler(_PictureBox_MouseMove); _PictureBox.MouseUp -= new MouseEventHandler(_PictureBox_MouseUp); } } void Start( ) { if( (_Thread != null) && (_Thread.IsAlive) ) { Kill(); } CreateGraphicsObjects(); ThreadStart ts = new ThreadStart( Draw ); _Thread = new Thread( ts ); //_Thread.Priority = ThreadPriority.BelowNormal; //Option _Thread.IsBackground = true; WorkStopped = false; _Thread.Start(); } void CreateGraphicsObjects( ) { if( _bmp == null ) { _bmp = new Bitmap( _PictureBox.Width, _PictureBox.Height ); _PictureBox.BackgroundImage = _bmp; } else { if( (_bmp.Width != _PictureBox.Width) || (_bmp.Height != _PictureBox.Height) ) { _bmp.Dispose(); _bmp = new Bitmap( _PictureBox.Width, _PictureBox.Height ); _PictureBox.BackgroundImage = _bmp; } } _width = _bmp.Width; _height = _bmp.Height; _realMin = _realCenter - _deltaReal * _width / 2; _imgMin = _imgCenter - _deltaImg * _height / 2; if( ReadoutsDelegate != null ) { ReadoutsDelegate( _realCenter, _imgCenter, _deltaReal ); } //Make a bitmap to draw user selection on Bitmap bmp = new Bitmap( _PictureBox.Width, _PictureBox.Height ); if( _PictureBox.Image != null ) { _PictureBox.Image.Dispose(); } _PictureBox.Image = bmp; } protected void UpdateImage( Bitmap ColBmp, int Col ) { Graphics g = null; try { g = Graphics.FromImage( _PictureBox.BackgroundImage ); g.DrawImage( ColBmp, Col, 0, ColBmp.Width, _height ); _PictureBox.Refresh(); Application.DoEvents(); } finally { if( g != null ) { g.Dispose(); } } } public void Kill( ) { if( _Thread == null ) { return; } if( _Thread.IsAlive == false ) { return; } WorkStopped = true; _ResetEvent.Set(); //Wait for thread to die _Thread.Join(); WorkStopped = false; _ResetEvent.Reset(); } protected bool WorkStopped { get { bool b; lock(_Lock) { b = _WorkStopped; } return b; } set { lock(_Lock) { _WorkStopped = value; } } } public void Dispose( ) { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose( bool disposing ) { if (disposing) { //Called by Dispose() if (_Thread != null) { Kill(); } } } void DrawSelection( Rectangle ZoomRect ) { if( _PictureBox.Image == null ) { return; } Graphics g = Graphics.FromImage( _PictureBox.Image ); ClearSelection( g ); Pen pen = new Pen( Color.LightBlue, 2 ); pen.DashPattern = new Single[] {1, 1, 2, 1}; g.DrawRectangle( pen, ZoomRect ); _ZoomRect = ZoomRect; _PictureBox.Refresh(); } void ClearSelection( Graphics g ) { g.Clear( Color.Transparent ); } void ClearSelection( ) { Graphics g = Graphics.FromImage( _PictureBox.Image ); g.Clear( Color.Transparent ); } void _PictureBox_MouseDown( object sender, MouseEventArgs e ) //Handles _PictureBox.MouseDown { _MouseDown = true; _LastPoint = new Point( e.X, e.Y ); } void _PictureBox_MouseMove( object sender, MouseEventArgs e ) { if( _MouseDown ) { int X1, Y1, X2, Y2; X1 = Math.Min( e.X, _LastPoint.X ); X2 = Math.Max( e.X, _LastPoint.X ) - X1; Y1 = Math.Min( e.Y, _LastPoint.Y ); Y2 = Math.Max( e.Y, _LastPoint.Y ) - Y1; Rectangle NewSelRect = new Rectangle( X1, Y1, X2, Y2 ); DrawSelection( NewSelRect ); } } void _PictureBox_MouseUp( object sender, MouseEventArgs e ) { _MouseDown = false; int X1, Y1, X2, Y2; if( (e.X == _LastPoint.X) && (e.Y == _LastPoint.Y) ) { double xCenter = _width / 2; double yCenter = _height / 2; double deltaReal = (e.X - xCenter) * _deltaReal; double deltaImg = (e.Y - yCenter) * _deltaImg; _realCenter += deltaReal; _imgCenter += deltaImg; Start(); } else { //User may have selected from right to left X1 = Math.Min( e.X, _LastPoint.X ); X2 = Math.Max( e.X, _LastPoint.X ); Y1 = Math.Min( e.Y, _LastPoint.Y ); Y2 = Math.Max( e.Y, _LastPoint.Y ); //If tiny selection, assume accidental mouseup if( Math.Abs(X1 - X2) < 3 || Math.Abs(Y1 - Y2) < 3 ) { ClearSelection(); } else { Zoom( X1, Y1, X2, Y2 ); } } } public void ZoomIn( ) { _deltaReal /= 2; _deltaImg /= 2; _realMin = _realCenter - (_width / 2) * _deltaReal; _imgMin = _imgCenter - (_height / 2) * _deltaImg; Start(); } public void ZoomOut( ) { _deltaReal *= 2; _deltaImg *= 2; _realMin = _realCenter - (_width / 2) * _deltaReal; _imgMin = _imgCenter - (_height / 2) * _deltaImg; Start(); } void Zoom( int X1, int Y1, int X2, int Y2 ) { double realMin = _realCenter - (_width / 2) * _deltaReal; double realMax = _realCenter + (_width / 2) * _deltaReal; double imgMin = _imgCenter - (_height / 2) * _deltaImg; double imgMax = _imgCenter + (_height / 2) * _deltaImg; double Old_realMin = realMin; double Old_imgMin = imgMin; double Old_realMax = realMax; double Old_imgMax = imgMax; realMin += (double)X1 * _deltaReal; realMax = Old_realMin + (double)X2 * _deltaReal; imgMin += (double)Y1 * _deltaImg; imgMax = Old_imgMin + (double)Y2 * _deltaImg; if( (realMin == realMax) || (imgMin == imgMax ) ) { ClearSelection(); return; } _realCenter = (realMin + realMax) / 2; _imgCenter = (imgMin + imgMax) / 2; _deltaReal = (realMax - realMin) / _width; _deltaImg = (imgMax - imgMin) / _height; if( _deltaReal > _deltaImg ) { _deltaImg = _deltaReal; } else { _deltaReal = _deltaImg; } Start(); } public void SaveBitmap( string fn, ImageFormat format ) { if( _bmp != null ) { _bmp.Save( fn, format ); } } public Bitmap Image { get { return( _bmp ); } } public int Iterations { get { return( _MaxIter ); } set { _MaxIter = value; } } public int[] ColorMap { get { return( _Color ); } set { _Color = value; } } public double Scale { get { return( _deltaReal ); } set { _deltaReal = value; _deltaImg = value; } } public double RealCenter { get { return( _realCenter ); } set { _realCenter = value; } } public double ImgCenter { get { return( _imgCenter ); } set { _imgCenter = value; } } } }