C#时间轴曲线图形编辑器开发1-基本功能

目录

一、前言

1、简介

2、开发过程

3、工程下载链接

二、基本功能实现

1、绘图面板创建

(1)界面布置

(2)显示面板代码

[(3) 面板水平方向、竖直方向移动功能实现](#(3) 面板水平方向、竖直方向移动功能实现)

(4)面板放大、缩小、恢复正常显示功能实现

(5)鼠标当前位置坐标值和界面显示

(6)面板实现效果操作演示

2、数据曲线在面板上显示

(1)曲线数据变量定义

(2)曲线数据生成按钮事件函数

(3)曲线是否显示复选框事件函数

(4)面板上绘制曲线图形

(5)添加曲线清除按钮

(6)曲线显示操作演示

(7)代码工程下载链接

3、鼠标在曲线上识别

(1)添加变量

(2)读取文本文件数据

(3)显示读取的两列数据曲线

(4)鼠标在两列数据曲线上识别

(5)代码下载链接

4、时间轴曲线当前所在位置指示线

(1)添加全局变量

(2)鼠标按键按下检测

(3)在DrawCure()中画竖直方向的指示线

(4)代码下载链接

5、鼠标拖动关键点

(1)添加全局变量

[(2) 添加几个测试的关键帧数据](#(2) 添加几个测试的关键帧数据)

(3)绘制关键帧和数据曲线

(4)关键帧数据编辑

(5)代码下载链接


一、前言

1、简介

通过制作简易的该曲线图形编辑器Demo,实现类似于Maya软件中的动画曲线编辑的功能。在指定的时间轴上插入关键帧,设置拖动该关键点,来编辑曲线数据。编辑好的数据导出保存在TXT文本中,同时曲线编辑器也可以读取之前导出的TXT文本数据再继续进行编辑。

操作演示

自制曲线编辑器Demo

Maya软件动画编辑

2、开发过程

实现曲线编辑的功能,大致分五个过程,分包为

(1)曲线绘图面板创建、显示数据曲线

(2)鼠标经过数据曲线上识别到该曲线

(3)当前时间轴位置指示线创建

(4)鼠标拖动已设置的关键帧、改变曲线

(5)任意位置添加关键帧、对关键帧数据插值得到曲线、曲线数据导出和导入

3、工程下载链接

基本功能,打包下载链接

https://download.csdn.net/download/panjinliang066333/88112693

全部功能测试代码

https://download.csdn.net/download/panjinliang066333/88103699

二、基本功能实现

1、绘图面板创建

(1)界面布置

创建C# Winform工程。

①添加pictureBox1图形控件作为曲线图形显示面板,添加hHScrollBarX、VScrollBarY控件作为显示面板水平方向、竖直方向移动。

②添加3个Button按钮,用作图形面板放大、缩小、和恢复正常。

③添加绘制曲线的按钮、添加复选框用于选择是否显示曲线

④添加Label控件,用于显示当前鼠标所在曲线图形面板中的坐标位置。

(2)显示面板代码

①、窗口上添加Form1_Load,

        private void Form1_Load(object sender, EventArgs e)
        {
            if (xLineDatas.Length > dataF.Length)
            {
                hScrollBarX.Maximum = (int)xLineDatas.Max();
            }
            else
            {
                hScrollBarX.Maximum = dataF.Length;
            }
            isSpline1Show = !checkSpline1Show.Checked;

            timer1.Start();
            cureDraw = new SplineEdit(pictureBox1.Height, pictureBox1.Width);
            DrawCure();
        }

②、添加定时器,创建定时器事件函数

         private void timer1_Tick(object sender, EventArgs e)
        {
            DrawCure();
        }

③、DrawCure()封装的代码如下

        private void DrawCure()
        {
            if (pictureBox1.Height > 0 && pictureBox1.Width > 0)
            {
                cureDraw.Height = pictureBox1.Height;
                cureDraw.Width = pictureBox1.Width;
            }

            pictureBox1.Image = cureDraw.DrawImage();
        }

cureDraw为自定义封装的类SplineEdit实例对象。

④、DrawImage(),创建显示坐标系面板

        /// <summary>
        /// 生成坐标系图片面板
        /// </summary>
        /// <returns></returns>
        public Bitmap DrawImage()
        {
            ImageBoardInit();   //绘图面板
            return bitMap;
        }      

ImageBoardInit():绘制面板

        /// <summary>
        /// 创建绘图面板-显示坐标系
        /// </summary>
        private void ImageBoardInit()
        {
            int tempCountX = 0;
            int tempCountY = 0;  

            //1、绘制X、Y坐标轴

            bitMap = new Bitmap((int)width, (int)height);  //根据给定的高度和宽度创建一个位图图像
            graphics = Graphics.FromImage(bitMap);    //从指定的 objBitmap 对象创建 objGraphics 对象 (即在objBitmap对象中画图)

            //根据给定颜色(LightGray)填充图像的矩形区域 (背景)
            graphics.DrawRectangle(new Pen(boardColor, 1), 0, 0, width - 1, height - 1);    //画边框
            graphics.FillRectangle(new SolidBrush(backColor), 1, 1, width - 2, height - 2); //填充边框

            //画X轴,注意图像的原始X轴和Y轴计算是以左上角为原点,向右和向下计算的
            xAxisPoint1.X = xSpace + xSliceBegin;
            xAxisPoint1.Y = (height / 2) + ySliceBegin;    //
            xAxisPoint2.X = width;
            xAxisPoint2.Y = xAxisPoint1.Y;
            graphics.DrawLine(new Pen(new SolidBrush(axisColor), 2), xAxisPoint1, xAxisPoint2);

            //画Y轴
            yAxisPoint1.X = xSpace + xSliceBegin;
            yAxisPoint1.Y = height;
            yAxisPoint2.X = xSpace + xSliceBegin;
            yAxisPoint2.Y = 0;
            graphics.DrawLine(new Pen(new SolidBrush(axisColor), 2), yAxisPoint1, yAxisPoint2);

            //2、面板标题
            //graphics.DrawString("曲线编辑器", new Font("宋体", fontSize), new SolidBrush(Color.Blue), new PointF(width / 2, ySpace / 2));

            //3、画X轴上刻度、刻度说明            
            xSlice = (width - xSpace) / xSliceCount;
            ySlice = (height / 2) / (ySliceCount / 2);

            int tempCountX1 = (int)(-xSliceBegin / xSlice);
            int tempCountY1 = (int)Math.Abs((ySliceBegin / ySlice));
            tempCountX = tempCountX1 + xSliceCount;
            tempCountY = tempCountY1 + ySliceCount / 2;
            //画网格虚线
            Pen penDashed = new Pen(new SolidBrush(Color.Black));
            penDashed.DashStyle = DashStyle.Dash;
            for (int i = 1; i < tempCountX + 1; i++)
            {
                //X轴刻度虚线
                graphics.DrawLine(penDashed, new PointF(i * xSlice + xSpace + xSliceBegin, 0), new PointF(i * xSlice + xSpace + xSliceBegin, height));
                //X轴刻度值标识文字
                string xStr = (i * xSliceValue).ToString();
                int nStrLength = xStr.Length;
                graphics.DrawString(xStr, new Font("宋体", fontSize), new SolidBrush(Color.Black), new PointF(i * xSlice + xSpace - fontSize * nStrLength + xSliceBegin, height / 2 + fontSize + ySliceBegin));
            }
            //Y轴虚线、刻度文字-正半轴
            for (int i = 0; i < tempCountY + 1; i++)
            {
                graphics.DrawLine(penDashed, new PointF(xSpace + xSliceBegin, height / 2 - i * ySlice + ySliceBegin), new PointF(width, height / 2 - i * ySlice + ySliceBegin));
                //Y轴刻度值标识文字
                string yStr = (i * ySliceValue).ToString();
                int nStrLength = yStr.Length;
                if (i > 0)
                    graphics.DrawString(yStr, new Font("宋体", fontSize), new SolidBrush(Color.Black), new PointF(xSpace - fontSize * nStrLength + xSliceBegin, height / 2 - i * ySlice + ySliceBegin));

            }
            //Y轴虚线、刻度文字-负半轴
            for (int i = 1; i < tempCountY + 1; i++)
            {
                graphics.DrawLine(penDashed, new PointF(xSpace + xSliceBegin, height / 2 + i * ySlice + ySliceBegin), new PointF(width, height / 2 + i * ySlice + ySliceBegin));
                //Y轴刻度值标识文字
                string yStr = (-i * ySliceValue).ToString();
                int nStrLength = yStr.Length;
                graphics.DrawString(yStr, new Font("宋体", fontSize), new SolidBrush(Color.Black), new PointF(xSpace - fontSize * nStrLength + xSliceBegin, height / 2 + i * ySlice + ySliceBegin));
            }
            //4、原点刻度说明
            graphics.DrawString("0", new Font("宋体", fontSize + 2), new SolidBrush(Color.Black), new PointF(xSpace - fontSize * 2 + xSliceBegin, height / 2 + ySliceBegin));
        }

⑤、制作出的图形面板显示如下

(3) 面板水平方向、竖直方向移动功能实现

分别添加hScrollBarX_ValueChanged、vScrollBarY_ValueChanged控件事件函数

hScrollBarX_ValueChanged:

        private void hScrollBarX_ValueChanged(object sender, EventArgs e)
        {
            cureDraw.XSliceBegin = -hScrollBarX.Value * cureDraw.XSlice / cureDraw.XSliceValue;
            Invalidate();   //更新界面
        }

vScrollBarY_ValueChanged:

        private void vScrollBarY_ValueChanged(object sender, EventArgs e)
        {
            cureDraw.YSliceBegin = vScrollBarY.Value * cureDraw.YSlice / cureDraw.YSliceValue;
            Invalidate();   //更新界面
        }

cureDraw.XSliceBegin:X轴数值0点在屏幕起始像素位置

cureDraw.YSliceBegin:Y轴数值0点在屏幕起始像素位置

(4)面板放大、缩小、恢复正常显示功能实现

分别添加按钮事件函数btnScaleBigger_Click、btnScaleSmaller_Click、btnReset_Click

btnScaleBigger_Click:

        private void btnScaleBigger_Click(object sender, EventArgs e)
        {
            cureDraw.XSliceCount -= 1;
            cureDraw.YSliceCount -= 1;
            Invalidate();   //更新界面
        }

btnScaleSmaller_Click:

        private void btnScaleSmaller_Click(object sender, EventArgs e)
        {
            cureDraw.XSliceCount += 1;
            cureDraw.YSliceCount += 1;
            Invalidate();   //更新界面
        }

btnReset_Click:

        private void btnReset_Click(object sender, EventArgs e)
        {
            cureDraw.XSliceCount = 15;
            cureDraw.YSliceCount = 10;
            hScrollBarX.Value = 0;
            vScrollBarY.Value = 0;
        }

(5)鼠标当前位置坐标值和界面显示

①控件pictureBox1上添加pictureBox1_MouseMove事件函数

        private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
        {
            int ex = 0, ey = 0;         //鼠标的坐标值
            int mouseToValueX = 0;
            float mouseToValueY = 0;    //鼠标位置对应的数值
            float fMouseToValueX = 0;

            ex = e.X;                   //鼠标在pictureBox1中X轴坐标值
            ey = e.Y;                   //鼠标在pictureBox1中Y轴坐标值
            fMouseToValueX = cureDraw.MousePosToValue_X(ex);
            mouseToValueX = (int)Math.Round(cureDraw.MousePosToValue_X(ex));
            mouseToValueY = cureDraw.MousePosToValue_Y(ey);
            if (mouseToValueX < 0)
            {
                mouseToValueX = 0;
            }
            if (mouseToValueX >= 0 && ex < pictureBox1.Width - 60)
            {
                labMousePos.Text = "X:" + mouseToValueX.ToString() + ";" + "Y:" + mouseToValueY.ToString();
                labMousePos.Location = new Point(ex + 50, ey + 40);
            }
            if (mouseToValueX >= 0 && ex > pictureBox1.Width - 60)
            {
                labMousePos.Text = "X:" + mouseToValueX.ToString() + ";" + "Y:" + mouseToValueY.ToString();
                labMousePos.Location = new Point(ex - 50, ey + 40);
            }
        }

②cureDraw.MousePosToValue_X:鼠标在pictureBox1面板上X轴的像素值转换为坐标系X轴的值

        /// <summary>
        /// 鼠标画板上横向位置转换成坐标x轴数值
        /// </summary>
        /// <param name="ex"></param>
        /// <returns></returns>
        public float MousePosToValue_X(float ex)
        {
            return (ex - xSpace - xSliceBegin) * xSliceValue / xSlice; ;
        }

③cureDraw.MousePosToValue_Y:鼠标在pictureBox1面板上Y轴的像素值转换为坐标系Y轴的值

        /// <summary>
        /// 鼠标画板上竖向位置转换成坐标y轴数值
        /// </summary>
        /// <param name="ey"></param>
        /// <returns></returns>
        public float MousePosToValue_Y(float ey)
        {
            return (ySliceBegin + height / 2 - ey) * ySliceValue / ySlice;
        }

(6)面板实现效果操作演示

2、数据曲线在面板上显示

曲线1为显示Sin曲线、曲线2为显示生成的随机数曲线。

曲线1显示函数,需要分别提供X轴、Y轴的坐标位置数组,且两个数组长度必须相等

public void DrawXY(float[] xDatas, float[] yDatas, Color splineColor, float tension, bool isPointFill)

曲线2显示函数,只需要曲线Y轴上位置数组。(数据的长度即为X轴数据)

public void DrawSpline(float[] yDatas, Color splineColor, float tension, bool isPointFill)

(1)曲线数据变量定义

        //曲线1
        bool isSpline1Show = false;
        float[] xLineDatas = new float[501];            //保存绘制的点 x、y轴位置
        float[] yLineDatas = new float[501];
        Color spline1Color = Color.Green;               //曲线1颜色  
        float tensionSpline1 = 0.5f;                    //曲线1粗细

        //曲线2
        bool isSpline2Show = false;
        float[] dataF = new float[10000];
        Color spline2Color = Color.Red;                 //曲线2颜色  
        float tensionSpline2 = 0.5f;                    //曲线2粗细
        Random rd = new Random();

(2)曲线数据生成按钮事件函数

分别添加曲线1按钮、曲线2按钮事件函数btnDrawSpline1_Click、btnDrawSpline2_Click

btnDrawSpline1_Click:

        private void btnDrawSpline1_Click(object sender, EventArgs e)
        {
            //isLineSplineShow = false;
            isSpline1Show = checkSpline1Show.Checked;

            //1、生成sin曲线,保存x、y坐标位置
            for (int i = 0; i < xLineDatas.Length; i++)
            {
                xLineDatas[i] = i;
                yLineDatas[i] = 32 * (float)Math.Sin(36 * i * Math.PI / 180);
            }

            Invalidate();   //更新界面
        }

btnDrawSpline2_Click:

        private void btnDrawSpline2_Click(object sender, EventArgs e)
        {            
            for (long i = 1; i < dataF.Length; i++)
            {
                dataF[i] = (float)rd.Next(-20, 20);
            }
            isSpline2Show = true;
        }

(3)曲线是否显示复选框事件函数

分别添加曲线1、曲线2显示复选框事件函数:heckSpline1Show_CheckedChanged、checkSpline2Show_CheckedChanged

heckSpline1Show_CheckedChanged

        private void checkSpline1Show_CheckedChanged(object sender, EventArgs e)
        {
            isSpline1Show = checkSpline1Show.Checked;
        }

checkSpline2Show_CheckedChanged

        private void checkSpline2Show_CheckedChanged(object sender, EventArgs e)
        {
            isSpline2Show = checkSpline2Show.Checked;
        }

(4)面板上绘制曲线图形

修改前面的DrawCure()函数

         private void DrawCure()
        {

            if (pictureBox1.Height > 0 && pictureBox1.Width > 0)        //若窗口最小化时候,则Height、Width都为0。DrawImage()创建图像会出错
            {
                cureDraw.Height = pictureBox1.Height;
                cureDraw.Width = pictureBox1.Width;
            }
            pictureBox1.Image = cureDraw.DrawImage();

            //曲线1显示
            if (isSpline1Show == true)
            {
                cureDraw.DrawXY(xLineDatas, yLineDatas, spline1Color, tensionSpline1, false);
            }

            //曲线2显示
            if (isSpline2Show == true)
            {
                cureDraw.DrawSpline(dataF, spline2Color, tensionSpline2, false);
            }           
        }

(5)添加曲线清除按钮

用于清除曲线数据和不再显示曲线

        private void btnDataClear_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < xLineDatas.Length; i++)
            {
                xLineDatas[i] = 0;
                yLineDatas[i] = 0;
            }

            for (int i = 0; i < dataF.Length; i++)
            {
                dataF[i] = 0.0f;
            }
            isSpline2Show = false;
        }

