How to transform a curve in a specific way

441 views Asked by At

I have the following curve (example data that's somewhat sparse, but should get the point across). There are four points along this curve (indicated by the arrows), that I'd like to use as reference points. These points need to be shifted by specified amounts (x1, x2, x3, and x4 respectively) in the direction the arrow species. It will always be these four locations that need to be shifted by specific amounts. However, it's not as easy as shifting those specific points, because I need to maintain the overall shape of the curve.

There are constraints that will always be maintained. The arrow associated with x2 will never overlap with the arrow specified by x1. The arrow associated with x3 will never surpass that of x2. The arrow associated with x4 will never surpass that associated with x3. The curve will always have this general shape and will always be shifted at these 4 points.

In other words, the arrows in the image below are representative examples of the way that this curve should be shifted.

enter image description here

How can I do this? One idea I had was to fit some sort of spline and then somehow transform that spline in an elegant way based on these points. But I'm really not sure.

Here is the data from this example in Matlab

x = [6; 7; 7.2; 7.3; 7.5; 7.7; 7.9; 8; 8.13; 8.2; 8.21; 8.31; 8.4; 8.41; 8.45; 8.47; 8.5; 8.6; 8.8; 9; 9.6; 10];
y = [0; 0.01; 0.02; 0.1; 1; 1.1; 0.9; 0.6; 0; -0.5; -1; -1.1; -0.93; -0.9; -0.7; -0.6; -0.2; 0; 0.1; 0.12; 0; 0];
plot(x,y);

Example of end-goal You can see how the entire curve has shifted by the specified amounts but the whole shape is maintained. enter image description here

1

There are 1 answers

10
Matt On BEST ANSWER

I assume you want to stretch the parts individually by moving the four points. Therefore it is a simple operation of adjusting all the points within the bounds. The overall shape of the curve is maintained as long as your constraint of non-overlapping is fulfilled.

Of course it is not as easy as shifting only the points. But you can do the following for each part of the curve.

  1. Shift to zero.
  2. Stretch with calculated factor.
  3. Shift back to where the part has to be in the final curve.

Here is an implementation doing exactly this. The indexes of the «moving points» are specified in ind and the amount to be moved in respect to the x-axis is specified in val.

x   = [6; 7; 7.2; 7.3; 7.5; 7.7; 7.9; 8; 8.13; 8.2; 8.21; 8.31; 8.4; 8.41; 8.45; 8.47; 8.5; 8.6; 8.8; 9; 9.6; 10];
y   = [0; 0.01; 0.02; 0.1; 1; 1.1; 0.9; 0.6; 0; -0.5; -1; -1.1; -0.93; -0.9; -0.7; -0.6; -0.2; 0; 0.1; 0.12; 0; 0];
ind = [  3;    6;    12;    21];        % The indexes of points to be moved
val = [0.1; -0.1;  -0.2;  -0.8];        % Value/amount of movement

% Extend scope to beginning and end of data
i = [1,ind',length(x)];                 % All the bounds
v = [0;val;0];                          % Movements of all bounds

% Split original curve in parts and store them in a cell array
orig = cell(1,length(i)-1);             % preallocation
for n = 1:(length(i)-1)
    orig{n} = [x(i(n):i(n+1)),y(i(n):i(n+1))];
end

% Stretch the parts and move them to the correct position
new = cell(size(orig));                 % preallocation
for n = 1:length(orig)
    a = orig{n}(1,1);                   % first x value
    b = orig{n}(end,1);                 % last x value
    p = (b-a+v(n+1)-v(n))/(b-a);        % factor to stretch
    xn = ((orig{n}(:,1)-a)*p)+a+v(n);   % calculate new x positions
    new{n} = [xn,orig{n}(:,2)];         % add result to cell-array
end

% Put the parts together
% Simple concenation creates double entries at moving points,
% therefore the first part is added outside the loop.
% Preallocation is omitted on purpose.
xn = new{1}(:,1);
yn = new{1}(:,2);
for n = 2:length(new)
    xn = [xn;new{n}(2:end,1)]; %#ok<AGROW>
    yn = [yn;new{n}(2:end,2)]; %#ok<AGROW>
end

% Display data
figure; hold on;
plot(x(ind),y(ind),'ro');               % Plot original points
plot(x,y,'b.-');                        % Plot original curve
plot(x(ind)+val,y(ind),'go');           % Plot new points
plot(xn,yn,'k.-');                      % Plot new curve
legend('Original points','Original curve','New points','New curve');

% Plot the new parts separately
%hold all;
%for n = 1:length(new)
%    nc = plot(new{n}(:,1),new{n}(:,2));
%end

This will give you the following result: Example result of given code.

On request: For a better understanding what happens, the following code is a simplified version, only shifting the last point by the given amount of value.

x = [6; 7; 7.2; 7.3; 7.5; 7.7; 7.9; 8; 8.13; 8.2; 8.21; 8.31; 8.4; 8.41; 8.45; 8.47; 8.5; 8.6; 8.8; 9; 9.6; 10];
y = [0; 0.01; 0.02; 0.1; 1; 1.1; 0.9; 0.6; 0; -0.5; -1; -1.1; -0.93; -0.9; -0.7; -0.6; -0.2; 0; 0.1; 0.12; 0; 0];

value = -1;             % the last point will be shifted by this value

p = (x(end)-x(1)+value)/(x(end)-x(1));

% Override of p
%p = 2.0;               % p=2.0 will double the length
%p = 0.5;               % p=0.5 will halve the length

x2 = ((x-x(1)) * p) + x(1);

figure; hold on;
plot(x,y,'*-');
plot(x2,y,'ro-');

This will give you the following result: Result of simplified version.