From b7ab155d3ca2ffb7cdbf6d64ad2759726c4f4773 Mon Sep 17 00:00:00 2001 From: Ky Leggiero Date: Thu, 16 Nov 2023 19:46:20 -0700 Subject: [PATCH] Added `distance` - Also added `CIVector` default conformance to `DualTwoDimensional` - Also added `magnitude` to `DualTwoDimensional`s where the `Length`s match and the distance can be determined. This was done for `CIVector` as sugar for its `.distance` between its two points - Improved description of how `Rectangle`'s `DualTwoDimensional` conformance needs both `Length`s to be the same. Thankfully didn't need to remove the `associatedtype Length` - Added an "Easy To Adopt" section to the README, inspired by this new `.distance` API --- README.md | 9 +++++ .../Basic Protocols/Rectangle.swift | 5 ++- .../CIVector + DualTwoDimensional.swift | 40 +++++++++++++++++++ .../DualTwoDimensional Extensions.swift | 28 +++++++++++++ .../Point2D Extensions.swift | 23 +++++++++++ Tests/RectangleToolsTests/Point Tests.swift | 25 ++++++++++++ 6 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 Sources/RectangleTools/Default Conformances/CIVector + DualTwoDimensional.swift create mode 100644 Tests/RectangleToolsTests/Point Tests.swift diff --git a/README.md b/README.md index 16c8277..475aaba 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,15 @@ Who knew there was so much to be done with rectangles? +## Easy To Adopt + +This library aims to never get in your way. Minimal arbitrary decisions, maximum flexibility. + +For example, other frameworks might say that they help you find the distance from any `CGPoint` to another, but doesn't provide that functionality to any other type. +This one doesn't care what types the two are, as long as thier `x` and `y` coordinates use the same type. That means if you want to measure the distance from some `CGPoint` to some custom 2D point-like structure which also uses `CGFlaot`s, this will happily let you do that with no fuss. + + + ## Thoroughly Tested ## Over 2,000 test assertions prove that this library works as it says it does diff --git a/Sources/RectangleTools/Basic Protocols/Rectangle.swift b/Sources/RectangleTools/Basic Protocols/Rectangle.swift index 87e5af2..a97735d 100644 --- a/Sources/RectangleTools/Basic Protocols/Rectangle.swift +++ b/Sources/RectangleTools/Basic Protocols/Rectangle.swift @@ -13,7 +13,10 @@ import Foundation // MARK: - Rectangle /// A two-dimensional rectangle -public protocol Rectangle: DualTwoDimensional, CartesianMeasurable { +public protocol Rectangle: DualTwoDimensional, CartesianMeasurable +where FirstDimensionPair.Length == SecondDimensionPair.Length, + FirstDimensionPair.Length == Self.Length +{ /// The unit in which the origin and size are defined associatedtype Length diff --git a/Sources/RectangleTools/Default Conformances/CIVector + DualTwoDimensional.swift b/Sources/RectangleTools/Default Conformances/CIVector + DualTwoDimensional.swift new file mode 100644 index 0000000..a5944f4 --- /dev/null +++ b/Sources/RectangleTools/Default Conformances/CIVector + DualTwoDimensional.swift @@ -0,0 +1,40 @@ +// +// CIVector + DualTwoDimensional.swift +// +// +// Created by The Northstar✨ System on 2023-11-16. +// + +import CoreImage + + + +extension CIVector: DualTwoDimensional { + + public var firstDimensionPair: FirstDimensionPair { + .init(x: x, y: y) + } + + + public var secondDimensionPair: SecondDimensionPair { + .init(x: z, y: w) + } + + + + public typealias FirstDimensionPair = CGPoint + public typealias SecondDimensionPair = CGPoint +} + + + +public extension DualTwoDimensional where Self: CIVector { + + init(firstDimensionPair: FirstDimensionPair, + secondDimensionPair: SecondDimensionPair) { + self.init(x: firstDimensionPair.x, + y: firstDimensionPair.y, + z: secondDimensionPair.x, + w: secondDimensionPair.y) + } +} diff --git a/Sources/RectangleTools/Synthesized Conveniences/DualTwoDimensional Extensions.swift b/Sources/RectangleTools/Synthesized Conveniences/DualTwoDimensional Extensions.swift index f3bb7a2..6723df1 100644 --- a/Sources/RectangleTools/Synthesized Conveniences/DualTwoDimensional Extensions.swift +++ b/Sources/RectangleTools/Synthesized Conveniences/DualTwoDimensional Extensions.swift @@ -8,6 +8,8 @@ import Foundation +import MultiplicativeArithmetic + public extension DualTwoDimensional @@ -179,3 +181,29 @@ public extension DualTwoDimensional @inlinable static var one: Self { self.init(firstDimensionPair: .one, secondDimensionPair: .one) } } + + + +public extension DualTwoDimensional +where FirstDimensionPair.Length == SecondDimensionPair.Length +{ + typealias Length = FirstDimensionPair.Length +} + + + +// MARK: - Math! + +public extension DualTwoDimensional +where FirstDimensionPair: Point2D, + SecondDimensionPair: Point2D, + FirstDimensionPair.Length == SecondDimensionPair.Length, + Length: MultiplicativeArithmetic, + Length: AdditiveArithmetic, + Length: ExpressibleByIntegerLiteral +{ + /// The magnitude of a vector is the distance from its anchor to its farthest indicated location + var magnitude: Length { + firstDimensionPair.distance(to: secondDimensionPair) + } +} diff --git a/Sources/RectangleTools/Synthesized Conveniences/Point2D Extensions.swift b/Sources/RectangleTools/Synthesized Conveniences/Point2D Extensions.swift index bd5f683..e540826 100644 --- a/Sources/RectangleTools/Synthesized Conveniences/Point2D Extensions.swift +++ b/Sources/RectangleTools/Synthesized Conveniences/Point2D Extensions.swift @@ -8,6 +8,8 @@ import Foundation +import MultiplicativeArithmetic + public extension Point2D { @@ -19,3 +21,24 @@ public extension Point2D { self.init(x: other.x, y: other.y) } } + + + +public extension Point2D +where Length: MultiplicativeArithmetic, + Length: AdditiveArithmetic, + Length: ExpressibleByIntegerLiteral +{ + /// Measures the distance from this point to another point + /// + /// - Parameter other: The remote point, to which you want to know the distance from this point + /// - Returns: An absolute distance to the other point (always greater than zero, not implying any direction) + func distance(to other: Other) -> Length + where Other.Length == Self.Length + { + sqrt( + (other.x - x).pow(2) + + (other.y - y).pow(2) + ) + } +} diff --git a/Tests/RectangleToolsTests/Point Tests.swift b/Tests/RectangleToolsTests/Point Tests.swift new file mode 100644 index 0000000..dc98532 --- /dev/null +++ b/Tests/RectangleToolsTests/Point Tests.swift @@ -0,0 +1,25 @@ +// +// Point Tests.swift +// +// +// Created by The Northstar✨ System on 2023-10-26. +// + +import XCTest +import RectangleTools + + + +final class Point_Tests: XCTestCase { + + func testDistance() { + XCTAssertEqual(CGPoint(x: 0, y: 0).distance(to: CGPoint(x: 1, y: 1)), sqrt(2)) + XCTAssertEqual(CGPoint(x: 0, y: 0).distance(to: CGPoint(x: -1, y: -1)), sqrt(2)) + } + + + func testMagnitude() { + // https://www.wolframalpha.com/input?i=distance+from+%28-2%2C-1%29+to+%285%2C6%29 + XCTAssertEqual(CIVector(x: -2, y: -1, z: 5, w: 6).magnitude, 7 * sqrt(2)) + } +}