Как да трансформирате крива по определен начин

Имам следната крива (примерни данни, които са донякъде оскъдни, но трябва да разберат целта). Има четири точки по тази крива (указани със стрелките), които бих искал да използвам като референтни точки. Тези точки трябва да бъдат изместени с определени количества (x1, x2, x3 и x4 съответно) в посоката на стрелката. Тези четири местоположения винаги ще трябва да бъдат преместени с конкретни суми. Въпреки това не е толкова лесно, колкото преместването на тези конкретни точки, защото трябва да поддържам цялостната форма на кривата.

Има ограничения, които винаги ще се поддържат. Стрелката, свързана с x2, никога няма да се припокрие със стрелката, посочена от x1. Стрелката, свързана с x3, никога няма да надмине тази на x2. Стрелката, свързана с x4, никога няма да надмине тази, свързана с x3. Кривата винаги ще има тази обща форма и винаги ще бъде изместена в тези 4 точки.

С други думи, стрелките в изображението по-долу са представителни примери за начина, по който тази крива трябва да бъде изместена.

въведете описание на изображението тук

Как мога да направя това? Една идея, която имах, беше да напасна някакъв вид сплайн и след това по някакъв начин да трансформирам този сплайн по елегантен начин въз основа на тези точки. Но наистина не съм сигурен.

Ето данните от този пример в 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);

Пример за крайна цел Можете да видите как цялата крива се е изместила с посочените количества, но цялата форма се запазва. въведете описание на изображението тук


person CodeGuy    schedule 21.06.2015    source източник
comment
Как генерирахте тази крива на първо място? По-конкретно, как дефинирахте формата във всеки интервал (x1,x2), (x2, x3) и (x3, x4)?   -  person fang    schedule 21.06.2015
comment
Това са експериментални данни, а не генерирани от модел...   -  person CodeGuy    schedule 21.06.2015


Отговори (1)


Предполагам, че искате да разтегнете частите поотделно, като преместите четирите точки. Следователно това е проста операция за коригиране на всички точки в рамките на границите. Общата форма на кривата се запазва докато е изпълнено вашето ограничение за неприпокриване.

Разбира се, не е толкова лесно, колкото преместването на само точките. Но можете да направите следното за всяка част от кривата.

  1. Shift до нула.
  2. Разтягане с изчислен фактор.
  3. Преместете обратно до мястото, където частта трябва да бъде в крайната крива.

Ето приложение, което прави точно това. Индексите на «подвижните точки» са посочени в ind, а количеството, което трябва да се премести по отношение на оста x, е указано в 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

Това ще ви даде следния резултат: Примерен резултат от даден код.

При поискване: За по-добро разбиране какво се случва, следният код е опростена версия, която само измества последната точка с дадената стойност от 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-');

Това ще ви даде следния резултат: Резултат от опростена версия.

person Matt    schedule 21.06.2015
comment
Много благодаря. Какво имаш предвид под преместване на всяка част на нула? - person CodeGuy; 21.06.2015
comment
(orig{n}(:,1)-a) премества частта n на нула. Тогава умножението по p не дава допълнително отместване. След умножението измества резултата обратно с a+v(n). Без преместване това би довело до изкривена крива. - person Matt; 21.06.2015
comment
Съжалявам, все още се опитвам да разбера това. Можете ли да обясните как изчислявате коефициента на разтягане p. - person CodeGuy; 21.06.2015
comment
Имаме точки [2 4 6]. Искаме да изместим 6 с v=-2, но запазвайки „формата“. Резултатът е [2 3 4], когато 2 е фиксирана точка. За да направим това, можем да изместим [2 4 6] към началото, като извадим първата стойност (2). -› [0 2 4]. Тогава трябва да знаем коефициента на разтягане p. p е относителната разлика на дължината след/преди -› (6-2-v)/(6-2)=2/4=0.5=p. Сега изчисляваме [0 2 4]*p=[0 1 2]. Сега трябва да се преместим обратно към фиксираната точка (2). -› [0 1 2]+2=[2 3 4]. Ако има повече части, тогава p трябва да вземе предвид и двете граници a и b и двете смени v(n+1) и v(n). - person Matt; 21.06.2015
comment
Бихте ли ми направили услуга и пощенски код, който просто се измества като една част, за да мога да опитам да разбера какво се случва? Например код, който само измества втората референтна точка с x2. - person CodeGuy; 22.06.2015
comment
@CodeGuy: Добавих опростена версия към публикацията. Не взех втората точка, а последната като подвижна референция, така че цялата крива се измества/премества. Принципът зад него може да се види ясно. Надявам се да помогне, в противен случай просто ме попитайте. - person Matt; 22.06.2015
comment
Няколко коментара по-горе казвате, че имаме точки [2,4,6]. Имате предвид x-стойности или y-стойности? Точка има x и y стойност. Благодаря! - person CodeGuy; 01.07.2015
comment
@CodeGuy: Това са x-стойности, тъй като се преместват. y-стойностите остават същите и не се променят. - person Matt; 01.07.2015
comment
Мат, чудиш ли се дали можеш да добавиш код, който също да мащабира y-стойностите въз основа на референтните промени на y-стойностите? - person CodeGuy; 14.08.2015
comment
@CodeGuy: Можете да промените a = orig{n}(1,1) на a = orig{n}(1,2) и b = orig{n}(end,1); на b = orig{n}(end,2); и xn = ((orig{n}(:,1)-a)*p)+a+v(n); на yn = ((orig{n}(:,2)-a)*p)+a+v(n); и new{n} = [xn,orig{n}(:,2)]; на new{n} = [orig{n}(:,1),yn];. - person Matt; 14.08.2015