绘制直线和直线剪切

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace XnaGeometric
{
    public class CLine : IGeometric
    {
        enum PtDirection
        {
            //None = 0,
            N = 1,//North
            E = 2,//East
            S = 4,//South
            W = 8,//West
            NE = N | E,//NorthEast
            SE = S | E,//SouthEast
            SW = S | W,//SouthWest
            NW = N | W,//NorthWest
            C = 0,//Center
        }

        private Color m_color = Color.Green;
        private CPoint m_P0;//起始点
        private CPoint m_P1;//结束点


        public Color Color 
        { 
            get { return this.m_color; }
            set { this.m_color = value; }
        }

        public CPoint P0
        {
            get { return this.m_P0; }
            set { this.m_P0 = value; }
        }

        public CPoint P1
        {
            get { return this.m_P1; }
            set { this.m_P1 = value; }
        }

        public CLine(CPoint P0, CPoint P1)
        {

            this.m_P0 = P0;
            this.m_P1 = P1;
        }

        

        /// <summary>
        /// 通过此直线方程计算x坐标
        /// //x = (y - y0)*(x1 - x0)/(y1 - y0) + x0
        /// </summary>
        private void ComputeX(ref float x, float y,
            float dx, float dy,
            float x0, float y0)
        {
            x = (y - y0) * dx / dy + x0;
            //x = (int)(x + ((float)Math.Sign(x)) * 0.5f);
        }
        /// <summary>
        /// y = (x - x0)*(y1 - y0)/(x1 - x0) + y0
        /// </summary>
        private void ComputeY(float x, ref float y, 
            float dx, float dy, 
            float x0, float y0)
        {
            y = (x - x0) * dy / dx + y0;
            //y = (int)(y + ((float)Math.Sign(y)) * 0.5f);
        }

        private int Round(float value)
        {
            return (int)(value + ((float)Math.Sign(value)) * 0.5f);
        }

        private bool IsAtLR(Rectangle rect, float x) { return x >= rect.Left && x <= rect.Right; }
        private bool IsAtTB(Rectangle rect, float y) { return y >= rect.Top && y <= rect.Bottom; }

        private bool IsSameDir(PtDirection src, PtDirection dest)
        {
            return ((PtDirection)(src & dest) == dest);
        }

        private bool Clip(ref CPoint ptCliped,
            PtDirection ptDir, 
            Rectangle rect,
            float x0, float y0,
            float x1, float y1, 
            float dx, float dy)
        {
            float clipX = ptCliped.X, clipY = ptCliped.Y;
            if (IsSameDir(ptDir, PtDirection.N))
            {
                clipY = rect.Top;
                this.ComputeX(ref clipX, clipY, dx, dy, x0, y0);
                //NE
                if (clipX > rect.Right)
                {
                    clipX = rect.Right;
                    this.ComputeY(clipX, ref clipY, dx, dy, x0, y0);
                    if (!IsAtTB(rect, clipY)) return false;
                }//NW
                else if (clipX < rect.Left)
                {
                    clipX = rect.Left;
                    this.ComputeY(clipX, ref clipY, dx, dy, x0, y0);
                    if (!IsAtTB(rect, clipY)) return false;
                }
                //else { //true}
            }
            else if (IsSameDir(ptDir, PtDirection.E))
            {
                clipX = rect.Right;
                ComputeY(clipX, ref clipY, dx, dy, x0, y0);
                if (clipY < rect.Top)
                {
                    clipY = rect.Y;
                    this.ComputeX(ref  clipX, clipY, dx, dy, x0, y0);
                    if (!IsAtLR(rect, clipX)) return false;
                }
                else if (clipY > rect.Bottom)
                {
                    clipY = rect.Bottom;
                    this.ComputeX(ref  clipX, clipY, dx, dy, x0, y0);
                    if (!IsAtLR(rect, clipX)) return false;
                }
            }
            else if (IsSameDir(ptDir, PtDirection.S))
            {
                clipY = rect.Bottom;
                this.ComputeX(ref clipX, clipY, dx, dy, x0, y0);
                //NE
                if (clipX > rect.Right)
                {
                    clipX = rect.Right;
                    this.ComputeY(clipX, ref clipY, dx, dy, x0, y0);
                    if (!IsAtTB(rect, clipY)) return false;
                }//NW
                else if (clipX < rect.Left)
                {
                    clipX = rect.Left;
                    this.ComputeY(clipX, ref clipY, dx, dy, x0, y0);
                    if (!IsAtTB(rect, clipY)) return false;
                }
            }
            else if (IsSameDir(ptDir, PtDirection.W))
            {
                clipX = rect.Left;
                ComputeY(clipX, ref clipY, dx, dy, x0, y0);
                if (clipY < rect.Top)
                {
                    clipY = rect.Y;
                    this.ComputeX(ref  clipX, clipY, dx, dy, x0, y0);
                    if (!IsAtLR(rect, clipX)) return false;
                }
                else if (clipY > rect.Bottom)
                {
                    clipY = rect.Bottom;
                    this.ComputeX(ref  clipX, clipY, dx, dy, x0, y0);
                    if (!IsAtLR(rect, clipX)) return false;
                }
            }
            ptCliped.X = this.Round(clipX);
            ptCliped.Y = this.Round(clipY);
            return true;
        }

        private bool ClipLine(ref CPoint newP0, ref CPoint newP1, Rectangle rect)
        {
            Point tmpP0 = new Point(newP0.X, newP0.Y);
            Point tmpP1 = new Point(newP1.X, newP1.Y);
            //两个点在绘制区域里面
            bool bContainP0 = rect.Contains(tmpP0);
            bool bContainP1 = rect.Contains(tmpP1);
            if (bContainP0 && bContainP1)
            {
                return true;
            }
            #region Relate
            //       *                       * 
            //  NW   *          N            *  NE
            //       *                       * 
            //***************************************
            //       *                       * 
            //       *                       * 
            //       *                       * 
            //   W   *         C             *   E
            //       *                       * 
            //       *                       * 
            //       *                       * 
            //***************************************
            //       *                       * 
            //  SW   *          S            *   SE
            //       *                       * 
            #endregion
            //获取P0, P1的所在的方位
            PtDirection dir0 = PtDirection.C;
            PtDirection dir1 = PtDirection.C;
            this.GetPtDir(rect, ref dir0, tmpP0);
            this.GetPtDir(rect, ref dir1, tmpP1);
            float x0 = tmpP0.X, y0 = tmpP0.Y;
            float x1 = tmpP1.X, y1 = tmpP1.Y;
            float dx = x1 - x0;
            float dy = y1 - y0;
            bool bP0Clip = Clip(ref newP0, dir0, rect, x0, y0, x1, y1, dx, dy);
            bool bP1Clip = Clip(ref newP1, dir1, rect, x0, y0, x1, y1, dx, dy);
            return (bP0Clip && bP1Clip);
        }
        /// <summary>
        /// 获取线的形状
        /// </summary>
        /// <param name="buffer">绘线的缓冲区</param>
        /// <param name="rectSrc">源区域(一般是剪切区域)</param>
        /// <param name="rectDest">目标区域(一般是屏幕缓冲区域)</param>
        public void Get(ref Color[] buffer, Rectangle rectSrc, Rectangle rectDest)
        {
            CPoint newP0 = P0.Copy();
            CPoint newP1 = P1.Copy();
            Rectangle rectCliped = AdjustClipRect(rectSrc, rectDest);
            if (!ClipLine(ref newP0, ref newP1, rectCliped)) return;
            this.GetLine(newP0, newP1, ref buffer, rectDest.Width);
        }

        /// <summary>
        /// 调整剪切区域
        /// </summary>
        /// <param name="rectSrc">源区域(即剪切区域)</param>
        /// <param name="rectDest">目标区域(例如屏幕)</param>
        /// <returns>返回调整后的剪切区域</returns>
        private Rectangle AdjustClipRect(Rectangle rectSrc, Rectangle rectDest)
        {
            Rectangle rectCliped = rectSrc;
            if (rectCliped.Left < rectDest.Left)
            {
                rectCliped.X = rectDest.Left;
            }
            if (rectCliped.Right > rectDest.Right)
            {
                rectCliped.Width = rectCliped.Right - rectDest.Right + 1;
            }
            if (rectCliped.Top < rectDest.Top)
            {
                rectCliped.Y = rectDest.Top;
            }
            if (rectCliped.Bottom > rectDest.Bottom)
            {
                rectCliped.Height = rectCliped.Bottom - rectDest.Bottom;
            }
            return rectCliped;
        }

        private void GetPtDir(Rectangle rect, ref PtDirection ptDir, Point pt)
        {
            if (rect.Contains(pt))
            {
                ptDir = PtDirection.C;
                return;
            }
            else
            {
                if (pt.Y < rect.Top) { ptDir |= PtDirection.N; }
                else if (pt.Y > rect.Bottom) { ptDir |= PtDirection.S;}

                if (pt.X < rect.Left) { ptDir |= PtDirection.W; }
                else if (pt.X > rect.Right) { ptDir |= PtDirection.E; }
            }
        }

        /// <summary>
        /// 没有剪切的版本
        /// </summary>
        private void GetLine(CPoint P0, CPoint P1,
            ref Color[] buffer, int scrWidth)
        { 
            CPoint ptExtra = P1 - P0;
            int dx = ptExtra.X, dy = ptExtra.Y;
            int yStep = 1, xStep = 1;
            if (dx < 0) { dx = -dx; xStep = -1; }
            if (dy < 0) { dy = -dy; yStep = -1; }
            int dx2 = dx << 1;
            int dy2 = dy << 1;
            
            //int yStart = P0.Y, yEnd = P1.Y,
            //    xStart = P0.X, xEnd = P1.X;
            int xi = 0, yi = 0;
            int x = P0.X, y = P0.Y;
            //int ix = xStart, iy = yStart;
            
            int index = 0;

            if (dx >= dy)
            {//0 <= 斜率的绝对值 < 1,以x为增量
                int error = -dx;
                //if (dy > 0) { yStep = 1; }
                //else { yStep = -1; }
                //yStep = dy > 0 ? 1 : -1;
                for (xi = 0; xi <= dx; xi++)
                {
                    index = x + y * scrWidth;
                    buffer[index] = this.Color;
                    error += dy2;
                    if (error >= 0)
                    {
                        y += yStep;
                        error -= dx2;
                    }
                    x += xStep;
                }
            }
            else
            {//斜率的绝对值 >= 1,以y为增量
                int error = -dy;
                for (yi = 0; yi <= dy; yi++)
                {
                    index = x + y * scrWidth;
                    buffer[index] = this.Color;
                    error += dx2;
                    if (error >= 0)
                    {
                        x += xStep;
                        error -= dy2;
                    }
                    y += yStep;
                }
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值