(6)曲线显示操作演示

(7)代码工程下载链接

该链接为个人使用。

链接:https://pan.baidu.com/s/1K6HFh56YfvwvoAH5nrdJmg

提取码:vfym

--来自百度网盘超级会员V4的分享

3、鼠标在曲线上识别

读取TXT文本文件中的两列数据,将两列数据以曲线显示出来,当鼠标移动经过曲线时显示当前坐标的Label控件字体颜色变成当前识别的曲线颜色。

曲线识别效果

(1)添加变量

        //曲线-导入数据
        bool isSplineAxis = false;
        float[] axis1Data, axis2Data;
        Color Axis1Color = Color.Red;                   //Axis1曲线颜色
        Color Axis2Color = Color.Blue;                  //Axis2曲线颜色
        bool isMouseOnAxis1, isMouseOnAxis2;            //检测鼠标是否在曲线上

(2)读取文本文件数据

文本文件数据,附在下载链接中。

如效果图所示添加按钮控件,导入读取文本数据。添加btnImportTxtDatas_Click事件函数

        private void btnImportTxtDatas_Click(object sender, EventArgs e)
        {
            try
            {

                OpenFileDialog ofd = new OpenFileDialog();
                ofd.Filter = "All files(*.*)|*.*|文本文件(*.csv)|*.csv|文本文件(*.txt)|*.txt";
                if (ofd.ShowDialog() != DialogResult.OK)
                {
                    return;
                }
                int dataColumNums = 0;              //动作文件列表数
                int dataLineNums;                   //动作行数
                double[,] axisDataArrayRead;                


                string[] lines = File.ReadAllLines(ofd.FileName, Encoding.Default).ToArray();
                dataColumNums = CharNum(lines[0], ";");
                dataLineNums = lines.Length;

                axisDataArrayRead = new double[dataColumNums, dataLineNums];
                for (int i = 0; i < lines.Length; i++)
                {
                    string[] seg = lines[i].Split(';');    //英文逗号分隔符
                    for (int j = 0; j < dataColumNums; j++)
                    {
                        axisDataArrayRead[j, i] = Convert.ToDouble(seg[j]);
                    }
                }
                //
                axis1Data = new float[dataLineNums];
                axis2Data = new float[dataLineNums];
                for(int i=0;i<dataLineNums;i++)
                {
                    axis1Data[i] = (float)axisDataArrayRead[0, i];
                    axis2Data[i] = (float)axisDataArrayRead[1, i];
                }
                isSplineAxis = true;
                hScrollBarX.Maximum = dataLineNums + 10;
            }
            catch
            {
                isSplineAxis = false;
                MessageBox.Show("文件解析异常", "提示");
            }
        }

