当前位置: 首页 > news >正文

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的分享

相关文章:

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

目录 一、前言 1、简介 2、开发过程 3、工程下载链接 二、基本功能实现 1、绘图面板创建 &#xff08;1&#xff09;界面布置 &#xff08;2&#xff09;显示面板代码 &#xff08;3&#xff09; 面板水平方向、竖直方向移动功能实现 &#xff08;4&#xff09;面板放…...

elasticsearch查询操作(DSL语句方式)

说明&#xff1a;本文介绍在kibana&#xff0c;es的可视化界面上对文档的查询操作&#xff1b; 添加数据 先使用API&#xff0c;创建索引库&#xff0c;并且把数据从MySQL中查出来&#xff0c;传到ES上&#xff0c;参考&#xff08;http://t.csdn.cn/NaTHg&#xff09; 索引库…...

JavaScript详解

目录 一、JavaScript是什么&#xff1f; 1.1、JavaScript 和 HTML 和 CSS 之间的关系 1.2、JavaScript 运行过程 1.3、JavaScript 的组成 二、JavaScript 的书写形式 1. 行内式 2. 内嵌式 3、外部式 注释 三、输入输出 输入: prompt 输出: alert 输出: …...

电缆振荡波局部放电检测定位技术

电缆振荡波检测技术主要用于交联聚乙烯电力电缆检测&#xff0c;是属于离线检测的一种有效形式 。该技术基于LCR阻尼振荡原理&#xff0c;在完成电缆直流充电的基础上&#xff0c;通过内置的高压电抗器、高压实时固态开关与试品电缆形成阻尼振荡电压波&#xff0c;在试品电缆上…...

AI Chat 设计模式:10. 组合模式

本文是该系列的第八篇&#xff0c;采用问答式的方式展开&#xff0c;问题由我提出&#xff0c;答案由 Chat AI 作出&#xff0c;灰色背景的文字则主要是我的一些思考和补充。 问题列表 Q.1 给我介绍一下组合模式A.1Q.2 好的&#xff0c;给我举一个组合模式的例子&#xff0c;使…...

【Nginx12】Nginx学习:HTTP核心模块(九)浏览器缓存与try_files

Nginx学习&#xff1a;HTTP核心模块&#xff08;九&#xff09;浏览器缓存与try_files 浏览器缓存在 Nginx 的 HTTP 核心模块中其实只有两个简单的配置&#xff0c;这一块也是 HTTP 的基础知识。之前我们就一直在强调&#xff0c;学习 Nginx 需要的就是各种网络相关的基础知识&…...

【1】-Locust性能测试工具介绍与安装

Locust介绍 locust是一个开源的压测工具&#xff0c;其官网地址是Locust - A modern load testing framework&#xff0c;通过编写Python代码&#xff0c;可以轻松实现百万级的并发&#xff0c;相对于我们熟悉的Jmeter来说&#xff0c;其对压测机的要求更低&#xff0c;而且使…...

基于拉格朗日-遗传算法的最优分布式能源DG选址与定容(Matlab代码实现)

目录 1 概述 2 数学模型 2.1 问题表述 2.2 DG的最佳位置和容量&#xff08;解析法&#xff09; 2.3 使用 GA 进行最佳功率因数确定和 DG 分配 3 仿真结果与讨论 3.1 33 节点测试配电系统的仿真 3.2 69 节点测试配电系统仿真 4 结论 1 概述 为了使系统网损达到最低值&a…...

【已解决】jupyter notebook里已经安装了第三方库,还是提示导入失败

在jupyter notebook中运行Python代码&#xff0c;明明已经安装了第三方库&#xff0c;还是提示导入失败。 以导入pandas库为例&#xff0c;其他库同理&#xff1a; 报错代码&#xff1a; import pandas报错原因&#xff1a; 电脑上存在多个python运行环境&#xff08;比如&a…...

Mybatis使用collection映射一对多查询分页问题

