在 CGRect 中修剪直线 (2 CGPoint)
Trimming a straight line (2 CGPoint) inside a CGRect
给定一个 CGRect
和一条用 2 CGPoint
创建的直线,有没有办法找到直线与矩形相交的坐标?
根据上图:我想要实现的是用与矩形边界相交的红点替换矩形外的点。
简而言之,我正在寻找一种方法来 trim 矩形内的一行。
这是一道数学题,但我想知道如何尽可能使用基础来解决这个问题。
根据最新的评论:看来Core Graphics在这个过程中并不能真正起到帮助作用。我可以在 Swift?
中转换的任何其他提示或公式
类似这样的东西(轻微测试),基于 How do you detect where two line segments intersect?
import CoreGraphics
let rect = CGRect(x: 10, y: 10, width: 100, height: 100)
let point1 = CGPoint(x: 200, y: 200)
let point2 = CGPoint(x: 20, y: 20)
struct LineSegment {
var point1: CGPoint
var point2: CGPoint
func intersection(with line: LineSegment) -> CGPoint? {
// We'll use Gavin's interpretation of LeMothe:
//
let p0_x = self.point1.x
let p0_y = self.point1.y
let p1_x = self.point2.x
let p1_y = self.point2.y
let p2_x = line.point1.x
let p2_y = line.point1.y
let p3_x = line.point2.x
let p3_y = line.point2.y
let s1_x = p1_x - p0_x
let s1_y = p1_y - p0_y
let s2_x = p3_x - p2_x
let s2_y = p3_y - p2_y
let denom = (-s2_x * s1_y + s1_x * s2_y)
// Make sure the lines aren't parallel
guard denom != 0 else { return nil } // parallel
let s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / denom
let t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / denom
// We've parameterized these lines as "origin + scale*vector"
// (s is the "scale" along one line, t is the "scale" along the other.
// At scale=0, we're at the origin at scale=1, we're at the terminus.
// Make sure we crossed between those. For more on what I mean by
// "parameterized" and why we go from 0 to 1, look up Bezier curves.
// We're just making a 1-dimentional Bezier here.
guard (0...1).contains(s) && (0...1).contains(t) else { return nil }
// Collision detected
return CGPoint(x: p0_x + (t * s1_x), y: p0_y + (t * s1_y))
}
}
extension CGRect {
var edges: [LineSegment] {
return [
LineSegment(point1: CGPoint(x: minX, y: minY), point2: CGPoint(x: minX, y: maxY)),
LineSegment(point1: CGPoint(x: minX, y: minY), point2: CGPoint(x: maxX, y: minY)),
LineSegment(point1: CGPoint(x: minX, y: maxY), point2: CGPoint(x: maxX, y: maxY)),
LineSegment(point1: CGPoint(x: maxX, y: minY), point2: CGPoint(x: maxX, y: maxY)),
]
}
func intersection(with line: LineSegment) -> CGPoint? {
// Let's be super-simple here and require that one point be in the box and one point be outside,
// then we can ignore lots of corner cases
guard contains(line.point1) && !contains(line.point2) ||
contains(line.point2) && !contains(line.point1) else { return nil }
// There are four edges. We might intersect with any of them (we know
// we intersect with exactly one, based on the previous guard.
// We could do a little math and figure out which one it has to be,
// but the `if` would be really tedious, so let's just check them all.
for edge in edges {
if let p = edge.intersection(with: line) {
return p
}
}
return nil
}
}
rect.intersection(with: LineSegment(point1: point1, point2: point2))
给定一个 CGRect
和一条用 2 CGPoint
创建的直线,有没有办法找到直线与矩形相交的坐标?
根据上图:我想要实现的是用与矩形边界相交的红点替换矩形外的点。
简而言之,我正在寻找一种方法来 trim 矩形内的一行。
这是一道数学题,但我想知道如何尽可能使用基础来解决这个问题。
根据最新的评论:看来Core Graphics在这个过程中并不能真正起到帮助作用。我可以在 Swift?
中转换的任何其他提示或公式类似这样的东西(轻微测试),基于 How do you detect where two line segments intersect?
import CoreGraphics
let rect = CGRect(x: 10, y: 10, width: 100, height: 100)
let point1 = CGPoint(x: 200, y: 200)
let point2 = CGPoint(x: 20, y: 20)
struct LineSegment {
var point1: CGPoint
var point2: CGPoint
func intersection(with line: LineSegment) -> CGPoint? {
// We'll use Gavin's interpretation of LeMothe:
//
let p0_x = self.point1.x
let p0_y = self.point1.y
let p1_x = self.point2.x
let p1_y = self.point2.y
let p2_x = line.point1.x
let p2_y = line.point1.y
let p3_x = line.point2.x
let p3_y = line.point2.y
let s1_x = p1_x - p0_x
let s1_y = p1_y - p0_y
let s2_x = p3_x - p2_x
let s2_y = p3_y - p2_y
let denom = (-s2_x * s1_y + s1_x * s2_y)
// Make sure the lines aren't parallel
guard denom != 0 else { return nil } // parallel
let s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / denom
let t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / denom
// We've parameterized these lines as "origin + scale*vector"
// (s is the "scale" along one line, t is the "scale" along the other.
// At scale=0, we're at the origin at scale=1, we're at the terminus.
// Make sure we crossed between those. For more on what I mean by
// "parameterized" and why we go from 0 to 1, look up Bezier curves.
// We're just making a 1-dimentional Bezier here.
guard (0...1).contains(s) && (0...1).contains(t) else { return nil }
// Collision detected
return CGPoint(x: p0_x + (t * s1_x), y: p0_y + (t * s1_y))
}
}
extension CGRect {
var edges: [LineSegment] {
return [
LineSegment(point1: CGPoint(x: minX, y: minY), point2: CGPoint(x: minX, y: maxY)),
LineSegment(point1: CGPoint(x: minX, y: minY), point2: CGPoint(x: maxX, y: minY)),
LineSegment(point1: CGPoint(x: minX, y: maxY), point2: CGPoint(x: maxX, y: maxY)),
LineSegment(point1: CGPoint(x: maxX, y: minY), point2: CGPoint(x: maxX, y: maxY)),
]
}
func intersection(with line: LineSegment) -> CGPoint? {
// Let's be super-simple here and require that one point be in the box and one point be outside,
// then we can ignore lots of corner cases
guard contains(line.point1) && !contains(line.point2) ||
contains(line.point2) && !contains(line.point1) else { return nil }
// There are four edges. We might intersect with any of them (we know
// we intersect with exactly one, based on the previous guard.
// We could do a little math and figure out which one it has to be,
// but the `if` would be really tedious, so let's just check them all.
for edge in edges {
if let p = edge.intersection(with: line) {
return p
}
}
return nil
}
}
rect.intersection(with: LineSegment(point1: point1, point2: point2))