NSAffineTransform เป็นคลาสจาก AppKit (macOS) ที่แทนการแปลงแบบ affine ในกราฟิกสองมิติ มันห่อหุ้มเมทริกซ์ 3×3 ที่อธิบายการรวมกันของการเลื่อน การปรับขนาด การหมุน และการเฉือนของพิกัด ตามเอกสารของ Apple (2026) NSAffineTransform ถูกใช้อย่างแพร่หลายใน Core Graphics เพื่อแปลงเส้นทาง จุด และบริบทกราฟิกบน macOS
ประเด็นสำคัญ
NSAffineTransform เป็นคลาสจากเฟรมเวิร์ก AppKit ที่มีเฉพาะบน macOS มันแทนการแปลงแบบ affine ในปริภูมิสองมิติผ่านเมทริกซ์ 3×3 แตกต่างจาก CGAffineTransform จาก Core Graphics (ซึ่งมีบน iOS) NSAffineTransform เป็นตัวห่อหุ้มเชิงวัตถุรอบโครงสร้าง CGAffineTransform เพิ่มเมธอดที่สะดวกสำหรับการทำงานกับ NSBezierPath, NSPoint และ NSSize
การแปลงแบบ affine คือการแปลงของระนาบที่รักษาความสัมพันธ์ “จุดอยู่บนเส้น” และความขนานของเส้น กล่าวอีกนัยหนึ่ง หลังจากการแปลงแบบ affine เส้นขนานยังคงขนานและเส้นตรงยังคงตรง อย่างไรก็ตาม ระยะทาง มุม และพื้นที่สามารถเปลี่ยนแปลงได้: การปรับขนาด การหมุน และการเฉือนเป็นกรณีพิเศษของการแปลงแบบ affine
บน iOS สิ่งที่เทียบเท่ากับ NSAffineTransform คือโครงสร้าง CGAffineTransform จาก Core Graphics API ทั้งสองใช้แบบจำลองทางคณิตศาสตร์เดียวกัน — เมทริกซ์การแปลงแบบ affine 3×3 ที่แถวสุดท้ายคือ [0, 0, 1] เสมอ ความแตกต่างคือ NSAffineTransform เป็นคลาส (มี retain/release ใน Objective-C) ในขณะที่ CGAffineTransform เป็นโครงสร้าง (ชนิดค่า) ใน Swift และ C
NSAffineTransform รองรับการแปลงพื้นฐานสี่ชนิด: translate (การเลื่อน), scale (การปรับขนาด), rotate (การหมุน) และ shear (การเฉือน) แต่ละชนิดแทนด้วยเมธอดแยกต่างหากและสามารถประยุกต์ใช้กับวัตถุการแปลงที่มีอยู่หรือใช้สร้างใหม่
| การดำเนินการ | เมธอด NSAffineTransform | พารามิเตอร์ | ผลลัพธ์ทางภาพ |
|---|---|---|---|
| Translate | translateXBy:yBy: | deltaX, deltaY | เลื่อนวัตถุตามแกน X และ Y |
| Scale | scaleBy: / scaleXBy:yBy: | factor / scaleX, scaleY | ขยายหรือย่อขนาด |
| Rotate | rotateByDegrees: / rotateByRadians: | มุมในหน่วยองศาหรือเรเดียน | หมุนรอบจุดกำเนิด |
| Shear | ผ่าน setTransformStruct | ส่วนประกอบ m12, m21 ของเมทริกซ์ | เอียงวัตถุตามแนวแกน |
Translate เพิ่มออฟเซ็ตให้กับพิกัดของทุกจุดของวัตถุ พารามิเตอร์ deltaX และ deltaY กำหนดปริมาณการเลื่อนในแนวนอนและแนวตั้งตามลำดับ ค่า deltaX ที่เป็นบวกเลื่อนไปทางขวา deltaY เลื่อนขึ้น Translate ไม่เปลี่ยนรูปร่างหรือขนาดของวัตถุ — เปลี่ยนเฉพาะตำแหน่ง
Scale คูณพิกัดของจุดด้วยตัวประกอบที่กำหนด ด้วยการปรับขนาดแบบสม่ำเสมอ (พารามิเตอร์เดียว) วัตถุจะขยายหรือย่อตามสัดส่วน ด้วยการปรับขนาดไม่สม่ำเสมอ รูปร่างจะบิดเบือน ตัวประกอบ 1.0 คงขนาดไว้ มากกว่า 1.0 ขยายใหญ่ จาก 0.0 ถึง 1.0 ย่อเล็ก ค่าลบสร้างภาพสะท้อน
Rotate หมุนวัตถุรอบจุดกำเนิดด้วยมุมที่กำหนด มุมหมุนที่เป็นบวกสอดคล้องกับการหมุนทวนเข็มนาฬิกา rotateByDegrees รับองศา (0–360) rotateByRadians รับเรเดียน (0–2π) การหมุนจะกระทำเทียบกับจุด (0, 0) เสมอ ดังนั้นการหมุนรอบจุดใดก็ได้ จำเป็นต้องรวม rotate กับ translate
การแปลงแบบ affine ใน 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 การกลับด้านสร้างการแปลง affine ใหม่ที่ยกเลิกการแปลงต้นฉบับ: หากคุณใช้การแปลงต้นฉบับกับจุดแล้วตามด้วยการกลับด้าน จุดจะกลับไปยังตำแหน่งเดิม การกลับด้านมีประโยชน์สำหรับการยกเลิกการแปลงบริบทธ์ก่อนวาดองค์ประกอบ UI ที่ไม่ควรถูกแปลง
ในการตรวจสอบว่าการแปลงเป็น เอกลักษณ์ (ไม่เปลี่ยนพิกัด) หรือไม่ ให้ใช้คุณสมบัติ isIdentity การแปลงเอกลักษณ์คือเมทริกซ์หน่วย — การคูณด้วยมันจะไม่เปลี่ยนพิกัดของจุด วัตถุ NSAffineTransform ใหม่ถูกสร้างขึ้นเป็นการแปลงเอกลักษณ์ คุณสมบัตินี้มีประโยชน์สำหรับการปรับให้เหมาะสม: หากการแปลงเป็นเอกลักษณ์ สามารถข้ามการประยุกต์ใช้ได้
NSAffineTransform สามารถประยุกต์ใช้กับวัตถุสี่ชนิด: NSPoint, NSSize, NSBezierPath และบริบทกราฟิก NSGraphicsContext แต่ละเป้าหมายมีเมธอดแยก: transformPoint:, transformSize:, transformBezierPath: และ set
เมธอด transformPoint: คูณเมทริกซ์การแปลงด้วยเวกเตอร์พิกัดของจุด (x, y, 1) และคืนค่าตำแหน่งใหม่ของจุด transformSize: ทำงานคล้ายกันแต่ไม่คำนึงถึงส่วนประกอบ tX และ tY — การเลื่อนไม่ถูกนำไปใช้ มีเพียงการปรับขนาด การหมุน และการเฉือน ซึ่งมีประโยชน์สำหรับการแปลงเวกเตอร์และขนาดที่ไม่ควรถูกเลื่อน
เมธอด transformBezierPath: สร้างวัตถุ NSBezierPath ใหม่ ซึ่งแต่ละจุดถูกแปลงตามเมทริกซ์ NSAffineTransform เส้นทางเดิมไม่ถูกแก้ไข — สำเนาที่มีพิกัดแปลงแล้วถูกสร้างขึ้น นี่เป็นการดำเนินการที่แพงสำหรับเส้นทางที่ซับซ้อนที่มีโหนดจำนวนมาก
เมธอด set ประยุกต์ใช้การแปลงกับบริบทกราฟิกปัจจุบัน (NSGraphicsContext) โดยแก้ไข CTM (Current Transformation Matrix) ของมัน หลังจากเรียก set การวาดทั้งหมดที่ตามมาในบริบทนี้จะถูกแปลง สำหรับการแปลงชั่วคราว ให้ใช้ saveGraphicsState / restoreGraphicsState ก่อนและหลัง set
มาดู ตัวอย่างเชิงปฏิบัติ ของการใช้ NSAffineTransform ในแอปพลิเคชัน macOS เพื่อแปลงวัตถุกราฟิก
ตัวอย่างนี้สร้าง NSAffineTransform ที่รวมการเลื่อน การหมุน และการปรับขนาด ขั้นแรก วัตถุถูกเลื่อนโดย (100, 50) จากนั้นหมุน 45 องศา และสุดท้ายขยาย 1.5 เท่า ลำดับมีความสำคัญ: การหมุนหลังการเลื่อนจะหมุนเทียบกับจุดกำเนิดใหม่
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 ช่วยให้กลับไปยังเมทริกซ์เดิม
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 ด้วยพิกัดที่แปลงแล้ว เส้นทางเดิมไม่เปลี่ยนแปลง และสำเนาที่แปลงแล้วสามารถวาดแยกต่างหากได้ — ซึ่งมีประโยชน์สำหรับการสร้างองค์ประกอบที่ซ้ำกันในตำแหน่งต่างๆ
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 เป็นคลาสจาก AppKit (เฉพาะ macOS) ในขณะที่ CGAffineTransform เป็นโครงสร้างจาก Core Graphics (iOS และ macOS) NSAffineTransform เป็นตัวห่อหุ้มเชิงวัตถุและให้เมธอดสำหรับทำงานกับ NSBezierPath CGAffineTransform เป็นโครงสร้างชนิดค่าแบบเบาที่ใช้กับ CGPath
ในการหมุนรอบศูนย์กลาง ให้ทำ สามขั้นตอน: 1) เลื่อนไปยังศูนย์กลางของวัตถุ (ย้ายจุดกำเนิดไปยังศูนย์กลาง), 2) หมุนด้วยมุมที่ต้องการ, 3) เลื่อนกลับด้วยเวกเตอร์ศูนย์กลางที่เป็นลบ การรวมกันเลื่อน—หมุน—เลื่อนให้การหมุนรอบจุดใดก็ได้ แทนที่จะหมุนรอบจุดกำเนิด
ไม่ 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 กลับด้าน
สรุป
invert ยกเลิกการแปลงแบบ affine ใดๆเราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม