这篇文章里的代码的C#版本 (穿过已知点画平滑曲线(3次贝塞尔曲线)

来自:https://blog.youkuaiyun.com/microchenhong/article/details/6316332

真正的原文:https://www.cnblogs.com/muxue/archive/2010/06/23/1763886.html

原文的原文:http://agg.sourceforge.net/antigrain.com/research/bezier_interpolation/index.html#PAGE_BEZIER_INTERPOLATION

没时间研究,先翻译过来

Point类用的是wpf里的Point,就是里面有double类型的X,Y,没有其他依赖

void createCurve(Point[] originPoint, int originCount, List<Point> curvePoint)
        {
            //控制点收缩系数 ,经调试0.6较好
            double scale = 0.6;
            Point[] midpoints = new Point[originCount];
            //生成中点       
            for (int i = 0; i < originCount; i++)
            {
                int nexti = (i + 1) % originCount;
                midpoints[i].X = (originPoint[i].X + originPoint[nexti].X) / 2.0;
                midpoints[i].Y = (originPoint[i].Y + originPoint[nexti].Y) / 2.0;
            }

            //平移中点  
            Point[] extrapoints = new Point[2 * originCount];
            for (int i = 0; i < originCount; i++)
            {
                int nexti = (i + 1) % originCount;
                int backi = (i + originCount - 1) % originCount;
                Point midinmid = new Point();
                midinmid.X = (midpoints[i].X + midpoints[backi].X) / 2.0;
                midinmid.Y = (midpoints[i].Y + midpoints[backi].Y) / 2.0;
                double o
<think>我们被要求通过已知点绘制平滑曲线。常见的方法包括插值法(如样条插值)和拟合方法(如多项式拟合)。这需要穿过已知点,因此插值方法更合适。 常见的插值方法有线性插值、多项式插值、样条插值等。线性插值不够平滑,多项式插值在点数多时容易振荡,通常使用样条插值(尤其是三样条)来获得平滑曲线。 我们将介绍两种主要方法:三样条插值和贝塞尔曲线(如果要求严格通过点,则使用样条;贝塞尔曲线一般不严格通过所有点,但可以通过分段贝塞尔曲线实现,这不展开)。 因此,重点放在三样条插值上。 步骤: 1. 收集已知点:确保有一组已知的坐标点。 2. 选择插值方法:使用三样条插值,因为它能保证曲线连续且一阶、二阶导数连续,从而光滑。 3. 计算插值:利用数学库(如SciPy)进行计算,然后绘制。 以Python为例,使用SciPy库的`CubicSpline`类。 代码示例: ```python import numpy as np import matplotlib.pyplot as plt from scipy.interpolate import CubicSpline # 已知点 x = np.array([0, 1, 2, 3, 4]) y = np.array([0, 3, 1, 4, 2]) # 创建三样条插值对象 cs = CubicSpline(x, y, bc_type=&#39;natural&#39;) # &#39;natural&#39;边界条件,即二阶导在两端为0 # 生成插值点 x_new = np.linspace(0, 4, 100) y_new = cs(x_new) # 绘制 plt.plot(x, y, &#39;o&#39;, label=&#39;原始点&#39;) plt.plot(x_new, y_new, label=&#39;三样条曲线&#39;) plt.legend() plt.show() ``` 注意:边界条件`bc_type`可以选择&#39;natural&#39;(自然样条,端点二阶导为0)或&#39;clamped&#39;(端点一阶导指定),默认为&#39;not-a-knot&#39;(非扭结)。 另外,如果使用其他工具,如Excel或MATLAB,也有相应的插值功能。 方法二:使用B样条曲线(在点数较多时更稳定),但三样条已经足够。 方法三:如果对曲线要求不高,也可以使用多项式插值(但点数多时不推荐),使用`numpy.polyfit`和`poly1d`。 由于要求穿过所有点,多项式插值在点数较少时可以,点数多时振荡严重,所以推荐样条。 因此,主要推荐三样条插值法。 相关问题部分必须基于此回答。 </think>### 穿过已知点画平滑曲线的方法 以下为实现穿过已知平滑曲线的常用方法(以Python为例): #### 1. **三样条插值法** 使用`scipy.interpolate.CubicSpline`构造分段三多项式,保证曲线光滑且严格通过所有点。边界条件通常选择自然样条(端点二阶导数为0): ```python import numpy as np import matplotlib.pyplot as plt from scipy.interpolate import CubicSpline # 已知点数据 x = np.array([0, 1, 3, 4, 7]) y = np.array([2, 1, 4, 3, 5]) # 创建样条对象 cs = CubicSpline(x, y, bc_type=&#39;natural&#39;) # 生成平滑曲线 x_new = np.linspace(min(x), max(x), 500) y_new = cs(x_new) # 绘图 plt.plot(x, y, &#39;ro&#39;, label=&#39;原始点&#39;) plt.plot(x_new, y_new, &#39;b-&#39;, label=&#39;三样条曲线&#39;) plt.legend() plt.show() ``` #### 2. **Akima插值法** 对局部变化敏感,避免多项式插值的过度振荡,适合非均匀分布点: ```python from scipy.interpolate import Akima1DInterpolator akima = Akima1DInterpolator(x, y) y_smooth = akima(x_new) ``` #### 3. **参数化B样条曲线** 通过`scipy.interpolate.splprep`构造参数化B样条,特别适合非单调数据: ```python from scipy.interpolate import splprep, splev tck, u = splprep([x, y], s=0) # s=0强制通过所有点 u_new = np.linspace(0, 1, 500) x_spline, y_spline = splev(u_new, tck) ``` #### 数学原理关键点 - **三样条分段函数**:每段曲线由三多项式 $$S_i(x) = a_i + b_i(x - x_i) + c_i(x - x_i)^2 + d_i(x - x_i)^3$$ 描述 - **连续性条件**:连接点处满足 $$S_i(x_{i+1}) = S_{i+1}(x_{i+1})$$(位置连续) $$\frac{dS_i}{dx}\bigg|_{x=x_{i+1}} = \frac{dS_{i+1}}{dx}\bigg|_{x=x_{i+1}}$$(一阶导连续) $$\frac{d^2S_i}{dx^2}\bigg|_{x=x_{i+1}} = \frac{d^2S_{i+1}}{dx^2}\bigg|_{x=x_{i+1}}$$(二阶导连续) #### 注意事项 - 点集需按x坐标单调排列(非单调时需参数化处理) - 避免点间距差异过大,必要时进行数据重采样 - 工业软件替代方案:MATLAB的`pchip`、Excel的平滑散点图功能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值