(3)显示读取的两列数据曲线

修改DrawCure(),添加代码

        private void DrawCure()
        {
            if (pictureBox1.Height > 0 && pictureBox1.Width > 0)        //若窗口最小化时候,则Height、Width都为0。DrawImage()创建图像会出错
            {
                cureDraw.Height = pictureBox1.Height;
                cureDraw.Width = pictureBox1.Width;
            }            
            pictureBox1.Image = cureDraw.DrawImage();

            //曲线1显示
            if (isSpline1Show == true)
            {
                cureDraw.DrawXY(xLineDatas, yLineDatas, spline1Color, tensionSpline1, false);
            }
            //曲线2显示
            if (isSpline2Show == true && spline2DataState == 1)
            {
                cureDraw.DrawSpline(dataF, spline2Color, tensionSpline2, false);                
            }

            //读取的文本文件两列数据显示
            if(isSplineAxis)
            {
                if (checkAxis1.Checked)
                {
                    cureDraw.DrawSpline(axis1Data, Axis1Color, 0.5f, false);
                }
                if (checkAxis2.Checked)
                {
                    cureDraw.DrawSpline(axis2Data, Axis2Color, 0.5f, false);
                }                
                
            }

        }

(4)鼠标在两列数据曲线上识别

修改timer1_Tick()函数

        private void timer1_Tick(object sender, EventArgs e)
        {
            DrawCure();

            //检测鼠标是否在曲线上
            if(isSplineAxis)
            {
                int nIndex = 0;

                //第一列数据曲线识别
                if (checkAxis1.Checked)
                {
                    nIndex = currentValue_X;
                    if (nIndex >= axis1Data.Length)
                    {
                        nIndex = axis1Data.Length-1;
                    }
                    float selectValue_Y_Axis1 = axis1Data[nIndex];
                    if (Math.Abs(selectValue_Y_Axis1 - currentValue_Y) < cureDraw.YSliceValue/8)
                    {
                        isMouseOnAxis1 = true;
                        labMousePos.ForeColor = Axis1Color;
                    }
                    else
                    {
                        isMouseOnAxis1 = false;
                        //labMousePos.ForeColor = Color.Black;
                    }
                }

                //第二列数据曲线识别
                if (checkAxis2.Checked)
                {
                    nIndex = currentValue_X;
                    if (nIndex >= axis1Data.Length)
                    {
                        nIndex = axis1Data.Length-1;
                    }
                    float selectValue_Y_Axis2 = axis2Data[nIndex];
                    if (Math.Abs(selectValue_Y_Axis2 - currentValue_Y) < cureDraw.YSliceValue / 8)
                    {
                        isMouseOnAxis2 = true;
                        labMousePos.ForeColor = Axis2Color;
                    }
                    else
                    {
                        isMouseOnAxis2 = false;
                        //labMousePos.ForeColor = Color.Black;
                    }
                }


                if(isMouseOnAxis1==false && isMouseOnAxis2==false)
                {
                    labMousePos.ForeColor = Color.Black;
                }

            }

        }

