NSAffineTransform — какво е това, видове трансформации и как да се прилагат

Автор: IT Sectr Публикувано: 2026-07-22 Време за четене: 9 мин

NSAffineTransform — е клас от AppKit (macOS), който представлява афинно преобразувание в двумерната графика. Той капсулира матрица 3×3, която описва комбинация от преместване, мащабиране, завъртане и срязване на координати. Според Apple Documentation (2026), NSAffineTransform се използва широко в Core Graphics за трансформиране на пътища, точки и графични контексти на macOS.

Основни моменти

  • NSAffineTransform — клас на AppKit за афинни трансформации на macOS с матрица 3×3
  • Афинно преобразувание запазва успоредността на линиите, но може да променя разстояния и ъгли
  • Основни операции: translate (преместване), scale (мащабиране), rotate (завъртане), shear (срязване)
  • Трансформациите се комбинират чрез умножение на матрици — редът на операциите влияе на резултата
  • NSAffineTransform се прилага към точки, размери, пътища и графичен контекст на Core Graphics

Какво е NSAffineTransform?

NSAffineTransform — е клас от рамката AppKit, достъпен само на macOS. Той представлява афинно преобразувание в двумерното пространство чрез матрица 3×3. За разлика от CGAffineTransform от Core Graphics (който е достъпен на iOS), NSAffineTransform е обектно-ориентирана обвивка на структурата CGAffineTransform, добавяща удобни методи за работа с NSBezierPath, NSPoint и NSSize.

Афинно преобразувание — е трансформация на равнината, която запазва отношението „точка лежи на права линия“ и успоредността на линиите. С други думи, след афинно преобразувание успоредните линии остават успоредни, а правите линии остават прави. Въпреки това, разстоянията, ъглите и площите могат да се променят: мащабирането, завъртането и срязването са специални случаи на афинни преобразувания.

На iOS аналогът на NSAffineTransform е структурата CGAffineTransform от Core Graphics. И двата API се основават на един и същ математически модел — матрица на афинно преобразувание 3×3, където последният ред винаги е [0, 0, 1]. Разликата е, че NSAffineTransform е клас (с retain/release в Objective-C), докато CGAffineTransform е структура (стойностен тип) в Swift и C.

Видове афинни трансформации

NSAffineTransform поддържа четири основни типа трансформации: translate (преместване), scale (мащабиране), rotate (завъртане) и shear (срязване). Всеки тип е представен от отделен метод и може да бъде приложен както към съществуващ обект на трансформация, така и използван за създаване на нов.

ОперацияМетод на NSAffineTransformПараметриВизуален ефект
TranslatetranslateXBy:yBy:deltaX, deltaYПреместване на обект по осите X и Y
ScalescaleBy: / scaleXBy:yBy:factor / scaleX, scaleYУвеличаване или намаляване на размерите
RotaterotateByDegrees: / rotateByRadians:ъгъл в градуси или радианиЗавъртане около началото на координатите
ShearЧрез setTransformStructкомпоненти m12, m21 на матрицатаСрязване на обект по оста

Translate — преместване

Translate добавя преместване към координатите на всички точки на обекта. Параметрите deltaX и deltaY определят големината на преместването хоризонтално и вертикално. Положителна стойност на deltaX премества надясно, deltaY — нагоре. Translate не променя формата или размера на обекта — само неговата позиция.

Scale — мащабиране

Scale умножава координатите на точките по даден фактор. При равномерно мащабиране (един параметър) обектът се увеличава или намалява пропорционално. При неравномерно мащабиране формата се изкривява. Фактор 1.0 оставя размера непроменен, по-голям от 1.0 увеличава, от 0.0 до 1.0 намалява. Отрицателните стойности създават огледален образ.

Rotate — завъртане

Rotate завърта обекта около началото на координатите на даден ъгъл. Положителният ъгъл на завъртане съответства на въртене обратно на часовниковата стрелка. rotateByDegrees приема градуси (0–360), rotateByRadians — радиани (0–2π). Завъртането винаги се извършва спрямо точката (0, 0), следователно за завъртане около произволна точка, rotate трябва да се комбинира с translate.

Матрично представяне 3×3

