Files
OctaveFunctions/PlotBezier.m

184 lines
6.0 KiB
Matlab

function PlotBezier( bezier_points )
% Display the Bezier curve
% This goes in a file named PlotBezier.m
% Clear the children of the current axes
cla
% Define the array of parametervectors as a column vector, so calculation
% of points on the Bezier curve can be done in one statement
t = (0:0.05:1)';
number_segments = size(bezier_points,1);
for k = 1:number_segments
P0 = bezier_points{k,1};
P1 = bezier_points{k,2};
P2 = bezier_points{k,3};
P3 = bezier_points{k,4};
% Compute the points on this curve segment and plot them
bezier = ((1 - t).^3)*P0 + (3*t.*(1 - t).^2)*P1 + (3*t.^2.*(1 - t))*P2 + t.^3*P3;
plot(bezier(:,1),bezier(:,2))
hold on
% Plot dotted lines first so they will be behind the nodes and control
% points and thus not intercept the clicks on the nodes and control
% points
plot([P0(1) P1(1)],[P0(2) P1(2)],':')
plot([P2(1) P3(1)],[P2(2) P3(2)],':')
% Plot the nodes and control points
plot(P0(1),P0(2),'o','MarkerFaceColor','k','ButtonDownFcn',@ClickMarker)
plot(P1(1),P1(2),'o','ButtonDownFcn',@ClickMarker)
plot(P2(1),P2(2),'o','ButtonDownFcn',@ClickMarker)
end
% Plot the last node
plot(P3(1),P3(2),'o','MarkerFaceColor','k','ButtonDownFcn',@ClickMarker)
% Set the limits
xlim([0 1])
ylim([0 1])
function ClickMarker(source,event)
set(ancestor(source,'figure'),'WindowButtonMotionFcn',{@DragMarker,source})
set(ancestor(source,'figure'),'WindowButtonupFcn',@StopDragging)
end
function DragMarker(figure,event,source)
% Get current axes and coordinates of the screen point
h1=gca;
coordinates=get(h1,'currentpoint');
coordinates = coordinates(1,1:2);
% Find the closest node or control point. This code is simplistic in the
% sense that it always takes the nearest node or control point. If a node
% or control point is dragged near another node or control point, then the
% other one will be moved.
segment_index = -1;
node_control_index = -1;
distance = Inf;
for m = 1:size(bezier_points,1)
for n = 1:4
if (isinf(distance))
distance = norm(bezier_points{m,n}-coordinates);
segment_index = m;
node_control_index = n;
else
new_distance = norm(bezier_points{m,n}-coordinates);
if (new_distance < distance)
distance = new_distance;
segment_index = m;
node_control_index = n;
end
end
end
end
% Modify the nodes and control points
old_point = bezier_points{segment_index,node_control_index};
bezier_points{segment_index,node_control_index} = coordinates;
% For an intermediate node, the coodinates are stored twice. Update the
% other copy
if (segment_index < number_segments && node_control_index == 4)
bezier_points{segment_index+1,1} = coordinates;
end
if (1 < segment_index && node_control_index == 1)
bezier_points{segment_index-1,4} = coordinates;
end
% Moving a node or control point causes movement of other control points.
% If a node is moved, the control points on either side need to be moved. If a
% control point is moved, its partner acros the node must be moved
if (node_control_index == 1 || node_control_index == 4)
% Node was moved. Move attached control points
translation_vector = bezier_points{segment_index,node_control_index} - old_point;
if (segment_index == 1 && node_control_index == 1)
bezier_points{segment_index,2} = bezier_points{segment_index,2} + translation_vector;
elseif (segment_index == number_segments && node_control_index == 4)
bezier_points{segment_index,3} = bezier_points{segment_index,3} + translation_vector;
else
if (node_control_index == 1)
bezier_points{segment_index,2} = bezier_points{segment_index,2} + translation_vector;
bezier_points{segment_index-1,3} = bezier_points{segment_index-1,3} + translation_vector;
else
bezier_points{segment_index+1,2} = bezier_points{segment_index+1,2} + translation_vector;
bezier_points{segment_index,3} = bezier_points{segment_index,3} + translation_vector;
end
end
else
% Control point was moved. Rotate its partner
if ((segment_index == 1 && node_control_index == 2) || (segment_index == number_segments && node_control_index == 3))
% No partner. Nothing else to do
else
new_point = bezier_points{segment_index,node_control_index};
if (node_control_index == 2)
% Rotate previous control point
rotation_center = bezier_points{segment_index,1};
other_control_point = bezier_points{segment_index-1,3};
else
% Rotate next control point
rotation_center = bezier_points{segment_index,4};
other_control_point = bezier_points{segment_index+1,2};
end
new_vector = new_point - rotation_center;
new_vector = new_vector/norm(new_vector);
old_vector = old_point - rotation_center;
old_vector = old_vector/norm(old_vector);
cos_theta = sum(old_vector.*new_vector);
sin_theta = old_vector(1)*new_vector(2) - new_vector(1)*old_vector(2);
rotation_matrix = [cos_theta -sin_theta;sin_theta cos_theta];
other_control_point = other_control_point - rotation_center;
rotated_other_control_point = (rotation_matrix*(other_control_point'))' + rotation_center;
if (node_control_index == 2)
bezier_points{segment_index-1,3} = rotated_other_control_point;
else
bezier_points{segment_index+1,2} = rotated_other_control_point;
end
end
end
% Plot the updated curve
PlotBezier(bezier_points);
end
function StopDragging(figure,event)
set(figure,'WindowButtonMotionFcn','')
set(figure,'WindowButtonUpFcn','')
end
end