(5)代码下载链接

该链接为个人使用。

链接:https://pan.baidu.com/s/1FQNVWU-PLIgaS3KmQz_BGw

提取码:dtcj

--来自百度网盘超级会员V4的分享

4、时间轴曲线当前所在位置指示线

创建一条竖直方向的线,用来指示当前鼠标点击所在的X轴位置。该线的作用是在编辑数据和运行数据的时候可以很方便的看出当前所运行到的时间点。

指示线效果如下所示

(1)添加全局变量

        //鼠标当前在画图面板上的像素坐标,对应的坐标轴数值
        int currentValue_X;
        float currentValue_Y;
        int last_CurrentValueX = 0;

        bool isLeftButtonDowm = false, isMiddleButtonDown = false, isRightButtonDown = false;

(2)鼠标按键按下检测

控件pictureBox1添加pictureBox1_MouseDown、pictureBox1_MouseUp事件函数,用来检测鼠标按键按下和抬起。

pictureBox1_MouseDown

        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                isLeftButtonDowm = true;
            }
            if (e.Button == MouseButtons.Middle)
            {
                isMiddleButtonDown = true;
            }
            if (e.Button == MouseButtons.Right)
            {
                isRightButtonDown = true;
            }
        }

pictureBox1_MouseUp

        private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                isLeftButtonDowm = false;
            }
            if (e.Button == MouseButtons.Middle)
            {
                isMiddleButtonDown = false;
            }
            if (e.Button == MouseButtons.Right)
            {
                isRightButtonDown = false;
            }
        }

