Форум Flasher.ru
Ближайшие курсы в Школе RealTime
Список интенсивных курсов: [см.]  
  
Специальные предложения: [см.]  
  
 
Блоги Правила Справка Пользователи Календарь Поиск рулит! Сообщения за день Все разделы прочитаны
 

Вернуться   Форум Flasher.ru > Flash > ActionScript 3.0 > Статьи

Версия для печати  Отправить по электронной почте    « Предыдущая тема | Следующая тема »  
Опции темы Опции просмотра
 
Создать новую тему Закрытая тема
Старый 13.03.2008, 20:45
Iv вне форума Посмотреть профиль Отправить личное сообщение для Iv Посетить домашнюю страницу Iv Найти все сообщения от Iv
  № 21  
Iv
 
Аватар для Iv

Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
По умолчанию Определение следующей цели

Давайте определимся, с тем, чего мы будем добиваться на этом этапе и критерях его окончания.

Тот факт, что редактор не показывает нам ошибки нетипизированного доступа вовсе не означает, что в коде их нет.

Неявный нетипизированный доступ - это применение наборов переменных представляющих одну сущность вместо применения объекта такого типа, который отражает сущность.

К примеру, точку на плоскости в координатах x=10, y=15 можно представить разными способами:
Код AS3:
var point1:Point = new Point(10, 15);
var point2:Array = new Array(10, 15);
var point3:Object = {x:10, y:15};
Но только первый способ применяет объект такого типа, который отражает сущность точки. В остальных случаях можно говорить о скрытом нетипизированном доступе.

Пройдемся по классам и отметим для себя такие случаи:

Класс SVGDisplayInFlash

метод getShapes:
- массивы хранят данные, а не объекты, представляющие сущности.

Класс Math2

метод getQuadBez_RP:
- в аргументах передаются точки вместо кривой Безье 3го порядка;
- внутри метода имеются обращения к массивам данных вместо обращений к сущностям.

метод intersect2Lines:
- в аргументах передаются точки вместо отрезков;

метод midpoint:
- в аргументах передаются координаты вместо отрезка;

метод bezierSplit:
- в аргументах передаются координаты вместо кривой Безье 3го порядка;
- возвращаемое значение содержит массивы данных вместо кривых Безье 3го порядка.

Класс PathToArray

метод makeDrawCmds:
- в аргументах и в теле метода широко используются массивы данных вместо классов, представляющих сущности.

Класс String2

случаев скрытого нетипизированного доступа нет.


Итак, наша задача на следующем шаге - избавиться от случаев скрытого нетипизированного доступа.
__________________
http://realaxy.com


Последний раз редактировалось iNils; 20.12.2010 в 13:24.
Старый 13.03.2008, 21:31
Iv вне форума Посмотреть профиль Отправить личное сообщение для Iv Посетить домашнюю страницу Iv Найти все сообщения от Iv
  № 22  
Iv
 
Аватар для Iv

Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
Следуя нашим правилам, начинаем с методов, правка которых вызовет наименьшее влияние на проект.

Этип критериям лучше всего подходит приватный метод midpoint: у него правильно типизированное возвращаемое значение, а поскольку он приватный, то аргументы для него будут изготовлены в этом же классе.

Методология та-же, что и применятась ранее: вызываем ошибку и исправляем ее.

Продублируем и закомментируем строку объявления метода. На память.
Заменим все аргументы метода на один (line:LineSVG).
Разумеется, такого типа не существует и, поэтому редактор подсветит ошибки.
Выделяем LineSVG и комбинацией CTRL+1 вызываем quick fix, соглашаемся на предложение создать класс LineSVG. Сохраняем его, возвращаемся.
Честно говоря, глядя на код хочется пойти по более короткому, хотя и теоретически опасному пути: немедленно перенести метод midpoint в класс LineSVG, поскольку он явно завистлив к данным этого класса и не использует данных текущего класса. Ну, что-ж, сократим путь. Переносим вместе с комментариями.