场景&#xff1a;页面展示列表&#xff0c;需要查询多的字段&#xff0c;和一的字段。并且还要分页。 这时候直接想到的是手写sql。 /*** 标签*/private List<BasicResidentTags> tags;Data TableName("basic_resident_tags") public class BasicResidentTag…...

Linux/Windows路由管理

本文主要介绍如果通过linux/Windows命令添加IPV6地址&#xff0c;查看添加IPV6默认路由&#xff0c;查看IPV6邻居缓存 一、Linux 1、查看地址 IPV4: route netstat -route ip route IPV6: ip -6 route show route -A inet6 route -62、添加IPV6地址 ip -6 addr add <…...

openpnp - 设备矫正的零碎记录

文章目录 openpnp - 设备矫正的零碎记录概述笔记设备内部不能有任何强干扰源相机就选100W像素的就行, 没有特殊要求openpnp软件的选择视觉归位必须禁止轴的赤隙矫正不用做运行openpnp软件的计算机, 必须是台式机校验完成后, 数据占用的体积END openpnp - 设备矫正的零碎记录 概…...

Linux内核中的链表、红黑树和KFIFO

lLinux内核代码中广泛使用了链表、红黑树和KFIFO。 一、 链表 linux内核代码大量使用了链表这种数据结构。链表是在解决数组不能动态扩展这个缺陷而产生的一种数据结构。链表所包含的元素可以动态创建并插入和删除。链表的每个元素都是离散存放的&#xff0c;因此不需要占用连…...

【C++】做一个飞机空战小游戏(二)——利用getch()函数实现键盘控制单个字符移动

[导读]本系列博文内容链接如下&#xff1a; 【C】做一个飞机空战小游戏(一)——使用getch()函数获得键盘码值 【C】做一个飞机空战小游戏(二)——利用getch()函数实现键盘控制单个字符移动 在【C】做一个飞机空战小游戏(一)——使用getch()函数获得键盘码值一文中介绍了如何利用…...

Android 设备兼容性使用(详细版)

经典好文推荐,通过阅读本文,您将收获以下知识点: 一、设备兼容性分类 二、硬件设备兼容 三、软件 APP 兼容 四、兼容不同语言 五、兼容不同分辨率 六、兼容不同屏幕方向布局 七、兼容不同硬件 Feature 八、兼容不同SDK平台 一、设备兼容性分类 Android设计用于运行在许多不同…...

React 中的常见 API 和生命周期函数

目录 useStateuseEffectuseRefdangerouslySetInnerHTML生命周期函数 constructorcomponentDidMountstatic getDerivedStateFromPropsshouldComponentUpdatecomponentDidUpdatecomponentWillUnmount useState useState 是 React 的一个 Hook&#xff0c;用于在函数组件中添加…...

神经网络中遇到的 python 函数(Pytorch)

1.getattr() 函数用于返回一个对象属性值。 def getattr(object, name, defaultNone): # known special case of getattr"""getattr(object, name[, default]) -> valueGet a named attribute from an object; getattr(x, y) is equivalent to x.y.When a …...

分布式事务及解决方案

1、分布式事务 分布式事务就是在一个交易中各个服务之间的相互调用必须要同时成功或者同时失败&#xff0c;保持一致性和可靠性。在单体项目架构中&#xff0c;在多数据源的情况下也会发生 分布式事务问题。本质上来说&#xff0c;分布式事务就是为了保证不同数据库的数据一致性…...

【宏定义】——编译时校验

文章目录 编译时校验功能描述代码实现示例代码正常编译示例编译错误示例预处理之后的结果 代码解析!!estruct {int:-!!(e); }sizeof(struct {int:-!!(e); }) 参考代码 编译时校验 功能描述 用于在编译时检查一个条件是否为真&#xff0c;如果条件为真则会编译失败&#xff0c…...

C#学习系列之System.Windows.Data Error: 40报错

