Click here to Skip to main content
15,885,309 members
Articles / Desktop Programming / Windows Forms

XNA Image Reflector. Easily Create Web2.0-like Reflected Images

Rate me:
Please Sign up or sign in to vote.
4.71/5 (39 votes)
23 Nov 2007CPOL7 min read 76.3K   1.5K   79  
This article describes XNAImageReflector - a Windows application that integrates XNA rendering and maths for easily creating web2.0-like reflected images, with post-processing effects.
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using XNA = Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using PrimitivesSample;
using System.Windows.Forms;

namespace SMX
{
    /// <summary>
    /// Author: I�aki Ayucar (http://graphicdna.blogspot.com)
    /// Date: 23/11/2007
    /// 
    /// This software is distributed "for free" for any non-commercial usage. The software is provided �as-is.� 
    /// You bear the risk of using it. The contributors give no express warranties, guarantees or conditions.
    /// </summary>
    public class ReflectionMark
    {
        public enum eMouseMode
        {
            MovePoint,
            MoveMark,
            CreatePoint,
            None,
        }

        private const int cRadiusNodes = 8;
        private const int cHalfRadiusNodes = (int)((float)cRadiusNodes * 0.5f);

        private Color mColor = XNA.Graphics.Color.DarkGray;
        private int mWidth, mHeight;
        public int Width
        {
            get { return mWidth; }
            set
            {
                mWidth = value;
                this.ClientSizeChanged();
            }
        }
        public int Height
        {
            get { return mHeight; }
            set
            {
                mHeight = value;
                this.ClientSizeChanged();
            }
        }

        public List<Vector2> mPoints = null;               
        private PrimitiveBatch mPrimitiveBatch = null;

        private System.Drawing.Point mLastMousePos;
        private eMouseMode mMouseMode = eMouseMode.None;
        private int mPointMovingIdx = -1;
        public eMouseMode MouseMode
        {
            get { return mMouseMode; }
            set
            {
                mMouseMode = value;

                switch (value)
                {
                    case eMouseMode.MovePoint:
                        if (mPointMovingIdx > 0 && mPointMovingIdx < mPoints.Count - 1)
                            Cursor.Current = Cursors.NoMove2D;
                        else Cursor.Current = Cursors.NoMoveVert;
                        break;
                    case eMouseMode.MoveMark:
                        Cursor.Current = Cursors.NoMoveVert;
                        break;
                    case eMouseMode.CreatePoint:
                        Cursor.Current = Cursors.Cross;
                        break;
                    default:
                        Cursor.Current = Cursors.Default;
                        break;
                }

                if (this.MouseModeChanged != null)
                    this.MouseModeChanged(this, EventArgs.Empty);
            }
        }

        public event EventHandler ReflectionMarkChanged = null;
        public event EventHandler ReflectionMarkChanging = null;
        public event EventHandler MouseModeChanged = null;


