Click here to Skip to main content
15,893,486 members
Articles / Programming Languages / C#

Mars Mission (3) : structure interior editor

Rate me:
Please Sign up or sign in to vote.
4.94/5 (18 votes)
21 Mar 2011CPOL27 min read 46.6K   2.2K   14  
strategy/action game defending the solar system : collision detection between a polygon object moving inside a polygon container
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using System.IO;
using System.Threading;
using Mars_Mission;

namespace Sprite
{
    public enum enuMirror { none, vertical, horizontal, VerticalAndHorizontal }

    public struct udtSprite_StorageRetrieval
    {
        public string myName;
        public udtLimb_StorageRetrieval[] limbs;
        public int intImagesPerRotation;
    }

    public struct udtLimb_StorageRetrieval
    {
        public string myName;
        public Bitmap[] bmp;
        public bool IRotate;
        public bool AlwaysVertical;
        public classMath.classRadialCoor radMaster;
        public int MasterIndex;
        public classMath.classRadialCoor radSlave;
        public string hingeName;
        public int intNumImagesPerQuarterRotation;
    }

    class formGenerateFirstQuarterImages : Form
    {
        classSpriteLimb limb;
        Bitmap[] bmpSpriteLimb;
        Bitmap bmpOriginal;
        ProgressBar proBar = new ProgressBar();
        System.Windows.Forms.Timer tmrGenerateFirstQuarterImagesFromOriginal = new System.Windows.Forms.Timer();
        int intImageCounter = 1;
        Label lbl = new Label();

        public formGenerateFirstQuarterImages(ref classSpriteLimb Limb, Bitmap BmpOriginal)
        {
            limb = Limb;
			ShowInTaskbar = false;
            FormBorderStyle = FormBorderStyle.None;
            bmpOriginal = BmpOriginal;
            Controls.Add(proBar);
            Controls.Add(lbl);
            BackColor
                = lbl.BackColor
                = proBar.BackColor
                = Color.Teal;
            ForeColor
                = lbl.ForeColor
                = proBar.ForeColor
                = Color.Black;


            if (limb.IRotate)
            {
                if (bmpSpriteLimb == null)
                    bmpSpriteLimb = new Bitmap[(int)limb.mainSprite.ImagesPerRotation];
                bmpSpriteLimb[0] = bmpOriginal;
                limb.bmpSpriteLimb = bmpSpriteLimb;
                limb.myCurrentImage = bmpSpriteLimb[0];
            }
            else
            {
                bmpSpriteLimb = new Bitmap[(int)limb.mainSprite.ImagesPerRotation];
            }

            tmrGenerateFirstQuarterImagesFromOriginal.Interval = 5;
            tmrGenerateFirstQuarterImagesFromOriginal.Tick += new EventHandler(tmrGenerateFirstQuarterImagesFromOriginal_Tick);
            VisibleChanged += new EventHandler(formGenerateFirstQuarterImages_VisibleChanged);
        }

        void tmrGenerateFirstQuarterImagesFromOriginal_Tick(object sender, EventArgs e)
        {
            tmrGenerateFirstQuarterImagesFromOriginal.Enabled = false;

            Color clrTransparent = bmpSpriteLimb[0].GetPixel(0, 0);
            double dblRotationAngle = 0;

            dblRotationAngle = Math.PI + (Math.PI * (double)intImageCounter / (limb.mainSprite.ImagesPerRotation / 2));
            bmpSpriteLimb[intImageCounter] = rotate(bmpSpriteLimb[0], dblRotationAngle);
            intImageCounter++;
            proBar.Value = intImageCounter;

            if (intImageCounter < (int)(limb.mainSprite.ImagesPerRotation / 4))
                tmrGenerateFirstQuarterImagesFromOriginal.Enabled = true;
            else
                Dispose();
        }

        void formGenerateFirstQuarterImages_VisibleChanged(object sender, EventArgs e)
        {
            if (Visible)
            {
                Width = (int)(Screen.PrimaryScreen.WorkingArea.Width * .6);
                Height = proBar.Height + lbl.Height;
                lbl.Top = 0;
                lbl.Left = 0;
                lbl.Width
                    = proBar.Width
                    = Width;
                proBar.Top = lbl.Top + lbl.Height;
                proBar.Maximum = (int)(limb.mainSprite.ImagesPerRotation / 4);
                proBar.Value = intImageCounter;

                Left = (Screen.PrimaryScreen.WorkingArea.Width - Width) / 2;
                Top = (Screen.PrimaryScreen.WorkingArea.Height - Height) / 2;
                lbl.Text = "Rotating images for limb \"" + limb.Name + "\".";

                if (limb.IRotate)
                    tmrGenerateFirstQuarterImagesFromOriginal.Enabled = true;
                else
                    Dispose();
            }
        }

        Bitmap rotate(Bitmap bmpInput, double dblAngleRotation)
        {
            int intMinSquare = bmpInput.Width > bmpInput.Height
                                ? 3 * bmpInput.Width
                                : 3 * bmpInput.Height;
            Color clrHole = Color.YellowGreen;   // some color assumed NOT in the input bmp

            Bitmap bmpCopy = new Bitmap(intMinSquare, intMinSquare);

            Point ptTL;
            Point ptBR;
            Point ptCopyCenter = new Point(bmpCopy.Width / 2, bmpCopy.Height / 2);
            Point ptOriginalCenter = new Point(bmpInput.Width / 2, bmpInput.Height / 2);

            using (Graphics g = Graphics.FromImage(bmpCopy))
            {
                g.FillRectangle(new SolidBrush(clrHole), new Rectangle(new Point(0, 0), bmpCopy.Size));
                ptTL = new Point(ptCopyCenter.X, ptCopyCenter.Y);
                ptBR = new Point(ptCopyCenter.X, ptCopyCenter.Y);

                for (int intX = 0; intX < bmpInput.Width; intX++)
                {
                    for (int intY = 0; intY < bmpInput.Height; intY++)
                    {
                        int intDX = ptOriginalCenter.X - intX;
                        int intDY = ptOriginalCenter.Y - intY;
                        classMath.classRadialCoor udrOriginalRad = new classMath.classRadialCoor(classMath.arcTan(intDX, intDY), Math.Sqrt(intDX * intDX + intDY * intDY));
                        classMath.classRadialCoor udrCopyRad = new classMath.classRadialCoor(udrOriginalRad.angle + dblAngleRotation, udrOriginalRad.radius);
                        Point ptCopy = new Point((int)(ptCopyCenter.X + udrCopyRad.radius * Math.Cos(udrCopyRad.angle)),
                                                 (int)(ptCopyCenter.Y + udrCopyRad.radius * Math.Sin(udrCopyRad.angle)));

                        try { bmpCopy.SetPixel(ptCopy.X, ptCopy.Y, bmpInput.GetPixel(intX, intY)); }
                        catch (Exception) { }

                        // keep track of limits of output image
                        if (ptCopy.X < ptTL.X)
                            ptTL.X = ptCopy.X;
                        if (ptCopy.Y < ptTL.Y)
                            ptTL.Y = ptCopy.Y;
                        if (ptCopy.X > ptBR.X)
                            ptBR.X = ptCopy.X;
                        if (ptCopy.Y > ptBR.Y)
                            ptBR.Y = ptCopy.Y;
                    }
                }
            }

            // write image to output array
            Size szOfImage = new Size(ptBR.X - ptTL.X, ptBR.Y - ptTL.Y);
            Rectangle recSrc = new Rectangle(ptTL, szOfImage);
            Rectangle recDest = new Rectangle(new Point(0, 0), szOfImage);

            // fill the blanks missed when rotating image
            // scanning x from 0 to width created a problem in the fill-blank
            for (int intX = ptTL.X; intX < ptBR.X; intX++)
                for (int intY = ptTL.Y; intY < ptBR.Y; intY++)
                {
                    Color clrTest = bmpCopy.GetPixel(intX, intY);
                    if (clrTest.A == clrHole.A
                        && clrTest.R == clrHole.R
                        && clrTest.G == clrHole.G
                        && clrTest.B == clrHole.B)
                    {
                        classPixilateColorFind[] udrColorFind = new classPixilateColorFind[0];
                        if (intX > 0 && intX < bmpCopy.Width - 2 && intY > 0 && intY < bmpCopy.Height - 2)
                        {
                            Point ptUp = new Point(intX, intY - 1);
                            Point ptDown = new Point(intX, intY + 1);
                            Point ptLeft = new Point(intX - 1, intY);
                            Point ptRight = new Point(intX + 1, intY);
                            Point ptUL = new Point(intX - 1, intY - 1);
                            Point ptUR = new Point(intX + 1, intY - 1);
                            Point ptDR = new Point(intX + 1, intY + 1);
                            Point ptDL = new Point(intX - 1, intY + 1);

                            addPixilateColorFound(ref udrColorFind, bmpCopy.GetPixel(ptUp.X, ptUp.Y));
                            addPixilateColorFound(ref udrColorFind, bmpCopy.GetPixel(ptDown.X, ptDown.Y));
                            addPixilateColorFound(ref udrColorFind, bmpCopy.GetPixel(ptLeft.X, ptLeft.Y));
                            addPixilateColorFound(ref udrColorFind, bmpCopy.GetPixel(ptRight.X, ptRight.Y));
                            addPixilateColorFound(ref udrColorFind, bmpCopy.GetPixel(ptDL.X, ptDL.Y));
                            addPixilateColorFound(ref udrColorFind, bmpCopy.GetPixel(ptDR.X, ptDR.Y));
                            addPixilateColorFound(ref udrColorFind, bmpCopy.GetPixel(ptUL.X, ptUL.Y));
                            addPixilateColorFound(ref udrColorFind, bmpCopy.GetPixel(ptUR.X, ptUR.Y));
                            reorderPixilateColorFound(ref udrColorFind);
                            if (udrColorFind[0].numFound >= 2)
                                try { bmpCopy.SetPixel(intX, intY, udrColorFind[0].clr); }
                                catch (Exception) { }
                            else
                            {
                                int intDX = ptCopyCenter.X - intX;
                                int intDY = ptCopyCenter.Y - intY;
                                classMath.classRadialCoor udrOriginalRad = new classMath.classRadialCoor(classMath.arcTan(intDX, intDY), Math.Sqrt(intDX * intDX + intDY * intDY));
                                classMath.classRadialCoor udrCopyRad = new classMath.classRadialCoor(udrOriginalRad.angle - dblAngleRotation, udrOriginalRad.radius);
                                Point ptCopy = new Point((int)(ptOriginalCenter.X + udrCopyRad.radius * Math.Cos(udrCopyRad.angle)),
                                                         (int)(ptOriginalCenter.Y + udrCopyRad.radius * Math.Sin(udrCopyRad.angle)));

                                if (ptCopy.X >= 0 && ptCopy.X < bmpCopy.Width && ptCopy.Y >= 0 && ptCopy.Y < bmpCopy.Height)
                                    try { bmpCopy.SetPixel(intX, intY, bmpInput.GetPixel(ptCopy.X, ptCopy.Y)); }
                                    catch (Exception) { }
                            }

                            classPixilateColorFind.clear();
                        }
                    }
                }
            if (recDest.Width < 1)
                recDest.Width = 1;
            if (recDest.Height < 1)
                recDest.Height = 1;
            Bitmap bmpTemp = new Bitmap(recDest.Width, recDest.Height);

            using (Graphics g = Graphics.FromImage(bmpTemp))
            {
                g.FillRectangle(new SolidBrush(Color.White), new Rectangle(0, 0, bmpTemp.Width, bmpTemp.Height));
                bmpCopy.MakeTransparent(bmpCopy.GetPixel(0, 0));
                g.DrawImage(bmpCopy, recDest, recSrc, GraphicsUnit.Pixel);
            }
            return bmpTemp;
        }

        private void addPixilateColorFound(ref classPixilateColorFind[] udrPixilateColorFind, Color clrFound)
        {
            if (udrPixilateColorFind == null)
            {
                udrPixilateColorFind = new classPixilateColorFind[1];
                udrPixilateColorFind[0] = classPixilateColorFind.getPixilateColorFind();
                udrPixilateColorFind[0].clr = clrFound;
                udrPixilateColorFind[0].numFound = 1;
            }
            else
            {
                for (int intClrCounter = 0; intClrCounter < udrPixilateColorFind.Length; intClrCounter++)
                    if (udrPixilateColorFind[intClrCounter].clr == clrFound)
                    {
                        udrPixilateColorFind[intClrCounter].numFound++;
                        return;
                    }
                Array.Resize<classPixilateColorFind>(ref udrPixilateColorFind, udrPixilateColorFind.Length + 1);
                udrPixilateColorFind[udrPixilateColorFind.Length - 1] = classPixilateColorFind.getPixilateColorFind();
                udrPixilateColorFind[udrPixilateColorFind.Length - 1].clr = clrFound;
                udrPixilateColorFind[udrPixilateColorFind.Length - 1].numFound = 1;
            }
        }

        void reorderPixilateColorFound(ref classPixilateColorFind[] udrPixilateColorFind)
        {
            int intBestFind;
            for (int intOuterLoop = 0; intOuterLoop < udrPixilateColorFind.Length - 1; intOuterLoop++)
            {
                intBestFind = intOuterLoop;
                for (int intInnerLoop = intOuterLoop + 1; intInnerLoop < udrPixilateColorFind.Length; intInnerLoop++)
                {
                    if (udrPixilateColorFind[intInnerLoop].numFound > udrPixilateColorFind[intBestFind].numFound)
                        intBestFind = intInnerLoop;
                }
                if (intBestFind != intOuterLoop)
                {
                    classPixilateColorFind udrTemp = udrPixilateColorFind[intOuterLoop];
                    udrPixilateColorFind[intOuterLoop] = udrPixilateColorFind[intBestFind];
                    udrPixilateColorFind[intBestFind] = udrTemp;
                }
            }
        }
    }

    /// <summary>
    /// a hinge connects to limbs together
    /// radial coordinates are relative to the center of each image
    /// the slave's master coordinate is place at the same location as its master's slave coordinate onto the screen
    /// </summary>
    public class classSpriteHinge
    {
        public classSprite mainSprite;
        string myName;
        public bool bolFlag = true;
        public int intMyHingeIndex;
        public string name { get { return myName; } }

        double myAngle;
        /// <summary>
        /// angle of slave limb relative to angle of master limb
        /// </summary>
        public double Angle
        {
            get { return myAngle; }
            set
            {
                double dblTestAngle = value - (Math.PI / mainSprite.ImagesPerRotation);
                if (dblTestAngle < 0)
                    dblTestAngle += (Math.PI * 2.0);
                int intDir = (int)(Math.Ceiling(dblTestAngle / (Math.PI / (mainSprite.ImagesPerRotation / 2))));
                myAngle = intDir * Math.PI / (mainSprite.ImagesPerRotation / 2);
            }
        }

        public classSpriteHinge(string name) { myName = name; }
        /// <summary>
        /// a hinge connects to limbs together
        /// radial coordinates are relative to the center of each image
        /// the slave's master coordinate is place at the same location as its master's slave coordinate onto the screen    
        /// </summary>
        /// <param name="name">name of this hinge, e.g. "elbow"</param>
        /// <param name="RadCoor_Master">radial coordinate (radians, pixels) of location of joint on master image relative to center</param>
        /// <param name="RadCoor_Slave">radial coordinate (radians, pixels) of location of joint on slave image relative to center</param>
        /// <param name="LimbMaster">pointer to Master-Limb</param>
        /// <param name="LimbSlave">pointer to Slave-Limb</param>
        public classSpriteHinge(string name, classMath.classRadialCoor RadCoor_Master, classMath.classRadialCoor RadCoor_Slave, classSpriteLimb LimbMaster, classSpriteLimb LimbSlave)
        {
            cRadCoor_Master = RadCoor_Master;
            cRadCoor_Slave = RadCoor_Slave;
            classLimb_Master = LimbMaster;
            classLimb_Slave = LimbSlave;
            myName = name;
        }

        classMath.classRadialCoor cRadCoor_Master;
        /// <summary>
        /// radial coordinate location relative to center of image
        /// </summary>
        public classMath.classRadialCoor RadCoor_Master
        {
            get { return cRadCoor_Master; }
            set { cRadCoor_Master = value; }
        }

        classMath.classRadialCoor cRadCoor_Slave;
        /// <summary>
        /// radial coordinate location relative to center of image
        /// </summary>
        public classMath.classRadialCoor RadCoor_Slave
        {
            get { return cRadCoor_Slave; }
            set { cRadCoor_Slave = value; }
        }
        classSpriteLimb classLimb_Master;
        /// <summary>
        /// pointer to limb connected to this hinge
        /// </summary>
        public classSpriteLimb Limb_Master
        {
            get { return classLimb_Master; }
            set { classLimb_Master = value; }
        }

        classSpriteLimb classLimb_Slave;
        /// <summary>
        /// pointer to limb connected to this hinge
        /// </summary>
        public classSpriteLimb Limb_Slave
        {
            get { return classLimb_Slave; }
            set { classLimb_Slave = value; }
        }

        Point pt;
        public Point Loc
        {
            get { return pt; }
            set { pt = value; }
        }
    }

    
    public class classSpriteLimb
    {
        public Sprite.classSprite mainSprite;
        public bool bolFlag = true;
        public Rectangle recDest;
        public Bitmap myCurrentImage;

        public classSpriteLimb[] mySlaveLimbs;
        int intMySlaveID;
        /// <summary>
        /// my index in my master's slave limb array
        /// </summary>
        public int MySlaveID
        {
            get { return intMySlaveID; }
            set { intMySlaveID = value; }
        }

        public int intMyLimbID;

        bool bolAlwaysVertical = false;
        /// <summary>
        /// determines whether generated images are flipped to remain vertical or rotated 360 degrees
        /// </summary>
        public bool AlwaysVertical { get { return bolAlwaysVertical; } }

        string myName;
        public string Name { get { return myName; } }

        Point ptLoc;
        /// <summary>
        /// location of top-left corner of image's rectangle
        /// </summary>
        public Point Loc
        {
            get { return ptLoc; }
            set { ptLoc = value; }
        }

        int dir;
        /// <summary>
        /// 0-15 where 0 is east, 4 north, etc.
        /// also corresponds to the bmpSpriteLimb's index of currently displayed image
        /// </summary>
        public int Dir
        {
            get { return dir; }
            set { dir = value; }
        }

        double angle;
        /// <summary>
        /// at which this limb currently has on the screen
        /// </summary>
        public double Angle
        {
            get 
            {
                return angle;
            }
            set { angle = value; }
        }

        /// <summary>
        /// this limb's images
        /// </summary>
        public Bitmap[] bmpSpriteLimb;

        bool bolIRotate = true;
        /// <summary>
        /// boolean variable which determines whether this limb rotates or animates
        /// when true, SpriteLimbImages is expected to consist of N(num images per rotation) images
        /// when false, SpriteLimbImages is either one or many images which are used to animate this limb
        /// </summary>
        public bool IRotate
        {
            get { return bolIRotate; }
            set { bolIRotate = value; }
        }

        //bool bolIHaveNoMaster = false;
        //public bool IHaveNoMaster { get { return bolIHaveNoMaster; } }
        public classSpriteHinge MasterHinge;

