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

目录

一、前言

1、简介

2、开发过程

3、工程下载链接

二、基本功能实现

1、绘图面板创建

(1)界面布置

(2)显示面板代码

(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) 添加几个测试的关键帧数据

(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)曲线数据变量定义

        //曲线1bool isSpline1Show = false;float[] xLineDatas = new float[501];            //保存绘制的点 x、y轴位置float[] yLineDatas = new float[501];Color spline1Color = Color.Green;               //曲线1颜色  float tensionSpline1 = 0.5f;                    //曲线1粗细
        //曲线2bool 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的分享

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/11029.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构】实验五:栈

实验五 栈 一、实验目的与要求 1&#xff09;熟悉栈的类型定义和基本操作&#xff1b; 2&#xff09;灵活应用栈解决具体应用问题。 二、实验内容 1、判断回文数&#xff0c;回文是指正读反读均相同的字符序列&#xff0c;如“1221”和“12321”均是回文&#xff0c;但“…

与传统透明屏相比,BOE透明屏有哪些特点优势?

BOE透明屏是一种新型的显示技术&#xff0c;它能够实现透明度高达90%以上的显示效果。这种屏幕可以应用于各种领域&#xff0c;如商业展示、智能家居、汽车行业等&#xff0c;具有广阔的市场前景。 BOE透明屏采用了先进的光学技术&#xff0c;通过控制光的传播和折射&#xff…

自建纯内网iot平台服务,软硬件服务器全栈实践

基于以下几个考虑&#xff0c;自制硬件设备&#xff0c;mqtt内网服务器。 1.米家app不稳定&#xff0c;逻辑在云端或xiaomi中枢网关只支持少部分在本地计算。 2.监控homeassistant官方服务有大量数据交互。可能与hass安装小米账户有关。 3.硬件&#xff1a;原理图&#xff0c;l…

【KVC补充 Objective-C语言】

一、KVC补充 好,那么接下来,再给大家说一下这个KVC 1.首先我们说,这个KVC,就是指的什么 key value coding 吧 全称就是叫做(Key Value Coding),这是它的全称 那么,你在帮助文档里面搜的时候,你就搜key-value coding 是不是这个啊,key-value coding 然后点击,进…

Unity XML3——XML序列化

一、XML 序列化 ​ 序列化&#xff1a;把对象转化为可传输的字节序列过程称为序列化&#xff0c;就是把想要存储的内容转换为字节序列用于存储或传递 ​ 反序列化&#xff1a;把字节序列还原为对象的过程称为反序列化&#xff0c;就是把存储或收到的字节序列信息解析读取出来…

尚医通06:数据字典+EasyExcel+mongodb

内容介绍 1、数据字典列表前端 2、EasyExcel介绍、实例 3、数据字典导出接口、前端 4、数据字典导入接口、前端 5、数据字典添加redis缓存 6、MongoDB简介 7、MongoDB安装 8、MongoDB基本概念 数据字典列表前端 1、测试问题 &#xff08;1&#xff09;报错日志 &am…

SpringBoot复习:(4)打成的jar包是如何启动的?

jar包通过MANIFEST的Main-Class指定了主类JarLauncher, JarLauncher的main方法代码如下&#xff1a; 其中调用的launch的代码如下&#xff1a; 首先&#xff0c;创建了一个自定义的ClassLoader,代码如下&#xff1a; 其中调用的重载的createClassLoader代码如下&#xff1…

STM32MP157驱动开发——按键驱动(中断)

文章目录 编写使用中断的按键驱动程序编程思路设备树相关驱动代码相关 代码修改设备树文件gpio_key_drv.cMakefile编译测试 编写使用中断的按键驱动程序 对于使用中断的按键驱动&#xff0c;内核自带的驱动程序 drivers/input/keyboard/gpio_keys.c 就可以&#xff0c;需要做的…

cpolar内网穿透工具

文章目录 cpolar内网穿透工具 cpolar内网穿透工具 科学技术的发展日新月异&#xff0c;电子设备在人们的生活中已成为不可或缺的工具&#xff0c;甚至在很多情况下&#xff0c;各类型的电子设备已经成为工作的核心&#xff0c;虽然移动设备越来越小巧&#xff0c;功能也越来越…

基于netlify生成custom SSL certificate

&#xff08;1&#xff09;腾讯云申请 &#xff08;2&#xff09;域名控制台解析 &#xff08;3&#xff09;Nginx下载&#xff08;crt: CA certificate Chain)

