C#自定义曲线便器功能实现(简化版)

news2025/4/1 9:03:44

目录

一、曲线编辑器实现功能

二、实现方法说明

三、关键代码说明

1、绘制背景板和曲线

2、绘制坐标系面板

3、绘制曲线

四、工程下载连接


一、曲线编辑器实现功能

添加或者删除控制点,通过移动控制点来修改曲线形状

二、实现方法说明

            1、坐标系系统:

            使用0-500的范围映射到屏幕坐标系

            自动绘制网格线(间隔50单位)

            坐标轴显示在左侧和底部

            2、控制点功能:

            左键点击空白区域添加新控制点

            拖动现有控制点调整位置

            自动按X坐标排序保持曲线有效性

            限制控制点移动范围(防止越界和重叠)

           3、 曲线绘制:

            使用GraphicsPath.AddCurve方法实现平滑曲线

            张力系数设为0.5(可调整)

            实时更新曲线显示
          4、坐标转换:

            MapXToScreen/MapYToScreen:将逻辑坐标转换为屏幕坐标

            MapScreenToLogical:将屏幕坐标转换为逻辑坐标

         5、交互优化:

            双缓冲技术消除闪烁

            抗锯齿处理

            控制点吸附范围判断
        二、使用说明:

        点击空白区域添加新控制点

        拖动红色控制点调整曲线形状

        控制点自动按X坐标排序

        曲线实时更新

        三、扩展建议:

        添加右键菜单删除控制点

        实现曲线预设功能

        添加数值输入框精确调整坐标

        增加坐标轴标签

        实现数据导出/导入功能

        添加撤销/重做功能
         * 

        该实现提供了基本的曲线编辑功能,可以作为图像处理工具的基础模块,后续可根据具体需求添加更多高级功能。

三、关键代码说明

1、绘制背景板和曲线

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            //base.OnPaint(e);
            Graphics g = e.Graphics;
            g.SmoothingMode = SmoothingMode.AntiAlias;

            // 绘制坐标系背景
            DrawCoordinateSystem(g);

            // 绘制控制点
            foreach (var point in controlPoints)
            {
                DrawControlPoint(g, point);
            }

            // 绘制曲线
            if (controlPoints.Count >= 2)
            {
                DrawCurve(g);
            }
        }

2、绘制坐标系面板

private void DrawCoordinateSystem(Graphics g)
        {
            // 绘制坐标系背景
            g.FillRectangle(Brushes.White, coordinateArea);

            // 绘制坐标轴
            using (Pen axisPen = new Pen(Color.Black, 2))
            {
                // X轴
                g.DrawLine(axisPen,
                    coordinateArea.Left,
                    coordinateArea.Bottom,
                    coordinateArea.Right,
                    coordinateArea.Bottom);

                // Y轴
                g.DrawLine(axisPen,
                    coordinateArea.Left,
                    coordinateArea.Bottom,
                    coordinateArea.Left,
                    coordinateArea.Top);
            }

            // 绘制网格线
            using (Pen gridPen = new Pen(Color.LightGray, 1))
            {
                // X轴网格
                for (float x = 0; x <= 500; x += 50)
                {
                    float screenX = MapXToScreen(x);
                    g.DrawLine(gridPen, screenX, coordinateArea.Top, screenX, coordinateArea.Bottom);
                }

                // Y轴网格
                for (float y = 0; y <= 500; y += 50)
                {
                    float screenY = MapYToScreen(y);
                    g.DrawLine(gridPen, coordinateArea.Left, screenY, coordinateArea.Right, screenY);
                }
            }
        }

3、绘制曲线

        private void DrawCurve(Graphics g)
        {
            if (controlPoints.Count < 2) return;

            List<PointF> screenPoints = new List<PointF>();
            foreach (var point in controlPoints)
            {
                screenPoints.Add(new PointF(
                    MapXToScreen(point.X),
                    MapYToScreen(point.Y)));
            }

            // 使用GraphicsPath绘制平滑曲线
            using (GraphicsPath path = new GraphicsPath())
            using (Pen curvePen = new Pen(Color.Blue, 2))
            {
                path.AddCurve(screenPoints.ToArray(), 0.5f); // 张力系数
                g.DrawPath(curvePen, path);
            }
        }

