WPF 绘制过顶点的圆滑曲线 (样条,贝塞尔)

在一个WPF项目中要用到样条曲线,必须过顶点,圆滑后还不能太走样,捣鼓一番,发现里面颇有玄机,于是把我多方抄来改造的方法发出来,方便新手:

如上图,看代码吧:

----------------------------------------

前台页面:

<Window x:Class="Wpf_north_demo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Wpf_north_demo"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Canvas x:Name="ca1" Background="White" MouseLeftButtonDown="ca1_MouseLeftButtonDown" MouseMove="ca1_MouseMove" MouseRightButtonDown="ca1_MouseRightButtonDown">

            <Polyline x:Name="path_lines" Stroke="Silver" StrokeThickness="1" StrokeDashArray="1 1 1" IsHitTestVisible="False">
            </Polyline>

            <Path x:Name="path1" Stroke="Red" StrokeThickness="1" IsHitTestVisible="False">
                <Path.Data>
                    <PathGeometry x:Name="pathGeometry1">
                    </PathGeometry>
                </Path.Data>
            </Path>
            
            
        </Canvas>

        <Canvas x:Name="ca_top" IsHitTestVisible="False"/>

        <TextBlock  HorizontalAlignment="Left" VerticalAlignment="Top" Margin="5" Text="左键绘制,右键结束" IsHitTestVisible="False"/>
    </Grid>
</Window>

后台代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Wpf_north_demo
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            
        }

        int _num = 0;
        bool _started = false;
        List<Point> _seed = new List<Point>();
        private void ca1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if(!_started)
            {
                _num = 0;
                _seed.Clear();
                _started = true;
                ca_top.Children.Clear();
                path_lines.Points.Clear();
                pathGeometry1.Figures.Clear();
            }

            while (path_lines.Points.Count > _num && _num > 0)
            {
                path_lines.Points.RemoveAt(path_lines.Points.Count - 1);
            }

            _seed.Add(e.GetPosition(ca1));
            _num = _seed.Count;

            path_lines.Points.Add(_seed[_num - 1]);

            ca_top.Children.Add(new Ellipse
            {
                Width = 6,
                Height = 6,
                Stroke = Brushes.Blue,
                Fill = Brushes.Lime,
                Margin = new Thickness(_seed[_num - 1].X - 3, _seed[_num - 1].Y - 3, 0, 0)
            });
        }

        private void ca1_MouseMove(object sender, MouseEventArgs e)
        {
            if (_started && e.LeftButton == MouseButtonState.Released && _num > 0)
            {
                while (path_lines.Points.Count > _num)
                {
                    path_lines.Points.RemoveAt(path_lines.Points.Count - 1);
                }
                path_lines.Points.Add(e.GetPosition(ca1));
            }
        }

        private void ca1_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            if(_started)
            {
                while (path_lines.Points.Count > _num && _num > 0)
                {
                    path_lines.Points.RemoveAt(path_lines.Points.Count - 1);
                }

                _seed.Add(e.GetPosition(ca1));
                _num = _seed.Count;

                path_lines.Points.Add(_seed[_num - 1]);
                ca_top.Children.Add(new Ellipse
                {
                    Width = 6,
                    Height = 6,
                    Stroke = Brushes.Blue,
                    Fill = Brushes.Lime,
                    Margin = new Thickness(_seed[_num - 1].X - 3, _seed[_num - 1].Y - 3, 0, 0)
                });

                BezierHelper.DrawBezierPolyline(pathGeometry1, _seed, false);
            }
            else
            {
                _num = 0;
                _seed.Clear();
                ca_top.Children.Clear();
                path_lines.Points.Clear();
                pathGeometry1.Figures.Clear();
            }

            _started = false;
        }
       
    }

    public class BezierHelper
    {
        public static void DrawBezierPolyline(PathGeometry geo, List<Point> list, bool close)
        {
            geo.Figures.Clear();
            if (list.Count > 0)
            {
                PathFigure pf = new PathFigure() { IsClosed = close };

                pf.StartPoint = list[0];
                List<Point> controls = new List<Point>();
                for (int i = 0; i < list.Count; i++)
                {
                    Point control_01, control_02;
                    GetControlPoint(list, i, out control_01, out control_02);
                    controls.Add(control_01);
                    controls.Add(control_02);
                }

                for (int i = 1; i < list.Count; i++)
                {
                    BezierSegment bs = new BezierSegment(controls[i * 2 - 1], controls[i * 2], list[i], true);
                    bs.IsSmoothJoin = true;

                    pf.Segments.Add(bs);
                }
                geo.Figures.Add(pf);
            }
        }

        static void GetControlPoint(List<Point> list, int idx, out Point control_01, out Point control_02)
        {
            if (idx == 0)
            {
                control_01 = list[0];
            }
            else
            {
                control_01 = GetAverage(list[idx - 1], list[idx]);
            }
            if (idx == list.Count - 1)
            {
                control_02 = list[list.Count - 1];
            }
            else
            {
                control_02 = GetAverage(list[idx], list[idx + 1]);
            }
            Point ave = GetAverage(control_01, control_02);
            Point sh = Sub(list[idx], ave);
            control_01 = Mul(Add(control_01, sh), list[idx], 0.6);
            control_02 = Mul(Add(control_02, sh), list[idx], 0.6);
        }

        static Point GetAverage(Point x, Point y)
        {
            return new Point((x.X + y.X) / 2, (x.Y + y.Y) / 2);
        }

        static Point Add(Point x, Point y)
        {
            return new Point(x.X + y.X, x.Y + y.Y);
        }

        static Point Sub(Point x, Point y)
        {
            return new Point(x.X - y.X, x.Y - y.Y);
        }

        static Point Mul(Point x, Point y, double d)
        {
            Point temp = Sub(x, y);
            temp = new Point(temp.X * d, temp.Y * d);
            temp = Add(y, temp);
            return temp;
        }
    }

}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringCloudAlibaba实战入门之路由网关Gateway断言(十二)