        /// <summary>
        /// a limb consists of an array of images with their corresponding hinge locations
        /// </summary>
        /// <param name="IRotate">
        /// when this is set the indices of udrSpriteLimbImage are direction of rotation, e.g. action-arm, these are created if not provided
        /// when this is not set these are animation images, e.g. walking legs</param>
        /// </param>
        /// <param name="AlwaysVertical">
        /// determines whether generated images are flipped to remain vertical or rotated 360 degrees
        /// </param>
        /// <param name="_myName">
        /// used for ease of debugging e.g. "Torso", "left arm", "right hand"
        /// </param>
        /// <param name="bmpOriginal">
        /// first elements of array of images
        /// </param>
        public classSpriteLimb(ref classSprite sprite, bool IRotate, bool AlwaysVertical, string _myName)
        {
            mainSprite = sprite;
            myName = _myName;
            bolIRotate = IRotate;
            bolAlwaysVertical = AlwaysVertical;

            if (IRotate)
            {
                bmpSpriteLimb = new Bitmap[(int)mainSprite.ImagesPerRotation];
            }
            else
            { // debug here (not done)
                bmpSpriteLimb = new Bitmap[(int)mainSprite.ImagesPerRotation];
            }
        }

        #region "initialize images"
        public void initImages(Bitmap bmpOriginal)
        {
            classSpriteLimb cMyReference = this;
            if (bmpSpriteLimb == null || bmpSpriteLimb.Length != mainSprite.ImagesPerRotation)
            {
                bmpSpriteLimb = new Bitmap[(int)mainSprite.ImagesPerRotation];
            }
			if (bmpOriginal.Width > 3 && bmpOriginal.Height > 3)
			{
				formGenerateFirstQuarterImages frmGenerateFirstQuarterImages = new formGenerateFirstQuarterImages(ref cMyReference, bmpOriginal);
				frmGenerateFirstQuarterImages.ShowDialog();
			}
        }
        /// <summary>
        /// returns image appropriate for this limb at current angle
        /// selected from array of N images
        /// </summary>
        public Bitmap getLimbImage() { return getLimbImage(ref bmpSpriteLimb, angle); }

        /// <summary>
        /// returns image appropriate for this limb at input angle
        /// selected from array of N images
        /// </summary>
        /// <param name="dblAngle">
        /// angled image to be returned
        /// </param>
        /// <returns></returns>
        public Bitmap getLimbImage(double dblAngle) { return getLimbImage(ref bmpSpriteLimb, dblAngle); }

        /// <summary>
        /// returns image appropriate for this limb at input angle
        /// selected from array of N images
        /// </summary>
        /// <param name="udrSpriteImage">array of N/4 images pointing in N angles where east is 0th, North-east is 2nd, North 4th, etc.</param>
        /// <param name="dblAngle">angle in radians where 0 is east Pi/2 is north, Pi is west, and 3Pi/2 is south</param>
        /// <returns></returns>
        public Bitmap getLimbImage(ref Bitmap[] udrSpriteImage, double dblAngle)
        {
            return getLimbImage(ref udrSpriteImage, dblAngle, mainSprite.eMirror);
        }

        public Bitmap getLimbImage(ref Bitmap[] udrSpriteImage, double dblAngle, enuMirror eMirror)
        {
            angle = dblAngle;
            while (angle < 0)
                angle += (2.0 * Math.PI);

            while (angle > 2.0 * Math.PI)
                angle -= (2.0 * Math.PI);

            setDir();
            switch (eMirror)
            {
                case enuMirror.horizontal:
                    int intImageSourceDir_HFlip = getDir(classMath.cleanAngle(angle));
                    Bitmap bmpRetValHorizontalMirror = new Bitmap(getLimbImageFromArray_ByDir(ref udrSpriteImage, intImageSourceDir_HFlip));
                    bmpRetValHorizontalMirror.RotateFlip(RotateFlipType.RotateNoneFlipX);
                    return bmpRetValHorizontalMirror;

                
                case enuMirror.vertical:
                    int intImageSourceDir_VFlip = getDir(angle);
                    Bitmap bmpRetValVerticalMirror = new Bitmap(getLimbImageFromArray_ByDir(ref udrSpriteImage, intImageSourceDir_VFlip));
                    bmpRetValVerticalMirror.RotateFlip(RotateFlipType.RotateNoneFlipY);
                    return bmpRetValVerticalMirror;

                case enuMirror.VerticalAndHorizontal:
                    int intImageSourceDir_VHFlip = getDir(classMath.cleanAngle(-angle));
                    Bitmap bmpRetValVerticalHorizontalMirror = new Bitmap(getLimbImageFromArray_ByDir(ref udrSpriteImage, intImageSourceDir_VHFlip));
                    return bmpRetValVerticalHorizontalMirror;


                default:
                case enuMirror.none:
                    Bitmap bmpRetval =  new Bitmap(getLimbImageFromArray_ByDir(ref udrSpriteImage, dir));
                    return bmpRetval;
            }
        }

        public void setDir() { setDir(angle); }
        public void setDir(double dblAngle)
        {
            dir = getDir(dblAngle);
            angle = dir * (Math.PI / (mainSprite.ImagesPerRotation / 2.0));
        }

        public int getDir(double dblAngle)
        {
            double dblTestAngle = dblAngle - (Math.PI / mainSprite.ImagesPerRotation);
            if (dblTestAngle < 0)
                dblTestAngle += (Math.PI * 2.0);
            int intPicIndex = (int)(Math.Ceiling(dblTestAngle / (Math.PI / (mainSprite.ImagesPerRotation / 2.0))));

            if (intPicIndex == (int)mainSprite.ImagesPerRotation)
                intPicIndex = 0;
            return intPicIndex;
        }

        void loadImageIntoArray(ref Bitmap[] udrSpriteImage, int intDir)
        {
            // get address of this image's index
            mainSprite.cFileStream.fs.Position = mainSprite.getLimbImageIndex(intMyLimbID, intDir);
            long lngPosition = (long)classSpriteFileStream.formatter.Deserialize(mainSprite.cFileStream.fs);
            if (lngPosition > 0)
            {
                mainSprite.cFileStream.fs.Position = lngPosition;
				udrSpriteImage[intDir] = (Bitmap)classSpriteFileStream.formatter.Deserialize(mainSprite.cFileStream.fs);
            }
        }

        public Bitmap getLimbImageFromArray_ByDir(int intDir) { return getLimbImageFromArray_ByDir(ref bmpSpriteLimb, intDir); }
        /// <summary>
        /// returns image appropriate for this limb at input direction
        /// selected from array of N images
        /// </summary>
        /// <param name="udrSpriteImage">array of 4 images pointing in N angles where east is 0th, North-east is 2nd, North 4th, etc.</param>
        /// <param name="intDir">indicates the desired direction of image where 0th is east, 4th is north, 8 is west and 12th south, etc.</param>
        /// <returns></returns>
        public Bitmap getLimbImageFromArray_ByDir(ref Bitmap[] udrSpriteLimbImage, int intDir)
        {
            if (udrSpriteLimbImage == null)
                return null;

            if (intDir < 0 || intDir >= mainSprite.ImagesPerRotation)
            {
                MessageBox.Show("error getLimbImageFromArray_ByDir");
                return getLimbImageFromArray_ByDir(ref udrSpriteLimbImage, 0);
            }
            else
            {
                if (udrSpriteLimbImage.Length > 1)
                {
                   if (intDir < (mainSprite.ImagesPerRotation / 4.0))
                    {
                        if (udrSpriteLimbImage[intDir]== null)
                            loadImageIntoArray(ref udrSpriteLimbImage, intDir);
                        return udrSpriteLimbImage[intDir];
                    }
                    else
                    {
                        if (bmpSpriteLimb[intDir] != null && false)
                        {
                            if (udrSpriteLimbImage[intDir] == null)
                                loadImageIntoArray(ref udrSpriteLimbImage, intDir);
                            return bmpSpriteLimb[intDir];
                        }
                        else
                        {
                            int intBaseImage = intDir % (int)(mainSprite.ImagesPerRotation / 4.0);
                            int intQuarterRotations = (int)Math.Floor((double)intDir / (mainSprite.ImagesPerRotation / 4.0));
                            Bitmap bmpRetVal;// = new Bitmap();

                            if (udrSpriteLimbImage[intBaseImage] == null)
                                loadImageIntoArray(ref udrSpriteLimbImage, intBaseImage);
                            bmpRetVal = (Bitmap)udrSpriteLimbImage[intBaseImage].Clone();

                            switch (intQuarterRotations)
                            {
                                case 3:
                                    bmpRetVal.RotateFlip(RotateFlipType.Rotate270FlipNone);
                                    break;

                                case 2:
                                    bmpRetVal.RotateFlip(RotateFlipType.Rotate180FlipNone);
                                    break;

                                case 1:
                                    bmpRetVal.RotateFlip(RotateFlipType.Rotate90FlipNone);
                                    break;
                            }
                            return bmpRetVal;
                        }
                    }
                    //}
                }
                else
                {
                    if (udrSpriteLimbImage[intDir] == null)
                        loadImageIntoArray(ref udrSpriteLimbImage, intDir);
                    return udrSpriteLimbImage[intDir];
                }
            }
        }
        #endregion

        public void addLimb(classSpriteLimb newLimb, string hingeName, classMath.classRadialCoor radMasterJoint, classMath.classRadialCoor radSlaveJoint)
        {
            if (mySlaveLimbs == null)
                mySlaveLimbs = new classSpriteLimb[0];
            // add new limb to this limb
            Array.Resize<classSpriteLimb>(ref mySlaveLimbs, mySlaveLimbs.Length + 1);
            mySlaveLimbs[mySlaveLimbs.Length - 1] = newLimb;
            mySlaveLimbs[mySlaveLimbs.Length - 1].MasterHinge = new classSpriteHinge(hingeName, radMasterJoint, radSlaveJoint, this, newLimb);
            mySlaveLimbs[mySlaveLimbs.Length - 1].MasterHinge.mainSprite = mainSprite;
            newLimb.intMySlaveID = mySlaveLimbs.Length - 1;

            // add new hinge to sprite's hinges
            Array.Resize<Sprite.classSpriteHinge>(ref mainSprite.hinges, mainSprite.hinges.Length + 1);
            mainSprite.hinges[mainSprite.hinges.Length - 1] = mySlaveLimbs[mySlaveLimbs.Length - 1].MasterHinge;
            mainSprite.hinges[mainSprite.hinges.Length - 1].intMyHingeIndex = mainSprite.hinges.Length - 1;

            // add new limb to sprite' limbs
            Array.Resize<Sprite.classSpriteLimb>(ref mainSprite.limbs, mainSprite.limbs.Length + 1);
            mainSprite.limbs[mainSprite.limbs.Length - 1] = newLimb;
            newLimb.intMyLimbID = mainSprite.limbs.Length - 1;

        }

        public void subLimb(ref classSpriteLimb thisLimb)
        {
            if (thisLimb.myName.CompareTo(mySlaveLimbs[thisLimb.MySlaveID].myName) != 0)
                MessageBox.Show("we have a problem!");

            disposeAllHinges(ref thisLimb);

            mySlaveLimbs[thisLimb.MySlaveID] = mySlaveLimbs[mySlaveLimbs.Length - 1];
            Array.Resize<Sprite.classSpriteLimb>(ref mySlaveLimbs, mySlaveLimbs.Length - 1);
        }

        void disposeAllHinges(ref Sprite.classSpriteLimb thisLimb)
        {
            for (int intSlaveCounter = 0; intSlaveCounter < mySlaveLimbs.Length; intSlaveCounter++)
                disposeAllHinges(ref mySlaveLimbs[intSlaveCounter]);

            for (int intHingeCounter = 0; intHingeCounter < mainSprite.hinges.Length; intHingeCounter++)
                if (mainSprite.hinges[intHingeCounter].name.CompareTo(thisLimb.MasterHinge.name) == 0)
                {
                    mainSprite.hinges[intHingeCounter] = mainSprite.hinges[mainSprite.hinges.Length - 1];
                    Array.Resize<Sprite.classSpriteHinge>(ref mainSprite.hinges, mainSprite.hinges.Length - 1);
                }
        }
    }

    public class classSpriteMaker
    {
        udtSprite_StorageRetrieval udrStorageSprite;
        const int conMaxNameStringSize = 50;
        const string conStrNull = "---NULL---";
        public const string conMasterLimbName = "-Master-Limb-";
        public static classSpriteFileStream cFileStreamsTree;

        public static classSpriteFileStream _defaultFS;
        static string strDefaultFilestreamFilename = classSpriteFileStream.strWorkingDirectory  + "DEFAULTfilestream.sp3";
        public static classSpriteFileStream getDefaultFileStream()
        {
            if (_defaultFS != null)
                if (_defaultFS.fs != null)
                    _defaultFS.fs.Close();
            return new classSpriteFileStream(strDefaultFilestreamFilename);
        }

        classSpriteFileStream getFileStream(string strFilename)
        {
            if (cFileStreamsTree == null)
            {
				// debug start
				if (System.IO.File.Exists(classSpriteFileStream.strCache_BmpAndTLInfo_Filename))
					System.IO.File.Delete(classSpriteFileStream.strCache_BmpAndTLInfo_Filename);

				if (System.IO.File.Exists(classSpriteFileStream.strCache_BmpInfo_RAMCacheRebuild))
					System.IO.File.Delete(classSpriteFileStream.strCache_BmpInfo_RAMCacheRebuild);
				// debug end


                cFileStreamsTree = new classSpriteFileStream(strFilename);
                return cFileStreamsTree;
            }
            else
            {
                strFilename = strFilename.ToUpper();
                classSpriteFileStream cThisFS = cFileStreamsTree;
                while (true)
                {
                    if (strFilename.CompareTo(cThisFS.strFilename) > 0)
                    {
                        if (cThisFS.right != null)
                            cThisFS = cThisFS.right;
                        else
                        {
                            cThisFS.right = new classSpriteFileStream(strFilename);
                            return cThisFS.right;
                        }
                    }
                    else if (strFilename.CompareTo(cThisFS.strFilename) < 0)
                    {
                        if (cThisFS.left != null)
                            cThisFS = cThisFS.left;
                        else
                        {
                            cThisFS.left = new classSpriteFileStream(strFilename);
                            return cThisFS.left;
                        }
                    }
                    else
                    {
                        return cThisFS;
                    }
                }
            }
        }
       
        BinaryFormatter formatter = new BinaryFormatter();

        public classSprite getSprite(bool masterLimbRotates, bool AlwaysVertical, string _myName)
        { return getSprite(masterLimbRotates, 4, AlwaysVertical, _myName); }
        public classSprite getSprite(bool masterLimbRotates, int ImagesPerQuarterRotation, bool AlwaysVertical, string _myName)
        {
            classSprite spriteRetVal = new classSprite(ImagesPerQuarterRotation);
            spriteRetVal.myName = _myName;
            spriteRetVal.MasterLimb = new classSpriteLimb(ref spriteRetVal, masterLimbRotates, AlwaysVertical, conMasterLimbName);
			return spriteRetVal;
        }

        #region "Storage and Retrieval"
        public classSprite loadSprite(string strFilename)
        {
            int intcutchr = strFilename.IndexOf(".");
            if (intcutchr > 0)
                strFilename = strFilename.Substring(0, intcutchr);
            strFilename += ".sp3";
            if (System.IO.File.Exists(strFilename))
            {
                classSprite sprRetVal = loadSprite_V3(strFilename);
                return sprRetVal;
            }
            else
            {
                strFilename = strFilename.Replace(".sp3", ".sp2");
                if (System.IO.File.Exists(strFilename))
                {
                    classSprite sprRetVal = loadSprite_V2(strFilename);
                    saveSprite(ref sprRetVal, strFilename);
                    return sprRetVal;
                }
                else
                {
                    classSprite sprRetVal = loadSprite_V1(strFilename);
                    saveSprite(ref sprRetVal, strFilename);
                    return sprRetVal;
                }
            }
        }

		public classSprite loadSprite_V3(string strFilename)
		{
			udtSprite_StorageRetrieval udrThisSprite = new udtSprite_StorageRetrieval();

			int intcutchr = strFilename.IndexOf(".");
			if (intcutchr > 0)
				strFilename = strFilename.Substring(0, intcutchr);
			strFilename += ".sp3";
			classSpriteFileStream cFilestream = getFileStream(strFilename);
			cFilestream.fs.Position = 0;
			udrThisSprite.myName = (string)formatter.Deserialize(cFilestream.fs);
			udrThisSprite.intImagesPerRotation = (int)formatter.Deserialize(cFilestream.fs);
			int intNumLimbs = (int)formatter.Deserialize(cFilestream.fs);

			udrThisSprite.limbs = new udtLimb_StorageRetrieval[intNumLimbs];

			for (int intLimbCounter = 0; intLimbCounter < intNumLimbs; intLimbCounter++)
			{
				udrThisSprite.limbs[intLimbCounter].myName = (string)formatter.Deserialize(cFilestream.fs);

				udrThisSprite.limbs[intLimbCounter].IRotate = (bool)formatter.Deserialize(cFilestream.fs);
				udrThisSprite.limbs[intLimbCounter].AlwaysVertical = (bool)formatter.Deserialize(cFilestream.fs);

				udrThisSprite.limbs[intLimbCounter].bmp = new Bitmap[(int)(udrThisSprite.intImagesPerRotation / 4)];

				udrThisSprite.limbs[intLimbCounter].radMaster = new classMath.classRadialCoor();
				udrThisSprite.limbs[intLimbCounter].radMaster.angle = (double)formatter.Deserialize(cFilestream.fs);
				udrThisSprite.limbs[intLimbCounter].radMaster.radius = (double)formatter.Deserialize(cFilestream.fs);

				udrThisSprite.limbs[intLimbCounter].radSlave = new classMath.classRadialCoor();
				udrThisSprite.limbs[intLimbCounter].radSlave.angle = (double)formatter.Deserialize(cFilestream.fs);
				udrThisSprite.limbs[intLimbCounter].radSlave.radius = (double)formatter.Deserialize(cFilestream.fs);

				udrThisSprite.limbs[intLimbCounter].MasterIndex = (int)formatter.Deserialize(cFilestream.fs);
				udrThisSprite.limbs[intLimbCounter].hingeName = (string)formatter.Deserialize(cFilestream.fs);
			}

			classSprite retval = getSprite(udrThisSprite.limbs[0].IRotate, udrThisSprite.intImagesPerRotation / 4, udrThisSprite.limbs[0].AlwaysVertical, udrThisSprite.myName);
			retval.cFileStream = cFilestream;
			Bitmap bmpMaster = new Bitmap(2, 2);
			using (Graphics g = Graphics.FromImage(bmpMaster))
			{
				using (SolidBrush br = new SolidBrush(Color.White))
				{
					g.FillRectangle(br, new Rectangle(0, 0, bmpMaster.Width, bmpMaster.Height));
				}
			}
			retval.MasterLimb.initImages(bmpMaster);

			for (int intLimbCounter = 0; intLimbCounter < udrThisSprite.limbs.Length; intLimbCounter++)
			{
				Sprite.classSpriteLimb masterLimb = retval.getLimbByName(udrThisSprite.limbs[udrThisSprite.limbs[intLimbCounter].MasterIndex].myName);
				if (masterLimb == null)
					masterLimb = retval.MasterLimb;

				Sprite.classSpriteLimb newLimb = new Sprite.classSpriteLimb(ref retval,
											udrThisSprite.limbs[intLimbCounter].IRotate,
											udrThisSprite.limbs[intLimbCounter].AlwaysVertical,
											udrThisSprite.limbs[intLimbCounter].myName.Trim());
				masterLimb.addLimb(newLimb,
								   udrThisSprite.limbs[intLimbCounter].hingeName.Trim(),
								   new classMath.classRadialCoor(udrThisSprite.limbs[intLimbCounter].radMaster.angle, udrThisSprite.limbs[intLimbCounter].radMaster.radius),
								   new classMath.classRadialCoor(udrThisSprite.limbs[intLimbCounter].radSlave.angle, udrThisSprite.limbs[intLimbCounter].radSlave.radius));
			}

			// load configurations
			int intNumConfigurations = (int)formatter.Deserialize(cFilestream.fs);

			Array.Resize<classConfiguration>(ref retval.Configurations, intNumConfigurations);
			for (int intConfigurationCounter = 0; intConfigurationCounter < retval.Configurations.Length; intConfigurationCounter++)
			{
				classConfiguration thisCon = new classConfiguration();
				thisCon.mainSprite = retval;
				thisCon.name = ((string)formatter.Deserialize(cFilestream.fs)).Trim();

				int intNumberLimbs = (int)formatter.Deserialize(cFilestream.fs);
				Array.Resize<classSpriteLimb>(ref thisCon.LimbDrawSequence, intNumberLimbs);
				for (int intLimbCounter = 0; intLimbCounter < thisCon.LimbDrawSequence.Length; intLimbCounter++)
				{
					string strConLimbName = ((string)formatter.Deserialize(cFilestream.fs)).Trim();
					thisCon.LimbDrawSequence[intLimbCounter] = retval.getLimbByName(strConLimbName);

					if (thisCon.LimbDrawSequence[intLimbCounter] == null)
						if (retval.MasterLimb.Name.ToUpper().Trim().CompareTo(strConLimbName.Trim().ToUpper()) == 0)
							thisCon.LimbDrawSequence[intLimbCounter] = retval.MasterLimb;
				}

				int intNumSteps = (int)formatter.Deserialize(cFilestream.fs);
				Array.Resize<classConfigurationStep>(ref thisCon.steps, intNumSteps);
				for (int intStepCounter = 0; intStepCounter < thisCon.steps.Length; intStepCounter++)
				{
					thisCon.steps[intStepCounter] = new classConfigurationStep();
					Array.Resize<classConfigurationStepHinge>(ref thisCon.steps[intStepCounter].hingeDetails, retval.hinges.Length);
					thisCon.steps[intStepCounter].hingeDetails = new classConfigurationStepHinge[retval.hinges.Length];
					for (int intHingeCounter = 0; intHingeCounter < thisCon.steps[intStepCounter].hingeDetails.Length; intHingeCounter++)
					{
						string strConStepHingeName = ((string)formatter.Deserialize(cFilestream.fs)).Trim();
						thisCon.steps[intStepCounter].hingeDetails[intHingeCounter] = new classConfigurationStepHinge();
						thisCon.steps[intStepCounter].hingeDetails[intHingeCounter].hinge = retval.getHingeByName(strConStepHingeName);
						thisCon.steps[intStepCounter].hingeDetails[intHingeCounter].angle = (double)formatter.Deserialize(cFilestream.fs);
						thisCon.steps[intStepCounter].hingeDetails[intHingeCounter].RadCoor_Master = retval.hinges[intHingeCounter].RadCoor_Master;
						thisCon.steps[intStepCounter].hingeDetails[intHingeCounter].RadCoor_Slave = retval.hinges[intHingeCounter].RadCoor_Slave;
					}
				}
				retval.Configurations[intConfigurationCounter] = thisCon;
			}

			retval.cFileStream.lngStartIndex = cFilestream.fs.Position;
			long lngFubu = (long)formatter.Deserialize(cFilestream.fs);
			retval.cFileStream.lngSizeLongIndexInFileStream = cFilestream.fs.Position - retval.cFileStream.lngStartIndex;

			return retval;
		}

