Практический пример использования перегрузки операторов для библиотеки CoreGraphics

от автора


Предистория

Под впечатлением статей про Sprite Kit и про GestureRecognizer возникла идея портировать простенькое приложение себе на телефон. А так как там достаточно часто используются структуры CGPoint, то озадачился изучением перегрузки операторов в Swift и подсмотрел тут.

Реализация

В несколько шагов создал себе некоторую «библиотеку» операторов перегрузки для удобной работы с выражениями для структур CGPoint. Заодно на каждом шаге потестировал тестирование в XCode.

Фактически реализована полная алгебра операций для линейного пространства векторов (структуры CGPoint), скаляров (CGFloat) и частично операторов (CGAffineTransform).

Так как, данные функции перегрузки операторов описаны глобально, то функционал добавляется в любой проект легко — через добавление исходника.

Конечно, примеров использования перегрузки на вымышленных ситуациях достаточно много, но данная «библиотека операторов» возможно окажется:

  1. чуть более конкретным примером,
  2. готовым решением, дающим экономии пары часов рутинного набора и отладки простейшего кода (листинг исходника целиком приведен в конце).

Шаг за шагом

Шаг 1: операторы сложения/вычитания векторов

Стоит обратить внимание, как во втором случае с помощью prefix указывается на унарность оператора-префикса.

func +(left: CGPoint, right: CGPoint)->CGPoint{ 	return CGPoint(x: left.x+right.x, y: left.y+right.y) } prefix func -(right: CGPoint)->CGPoint{ 	return CGPoint(x: -right.x, y: -right.y) } func -(left: CGPoint, right: CGPoint)->CGPoint{ 	return left + (-right) } 

Шаг 2: умножение и деление вектора на скаляр

Здесь стоит обратить внимание на то, что первые два оператора образуют «симметрию» умножения скаляра и вектора.

func *(left: CGPoint, right: CGFloat)->CGPoint{ 	return CGPoint(x: left.x*right, y: left.y*right) } func *(left: CGFloat, right: CGPoint)->CGPoint{ 	return right*left } func /(left: CGPoint, right: CGFloat)->CGPoint{ 	return left*(1/right) } 

Шаг 3: совмещение вышеперечисленных операций с присвоением

Важно обратить внимание, как с помощью inout описывается изменяемость левого аргумента, что необходимо для операций с присвоением.

func +=(inout left:CGPoint, right:CGPoint){ 	left = left+right } func -=(inout left:CGPoint, right:CGPoint){ 	left = left-right } func *=(inout left:CGPoint, right:CGFloat){ 	left = left*right } func /=(inout left:CGPoint, right:CGFloat){ 	left = left/right } 

Шаг 4: скалярное и 'смешанное' произведение

Очень полезные вещи, так как первое произведение дает скаляр и пропорционально косинусу угла между векторами, а второе произведение является псевдоскаляром (то есть меняет знак при перестановке множителей) и пропорционально синусу угла между векторами (жест «вращение» вычисляет величину угла вероятнее всего как раз на базе этого выражения).

func *(left: CGPoint, right: CGPoint)->CGFloat{ 	return left.x*right.x + left.y*right.y } func ^(left: CGPoint, right: CGPoint)->CGFloat{ 	return left.x*right.y - left.y*right.x } 

Шаг 5: произведение вектора на оператор аффинного преобразования

Здесь порядка ради вводится только умножение вектора на оператор справа.

func *(left: CGPoint, right: CGAffineTransform)->CGPoint{ 	return CGPointApplyAffineTransform(left, right) } func *=(inout left:CGPoint, right:CGAffineTransform){ 	left = left*right } 

Шаг 6: произведение операторов аффинного преобразования

func *(left: CGAffineTransform, right: CGAffineTransform)->CGAffineTransform{ 	return CGAffineTransformConcat(left, right) } func *=(inout left:CGAffineTransform, right:CGAffineTransform){ 	left = left*right } 

Заключение

Приведенный пример являет необходимым и достаточным набором в текущем понимании моих потребностей. В частности, поэтому не реализована алгебра для матриц и для аналогичных структур (напр., CGSize).

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

Весь код целиком

Это можно уже копировать непосредственно в проект.

import Foundation  func +(left: CGPoint, right: CGPoint)->CGPoint{ 	return CGPoint(x: left.x+right.x, y: left.y+right.y) } prefix func -(right: CGPoint)->CGPoint{ 	return CGPoint(x: -right.x, y: -right.y) } func -(left: CGPoint, right: CGPoint)->CGPoint{ 	return left + (-right) } ///////////////////////////////////////////////////  func *(left: CGPoint, right: CGFloat)->CGPoint{ 	return CGPoint(x: left.x*right, y: left.y*right) } func *(left: CGFloat, right: CGPoint)->CGPoint{ 	return right*left } func /(left: CGPoint, right: CGFloat)->CGPoint{ 	return left*(1/right) } ///////////////////////////////////////////////////  func +=(inout left:CGPoint, right:CGPoint){ 	left = left+right } func -=(inout left:CGPoint, right:CGPoint){ 	left = left-right } func *=(inout left:CGPoint, right:CGFloat){ 	left = left*right } func /=(inout left:CGPoint, right:CGFloat){ 	left = left/right } /////////////////////////////////////////////////// ///////////////////////////////////////////////////  func *(left: CGPoint, right: CGPoint)->CGFloat{ 	return left.x*right.x + left.y*right.y } func /(left: CGPoint, right: CGPoint)->CGFloat{ 	return left.x*right.y - left.y*right.x } /////////////////////////////////////////////////// ///////////////////////////////////////////////////  func *(left: CGPoint, right: CGAffineTransform)->CGPoint{ 	return CGPointApplyAffineTransform(left, right) } func *=(inout left:CGPoint, right:CGAffineTransform){ 	left = left*right } ///////////////////////////////////////////////////  func *(left: CGAffineTransform, right: CGAffineTransform)->CGAffineTransform{ 	return CGAffineTransformConcat(left, right) } func *=(inout left:CGAffineTransform, right:CGAffineTransform){ 	left = left*right } 

ссылка на оригинал статьи http://habrahabr.ru/post/271647/