(3)在DrawCure()中画竖直方向的指示线

            //时间轴位置指示线
            cureDraw.DrawCurrentLine(last_CurrentValueX);
            if (isLeftButtonDowm)
            {
                last_CurrentValueX = currentValue_X;
            }

DrawCurrentLine()函数如下封装

        /// <summary>
        /// 绘制当前鼠标所在X轴位置的数值,指示直线
        /// </summary>
        /// <param name="CurrentValue_X"></param>
        public void DrawCurrentLine(int CurrentValue_X)
        {

            Brush currentB = new SolidBrush(Color.Red);        //
            float ex = ValueToMousePoint_ex(CurrentValue_X);
            graphics.DrawLine(new Pen(currentB), ex, 0, ex, height);

        }

(4)代码下载链接

该链接为个人使用。

链接:https://pan.baidu.com/s/13c0C1dTqTgW-5G3uYWekOA

提取码:u6n0

--来自百度网盘超级会员V4的分享

5、鼠标拖动关键点

鼠标选中关键帧数据点,滑动鼠标实现对关键帧数据点的拖动。

(1)添加全局变量

需要拖拽的点叫关键帧点,每个关键帧点包括两个数据:X轴中位置、Y轴中位置。创建字典集合keyListDatas 来保存关键帧数据。

        //曲线编辑
        Dictionary<int, float> keyListDatas = new Dictionary<int, float>();
        Color editSplineColor = Color.Blue;                  //曲线颜色
        bool isMouseOnKeyPoint = false;     //鼠标是否在关键点上检测
        bool isKeyEditDataMoveCan = false;
        int dataLength = 0;
        float[] myEditDatas;                //曲线数据-绘制曲线显示
        float[] keyEditDatas_X;             //关键帧-方框点绘制-X轴数据
        float[] keyEditDatas_Y;             //关键帧-方框点绘制-X轴数据

        bool isDataEdit = false;
        int keyEditFrame = 0;