上一节课中我们初步讲解了网关的基本概念、基本功能,并且带大家实战体验了一下网关的初步效果,这节课我们继续学习关于网关的一些更高级有用功能,比如本篇文章的断言。 一、网关主要组成部分 上图中是核心的流程图,最主要的就是Route、Predicates 和 Filters 作用于特定路…

【Linux进程】进程信号(信号的保存与处理)

目录 前言 1. 信号的默认行为 2. 信号的保存 信号集操作函数 sigprocmask sigpending 3. 信号的处理 信号的处理过程 思考 4. sigaction 5. SIGCHLD信号 6. 可重入函数 7. volatile 总结 前言 上文介绍了信号&#xff0c;以及信号的产生&#xff0c;本文继续来聊一…

论文阅读 - 《Large Language Models Are Zero-Shot Time Series Forecasters》

Abstract 通过将时间序列编码为数字组成的字符串&#xff0c;我们可以将时间序列预测当做文本中下一个 token预测的框架。通过开发这种方法&#xff0c;我们发现像GPT-3和LLaMA-2这样的大语言模型在下游任务上可以有零样本时间序列外推能力上持平或者超过专门设计的时间序列训…

Llama系列关键知识总结

系列文章目录 第一章&#xff1a;LoRA微调系列笔记 第二章&#xff1a;Llama系列关键知识总结 文章目录 系列文章目录Llama: Open and Efficient Foundation Language Models关键要点LLaMa模型架构&#xff1a;Llama2分组查询注意力 (GQA) Llama3关键信息 引用&#xff1a; Ll…

项目实践-贪吃蛇小游戏

目录 声明 1、前言 2、实现目标 3、技术要点 4、Win32API介绍 4.1、Win32API 4.2、控制台程序 4.3、控制台屏幕上的坐标COORD 4.4、GetStdHandle 4.5、GetConsoleCursorInfo 4.6、SetConsoleCursorInfo 4.7、SetConsoleCursorPosition 4.8、GetAsyncKeyState 5、…

Java编程题_面向对象和常用API01_B级

Java编程题_面向对象和常用API01_B级 第1题 面向对象、异常、集合、IO 题干: 请编写程序&#xff0c;完成键盘录入学生信息&#xff0c;并计算总分将学生信息与总分一同写入文本文件 需求&#xff1a;键盘录入3个学生信息(姓名,语文成绩,数学成绩) 求出每个学生的总分 ,并…

Jmeter自学【8】- 使用JMeter模拟设备通过MQTT发送数据

今天使用jmeter推送数据到MQTT&#xff0c;给大家分享一下操作流程。 一、安装JMeter 参考文档&#xff1a;Jmeter自学【1】- Jmeter安装、配置 二、安装MQTT插件 1、下载插件 我的Jmeter版本是5.6.3&#xff0c;用到的插件是&#xff1a;mqtt-xmeter-2.0.2-jar-with-depe…

Uniapp跨域请求

1.什么是跨域 是指当一个请求的URL的协议、域名或端口与当前页面的URL不同时&#xff0c;该请求被视为跨域请求。跨域是一种安全策略&#xff0c;用于限制一个域的网页如何与另一个域的资源进行交互。就比如我们进行前端向后端进行发送请求的时候&#xff0c;如果是开发前后端…