        public classSprite loadSprite_V2(string strFilename)
        {
            udtSprite_StorageRetrieval udrThisSprite = new udtSprite_StorageRetrieval();

            int intcutchr = strFilename.IndexOf(".");
            if (intcutchr > 0)
                strFilename = strFilename.Substring(0, intcutchr);
            strFilename += ".sp2";

            FileStream fs = new FileStream(strFilename, FileMode.Open);

            BinaryFormatter formatter = new BinaryFormatter();
            udrThisSprite.myName = (string)formatter.Deserialize(fs);
            udrThisSprite.intImagesPerRotation = 16; // original default value
            int intNumLimbs = (int)formatter.Deserialize(fs);

            udrThisSprite.limbs = new udtLimb_StorageRetrieval[intNumLimbs];

            for (int intLimbCounter = 0; intLimbCounter < intNumLimbs; intLimbCounter++)
            {
                udrThisSprite.limbs[intLimbCounter].myName = (string)formatter.Deserialize(fs);

                udrThisSprite.limbs[intLimbCounter].IRotate = (bool)formatter.Deserialize(fs);
                udrThisSprite.limbs[intLimbCounter].AlwaysVertical = (bool)formatter.Deserialize(fs);

                int intNumBaseImages = (int)formatter.Deserialize(fs);
                udrThisSprite.limbs[intLimbCounter].bmp = new Bitmap[intNumBaseImages];
                for (int intBaseImageCounter = 0; intBaseImageCounter < intNumBaseImages; intBaseImageCounter++)
                    udrThisSprite.limbs[intLimbCounter].bmp[intBaseImageCounter] = (Bitmap)formatter.Deserialize(fs);

                udrThisSprite.limbs[intLimbCounter].radMaster = new classMath.classRadialCoor();
                udrThisSprite.limbs[intLimbCounter].radMaster.angle = (double)formatter.Deserialize(fs);
                udrThisSprite.limbs[intLimbCounter].radMaster.radius = (double)formatter.Deserialize(fs);

                udrThisSprite.limbs[intLimbCounter].radSlave = new classMath.classRadialCoor();
                udrThisSprite.limbs[intLimbCounter].radSlave.angle = (double)formatter.Deserialize(fs);
                udrThisSprite.limbs[intLimbCounter].radSlave.radius = (double)formatter.Deserialize(fs);

                udrThisSprite.limbs[intLimbCounter].MasterIndex = (int)formatter.Deserialize(fs);
                udrThisSprite.limbs[intLimbCounter].hingeName = (string)formatter.Deserialize(fs);
            }

            classSprite retval = getSprite(udrThisSprite.limbs[0].IRotate, udrThisSprite.limbs[0].AlwaysVertical, udrThisSprite.myName);

            for (int intLimbCounter = 1; intLimbCounter < udrThisSprite.limbs.Length; intLimbCounter++)
            {
                Sprite.classSpriteLimb masterLimb = retval.getLimbByName(udrThisSprite.limbs[udrThisSprite.limbs[intLimbCounter].MasterIndex].myName);
                if (masterLimb == null)
                    masterLimb = retval.MasterLimb;
                Sprite.classSpriteLimb newLimb = new Sprite.classSpriteLimb(ref retval,
                                            udrThisSprite.limbs[intLimbCounter].IRotate,
                                            udrThisSprite.limbs[intLimbCounter].AlwaysVertical,
                                            udrThisSprite.limbs[intLimbCounter].myName.Trim());
                masterLimb.addLimb(newLimb,
                                   udrThisSprite.limbs[intLimbCounter].hingeName.Trim(),
                                   new classMath.classRadialCoor(udrThisSprite.limbs[intLimbCounter].radMaster.angle, udrThisSprite.limbs[intLimbCounter].radMaster.radius),
                                   new classMath.classRadialCoor(udrThisSprite.limbs[intLimbCounter].radSlave.angle, udrThisSprite.limbs[intLimbCounter].radSlave.radius));
            }
            
            // load limb draw sequence here -> no longer used in new sprite format
            int intNumDrawPointers = (int)formatter.Deserialize(fs);
            string[] strNames = new string[intNumDrawPointers];
            for (int intDrawCounter = 0; intDrawCounter < intNumDrawPointers; intDrawCounter++)
            {
                string strNameLimb = ((string)formatter.Deserialize(fs)).Trim();
            }

            // load configurations
            int intNumConfigurations = (int)formatter.Deserialize(fs);

            Array.Resize<classConfiguration>(ref retval.Configurations, intNumConfigurations);
            for (int intConfigurationCounter = 0; intConfigurationCounter < retval.Configurations.Length; intConfigurationCounter++)
            {
                classConfiguration thisCon = new classConfiguration();
                thisCon.mainSprite = retval;
                thisCon.name = ((string)formatter.Deserialize(fs)).Trim();

                int intNumberLimbs = (int)formatter.Deserialize(fs);
                Array.Resize<classSpriteLimb>(ref thisCon.LimbDrawSequence, intNumberLimbs);
                for (int intLimbCounter = 0; intLimbCounter < thisCon.LimbDrawSequence.Length; intLimbCounter++)
                {
                    string strConLimbName = ((string)formatter.Deserialize(fs)).Trim();
                    thisCon.LimbDrawSequence[intLimbCounter] = retval.getLimbByName(strConLimbName);

                    if (thisCon.LimbDrawSequence[intLimbCounter] == null)
                        if (retval.MasterLimb.Name.ToUpper().Trim().CompareTo(strConLimbName.Trim().ToUpper()) == 0)
                            thisCon.LimbDrawSequence[intLimbCounter] = retval.MasterLimb;
                }

                int intNumSteps = (int)formatter.Deserialize(fs);
                Array.Resize<classConfigurationStep>(ref thisCon.steps, intNumSteps);
                for (int intStepCounter = 0; intStepCounter < thisCon.steps.Length; intStepCounter++)
                {
                    thisCon.steps[intStepCounter] = new classConfigurationStep();
                    Array.Resize<classConfigurationStepHinge>(ref thisCon.steps[intStepCounter].hingeDetails, intNumberLimbs);
                    for (int intHingeCounter = 0; intHingeCounter < thisCon.steps[intStepCounter].hingeDetails.Length; intHingeCounter++)
                    {
                        string strConStepHingeName = ((string)formatter.Deserialize(fs)).Trim();
                        thisCon.steps[intStepCounter].hingeDetails[intHingeCounter].hinge = retval.getHingeByName(strConStepHingeName);
                        thisCon.steps[intStepCounter].hingeDetails[intHingeCounter].angle = (double)formatter.Deserialize(fs);
                    }
                }

                retval.Configurations[intConfigurationCounter] = thisCon;
            }
            fs.Close();

            return retval;
        }


        public classSprite loadSprite_V1(string strFilename)
        {
            udtSprite_StorageRetrieval udrThisSprite = new udtSprite_StorageRetrieval();

            int intcutchr = strFilename.IndexOf(".");
            if (intcutchr > 0)
                strFilename = strFilename.Substring(0, intcutchr);
            strFilename += ".spr";

            FileStream fs = new FileStream(strFilename, FileMode.Open);

            BinaryFormatter formatter = new BinaryFormatter();
            udrThisSprite.myName = (string)formatter.Deserialize(fs);

            udrThisSprite.intImagesPerRotation = 16; // original default value
            int intNumLimbs = (int)formatter.Deserialize(fs);

            udrThisSprite.limbs = new udtLimb_StorageRetrieval[intNumLimbs];

            for (int intLimbCounter = 0; intLimbCounter < intNumLimbs; intLimbCounter++)
            {
                udrThisSprite.limbs[intLimbCounter].myName = (string)formatter.Deserialize(fs);

                udrThisSprite.limbs[intLimbCounter].IRotate = (bool)formatter.Deserialize(fs);
                udrThisSprite.limbs[intLimbCounter].AlwaysVertical = (bool)formatter.Deserialize(fs);

                udrThisSprite.limbs[intLimbCounter].bmp = new Bitmap[1];
                udrThisSprite.limbs[intLimbCounter].bmp[0] = (Bitmap)formatter.Deserialize(fs);

                udrThisSprite.limbs[intLimbCounter].radMaster = new classMath.classRadialCoor();
                udrThisSprite.limbs[intLimbCounter].radMaster.angle = (double)formatter.Deserialize(fs);
                udrThisSprite.limbs[intLimbCounter].radMaster.radius = (double)formatter.Deserialize(fs);

                udrThisSprite.limbs[intLimbCounter].radSlave = new classMath.classRadialCoor();
                udrThisSprite.limbs[intLimbCounter].radSlave.angle = (double)formatter.Deserialize(fs);
                udrThisSprite.limbs[intLimbCounter].radSlave.radius = (double)formatter.Deserialize(fs);

                udrThisSprite.limbs[intLimbCounter].MasterIndex = (int)formatter.Deserialize(fs);
                udrThisSprite.limbs[intLimbCounter].hingeName = (string)formatter.Deserialize(fs);
            }

            classSprite retval = getSprite(udrThisSprite.limbs[0].IRotate, udrThisSprite.limbs[0].AlwaysVertical, udrThisSprite.myName);

            for (int intLimbCounter = 1; intLimbCounter < udrThisSprite.limbs.Length; intLimbCounter++)
            {
                Sprite.classSpriteLimb masterLimb = retval.getLimbByName(udrThisSprite.limbs[udrThisSprite.limbs[intLimbCounter].MasterIndex].myName);
                if (masterLimb == null)
                    masterLimb = retval.MasterLimb;
                Sprite.classSpriteLimb newLimb = new Sprite.classSpriteLimb(ref retval,
                                            udrThisSprite.limbs[intLimbCounter].IRotate,
                                            udrThisSprite.limbs[intLimbCounter].AlwaysVertical,
                                            udrThisSprite.limbs[intLimbCounter].myName.Trim());
                masterLimb.addLimb(newLimb,
                                   udrThisSprite.limbs[intLimbCounter].hingeName.Trim(),
                                   new classMath.classRadialCoor(udrThisSprite.limbs[intLimbCounter].radMaster.angle, udrThisSprite.limbs[intLimbCounter].radMaster.radius),
                                   new classMath.classRadialCoor(udrThisSprite.limbs[intLimbCounter].radSlave.angle, udrThisSprite.limbs[intLimbCounter].radSlave.radius));
            }

            // load limb draw sequence here -> no longer used in new sprite format
            int intNumDrawPointers = (int)formatter.Deserialize(fs);
            string[] strNames = new string[intNumDrawPointers];
            for (int intDrawCounter = 0; intDrawCounter < intNumDrawPointers; intDrawCounter++)
            {
                string strNameLimb = ((string)formatter.Deserialize(fs)).Trim();
            }

            // load configurations
            int intNumConfigurations = (int)formatter.Deserialize(fs);

            Array.Resize<classConfiguration>(ref retval.Configurations, intNumConfigurations);
            for (int intConfigurationCounter = 0; intConfigurationCounter < retval.Configurations.Length; intConfigurationCounter++)
            {
                classConfiguration thisCon = new classConfiguration();
                thisCon.mainSprite = retval;
                thisCon.name = ((string)formatter.Deserialize(fs)).Trim();

                int intNumberLimbs = (int)formatter.Deserialize(fs);
                Array.Resize<classSpriteLimb>(ref thisCon.LimbDrawSequence, intNumberLimbs);
                for (int intLimbCounter = 0; intLimbCounter < thisCon.LimbDrawSequence.Length; intLimbCounter++)
                {
                    string strConLimbName = ((string)formatter.Deserialize(fs)).Trim();
                    thisCon.LimbDrawSequence[intLimbCounter] = retval.getLimbByName(strConLimbName);

                    if (thisCon.LimbDrawSequence[intLimbCounter] == null)
                        if (retval.MasterLimb.Name.ToUpper().Trim().CompareTo(strConLimbName.Trim().ToUpper()) == 0)
                            thisCon.LimbDrawSequence[intLimbCounter] = retval.MasterLimb;
                }

                int intNumSteps = (int)formatter.Deserialize(fs);
                Array.Resize<classConfigurationStep>(ref thisCon.steps, intNumSteps);
                for (int intStepCounter = 0; intStepCounter < thisCon.steps.Length; intStepCounter++)
                {
                    thisCon.steps[intStepCounter] = new classConfigurationStep();
                    Array.Resize<classConfigurationStepHinge>(ref thisCon.steps[intStepCounter].hingeDetails, intNumberLimbs);
                    for (int intHingeCounter = 0; intHingeCounter < thisCon.steps[intStepCounter].hingeDetails.Length; intHingeCounter++)
                    {
                        string strConStepHingeName = ((string)formatter.Deserialize(fs)).Trim();
                        thisCon.steps[intStepCounter].hingeDetails[intHingeCounter].hinge = retval.getHingeByName(strConStepHingeName);
                        thisCon.steps[intStepCounter].hingeDetails[intHingeCounter].angle = (double)formatter.Deserialize(fs);
                    }
                }
                retval.Configurations[intConfigurationCounter] = thisCon;
            }
            fs.Close();
            return retval;
        }

        /// <summary>
        /// turns any string into a string of exact size (conMaxNameStringSize) for storage
        /// </summary>
        /// <param name="strName">input string of any length</param>
        /// <returns>output copy of input parameter set to specific storage string length</returns>
        string getNameFromString(string strName) { return (strName.Trim().PadRight(conMaxNameStringSize)).Substring(0, conMaxNameStringSize); }

