首页 > 其他分享 >matlab点击实时绘制bezier曲线

matlab点击实时绘制bezier曲线

时间:2024-10-19 18:18:59浏览次数:3  
标签:controlPointsX bezierPlot 曲线 end Bezier bezier 点击 matlab data

贝塞尔曲线的原理

贝塞尔曲线是通过一组控制点定义的参数曲线。曲线不会直接穿过这些点,但这些点会影响曲线的形状。控制点确定了曲线的弯曲程度和方向。贝塞尔曲线的数学公式基于伯恩斯坦多项式(Bernstein Polynomials)。

对于一个n次的贝塞尔曲线,它由n+1个控制点 P_0,P_1,...,P_n​ 定义。曲线的参数方程为:

其中,B(t)是曲线上任意一点,t是参数,取值范围是0到1之间.

\binom{n}{i}是组合数,表示从n个不同元素中取i个元素的组合数,计算公式为\binom{n}{i}=\frac{n!}{i!(n-i)!}

对于每个 t的值,贝塞尔曲线上的点 B(t) 是所有控制点的加权和。权重由伯恩斯坦多项式决定,这些多项式随 t 的变化而变化,确保曲线从P_0 开始,以 P_n结束。

贝塞尔曲线的性质

  1. 起点和终点:曲线总是从第一个控制点 P_0开始,以最后一个控制点P_n ​ 结束。
  2. 曲线方向:曲线在 P_0 和 P_n处的切线方向由相邻控制点之间的连线决定。
  3. 平滑性:高阶贝塞尔曲线可以非常平滑,因为它们可以包含多个拐点。
  4. 变权和仿射不变性:贝塞尔曲线在仿射变换(如平移、旋转、缩放)下保持不变。

本文代码实现采用三次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

相关文章