基于Resnet、LSTM、Shufflenet及CNN网络的Daily_and_Sports_Activities数据集仿真

在深度学习领域&#xff0c;不同的网络结构设计用于解决特定的问题。本文将详细分析四种主流网络结构&#xff1a;卷积神经网络&#xff08;CNN&#xff09;、残差网络&#xff08;ResNet&#xff09;、长短期记忆网络&#xff08;LSTM&#xff09;和洗牌网络&#xff08;Shuff…

算法进阶:贪心算法

贪心算法是一种简单而直观的算法思想&#xff0c;它在每一步选择中都采取在当前状态下最优的选择&#xff0c;以期望最终得到全局最优解。贪心算法通常适用于一些具有最优子结构的问题&#xff0c;即问题的最优解可以通过一系列局部最优解的选择得到。 贪心算法的基本思路是&a…

139.《python中的正则详解》

文章目录 什么是正则正则表达式语法正则demo1.匹配模式2.finditer3.正则分组4.非捕获组5.分组的引用6. 正则替换7.正则切割7.正则「或」7.枚举取反 面试题 前言: 拉开差距的不是上班的8小时,而是下班后的16小时,同志们,加油,卷起!!! 什么是正则 1.正则表达式是一种高级文本处理…

从安全角度看 SEH 和 VEH

从安全角度看 SEH 和 VEH 异常处理程序是处理程序中不可预见的错误的基本方法之一 https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/exceptions/ SEH——结构化异常处理程序 就其工作方式而言&#xff0c;异常处理程序与其他处理程序相比相当基础&#xff0…

ESP-IDF学习记录(3)ESP-IDF组件管理

既然官方把这个组件管理按钮放置的这么明显&#xff0c;就一定有他的用心良苦&#xff0c;今天学习一下这个组件管理。 Componments manager 1.给当前项目安装组件 IDF Component Manager and ESP Component Registry Documentation — IDF Component Management documenta…

华为麦芒5(安卓6)termux记录 使用ddns-go,alist

下载0.119bate1 安卓5和6版本,不能换源,其他源似乎都用不了,如果root可以直接用面具模块 https://github.com/termux/termux-app/releases/download/v0.119.0-beta.1/termux-app_v0.119.0-beta.1apt-android-5-github-debug_arm64-v8a.apk 安装ssh(非必要) pkg install open…

园区网综合拓扑实验

一、实验要求 实验拓扑图如上图所示 1、按照图示的VLAN及IP地址需求&#xff0c;完成相关配置 2、要求SW1为VLAN 2/3的主根及主网关 SW2为vlan 20/30的主根及主网关 SW1和SW2互为备份 3、可以使用super vlan&#xff08;本实验未使用&#xff09; 4、上层…

JSON 系列之4:JSON_VALUE

JSON_VALUE的作用&#xff0c;简单来说&#xff0c;就是从JSON到SQL&#xff1a; SQL/JSON function JSON_VALUE selects JSON data and returns a SQL scalar or an instance of a user-defined SQL object type or SQL collection type (varray, nested table) 所以&#xff…

设置首选网络类型以及调用Android框架层的隐藏API

在Android SDK中提供的framework.jar是阉割版本的&#xff0c;比如有些类标记为hide&#xff0c;这些类不会被打包到这个jar中&#xff0c;而有些只是类中的某个方法或或属性被标记为hide&#xff0c;则这些类或属性会被打包到framework.jar&#xff0c;但是我们无法调用&#…

Mac 12.1安装tiger-vnc问题-routines:CRYPTO_internal:bad key length

背景&#xff1a;因为某些原因需要从本地mac连接远程linxu桌面查看一些内容&#xff0c;必须使用桌面查看&#xff0c;所以ssh无法满足&#xff0c;所以决定安装vnc客户端。 问题&#xff1a; 在mac上通过 brew install tiger-vnc命令安装, 但是报错如下&#xff1a; > D…

【Java-tesseract】OCR图片文本识别

文章目录 一、需求二、概述三、部署安装四、技术细节五、总结 一、需求 场景需求:是对识别常见的PNG,JPEG,TIFF,GIF图片识别&#xff0c;环境为离线内网。组件要求开源免费&#xff0c;并且可以集成Java生成接口服务。 二、概述 我不做选型对比了,我筛选测试了下Tesseract(v…

PCIe和DMA:数据传输的“双子星“

简单来说&#xff0c;PCIe是一种硬件总线标准&#xff0c;就像高速公路&#xff1b;DMA是一种数据传输机制&#xff0c;就像在高速公路上行驶的卡车。所以这两个是两种不同的概念。 理解PCIe传输 PCIe&#xff08;PCI Express&#xff09;是一种硬件接口规范&#xff0c;定义…