        public void saveSprite(ref Sprite.classSprite thisSprite, string strFilename)
        {
            udrStorageSprite = new udtSprite_StorageRetrieval();
            udrStorageSprite.myName = getNameFromString(thisSprite.myName);
            udrStorageSprite.intImagesPerRotation = (int)thisSprite.ImagesPerRotation;
            udrStorageSprite.limbs = new udtLimb_StorageRetrieval[0];
            createStorageSpriteLimbs(ref thisSprite.MasterLimb);
            strFilename = strFilename.ToUpper().Replace(".SPR", ".SP3").Replace(".sp2", ".sp3");
            if (thisSprite.cFileStream == null)
                thisSprite.cFileStream = getFileStream(strFilename);
            thisSprite.cFileStream.fs.Position = 0;
            formatter.Serialize(thisSprite.cFileStream.fs, udrStorageSprite.myName);
            formatter.Serialize(thisSprite.cFileStream.fs, udrStorageSprite.intImagesPerRotation);
            formatter.Serialize(thisSprite.cFileStream.fs, udrStorageSprite.limbs.Length);

            for (int intLimbCounter = 0; intLimbCounter < udrStorageSprite.limbs.Length; intLimbCounter++)
            {
                formatter.Serialize(thisSprite.cFileStream.fs, udrStorageSprite.limbs[intLimbCounter].myName);
                formatter.Serialize(thisSprite.cFileStream.fs, udrStorageSprite.limbs[intLimbCounter].IRotate);
                formatter.Serialize(thisSprite.cFileStream.fs, udrStorageSprite.limbs[intLimbCounter].AlwaysVertical);

                formatter.Serialize(thisSprite.cFileStream.fs, udrStorageSprite.limbs[intLimbCounter].radMaster.angle);
                formatter.Serialize(thisSprite.cFileStream.fs, udrStorageSprite.limbs[intLimbCounter].radMaster.radius);

                formatter.Serialize(thisSprite.cFileStream.fs, udrStorageSprite.limbs[intLimbCounter].radSlave.angle);
                formatter.Serialize(thisSprite.cFileStream.fs, udrStorageSprite.limbs[intLimbCounter].radSlave.radius);

                formatter.Serialize(thisSprite.cFileStream.fs, udrStorageSprite.limbs[intLimbCounter].MasterIndex);
                formatter.Serialize(thisSprite.cFileStream.fs, udrStorageSprite.limbs[intLimbCounter].hingeName);
            }

            // save configuration info
            if (thisSprite.Configurations == null)
                formatter.Serialize(thisSprite.cFileStream.fs, 0);
            else
            {
                formatter.Serialize(thisSprite.cFileStream.fs, thisSprite.Configurations.Length);
                for (int intConfigurationCounter = 0; intConfigurationCounter < thisSprite.Configurations.Length; intConfigurationCounter++)
                {
                    classConfiguration thisCon = thisSprite.Configurations[intConfigurationCounter];
                    formatter.Serialize(thisSprite.cFileStream.fs, getNameFromString(thisCon.name));

                    formatter.Serialize(thisSprite.cFileStream.fs, thisCon.LimbDrawSequence.Length);
                    for (int intHingeCounter = 0; intHingeCounter < thisCon.LimbDrawSequence.Length; intHingeCounter++)
                        formatter.Serialize(thisSprite.cFileStream.fs, getNameFromString(thisCon.LimbDrawSequence[intHingeCounter].Name));

                    formatter.Serialize(thisSprite.cFileStream.fs, thisCon.steps.Length);

                    for (int intStepCounter = 0; intStepCounter < thisCon.steps.Length; intStepCounter++)
                    {
                        for (int intHingeCounter = 0; intHingeCounter < thisCon.steps[intStepCounter].hingeDetails.Length; intHingeCounter++)
                        {
                            if (thisCon.steps[intStepCounter].hingeDetails[intHingeCounter].hinge == null)
                            {
                                formatter.Serialize(thisSprite.cFileStream.fs, (getNameFromString(conStrNull)));
                                formatter.Serialize(thisSprite.cFileStream.fs, (double)0.0);
                            }
                            else
                            {
                                formatter.Serialize(thisSprite.cFileStream.fs, getNameFromString(thisCon.steps[intStepCounter].hingeDetails[intHingeCounter].hinge.name));
                                formatter.Serialize(thisSprite.cFileStream.fs, thisCon.steps[intStepCounter].hingeDetails[intHingeCounter].angle);
                            }
                        }
                    }
                    thisSprite.Configurations[intConfigurationCounter] = thisCon;
                }
            }

            thisSprite.cFileStream.lngStartIndex = thisSprite.cFileStream.fs.Position;
            formatter.Serialize(thisSprite.cFileStream.fs, (long)0);   /// measure the length of a single long integer on the filestream            
            thisSprite.cFileStream.lngSizeLongIndexInFileStream = thisSprite.cFileStream.fs.Position - thisSprite.cFileStream.lngStartIndex;

            /// make room for all image indices
            for (int intLimbCounter = 0; intLimbCounter < thisSprite.limbs.Length; intLimbCounter++)
            {
                for (int intImageCounter = 0; intImageCounter < (int)(thisSprite.ImagesPerRotation / 4); intImageCounter++)
                    formatter.Serialize(thisSprite.cFileStream.fs, (long)(-1));
            }

            long lngIndexNextImage = thisSprite.cFileStream.fs.Position;
            /// save images and record their indices
            for (int intLimbCounter = 0; intLimbCounter < udrStorageSprite.limbs.Length; intLimbCounter++)
            {
                // test if all images exist
                bool bolImagesExist = true;
                for (int intImageCounter = 0; intImageCounter < udrStorageSprite.limbs[intLimbCounter].bmp.Length && intImageCounter < thisSprite.ImagesPerRotation / 4; intImageCounter++)
                {
                    if (udrStorageSprite.limbs[intLimbCounter].bmp[intImageCounter] == null)
                    {
                        bolImagesExist = false;
                        break;
                    }
                }
                if (!bolImagesExist)
                {
                    thisSprite.limbs[intLimbCounter].initImages(thisSprite.limbs[intLimbCounter].bmpSpriteLimb[0]);
                    udrStorageSprite.limbs[intLimbCounter].bmp = thisSprite.limbs[intLimbCounter].bmpSpriteLimb;
                }
                
                for (int intImageCounter = 0; intImageCounter < udrStorageSprite.limbs[intLimbCounter].bmp.Length && intImageCounter < thisSprite.ImagesPerRotation / 4; intImageCounter++)
                {
                    long lngIndexThisImage = lngIndexNextImage;
                    
                    thisSprite.cFileStream.fs.Position = lngIndexThisImage;
                    formatter.Serialize(thisSprite.cFileStream.fs, udrStorageSprite.limbs[intLimbCounter].bmp[intImageCounter]);
                    
                    lngIndexNextImage = thisSprite.cFileStream.fs.Position;
                    thisSprite.cFileStream.fs.Position = thisSprite.getLimbImageIndex(intLimbCounter, intImageCounter);
                    formatter.Serialize(thisSprite.cFileStream.fs, lngIndexThisImage);
                }
            }
        }

        void createStorageSpriteLimbs(ref Sprite.classSpriteLimb thisLimb)
        {
            udtLimb_StorageRetrieval udrTemp = new udtLimb_StorageRetrieval();
            udrTemp.radMaster = new classMath.classRadialCoor();
            udrTemp.radSlave = new classMath.classRadialCoor();

            if (thisLimb.MasterHinge != null)
            {
                Array.Resize<udtLimb_StorageRetrieval>(ref udrStorageSprite.limbs, udrStorageSprite.limbs.Length + 1);

                udrTemp.myName = getNameFromString(thisLimb.Name);

                udrTemp.AlwaysVertical = thisLimb.AlwaysVertical;
                udrTemp.IRotate = thisLimb.IRotate;

                udrTemp.bmp = new Bitmap[thisLimb.bmpSpriteLimb.Length];
                for (int intBaseImageCounter = 0; intBaseImageCounter < (int)(thisLimb.bmpSpriteLimb.Length / 4); intBaseImageCounter++)
                    udrTemp.bmp[intBaseImageCounter] = thisLimb.bmpSpriteLimb[intBaseImageCounter];

                udrTemp.radMaster.angle = thisLimb.MasterHinge.RadCoor_Master.angle;
                udrTemp.radMaster.radius = thisLimb.MasterHinge.RadCoor_Master.radius;

                udrTemp.radSlave.angle = thisLimb.MasterHinge.RadCoor_Slave.angle;
                udrTemp.radSlave.radius = thisLimb.MasterHinge.RadCoor_Slave.radius;

                udrTemp.MasterIndex = thisLimb.MasterHinge.Limb_Master.intMyLimbID;

                udrTemp.hingeName = getNameFromString(thisLimb.MasterHinge.name);
                
                udrStorageSprite.limbs[udrStorageSprite.limbs.Length - 1] = udrTemp;
            }

            if (thisLimb.mySlaveLimbs != null)
                for (int intSlaveCounter = 0; intSlaveCounter < thisLimb.mySlaveLimbs.Length; intSlaveCounter++)
                    createStorageSpriteLimbs(ref thisLimb.mySlaveLimbs[intSlaveCounter]);
        }

        #endregion
    }

    public class classBmpAndPtTLInfo
    {
        public Bitmap bmp;
        public Point ptTL;
        public classConfigStepLimbPosition[] limbPositions;
		public classConfigStepHingePosition[] hingePositions;
		public classCacheTreeNodeKeys cKey;

		public classBmpAndPtTLInfo Copy()
		{
			classBmpAndPtTLInfo cRetVal = new classBmpAndPtTLInfo();
			cRetVal.bmp = new Bitmap(bmp);
			cRetVal.ptTL = ptTL;
			cRetVal.limbPositions = new classConfigStepLimbPosition[limbPositions.Length];
			for (int intLimbcounter = 0; intLimbcounter < limbPositions.Length; intLimbcounter++)
				cRetVal.limbPositions[intLimbcounter] = limbPositions[intLimbcounter].Copy();

			cRetVal.hingePositions = new classConfigStepHingePosition[hingePositions.Length];
			for (int intHingeCounter = 0; intHingeCounter < hingePositions.Length; intHingeCounter++)
				cRetVal.hingePositions[intHingeCounter] = hingePositions[intHingeCounter].Copy();
			return cRetVal;
		}
    }

    public class classConfigStepLimbPosition
    {
		public classMath.classRadialCoor cRad;
        public Point pt;
        public double dblAngle;
		public classConfigStepLimbPosition Copy()
		{
			classConfigStepLimbPosition cRetVal = new classConfigStepLimbPosition();
			cRetVal.cRad = cRad.Copy();
			cRetVal.pt = pt;
			cRetVal.dblAngle = dblAngle;
			return cRetVal;
		}
    }

	public class classConfigStepHingePosition
	{
		public classMath.classRadialCoor cRad;
		public Point pt;

		public classConfigStepHingePosition Copy()
		{
			classConfigStepHingePosition cRetVal = new classConfigStepHingePosition();
			cRetVal.cRad = cRad.Copy();
			cRetVal.pt = pt;
			return cRetVal;
		}
	}

    public class classConfigurationStep
	{
		public classConfigurationStepHinge[] hingeDetails;
    }
    
    public class classCacheTreeNodeKeys
    {
		public enum enuKeyIndex {filename, ConfigIndex, StepNum, Angle, Size, Mirror };
		public double[] dblKeys = new double[5];
		public string strFilename;

		public classCacheTreeNodeKeys(string StrFilename,
									     int intConfigIndex,
									     int intStepNum,
									  double dblAngle,
									  double dblSize,
								   enuMirror eMirror)
        {
			int intIndexLastBackSlash = StrFilename.LastIndexOf("\\");
			if (intIndexLastBackSlash > 0)
				strFilename = StrFilename.Substring(intIndexLastBackSlash + 1);
			else
				strFilename = StrFilename;
			if (strFilename.Length > 4 && strFilename.Contains("."))
				strFilename = strFilename.Substring(0, strFilename.Length - 4);
			dblKeys[0] = (double)intConfigIndex;
			dblKeys[1] = (double)intStepNum;
			dblKeys[2] = Math.Round( dblAngle, 4);
			dblKeys[3] =Math.Round( dblSize,4);
			dblKeys[4] = (double)(int)eMirror;
        }

		public string Filename { get { return strFilename; } }
		public int ConfigIndex { get { return (int)dblKeys[0]; } }
		public int Step { get { return (int)dblKeys[1]; } }
		public double  Angle{ get { return (double)dblKeys[2]; } }
		public double Size { get { return (double)dblKeys[3]; } }
		public enuMirror Mirror { get { return (enuMirror)dblKeys[4]; } }
    }

	public class classCacheTernaryTree_CharKeys_Node
	{
		static int intIDCounter = 0;
		public int intID;

		public char chrKey = (char)0;
		public classCacheTernaryTree_CharKeys_Node Up = null;
		public classCacheTernaryTree_CharKeys_Node Down = null;
		public classCacheTernaryTree_CharKeys_Node Next = null;
		public classCacheTernaryTree_DoubleKeys_Node cTerDoubleNode;

		public classCacheTernaryTree_CharKeys_Node(char Key)
		{
			chrKey = Key;
			intID = intIDCounter++;
		}
	}

    public class classCacheTernaryTree_DoubleKeys_Node
    {
		static int intIDCounter = 0;
		public int intID;

		public double dblKey = 0;
        public classCacheTernaryTree_DoubleKeys_Node Up = null;
        public classCacheTernaryTree_DoubleKeys_Node Down = null;
        public classCacheTernaryTree_DoubleKeys_Node Next = null;
        public long lngFSPosition = -1;
        public classBmpAndPtTLInfo cBmpAndPtTLInfo = null;// optional RAM storage

        public classCacheTernaryTree_DoubleKeys_Node(double Key)
        {
			dblKey = Key;
			intID = intIDCounter++;
        }

		static public double convertCacheKey(object objKey, int intKeyIndex)
		{
			switch (intKeyIndex)
			{
				case 0: // uint FS_ID
					return (double)(GraphicsUnit)objKey;

				case 1: // int config index
				case 2: // int step
				case 3: // int dir
					return (double)(int)objKey;

				case 4: // double size
					return (double)objKey;

				case 5: // enuMirror mirror
					return (double)(int)(enuMirror)objKey;

				default:
					return -1.0;
			}
		}
    }

    
    public class classConfigurationStepHinge
    {
        public classSpriteHinge hinge;
        public double angle;
        classMath.classRadialCoor cRadCoor_Master;
        /// <summary>
        /// radial coordinate location relative to center of image
        /// </summary>
        public classMath.classRadialCoor RadCoor_Master
        {
            get { return cRadCoor_Master; }
            set { cRadCoor_Master = value; }
        }

        classMath.classRadialCoor cRadCoor_Slave;
        /// <summary>
        /// radial coordinate location relative to center of image
        /// </summary>
        public classMath.classRadialCoor RadCoor_Slave
        {
            get { return cRadCoor_Slave; }
            set { cRadCoor_Slave = value; }
        }
    }

    public class classConfiguration
    {
        public string name;
        public classSprite mainSprite;
        public classSpriteLimb[] LimbDrawSequence;
        public classConfigurationStep[] steps;

        /// <summary>
        /// returns the index of a limb in this configuration's DrawSequence identified by its name string
        /// </summary>
        /// <param name="strLimbName">name of limb to find</param>
        /// <returns>integer index of limb in drawSequence array, -1 if not found</returns>
        public int getLimbDrawSequenceIndex(string strLimbName)
        {
            strLimbName = strLimbName.ToUpper().Trim();
            for (int intLimbCounter = 0; intLimbCounter < LimbDrawSequence.Length; intLimbCounter++)
                if (strLimbName.CompareTo(LimbDrawSequence[intLimbCounter].Name.Trim().ToUpper()) == 0)
                    return intLimbCounter;

            return -1;
        }
    }

    public class formSprite : PerPixelForm.PerPixelAlphaForm
    {
        public classSprite cSprite;
        public formSprite()
        {
            FormBorderStyle = FormBorderStyle.None;
            ShowInTaskbar = false;
            Visible = false;
            TopMost = true;
        }

        public void drawMe(int ConfigurationIndex, int ConfigurationStep, Point ptLoc, enuMirror Mirror, double angle, double size)
        {
            cSprite.DisplaySize = size;

            _intConfiguration = ConfigurationIndex;
            _intStep = ConfigurationStep;
            _dblSize = size;
            _eMirror = Mirror;
            _dblAngle = angle;

            drawMe();

            Pos = ptLoc;
            Visible = true;
            BringToFront();

        }

        void drawMe()
        {
			classBmpAndPtTLInfo cBmpImage = cSprite.getImageOnBitmap(intConfiguration, intStep, dblAngle, dblSize, eMirror, false);
            SetBitmap(cBmpImage.bmp);
        }

        bool _bolForceDraw = false;
        public bool ForceDraw
        {
            get { return _bolForceDraw; }
            set
            {
                _bolForceDraw = value;
                drawMe();
            }
        }


        int _intConfiguration;
        public int intConfiguration
        {
            get { return _intConfiguration; }
            set
            {
                _intConfiguration = value;
                drawMe();
            }
        }
        int _intStep;
        public int intStep
        {
            get { return _intStep; }
            set
            {
                _intStep = value;
                drawMe();
            }
        }

        double _dblSize;
        public double dblSize
        {
            get { return _dblSize; }
            set
            {
                _dblSize = value;
                drawMe();
            }
        }

        enuMirror _eMirror;
        public enuMirror eMirror
        {
            get { return _eMirror; }
            set
            {
                _eMirror = value;
                drawMe();
            }
        }

        int _intDir;
        public int intDir
        {
            get { return _intDir; }
            set
            {
                _intDir = value;
                drawMe();
            }
        }

        Point _pos;
        public Point Pos
        {
            get { return _pos; }
            set
            {
                _pos = value;
                PlaceMe();
            }
        }

        void PlaceMe()
        {
			classBmpAndPtTLInfo cBmpPtTLInfo = classSpriteFileStream.searchCache(new classCacheTreeNodeKeys(cSprite.cFileStream.strFilename, intConfiguration, intStep, intDir, cSprite.DisplaySize, eMirror));
			int intGrabLimbIndex = 0;
            if (cSprite.limStationary != null)
            {
                for (int intLimbCounter = 0; intLimbCounter < cBmpPtTLInfo.limbPositions.Length; intLimbCounter++)
                {
					if (cSprite.limStationary == cSprite.limbs[intLimbCounter])
                    {
                        intGrabLimbIndex = intLimbCounter;
                        break;
                    }
                }
                Left = _pos.X + cBmpPtTLInfo.ptTL.X - cBmpPtTLInfo.limbPositions[intGrabLimbIndex].pt.X;
                Top = _pos.Y + cBmpPtTLInfo.ptTL.Y - cBmpPtTLInfo.limbPositions[intGrabLimbIndex].pt.Y;
                return;
            }

            Left = _pos.X + cBmpPtTLInfo.ptTL.X;
            Top = _pos.Y + cBmpPtTLInfo.ptTL.Y;
        }

        double _dblAngle;
        public double dblAngle
        {
            get { return _dblAngle; }
            set
            {
                _dblAngle = value;
                drawMe();
            }
        }

        public void loadSprite(string strFilename)
        {
			cSprite =classSprite.spriteMaker.loadSprite(strFilename);
        }
    }

    public class classSpriteFileStream
    {
		/// <summary>
		/// every base value (angle =0, size = 1.0) of every step of all configurations are stored in the cache-file and retrieved in subsequent sessions whether the bolIAmCached variable is set to true or not.  Setting this boolean to true forces all variations of Size & Angle to be cached as well resulting in a longer RAM-cache load time at start-up.
		/// </summary>
		public bool bolIAmCached = true;
        public FileStream fs;
        public long lngStartIndex;
        public long lngSizeLongIndexInFileStream;
        public classSpriteFileStream left;
        public classSpriteFileStream right;
		public string strFilename;

		/// <summary>
		/// when bolDebug is true, the cache file is cleared and the RAM-cache is set to null at startup.
		/// </summary>
		public static bool bolDebug = false;

		public static FileStream fsCache_BmpInfo;
		public static FileStream fsCache_BmpInfo_Rebuild;
		public static BinaryFormatter formatter = new BinaryFormatter();
		static Semaphore semaCacheTree = new Semaphore(1, 1);
		static Semaphore semaCacheFS = new Semaphore(1, 1);
		public static string strWorkingDirectory = System.IO.Directory.GetCurrentDirectory()
																		.ToUpper(new System.Globalization.CultureInfo("en-US"))
																		.Replace("BIN\\RELEASE", "")
																		.Replace("BIN\\DEBUG", "");
		public static string strCache_BmpAndTLInfo_Filename = strWorkingDirectory + "Cache_BmpAndPtTLInfo.fs";
		public static string strCache_BmpInfo_RAMCacheRebuild = strWorkingDirectory + "Cache_BmpInfoRAMRebuild.fs";
		static public classCacheTernaryTree_CharKeys_Node cache;
		
		static uint uintIDCounter;
		uint uintFS_ID;
		public uint FS_ID
		{
			get { return uintFS_ID; }
		}

		static bool bolCacheFileInitialized = false;

		public void clearCache()
		{
			if (fsCache_BmpInfo != null)
				fsCache_BmpInfo.Close();
			if (System.IO.File.Exists(strCache_BmpAndTLInfo_Filename))
				System.IO.File.Delete(strCache_BmpAndTLInfo_Filename);


			if (fsCache_BmpInfo_Rebuild != null)
				fsCache_BmpInfo_Rebuild.Close();
			if (System.IO.File.Exists(strCache_BmpInfo_RAMCacheRebuild))
				System.IO.File.Delete(strCache_BmpInfo_RAMCacheRebuild);

            fsCache_BmpInfo = new FileStream(strCache_BmpAndTLInfo_Filename, FileMode.Create);
            fsCache_BmpInfo_Rebuild = new FileStream(strCache_BmpInfo_RAMCacheRebuild, FileMode.Create);
		}

