C#时间轴曲线图形编辑器开发2-核心功能实现

目录

三、关键帧编辑

1、新建Winform工程

(1)界面布局

 (2)全局变量

2、关键帧添加和删除

(1)鼠标在曲线上识别

(2)键盘按键按下捕捉

(3)关键帧添加、删除

 (4)修改关键帧值

3、曲线插值

(1)三次样条插值

(2)工程代码下载链接

四、曲线数据导出和读取

1、数据导出

 (1)添加导出按钮

(2)工程代码下载链接

2、读取导出的数据

(1)手动按钮创建曲线

 (2)读取导出得关键帧数据

 (3)工程代码下载链接

五、全部工程下载

1、基本功能

2、核心功能


接上一节《C#时间轴曲线图形编辑器开发1-基本功能》继续。

C#时间轴曲线图形编辑器开发1-基本功能_Big_潘大师的博客-CSDN博客

三、关键帧编辑

1、新建Winform工程

重新创建新的Winform工程测试

(1)界面布局

 (2)全局变量

        KeyDatasClass _keyData = new KeyDatasClass();SplineEdit cureDraw;float tensionSpline1 = 0.5f;                    //曲线1粗细//曲线编辑Dictionary<int, float> keyListDatas = new Dictionary<int, float>();     //关键帧集合Color editSplineColor = Color.Blue;                  //曲线颜色bool isMouseOnKeyPoint = false;     //鼠标是否在关键点上检测int dataLength = 0;float[] myEditDatas;                //绘制的曲线数据    int[] KeyDatasflag;                 //当前曲线数据是否是关键帧数据标值位float[] keyEditDatas_X;             //关键帧-方框点绘制-X轴数据float[] keyEditDatas_Y;             //关键帧-方框点绘制-X轴数据bool isDataEdit = false;            //关键帧是否要编辑bool isKeyEditDataMoveCan = false;int keyEditFrame = 0;               //当前要编辑的关键帧数据//鼠标当前在画图面板上的像素坐标,对应的坐标轴数值bool isMouseOnSpline;           //鼠标在曲线上检测int currentValue_X;float currentValue_Y;int last_CurrentValueX = 0;int ex = 0, ey = 0;             //鼠标的坐标值bool isLeftButtonDowm = false, isMiddleButtonDown = false, isRightButtonDown = false;bool isCtrlKeyDown = false;bool isShiftDown = false;

2、关键帧添加和删除

当检测鼠标在曲线上的时候,通过键盘和鼠标的组合按键操作,可以进行关键帧添加和删除操作。

(1)鼠标在曲线上识别

重新整理曲线识别代码,将其代码封装。将封装好的代码放在timer1_Tick中执行

        /// <summary>/// 检测鼠标是否在绘制得曲线上/// </summary>private void MouseOnSplineCheck(){//if (currentValue_X < dataLength){int nIndex = currentValue_X;if (nIndex >= myEditDatas.Length){nIndex = myEditDatas.Length - 1;if (nIndex < 0){nIndex = 0;}}float selectValue_Y = myEditDatas[nIndex];if (Math.Abs(selectValue_Y - currentValue_Y) < cureDraw.YSliceValue / 8){isMouseOnSpline = true;labMousePos.ForeColor = editSplineColor;}else{isMouseOnSpline = false;labMousePos.ForeColor = Color.Black;}}else{labMousePos.ForeColor = Color.Black;}}

(2)键盘按键按下捕捉

①在窗口属性中找到KeyPreview设置为True

 ②在窗口事件程序中分别添加KeyDowm、KeyUp事件函数

 MainForm_KeyDown():

        private void MainForm_KeyDown(object sender, KeyEventArgs e){Keys k = e.KeyCode;if (k == Keys.ControlKey){isCtrlKeyDown = true;}if (k == Keys.ShiftKey){isShiftDown = true;}}

MainForm_KeyUp():

        private void MainForm_KeyUp(object sender, KeyEventArgs e){Keys k = e.KeyCode;if (k == Keys.ControlKey){isCtrlKeyDown = false;}if (k == Keys.ShiftKey){isShiftDown = false;}}

(3)关键帧添加、删除

①关键帧添加

鼠标在曲线上的时候,Ctrl键+鼠标左键按下,添加关键帧