Теперь, собственно, нужно определиться с тем, что будет из себя представлять этот класс и как будет создаваться.
Задаем конструктор, метод инициализации экземпляра класса и get set методы доступа к стартовой и конечной точкам отрезка.
После этого делаем метод midpoint публичным и не статическим и реализуем аналогичное поведение, но с использованием стандартных методов. Заодно исправим комментарии к методу.

В итоге получаем такой класс:
Код AS3:
package com.itechnica.svg {
	import flash.geom.Point;	
 
	public class LineSVG {
 
		private var startPoint : Point;
		private var endPoint : Point;
 
		public function LineSVG(start:Point, end : Point) {
			initInstance(start, end);
		}
 
		private function initInstance(start : Point, end : Point) : void {
			startPoint = start;
			endPoint = end;
		}
 
		public function get start() : Point {
			return startPoint;
		}
		public function set start(value : Point):void {
			startPoint = value;
		}
		public function get end() : Point {
			return endPoint;
		}
		public function set end(value : Point):void {
			endPoint = value;
		}
 
 
		/**
		* @returns Point the midpoint of current line segment
		*/
		public function midpoint():Point {
			return Point.interpolate(startPoint, endPoint, 0.5);
		}
	}
 
}

Теперь можно приступить к исправлению образовавшихся ошибок в методе bezierSplit.
__________________
http://realaxy.com


Последний раз редактировалось iNils; 20.12.2010 в 13:24.
Старый 14.03.2008, 13:46
Iv вне форума Посмотреть профиль Отправить личное сообщение для Iv Посетить домашнюю страницу Iv Найти все сообщения от Iv
  № 23  
Iv
 
Аватар для Iv

Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
Исправим все строки, в которых применяется midpoint. Поступим следующим образом:
- продублируем и закомментируем верхнюю;
- удалим ошибочный вызов метода;
- вместо него впишем создание объекта LineSVG с пустыми new Point();
- из верхней строки скопируем пары координат и соответственно вставим в new Point()
- добавим вызов метода midpoint()

Результат должен получиться таким:
Код AS3:
// var p01:Point = midpoint (p1x, p1y, c1x, c1y);
var p01:Point = new LineSVG(new Point(p1x, p1y), new Point(c1x, c1y)).midpoint();
После этого можно скопировать получившуюся строку, закомментировать строки содержащие ошибки, и под каждой из них вставить скопированную строку. Затем аккуратно из закомментированной строки перенести имя переменной и координаты точек.
В итоге получаем отсутствие ошибок, а это значит, что мы можем протестировать плоды наших усилий.
Ошибок нет, переходим к типизации возвращаемого значения и аргументов.
__________________
http://realaxy.com


Последний раз редактировалось iNils; 20.12.2010 в 13:24.
Старый 14.03.2008, 15:59
Iv вне форума Посмотреть профиль Отправить личное сообщение для Iv Посетить домашнюю страницу Iv Найти все сообщения от Iv
  № 24  
Iv
 
Аватар для Iv

Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
Smile создаем класс CubicBezierSVG

В качестве аргументов методу bezierSplit передается четыре пары координат x и y, описывающих одну сущность - кривую Безье третьего порядка.
Чтобы описать эту сущность, создаем класс, в котором описываем базовые свойства кривой:

Код AS3:
package com.itechnica.svg {
	import flash.geom.Point;		
 
	public class CubicBezierSVG {
 
		private var startPoint : Point;
		private var startControlPoint : Point;
		private var endControlPoint : Point;
		private var endPoint : Point;
 
		public function CubicBezierSVG(start:Point, startControl:Point, endControl:Point, end:Point) {
			initInstance(start, startControl, endControl, end);
		}
 
		private function initInstance(start : Point, startControl : Point, endControl : Point, end : Point) : void {
			startPoint = start;
			startControlPoint = startControl;
			endControlPoint = endControl;
			endPoint = end;
		}
 
		public function get start() : Point {
			return startPoint;
		}
		public function set start(value : Point):void {
			startPoint = value;
		}
 
		public function get startControl() : Point {
			return startControlPoint;
		}
		public function set startControl(value : Point):void {
			startControlPoint = value;
		}
 
		public function get endControl() : Point {
			return endControlPoint;
		}
		public function set endControl(value : Point):void {
			endControlPoint = value;
		}
		public function get end() : Point {
			return endPoint;
		}
		public function set end(value : Point):void {
			endPoint = value;
		}
 
 
	}
}
И без специальных приборов видно, что если аргументы и возвращаемые значения метода bezierSplit класса Math2 заменить на объект CubicBezierSVG, то он будет завистлив к данным этого класса. В этот раз мы не будем шаг за шагом рефакторить метод, а с шашкой наголо создадим аналогичный метод в классе CubicBezierSVG.
Для этого просто скопируем метод в класс, продублируем и закомментируем верхний - на память.
Переименовываем метод в split, и делаем его публичным и не статическим. Удаляем аргументы. Редактор расцвел массой ошибок. Исправим их, заменив создание объектов Point из координат на соответствующие объекты Point текущего класса.
Для этого подглядываем в аргументы закомментированного класса: там точки идут по парам представляя контрольные точки кривой Безье.
Точки заменяем в том порядке, в котором они идут в аргументах закомментированного метода bezierSplit.
В итоге избавляемся от всех ошибок и видим, что можем удалить объявление точек p1 и p2 и заменить их непосредственно на startPoint и endPoint.
Для этого копируем startPoint в буфер обмена, удаляем строку инициализации p1, и там, где подсветилась ошибка вставляем из буфера обмена startPoint. Затем делаем то-же самое с p2.
Заменяем вложенные массивы на создание объектов CubicBezierSVG. После чего можем удалить закомментированный метод.

Затем мы можем заменить все new Point(...) на ранее полученные точки.

Полученный результат должен быть таким:

Код AS3:
public function split():Array {
	var p01:Point = new LineSVG(startPoint, startControlPoint).midpoint();
	var p12:Point = new LineSVG(startControlPoint, endControlPoint).midpoint();
	var p23:Point = new LineSVG(endControlPoint, endPoint).midpoint();
 
	var p02:Point = new LineSVG(p01, p12).midpoint();
	var p13:Point = new LineSVG(p12, p23).midpoint();
	var p03:Point = new LineSVG(p02, p13).midpoint();
 
	return [
		new CubicBezierSVG(startPoint, p01, p02, p03), 
		new CubicBezierSVG(p03, p13, p23, endPoint)
	];
}
Но на этом не остановимся и зададим читабельные имена и приведем объявления объектов в соответствие с их поведением:
Код AS3:
public function split():Array {
	const startMidpoint:Point = new LineSVG(startPoint, startControlPoint).midpoint();
	const middleMidpoint:Point = new LineSVG(startControlPoint, endControlPoint).midpoint();
	const endMidpoint:Point = new LineSVG(endControlPoint, endPoint).midpoint();
 
	const startMiddleMidpoint:Point = new LineSVG(startMidpoint, middleMidpoint).midpoint();
	const middleEndMidpoint:Point = new LineSVG(middleMidpoint, endMidpoint).midpoint();
	const centerMidpoint:Point = new LineSVG(startMiddleMidpoint, middleEndMidpoint).midpoint();
 
	return [
		new CubicBezierSVG(startPoint, startMidpoint, startMiddleMidpoint, centerMidpoint), 
		new CubicBezierSVG(centerMidpoint, middleEndMidpoint, endMidpoint, endPoint)
		];
}
Перейдем к замене использования метода Math2.bezierSplit на новый метод split класса CubicBezierSVG.
__________________
http://realaxy.com


Последний раз редактировалось iNils; 20.12.2010 в 13:24.
Старый 14.03.2008, 16:22
Iv вне форума Посмотреть профиль Отправить личное сообщение для Iv Посетить домашнюю страницу Iv Найти все сообщения от Iv
  № 25  
Iv
 
Аватар для Iv

Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
По умолчанию Тестирование нового метода