		/// <summary>
		/// opens cache files and initializes RAM cache trees given existing file
		/// </summary>
		public class FormOpenCache : Form
		{
			ProgressBar proBar = new ProgressBar();
			Label lbl = new Label();
			System.Windows.Forms.Timer tmr = new System.Windows.Forms.Timer();
			string strTitleBase = "";
			int intNumRecordsLoaded = 0;

			public FormOpenCache()
			{
				Controls.Add(proBar);
				Controls.Add(lbl);
				strTitleBase  = "loading RAM Cache : ";
				lbl.AutoSize = true;
				FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
				BackColor = Color.WhiteSmoke;
				ShowInTaskbar = false;
				VisibleChanged += new EventHandler(openCache_VisibleChanged);

				tmr.Interval = 5;				
				tmr.Tick += new EventHandler(tmr_Tick);
			}

			void openCache_VisibleChanged(object sender, EventArgs e)
			{
				if (Visible)
				{
					Left = (int)(Screen.PrimaryScreen.WorkingArea.Width * .1);
					proBar.Width
						= Width
						= (Screen.PrimaryScreen.WorkingArea.Width - 2 * Left);
					proBar.Left = 0;
					lbl.Left = 0;
					lbl.Top = 0;
					proBar.Top = lbl.Top + lbl.Height;
					Height = proBar.Top + proBar.Height;
					Top = (Screen.PrimaryScreen.WorkingArea.Height - Height) / 2;
					proBar.Visible = true;
					BringToFront();

					if (!System.IO.File.Exists(strCache_BmpAndTLInfo_Filename) || !System.IO.File.Exists(strCache_BmpInfo_RAMCacheRebuild))
					{
						fsCache_BmpInfo = new FileStream(strCache_BmpAndTLInfo_Filename, FileMode.Create);
						fsCache_BmpInfo_Rebuild = new FileStream(strCache_BmpInfo_RAMCacheRebuild, FileMode.Create);
					}
					else
					{
						/// there is an existing cache file -> open its File-stream and rebuild RAM cache trees with it
						fsCache_BmpInfo = new FileStream(strCache_BmpAndTLInfo_Filename, FileMode.Open);
						fsCache_BmpInfo_Rebuild = new FileStream(strCache_BmpInfo_RAMCacheRebuild, FileMode.Open);
					}
					fsCache_BmpInfo.Position = 0;
					proBar.Value = 0;
					proBar.Maximum = (int)(fsCache_BmpInfo_Rebuild.Length % int.MaxValue);

					tmr.Enabled = true;
				}
			}
			void tmr_Tick(object sender, EventArgs e)
			{
				tmr.Enabled = false;

				if (fsCache_BmpInfo_Rebuild.Position >= fsCache_BmpInfo_Rebuild.Length)
				{
					Dispose();
					return;
				}
			
				// read the next record sequentially
				
				// read key
				lbl.Text = strTitleBase + intNumRecordsLoaded.ToString();
				intNumRecordsLoaded++;
				proBar.Value = (int)(fsCache_BmpInfo_Rebuild.Position % int.MaxValue);
				long lngPosition =(long)formatter.Deserialize( fsCache_BmpInfo_Rebuild); // remember the position of the start of this BmpInfo record
				classCacheTreeNodeKeys cKey = new classCacheTreeNodeKeys(
																(string)formatter.Deserialize(fsCache_BmpInfo_Rebuild),
																(int)(double)formatter.Deserialize(fsCache_BmpInfo_Rebuild),
																(int)(double)formatter.Deserialize(fsCache_BmpInfo_Rebuild),
																(double)formatter.Deserialize(fsCache_BmpInfo_Rebuild),
																(double)formatter.Deserialize(fsCache_BmpInfo_Rebuild),
																(enuMirror)(int)(double)formatter.Deserialize(fsCache_BmpInfo_Rebuild)
															);

				
				/// cache the location of this bmpInfo into the RAM tree
				classCacheTernaryTree_DoubleKeys_Node cTerDoubleNode = null;

				if (cache == null)
				{ // first element into tree
					cache = initTernary_Char_Tree(cKey.Filename);
				}

				// traverse char ternary tree to find filename
				classCacheTernaryTree_CharKeys_Node cTerCharNode = cache;
				bool bolLoop = true;
				int intCharIndex = 0;
				while (bolLoop)
				{
					if (cKey.strFilename[intCharIndex] > cTerCharNode.chrKey )
					{
						if (cTerCharNode.Down == null)
							cTerCharNode.Down = new classCacheTernaryTree_CharKeys_Node(cKey.strFilename[intCharIndex]);
						cTerCharNode = cTerCharNode.Down;
					}//0.ID, 1.Config, 2.Step, 3.Dir, 4.Size, 5.Mirror
					else if (cKey.strFilename[intCharIndex] < cTerCharNode.chrKey)
					{
						if (cTerCharNode.Up == null)
							cTerCharNode.Up = new classCacheTernaryTree_CharKeys_Node(cKey.strFilename[intCharIndex]);
						cTerCharNode = cTerCharNode.Up;
					}//0.ID, 1.Config, 2.Step, 3.Dir, 4.Size, 5.Mirror
					else
					{
						if (intCharIndex == cKey.strFilename.Length - 1)
						{ // match found
							if (cTerCharNode.cTerDoubleNode == null)
							{
								cTerCharNode.cTerDoubleNode
									= cTerDoubleNode
									= initTernary_Double_Tree(cKey);
							}
							else
								cTerDoubleNode = cTerCharNode.cTerDoubleNode;
							bolLoop = false;
						}//0.ID, 1.Config, 2.Step, 3.Dir, 4.Size, 5.Mirror
						else
						{
							intCharIndex++;
							if (cTerCharNode.Next == null)
								cTerCharNode.Next = new classCacheTernaryTree_CharKeys_Node(cKey.strFilename[intCharIndex]);
							cTerCharNode = cTerCharNode.Next;
						}//0.ID, 1.Config, 2.Step, 3.Dir, 4.Size, 5.Mirror
					}
				}

				int intKeyIndex = 0;
				bolLoop = true;
				while (bolLoop)
				{
					if (cKey.dblKeys[intKeyIndex] > cTerDoubleNode.dblKey)
					{
						if (cTerDoubleNode.Down == null)
							cTerDoubleNode.Down = new classCacheTernaryTree_DoubleKeys_Node(cKey.dblKeys[intKeyIndex]);
						cTerDoubleNode = cTerDoubleNode.Down;
					}//0.ID, 1.Config, 2.Step, 3.Dir, 4.Size, 5.Mirror
					else if (cKey.dblKeys[intKeyIndex] < cTerDoubleNode.dblKey)
					{
						if (cTerDoubleNode.Up == null)
							cTerDoubleNode.Up = new classCacheTernaryTree_DoubleKeys_Node(cKey.dblKeys[intKeyIndex]);
						cTerDoubleNode = cTerDoubleNode.Up;
					}//0.ID, 1.Config, 2.Step, 3.Dir, 4.Size, 5.Mirror
					else
					{
						if (intKeyIndex == cKey.dblKeys.Length - 1)
						{ // match found
							if (cTerDoubleNode.lngFSPosition > 0)
								MessageBox.Show("error : rebuild RAM Cache repeating itself");
							cTerDoubleNode.lngFSPosition = lngPosition;
							bolLoop = false;
						}//0.ID, 1.Config, 2.Step, 3.Dir, 4.Size, 5.Mirror
						else
						{
							intKeyIndex++;
							if (cTerDoubleNode.Next == null)
								cTerDoubleNode.Next = new classCacheTernaryTree_DoubleKeys_Node(cKey.dblKeys[intKeyIndex]);
							cTerDoubleNode = cTerDoubleNode.Next;
						}//0.ID, 1.Config, 2.Step, 3.Dir, 4.Size, 5.Mirror
					}
				}
				//semaCacheTree.Release();
				//semaCacheFS.Release();

				if (fsCache_BmpInfo_Rebuild.Position < fsCache_BmpInfo_Rebuild.Length)
					tmr.Enabled = true;
				else
					Dispose();
			}
		}

		public classSpriteFileStream(string filename)
        {
			if (!bolCacheFileInitialized)
			{
                //if (bolDebug)
                //    clearCache();
				FormOpenCache frmOpenCache = new FormOpenCache();
				frmOpenCache.ShowDialog();
                //clearCache();
				bolCacheFileInitialized = true;
			}

            strFilename = filename.ToUpper();
            if (System.IO.File.Exists(strFilename))
                fs = new FileStream(strFilename, FileMode.Open);
            else
                fs = new FileStream(strFilename, FileMode.Create);
			uintFS_ID = uintIDCounter++;
        }

		
		static bool CacheInRAM
		{
			get { return classSprite.CacheInRAM; }
		}
		
		/// <summary>
		/// retrieves bmp-info for sprite from the cache-file stream using the information returned by the appendBmpPtTLInfoToFS()
		/// </summary>
		/// <param name="lngPositionOfInfoOnIndexCacheFS">position on index-cache-fs used to retrieve bmp-info</param>
		/// <returns>requested bmp-info</returns>
		static public classBmpAndPtTLInfo getBmpPtTLInfoFromFS(long lngFSPosition)
		{
			semaCacheFS.WaitOne();

			fsCache_BmpInfo.Position = lngFSPosition;
			classBmpAndPtTLInfo cBmpAndPtTLInfo = new classBmpAndPtTLInfo();

			cBmpAndPtTLInfo.bmp = (Bitmap)formatter.Deserialize(fsCache_BmpInfo);
			cBmpAndPtTLInfo.ptTL = (Point)formatter.Deserialize(fsCache_BmpInfo);
			cBmpAndPtTLInfo.limbPositions = new classConfigStepLimbPosition[(int)formatter.Deserialize(fsCache_BmpInfo)];
			for (int intLimbPositionCounter = 0; intLimbPositionCounter < cBmpAndPtTLInfo.limbPositions.Length; intLimbPositionCounter++)
			{
				cBmpAndPtTLInfo.limbPositions[intLimbPositionCounter] = new classConfigStepLimbPosition();
				cBmpAndPtTLInfo.limbPositions[intLimbPositionCounter].dblAngle = (double)formatter.Deserialize(fsCache_BmpInfo);
				cBmpAndPtTLInfo.limbPositions[intLimbPositionCounter].pt = (Point)formatter.Deserialize(fsCache_BmpInfo);

				cBmpAndPtTLInfo.limbPositions[intLimbPositionCounter].cRad = new classMath.classRadialCoor( (double)formatter.Deserialize(fsCache_BmpInfo), (double)formatter.Deserialize(fsCache_BmpInfo));
			}
			cBmpAndPtTLInfo.hingePositions = new classConfigStepHingePosition[cBmpAndPtTLInfo.limbPositions.Length];
			for (int intHingePositionCounter = 0; intHingePositionCounter < cBmpAndPtTLInfo.limbPositions.Length; intHingePositionCounter++)
			{
				cBmpAndPtTLInfo.hingePositions[intHingePositionCounter] = new classConfigStepHingePosition();
				cBmpAndPtTLInfo.hingePositions[intHingePositionCounter].cRad = new classMath.classRadialCoor((double)formatter.Deserialize(fsCache_BmpInfo), (double)formatter.Deserialize(fsCache_BmpInfo));
				cBmpAndPtTLInfo.hingePositions[intHingePositionCounter].pt = (Point)formatter.Deserialize(fsCache_BmpInfo);
			}
			semaCacheFS.Release();
			return cBmpAndPtTLInfo;
		}
		
		/// <summary>
		/// appends input bmp-info to the cache file-stream and returns the position of index in index-file
		/// </summary>
		/// <param name="cBmpAndPtTLInfo">data to be sotred</param>
		/// <returns>position in index-cache-filestream used to retrieve info in getBmpPtTLInfoFromFs</returns>
		static public long appendBmpPtTLInfoToFS(ref classBmpAndPtTLInfo cBmpAndPtTLInfo)
		{
			semaCacheFS.WaitOne();
			// position bmpfile ready to append
			long lngFSPosition = fsCache_BmpInfo.Length;
			fsCache_BmpInfo.Position = lngFSPosition;

			// write keyinfo on rebuild-RAMcache file
			fsCache_BmpInfo_Rebuild.Position = fsCache_BmpInfo_Rebuild.Length;
			formatter.Serialize(fsCache_BmpInfo_Rebuild, fsCache_BmpInfo.Position);
			formatter.Serialize(fsCache_BmpInfo_Rebuild, cBmpAndPtTLInfo.cKey.Filename);
			for (int intKeyCounter = 0; intKeyCounter < cBmpAndPtTLInfo.cKey.dblKeys.Length; intKeyCounter++)
				formatter.Serialize(fsCache_BmpInfo_Rebuild, cBmpAndPtTLInfo.cKey.dblKeys[intKeyCounter]);

			// write bmp-info onto bmpfile
			formatter.Serialize(fsCache_BmpInfo, cBmpAndPtTLInfo.bmp);
			formatter.Serialize(fsCache_BmpInfo, cBmpAndPtTLInfo.ptTL);
			formatter.Serialize(fsCache_BmpInfo, cBmpAndPtTLInfo.limbPositions.Length);
			for (int intLimbPositionCounter = 0; intLimbPositionCounter < cBmpAndPtTLInfo.limbPositions.Length; intLimbPositionCounter++)
			{
				formatter.Serialize(fsCache_BmpInfo, cBmpAndPtTLInfo.limbPositions[intLimbPositionCounter].dblAngle);
				formatter.Serialize(fsCache_BmpInfo, cBmpAndPtTLInfo.limbPositions[intLimbPositionCounter].pt);
				formatter.Serialize(fsCache_BmpInfo, cBmpAndPtTLInfo.limbPositions[intLimbPositionCounter].cRad.angle);
				formatter.Serialize(fsCache_BmpInfo, cBmpAndPtTLInfo.limbPositions[intLimbPositionCounter].cRad.radius);
			}

			for (int intHingePositionCounter = 0; intHingePositionCounter < cBmpAndPtTLInfo.hingePositions.Length; intHingePositionCounter++)
			{
				formatter.Serialize(fsCache_BmpInfo, cBmpAndPtTLInfo.hingePositions[intHingePositionCounter].cRad.angle);
				formatter.Serialize(fsCache_BmpInfo, cBmpAndPtTLInfo.hingePositions[intHingePositionCounter].cRad.radius);
				formatter.Serialize(fsCache_BmpInfo, cBmpAndPtTLInfo.hingePositions[intHingePositionCounter].pt);
			}

			semaCacheFS.Release();
			return lngFSPosition;
		}

		static public classBmpAndPtTLInfo searchCache(ref classCacheTernaryTree_DoubleKeys_Node cacheRootNode,
																			 string strFilename,
																				int intConfig,
																				int intStep,
																			 double dblAngle,
																			 double dblSize,
																		  enuMirror eMirror)

		{ return searchCache(new classCacheTreeNodeKeys(strFilename, intConfig, intStep, dblAngle, dblSize, eMirror)); }

		static public classBmpAndPtTLInfo searchCache(classCacheTreeNodeKeys cKey)
		{
			if (cache == null)
				return null;
			classCacheTernaryTree_DoubleKeys_Node cTerDoubleNode = null;

			// search binary tree
			classCacheTernaryTree_CharKeys_Node  cTerCharNode = cache;
			int intCharIndex = 0;
			bool bolLoop = true;
			while ( bolLoop)
			{
				char chrSearchKey = cKey.strFilename[intCharIndex];
				if (chrSearchKey > cTerCharNode.chrKey)
				{
					if (cTerCharNode.Down == null)
						return null;
					else
						cTerCharNode = cTerCharNode.Down;
				}//0.ID, 1.Config, 2.Step, 3.Dir, 4.Size, 5.Mirror
				else if (chrSearchKey < cTerCharNode.chrKey)
				{
					if (cTerCharNode.Up == null)
						return null;
					else
						cTerCharNode = cTerCharNode.Up;
				}
				else
				{//0.ID, 1.Config, 2.Step, 3.Dir, 4.Size, 5.Mirror
					if (intCharIndex == cKey.strFilename.Length - 1)
					{
						if (cTerCharNode.cTerDoubleNode == null)
							return null;
						else
						{
							cTerDoubleNode = cTerCharNode.cTerDoubleNode;
							bolLoop = false;
						}
					}
					else
					{
						intCharIndex++;
						cTerCharNode = cTerCharNode.Next;
					}
				}
			}
			
			int intKeyIndex = 0;
			while (true)
			{
				double dblSearchKey = cKey.dblKeys[intKeyIndex];
				if (dblSearchKey > cTerDoubleNode.dblKey)
				{
					if (cTerDoubleNode.Down == null)
						return null;
					else
						cTerDoubleNode = cTerDoubleNode.Down;
				}//0.ID, 1.Config, 2.Step, 3.Dir, 4.Size, 5.Mirror
				else if (dblSearchKey < cTerDoubleNode.dblKey)
				{
					if (cTerDoubleNode.Up == null)
						return null;
					else
						cTerDoubleNode = cTerDoubleNode.Up;
				}
				else
				{//0.ID, 1.Config, 2.Step, 3.Dir, 4.Size, 5.Mirror
					if (intKeyIndex == cKey.dblKeys.Length - 1)
					{
						if (CacheInRAM)
							return cTerDoubleNode.cBmpAndPtTLInfo;
						else
							return getBmpPtTLInfoFromFS(cTerDoubleNode.lngFSPosition);
					}
					else
					{
						intKeyIndex++;
						cTerDoubleNode = cTerDoubleNode.Next;
					}
				}
			}
		}

		static classCacheTernaryTree_DoubleKeys_Node initTernary_Double_Tree(classCacheTreeNodeKeys cKey)
		{
			classCacheTernaryTree_DoubleKeys_Node cRetVal = new classCacheTernaryTree_DoubleKeys_Node(cKey.dblKeys[0]);
			classCacheTernaryTree_DoubleKeys_Node cTerNode = cRetVal;
			for (int intKeyCounter = 1; intKeyCounter < cKey.dblKeys.Length; intKeyCounter++)
			{
				cTerNode.Next = new classCacheTernaryTree_DoubleKeys_Node(cKey.dblKeys[intKeyCounter]);
				cTerNode = cTerNode.Next;
			}//0.ID, 1.Config, 2.Step, 3.Dir, 4.Size, 5.Mirror
			return cRetVal;
		}

		static classCacheTernaryTree_CharKeys_Node initTernary_Char_Tree(string strFilename)
		{
			classCacheTernaryTree_CharKeys_Node cRetVal = new classCacheTernaryTree_CharKeys_Node(strFilename[0]);
			classCacheTernaryTree_CharKeys_Node cTerNode = cRetVal;
			for (int intKeyCounter = 1; intKeyCounter < strFilename.Length; intKeyCounter++)
			{
				cTerNode.Next = new classCacheTernaryTree_CharKeys_Node(strFilename[intKeyCounter]);
				cTerNode = cTerNode.Next;
			}//0.ID, 1.Config, 2.Step, 3.Dir, 4.Size, 5.Mirror
			return cRetVal;
		}

