iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🕊

[Swift] How to determine if a point lies on a line segment in a 2D coordinate system

に公開

Explanation

As the title suggests.
Environment: Xcode 12.5.1

import Foundation

struct RectangularCoordinateSystemPoint {
    let x: Double
    let y: Double
    
    init(_ x: Double, _ y: Double) {
        self.x = x
        self.y = y
    }
}

extension RectangularCoordinateSystemPoint {
    // Uses the principle that for a relation A--P--B, the inequality AP + PB < AB does not hold.
    // * Note: Evaluating as AP + PB = AB may fail due to floating-point errors in Double, so .ulpOfOne is used instead.
    func isIncludeLineSegment(consistingOf endPoint: (a: RectangularCoordinateSystemPoint, b: RectangularCoordinateSystemPoint)) -> Bool {
        let distanceToA = self.distance(to: endPoint.a)
        let distanceToB = self.distance(to: endPoint.b)
        let distanceAB = endPoint.a.distance(to: endPoint.b)
        return distanceToA + distanceToB - distanceAB < .ulpOfOne
    }

    func distance(to target: RectangularCoordinateSystemPoint) -> Double {
        return sqrt(self.diffX(to: target) * self.diffX(to: target) + self.diffY(to: target) * self.diffY(to: target)) // pow() could be used for squaring, but it's avoided because it's cumbersome with specific Double type requirements.
    }
    
    func diffX(to target: RectangularCoordinateSystemPoint) -> Double {
        return self.x - target.x
    }
    
    func diffY(to target: RectangularCoordinateSystemPoint) -> Double {
        return self.y - target.y
    }
}

extension RectangularCoordinateSystemPoint {
    // Another evaluation method (might have issues)
    func isIncludeLineSegmentOther(consistingOf endPoint: (a: RectangularCoordinateSystemPoint, b: RectangularCoordinateSystemPoint)) -> Bool {
        if (endPoint.a.x <= self.x && self.x <= endPoint.b.x) || (endPoint.b.x <= self.x && self.x <= endPoint.a.x) {
            if (endPoint.a.y <= self.y && self.y <= endPoint.b.y) || (endPoint.b.y <= self.y && self.y <= endPoint.a.y) {
                if (self.y * (endPoint.a.x - endPoint.b.x)) + (endPoint.a.y * (endPoint.b.x - self.x)) + (endPoint.b.y * (self.x - endPoint.a.x)) == 0 {
                    // Point P is on the line segment AB
                    return true
                }
            }
        }
        return false
    }
}

/// Test
/// y = 2x + 1
/// 5 listed points: (0, 1), (1, 3), (2,5), (3,7), (4,8)
/// Consider the line segment between (1,3) and (3, 7)
let point01 = RectangularCoordinateSystemPoint(0,1)
let point13 = RectangularCoordinateSystemPoint(1,3)
let point25 = RectangularCoordinateSystemPoint(2,5)
let point37 = RectangularCoordinateSystemPoint(3,7)
let point48 = RectangularCoordinateSystemPoint(4,8)
let point00 = RectangularCoordinateSystemPoint(0,0) // Another point not on the line

/// Test with isIncludeLineSegment
print(point01.isIncludeLineSegment(consistingOf: (point13, point37))) // false
print(point13.isIncludeLineSegment(consistingOf: (point13, point37))) // true
print(point25.isIncludeLineSegment(consistingOf: (point13, point37))) // true
print(point37.isIncludeLineSegment(consistingOf: (point13, point37))) // true
print(point48.isIncludeLineSegment(consistingOf: (point13, point37))) // false
print(point00.isIncludeLineSegment(consistingOf: (point13, point37))) // false

/// Test with isIncludeLineSegmentOther
print(point01.isIncludeLineSegmentOther(consistingOf: (point13, point37))) // false
print(point13.isIncludeLineSegmentOther(consistingOf: (point13, point37))) // true
print(point25.isIncludeLineSegmentOther(consistingOf: (point13, point37))) // true
print(point37.isIncludeLineSegmentOther(consistingOf: (point13, point37))) // true
print(point48.isIncludeLineSegmentOther(consistingOf: (point13, point37))) // false
print(point00.isIncludeLineSegmentOther(consistingOf: (point13, point37))) // false
GitHubで編集を提案

Discussion