Click here to Skip to main content
15,891,136 members
Articles / Multimedia / GDI+

Rotating Picture Tray

Rate me:
Please Sign up or sign in to vote.
4.29/5 (9 votes)
17 Nov 2009CPOL10 min read 60.4K   1.3K   34  
Allows the user to view a collection of pictures by selecting them from a rotating tray.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

public class cLibPicTray
{

    [Serializable]
    public struct udtTrayLoc
    {
        public double angle;
        public double x;
        public double y;
    }

    [Serializable]
    public struct udtPicBoxTrayElement
    {
        public classPicBox picBox;
        public udtTrayLoc trayLoc;
        public double dblXOverYRatio;
    }

    public struct udtCartesian
    {
        public double x;
        public double y;
    }

    public enum enuTurning { Clockwise = -1, notTurning, CounterClockwise }

    public udtPicBoxTrayElement[] udrPicBoxTrayElements;

    public double dblCurrentAngle;
    double dblMinSizeFraction = 0.5;
    udtCartesian udrMaxSizeFraction = new udtCartesian();
    udtCartesian udrCenterFraction = new udtCartesian();
    double dblTurnAngleStep;
    enuTurning turningAngle;

    double dblCircleRadius;
    double dblCircleRadiusFactor;

    public System.Windows.Forms.PictureBox picBackground = new System.Windows.Forms.PictureBox();

    udtCartesian udrMaxSize; // maximum picture size
    udtCartesian udrCenter;  // center of the circle
    udtCartesian udrMouse;
    public int intSelected;
    public bool bolMouseMoveOverAutoSelect = false;
    public System.Threading.Thread clickPicThread;
    public bool bolClickPicBool;
    public bool bolPopUpClickedPic = false;
    public bool bolCarousel = false;


    public System.Windows.Forms.Timer tmrRotate = new System.Windows.Forms.Timer();
    public cLibPicTray()
    {
        picBackground.SizeChanged += new EventHandler(picBackground_SizeChanged);
        tmrRotate.Tick += new EventHandler(tmrRotate_Tick);

        resetStandardParameters();

        udrPicBoxTrayElements = null;
        turningAngle = enuTurning.notTurning;

        handle_SizeChange();
    }

    public void resetStandardParameters()
    {
        dblTurnAngleStep = Math.PI / 64;
        setCircleRadius(1.5);
        setMaxHeightFraction(0.7);
        setMaxWidthFraction(1);
        setMinSizeFraction(0.2);
        setCenterFraction_X(0.5);
        setCenterFraction_Y(0.15);
        bolMouseMoveOverAutoSelect = true;
        bolClickPicBool = false;
        bolPopUpClickedPic = false;
    }

    public void setCenterFraction_X(double dblFractionFromLeftOfPanel)
    { udrCenterFraction.x = dblFractionFromLeftOfPanel; }

    public void setCenterFraction_Y(double dblFractionFromTopOfPanel)
    { udrCenterFraction.y = dblFractionFromTopOfPanel; }

    public void setBackGroundImage(string strFilename)
    { picBackground.Load(strFilename); }

    public void setBackGroundImage(System.Drawing.Image  imgLocal)
    { picBackground.Image = imgLocal; }

    public void setCircleRadius(double dblFactor)
    { dblCircleRadiusFactor = dblFactor; }

    public void setMinSizeFraction(double dblMinSize)
    {
        if (dblMinSizeFraction < 0 || dblMinSizeFraction > 1)
            return;
        dblMinSizeFraction = dblMinSize;
    }

    public void setMaxWidthFraction(double dblFractionOfPanelWidth)
    { udrMaxSizeFraction.x = dblFractionOfPanelWidth; }

    public void setMaxHeightFraction(double dblFractionOfPanelHeight)
    { udrMaxSizeFraction.y = dblFractionOfPanelHeight; }