		static public void Cache(ref classBmpAndPtTLInfo cBmpAndPtTLInfo, classCacheTreeNodeKeys cKey)
		{
			semaCacheTree.WaitOne();
			classCacheTernaryTree_DoubleKeys_Node cTerDoubleNode = null;
			cBmpAndPtTLInfo.cKey = cKey;

			if (cache == null)
			{ // first element into tree
				cache = initTernary_Char_Tree(cKey.Filename);
				cache.cTerDoubleNode = initTernary_Double_Tree(cKey);
			}

			// traverse char ternary tree to file appropriate file
			classCacheTernaryTree_CharKeys_Node cTerCharNode = cache;
			int intCharIndex = 0;
			bool bolLoop = true;
			while (bolLoop)
			{
				if (cKey.strFilename[intCharIndex] > cTerCharNode.chrKey)
				{
					if (cTerCharNode.Down == null)
						cTerCharNode.Down = new classCacheTernaryTree_CharKeys_Node(cKey.strFilename[intCharIndex]);
					cTerCharNode = cTerCharNode.Down;
				}//0.ID, 1.Config, 2.Step, 3.Dir, 4.Size, 5.Mirror
				else if (cKey.strFilename[intCharIndex] < cTerCharNode.chrKey)
				{
					if (cTerCharNode.Up == null)
						cTerCharNode.Up = new classCacheTernaryTree_CharKeys_Node(cKey.strFilename[intCharIndex]);
					cTerCharNode = cTerCharNode.Up;
				}//0.ID, 1.Config, 2.Step, 3.Dir, 4.Size, 5.Mirror
				else
				{
					if (intCharIndex == cKey.strFilename.Length - 1)
					{ // match found
						if (cTerCharNode.cTerDoubleNode == null)
						{ // data not yet cached in HD
							cTerCharNode.cTerDoubleNode = initTernary_Double_Tree(cKey);
						}
						cTerDoubleNode = cTerCharNode.cTerDoubleNode;
						bolLoop = false;
					}//0.ID, 1.Config, 2.Step, 3.Dir, 4.Size, 5.Mirror
					else
					{
						intCharIndex++;
						if (cTerCharNode.Next == null)
							cTerCharNode.Next = new classCacheTernaryTree_CharKeys_Node(cKey.strFilename[intCharIndex]);
						cTerCharNode = cTerCharNode.Next;
					}//0.ID, 1.Config, 2.Step, 3.Dir, 4.Size, 5.Mirror
				}
			}

			// traverse double-keys ternary tree to cache this Bmp-info
			int intKeyIndex = 0;
			while (true)
			{
				if (cKey.dblKeys[intKeyIndex] > cTerDoubleNode.dblKey)
				{
					if (cTerDoubleNode.Down == null)
						cTerDoubleNode.Down = new classCacheTernaryTree_DoubleKeys_Node(cKey.dblKeys[intKeyIndex]);
					cTerDoubleNode = cTerDoubleNode.Down;
				}//0.ID, 1.Config, 2.Step, 3.Dir, 4.Size, 5.Mirror
				else if (cKey.dblKeys[intKeyIndex] < cTerDoubleNode.dblKey)
				{
					if (cTerDoubleNode.Up == null)
						cTerDoubleNode.Up = new classCacheTernaryTree_DoubleKeys_Node(cKey.dblKeys[intKeyIndex]);
					cTerDoubleNode = cTerDoubleNode.Up;
				}//0.ID, 1.Config, 2.Step, 3.Dir, 4.Size, 5.Mirror
				else
				{
					if (intKeyIndex == cKey.dblKeys.Length - 1)
					{ // match found
						if (cTerDoubleNode.lngFSPosition < 0)
						{ // data not yet cached in HD
							cTerDoubleNode.lngFSPosition = appendBmpPtTLInfoToFS(ref cBmpAndPtTLInfo);
						}
						if (CacheInRAM)
							cTerDoubleNode.cBmpAndPtTLInfo = cBmpAndPtTLInfo; 
						semaCacheTree.Release();
						return;
					}//0.ID, 1.Config, 2.Step, 3.Dir, 4.Size, 5.Mirror
					else
					{
						intKeyIndex++;
						if (cTerDoubleNode.Next == null)
							cTerDoubleNode.Next = new classCacheTernaryTree_DoubleKeys_Node(cKey.dblKeys[intKeyIndex]);
						cTerDoubleNode = cTerDoubleNode.Next;
					}//0.ID, 1.Config, 2.Step, 3.Dir, 4.Size, 5.Mirror
				}
			}
		}
    }
	
	public class classSprite
    {
		public static classSpriteMaker spriteMaker = new classSpriteMaker();
		public classSpriteLimb limStationary;
        public classSpriteLimb MasterLimb;
        public classConfiguration[] Configurations;
        classConfiguration _conCurrent;
		public classConfiguration conCurrent { get { return _conCurrent; } }

        int _intCurrent_Configuration;
		public int intCurrent_Configuration
		{
			get {return _intCurrent_Configuration; }
			set
			{
				if (value >= 0 && value < Configurations.Length)
				{
					_intCurrent_Configuration = value;
					_conCurrent = Configurations[_intCurrent_Configuration];
				}
			}
		}

        public int intCurrent_ConfigurationStep;
        int intImagesPerRotation;
        double dblImagesPerRotation;
        public classSpriteFileStream cFileStream;
        public enuMirror eMirror;

        /// <summary>
        /// when set output configuration images are cached in RAM.  Memory consumption is not checked and may result in excessively large memory costs if sprite images DisplaySize varies frequently.
        /// </summary>
        static bool bolRamCache;        
        public static bool CacheInRAM
        {
            get { return bolRamCache; }
            set
            {
                bolRamCache = value;
            }
        }

        public double ImagesPerRotation
        {
            get { return dblImagesPerRotation; }
            set
            {
                intImagesPerRotation = (int)Math.Floor(value);
                dblImagesPerRotation = (double)intImagesPerRotation;
            }
        }

        public classSprite()
        {
            setImagesPerRotation(16);
        }

        public classSprite(int intImagesPerQuarterRotation)
        {
            setImagesPerRotation(intImagesPerQuarterRotation);
        }
		
        void setImagesPerRotation(int intImagesPerQuarterRotation)
        {
            ImagesPerRotation = intImagesPerQuarterRotation * 4;			
        }

        public long getLimbImageIndex(int intLimb, int intImage)
        {
            return (long)(cFileStream.lngStartIndex + (cFileStream.lngSizeLongIndexInFileStream * ImagesPerRotation / 4) * intLimb + (cFileStream.lngSizeLongIndexInFileStream * intImage));
        }

        public Bitmap getLimbImage(int intLimb, int intImage)
        {
            cFileStream.fs.Position = getLimbImageIndex(intLimb, intImage);
			cFileStream.fs.Position = (long)classSpriteFileStream.formatter.Deserialize(cFileStream.fs);
			return (Bitmap)classSpriteFileStream.formatter.Deserialize(cFileStream.fs);
        }

        string strMyName = "";
        public string myName
        {
            get { return strMyName; }
            set { strMyName = value; }
        }

        double dblDisplaySize = 1.0;
        /// <summary>
        /// double variable factor which determines size of output image relative to original images size where 1.0 is the same
        /// </summary>
        public double DisplaySize
        {
            get { return dblDisplaySize; }
            set { dblDisplaySize = value; }
        }

        public classSpriteLimb[] limbs = new classSpriteLimb[0];
        public Sprite.classSpriteHinge[] hinges = new classSpriteHinge[0];
        /// <summary>
        /// displays the sprite in the specified configuration and step at a point on referenced bitmap
        /// all of the sprite's hinges are set to their specified configuration and step values
        /// </summary>
        /// <param name="bmpOutput">reference bitmap on which the sprite is to be drawn</param>
        /// <param name="intConfigurationIndex">sprite's output configuration index value</param>
        /// <param name="intConfigurationStep">sprite's output configuration step value</param>
        /// <param name="ptOnScreenLocation">point on the screen where the center of the sprite's master limb is to be located</param>
        public void putConfigurationOnScreen(ref Bitmap bmpOutput,
                                                 int intConfigurationIndex,
                                                 int intConfigurationStep,
                                                 Point ptOnScreenLocation)
        {
            putConfigurationOnScreen(ref bmpOutput,
                                         intConfigurationIndex,
                                         intConfigurationStep,
                                         ptOnScreenLocation,
                                         0,
                                         DisplaySize,
                                         eMirror,
                                         false);
        }

        /// <summary>
        /// displays the sprite in the specified configuration and step at a point on referenced bitmap
        /// all of the sprite's hinges are set to their specified configuration and step values
        /// </summary>
        /// <param name="bmpOutput">reference bitmap on which the sprite is to be drawn</param>
        /// <param name="strConfigurationName">sprite's output configuration name</param>
        /// <param name="intConfigurationStep">sprite's output configuration step value</param>
        /// <param name="ptOnScreenLocation">point on the screen where the center of the sprite's master limb is to be located</param>
        public void putConfigurationOnScreen(ref Bitmap bmpOutput,
                                                 string strConfigurationName,
                                                 int intConfigurationStep,
                                                 Point ptOnScreenLocation)
        {
            putConfigurationOnScreen(ref bmpOutput,
                                         getIndexOfConfigurationByName(strConfigurationName),
                                         intConfigurationStep,
                                         ptOnScreenLocation,
                                         0,
                                         DisplaySize,
                                         eMirror,
                                         false);
        }

        /// <summary>
        /// displays the sprite in the specified configuration and step at a point on referenced bitmap
        /// all of the sprite's hinges are set to their specified configuration and step values
        /// </summary>
        /// <param name="bmpOutput">reference bitmap on which the sprite is to be drawn</param>
        /// <param name="strConfigurationName">sprite's output configuration name</param>
        /// <param name="intConfigurationStep">sprite's output configuration step value</param>
        /// <param name="ptOnScreenLocation">point on the screen where the center of the sprite's master limb is to be located</param>
        /// <param name="DisplaySize">double variable which is multiplied times all size relevant parameters in the drawing sequence 1.0=original image size</param>
        /// <param name="mirror">configuration image reflected in any of four directions, (none, vertical, horizontal or both)</param>
        public void putConfigurationOnScreen(ref Bitmap bmpOutput,
                                                 string strConfigurationName,
                                                 int intConfigurationStep,
                                                 Point ptOnScreenLocation,
                                                 double displaySize,
                                                 enuMirror mirror)
        {
            putConfigurationOnScreen(ref bmpOutput,
                                        getIndexOfConfigurationByName(strConfigurationName),
                                        intConfigurationStep,
                                        ptOnScreenLocation,
                                        0,
                                        displaySize,
                                        mirror,
                                        false);
        }

        /// <summary>
        /// displays the sprite in the specified configuration and step at a point on referenced bitmap
        /// all of the sprite's hinges are set to their specified configuration and step values
        /// </summary>
        /// <param name="bmpOutput">reference bitmap on which the sprite is to be drawn</param>
        /// <param name="intConfigurationIndex">sprite's output configuration index value</param>
        /// <param name="intConfigurationStep">sprite's output configuration step value</param>
        /// <param name="ptOnScreenLocation">point on the screen where the center of the sprite's master limb is to be located</param>
        /// <param name="DisplaySize">double variable which is multiplied times all size relevant parameters in the drawing sequence 1.0=original image size</param>
        /// <param name="mirror">configuration image reflected in any of four directions, (none, vertical, horizontal or both)</param>
        public void putConfigurationOnScreen(ref Bitmap bmpOutput,
                                                 int intConfigurationIndex,
                                                 int intConfigurationStep,
                                                 Point ptOnScreenLocation,
                                                 double dblAngle,
                                                 double DisplaySize,
                                                 enuMirror mirror,
                                                 bool bolForceRebuild)
        {
            if (bmpOutput == null)
                return;

            if (Configurations == null 
                || Configurations.Length <= intConfigurationIndex
                || intConfigurationIndex < 0
                || intConfigurationStep < 0
                || Configurations[intConfigurationIndex].steps == null
                || Configurations[intConfigurationIndex].steps.Length <= intConfigurationStep
               )
                return;

            intCurrent_ConfigurationStep = intConfigurationStep;
            intCurrent_Configuration = intConfigurationIndex;

            eMirror = mirror;

            if (MasterLimb.IRotate)
            {
                MasterLimb.Angle = dblAngle;
                MasterLimb.setDir();
            }


			if (Configurations[intConfigurationIndex].steps != null)
			{
				classBmpAndPtTLInfo cBmpAndPtTLInfo = getImageOnBitmap(intConfigurationIndex, intConfigurationStep, dblAngle, DisplaySize, mirror, false);
				using (Graphics g = Graphics.FromImage(cBmpAndPtTLInfo.bmp))
					g.DrawImage(cBmpAndPtTLInfo.bmp, new Point(ptOnScreenLocation.X - cBmpAndPtTLInfo.ptTL.X, ptOnScreenLocation.Y - cBmpAndPtTLInfo.ptTL.Y));
			}
            if (eMirror == enuMirror.VerticalAndHorizontal)
                rotateMasterLimbSlaveByPi();
        }

		
        /// <summary>
        /// this function arranges the sprite's hinges according to the values stored in its configurations for a specific configuration & step
        /// </summary>
        /// <param name="intConfigurationIndex">configuration index to which hinges are to be set</param>
        /// <param name="intConfigurationStep">configuration step to which hinges are to be set</param>
		public void setSpriteHingesToConfiguration(int intConfigurationIndex, int intConfigurationStep)
		{
			intCurrent_Configuration = intConfigurationIndex;
			intCurrent_ConfigurationStep = intConfigurationStep;

			if (intConfigurationIndex >= 0 && intConfigurationIndex < Configurations.Length)
			{
				intCurrent_Configuration = intConfigurationIndex;
			}
			if (intConfigurationStep < 0)
				intConfigurationStep = 0;

			// set up hinges for unmirrored configuration
			for (int intHingeCounter = 0;
				 intHingeCounter < Configurations[intConfigurationIndex].steps[intConfigurationStep].hingeDetails.Length;
				 intHingeCounter++)
				try
				{
					hinges[intHingeCounter].Angle
						  = Configurations[intConfigurationIndex]
								  .steps[intConfigurationStep]
								  .hingeDetails[intHingeCounter]
								  .angle;
				}
				catch (Exception) { }
		}

        void rotateMasterLimbSlaveByPi()
        {
            enuMirror eRememberMirror = eMirror;
            eMirror = enuMirror.none;
            for (int intSlaveCounter = 0; intSlaveCounter < MasterLimb.mySlaveLimbs.Length; intSlaveCounter++)
                MasterLimb.mySlaveLimbs[intSlaveCounter].MasterHinge.Angle = classMath.cleanAngle(MasterLimb.mySlaveLimbs[intSlaveCounter].MasterHinge.Angle + Math.PI);
            eMirror = eRememberMirror;
        }

        SolidBrush brBack;
        /// <summary>
        /// set the color of sprite's outline.  setting it to White resets to default;
        /// </summary>
        public Color ColorOutline
        {
            get
            {
                if (brBack != null)
                    return brBack.Color;
                else
                    return Color.White;
            }
            set
            {
                if (value.R == Color.White.R
                    && value.G == Color.White.G
                    && value.B == Color.White.B
                    && value.A == Color.White.A)
                    brBack = null;
                else
                    brBack = new SolidBrush(value);
            }
        }

        bool bolUseCachedImages = true;
        public bool UseCachedImages
        {
            get { return bolUseCachedImages; }
            set
            {
                bolUseCachedImages = value;
            }
        }

		public void setBmpInfoImage(ref classBmpAndPtTLInfo cBmpInfo, ref classSpriteLimb[] limbDrawSequence, double dblAngle, double dblSize, enuMirror eMirror)
		{
			
		//dblAngle = 0;
			Bitmap[] bmpLimbImages = new Bitmap[limbDrawSequence.Length];
			Rectangle[] recDest = new Rectangle[limbDrawSequence.Length];
			Point ptTL = new Point(), ptBR = new Point();
			// cycle through each limb
			for (int intLimbCounter = 0; intLimbCounter < cBmpInfo.limbPositions.Length; intLimbCounter++)
			{
				// reposition limb
				cBmpInfo.limbPositions[intLimbCounter].pt = new Point((int)(cBmpInfo.limbPositions[intLimbCounter].cRad.radius * dblSize * Math.Cos(cBmpInfo.limbPositions[intLimbCounter].cRad.angle + dblAngle)),
																	  (int)(cBmpInfo.limbPositions[intLimbCounter].cRad.radius * dblSize * Math.Sin(cBmpInfo.limbPositions[intLimbCounter].cRad.angle + dblAngle)));
				// reposition hinge
				cBmpInfo.hingePositions[intLimbCounter].pt = new Point((int)(cBmpInfo.hingePositions[intLimbCounter].cRad.radius * dblSize * Math.Cos(cBmpInfo.hingePositions[intLimbCounter].cRad.angle + dblAngle)),
																	  (int)(cBmpInfo.hingePositions[intLimbCounter].cRad.radius * dblSize * Math.Sin(cBmpInfo.hingePositions[intLimbCounter].cRad.angle + dblAngle)));
				// set up limb's image for this output
				switch (eMirror)
				{
					case enuMirror.none:
						bmpLimbImages[intLimbCounter] = new Bitmap(limbs[intLimbCounter].getLimbImage(cBmpInfo.limbPositions[intLimbCounter].dblAngle + dblAngle));
						break;

					case enuMirror.vertical:
						bmpLimbImages[intLimbCounter] = new Bitmap(limbs[intLimbCounter].getLimbImage(ref limbs[intLimbCounter].bmpSpriteLimb, classMath.getMirrorAngle(cBmpInfo.limbPositions[intLimbCounter].dblAngle + dblAngle, enuMirror.vertical), enuMirror.none));
						bmpLimbImages[intLimbCounter].RotateFlip(RotateFlipType.RotateNoneFlipY);
						break;

					case enuMirror.horizontal:
						bmpLimbImages[intLimbCounter] = new Bitmap(limbs[intLimbCounter].getLimbImage(ref limbs[intLimbCounter].bmpSpriteLimb, classMath.getMirrorAngle(cBmpInfo.limbPositions[intLimbCounter].dblAngle + dblAngle, enuMirror.horizontal), enuMirror.none));
						bmpLimbImages[intLimbCounter].RotateFlip(RotateFlipType.RotateNoneFlipX);
						break;

					case enuMirror.VerticalAndHorizontal:
						bmpLimbImages[intLimbCounter] = new Bitmap(limbs[intLimbCounter].getLimbImage(cBmpInfo.limbPositions[intLimbCounter].dblAngle + dblAngle));
						break;
				}
				if (ptTL.X > cBmpInfo.limbPositions[intLimbCounter].pt.X - (int)(bmpLimbImages[intLimbCounter].Width * dblSize / 2))
					ptTL.X = cBmpInfo.limbPositions[intLimbCounter].pt.X - (int)(bmpLimbImages[intLimbCounter].Width * dblSize / 2);
				if (ptTL.Y > cBmpInfo.limbPositions[intLimbCounter].pt.Y - (int)(bmpLimbImages[intLimbCounter].Height * dblSize / 2))
					ptTL.Y = cBmpInfo.limbPositions[intLimbCounter].pt.Y - (int)(bmpLimbImages[intLimbCounter].Height * dblSize / 2);
				if (ptBR.X < cBmpInfo.limbPositions[intLimbCounter].pt.X + (int)(bmpLimbImages[intLimbCounter].Width * dblSize / 2))
					ptBR.X = cBmpInfo.limbPositions[intLimbCounter].pt.X + (int)(bmpLimbImages[intLimbCounter].Width * dblSize / 2);
				if (ptBR.Y < cBmpInfo.limbPositions[intLimbCounter].pt.Y + (int)(bmpLimbImages[intLimbCounter].Height * dblSize / 2))
					ptBR.Y = cBmpInfo.limbPositions[intLimbCounter].pt.Y + (int)(bmpLimbImages[intLimbCounter].Height * dblSize / 2);
			}

			// create new output image
			Size szNewImage = new Size(ptBR.X - ptTL.X, ptBR.Y - ptTL.Y);
			if (szNewImage.Height < 1) szNewImage.Height = 1;
			if (szNewImage.Width < 1) szNewImage.Width  = 1;
			cBmpInfo.ptTL = new Point(-ptTL.X, -ptTL.Y);

			// build Dest rectangles
			for (int intLimbCounter = 0; intLimbCounter < cBmpInfo.limbPositions.Length; intLimbCounter++)
			{
				// set up image dest-rect
				recDest[intLimbCounter] = new Rectangle((int)(cBmpInfo.ptTL.X + cBmpInfo.limbPositions[intLimbCounter].pt.X - bmpLimbImages[intLimbCounter].Width * dblSize  / 2),
														(int)(cBmpInfo.ptTL.Y + cBmpInfo.limbPositions[intLimbCounter].pt.Y - bmpLimbImages[intLimbCounter].Height * dblSize / 2),
														(int)(bmpLimbImages[intLimbCounter].Width * dblSize),
														(int)(bmpLimbImages[intLimbCounter].Height * dblSize));
				
			}
			

			cBmpInfo.bmp = new Bitmap(szNewImage.Width, szNewImage.Height);

			// draw limbs in draw sequence where they belong on new image
			using (Graphics g = Graphics.FromImage(cBmpInfo.bmp))
			{
				if (brBack != null)
					g.FillRectangle(brBack, new Rectangle(0, 0, cBmpInfo.bmp.Width, cBmpInfo.bmp.Height));
				for (int intLimCounter = 0; intLimCounter < limbDrawSequence.Length; intLimCounter++)
				{
					Rectangle recSrc = new Rectangle(0, 0, bmpLimbImages[limbDrawSequence[intLimCounter].intMyLimbID].Width, bmpLimbImages[limbDrawSequence[intLimCounter].intMyLimbID].Height);
					bmpLimbImages[limbDrawSequence[intLimCounter].intMyLimbID].MakeTransparent();
					g.DrawImage(bmpLimbImages[limbDrawSequence[intLimCounter].intMyLimbID], recDest[limbDrawSequence[intLimCounter].intMyLimbID], recSrc, GraphicsUnit.Pixel);
				}
			}
			if (MakeSpriteTransparent)
				cBmpInfo.bmp.MakeTransparent();
			dblDisplaySize = dblSize;
			MasterLimb.Angle = dblAngle;
		}