4、鼠标按下添加控制点和移动控制点

        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            //base.OnMouseDown(e);

            // 检查是否点击控制点
            for (int i = 0; i < controlPoints.Count; i++)
            {
                PointF logicalPoint = controlPoints[i];
                float screenX = MapXToScreen(logicalPoint.X);
                float screenY = MapYToScreen(logicalPoint.Y);

                if (Math.Abs(e.X - screenX) < pointRadius * 2 &&
                    Math.Abs(e.Y - screenY) < pointRadius * 2)
                {
                    selectedPointIndex = i;
                    return;
                }
            }

            // 添加新点(限制在坐标系区域内)
            if (coordinateArea.Contains(e.Location))
            {
                PointF newPoint = MapScreenToLogical(e.Location);
                controlPoints.Add(newPoint);
                controlPoints.Sort((a, b) => a.X.CompareTo(b.X)); // 按X坐标排序
                selectedPointIndex = controlPoints.IndexOf(newPoint);
                this.Invalidate();
            }

        }
        private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            //base.OnMouseMove(e);

            if (selectedPointIndex >= 0 && e.Button == MouseButtons.Left)
            {
                // 限制移动范围在坐标系内
                float x = Math.Max(coordinateArea.Left, Math.Min(e.X, coordinateArea.Right));
                float y = Math.Max(coordinateArea.Top, Math.Min(e.Y, coordinateArea.Bottom));

                // 更新控制点位置
                PointF newPoint = MapScreenToLogical(new Point((int)x, (int)y));

                // 保持X坐标有序
                if (selectedPointIndex > 0 && newPoint.X < controlPoints[selectedPointIndex - 1].X)
                    newPoint.X = controlPoints[selectedPointIndex - 1].X;

                if (selectedPointIndex < controlPoints.Count - 1 && newPoint.X > controlPoints[selectedPointIndex + 1].X)
                    newPoint.X = controlPoints[selectedPointIndex + 1].X;

                controlPoints[selectedPointIndex] = newPoint;
                this.Invalidate();
            }
        }

四、工程下载连接

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

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

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

相关文章

解锁U盘属性0字节困境,重获数据生机

在数字化浪潮中&#xff0c;U盘宛如一位忠诚的“数据信使”&#xff0c;频繁穿梭于各种设备之间&#xff0c;为我们存储和传输着重要信息。然而&#xff0c;当U盘突然显示属性为0字节时&#xff0c;就如同这位信使突然“失声”&#xff0c;让我们陷入了数据丢失的恐慌之中。U盘…

⭐算法OJ⭐二叉树的直径【树】(C++实现)Binary Tree Paths

543. Binary Tree Paths&#xff08;二叉树的直径&#xff09; Given the root of a binary tree, return the length of the diameter of the tree. The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or m…

字典树与01trie

字典树简介 当我们通过字典查一个字或单词的时候&#xff0c;我们会通过前缀或关键字的来快速定位一个字的位置&#xff0c;进行快速查找。 字典树就是类似字典中索引表的一种数据结构&#xff0c;能够帮助我们快速定位一个字符串的位置。 字典树是一种存储字符串的数据结构…

vue - [Vue warn]: Duplicate keys detected: ‘0‘. This may cause an update error.

问题描述&#xff1a; vue项目中&#xff0c;对表单数组赋值时&#xff0c;控制台抛出警告&#xff1a; 问题代码&#xff1a; 问题分析&#xff1a; 1、Vue 要求每个虚拟 DOM 节点必须有唯一的 key。该警告信息通常出现在使用v-for循环的场景中&#xff0c;多个同级节点使用…

各类神经网络学习:(三)RNN 循环神经网络(中集),同步多对多结构的详细解释

上一篇下一篇RNN&#xff08;上集&#xff09;RNN&#xff08;下集&#xff09; 同步多对多结构 1&#xff09;结构详解 ①图解&#xff1a; ②参数含义&#xff1a; x t x_t xt​ &#xff1a;表示每一个时刻的输入&#xff1b; o t o_t ot​ &#xff1a;表示每一个时刻的输…

UR5e机器人位姿

UR5e 作为一款 6 自由度协作机器人&#xff0c;其末端执行器的位姿&#xff08;位置与姿态的组合&#xff09;控制是实现精准操作的核心。在笛卡尔坐标系中&#xff0c;位姿通常用齐次变换矩阵表示&#xff0c;包含末端的三维位置&#xff08;x, y, z&#xff09;和三维姿态&am…

导入 Excel 规则批量修改或删除 PDF 文档内容

需要对 PDF 文档内容进行修改的时候&#xff0c;通常我们会需要借助一些专业的工具来帮我们完成。那我们如果需要修改的 PDF 文档较多的时候&#xff0c;有什么方法可以帮我们实现批量操作呢&#xff1f;今天这篇文章就给大家介绍一下当我们需要批量修改多个 PDF 文档的时候&am…

ISIS-1 ISIS概述