        /// <summary>
        /// 
        /// </summary>
        public ReflectionMark()
        {
            mPoints = new List<Vector2>();

            this.ResetMark();
        }
        /// <summary>
        /// 
        /// </summary>
        public void ResetMark()
        {
            this.mPoints.Clear();

            this.mPoints.Add(new Vector2());
            this.mPoints.Add(new Vector2());

            this.ClientSizeChanged();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        private void ClientSizeChanged()
        {
            if (this.mPoints == null || this.mPoints.Count == 0 || this.mWidth == 0 || this.mHeight == 0)
                return;

            if (this.mPoints[0] == Vector2.Zero)
                this.mPoints[0] = new Vector2(cHalfRadiusNodes, (float)this.mHeight * 0.5f);
            else this.mPoints[0] = new Vector2(cHalfRadiusNodes, this.mPoints[0].Y); ;

            if (this.mPoints[mPoints.Count - 1] == Vector2.Zero)
                this.mPoints[mPoints.Count - 1] = new Vector2(this.mWidth - cHalfRadiusNodes, (float)this.mHeight * 0.5f);
            else this.mPoints[mPoints.Count - 1] = new Vector2(this.mWidth - cHalfRadiusNodes, this.mPoints[mPoints.Count - 1].Y); ;
        }

        #region Device Events
        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
            if (this.mPrimitiveBatch != null)
                this.mPrimitiveBatch.Dispose();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="pDevice"></param>
        public void LoadGraphicsContent(GraphicsDevice pDevice)
        {
            mPrimitiveBatch = new PrimitiveBatch(pDevice);
        }
        #endregion

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pDevice"></param>
        public void Render(GraphicsDevice pDevice)
        {
            if (this.mPoints.Count == 0)
                return;

            // Draw lines
            mPrimitiveBatch.Begin(PrimitiveType.LineList);
            for (int i = 0; i < this.mPoints.Count-1; i++)
            {
                mPrimitiveBatch.AddVertex(this.mPoints[i], this.mColor);
                mPrimitiveBatch.AddVertex(this.mPoints[i + 1], this.mColor);
                //batch.AddVertex(this.mPoints[(i + 1) % transformedVertices.Length], ShapeColor);
            }
            mPrimitiveBatch.End();

            // Draw Nodes
            for (int i = 0; i < this.mPoints.Count; i++)
            {
                mPrimitiveBatch.Begin(PrimitiveType.LineList);
                mPrimitiveBatch.AddVertex(this.mPoints[i] + new Vector2(-cHalfRadiusNodes, -cHalfRadiusNodes), this.mColor);
                mPrimitiveBatch.AddVertex(this.mPoints[i] + new Vector2(cHalfRadiusNodes, -cHalfRadiusNodes), this.mColor);

                mPrimitiveBatch.AddVertex(this.mPoints[i] + new Vector2(cHalfRadiusNodes, -cHalfRadiusNodes), this.mColor);
                mPrimitiveBatch.AddVertex(this.mPoints[i] + new Vector2(cHalfRadiusNodes, cHalfRadiusNodes), this.mColor);

                mPrimitiveBatch.AddVertex(this.mPoints[i] + new Vector2(cHalfRadiusNodes, cHalfRadiusNodes), this.mColor);
                mPrimitiveBatch.AddVertex(this.mPoints[i] + new Vector2(-cHalfRadiusNodes, cHalfRadiusNodes), this.mColor);

                mPrimitiveBatch.AddVertex(this.mPoints[i] + new Vector2(-cHalfRadiusNodes, cHalfRadiusNodes), this.mColor);
                mPrimitiveBatch.AddVertex(this.mPoints[i] + new Vector2(-cHalfRadiusNodes, -cHalfRadiusNodes), this.mColor);
                mPrimitiveBatch.End();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="idx"></param>
        /// <param name="pNewValue"></param>
        public void ChangePoint(int idx, Vector2 pDif)
        {
            if (this.mWidth == 0 || this.mHeight == 0)
                return;

            Vector2 newValue = this.mPoints[idx];
            newValue += pDif;

            float newx = Math.Max(cHalfRadiusNodes, newValue.X);
            newx = Math.Min(this.mWidth - cHalfRadiusNodes, newx);
            float newy = Math.Max(cHalfRadiusNodes, newValue.Y);
            newy = Math.Min(this.mHeight - cHalfRadiusNodes, newy);

            this.mPoints[idx] = new Vector2(newx, newy);

            if (this.ReflectionMarkChanging != null)
                this.ReflectionMarkChanging(this, EventArgs.Empty);

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="pVec"></param>
        public void CreatePoint(Vector2 pVec)
        {
            int idxInsert = 0;

            // Detect if point is too close
            foreach (Vector2 vec in this.mPoints)
            {
                if (vec == pVec)
                    return;

                float dist = (vec - pVec).Length();
                if (dist < 0.01f)
                    return;
            }

            // Calc the index in mPoints to insert in
            for (int i = 0; i < this.mPoints.Count; i++)
            {
                if (pVec.X < this.mPoints[i].X)
                {
                    idxInsert = i;
                    break;
                }
            }

            this.mPoints.Insert(idxInsert, pVec);
        }

        #region Mouse
        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        public void OnMouseUp(System.Windows.Forms.MouseEventArgs e)
        {
            if (this.mMouseMode != eMouseMode.None)
            {
                if (this.ReflectionMarkChanged != null)
                    this.ReflectionMarkChanged(this, EventArgs.Empty);
            }

            this.mPointMovingIdx = -1;
            this.MouseMode = eMouseMode.None;

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        public void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
        {
            if (e.Button != MouseButtons.Left)
            {
                this.MouseMode = eMouseMode.None;
                return;
            }
            Vector2 mousepos = new Vector2(e.Location.X, e.Location.Y);
            switch (this.mMouseMode)
            {
                case eMouseMode.CreatePoint:

                    this.CreatePoint(mousepos);
                    break;
                default:
                    this.mPointMovingIdx = -1;
                    for (int i = 0; i < this.mPoints.Count; i++)
                    {
                        if (Vector2InsideCircle(mousepos, this.mPoints[i], cRadiusNodes))
                        {
                            this.mPointMovingIdx = i;
                            this.MouseMode = eMouseMode.MovePoint;
                            return;
                        }
                    }

                    if (Vector2InReflectionMark(mousepos))
                    {

                        this.MouseMode = eMouseMode.MoveMark;
                        return;
                    }


                    this.MouseMode = eMouseMode.None;

                    break;

            }

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        public void OnMouseMove(System.Windows.Forms.MouseEventArgs e)
        {
            Vector2 dif = new Vector2(e.X - mLastMousePos.X, e.Y - mLastMousePos.Y);
            this.mLastMousePos = e.Location;
            Vector2 mousepos = new Vector2(e.Location.X, e.Location.Y);

            switch (this.mMouseMode)
            {
                case eMouseMode.MovePoint:
                    if (this.mPointMovingIdx != -1)
                    {
                        // Don�t allow points of sides to move in the X direction
                        if (this.mPointMovingIdx == 0 || this.mPointMovingIdx == this.mPoints.Count - 1)
                            dif.X = 0f;

                        this.ChangePoint(mPointMovingIdx, dif);
                    }
                    break;
                case eMouseMode.MoveMark:
                    for (int i = 0; i < this.mPoints.Count; i++)
                    {
                        // Don�t allow points of sides to move in the X direction
                        Vector2 dif2 = dif;
                        if (i == 0 || i == this.mPoints.Count - 1)
                            dif2.X = 0f;

                        this.ChangePoint(i, dif2);
                    }
                    break;
                case eMouseMode.CreatePoint:
                    if (this.Vector2InReflectionMark(mousepos))
                        Cursor.Current = Cursors.Cross;
                    else Cursor.Current = Cursors.Default;
                    break;
                default:
                    for (int i = 0; i < this.mPoints.Count; i++)
                    {
                        if (this.Vector2InsideCircle(mousepos, this.mPoints[i], cRadiusNodes))
                        {
                            if (i > 0 && i < mPoints.Count - 1)
                                Cursor.Current = Cursors.NoMove2D;
                            else Cursor.Current = Cursors.NoMoveVert;
                            return;
                        }
                    }

                    if (this.Vector2InReflectionMark(mousepos))
                        Cursor.Current = Cursors.NoMoveVert;
                    else Cursor.Current = Cursors.Default;

                    break;
            }


        }
        #endregion

        #region Maths
        /// <summary>
        /// 
        /// </summary>
        /// <param name="pPt"></param>
        /// <param name="pCirclePos"></param>
        /// <param name="pCircleRad"></param>
        /// <returns></returns>
        private bool Vector2InsideCircle(Vector2 pPt, Vector2 pCirclePos, int pCircleRad)
        {
            Vector2 dif = pPt - pCirclePos;
            return (dif.Length() <= pCircleRad);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        /// <param name="Vector2"></param>
        /// <param name="pVector2WasInsideSegment"></param>
        /// <returns></returns>
        private bool ClosestVector2On2DSegment(Vector2 p1, Vector2 p2, Vector2 point, out Vector2 pClosestVector2)
        {
            // Check if Vector2 is outside the segment in the p1 side
            Vector2 ca = point - p1;
            Vector2 segDir = p2 - p1;
            float dot_ta = Vector2.Dot(ca, segDir);
            if (dot_ta <= 0)
            {
                pClosestVector2 = p1;
                return false;
            }

            // Check if Vector2 is outside the segment in the p2 side
            Vector2 cb = point - p2;
            float dot_tb = Vector2.Dot(cb, -segDir);
            if (dot_tb <= 0)
            {
                pClosestVector2 = p2;
                return false;
            }

            float dot_tatb = dot_ta + dot_tb;
            Vector2 nearest = new Vector2(segDir.X * (dot_ta / dot_tatb), segDir.Y * (dot_ta / dot_tatb));
            nearest += p1;

            pClosestVector2 = nearest;
            return true;

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="pPt"></param>
        /// <returns></returns>
        private bool Vector2InReflectionMark(Vector2 pPt)
        {
            Vector2 closest = Vector2.Zero;

            for (int i = 0; i < this.mPoints.Count - 1; i++)
            {
                if (ClosestVector2On2DSegment(this.mPoints[i], this.mPoints[i + 1], pPt, out closest))
                {
                    Vector2 dif = pPt - closest;
                    if (dif.Length() < cHalfRadiusNodes)
                        return true;

                }
            }
            return false;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public float GetMaxY()
        {
            float maxY = float.MinValue;
            foreach(Vector2 vec in mPoints)
            {
                if (vec.Y > maxY)
                    maxY = vec.Y;
            }
            return maxY;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="pX"></param>
        /// <returns></returns>
        public float GetYAt(float pX)
        {
            float y = float.MinValue;

            for (int i = 0; i < this.mPoints.Count - 1; i++)
            {
                y = float.MinValue;
                y = this.GetYAtSegment(pX, this.mPoints[i], this.mPoints[i + 1]);
                if (y != float.MinValue)
                    return y;
            }
            return float.MinValue;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="pX"></param>
        /// <returns>The Y coordinate that corresponds to pX</returns>
        private float GetYAtSegment(float pX, Vector2 p1, Vector2 p2)
        {
            // If point is outside the limits of this segment, just quit
            if (pX < Math.Min(p1.X-cHalfRadiusNodes, p2.X-cHalfRadiusNodes))
                return float.MinValue;
            if (pX > Math.Max(p1.X + cHalfRadiusNodes, p2.X + cHalfRadiusNodes))
                return float.MinValue;

            float difx = (p2.X+(float)cHalfRadiusNodes) - (p1.X-(float)cHalfRadiusNodes);
            if (difx == 0)
                return float.MinValue;

            float dify = p2.Y - p1.Y;
            float prop = dify / difx;
            return p1.Y + (prop * (pX - (p1.X - (float)cHalfRadiusNodes)));

        }

        #endregion
    }
}

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
Software Developer (Senior)
Spain Spain
Inaki Ayucar is a Microsoft MVP in DirectX/XNA, and a software engineer involved in development since his first Spectrum 48k, in the year 1987. He is the founder and chief developer of The Simax Project (www.simaxvirt.com) and is very interested in DirectX/XNA, physics, game development, simulation, C++ and C#.

His blog is: http://graphicdna.blogspot.com

To contact Inaki: iayucar@simax.es

Comments and Discussions