贝塞尔曲线的原理
贝塞尔曲线是通过一组控制点定义的参数曲线。曲线不会直接穿过这些点,但这些点会影响曲线的形状。控制点确定了曲线的弯曲程度和方向。贝塞尔曲线的数学公式基于伯恩斯坦多项式(Bernstein Polynomials)。
对于一个n次的贝塞尔曲线,它由n+1个控制点 定义。曲线的参数方程为:
其中,是曲线上任意一点,t是参数,取值范围是0到1之间.
是组合数,表示从n个不同元素中取i个元素的组合数,计算公式为
对于每个 t的值,贝塞尔曲线上的点 B(t) 是所有控制点的加权和。权重由伯恩斯坦多项式决定,这些多项式随 t 的变化而变化,确保曲线从 开始,以 结束。
贝塞尔曲线的性质
- 起点和终点:曲线总是从第一个控制点 开始,以最后一个控制点 结束。
- 曲线方向:曲线在 和 处的切线方向由相邻控制点之间的连线决定。
- 平滑性:高阶贝塞尔曲线可以非常平滑,因为它们可以包含多个拐点。
- 变权和仿射不变性:贝塞尔曲线在仿射变换(如平移、旋转、缩放)下保持不变。
本文代码实现采用三次bezier曲线,即至少四个点绘制才能绘制曲线。
功能:
鼠标左键点击添加有序点,并绘制bezier曲线。
右键删除点,重新绘制曲线。(另一种方法是右键返回上一个操作)
采用固定边界,均匀参数化。(也可采用弦长参数化,中心参数化等)
创建了清空按钮,一键清空画布上的所有点。
退出按钮。
function draw_bezier_curve()
% 创建图形窗口
hFig = figure('name', '绘制三次Bezier曲线', 'NumberTitle', 'off', 'MenuBar', 'none', 'ToolBar', 'none');
axis([-10 10 -10 10]); % 设置坐标轴范围
grid on;
hold on;
% 初始化控制点数组
controlPointsX = [];
controlPointsY = [];
% 初始化Bezier曲线的图形句柄
bezierPlot = [];
% 创建按钮并设置其回调函数
%uicontrol('Style', 'pushbutton', 'String', '均匀参数化', ...
%'Position', [150 10 100 30], 'Callback', @uniform_parameterization);
% 创建一个按钮并设置其回调函数
uicontrol('Style', 'pushbutton', 'String', '清空所有点', ...
'Position', [260 10 100 30], 'Callback', @clear_points);
% 创建一个退出按钮并设置其回调函数
uicontrol('Style', 'pushbutton', 'String', '退出', ...
'Position', [370 10 100 30], 'Callback', @exit_callback);
% 存储初始数据
guidata(hFig, struct('controlPointsX', controlPointsX, 'controlPointsY', controlPointsY, 'bezierPlot', bezierPlot));
% 获取控制点
while true
[x, y, button] = ginput(1); % 获取一个控制点
if isempty(x) || isempty(y) % 如果用户点击了关闭按钮,则退出
break;
end
% 检查点击是否在当前轴的范围内
ax = gca;
xLim = get(ax, 'XLim');
yLim = get(ax, 'YLim');
if x < xLim(1) || x > xLim(2) || y < yLim(1) || y > yLim(2)
continue; % 如果点击超出范围,忽略此次点击
end
% 获取当前图形数据
data = guidata(hFig);
controlPointsX = data.controlPointsX;
controlPointsY = data.controlPointsY;
bezierPlot = data.bezierPlot;
% 判断点击的是左键还是右键
if button(1) == 1 % 左键,添加控制点
controlPointsX = [controlPointsX; x];
controlPointsY = [controlPointsY; y];
cla; % 清除当前轴
plot(controlPointsX, controlPointsY, 'bo'); % 绘制控制点
drawnow; % 立即更新图形
% 如果控制点数量超过3个,绘制Bezier曲线
if length(controlPointsX) >= 4
% 清除旧的Bezier曲线
if ~isempty(bezierPlot)
delete(bezierPlot);
end
t = linspace(0, 1, 100); % 参数t的取值范围
Bx = zeros(size(t)); % 初始化Bezier曲线的x坐标
By = zeros(size(t)); % 初始化Bezier曲线的y坐标
% 计算Bezier曲线的点
for i = 1:length(t)
for j = 1:length(controlPointsX)
bezierTerm = nchoosek(length(controlPointsX) - 1, j - 1) * (1 - t(i))^(length(controlPointsX) - j) * t(i)^(j - 1);
Bx(i) = Bx(i) + bezierTerm * controlPointsX(j);
By(i) = By(i) + bezierTerm * controlPointsY(j);
end
end
% 绘制Bezier曲线
bezierPlot = plot(Bx, By, 'r-'); % 绘制Bezier曲线
end
elseif button(1) == 3 && ~isempty(controlPointsX) % 右键,删除最后一个控制点
if length(controlPointsX) > 0
controlPointsX(end) = [];
controlPointsY(end) = [];
cla; % 清除当前轴
plot(controlPointsX, controlPointsY, 'bo'); % 重新绘制控制点
drawnow; % 立即更新图形
% 清除旧的Bezier曲线
if ~isempty(bezierPlot)
delete(bezierPlot);
bezierPlot = [];
end
% 如果控制点数量仍然超过3个,重新绘制Bezier曲线
if length(controlPointsX) >= 4
t = linspace(0, 1, 100); % 参数t的取值范围
Bx = zeros(size(t)); % 初始化Bezier曲线的x坐标
By = zeros(size(t)); % 初始化Bezier曲线的y坐标
% 计算Bezier曲线的点
for i = 1:length(t)
for j = 1:length(controlPointsX)
bezierTerm = nchoosek(length(controlPointsX) - 1, j - 1) * (1 - t(i))^(length(controlPointsX) - j) * t(i)^(j - 1);
Bx(i) = Bx(i) + bezierTerm * controlPointsX(j);
By(i) = By(i) + bezierTerm * controlPointsY(j);
end
end
% 绘制Bezier曲线
bezierPlot = plot(Bx, By, 'r-'); % 绘制Bezier曲线
end
end
end
% 更新图形数据
guidata(hFig, struct('controlPointsX', controlPointsX, 'controlPointsY', controlPointsY, 'bezierPlot', bezierPlot));
end
xlabel('X');
ylabel('Y');
title('三次Bezier曲线');
end
% 按钮的回调函数,用于清空所有点
function clear_points(~, ~)
hFig = gcf; % 获取当前图形窗口
data = guidata(hFig);
data.controlPointsX = [];
data.controlPointsY = [];
data.bezierPlot = [];
cla; % 清除当前轴
plot(data.controlPointsX, data.controlPointsY, 'bo'); % 重新绘制控制点
drawnow; % 立即更新图形
if ~isempty(data.bezierPlot)
delete(data.bezierPlot);
end
guidata(hFig, data); % 更新图形数据
end
% 均匀参数化方法
function uniform_parameterization(~, ~)
% 获取当前图形窗口
hFig = gcf;
% 获取存储的图形数据
data = guidata(hFig);
controlPointsX = data.controlPointsX;
controlPointsY = data.controlPointsY;
% 如果控制点数量不足4个,不进行参数化
if length(controlPointsX) < 4
return;
end
% 参数t的取值范围
t = linspace(0, 1, 100);
% 初始化Bezier曲线的x坐标和y坐标
Bx = zeros(size(t));
By = zeros(size(t));
% 计算Bezier曲线的点
for i = 1:length(t)
for j = 1:length(controlPointsX)
bezierTerm = nchoosek(length(controlPointsX) - 1, j - 1) * (1 - t(i))^(length(controlPointsX) - j) * t(i)^(j - 1);
Bx(i) = Bx(i) + bezierTerm * controlPointsX(j);
By(i) = By(i) + bezierTerm * controlPointsY(j);
end
end
% 清除旧的Bezier曲线
if ~isempty(data.bezierPlot)
delete(data.bezierPlot);
end
% 绘制Bezier曲线
data.bezierPlot = plot(Bx, By, 'r-');
% 更新图形数据
guidata(hFig, data);
% 立即更新图形
drawnow;
end
% 退出按钮的回调函数
function exit_callback(~, ~)
close(gcf);
end
效果如下,ui界面和功能可以自己根据需求更新。
标签:controlPointsX,bezierPlot,曲线,end,Bezier,bezier,点击,matlab,data From: https://blog.csdn.net/Dengdabenzhu/article/details/143054110