前面几章我们介绍了OSPF的基础工作原理以及怎样交互LSA形成LSDB链路状态数据库的 这一章我们来介绍另一个链路状态路由协议,ISIS路由协议 一、概述 ISIS(Intermediate System to Intermediate System,中间系统到中间系统)是由ISO(International Organization for Standardiza…

茱元游戏TV2.9.3 | 适配多设备的经典街机游戏集合

茱元游戏TV是一款专为TV端设计的游戏软件&#xff0c;同时适配手机、投影仪和车机等多种设备。尽管其兼容性一般&#xff0c;仅支持安卓9.0以上系统&#xff0c;但它提供了丰富的经典街机游戏资源&#xff0c;非常适合8090后怀旧游玩。注意&#xff0c;游戏需先下载才能玩&…

RTD2525BE《HDMI转EDP,DP转EDP》显示器芯片

一、产品概述 瑞昱RTD2525BE是一款专为高端显示设备设计的多接口转换芯片&#xff0c;支持HDMI 2.0与DisplayPort&#xff08;DP&#xff09;1.4双输入&#xff0c;并高效转换为嵌入式DisplayPort&#xff08;eDP&#xff09;输出。该芯片集成先进信号处理技术&#xff0c;支持…

SvelteKit 最新中文文档教程(10)—— 部署 Cloudflare Pages 和 Cloudflare Workers

前言 Svelte&#xff0c;一个语法简洁、入门容易&#xff0c;面向未来的前端框架。 从 Svelte 诞生之初&#xff0c;就备受开发者的喜爱&#xff0c;根据统计&#xff0c;从 2019 年到 2024 年&#xff0c;连续 6 年一直是开发者最感兴趣的前端框架 No.1&#xff1a; Svelte …

springboot使用阿里限流框架-sentinel

当前项目源码 控制台下载 启动bin中的看板服务&#xff1a;账号密码:sentinel/sentinel 官方文档地址 项目引入依赖 <!-- sentinel注解支持 --> <dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-annotation-aspectj<…

鸿蒙特效教程10-卡片展开/收起效果

鸿蒙特效教程10-卡片展开/收起效果 在移动应用开发中&#xff0c;卡片是一种常见且实用的UI元素&#xff0c;能够将信息以紧凑且易于理解的方式呈现给用户。 本教程将详细讲解如何在HarmonyOS中实现卡片的展开/收起效果&#xff0c;通过这个实例&#xff0c;你将掌握ArkUI中状…

Qt在模块依靠情况下资源文件名称和资源名称的使用限制

概述 在Qt中使用添加资源文件的时候&#xff0c;对于资源文件名称的定义&#xff0c;往往是较为随意的。 但是当涉及到Qt库依赖的时候&#xff0c;则可能需要遵守一定的规则&#xff0c;否则可能出现文件找不到或者错误加载的问题。 环境 环境名称Qt 版本系统版本LinuxQt 5.…

MTK Android12-Android13 设置系统默认语言

Android 系统&#xff0c;默认语言 文章目录 需求&#xff1a;场景 参考资料实现方案实现思路编译脚本熟悉-平台熟悉mssi_64_cnkernel-4.19 解决方案修改文件-实现方案 源码分析PRODUCT_LOCALES 引用PRODUCT_DEFAULT_LOCALE 定义get-default-product-locale 方法定义PRODUCT_DE…

贪心算法——思路与例题

贪心算法&#xff1a;当我们分析一个问题时&#xff0c;我们往往先以最优的方式来解决问题&#xff0c;所以顾名思义为贪心。 例题1 题目分析&#xff1a;这题利用贪心算法来分析&#xff0c;最优解&#xff08;可容纳人数最多时&#xff09;一定是先考虑六人桌&#xff0c;然…

网络华为HCIA+HCIP 防火墙

防火墙部署模式 路由模式 有路由器的功能 路由器干的活 他都得干 透明模式 旁挂模式 IDS 端口镜像 VPN

WordPress超级菜单插件UberMenu v3.78汉化版

一、插件介绍 UberMenu 是一款功能强大的 WordPress 超级菜单插件,能够帮助站长创建响应式、可自定义的多级菜单。该插件支持动态内容加载、图标、图片、搜索框等丰富功能,并且兼容大多数 WordPress 主题。 UberMenu v3.78 经过完整汉化,适用于中文站点用户,让操作更加直观…

SQL中体会多对多

我们可以根据学生与课程多对多关系的数据库模型&#xff0c;给出实际的表数据以及对应的查询结果示例&#xff0c;会用到JOINLEFT JOIN两种连接 1. 学生表&#xff08;students&#xff09; student_idstudent_name1张三2李四3王五 2. 课程表&#xff08;courses&#xff09…

23种设计模式-备忘录(Memento)设计模式

备忘录设计模式 &#x1f6a9;什么是备忘录设计模式&#xff1f;&#x1f6a9;备忘录设计模式的特点&#x1f6a9;备忘录设计模式的结构&#x1f6a9;备忘录设计模式的优缺点&#x1f6a9;备忘录设计模式的Java实现&#x1f6a9;代码总结&#x1f6a9;总结 &#x1f6a9;什么是…