(2) 添加几个测试的关键帧数据

在Form1_Load()添加下面代码,添加关键帧数据

            //曲线编辑器数据
            dataLength = int.Parse(txtDataLength.Text);
            myEditDatas = new float[dataLength];
            //字典集合中添加4个随机数据
            keyListDatas.Add(100 + dataLength / 2, 40.0f);
            keyListDatas.Add(0, 0.0f);
            keyListDatas.Add(dataLength / 2, 10.0f);
            keyListDatas.Add(dataLength - 1, 0.0f);
            keyListDatas[dataLength / 2] = 50.0f;           //修改字典集合中的值
            KeyDataTrans();

KeyDataTrans():

        /// <summary>
        /// 关键帧数据集合转数组
        /// </summary>
        private void KeyDataTrans()
        {                       
            //关键点得数据值,对应到曲线数据上
            foreach (KeyValuePair<int, float> item in keyListDatas)
            {
                int key = item.Key;
                float fValue = item.Value;
                myEditDatas[key] = fValue;
            }

            //
            List<float> fListTemp_X = new List<float> { };
            List<float> fListTemp_Y = new List<float> { };
            foreach (KeyValuePair<int, float> item in keyListDatas)
            {
                int key = item.Key;
                float fValue = item.Value;

                fListTemp_X.Add(key);
                fListTemp_Y.Add(fValue);
            }
            keyEditDatas_X = fListTemp_X.ToArray();
            keyEditDatas_Y = fListTemp_Y.ToArray();
        }