Афинно преобразувание в NSAffineTransform се представя от матрица 3×3, където последният ред винаги е [0, 0, 1]. Това е стандартно математическо представяне, което позволява представяне на комбинация от трансформации като произведение на матрици. Структурата на матрицата се определя чрез NSAffineTransformStruct, която съдържа шест важни компонента: m11, m12, m21, m22, tX, tY.

Компонентите m11, m12, m21, m22 отговарят за мащабиране, завъртане и срязване. Компонентите tX и tY отговарят за преместване. В стандартната конфигурация (единична матрица) m11 = 1, m22 = 1, останалите са 0. За получаване на текущите стойности на матрицата се използва transformStruct — свойството на NSAffineTransform, което връща структурата NSAffineTransformStruct.

Умножението на матрици е некомутативна операция. Това означава, че редът на прилагане на трансформациите има значение: translate + rotate дава различен резултат от rotate + translate. При комбиниране NSAffineTransform умножава новата матрица отляво на текущата: newMatrix = operation × currentMatrix. Затова последно добавената трансформация се прилага първа.

Комбиниране на трансформации

NSAffineTransform позволява комбиниране на няколко трансформации в един обект. Всяко извикване на translateXBy:yBy:, scaleBy: или rotateByDegrees: модифицира вътрешната матрица — умножава текущата матрица по матрицата на съответната трансформация. Това позволява създаване на сложна трансформация от последователност от прости операции.

За инвертиране на комбинирана трансформация се използва методът invert. Инверсията създава ново афинно преобразувание, което анулира оригиналното: ако приложим към точка оригиналното преобразувание, а след това инвертираното, точката се връща в началната позиция. Инверсията е полезна за анулиране на трансформация на контекста преди рисуване на UI елементи, които не трябва да бъдат трансформирани.

Проверката дали преобразуванието е тъждествено (не променя координатите) може да се извърши чрез свойството isIdentity. Тъждественото преобразувание е единичната матрица, при умножение с която координатите на точката не се променят. Нов обект NSAffineTransform се създава като тъждествено преобразувание. Това свойство е полезно за оптимизация: ако преобразуванието е тъждествено, неговото прилагане може да бъде пропуснато.

Прилагане към обекти и контекст

NSAffineTransform може да бъде приложен към четири типа обекти: NSPoint, NSSize, NSBezierPath и графичния контекст NSGraphicsContext. За всяка цел съществува отделен метод: transformPoint:, transformSize:, transformBezierPath: и set.

Трансформация на точки и размери

Метод transformPoint: умножава матрицата на трансформация по координатния вектор на точката (x, y, 1), връщайки новата позиция на точката. transformSize: работи аналогично, но не взема предвид компонентите tX и tY — преместването не се прилага, само мащабиране, завъртане и срязване. Това е полезно за трансформация на вектори и размери, които не трябва да бъдат премествани.

Трансформация на NSBezierPath пътища

Метод transformBezierPath: създава нов обект NSBezierPath, всяка точка на който е трансформирана според матрицата на NSAffineTransform. Оригиналният път не се променя — създава се копие с трансформирани координати. Това е скъпа операция за сложни пътища с голям брой възли.

Прилагане към графичен контекст

Метод set прилага трансформацията към текущия графичен контекст (NSGraphicsContext), модифицирайки неговия CTM (Current Transformation Matrix). След извикване на set, цялото последващо рисуване в този контекст ще бъде трансформирано. За временна трансформация използвайте saveGraphicsState / restoreGraphicsState преди и след set.

Примери за код в Swift

Нека разгледаме практически примери за използване на NSAffineTransform в macOS приложение за трансформация на графични обекти.

Създаване и комбиниране на трансформации

Примерът създава NSAffineTransform, който комбинира преместване, завъртане и мащабиране. Първо обектът се премества с (100, 50), след това се завърта на 45 градуса и накрая се увеличава 1.5 пъти. Редът е важен: rotate след translate дава завъртане спрямо новото начало на координатите.

swift
import AppKit

let transform = NSAffineTransform()
transform.translateXBy(100, yBy: 50)
transform.rotateByDegrees(45)
transform.scaleBy(1.5)

// Прилагане към точка
let point = NSPoint(x: 10, y: 20)
let transformedPoint = transform.transform(point)