Прежде чем заменять вызовы старого метода на новый, мы обязательно(!!!) должны протестировать наш новый метод split.
Это ключевой и, как вы убедитесь, совсем не лишний шаг в выбранном способе рефакторинга.
Создавая новый метод мы имеем право делать всё что нам заблагорассудится с новым кодом, но старый код не имеем права трогать. И, когда новый метод готов, мы тестируем его. Тестирование нового метода обязательно производится заменой всей логики старого метода на вызов нового метода.

Продублируем метод Math2.bezierSplit и закомментируем верхний, чтобы было легко откатиться в случае ошибки.
После чего удалим всю старую логику и заменим на новую:
Код AS3:
private static function bezierSplit(p1x:Number, p1y:Number, c1x:Number, c1y:Number, c2x:Number, c2y:Number, p2x:Number, p2y:Number):Array {
	var curve : CubicBezierSVG = new CubicBezierSVG(
		new Point(p1x, p1y), 
		new Point(c1x, c1y),
		new Point(c2x, c2y),
		new Point(p2x, p2y)); 
 
	var halves:Array = curve.split();
	var firstCurve : CubicBezierSVG = halves[0] as CubicBezierSVG; 
	var secondCurve : CubicBezierSVG = halves[1] as CubicBezierSVG; 
 
	return [
			[firstCurve.start, firstCurve.startControl, 
			firstCurve.endControl, firstCurve.end], 
 
			[secondCurve.start, secondCurve.startControl, 
			secondCurve.endControl, secondCurve.end] 
	];
}
Тестируем. Работает! Только после этого мы можем приступать к следующему шагу: замене вызовов старого метода на новый.
__________________
http://realaxy.com


Последний раз редактировалось iNils; 20.12.2010 в 13:24.
Старый 14.03.2008, 17:07
Iv вне форума Посмотреть профиль Отправить личное сообщение для Iv Посетить домашнюю страницу Iv Найти все сообщения от Iv
  № 26  
Iv
 
Аватар для Iv

Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
По умолчанию Задержка по пути

Оказывается рановато заменять старые вызовы на новые.
Взглянув на метод Math2.getQuadBez_RP видим, что предварительно стоит заняться удалением локальных переменных p1, c1, c2, p2, которые дублируют точки, передаваемые в аргументах.
Для этого комментируем строку инициализации переменной и заменяем подсвечивающиеся ошибки на соответствующий аргумент. После чего удаляем ненужные закомментированные строки и получаем в итоге вот такой метод:
Код AS3:
public static function getQuadBez_RP(point1:Point, control1:Point, control2:Point, point2:Point, k:Number, qcurves:Array):void {
	// find intersection between bezier arms
	var s:Point = intersect2Lines (point1, control1, control2, point2);
	// find distance between the midpoints
	var dx:Number = (point1.x + point2.x + s.x * 4 - (control1.x + control2.x) * 3) * .125;
	var dy:Number = (point1.y + point2.y + s.y * 4 - (control1.y + control2.y) * 3) * .125;
	// split curve if the quadratic isn't close enough
	if (dx*dx + dy*dy > k) {
		var halves:Array = bezierSplit (point1.x, point1.y, control1.x, control1.y, control2.x, control2.y, point2.x, point2.y);
		var bezier0:Array = halves[0]; 
		var bezier1:Array = halves[1];
		// recursive call to subdivide curve
		getQuadBez_RP (point1, bezier0[1], bezier0[2], bezier0[3], k, qcurves);
		getQuadBez_RP(bezier1[0], bezier1[1], bezier1[2], point2,    k, qcurves);
	} else {
		// end recursion by saving points
		qcurves.push({p1x:point1.x, p1y:point1.y, cx:s.x, cy:s.y, p2x:point2.x, p2y:point2.y});
	}
}
__________________
http://realaxy.com


Последний раз редактировалось iNils; 20.12.2010 в 13:25.
Старый 14.03.2008, 17:51
Iv вне форума Посмотреть профиль Отправить личное сообщение для Iv Посетить домашнюю страницу Iv Найти все сообщения от Iv
  № 27  