		public classBmpAndPtTLInfo getImageOnBitmap(int intConfigurationIndex, int intStep, double Angle, double DisplaySize, enuMirror mirror, bool bolForceRebuild)
		{
			MasterLimb.Angle = Angle;
			MasterLimb.setDir();
			dblDisplaySize = DisplaySize;
			eMirror = mirror;
			if (Configurations == null
				|| Configurations.Length < intConfigurationIndex
				|| intConfigurationIndex < 0)
				return null;

			intCurrent_Configuration = intConfigurationIndex;
			intCurrent_ConfigurationStep = intStep;
			classBmpAndPtTLInfo cBmpPtTLInfo = null;
			classCacheTreeNodeKeys cCacheKey = null;
				// try fetching the exact image we're looking for
				cCacheKey = new classCacheTreeNodeKeys(cFileStream.strFilename,
														intConfigurationIndex,
														intStep,
														Angle,
														DisplaySize,
														mirror);
				cBmpPtTLInfo = classSpriteFileStream.searchCache(cCacheKey);

				if (cBmpPtTLInfo != null
					&& !bolForceRebuild
					&& UseCachedImages)
				{
					return cBmpPtTLInfo;
				}

				// try fetching the same image at default size=1.0 & angle=0
				classCacheTreeNodeKeys cCacheKey_DefaultSizeAndAngle = new classCacheTreeNodeKeys(cFileStream.strFilename,
																								   intConfigurationIndex,
																								   intStep,
																								   0,
																								   1.0,
																								   mirror);
				cBmpPtTLInfo = classSpriteFileStream.searchCache(cCacheKey_DefaultSizeAndAngle);

				if (cBmpPtTLInfo != null
					&& !bolForceRebuild
					&& UseCachedImages)
				{
					cBmpPtTLInfo = cBmpPtTLInfo.Copy(); // make a copy (or the RAM-cached data will be corrupted) 
					// resize & angle default image to desired image
					setBmpInfoImage(ref cBmpPtTLInfo, ref Configurations[intConfigurationIndex].LimbDrawSequence, Angle, dblDisplaySize, mirror);
					if (cFileStream.bolIAmCached)
					{
						// create cache-key for new image
						cCacheKey = new classCacheTreeNodeKeys(cFileStream.strFilename, intConfigurationIndex, intStep, Angle, dblDisplaySize, mirror);
						// cache new image and return value
						classSpriteFileStream.Cache(ref cBmpPtTLInfo, cCacheKey);
					}
					return cBmpPtTLInfo;
				}
			

			// no cached image can be found -> build it
			Point ptTL = new Point(0, 0);
			Point ptBR = new Point(0, 0);
			classSpriteLimb[] limDrawSequence = conCurrent.LimbDrawSequence;

			switch (mirror)
			{
				case enuMirror.none:
					MasterLimb.Loc = new Point(0, 0);
					eMirror = enuMirror.none;
					if (dblDisplaySize == 1.0 && Angle == 0)
					{
						// put image together for size 1.0 & angle=0, using the original hinge connection angles
						MasterLimb.Angle = 0;
						MasterLimb.setDir();
						setSpriteHingesToConfiguration(intConfigurationIndex, intStep);
						putImageOntoScreen_recursion(ref MasterLimb, 1.0);

						cBmpPtTLInfo = new classBmpAndPtTLInfo();
						cBmpPtTLInfo.ptTL = new Point(-MasterLimb.myCurrentImage.Width / 2, -MasterLimb.myCurrentImage.Height / 2);
						ptBR = new Point(MasterLimb.myCurrentImage.Width / 2, MasterLimb.myCurrentImage.Height / 2);

						for (int intHingeCounter = 0; intHingeCounter < hinges.Length; intHingeCounter++)
						{
							Sprite.classSpriteLimb thisLimb = hinges[intHingeCounter].Limb_Slave;
							if (thisLimb.recDest.Left < cBmpPtTLInfo.ptTL.X)
								cBmpPtTLInfo.ptTL.X = thisLimb.recDest.Left;
							if (thisLimb.recDest.Left + thisLimb.recDest.Width > ptBR.X)
								ptBR.X = thisLimb.recDest.Left + thisLimb.recDest.Width;
							if (thisLimb.recDest.Top < cBmpPtTLInfo.ptTL.Y)
								cBmpPtTLInfo.ptTL.Y = thisLimb.recDest.Top;
							if (thisLimb.recDest.Top + thisLimb.recDest.Height > ptBR.Y)
								ptBR.Y = thisLimb.recDest.Top + thisLimb.recDest.Height;
						}

						if (ptBR.X == cBmpPtTLInfo.ptTL.X)
							ptBR.X += 1;
						if (ptBR.Y == cBmpPtTLInfo.ptTL.Y)
							ptBR.Y += 1;
						cBmpPtTLInfo.ptTL.X *= -1;
						cBmpPtTLInfo.ptTL.Y *= -1;

						// draw onto output bitmap
						cBmpPtTLInfo.bmp = new Bitmap(ptBR.X + cBmpPtTLInfo.ptTL.X, ptBR.Y + cBmpPtTLInfo.ptTL.Y);
						using (Graphics g = Graphics.FromImage(cBmpPtTLInfo.bmp))
						{
							if (brBack != null)
								g.FillRectangle(brBack, new Rectangle(0, 0, cBmpPtTLInfo.bmp.Width, cBmpPtTLInfo.bmp.Height));
							if (limDrawSequence != null)
							{
								cBmpPtTLInfo.limbPositions = new classConfigStepLimbPosition[limDrawSequence.Length];
								cBmpPtTLInfo.hingePositions = new classConfigStepHingePosition[limDrawSequence.Length];
								for (int intLimbCounter = 0; intLimbCounter < limDrawSequence.Length; intLimbCounter++)
								{
									Sprite.classSpriteLimb thisLimb = limDrawSequence[intLimbCounter];
									if (thisLimb != null)
									{
										thisLimb.myCurrentImage.MakeTransparent();

										if (thisLimb.MasterHinge != null)
											thisLimb.MasterHinge.Loc = new Point(thisLimb.MasterHinge.Loc.X + cBmpPtTLInfo.ptTL.X, thisLimb.MasterHinge.Loc.Y + cBmpPtTLInfo.ptTL.Y);

										int intDrawSequenceIndex = Configurations[intCurrent_Configuration].getLimbDrawSequenceIndex(thisLimb.Name.ToString());
										cBmpPtTLInfo.limbPositions[limDrawSequence[intDrawSequenceIndex].intMyLimbID] = new classConfigStepLimbPosition();
										if (intDrawSequenceIndex >= 0)
										{
											cBmpPtTLInfo.limbPositions[limDrawSequence[intDrawSequenceIndex].intMyLimbID] = new classConfigStepLimbPosition();
											cBmpPtTLInfo.limbPositions[limDrawSequence[intDrawSequenceIndex].intMyLimbID].pt = thisLimb.Loc;
											cBmpPtTLInfo.limbPositions[limDrawSequence[intDrawSequenceIndex].intMyLimbID].dblAngle = thisLimb.Angle;
										}

										g.DrawImage(new Bitmap(thisLimb.myCurrentImage, thisLimb.recDest.Size), new Point(cBmpPtTLInfo.ptTL.X + thisLimb.recDest.Location.X, cBmpPtTLInfo.ptTL.Y + thisLimb.recDest.Location.Y));
									}
								}
								for (int intHingeCounter = 0; intHingeCounter < cBmpPtTLInfo.hingePositions.Length; intHingeCounter++)
								{
									cBmpPtTLInfo.hingePositions[intHingeCounter] = new classConfigStepHingePosition();
									cBmpPtTLInfo.hingePositions[intHingeCounter].pt.X = hinges[intHingeCounter].Loc.X - cBmpPtTLInfo.ptTL.X;
									cBmpPtTLInfo.hingePositions[intHingeCounter].pt.Y = hinges[intHingeCounter].Loc.Y - cBmpPtTLInfo.ptTL.Y;
									hinges[intHingeCounter].Loc = cBmpPtTLInfo.hingePositions[intHingeCounter].pt;
								}
							}
						}

						// calculate radial positions of each limb & hinge for this unmirrored, unrotated, standard-sized config/step image
						for (int intLimbCounter = 0; intLimbCounter < cBmpPtTLInfo.limbPositions.Length; intLimbCounter++)
						{
							cBmpPtTLInfo.limbPositions[intLimbCounter].cRad = new classMath.classRadialCoor(classMath.arcTan(cBmpPtTLInfo.limbPositions[intLimbCounter].pt.X, cBmpPtTLInfo.limbPositions[intLimbCounter].pt.Y),
																											classMath.getRootOfSquares(cBmpPtTLInfo.limbPositions[intLimbCounter].pt.X, cBmpPtTLInfo.limbPositions[intLimbCounter].pt.Y));
							cBmpPtTLInfo.hingePositions[intLimbCounter].cRad = new classMath.classRadialCoor(classMath.arcTan(cBmpPtTLInfo.hingePositions[intLimbCounter].pt.X, cBmpPtTLInfo.hingePositions[intLimbCounter].pt.Y),
																											 classMath.getRootOfSquares(cBmpPtTLInfo.hingePositions[intLimbCounter].pt.X, cBmpPtTLInfo.hingePositions[intLimbCounter].pt.Y));
						}
						//if (cFileStream.bolIAmCached)
						{
							classCacheTreeNodeKeys cCacheKeyDefault = new classCacheTreeNodeKeys(cCacheKey.strFilename,
																								 cCacheKey.ConfigIndex,
																								 cCacheKey.Step,
																								 0,
																								 1.0,
																								 cCacheKey.Mirror);

							classSpriteFileStream.Cache(ref cBmpPtTLInfo,
															cCacheKeyDefault);
						}
						return cBmpPtTLInfo;
					}
					else
					{
						// load original sized unrotated unmirrored image default sized image
						cBmpPtTLInfo = getImageOnBitmap(intConfigurationIndex, intStep, 0, 1.0, enuMirror.none, false).Copy();
						setBmpInfoImage(ref cBmpPtTLInfo, ref Configurations[intConfigurationIndex].LimbDrawSequence, Angle, DisplaySize, enuMirror.none);
						if (cFileStream.bolIAmCached)
						{
							cCacheKey = new classCacheTreeNodeKeys(cFileStream.strFilename, intConfigurationIndex, intStep, Angle, DisplaySize, enuMirror.none);
							classSpriteFileStream.Cache(ref cBmpPtTLInfo,
															cCacheKey);
						}
						return cBmpPtTLInfo;
					}

				default: // mirrored image
					if (MasterLimb.Angle == 0)
					{
						// load original image default size, angle no-mirror
						cBmpPtTLInfo = getImageOnBitmap(intConfigurationIndex, intStep, 0, 1.0, enuMirror.none, false).Copy();
						// flip all limb positions, limb angles & hinge positions according to the desired mirror pos/angle
						switch (mirror)
						{
							case enuMirror.horizontal:
								// get an image for the original unmirrored configuration
								// scan through each limb and adjust positions and angles for each limb to reflect Mirror=horizontal
								for (int intLimbCounter = 0; intLimbCounter < cBmpPtTLInfo.limbPositions.Length; intLimbCounter++)
								{
									cBmpPtTLInfo.limbPositions[intLimbCounter].pt = new Point(-cBmpPtTLInfo.limbPositions[intLimbCounter].pt.X,
																							   cBmpPtTLInfo.limbPositions[intLimbCounter].pt.Y);
									cBmpPtTLInfo.limbPositions[intLimbCounter].dblAngle =classMath.getMirrorAngle( cBmpPtTLInfo.limbPositions[intLimbCounter].dblAngle, enuMirror.horizontal);

									cBmpPtTLInfo.hingePositions[intLimbCounter].pt = new Point(-cBmpPtTLInfo.hingePositions[intLimbCounter].pt.X,
																							    cBmpPtTLInfo.hingePositions[intLimbCounter].pt.Y);
								}
								break;

							case enuMirror.vertical:
								// get an image for the original unmirrored configuration
								// scan through each limb and adjust positions and angles for each limb to reflect Mirror=eMirror
								for (int intLimbCounter = 0; intLimbCounter < cBmpPtTLInfo.limbPositions.Length; intLimbCounter++)
								{
									cBmpPtTLInfo.limbPositions[intLimbCounter].pt = new Point(cBmpPtTLInfo.limbPositions[intLimbCounter].pt.X,
																							  -cBmpPtTLInfo.limbPositions[intLimbCounter].pt.Y);
									cBmpPtTLInfo.limbPositions[intLimbCounter].dblAngle = classMath.getMirrorAngle( cBmpPtTLInfo.limbPositions[intLimbCounter].dblAngle, enuMirror.vertical);

									cBmpPtTLInfo.hingePositions[intLimbCounter].pt = new Point(cBmpPtTLInfo.hingePositions[intLimbCounter].pt.X,
																							   -cBmpPtTLInfo.hingePositions[intLimbCounter].pt.Y);
								}
								break;

							default:
							case enuMirror.VerticalAndHorizontal:
								// get an image for the original unmirrored configuration
								// scan through each limb and adjust positions and angles for each limb to reflect Mirror=eMirror
								for (int intLimbCounter = 0; intLimbCounter < cBmpPtTLInfo.limbPositions.Length; intLimbCounter++)
								{
									cBmpPtTLInfo.limbPositions[intLimbCounter].pt = new Point(-cBmpPtTLInfo.limbPositions[intLimbCounter].pt.X,
																							  -cBmpPtTLInfo.limbPositions[intLimbCounter].pt.Y);
									cBmpPtTLInfo.limbPositions[intLimbCounter].dblAngle = classMath.cleanAngle(cBmpPtTLInfo.limbPositions[intLimbCounter].dblAngle + Math.PI);

									cBmpPtTLInfo.hingePositions[intLimbCounter].pt = new Point(-cBmpPtTLInfo.hingePositions[intLimbCounter].pt.X,
																							   -cBmpPtTLInfo.hingePositions[intLimbCounter].pt.Y);
								}
								break;
						}
						// calculate radial positions of each limb & hinge, and limb angle for mirror of original unmirrored/unrotated bmp
						for (int intLimbCounter = 0; intLimbCounter < cBmpPtTLInfo.limbPositions.Length; intLimbCounter++)
						{
							cBmpPtTLInfo.limbPositions[intLimbCounter].cRad = new classMath.classRadialCoor(classMath.arcTan(cBmpPtTLInfo.limbPositions[intLimbCounter].pt.X, cBmpPtTLInfo.limbPositions[intLimbCounter].pt.Y),
																											classMath.getRootOfSquares(cBmpPtTLInfo.limbPositions[intLimbCounter].pt.X, cBmpPtTLInfo.limbPositions[intLimbCounter].pt.Y));
							cBmpPtTLInfo.hingePositions[intLimbCounter].cRad = new classMath.classRadialCoor(classMath.arcTan(cBmpPtTLInfo.hingePositions[intLimbCounter].pt.X, cBmpPtTLInfo.hingePositions[intLimbCounter].pt.Y),
																											 classMath.getRootOfSquares(cBmpPtTLInfo.hingePositions[intLimbCounter].pt.X, cBmpPtTLInfo.hingePositions[intLimbCounter].pt.Y));
						}
						// cache the image
						setBmpInfoImage(ref cBmpPtTLInfo, ref Configurations[intConfigurationIndex].LimbDrawSequence, 0, 1.0, mirror);

						//if (cFileStream.bolIAmCached)
							classSpriteFileStream.Cache(ref cBmpPtTLInfo, new classCacheTreeNodeKeys(cCacheKey.strFilename, cCacheKey.ConfigIndex, cCacheKey.Step, 0, 1.0, mirror));

						// resize, cache resized image and return
						if (DisplaySize != 1.0)
						{
							cBmpPtTLInfo = cBmpPtTLInfo.Copy();
							setBmpInfoImage(ref cBmpPtTLInfo, ref Configurations[intConfigurationIndex].LimbDrawSequence, 0,DisplaySize, mirror);
							if (cFileStream.bolIAmCached)
							{
								cCacheKey = new classCacheTreeNodeKeys(cFileStream.strFilename, intConfigurationIndex, intStep, 0, DisplaySize, mirror);
								classSpriteFileStream.Cache(ref cBmpPtTLInfo, cCacheKey);
							}
						}
						return cBmpPtTLInfo;
					}
					else
					{  // get unrotated mirror image 
						cBmpPtTLInfo = getImageOnBitmap(intCurrent_Configuration, intCurrent_ConfigurationStep, 0, 1.0, mirror, false).Copy();
						setBmpInfoImage(ref cBmpPtTLInfo, ref Configurations[intConfigurationIndex].LimbDrawSequence, Angle, DisplaySize, mirror);
						if (cFileStream.bolIAmCached)
						{
							cCacheKey = new classCacheTreeNodeKeys(cFileStream.strFilename, intConfigurationIndex, intStep, Angle, dblDisplaySize, mirror);
							classSpriteFileStream.Cache(ref cBmpPtTLInfo,
															cCacheKey);
						}
						return cBmpPtTLInfo;
					}
			}
		}

        bool bolMakeSpriteTransparent = true;
        public bool MakeSpriteTransparent
        {
            get { return bolMakeSpriteTransparent; }
            set { bolMakeSpriteTransparent = value; }
        }

