Расскажу одну историю, которая приключилась со мной недавно. Если говорить "с начала", тогда стоит упомянуть, что недавно я познакомился с tween'ингом во flash. Понравились удобство, красота и прозрачность исполнения. Захотелось чего-то подобного в Delphi. Мне подсказали, что существует вот этот проект, в котором добрый человек как раз и реализовал анимацию между заданными значениями. Супер! О чем еще мечтать?! Но меня оттолкнули следующие вещи:
|
class function TAnimateEasing.elasticEaseOut(p: Extended; firstNum: integer; diff: integer): Extended; var c, period, s, amplitude: Extended; begin c := diff; if diff = 0 then Exit(c); //Divide by zero protect if p = 0 then Exit(firstNum); if p = 1 then Exit(c); period := 0.25; amplitude := c; if (amplitude < abs(c)) then begin amplitude := c; s := period / 4; end else begin s := period/(2*PI) * Math.ArcSin(c/amplitude); end; result := -(amplitude*Math.Power(2, -10*p) * sin( (p*1-s)*(2*PI)/period)) + c + firstNum; end;
и начал копировать по частям к себе. И здесь для всяких копи-пастеров заготовлена бомба! Если бы я бездумно копировал код из Интернета, то попался бы на вот какие расставленные капканы:
- при выполнении первого условия возвращается неверное значение (нужно вернуть firstNum)
- третье условие также возвращает неверное значение (нужно вернуть diff + firstNum)
- идем по коду дальше: мы сначала присваиваем amplitude := c; а потом при выполнении условия делаем это еще раз, зачем?
- если последнее условие не выполняется, тогда мы присваиваем s := period/(2*PI) * Math.ArcSin(c/amplitude); но ведь чуть раньше мы указали amplitude := c; получается, под арксинусом у нас единица, а результат такого арксинуса равен PI/2, итого присвоение сводится к s := period / 4; так зачем нам вообще условие if-then-else? неясно...
В итоге в моем модуле этот код сократился до такого:
class function TBaseEasingFunctions.elasticEaseIn(aStartValue, aDiffValue, aUnitValue: Single): Single; begin if (aDiffValue = 0) or (aUnitValue = 0) or (aUnitValue = 1) then begin if (aDiffValue = 0) then result := aStartValue; //Divide by zero protect if (aUnitValue = 0) then result := aStartValue; if (aUnitValue = 1) then result := aStartValue + aDiffValue; exit; end; result := (aDiffValue * Power(2, -10 * aUnitValue) * Sin((aUnitValue - 0.25/4)*(2*3.14)/0.25)) + aDiffValue + aStartValue; end;
Без ошибок, удобней читать, выше производительность и вообще все здорово...
Остальные функции пока не переводил к себе, так как tween'инг elasticEaseIn самый широкоиспользуемый и я начал именно с него. Так что наше дело движется...В общем, осталось собрать демку с анимационными движениями, описать здесь и выложить на всеобщее обозрение! Как вам такая идея?
Ждем демку с нетерпением
ОтветитьУдалитьоткровенно скажу, недавно при решении задачи дважды заполнил массив одинаковыми значениями, при этом циклы выглядели по разному. такие вещи можно попросту упустить когда обращаешь внимание на что-то другое. потому при повторном просмотре кода все это правится и сводится к нормальному виду.
ОтветитьУдалитьпо-моему писать класс, заточенный под собственные нужды это отличная идея, особенно если взять во внимание код из готового проекта))
приведенный участок кода уж слишком богат на "капканы" :) Может автор куда-то спешил или работал в потоке, потом его кто-то отвлек, он сбился и...
p.s
может стоит объединить
if (aDiffValue = 0) then result := aStartValue;
if (aUnitValue = 0) then result := aStartValue;
в
if (aDiffValue = 0 or aUnitValue = 0) then result := aStartValue;
?
у тебя дважды вычисляется каждое условие(с начала все три вместе, потом по отдельности), можно ведь как-то обойтись одним сравнением? это не принципиально и врятли даст прирост к скорости, только сделает код менее читабельным, но все же любопытно :)
Демку кроме исполняемой еще и в видео-формате, пожалуйста!))
soofX, ок, постараюсь в начале следующей недели выложить))
ОтветитьУдалитьAero, просто мне казалось, что это стандартные алгоритмы, которые просто копи-пастятся из проекта в проект... меньше всего я ожидал увидеть здесь проблемы.
может он и сбился, просто вроде во второй ревизии он комментарии добавлял - странно, что не проверил основной код, который выполняется постоянно...
по поводу условий и увеличения производительности... конечно, можно сделать так:
if(aDiffValue = 0)then
Exit(aStartValue);
и так по всем условиям...
но меня такие конструкции расстраивают, делают лично для меня код менее читабельным...
if(aDiffValue = 0)then
ОтветитьУдалитьresult := aStartValue;
почему бы не объединить это условие вместе с
if(aUnitValue = 0)then
result := aStartValue;
вот о чем я :)
да и мне скорее любопытно как ты относишься к таким конструкциям
if (aDiffValue = 0 or aUnitValue = 0) then result := aStartValue;
чем то, даст ли это прирост производительности)
Aero, сразу виден программист java))) мелочь, а выдает тебя с головой - отошел от делфи...
ОтветитьУдалитьскобки в выражении (aDiffValue = 0 or aUnitValue = 0) выставлены для делфи неверно ;) а в java, вроде, как раз так и расставляются))
по поводу применимости конструкции - да, перепишу как ты предложил, нормальная такая конструкция, то есть, если быть формальным: мое отношение "положительное"))
Делфи я запускаю в основном, что бы руками написать структуру, которая уже есть в Java.
ОтветитьУдалить