Iv
 
Аватар для Iv

Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
И вот, наконец можем заменить вызов метода Math2.bezierSplit на метод split класса CubicBezierSVG.

Дублируем весь код, находящийся внутри блока if (до строки с else) и закомментируем верхний.
Чтобы использовать метод split требуется экземпляр объекта CubicBezierSVG. Создаем его, заменяем метод со старого на новый и задаем новый тип переменным bezier0 и bezier1:
Код AS3:
var sourceBezier : CubicBezierSVG = new CubicBezierSVG(point1, control1, control2, point2);
var halves:Array = sourceBezier.split();
var bezier0:CubicBezierSVG = halves[0] as CubicBezierSVG; 
var bezier1:CubicBezierSVG = halves[1] as CubicBezierSVG;
Честно говоря, после этих действий я ожидал увидеть ошибки и исправить их. Но из-за примененных операторов доступа к массиву тип объектов не определяется и редактор не может обнаружить ошибки. Придется искать их другим способом.

Переименуем переменную bezier0 на firstHalf. Редактор кода подсвечивает ошибки и мы идем по ним, заменяя имя и обращение к точке, к которой осуществляется доступ.
Проделываем то-же самое с переменной bezier1, заменив ее имя на secondHalf.
Получаем в итоге:
Код AS3:
getQuadBez_RP (point1, firstHalf.startControl, firstHalf.endControl, firstHalf.end, k, qcurves);
getQuadBez_RP(secondHalf.start, secondHalf.startControl, secondHalf.endControl, point2, k, qcurves);
Тестируем, убеждаемся в том, что всё в порядке.
__________________
http://realaxy.com


Последний раз редактировалось iNils; 20.12.2010 в 13:25.
Старый 14.03.2008, 20:53
Iv вне форума Посмотреть профиль Отправить личное сообщение для Iv Посетить домашнюю страницу Iv Найти все сообщения от Iv
  № 28  
Iv
 
Аватар для Iv

Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
По умолчанию Рефакторинг intersect2Lines

Я напомню цель текущего шага: удаление случаев скрытого нетипизированного доступа.
Следующей мишенью наших действий станет метод Math2.intersect2Lines. Этот случай очень похож на предыдущий. Но, как я и обещал, чтобы разнообразить наш инструментарий способ рефакторинга будет выбран иной.

Вырежем и вставим метод в класс LineSVG, сделаем его публичным. Сохраним оба документа (CTRL+SHIFT+S). Используя панель Problems перейдем на строку с ошибкой и добавим "LineSVG." перед вызовом метода.

Поскольку мы намерены избавиться от скрытого нетипизированного доступа в аргументах метода, для начала снизим зависимость кода метода от аргументов.
В самом начале метода объявим два объекта first и second типа LineSVG и создадим их используя аргументы метода.
Везде далее в коде заменим использование аргументов на доступ через объекты LineSVG.
Теперь аргументы используются только для создания объектов LineSVG, чего мы и добивались.

Сделаем метод не статическим: удаляем static из объявления метода. Сохраняем документ, переходим на ошибку используя панель Problems. Создаем экземпляр LineSVG и от его имени вызываем метод intersect2Lines:
Код AS3:
var startArm : LineSVG = new LineSVG(point1, control1);
var s:Point = startArm.intersect2Lines (point1, control1, control2, point2);
Поскольку данные startArm мы можем получить в классе LineSVG, удаляем два первых аргумента в вызове intersect2Lines. Затем переходим на метод и в нем также удаляем два первых аргумента. Подсвечиваются ошибки в объявлении first, удаляем эту строку. Затем удаляем все обращения к first, поскольку сейчас это текущий объект.
Заменим аргументы на один: second : LineSVG и удалим в теле метода объявление second.
Перейдем на ошибки в методе getQuadBez_RP.
Скопируем строку объявления startArm, переименуем переменную в endArm и скопируем параметры из вызова метода intersect2Lines. После этого параметры заменим на endArm.
В результате наших действий имя метода перестало отражать его суть. Переименуем в getLineIntersection и исправим вызов метода.
В итоге, измененная чать метода Math2.getQuadBez_RP будет выглядеть так:
Код AS3:
var startArm : LineSVG = new LineSVG(point1, control1);
var endArm : LineSVG = new LineSVG(control2, point2);
var s:Point = startArm.getLineIntersection(endArm);
Имя аргумента метода getLineIntersection - second, больше не отражает сути. Переименуем в target.