(3)绘制关键帧和数据曲线

在DrawCure()中添加代码

            //曲线编辑器
            if (keyEditDatas_Y.Length>0)
            {
                cureDraw.DrawPoint(keyEditDatas_X, keyEditDatas_Y, editSplineColor, tensionSpline1, true);
            }
            if (myEditDatas.Length > 0)
            {
                cureDraw.DrawSpline(myEditDatas, editSplineColor, 0.5f, false);
            }

(4)关键帧数据编辑

在timer1_Tick()添加KeyPointEdit()

KeyPointEdit():

        private void KeyPointEdit()
        {
            //
            KeyDataTrans();

            //
            PointF[] pf=new PointF[keyEditDatas_X.Length];
            for(int i=0;i<keyEditDatas_X.Length;i++)
            {
                pf[i].X = cureDraw.ValueToMousePoint_ex((int)keyEditDatas_X[i]);
                pf[i].Y = cureDraw.ValueToMousePoint_ey(keyEditDatas_Y[i]);
            }

            //
            label7.BackColor = Color.Blue ;
            foreach (PointF pp  in pf)
            {
                //检测鼠标是否在圆点上
                GraphicsPath vGraphicsPath = new GraphicsPath();
                vGraphicsPath.AddEllipse(pp.X - 5, pp.Y - 5, 10, 10);       // 添加需要检测识别的点
                Region vRegion = new Region(vGraphicsPath);
                isMouseOnKeyPoint = vRegion.IsVisible(ex, ey);           // 判断点是否在圆中

                if (isMouseOnKeyPoint)
                {
                    Cursor.Current = Cursors.SizeNS;                    //设置鼠标为手指形
                    label7.BackColor = Color.Lime;
                    if(isLeftButtonDowm)
                    {
                        //isKeyEditDataMoveCan = true;
                    }
                    isKeyEditDataMoveCan = true;
                }
            }
            

            //
            if (isKeyEditDataMoveCan)
            {
                //
                for (int i = 0; i < keyEditDatas_X.Length; i++)
                {
                    if (Math.Abs(currentValue_X - keyEditDatas_X[i]) < 2)
                    {
                        isDataEdit = true;
                        keyEditFrame = (int)keyEditDatas_X[i];
                    }

                }

            }
            if (isLeftButtonDowm == false)
            {
                isDataEdit = false;
                isKeyEditDataMoveCan = false;
            }


            //
            if(isDataEdit)
            {
                keyListDatas[keyEditFrame] = currentValue_Y;
            }
        }