C#学习系列之System.Windows.Data Error: 40报错 前言报错内容解决总结 前言 在用户界面使用上&#xff0c;代码运行没有问题&#xff0c;但是后台报错&#xff0c;仔细研究了报错内容&#xff0c;解决问题&#xff0c;所以记录一下。 报错内容 System.Windows.Data Error: 4…...

【java安全】RMI

文章目录 【java安全】RMI前言RMI的组成RMI实现Server0x01 编写一个远程接口0x02 实现该远程接口0x03 Registry注册远程对象 Client 小疑问RMI攻击 【java安全】RMI 前言 RMI全称为&#xff1a;Remote Method Invocation 远程方法调用&#xff0c;是java独立的一种机制。 RM…...

rcu链表综合实践

基础知识 rcu-read copy update的缩写。和读写锁起到相同的效果。据说牛逼一点。对于我们普通程序员&#xff0c;要先学会使用&#xff0c;再探究其内部原理。 链表的数据结构&#xff1a; struct list_head {struct list_head *next, *prev; };还有一种&#xff1a;struct h…...

odoo16-python框架-动作

总结 1 模型和视图的 设计之美 view_ids, view_id,view_mode 最终目的都是为了生成views, 也就是视图. 模型是死的,像男人,一成不变 视图像女人,千变万化, 姿态万千 一阴一阳之谓道,设计之美又在这里得到了体现 2 所有的动作都可以通过web界面来配置 可以通过在"设…...

微信小程序——同一控件的点击与长按事件共存的解决方案

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…...

selenium自动化-获取元素属性信息

在写自动化过程中我们会想验证自己的代码是否正确&#xff0c;比如登录之后&#xff0c;通过用户名或其他信息来证明你登录成功&#xff0c;或者点击链接后&#xff0c;是否会跳转新的页面。通过获取元素属性信息&#xff0c;可以解决我们的疑惑。 一、获取内容对象的内容信息 …...

LabVIEW开发小型减阻试验平台

LabVIEW开发小型减阻试验平台 湍流摩擦在粘性流体的阻力中起着重要作用&#xff0c;减少湍流摩擦是流体力学领域的热门话题之一。在油气管道的长距离流体输送中&#xff0c;泵站提供的几乎所有动力都用于克服流体的胫骨摩擦。在流体输送领域&#xff0c;船舶的蒙皮摩擦阻力占总…...

解决分类任务中数据倾斜问题

大家好&#xff0c;在处理文本分类任务时&#xff0c;基准测试流行的自然语言处理架构的性能是建立对可用选项的理解的重要步骤。在这里&#xff0c;本文将深入探讨与分类相关的最常见的挑战之一——数据倾斜。如果你曾经将机器学习&#xff08;ML&#xff09;应用于真实世界的…...

Vue3 word如何转成pdf代码实现

&#x1f642;博主&#xff1a;锅盖哒 &#x1f642;文章核心&#xff1a;word如何转换pdf 目录 1.前端部分 2.后端部分 在Vue 3中&#xff0c;前端无法直接将Word文档转换为PDF&#xff0c;因为Word文档的解析和PDF的生成通常需要在后端进行。但是&#xff0c;你可以通过Vu…...

fpga--流水灯

fpga流水灯的设计 思路&#xff1a;外部时钟频率50mhz&#xff0c;若要实现每隔0.5s闪烁一次&#xff0c;则使用内部计数器计数到24999999拉高一个周期电平&#xff0c;当电平被拉高的时候&#xff0c;进行LED灯电平的设置&#xff0c;每次检测到高电平&#xff0c;就进行一位…...

51单片机:数码管和矩阵按键

目录 一:动态数码管模块 1:介绍 2:共阴极和共阳极 A:共阴极 B:共阳极 C:转化表 3:74HC138译码器 4:74HC138译码器控制动态数码管 5:数码管显示完整代码 二:矩阵按键模块 1:介绍 2:原理图 3:矩阵按键代码 一:动态数码管模块 1:介绍 LED数码管&#xff1a;数码管是一种…...