Удалим объявления временных переменных ссылающихся на существующие значения.Подробно процесс на примере:
- закомментируем строку var x1 : Number = start.x;
- скопируем start.x в буфер обмена;
- выделим первую ошибку: обращение к x1;
- CTRL+F, ставим фокус в поле Replace With, CTRL+V, в нем должна появиться строка "start.x";
- отмечаем чек боксы Case Sensitive и Whole Word
- жмем Replace All
Впрочем, вам возможно будет удобнее заменить выделением и вставкой.

Далее также поступаем с переменными y1, x4, y4 и получаем такой код:
Код AS3:
public function getLineIntersection(target : LineSVG) : Point {
	var dx1 : Number = end.x - start.x;
	var dx2 : Number = target.start.x - target.end.x;
 
	if (!(dx1 || dx2)) return null;
 
	var m1 : Number = (end.y - start.y) / dx1;
	var m2 : Number = (target.start.y - target.end.y) / dx2;
 
	if (!dx1) {
		return new Point(start.x, m2 * (start.x - target.end.x) + target.end.y);
	} else if (!dx2) {
		return new Point(target.end.x, m1 * (target.end.x - start.x) + start.y);
	}
	var xInt : Number = (-m2 * target.end.x + target.end.y + m1 * start.x - start.y) / (m1 - m2);
	var yInt : Number = m1 * (xInt - start.x) + start.y;
	return new Point(xInt, yInt);
}
Теперь мы можем более осознанно подойти к именованию временных переменных.
Заменяем dx1 на currentDistanceX, а dx2 на targetDistanceX, исправляя подсвечивающиеся ошибки сразу после каждого переименования. Пробуем поставить const вместо var. Ошибок нет, так и оставим.

Идем дальше вниз по коду. Добавляем фигурные скобки блоку if. Думайте что хотите, но я уверен, что if без фигурных скобок снижает читабельность кода, а пользы никакой.

В вычислении переменных m1 и m2 видим, что используются вычисления, аналогичные currentDistanceX и targetDistanceX.
Объявим аналогичные локальные константы currentDistanceY и targetDistanceY и присвоим им соответствующие значения скопировав из вычислений. Затем заменим вычисления на эти константы:
Код AS3:
const currentDistanceY : Number = end.y - start.y;
const targetDistanceY : Number = target.start.y - target.end.y;
 
var m1 : Number = currentDistanceY / currentDistanceX;
var m2 : Number = targetDistanceY / targetDistanceX;
Значение переменных стало куда очевиднее: это отношение высоты к ширине габаритного прямоугольника отрезков. Отношение высоты к ширине это тангенс. Переименовываем и делаем константами.
Код AS3:
const currentTangent : Number = currentDistanceY / currentDistanceX;
const targetTangent : Number = targetDistanceY / targetDistanceX;
Дальше по коды мы только переименуем переменные xInt и yInt на intersectionX и intersectionY и сделаем их константами. Остановимся на этом, ибо нет предела совершенству.
В итоге имеем вот такой метод:
Код AS3:
public function getLineIntersection(target : LineSVG) : Point {
	const currentDistanceX : Number = end.x - start.x;
	const targetDistanceX : Number = target.start.x - target.end.x;
 
	if (!(currentDistanceX || targetDistanceX)) { 
		return null;
	}
	const currentDistanceY : Number = end.y - start.y;
	const targetDistanceY : Number = target.start.y - target.end.y;
 
	const currentTangent : Number = currentDistanceY / currentDistanceX;
	const targetTangent : Number = targetDistanceY / targetDistanceX;
 
	if (!currentDistanceX) {
		return new Point(start.x, targetTangent * (start.x - target.end.x) + target.end.y);
	} else if (!targetDistanceX) {
		return new Point(target.end.x, currentTangent * (target.end.x - start.x) + start.y);
	}
	const intersectionX : Number = (-targetTangent * target.end.x + target.end.y + currentTangent * start.x - start.y) / (currentTangent - targetTangent);
	const intersectionY : Number = currentTangent * (intersectionX - start.x) + start.y;
	return new Point(intersectionX, intersectionY);
}
Тестируем код, радуемся, что всё получилось.
Потому, что программировать не надо бояться, программировать радоваться надо.