    int getSelectedImage(System.Windows.Forms.MouseEventArgs e)
    {
        udrMouse.x = e.X; udrMouse.y = e.Y;

        // set current image selected by mouse position - front to back
        int[] intRankingIndices = getRankingIndices();
        if (intRankingIndices == null)
            return -1;
        int intLocalSelected = -1;

        for (int intPicCounter = intRankingIndices.Length - 1; intPicCounter >= 0; intPicCounter--)
        {
            if (udrMouse.x > udrPicBoxTrayElements[intRankingIndices[intPicCounter]].picBox.Left
                && udrMouse.x < udrPicBoxTrayElements[intRankingIndices[intPicCounter]].picBox.Left + udrPicBoxTrayElements[intRankingIndices[intPicCounter]].picBox.Width)
                if (udrMouse.y > udrPicBoxTrayElements[intRankingIndices[intPicCounter]].picBox.Top
                    && udrMouse.y < udrPicBoxTrayElements[intRankingIndices[intPicCounter]].picBox.Top + udrPicBoxTrayElements[intRankingIndices[intPicCounter]].picBox.Height)
                {
                    intLocalSelected = intRankingIndices[intPicCounter];
                    break;
                }
        }
        return intLocalSelected;
    }


    void handle_PopUpPic(int intSelected)
    {
        try
        {
            cLibPicBoxViewer cLibPicBox = new cLibPicBoxViewer (udrPicBoxTrayElements[intSelected].picBox.Image, "");
            cLibPicBox.resizeToFitImage();
        }
        catch (Exception) { }
    }

    void handle_newSelection()
    {
        if (intSelected >= udrPicBoxTrayElements.Length)
            intSelected = udrPicBoxTrayElements.Length - 1;
        if (intSelected >= 0)
        {
            if (udrPicBoxTrayElements[intSelected].trayLoc.x > 0)
                turningAngle = enuTurning.Clockwise;
            else
                turningAngle = enuTurning.CounterClockwise;
            tmrRotate.Enabled = true;
        }
    }

    void tmrRotate_Tick(object sender, EventArgs e)
    {
        dblCurrentAngle += (int)turningAngle * dblTurnAngleStep;
        if (dblCurrentAngle < 0)
            dblCurrentAngle += (Math.PI * 2);
        else if (dblCurrentAngle > 2 * Math.PI)
            dblCurrentAngle -= (Math.PI * 2);
        repositionTray();
    }

    public void setTurningPeriod(int intDelayInMilliseconds)
    {
        if (intDelayInMilliseconds > 0)
        {
            tmrRotate.Interval = intDelayInMilliseconds;
            tmrRotate.Enabled = true;
        }
        else
            tmrRotate.Enabled = false;
    }

    public void setTurningAngularStepSize(double dblAngularStepSize)
    {
        dblTurnAngleStep = Math.Sign(dblTurnAngleStep) * dblAngularStepSize;
        tmrRotate.Enabled = (dblTurnAngleStep != 0);
    }

    public void setRotatingDirection(enuTurning turningDirection)
    {
        turningAngle = turningDirection;
        tmrRotate.Enabled = (turningDirection != enuTurning.notTurning);
        intSelected = -1;
    }

    void picBackground_SizeChanged(object sender, EventArgs e)
    { handle_SizeChange(); }

    public void handle_SizeChange()
    {
        udrMaxSize.x = (picBackground.Width * udrMaxSizeFraction.x);
        udrMaxSize.y = picBackground.Height * udrMaxSizeFraction.y;
        udrCenter.x = picBackground.Width * udrCenterFraction.x;
        udrCenter.y = picBackground.Height * udrCenterFraction.y;

        double dblMaxWidthCircle = picBackground.Width * 0.5;
        double dblMaxHeightCircle = picBackground.Height * 0.5;
        if (dblMaxHeightCircle < dblMaxWidthCircle)
            dblCircleRadius = dblMaxHeightCircle / 5;
        else
            dblCircleRadius = dblMaxWidthCircle / 5;
        dblCircleRadius *= dblCircleRadiusFactor;
        repositionTray();
    }

    public void deletePicture()
    { deletePicture(intSelected); }