// Прилагане към размер
let size = NSSize(width: 100, height: 50)
let transformedSize = transform.transform(size)

Трансформация на контекст за рисуване

Примерът показва прилагане на NSAffineTransform към графичен контекст чрез set и concat. След прилагане на трансформацията, цялото последващо рисуване се извършва в трансформираната координатна система. Запазването на графичното състояние чрез saveGraphicsState позволява връщане към оригиналната матрица.

swift
func drawWithTransform(in rect: NSRect) {
    guard let ctx = NSGraphicsContext.current
    else { return }

    ctx.saveGraphicsState()

    let xfm = NSAffineTransform()
    xfm.translateXBy(rect.midX, yBy: rect.midY)
    xfm.rotateByDegrees(30)
    xfm.scaleBy(0.8)
    xfm.concat()

    // Рисуване в трансформирана координатна система
    let path = NSBezierPath(roundedRect: NSRect(
        x: -50, y: -50,
        width: 100, height: 100
    ), xRadius: 10, yRadius: 10)
    NSColor.blue.setFill()
    path.fill()

    ctx.restoreGraphicsState()
}

Трансформация на NSBezierPath път

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

swift
func createTransformedTriangle() -> NSBezierPath {
    let triangle = NSBezierPath()
    triangle.move(to: NSPoint(x: 0, y: 0))
    triangle.line(to: NSPoint(x: 100, y: 0))
    triangle.line(to: NSPoint(x: 50, y: 100))
    triangle.close()

    let xfm = NSAffineTransform()
    xfm.translateXBy(200, yBy: 100)
    xfm.scaleXBy(1.5, yBy: 1.5)
    xfm.rotateByDegrees(45)

    let transformed = xfm.transform(triangle)
    return transformed
}

Често задавани въпроси

Каква е разликата между NSAffineTransform и CGAffineTransform?

NSAffineTransform — е клас от AppKit (само macOS), а CGAffineTransform — е структура от Core Graphics (iOS и macOS). NSAffineTransform е обектно-ориентирана обвивка и предоставя методи за работа с NSBezierPath. CGAffineTransform е лека структура от стойностен тип, използвана с CGPath.

Как да завъртим обект около неговия център?

За завъртане около центъра изпълнете три стъпки: 1) translate до центъра на обекта (преместване на началото на координатите в центъра), 2) rotate на желания ъгъл, 3) translate обратно с отрицателния вектор на центъра. Комбинацията translate—rotate—translate дава завъртане около произволна точка, а не около началото на координатите.

Може ли да се използва NSAffineTransform на iOS?

Не, NSAffineTransform е достъпен само на macOS в рамките на AppKit. На iOS се използва структурата CGAffineTransform от Core Graphics, която предоставя аналогични възможности: CGAffineTransformMakeTranslation, CGAffineTransformMakeScale, CGAffineTransformMakeRotation и CGAffineTransformConcat.

Как да анулираме трансформация?

Използвайте метода invert, който връща обратната трансформация. Ако умножим оригиналната трансформация по обратната, получаваме единична матрица (тъждествено преобразувание). За временно прилагане, извикайте saveGraphicsState преди set и restoreGraphicsState след него.

Как да създадем огледален образ?

Използвайте отрицателен мащаб: transform.scaleXBy(-1, yBy: 1) създава хоризонтален огледален образ, а scaleXBy(1, yBy: -1) — вертикален. За огледален образ спрямо произволна ос, комбинирайте translate (преместване до оста), scale с -1 и обратен translate.

Резюме

  • NSAffineTransform — клас на AppKit за афинни трансформации с матрица 3×3 на macOS
  • Четири основни типа: translate (преместване), scale (мащабиране), rotate (завъртане) и shear (срязване)
  • Матрицата 3×3 съдържа шест важни компонента: m11, m12, m21, m22, tX, tY
  • Редът на комбиниране влияе на резултата — умножението на матрици е некомутативно
  • Прилага се към NSPoint, NSSize, NSBezierPath и графичен контекст чрез CTM
  • Инверсия чрез метода invert анулира ефекта на всяко афинно преобразувание
  • На iOS аналогът е структурата CGAffineTransform от Core Graphics

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също