А если серьезно, то обратите внимание на то, что мы уже довольно давно работаем с кодом, но до сих пор так и не вникали в суть его логики. Более того, если вы обратили внимание, в процессе рефакторинга код понемногу сам нам рассказывает о себе.
__________________
http://realaxy.com


Последний раз редактировалось iNils; 20.12.2010 в 13:25.
Старый 14.03.2008, 21:48
Iv вне форума Посмотреть профиль Отправить личное сообщение для Iv Посетить домашнюю страницу Iv Найти все сообщения от Iv
  № 29  
Iv
 
Аватар для Iv

Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
По умолчанию рефакторим getQuadBez_RP

Вернемся в класс Math2.

Здесь мы видим сиротливо приютившийся метод getQuadBez_RP и мы понимаем, что из-за его завистливости к данным других объектов и вселенского одиночества в классе, он обречен на перемещение, а класс будет удален.

Поскольку в первую очередь мы намерены избавить метод от скрытого нетипизированного доступа в аргументах, то видим, что заменяться они будут на объект CubicBezierSVG. Туда и будем перемещать.
Правда, мы видим, что метод также использует класс LineSVG и, вполне возможно, что он будет завистлив к данным этого класса. Но это не проблема. Мы впоследствии сможем переместить, если потребуется.

Итак, приступим.
Перемещаем метод вместе с комментариями в класс CubicBezierSVG, сохраняем все документы и идем исправлять образовавшиеся ошибки в окне Problems. Не буду расписывать - вы и сами знаете что делать.
Тестируем. Затем удаляем класс Math2, при этом мысленно благодарим его за труд, который он делал в течение своей короткой, но очень яркой и полезной жизни.

Возвращаемся к методу getQuadBez_RP.
Как и в предыдущем случае, нам вначале потребуется избавиться от зависимости кода от аргументов метода, которые мы будем заменять на объект CubicBezierSVG.
Для этого вначале метода создаем экземпляр класса CubicBezierSVG:
Код AS3:
const source:CubicBezierSVG = new CubicBezierSVG(point1, control1, control2, point2);
И везде в коде метода вместо обращения к аргументам используем доступ через объект source.
Чтобы гарантировать себя от невнимательности, действуем по простой схеме:
- переименовываем аргумент;
- новое имя используем только в инициализации объекта source;
- остальные обращения заменяем на доступ через объект source.