(5)代码下载链接

该链接为个人使用。

链接:https://pan.baidu.com/s/1HVAWpqR-68OY98KmgUlJbw

提取码:hxvw

--来自百度网盘超级会员V4的分享

相关推荐
nbsaas-boot5 小时前
如何利用ChatGPT加速开发与学习:以BPMN编辑器为例
学习·chatgpt·编辑器
一棵开花的树,枝芽无限靠近你6 小时前
【PPTist】添加PPT模版
前端·学习·编辑器·html
热爱生活的五柒7 小时前
vscode利用ofExtensions插件可以调试单进程Openfoam,但是不能调试mpi多进程案例
ide·vscode·编辑器
为什么每天的风都这么大17 小时前
Vscode/Code-server无网环境安装通义灵码
ide·vscode·阿里云·编辑器·ai编程·code-server
陌上阳光1 天前
vscode连接远程开发机报错
ide·vscode·编辑器
羊子雄起1 天前
CKEditor前端样式和编辑器的样式不一致的问题
前端·编辑器
界面开发小八哥1 天前
「Java EE开发指南」如何使用Visual JSF编辑器设计JSP?(一)
java·ide·java-ee·编辑器·myeclipse
Bio Coder1 天前
vim 一次注释多行 的几种方法
linux·编辑器·vim·注释·快捷键·方法·取消注释
luckilyil2 天前
前端—Cursor编辑器
前端·编辑器
一棵开花的树,枝芽无限靠近你2 天前
【PPTist】开源PPT编辑器初体验
编辑器·powerpoint