SpringBoot 8种异步实现方式

前言&#xff1a;异步执行对于开发者来说并不陌生&#xff0c;在实际的开发过程中&#xff0c;很多场景多会使用到异步&#xff0c;相比同步执行&#xff0c;异步可以大大缩短请求链路耗时时间&#xff0c;比如&#xff1a;「发送短信、邮件、异步更新等」&#xff0c;这些都是…

如何解决大数据下滚动页面卡顿问题

原文合集地址如下&#xff0c;有需要的朋友可以关注 本文地址 合集地址 前言 之前遇到不分页直接获取到全部数据&#xff0c;前端滚动查看数据&#xff0c;页面就听卡顿的&#xff0c;当然这和电脑浏览器性能啥的还是有点关系。但根源还是一次性渲染数据过多导致的&#xf…

【C++从0到王者】第十三站:vector源码分析及手把手教你如何写一个简单的vector

文章目录 一、vector的源码分析1.分析思路2.构造函数和插入接口 二、手把手教你写一个简单的vector1.基本结构2.迭代器与私有成员变量的定义3.构造函数4.size和capacity5.迭代器函数接口6.析构函数7.reserve接口8.尾插9.operator[]运算符重载10.简单的测试前面的接口11.insert以…

【043】解密C++ STL:深入理解并使用 list 容器

解密C STL&#xff1a;深入理解并使用list容器 引言一、list 容器概述二、list容器常用的API2.1、构造函数2.2、数据元素插入和删除操作2.3、大小操作2.4、赋值操作2.5、数据的存取2.6、list容器的反转和排序 三、使用示例总结 引言 &#x1f4a1; 作者简介&#xff1a;一个热爱…

2023年深圳杯数学建模D题基于机理的致伤工具推断

2023年深圳杯数学建模 D题 基于机理的致伤工具推断 原题再现&#xff1a; 致伤工具的推断一直是法医工作中的热点和难点。由于作用位置、作用方式的不同&#xff0c;相同的致伤工具在人体组织上会形成不同的损伤形态&#xff0c;不同的致伤工具也可能形成相同的损伤形态。致伤…

7D透明屏的市场应用广泛,在智能家居中有哪些应用表现?

7D透明屏是一种新型的显示技术&#xff0c;它能够实现透明度高达70%以上的显示效果。这种屏幕可以应用于各种领域&#xff0c;如商业广告、展览展示、智能家居等&#xff0c;具有广阔的市场前景。 7D透明屏的工作原理是利用光学投影技术&#xff0c;将图像通过透明屏幕投射出来…

Talk | 南洋理工大学博士后研究员李祥泰:基于Transformer的视觉分割模型总结、回顾与展望

​ 本期为TechBeat人工智能社区第517期线上Talk&#xff01; 北京时间7月27日(周四)20:00&#xff0c;南洋理工大学博士后研究员—李祥泰的Talk已经准时在TechBeat人工智能社区开播了&#xff01; 他与大家分享的主题是: “基于Transformer的视觉分割模型总结、回顾与展望”&am…

C#多线程

C#多线程 C#多线程是C#学习中必不可少的知识&#xff0c;在实际开发中也能有效的提升用户体验&#xff0c;和程序性能。 文章目录 C#多线程前言一、什么是线程、什么是进程、什么是协程&#xff1f;协程优点缺点 线程优点缺点&#xff1a; 进程优点缺点&#xff1a; 二、C# 中…

使用Spring Boot实现Redis键过期回调功能

使用Spring Boot实现Redis键过期回调功能 当使用Redis作为缓存或数据存储的时候&#xff0c;有时候需要在键过期时执行一些特定的操作&#xff0c;比如清除相关数据或发送通知。在Spring Boot中&#xff0c;可以通过实现RedisMessageListener接口来实现Redis键过期回调功能。下…

基于“RWEQ+”集成技术在土壤风蚀模拟与风蚀模数估算、变化归因分析中的实践应用及SCI论文撰写

查看原文>>>基于“RWEQ”集成技术在土壤风蚀模拟与风蚀模数估算、变化归因分析中的实践应用及SCI论文撰写 土壤风蚀是一个全球性的环境问题。中国是世界上受土壤风蚀危害最严重的国家之一&#xff0c;土壤风蚀是中国干旱、半干旱及部分湿润地区土地荒漠化的首要过程。…