Вот что в итоге получилось:
Код AS3:
public static function getQuadBez_RP(start:Point, startControl:Point, endControl:Point, end:Point, k:Number, qcurves:Array):void {
	const source:CubicBezierSVG = new CubicBezierSVG(start, startControl, endControl, end);
	// find intersection between bezier arms
	var startArm : LineSVG = new LineSVG(source.start, source.startControl);
	var endArm : LineSVG = new LineSVG(source.endControl, source.end);
	var s:Point = startArm.getLineIntersection(endArm);
	// find distance between the midpoints
	var dx:Number = (source.start.x + source.end.x + s.x * 4 - (source.startControl.x + source.endControl.x) * 3) * .125;
	var dy:Number = (source.start.y + source.end.y + s.y * 4 - (source.startControl.y + source.endControl.y) * 3) * .125;
	// split curve if the quadratic isn't close enough
	if (dx*dx + dy*dy > k) {
		var sourceBezier : CubicBezierSVG = new CubicBezierSVG(source.start, source.startControl, source.endControl, source.end);
		var halves:Array = sourceBezier.split();
 
		var firstHalf:CubicBezierSVG = halves[0] as CubicBezierSVG; 
		var secondHalf:CubicBezierSVG = halves[1] as CubicBezierSVG;
		// recursive call to subdivide curve
		getQuadBez_RP (source.start, firstHalf.startControl, firstHalf.endControl, firstHalf.end, k, qcurves);
		getQuadBez_RP(secondHalf.start, secondHalf.startControl, secondHalf.endControl, source.end,    k, qcurves);
	} else {
		// end recursion by saving points
		qcurves.push({p1x:source.start.x, p1y:source.start.y, cx:s.x, cy:s.y, p2x:source.end.x, p2y:source.end.y});
	}
}
Следующий шаг, как вы помните, переход от статичного метода к методу экземпляра класса.
Удаляем ключевое слово static, сохраняемся и идем исправлять ошибки.
Принцип, как всегда прост:
- вначале метода объявляем переменную:
Код AS3:
var source:CubicBezierSVG;
- в коде, перед ошибочной строкой создаем экземпляр класса CubicBezierSVG:
Код AS3:
source = new CubicBezierSVG();
- копируем первые четыре аргумента метода getQuadBez_RP в аргументы конструктора CubicBezierSVG.
- заменяем обращение к статическому методу на обращение через экземпляр source.
- переходим к следующей ошибке, пока они не иссякнут.
- тестируем проект.
- не забываем радоваться тому, как у нас ловко всё получается.
__________________
http://realaxy.com


Последний раз редактировалось iNils; 20.12.2010 в 13:26.
Старый 15.03.2008, 16:04
Iv вне форума Посмотреть профиль Отправить личное сообщение для Iv Посетить домашнюю страницу Iv Найти все сообщения от Iv
  № 30  
Iv
 
Аватар для Iv

Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
А теперь можно заняться удалением теперь уже ненужных аргументов.

Заменим создание объекта source на присвоение ему this:
Код AS3:
const source:CubicBezierSVG = this;
И тут же тестируем и видим, что происходит переполнение стека. Причина совсем рядом: рекурсивный вызов метода.
До тех пор, пока использовались аргументы, в методе создавались новые объекты CubicBezierSVG. А как только перешли на использование this наша невнимательность выползла наружу: мы не добавили объекты, от имени которых рекурсивно вызывается метод getQuadBez_RP.
Исправим.
Для начала откатимся назад до рабочего состояния:
Код AS3:
const source:CubicBezierSVG = new CubicBezierSVG(start, startControl, endControl, end);
Вот тут-то и проявляется прелесть исправления кода короткими шагами.
Протеституем, убедимся, что всё работает.

Вот участок кода, который требует вмешательства:
Код AS3:
getQuadBez_RP (source.start, firstHalf.startControl, firstHalf.endControl, firstHalf.end, k, qcurves);
getQuadBez_RP(secondHalf.start, secondHalf.startControl, secondHalf.endControl, source.end, k, qcurves);
Мы видим, что аргументы каждого вызова берутся из разных объектов. Для вызова в основном используются данные половинок кривой sourceBezier, а отличаются от остальных в первом случае - первый аргумент, а во втором случае четвертый и оба берут данные из совсем другого объекта - source. Но такие ли разные объект source и sourceBezier? Мы видим, что вовсе нет. Объект sourceBezier создан полностью из данных объекта source. Так может попробовать удалить sourceBezier и использовать source вместо него?

В этот момент мы должны остановиться и сказать себе: это не рефакторинг. Это действия по изменению логики приложения. К таким действиям мы должны подходить с совсем другими правилами.
__________________
http://realaxy.com


Последний раз редактировалось iNils; 20.12.2010 в 13:27.
Создать новую тему Закрытая тема Часовой пояс GMT +4, время: 17:02.
Быстрый переход
  « Предыдущая тема | Следующая тема »  
Опции темы
Опции просмотра

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.


 


Часовой пояс GMT +4, время: 17:02.


Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2024, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.