        /// <summary>
        /// this function receives a limb which has already been angled and positioned
        /// this limb then places all of its slaves limbs by setting their respective positions & angles
        /// then calls itself once for each slave that recurses with each of these limbs
        /// </summary>
        /// <param name="thisLimb">reference to this recursion's master-limb which will recurse for each of its slave limbs</param>
        /// <param name="dblLocalSizeFactor">factor by which original image is multiplied when calculating positions of slave limbs</param>
        public void putImageOntoScreen_recursion(ref classSpriteLimb thisLimb,
                                                     double dblLocalSizeFactor)
        {
            thisLimb.setDir();
            thisLimb.myCurrentImage = thisLimb.getLimbImage();

            thisLimb.recDest = new Rectangle(new Point(thisLimb.Loc.X - (int)(thisLimb.myCurrentImage.Width * dblLocalSizeFactor / 2),
                                                       thisLimb.Loc.Y - (int)(thisLimb.myCurrentImage.Height * dblLocalSizeFactor / 2)),
                                             new Size((int)((double)thisLimb.myCurrentImage.Size.Width * dblLocalSizeFactor),
                                                      (int)((double)thisLimb.myCurrentImage.Size.Height * dblLocalSizeFactor)));
            if (thisLimb.recDest.Width < 1)
                thisLimb.recDest.Width = 1;
            if (thisLimb.recDest.Height < 1)
                thisLimb.recDest.Height = 1;

            classConfigurationStepHinge[] udrHingeDetails = Configurations[intCurrent_Configuration].steps[intCurrent_ConfigurationStep].hingeDetails;

            if (thisLimb.mySlaveLimbs != null)
                for (int intSlaveCounter = 0; intSlaveCounter < thisLimb.mySlaveLimbs.Length; intSlaveCounter++)
                {
                    classSpriteHinge cMasterHinge = udrHingeDetails[thisLimb.mySlaveLimbs[intSlaveCounter].intMyLimbID].hinge;
                    classMath.classRadialCoor cRadMH_MasterRad = udrHingeDetails[thisLimb.mySlaveLimbs[intSlaveCounter].intMyLimbID].RadCoor_Master;
                    classMath.classRadialCoor cRadMH_SlaveRad= udrHingeDetails[thisLimb.mySlaveLimbs[intSlaveCounter].intMyLimbID].RadCoor_Slave;
                   
                    double dblNewAngle = classMath.cleanAngle(thisLimb.Angle
                                                              + cMasterHinge.Angle);
                    
                    thisLimb.mySlaveLimbs[intSlaveCounter].Angle = dblNewAngle;
                    double dblAngleOfHingeAwayFromMaster =
                        classMath.cleanAngle((cRadMH_MasterRad.radius > 1
                                                                ? cRadMH_MasterRad.angle
                                                                : 0)
                                             + thisLimb.Angle);
                    cMasterHinge.Loc = new Point(thisLimb.Loc.X + (int)(cRadMH_MasterRad.radius * dblLocalSizeFactor * Math.Cos(dblAngleOfHingeAwayFromMaster )),
                                                 thisLimb.Loc.Y + (int)(cRadMH_MasterRad.radius * dblLocalSizeFactor * Math.Sin(dblAngleOfHingeAwayFromMaster)));
                    double dblAngleAwayFromHingeLoc = classMath.cleanAngle((cRadMH_SlaveRad.radius > 1
                                                                                                    ? cRadMH_SlaveRad.angle + Math.PI
                                                                                                    : 0)
                                                                           + thisLimb.mySlaveLimbs[intSlaveCounter].Angle);

                    thisLimb.mySlaveLimbs[intSlaveCounter].Loc = new Point(cMasterHinge.Loc.X + (int)(cRadMH_SlaveRad.radius * dblLocalSizeFactor * Math.Cos(dblAngleAwayFromHingeLoc)),
                                                                           cMasterHinge.Loc.Y + (int)(cRadMH_SlaveRad.radius * dblLocalSizeFactor * Math.Sin(dblAngleAwayFromHingeLoc)));

                    putImageOntoScreen_recursion(ref  thisLimb.mySlaveLimbs[intSlaveCounter],
                                                      dblLocalSizeFactor);
                }
        }

        /// <summary>
        /// returns a pointer to the limb identified by name
        /// </summary>
        /// <param name="LimbName">name of the limb to be searched</param>
        /// <returns>returns a pointer to an object of type classSpriteLimb</returns>
        public Sprite.classSpriteLimb getLimbByName(string LimbName)
        {
            LimbName = LimbName.Trim().ToUpper();
            for (int intLimbCounter =0; intLimbCounter < limbs.Length; intLimbCounter++)
            {
                if (limbs[intLimbCounter].Name.Trim().ToUpper() == LimbName)
                    return limbs[intLimbCounter];
            }
            if (MasterLimb.Name.Trim().CompareTo(LimbName) == 0)
                return MasterLimb;
            return null;
        }

      

        /// <summary>
        /// returns a pointer to the hinge identified by name
        /// </summary>
        /// <param name="HingeName">name of hinge to be searched</param>
        /// <returns>returns a pointer to an object of type classSpriteHinge </returns>
        public Sprite.classSpriteHinge getHingeByName(string HingeName)
        {
            HingeName = HingeName.ToUpper();
            for (int intHingeCounter = 0; intHingeCounter < hinges.Length; intHingeCounter++)
                if (hinges[intHingeCounter].name.ToUpper().CompareTo(HingeName) == 0)
                    return hinges[intHingeCounter];
            return null;
        }

        public Sprite.classConfiguration getConfigurationByName(string ConfigurationName)
        {
            ConfigurationName = ConfigurationName.Trim().ToUpper();
            for (int intConfigurationCounter = 0; intConfigurationCounter < Configurations.Length; intConfigurationCounter++)
                if (Configurations[intConfigurationCounter].name.Trim().ToUpper().CompareTo(ConfigurationName) == 0)
                    return Configurations[intConfigurationCounter];
            return null;
        }

        public int getIndexOfConfigurationByName(string ConfigurationName)
        {
            if (Configurations == null)
                return -1;
            ConfigurationName = ConfigurationName.Trim().ToUpper();
            for (int intConfigurationCounter = 0; intConfigurationCounter < Configurations.Length; intConfigurationCounter++)
                if (Configurations[intConfigurationCounter].name.Trim().ToUpper().CompareTo(ConfigurationName) == 0)
                    return intConfigurationCounter;
            return -1;
        }

        public int getIndexOfHingeByName(string hingeName)
        {
            hingeName = hingeName.Trim().ToUpper();
            for (int intHingeCounter = 0; intHingeCounter < hinges.Length; intHingeCounter++)
                if (hinges[intHingeCounter].name.Trim().ToUpper().CompareTo(hingeName) == 0)
                    return intHingeCounter;
            return -1;
        }

        public int getIndexOfLimbByName(string limbName)
        {
            limbName = limbName.Trim().ToUpper();
            for (int intlimbCounter = 0; intlimbCounter < hinges.Length; intlimbCounter++)
                if (hinges[intlimbCounter].Limb_Slave.Name.Trim().ToUpper().CompareTo(limbName) == 0)
                    return intlimbCounter;
            return -1;
        }
    }

    public class classPixilateColorFind
    {
        public Color clr;
        public int numFound;

        static classPixilateColorFind[] cPreviouslyCreated = new classPixilateColorFind[0];
        static int intCurrentUse;
        public static classPixilateColorFind getPixilateColorFind()
        {
            if (cPreviouslyCreated.Length > 0 && intCurrentUse < cPreviouslyCreated.Length)
            {
                return cPreviouslyCreated[intCurrentUse++];
            }
            else
            {
                Array.Resize<classPixilateColorFind>(ref cPreviouslyCreated, cPreviouslyCreated.Length + 1);
                cPreviouslyCreated[cPreviouslyCreated.Length - 1] = new classPixilateColorFind();
                return cPreviouslyCreated[intCurrentUse++];
            }
        }

        public static void clear()
        {
            intCurrentUse = 0;
        }
    }

	public class classSpriteComposite
	{
		public classSpriteCompositeElement[] cSpriteCompositeElementArray = new classSpriteCompositeElement[0];
		public classSpriteCompositeElement cMaster;
		public int[] intDrawSequence = new int[0];
		public Point ptTL;

		public classSpriteComposite(ref classSprite cSpriteMaster)
		{
			classSpriteComposite cMyReference = this;
			cMaster = new classSpriteCompositeElement(ref cMyReference);
			cMaster.cSprite = cSpriteMaster;
			intDrawSequence = new int[1];
			intDrawSequence[0] = 0;
		}

		public void animate()
		{
			for (int intSpriteCounter = 0; intSpriteCounter < cSpriteCompositeElementArray.Length; intSpriteCounter++)
			{
				classSpriteCompositeElement cThisCompEle = cSpriteCompositeElementArray[intSpriteCounter];
				classSprite cSpr = cThisCompEle.cSprite;
				cThisCompEle.intConfigurationStep = (cThisCompEle.intConfigurationStep + 1) % cSpr.Configurations[cThisCompEle.intConfigurationIndex].steps.Length;
			}
		}

		public Bitmap drawComposite(double dblAngle, double dblSize, enuMirror eMirror)
		{
			cMaster.Mirror = eMirror;
			cMaster.Angle = dblAngle;
			for (int intCECounter = 0; intCECounter < cSpriteCompositeElementArray.Length; intCECounter++)
			{
				classSpriteCompositeElement cThisCE = cSpriteCompositeElementArray[intCECounter];
				cThisCE.cBmp = cThisCE.cSprite.getImageOnBitmap(cThisCE.intConfigurationIndex, cThisCE.intConfigurationStep,cThisCE.Angle,dblSize, cThisCE.Mirror, false);
			}
			ptTL =new Point( -cSpriteCompositeElementArray[0].cBmp.ptTL.X,  -cSpriteCompositeElementArray[0].cBmp.ptTL.Y);
			Point ptBR = new Point(ptTL.X + cSpriteCompositeElementArray[0].cBmp.bmp.Width , ptTL.Y + cSpriteCompositeElementArray[0].cBmp.bmp.Height);
			cMaster.ptTL = new Point(0, 0);
			cMaster.PositionSlaves(ref ptTL, ref ptBR);
			cMaster.ptTL = new Point(0,0);
			Size sz = new Size(ptBR.X - ptTL.X, ptBR.Y - ptTL.Y);
			Bitmap bmpRetVal = new Bitmap(sz.Width, sz.Height);
			ptTL.X *= -1; ptTL.Y *= -1;
			using (Graphics g = Graphics.FromImage(bmpRetVal))
			{
				g.FillRectangle(classPensAndBrushes.brWhite, new Rectangle(0,0, bmpRetVal.Width, bmpRetVal.Height));
				for (int intIndexCounter = 0; intIndexCounter < intDrawSequence.Length; intIndexCounter++)
				{
					classSpriteCompositeElement cThisCE = cSpriteCompositeElementArray[intDrawSequence[intIndexCounter]];
					Rectangle recSrc = new Rectangle(0, 0, cThisCE.cBmp.bmp.Width , cThisCE.cBmp.bmp.Height);
					Rectangle recDest = new Rectangle(ptTL.X + cThisCE.ptTL.X - cThisCE.cBmp.ptTL.X,
													  ptTL.Y + cThisCE.ptTL.Y - cThisCE.cBmp.ptTL.Y,
													  cThisCE.cBmp.bmp.Width,
													  cThisCE.cBmp.bmp.Height);
					g.DrawImage(cThisCE.cBmp.bmp, recDest, recSrc, GraphicsUnit.Pixel);
				}
			}
			bmpRetVal.MakeTransparent();
			return bmpRetVal;
		}
	}

	public class classSpriteCompositeElement
	{
		public classSprite cSprite;
		public classSpriteComposite cComposite;
		public classSpriteCompositeElement cMaster;

		int intIndexMasterLimbLink;
		public int IndexMasterLimbLink
		{
			get { return intIndexMasterLimbLink; }
			set
			{
				if (value >= 0 && value < cMaster.cSprite.limbs.Length)
				{
					intIndexMasterLimbLink = value;
					cSprite.limStationary = cMaster.cSprite.limbs[intIndexMasterLimbLink];
				}
			}
		}
		public bool bolAngleTracksMaster = false;
		double dblAngle = 0;
		public double Angle
		{
			get { return dblAngle; }
			set
			{
				dblAngle = value;
				for (int intMySlaves = 0; intMySlaves < cSlaves.Length; intMySlaves++)
					if (cSlaves[intMySlaves].bolAngleTracksMaster)
						cSlaves[intMySlaves].Angle = dblAngle;
			}
		}
		public int intConfigurationIndex;
		public int intConfigurationStep;
		public double dblSizeRelativeToMaster = 1.0;
		public classBmpAndPtTLInfo cBmp = new classBmpAndPtTLInfo();
		public Point ptTL;

		public bool bolMirrorFollowsMaster = true; 
		enuMirror eMirror = enuMirror.none;
		public enuMirror Mirror
		{
			get { return eMirror; }
			set
			{
				eMirror = value;
				for (int intMySlaves = 0; intMySlaves < cSlaves.Length; intMySlaves++)
				{
					if (cSlaves[intMySlaves].bolMirrorFollowsMaster)
						cSlaves[intMySlaves].Mirror = eMirror;
				}
			}
		}

		public classSpriteCompositeElement[] cSlaves = new classSpriteCompositeElement[0];
		public int intMyIndex;		

		public classSpriteCompositeElement(ref classSpriteComposite cSpriteComposite)
		{
			cComposite = cSpriteComposite;
			Array.Resize<classSpriteCompositeElement>(ref cComposite.cSpriteCompositeElementArray, cComposite.cSpriteCompositeElementArray.Length + 1);
			cComposite.cSpriteCompositeElementArray[cComposite.cSpriteCompositeElementArray.Length - 1] = this;
			intMyIndex = cComposite.cSpriteCompositeElementArray.Length - 1;

			intMyDrawSequenceIndex = cComposite.intDrawSequence.Length;
			Array.Resize<int>(ref cComposite.intDrawSequence, cComposite.intDrawSequence.Length + 1);
			cComposite.intDrawSequence[cComposite.intDrawSequence.Length - 1] = intMyIndex;

			
		}

		int intMyDrawSequenceIndex = -1;
		public int myDrawSequenceIndex
		{
			get { return intMyDrawSequenceIndex; }
			set
			{
				if (value >= 0 && value < cComposite.intDrawSequence.Length && value != intMyDrawSequenceIndex)
				{
					if (value < intMyDrawSequenceIndex)
					{
						for (int intIndexCounter = intMyDrawSequenceIndex; intIndexCounter > value; intIndexCounter--)
							cComposite.intDrawSequence[intIndexCounter - 1] = cComposite.intDrawSequence[intIndexCounter];
					}
					else
					{
						for (int intIndexCounter = intMyDrawSequenceIndex; intIndexCounter < value; intIndexCounter++)
							cComposite.intDrawSequence[intIndexCounter + 1] = cComposite.intDrawSequence[intIndexCounter];
					}
				}
				intMyDrawSequenceIndex = value;
				cComposite.intDrawSequence[intMyDrawSequenceIndex] = intMyIndex;
			}
		}
		public void addSlave(ref classSprite cSprite_NewSlave, int intIndexMasterLimbLink, double dblDrawSizeRelativeToMaster)
		{
			Array.Resize<classSpriteCompositeElement>(ref cSlaves, cSlaves.Length + 1);
			classSpriteCompositeElement cNewSlave = new classSpriteCompositeElement(ref cComposite);

			cNewSlave.cComposite = cComposite;
			cNewSlave.cSprite = cSprite_NewSlave;
			cNewSlave.cMaster = this;
			cNewSlave.dblSizeRelativeToMaster = dblDrawSizeRelativeToMaster;
			cNewSlave.intIndexMasterLimbLink = intIndexMasterLimbLink;

			cSlaves[cSlaves.Length - 1] = cNewSlave;
			rebuildIndex();
		}

		public void removeSlave(ref classSpriteCompositeElement cSprite_Remove)
		{
			for (int intSlaveCounter = 0; intSlaveCounter < cSlaves.Length; intSlaveCounter++)
			{
				if (cSlaves[intSlaveCounter] == cSprite_Remove)
				{
					for (int intSlavesSlavesCounter = cSprite_Remove.cSlaves.Length; intSlavesSlavesCounter >= 0; intSlavesSlavesCounter--)
						cSprite_Remove.removeSlave(ref cSprite_Remove.cSlaves[intSlavesSlavesCounter]);
					cSlaves[intSlaveCounter] = cSlaves[cSlaves.Length - 1];
					Array.Resize<classSpriteCompositeElement>(ref cSlaves, cSlaves.Length - 1);
					break;
				}
			}
			for (int intSpriteCounter = 0; intSpriteCounter < cComposite.cSpriteCompositeElementArray.Length; intSpriteCounter++)
			{
				if (cComposite.cSpriteCompositeElementArray[intSpriteCounter] == cSprite_Remove)
				{
					cComposite.cSpriteCompositeElementArray[intSpriteCounter] = cComposite.cSpriteCompositeElementArray[cComposite.cSpriteCompositeElementArray.Length - 1];
					Array.Resize<classSpriteCompositeElement>(ref cComposite.cSpriteCompositeElementArray, cComposite.cSpriteCompositeElementArray.Length - 1);
					break;
				}
			}
			rebuildIndex();
		}

		void rebuildIndex()
		{
			for (int intSpriteCounter = 0; intSpriteCounter < cComposite.cSpriteCompositeElementArray.Length; intSpriteCounter++ )
			{
				cComposite.cSpriteCompositeElementArray[intSpriteCounter].intMyIndex = intSpriteCounter;
			}
		}

		public void PositionSlaves(ref Point ptFrame_TL, ref Point ptFrame_BR)
		{			
			for (int intSlaveCounter = 0; intSlaveCounter < cSlaves.Length; intSlaveCounter++)
			{
				classSpriteCompositeElement cThisSlave = cSlaves[intSlaveCounter];
				cThisSlave.ptTL = new Point(cBmp.limbPositions[cThisSlave.intIndexMasterLimbLink].pt.X + ptTL.X,
											cBmp.limbPositions[cThisSlave.intIndexMasterLimbLink].pt.Y + ptTL.Y);
				
				cThisSlave.PositionSlaves(ref ptFrame_TL, ref ptFrame_BR);
			}
			if (ptFrame_TL.X > ptTL.X - cBmp.ptTL.X)
				ptFrame_TL.X = ptTL.X - cBmp.ptTL.X;
			if (ptFrame_TL.Y > ptTL.Y - cBmp.ptTL.Y)
				ptFrame_TL.Y = ptTL.Y - cBmp.ptTL.Y;
			if (ptFrame_BR.X < ptTL.X - cBmp.ptTL.X + cBmp.bmp.Width)
				ptFrame_BR.X = ptTL.X - cBmp.ptTL.X + cBmp.bmp.Width;
			if (ptFrame_BR.Y < ptTL.Y - cBmp.ptTL.Y + cBmp.bmp.Height)
				ptFrame_BR.Y = ptTL.Y - cBmp.ptTL.Y + cBmp.bmp.Height;
		}

		/// <summary>
		/// generates the recDest required to place this CE's cBmp onto the Sprite-composite's output image
		/// </summary>
		/// <returns></returns>
		public Rectangle getDrawRectangle()
		{
			Rectangle recRetVal = new Rectangle();

			

			return recRetVal;
		}
	}

	
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
CEO unemployable
Canada Canada
Christ Kennedy grew up in the suburbs of Montreal and is a bilingual Quebecois with a bachelor’s degree in computer engineering from McGill University. He is unemployable and currently living in Moncton, N.B. writing his next novel.

Comments and Discussions