    public void deletePicture(int intIndexToDelete)
    {
        if (intIndexToDelete >= 0 && intIndexToDelete < udrPicBoxTrayElements.Length)
        {
            udrPicBoxTrayElements[intIndexToDelete].picBox.Dispose();
            for (int intPicCounter = intIndexToDelete; intPicCounter < udrPicBoxTrayElements.Length - 1; intPicCounter++)
                udrPicBoxTrayElements[intPicCounter] = udrPicBoxTrayElements[intPicCounter + 1];
            Array.Resize<udtPicBoxTrayElement>(ref udrPicBoxTrayElements, udrPicBoxTrayElements.Length - 1);
        }
        repositionTray();
        handle_newSelection();
    }

    public void addPicBox(ref System.Windows.Forms.PictureBox PicThisBox_new)
    {
        if (udrPicBoxTrayElements == null)
            udrPicBoxTrayElements = new udtPicBoxTrayElement[1];
        else
            Array.Resize<udtPicBoxTrayElement>(ref udrPicBoxTrayElements, udrPicBoxTrayElements.Length + 1);
        classPicBox PicThisBox = new classPicBox();
        PicThisBox.Image = PicThisBox_new.Image;
        udrPicBoxTrayElements[udrPicBoxTrayElements.Length - 1].picBox = PicThisBox;
        udrPicBoxTrayElements[udrPicBoxTrayElements.Length - 1].picBox.index = udrPicBoxTrayElements.Length - 1;
        picBackground.Controls.Add(udrPicBoxTrayElements[udrPicBoxTrayElements.Length - 1].picBox);
        PicThisBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
        udrPicBoxTrayElements[udrPicBoxTrayElements.Length - 1].dblXOverYRatio = (double)udrPicBoxTrayElements[udrPicBoxTrayElements.Length - 1].picBox.Width / (double)udrPicBoxTrayElements[udrPicBoxTrayElements.Length - 1].picBox.Height;

        PicThisBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
        handle_SizeChange();
        intSelected = udrPicBoxTrayElements.Length - 1;
        repositionTray();
        handle_newSelection();
        udrPicBoxTrayElements[udrPicBoxTrayElements.Length - 1].picBox.MouseMove += new System.Windows.Forms.MouseEventHandler(picBox_MouseMove);
        udrPicBoxTrayElements[udrPicBoxTrayElements.Length - 1].picBox.MouseClick += new System.Windows.Forms.MouseEventHandler(picBox_MouseClick);
    }