②关键帧删除

鼠标在曲线上的时候,Shift键+鼠标左键按下,删除当前关键帧

③添加pictureBox1控件的单击事件函数:pictureBox1_Click

        private void pictureBox1_Click(object sender, EventArgs e){//添加关键帧数据if(isMouseOnSpline && isCtrlKeyDown){if (currentValue_X > dataLength){MessageBox.Show("数据超过极限范围");return;}//判断关键帧是否可以添加,判断当前要添加的关键帧是否已经存在bool isKeyListCanAdd = false;for (int i = 0; i < keyEditDatas_X.Length; i++){if(currentValue_X!=keyEditDatas_X[i]){isKeyListCanAdd = true;}}for (int i = 0; i < keyEditDatas_X.Length; i++){if (currentValue_X == keyEditDatas_X[i]){isKeyListCanAdd = false;}}if (isKeyListCanAdd){//keyListDatas.Add(currentValue_X, currentValue_Y);     //关键帧值设置为当前位置值keyListDatas.Add(currentValue_X, 0.0f);                 //关键帧值设置为0}                }//删除当前关键帧if (isKeyEditDataMoveCan && isShiftDown){keyListDatas.Remove(keyEditFrame);  //删除关键帧数据myEditDatas[keyEditFrame] = 0.0f;   //将曲线数据值修改为0}}

 (4)修改关键帧值

     修改关键帧值,除了鼠标拖动外,还要需要可以手动输入操作,这样可以精确的给定关键帧值。

     实现方法,鼠标在关键帧的时候右键双击,弹出输入对话框。

①新建窗口

 打开该窗口读取当前关键帧值、点击确定或者按回车将输入的值传到关键帧上。

窗口需要设置键盘识别

添加键盘事件:KeyDataSetForm_KeyDown

判断Enter键按下

        private void KeyDataSetForm_KeyDown(object sender, KeyEventArgs e){Keys k = e.KeyCode;if (k == Keys.Enter){_Son.KeyDataSet = float.Parse(txtKeyDataSet.Text);this.Close();}if (k == Keys.Escape){this.Close();}}

使用构造函数的方法进行窗口传值。(操作方法见工程代码)

②pictureBox1控件添加双击事件:pictureBox1_DoubleClick

        private void pictureBox1_DoubleClick(object sender, EventArgs e){if (isRightButtonDown && (label7.BackColor == Color.Lime)){//MessageBox.Show("123");KeyDataSetForm kdf = new KeyDataSetForm(this, _keyData);_keyData.KeyDataSet = currentValue_Y;kdf.ShowDialog();keyListDatas[keyEditFrame] = _keyData.KeyDataSet;}}

3、曲线插值

     插值的目的是,根据当前关键帧的值得到一条完整的连续、圆滑曲线。这时就需要用到插值法,来计算曲线数组每个元素的值。

(1)三次样条插值

插值代码见程序工程。方法是,首先要将关键帧数据进行排序,然后再插值计算。

修改KeyDataTrans()代码,添加三次样条插值

        /// <summary>/// 关键帧数据集合转数组/// </summary>private void KeyDataTrans(){//关键点得数据值,对应到曲线数据上foreach (KeyValuePair<int, float> item in keyListDatas){int key = item.Key;float fValue = item.Value;//myEditDatas[key] = fValue;KeyDatasflag[key] = 1;          //关键帧所在的地方设置为1}//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();//三次样条插值PointClass[] points = new PointClass[keyEditDatas_X.Length];for (int i = 0; i < keyEditDatas_X.Length; i++){points[i] = new PointClass();points[i].x = keyEditDatas_X[i];points[i].y = keyEditDatas_Y[i];}PointClass.DeSortX(points); //排序try{double[] xs = new double[myEditDatas.Length];for (int i = 0; i < myEditDatas.Length; i++){xs[i] = i;}double[] Y = DataInterpolation.SplineInsertPoint(points, xs, 1);if (Y.Length == myEditDatas.Length){for (int i = 0; i < myEditDatas.Length; i++){myEditDatas[i] = (float)Y[i];}}}catch { }}

 未使用插值的曲线

 使用三次样条插值后曲线

 使用三次样条插值后操作演示

(2)工程代码下载链接

代码仅个人使用

链接:https://pan.baidu.com/s/1WLov3JLowmw_YS_wQT7QNw 
提取码:fi2f 
--来自百度网盘超级会员V4的分享

四、曲线数据导出和读取

1、数据导出

导出数据要求:数据分三列,第一列是行号、第二列是曲线数据、第三列是关键帧标志位。

 (1)添加导出按钮

btnSplineDataExport_Click():

        private void btnSplineDataExport_Click(object sender, EventArgs e){if (myEditDatas.Length < 1){MessageBox.Show("请先添加数据", "提示");return;}//string[] newLines = new string[myEditDatas.Length];for (int i = 0; i < myEditDatas.Length; i++){if (KeyDatasflag[i] == 1){newLines[i] = "L" + i.ToString() + "\t" + myEditDatas[i].ToString("0.00") + "\t" + "1";}else{newLines[i] = "L" + i.ToString() + "\t" + myEditDatas[i].ToString("0.00") + "\t" + "0";}//newLines[i] = myEditDatas[i].ToString("0.00");}string path = @"C:\Users\Administrator\Desktop\TestData.txt";using (System.IO.StreamWriter file = new System.IO.StreamWriter(path, true)){foreach (string line in newLines){file.WriteLine(line);// 直接追加文件末尾,换行 //file.Write(line);//直接追加文件末尾,不换行}}MessageBox.Show("导出完成", "提示");}

 曲线编辑和数据导出

(2)工程代码下载链接

代码仅个人使用

链接:https://pan.baidu.com/s/1Gpbe-fJGOqLdj7VxFTDdcg 
提取码:vyyq 
--来自百度网盘超级会员V4的分享

2、读取导出的数据

(1)手动按钮创建曲线

将窗口登陆时添加的关键帧数据,改成手动点击按钮生成。

 btnDataCreate_Click()

        private void btnDataCreate_Click(object sender, EventArgs e){//曲线编辑器数据dataLength = int.Parse(txtDataLength.Text);myEditDatas = new float[dataLength];KeyDatasflag = new int[myEditDatas.Length];//字典集合中添加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();}

修改MainForm_Load()

        private void MainForm_Load(object sender, EventArgs e){//timer1.Start();timer2.Start();cureDraw = new SplineEdit(pictureBox1.Height, pictureBox1.Width);DrawCure();}

修改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();//当前帧指示线cureDraw.DrawCurrentLine(last_CurrentValueX);if (isLeftButtonDowm){last_CurrentValueX = currentValue_X;if (myEditDatas != null){if (last_CurrentValueX > myEditDatas.Length){last_CurrentValueX = myEditDatas.Length;}}}//曲线编辑器if (keyListDatas.Count > 0)//if (keyEditDatas_Y.Length > 0)      //绘制关键帧{cureDraw.DrawPoint(keyEditDatas_X, keyEditDatas_Y, editSplineColor, tensionSpline1, true);}if (dataLength > 0)//if (myEditDatas.Length > 0)         //绘制曲线{cureDraw.DrawSpline(myEditDatas, editSplineColor, 0.5f, false);}}

 (2)读取导出得关键帧数据

btnSplineDataRead():

        private void btnSplineDataRead_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;float[] axis1Datas;string[] lines = File.ReadAllLines(ofd.FileName, Encoding.Default).ToArray();dataColumNums = CharNum(lines[0], "\t");dataLineNums = lines.Length;axisDataArrayRead = new double[dataColumNums, dataLineNums];axis1Datas = new float[dataLineNums];//解析数据for (int i = 0; i < lines.Length; i++){string[] seg = lines[i].Split('\t');         //英文逗号分隔符for (int j = 1; j < dataColumNums; j++)     //  {axisDataArrayRead[j, i] = Convert.ToDouble(seg[j]);}}//轴1数据for (int i = 0; i < lines.Length; i++){axis1Datas[i] = (float)axisDataArrayRead[1, i];}MessageBox.Show("读取完毕,总共 " + dataLineNums.ToString() + " 行", "提示");//dataLength = dataLineNums;myEditDatas = new float[dataLength];KeyDatasflag = new int[myEditDatas.Length];for (int i = 0; i < dataLength; i++){myEditDatas[i] = (float)axisDataArrayRead[1, i];KeyDatasflag[i] = (int)axisDataArrayRead[2, i];if (KeyDatasflag[i] == 1){keyListDatas.Add(i, myEditDatas[i]);}}}catch{MessageBox.Show("文件解析异常", "提示");}}

 (3)工程代码下载链接

3

链接:https://pan.baidu.com/s/1uj4xVkTCpGVTarcqiJCxGA 
提取码:w1o9 
--来自百度网盘超级会员V4的分享

4

链接:https://pan.baidu.com/s/1Ly3mKv2WZ9mfNCvjKAIDPA 
提取码:zydp 
--来自百度网盘超级会员V4的分享

五、全部工程下载

1、基本功能

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

2、核心功能

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

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

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

相关文章

Unity自定义后处理——用偏导数求图片颜色边缘

大家好&#xff0c;我是阿赵。   继续介绍屏幕后处理效果的做法。这次介绍一下用偏导数求图形边缘的技术。 一、原理介绍 先来看例子吧。   这个例子看起来好像是要给模型描边。之前其实也介绍过很多描边的方法&#xff0c;比如沿着法线方向放大模型&#xff0c;或者用Ndo…

Palo Alto Networks 智能网络安全保护任何地方的用户、应用和数据

Palo Alto Networks 不仅能够为数字企业提供当下所需的网络安全服务&#xff0c;还能为日后的工作打好安全基础&#xff0c;让企业无需在二者间权衡和纠结&#xff0c;这样的网络安全合作伙伴仅此一家。我们承诺将双管齐下&#xff0c;在保障数字企业的安全方面绝不妥协退让。下…

专题-【排序比较】

时间最好&#xff1a;直接和冒泡&#xff0c;n 堆排序和归并时间固定&#xff1a;nlog2n 空间&#xff1a;归并&#xff0c;n&#xff1b;快速&#xff0c;log2n 稳定&#xff1a;直冒归基

Sharding-JDBC分布式事务详解与实战

&#x1f680; ShardingSphere &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&…

K8s卷存储详解(二)

K8s卷存储详解&#xff08;二&#xff09; 持久卷持久卷&#xff08;PV&#xff09;持久卷申领&#xff08;PVC&#xff09;存储类&#xff08;StorageClass&#xff09;存储制备器&#xff08;Provisioner&#xff09;PV和PVC的生命周期持久卷的类型CSI 卷快照CSI 卷克隆 投射…

多租户的低代码平台,Saas开发平台:MateCloud

简介 MateCloud是一款基于Spring Cloud Alibaba的微服务架构。目前已经整合Spring Boot 2.7.0、 Spring Cloud 2021、Spring Cloud Alibaba 2021、Spring Security Oauth2、Feign、Dubbo、JetCache、RocketMQ等&#xff0c;支持多租户的低代码平台&#xff0c;Saas平台开发套件…

Elasticsearch-增删改查数据工作原理

集群 集群的基本概念&#xff1a; 集群&#xff1a;ES 集群由一个或多个 Elasticsearch 节点组成&#xff0c;每个节点配置相同的 cluster.name 即可加入集群&#xff0c;默认值为 “elasticsearch”。节点&#xff1a;一个 Elasticsearch 服务启动实例就是一个节点&#xff…

【Linux】线程同步条件变量

目录 1 线程同步的引入 2 条件变量&线程同步&竞争条件的概念 3 条件变量相关函数 初始化 销毁 等待条件满足 唤醒等待 4 demo代码——理解条件变量&线程同步 5 为什么 pthread_cond_wait 需要互斥量? 6 条件变量使用规范 1 线程同步的引入 例子生活化&…

【多任务编程-线程通信】

进程/线程通信的方式 某些应用程序中&#xff0c;进程/进程和线程/线程之间不可避免的进行通信&#xff0c;进行消息传递&#xff0c;数据共享等 同一进程的线程之间通信方式包括Windows中常用Event, Message等。 不同进程之间的通信可以利用Event, FileMapping(内存共享), W…

24考研数据结构-栈和队列的应用

目录 3.3.1栈在括号匹配中的应用流程图算法代码 3.3.2栈在表达式求值中的应用1. 中缀表达式 (需要界限符)2. 后缀表达式 (逆波兰表达式)中缀表达式转后缀表达式-手算重点&#xff1a;中缀表达式转后缀表达式-机算重点&#xff1a;后缀表达式的计算—机算 3.前缀表达式 (波兰表达…

杭电oj Simple Set Problem 双指针 尺取法 满注释版

&#x1f468;‍&#x1f3eb; 题目地址 输入 3 2 1 6 3 -7 7 10 4 9 -5 -9 2 8 5 4 3 3 8 2 10 8 1 -7 3 1 6 10 1 1 9输出 1 15 0使用快读&#xff0c;避免使用 Arrays.fill() 按需初始化 避免卡常 &#x1f351; 思路 &#x1f37a; AC code import java.io.*; import ja…

机器学习李宏毅学习笔记39

文章目录 前言一、大模型的发展趋势二、KNN LM总结 前言 大模型大资料 大模型的顿悟时刻 一、大模型的发展趋势 随数据量增加&#xff0c;模型可以从量变达到质变&#xff0c;从某一刻开始突然学会东西。 当成为大模型时&#xff0c;分数会从0,0突然变成100&#xff0c;完成“…

【Matplotlib 绘制折线图】

使用 Matplotlib 绘制折线图 在数据可视化中&#xff0c;折线图是一种常见的图表类型&#xff0c;用于展示随着变量的变化&#xff0c;某个指标的趋势或关系。Python 的 Matplotlib 库为我们提供了方便易用的功能来绘制折线图。 绘制折线图 下面的代码展示了如何使用 Matplo…

基于Centos 7虚拟机的磁盘操作(添加磁盘、分区、格式分区、挂载)

目录 一、添加硬盘 二、查看新磁盘 三、磁盘分区 3.1新建分区 3.2 格式分区 3.3 挂载分区 3.4 永久挂载新分区 3.5 取消挂载分区 一、添加硬盘 1.在虚拟机处选择编辑虚拟机设置&#xff0c;然后选择添加 2.选择硬盘&#xff0c;然后选择下一步 3.默认即可&#xff0c;下一步…

【6】toLocaleString、toLocaleDateString、toLocaleTimeString等toLocale系列方法的使用及案例

一、介绍 在当今前端开发的领域里&#xff0c;快速、高效的项目构建工具以及使用最新技术栈是非常关键的。ViteVue3 组合为一体的项目实战示例专栏将带领你深入了解和掌握这一最新的前端开发工具和框架。 作为下一代前端构建工具&#xff0c;Vite 在开发中的启动速度和热重载…

C语言:动态内存管理

文章目录 一、动态内存函数1. malloc2. calloc3. realloc4. free 二、常见的错误1.malloc或calloc开辟的空间未检查2.越界访问3.对非malloc和calloc开辟的空间&#xff0c;用free释放4.对同一块动态内存多次释放5.用free释放动态内存的一部分 三、通讯录(动态版本改写)总结 一、…

uni-app:模态框的实现(弹窗实现)

效果图 代码 标签 <template><view><!-- 按钮用于触发模态框的显示 --><button click"showModal true">显示模态框</button><!-- 模态框组件 --><view class"modal" v-if"showModal"><view cla…

探索APP开发的新趋势:人工智能和大数据的力量

随着5G技术的不断发展&#xff0c;人工智能和大数据将会更加广泛的应用于我们生活和工作中&#xff0c;作为 APP开发公司&#xff0c;应该及时的对新技术进行研发&#xff0c;进而更好的为用户服务。目前 APP开发已经不是传统的软件开发了&#xff0c;而是向移动互联网转型&…

iPhone 开机停留在苹果logo画面(已解决)

一、问题 如下图&#xff0c;开不了机&#xff1a; 标题 二、根因 存储空间满了。 三、解决方法 方法一 用苹果数据线&#xff08;最好是原装&#xff09;连接Mac电脑&#xff0c;在装有 macOS Catalina 10.15 或更高版本的 Mac 上&#xff0c;打开“访达”。在装有 macOS…

六、代理模式

文章目录 一、代理模式1、代理模式的好处和缺点1.1 代理模式理解加深 一、代理模式 为什么要学习代理模式&#xff1f; 代理模式是Spring AOP 以及 Spring MVC 的底层&#xff01;&#xff01;并且还是 JAVA 的23种设计模式之一&#xff01;&#xff01; 代理模式的分类&#…