|
|
|||||
Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
|
Создаем классы конкретных команд
Создаем классы команд, все они наследуются от DrawingCommand.
Вот как будут выглядеть конструкторы этих классов: Добавим во все классы инициализацию экземпляров и методы доступа к данным. И, раз уж мы принялись структурировать проект, в папке svg создадим папку geom и переместим туда классы CubicBezierSVG, LineSVG и QuadraticBezierSVG. Правим ошибки, тестируем.
__________________
http://realaxy.com Последний раз редактировалось iNils; 20.12.2010 в 13:32. |
|
|||||
Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
|
Замена
Возвращаемся в класс PathToArray и оцениваем возможность применения новых классов.
Только в этот момент обнаруживаю, что замена строковых данных на константы не везде сработала, поскольку строковые данные использовались не только в двойных кавычках, но и в одинарных. Производим замену на константы. Наша задача - заменить в массиве нетипизированные данные на объекты данных. Это нужно сделать как в процедуре наполнения массива, так и в процедуре использования. Массив наполняется в классе PathToArray, где называется "drawCmds" и используется в классе SVGDisplayInFlash, где называется "dCmds". Заменим эти имена на одно: drawingCommands. Начнем замену на объекты данных с класса PathToArray. Благодаря тому, что строки заменены на константы, нам трудно ошибиться какого класса команда должна быть использована. Дублируем старую команду, комментируем верхнюю, а в нижней делаем соответствующую замену. Первые три замены будут выглядеть так: // drawingCommands.push([DrawingCommand.FILL, [fill.color, fill.alpha]]); drawingCommands.push(new FillCommand(fill.color, fill.alpha)); // drawingCommands.push([DrawingCommand.STYLE, [stroke.width, stroke.color, stroke.alpha]]); drawingCommands.push(new StyleCommand(stroke.width, stroke.color, stroke.alpha)); // drawingCommands.push([DrawingCommand.MOVE, [firstP.x, firstP.y]]); drawingCommands.push(new MoveCommand(firstP.clone())); Заменяем все случаи добавления в массив и переходим к методу getShapes класса SVGDisplayInFlash. Здесь для замены мы используем наши новые возможности. Мы можем заменить условную логику на полиморфизм. Суть будет заключаться в том, что в каждом классе команды рисования мы создадим метод draw и будем вызывать его в цикле. Чтобы реализовать такое поведение можно пойти двумя путями: создать метод draw в классе DrawingCommand, а в подклассах его перекрыть, либо создать интерфейс и в классах рисования имплементировать его. Мы пойдем вторым путем, поскольку нам это поможет создавать сами методы. К тому-же нам не придется беспокоиться о том, что кому-то в голову придет использовать метод draw у экземпляра класса DrawingCommand. В папке draw cоздаем интерфейс IDrawable и описываем метод draw: package com.itechnica.svg.draw { import flash.display.Graphics; public interface IDrawable { function draw(target:Graphics):void; } } После этого редактор подсветит ошибку, используя CTRL+1 исправляем ее - добавится метод draw. Каждому классу задаем свою соответствующую логику этого метода, например, класс CurveCommand будет использовать curveTo: public function draw(target:Graphics):void { target.curveTo(controlPoint.x, controlPoint.y, end.x, end.y); } После чего вся логика, ранее реализованая в switch заменится на две строки: private function getShapes(iPath:Number):void { var drawingCommands:Array = new Array(); conv = new PathToArray(paths[iPath], drawingCommands); // draw the shapes in clips in holder movieclip // holder.createEmptyMovieClip("p"+iPath, iPath+1); const drawLayer:Sprite = new Sprite(); holderMc.addChild(drawLayer); const drawTarget:Graphics = drawLayer.graphics; for (var i:Number=0; i<drawingCommands.length; i++) { var command:IDrawable = drawingCommands[i] as IDrawable; command.draw(drawTarget); } // repeat til all paths have been read if (++iPath < nPathNodes) getShapes(iPath); }
__________________
http://realaxy.com Последний раз редактировалось iNils; 20.12.2010 в 13:32. |
|
|||||
Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
|
хоку
В раздумиях великих
по коду класса я брожу. PathToArray.
__________________
http://realaxy.com Последний раз редактировалось Iv; 17.03.2008 в 14:27. |
|
|||||
Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
|
Следующая цель
Следующая цель у меня сомнений не вызывает. Мне категорически не нравятся два огромных метода в классе PathToArray. Они огромны, из-за этого их логика сложна для восприятия.
Вспомните, каким был метод getShapes и каким он стал. В итоге хочется чего-то такого. Плюс к этому, не хочется повторяться: я помню, что статья учебная и главная цель научиться чему-то новому и полезному. Смотрим, что из себя структурно представляет метод makeDrawCmds. В цикле while имеется switch, в каждом из case-ов которого имеется логика конвертации набора данных в объект данных. Всё бы ничего, но у нас в case-ах имеются загадочные строковые данные, которые нужно сделать константами. Мы понимаем откуда они вообще взялись: они используются в svg для обозначения того, какие действия следует произвести с данными. Но это в общем. А вот что именно обозначают эти буквы мы не знаем. Поэтому осмысленные имена дать не можем. Чтобы разобраться, обратимся к первоисточнику: описанию формата SVG: http://www.w3.org/Graphics/SVG/
__________________
http://realaxy.com |
|
|||||
Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
|
Формат данных
Смотрим и обнаруживаем раздел SVG in Russian. В словарик заглядывать не надо, чтобы понять что это за линки.
Заглянем для общего развития на википедию: http://ru.wikipedia.org/wiki/SVG Погуляв по русским линкам я нашел: http://jre.cplire.ru/koi/oct01/5/text.html Цитата:
Цитата:
__________________
http://realaxy.com |
|
|||||
Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
|
class FormatSVG
Суммируем наши знания в коде: создадим класс FormatSVG и перенесем в него из описания формата все инструкции атрибута d:
package com.itechnica.svg { // http://www.w3.org/TR/SVG11/paths.html public class FormatSVG { // 8.3.2 The "moveto" commands public static const MOVE_TO_ABSOLUTE : String = "M"; public static const MOVE_TO_RELATIVE : String = "m"; // 8.3.3 The "closepath" command public static const CLOSEPATH_ABSOLUTE : String = "Z"; public static const CLOSEPATH_RELATIVE : String = "z"; // 8.3.4 The "lineto" commands public static const LINE_TO_ABSOLUTE : String = "L"; public static const LINE_TO_RELATIVE : String = "l"; public static const HORIZONTAL_LINE_TO_ABSOLUTE : String = "H"; public static const HORIZONTAL_LINE_TO_RELATIVE : String = "h"; public static const VERTICAL_LINE_TO_ABSOLUTE : String = "V"; public static const VERTICAL_LINE_TO_RELATIVE : String = "v"; // 8.3.6 The cubic Bйzier curve commands public static const CUBIC_CURVE_TO_ABSOLUTE : String = "C"; public static const CUBIC_CURVE_TO_RELATIVE : String = "c"; public static const CUBIC_SMOOTH_CURVE_TO_ABSOLUTE : String = "S"; public static const CUBIC_SMOOTH_CURVE_TO_RELATIVE : String = "s"; // 8.3.7 The quadratic Bйzier curve commands public static const QUADRATIC_CURVE_TO_ABSOLUTE : String = "Q"; public static const QUADRATIC_CURVE_TO_RELATIVE : String = "q"; public static const QUADRATIC_SMOOTH_CURVE_TO_ABSOLUTE : String = "T"; public static const QUADRATIC_SMOOTH_CURVE_TO_RELATIVE : String = "t"; // 8.3.8 The elliptical arc curve commands public static const ARC_TO_ABSOLUTE : String = "A"; public static const ARC_TO_RELATIVE : String = "a"; } }
__________________
http://realaxy.com Последний раз редактировалось iNils; 20.12.2010 в 13:33. |
|
|||||
Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
|
тест
Перед тем, как начать замену на константы, сделаем один ход, который и впоследствии нам пригодится.
Мы сделаем простой тест. Задача теста будет проста: сверять результаты работы скрипта с некими образцами. Для начала создадим методы toString в классах папки draw: DrawingCommand: public function toString() : String { throw new Error("com.itechnica.svg.draw.DrawingCommand"); return "com.itechnica.svg.draw.DrawingCommand"; } override public function toString() : String { return "curveTo(" + controlPoint.x + ", " + controlPoint.y + ", " + end.x + ", " + end.y + ")"; } override public function toString() : String { return "beginFill(0x"+colorValue.toString(16)+", "+ alphaValue+")"; } override public function toString() : String { return "lineTo(" + targetPoint.x + ", " + targetPoint.y + ")"; } override public function toString() : String { return "moveTo(" + targetPoint.x + ", " + targetPoint.y + ")"; } override public function toString() : String { return "beginFill("+thicknessValue+", 0x"+colorValue.toString(16)+", "+ alphaValue+")"; } Добавим код, который нам поможет легко получать тестовые строки и тестировать код: private function getShapes(iPath:Number):void { var drawingCommands:Array = new Array(); conv = new PathToArray(paths[iPath], drawingCommands); // TEST as3 const updateTestArray:Boolean = false; const testString:String = Test.testArray[iPath]; const currentString:String = drawingCommands.toString(); if (updateTestArray) { trace("[\""+currentString+"\"],"); } else if (currentString != testString) { throw new Error("SVGDisplayInFlash.getShapes test error"); } // END TEST as3 ................ После этого заменяем значение updateTestArray на false и компилируем еще раз. Если ошибок не возникло, значит тест пройден успешно. Нужно четко отдавать себе отчет в том, что тест не идеален. Он отлавливает только те случаи, когда изменяется результирующая строка. Но далеко не факт, что данный пример svg файла использует все теги svg. Но даже такой тест лучше чем ничего.
__________________
http://realaxy.com Последний раз редактировалось iNils; 20.12.2010 в 13:33. |
|
|||||
Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
|
Применение новых констант
Вернемся к константам класса FormatSVG.
Ранее в классе DrawingCommand мы уже использовали константы, описывающие эти же сущности. Пришел их черед - они отслужили своё. По очереди закомментируем их, а в местах использования заменим на соответствующие из нашего нового класса. Каждый раз тестируем проект. По ходу дела обнаруживаем, что неверно интерпретировали "S" - думали, что это стиль, а оказалось что это кубическая кривая. Всё-таки работа с первоисточником очень важна и к описанию формата SVG надо было обращаться раньше. Следующим шагом избавляемся от использования строковых данных в case-ах метода makeDrawCmds. Это занудная работа, поэтому выложу результат.
__________________
http://realaxy.com |
|
|||||
Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
|
Вынос
Теперь мы готовы к следующему шагу: выделению методов из makeDrawCmds.
Начнем с самого первого case: FormatSVG.MOVE_TO_ABSOLUTE. - копируем содержимое case до строки break. - создаем метод createMoveToCommand - вставляем скопированное в метод. - задаем аргументы, одноименные с переменными, вызывающими ошибку. Получаем такой метод: private function createMoveToCommand(firstP:Point, lastP:Point, svgCmds:Array, j:int) : void { // moveTo point firstP = lastP = new Point(Number(svgCmds[j]), Number(svgCmds[j + 1])); drawingCommands.push(new FillCommand(fill.color, fill.alpha)); drawingCommands.push(new StyleCommand(stroke.width, stroke.color, stroke.alpha)); drawingCommands.push(new MoveCommand(firstP.clone())); j += 2; if (j < svgCmds.length && !isNaN(Number(svgCmds[j]))) { do { // if multiple points listed, add the rest as lineTo points lastP = new Point(Number(svgCmds[j]), Number(svgCmds[j + 1])); drawingCommands.push(new LineCommand(lastP.clone())); firstP = lastP; j += 2; } while (j < svgCmds.length && !isNaN(Number(svgCmds[j]))); } } Видим, что в методе изменяется значение переменной j, которая в дальнейшем используется в коде. Добавляем возвращаемое значение: private function createMoveToCommand(firstP:Point, lastP:Point, svgCmds:Array, j:int) : int { ......... return j; } Тестируем, получаем ошибку. Вот сейчас пишу и понятия не имею, что это за ошибка и откуда она взялась. Нет, конечно я мог бы вначале добиться результата и затем с видом умника достать рояль из кустов. Но ведь так не бывает в реальной жизни. Я вначале пишу что и как хочу сделать, затем пытаюсь это реализовать и пишу что получилось. Вот сейчас на непонятность нарвался, и буду рассказывать как я в таких случаях поступаю. Еще раз проверил старый код: закомментировал вызов метода, раскомментировал код, протестировал, всё работает. Возвращаем комментарии обратно и смотрим, что еще может сносить. Вполне реально - переприсвоение firstP и lastP. Встречается в четырех строках метода. И, если первую строку мы в состоянии вынести за пределы метода, то остальные две не получится. Попробуем решить проблему так: - вынесем объявление точек из метод в класс, сделаем переменные приватными. - добавим в начало метода обнуление переменных - удалим первые два аргумента в вызове метода и в методе. Тестируем. Работает. Становится понятна причина: в аргументах мы объявили точки, и присвоение новых значений переменным, объявленным в аргументах не давало требуемой замены одноименных переменных в методе makeDrawCmds. Копируем вызов: переходим на case FormatSVG.LINE_TO_RELATIVE и вставляем. Переименовываем в createLineToRelativeCommand. После чего с помощью CTRL+1 создаем метод. Копируем и комментируем дальнейший код до break. Переходим на новый метод и вставляем в него скопированный код. Заменяем return null на return j. Тестируем. Работает. Аналогично двигаемся дальше. После каждого шага тестируем. Первой остановкой на этом пути стало появление сubicBezier. Поиском по коду смотрим каким образом она применяется. В коде нет случаев, когда сubicBezier присваивается другой переменной. Попробуем объявить ее локальной переменной. И не забываем изменить возвращаемое значение с null на j. Тестируем, всё ок. Дальше планируем в аналогичных случаях поступать также. Если по ходу дела нарываемся на бесконечный цикл, значит попросту забыли заменить null на return j. Нарывался два раза. После того, как закончили и протестировали, сносим закомментированные строки и ненужное более объявление переменной сubicBezier. В итоге должен получиться вот такой метод: private function makeDrawCmds(svgCmds:Array):void { var j:Number = 0; var cmd:String; firstP = lastP = lastC = null; do { cmd = svgCmds[j++]; switch (cmd) { case FormatSVG.MOVE_TO_ABSOLUTE : j = createMoveToCommand(svgCmds, j); break; case FormatSVG.LINE_TO_RELATIVE : j = createLineToRelativeCommand(svgCmds, j); break; case FormatSVG.LINE_TO_ABSOLUTE : j = createLineToCommand(svgCmds, j); break; case FormatSVG.HORIZONTAL_LINE_TO_RELATIVE : j = createHorizontalLineToRelativeCommand(svgCmds, j); break; case FormatSVG.HORIZONTAL_LINE_TO_ABSOLUTE : j = createHorizontalLineToCommand(svgCmds, j); break; case FormatSVG.VERTICAL_LINE_TO_RELATIVE : j = createVerticalLineToRelativeCommand(svgCmds, j); break; case FormatSVG.VERTICAL_LINE_TO_ABSOLUTE : j = createVerticalLineToCommand(svgCmds, j); break; case FormatSVG.QUADRATIC_CURVE_TO_RELATIVE : j = createQuadraticCurveToRelativeCommand(svgCmds, j); break; case FormatSVG.QUADRATIC_CURVE_TO_ABSOLUTE : j = createQuadraticCurveToCommand(svgCmds, j); break; case FormatSVG.CUBIC_CURVE_TO_RELATIVE : j = createCubicCurveToRelativeCommand(svgCmds, j); break; case FormatSVG.CUBIC_CURVE_TO_ABSOLUTE : j = createCubicCurveToCommand(svgCmds, j); break; case FormatSVG.CUBIC_SMOOTH_CURVE_TO_RELATIVE : j = createCubicSmoothCurveToRelativeCommand(svgCmds, j); break; case FormatSVG.CUBIC_SMOOTH_CURVE_TO_ABSOLUTE: j = createCubicSmoothCurveToCommand(svgCmds, j); break; case FormatSVG.CLOSEPATH_RELATIVE : case FormatSVG.CLOSEPATH_ABSOLUTE : j = createClosePathCommand(svgCmds, j); break; } // end switch } while (j < svgCmds.length); }
__________________
http://realaxy.com Последний раз редактировалось iNils; 20.12.2010 в 13:33. |
|
|||||
Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
|
Пространства имен
Раз уж взялись за этот метод, доведем дело до конца.
Этот switch мне очень не нравится. Что мы можем сделать, чтобы избавиться? Есть два пути: первый мы уже использовали в похожей ситуации: заменили условный выбор полиморфизмом в методе getShapes класса SVGDisplayInFlash. Подойдет ли аналогичный способ для этого случая? Теоретически, мы можем загнать код в такие рамки. Но это неудобно и потребует существенного изменения логики приложения, чего мы избегаем на данном этапе. Второй путь - применить пространства имен, его и попробуем применить. Делаем небольшой тест: - для начала создадим пространство имен затем заменим пространство имен private у метода createMoveToCommand на созданное: И создадим тестовый код в соответствующем case: case FormatSVG.MOVE_TO_ABSOLUTE : var createMoveToCommand:*; var space:Namespace = Namespace(cmd); var method:Function = space::createMoveToCommand as Function; trace(space+ " >>> "+method); j = method(svgCmds, j); break; Идея заключается в том, чтобы метод makeDrawCmds принял примерно такой вид: private function makeDrawCmds(svgCmds:Array):void { firstP = lastP = lastC = null; var j:Number = 0; var createDrawCommand:Function; var space:Namespace; var method:Function; do { space = Namespace(svgCmds[j++]); method = space::createDrawCommand as Function; j = method(svgCmds, j); } while (j < svgCmds.length); } В итоге мы получим вполне симпатичный метод makeDrawCmds и... несколько неприятностей вдовесок. Для объявления пространства имен мы не можем использовать константы, объявленные в классе FormatSVG. Мы должны обязательно их объявлять вот так: но не можем применить ни один их этих вариантов: private namespace MOVE_TO_ABSOLUTE = new Namespace(FormatSVG.MOVE_TO_ABSOLUTE); private namespace MOVE_TO_ABSOLUTE = FormatSVG.MOVE_TO_ABSOLUTE; Второй неприятный довесок: FDT плохо поддерживает работу с пространствами имен. Например, чтобы избавиться от подсвечивания ошибки, нам пришлось объявлять переменную: Хотя есть случаи, в которых использование пространств имен себя оправдывает, но это не наш случай.
__________________
http://realaxy.com Последний раз редактировалось iNils; 20.12.2010 в 13:34. |
Часовой пояс GMT +4, время: 20:18. |
|
« Предыдущая тема | Следующая тема » |
|
|