    void picBox_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e)
    {
        classPicBox pb = (classPicBox)sender;
        intSelected = pb.index;
        bolClickPicBool = true;
        handle_newSelection();
        if (clickPicThread != null)
            clickPicThread.Start();
        if (bolPopUpClickedPic)
            handle_PopUpPic(intSelected);
    }

    void picBox_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
    {
        classPicBox pb = (classPicBox)sender;
        if (bolMouseMoveOverAutoSelect)
        {
            int intTempSelected = pb.index;
            if (intTempSelected >= 0 & intTempSelected != intSelected)
            {
                intSelected = intTempSelected;
                handle_newSelection();
            }
        }
    }

    //void lblTransparentLabel_MouseClick(object sender, MouseEventArgs e)
    //{            
    //    intSelected = getSelectedImage(e);
    //    bolClickPicBool = true;
    //    handle_newSelection();
    //    if (clickPicThread != null)
    //        clickPicThread.Start();
    //    if (bolPopUpClickedPic)
    //        handle_PopUpPic(intSelected);
    //}

    //void lblTransparentLabel_MouseMove(object sender, MouseEventArgs e)
    //{
    //    if (bolMouseMoveOverAutoSelect)
    //    {
    //        int intTempSelected = getSelectedImage(e);
    //        if (intTempSelected >= 0 & intTempSelected != intSelected )
    //        {
    //            intSelected = intTempSelected;
    //            handle_newSelection();
    //        }
    //    }
    //}

    private int[] getRankingIndices()
    {
        // returns array of indices of elements as they appear back to front 
        //(nearest top of first first in order until reach the one nearest the bottom of the form as measured around unit circle)

        int[] intRetVal;
        //= new int[udrPicBoxTrayElements.Length];
        try
        { intRetVal = new int[udrPicBoxTrayElements.Length]; }
        catch (Exception) { return null; }
        // rank them front to back
        for (int intPicBoxCounter = 0; intPicBoxCounter < udrPicBoxTrayElements.Length; intPicBoxCounter++)
            intRetVal[intPicBoxCounter] = intPicBoxCounter;
        int intBestIndex, intTemp, intInnerLoop, intOuterLoop;
        double dblBestValue;
        for (intOuterLoop = 0; intOuterLoop < intRetVal.Length - 1; intOuterLoop++)
        {
            intBestIndex = intOuterLoop;
            dblBestValue = udrPicBoxTrayElements[intRetVal[intOuterLoop]].trayLoc.y;
            for (intInnerLoop = intOuterLoop + 1; intInnerLoop < intRetVal.Length; intInnerLoop++)
            {
                if (dblBestValue < udrPicBoxTrayElements[intRetVal[intInnerLoop]].trayLoc.y)
                {
                    dblBestValue = udrPicBoxTrayElements[intRetVal[intInnerLoop]].trayLoc.y;
                    intBestIndex = intInnerLoop;
                }
            }
            if (intBestIndex != intOuterLoop)
            {
                intTemp = intRetVal[intOuterLoop];
                intRetVal[intOuterLoop] = intRetVal[intBestIndex];
                intRetVal[intBestIndex] = intTemp;
            }
        }
        return intRetVal;
    }

    public void repositionTray()
    {
        double dblDeltaAngle;
        //lblTransparentLabel.Visible = false;

        if (udrPicBoxTrayElements == null)
            return;
        else if (udrPicBoxTrayElements.Length == 0)
            return;

        // distribute pics around the circle evenly : set their respective angles around the circle
        if (udrPicBoxTrayElements.Length - 1 > 0)
        {
            dblDeltaAngle = (2 * Math.PI) / (udrPicBoxTrayElements.Length);
            for (int intPicBoxCounter = 0; intPicBoxCounter < udrPicBoxTrayElements.Length; intPicBoxCounter++)
                udrPicBoxTrayElements[intPicBoxCounter].trayLoc.angle = Math.PI / 2 + intPicBoxCounter * dblDeltaAngle;
        }
        else
            udrPicBoxTrayElements[0].trayLoc.angle = Math.PI / 2;

        int intXSignOfSelectedPicBeforeMove = 0, intXSignOfSelectedPicAfterMove = 0;
        try
        { intXSignOfSelectedPicBeforeMove = Math.Sign(udrPicBoxTrayElements[intSelected].trayLoc.x); }
        catch (Exception)
        { }

        // place on the cartesian plane using the radius of circle and each picture's angle + circle rotation angle
        for (int intPicBoxCounter = 0; intPicBoxCounter < udrPicBoxTrayElements.Length; intPicBoxCounter++)
        {
            udrPicBoxTrayElements[intPicBoxCounter].trayLoc.x = Math.Cos(udrPicBoxTrayElements[intPicBoxCounter].trayLoc.angle + dblCurrentAngle - Math.PI / 2);
            udrPicBoxTrayElements[intPicBoxCounter].trayLoc.y = Math.Sin(udrPicBoxTrayElements[intPicBoxCounter].trayLoc.angle + dblCurrentAngle - Math.PI / 2);
        }

        int[] intRankingIndices = getRankingIndices();
        if (intRankingIndices == null)
            return;


        double dblMaxYReference = udrCenter.y + udrMaxSize.y;
        udtCartesian udrCirclePt;
        //create a ranking indice array to display them from back to front
        for (int intPicCounter = 0; intPicCounter < intRankingIndices.Length; intPicCounter++)
        {
            /// -   set their size accordingly (back is smaller, front is bigger) 
            double dblSizeAttenuationFactor; /// -1 is at the front, 1 is at the back
            double dblDistanceToFront = 1 + udrPicBoxTrayElements[intRankingIndices[intPicCounter]].trayLoc.y;
            // dblDistanceToFront ranges from 0(front) to 2(back)
            double dblFactorRange = 1 - dblMinSizeFraction;
            dblSizeAttenuationFactor = 1 - dblFactorRange * (dblDistanceToFront / 2);
            resizePic(ref udrPicBoxTrayElements[intRankingIndices[intPicCounter]], attenuateUdtCartesian(udrMaxSize, dblSizeAttenuationFactor));

            /// -   place them on the form (left,top)
            ///     - center the x value of pic loc on circle Pt, place top of pic on circle Pt
            udrCirclePt.x = udrCenter.x + dblCircleRadius * Math.Cos(udrPicBoxTrayElements[intRankingIndices[intPicCounter]].trayLoc.angle + dblCurrentAngle - Math.PI / 2);
            udrCirclePt.y = udrCenter.y - dblCircleRadius * Math.Sin(udrPicBoxTrayElements[intRankingIndices[intPicCounter]].trayLoc.angle + dblCurrentAngle - Math.PI / 2);
            udrPicBoxTrayElements[intRankingIndices[intPicCounter]].picBox.Left = (int)(udrCirclePt.x - udrPicBoxTrayElements[intRankingIndices[intPicCounter]].picBox.Width / 2);
            udrPicBoxTrayElements[intRankingIndices[intPicCounter]].picBox.Top = (int)(udrCirclePt.y);

            /// -   bring to front (will be covered by those that follow)
            udrPicBoxTrayElements[intRankingIndices[intPicCounter]].picBox.BringToFront();
        }

        try
        { picBackground.Refresh(); }
        catch (Exception)
        { }

        //try
        //{
        //    // the transparent label allows the user to click on the label
        //    // and select an image using the transparentLabel's event handlers
        //    lblTransparentLabel.BringToFront();
        //    lblTransparentLabel.Refresh();
        //    lblTransparentLabel.Visible = true;
        //}
        //catch (Exception)
        //{}

        try
        { intXSignOfSelectedPicAfterMove = Math.Sign(udrPicBoxTrayElements[intSelected].trayLoc.x); }
        catch (Exception)
        { }

        // when the user's selection crosses the X-axis and is last in the ranking indice array then 
        // it is in front and rotation can stop
        if (intSelected == intRankingIndices[intRankingIndices.Length - 1])
            if (intXSignOfSelectedPicAfterMove != intXSignOfSelectedPicBeforeMove)
                tmrRotate.Enabled = bolCarousel;
    }

    udtCartesian attenuateUdtCartesian(udtCartesian udrCar, double dblFactor)
    {
        udtCartesian udrRetVal = new udtCartesian();
        udrRetVal.x = udrCar.x * dblFactor;
        udrRetVal.y = udrCar.y * dblFactor;
        return udrRetVal;
    }

    void resizePic(ref udtPicBoxTrayElement udrThisTrayElement, udtCartesian udrMaxDimensions)
    {
        double dblMDXOverY = udrMaxDimensions.x / udrMaxDimensions.y;
        if (udrThisTrayElement.dblXOverYRatio > dblMDXOverY)
        {   // width is limiting factor
            udrThisTrayElement.picBox.Width = (int)udrMaxDimensions.x;
            udrThisTrayElement.picBox.Height = (int)(udrThisTrayElement.picBox.Width / udrThisTrayElement.dblXOverYRatio);
        }
        else
        {  // height is limiting factor
            udrThisTrayElement.picBox.Height = (int)udrMaxDimensions.y;
            udrThisTrayElement.picBox.Width = (int)(udrThisTrayElement.picBox.Height * udrThisTrayElement.dblXOverYRatio);
        }
    }

    public void resetPicTray()
    {
        for (int intPicCounter = 0; intPicCounter < udrPicBoxTrayElements.Length; intPicCounter++)
        {
            udrPicBoxTrayElements[intPicCounter].picBox.Visible = false;
            udrPicBoxTrayElements[intPicCounter].picBox.Dispose();
        }
        udrPicBoxTrayElements = null;

        dblTurnAngleStep = Math.PI / 64;
        dblMinSizeFraction = 0.5;
        udrMaxSizeFraction.x = udrMaxSizeFraction.y = 0.45;
        udrCenterFraction.x = 0.5;
        udrCenterFraction.y = 0.3;
        bolMouseMoveOverAutoSelect = false;
        bolClickPicBool = false;

        udrPicBoxTrayElements = null;
        turningAngle = enuTurning.notTurning;
    }
}

public class classPicBox : System.Windows.Forms.PictureBox
{
    public int index;
}

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