Обрезка прямой линии (2 точки CGPoint) внутри CGRect

Учитывая CGRect и линию, созданную с помощью 2 CGPoint, есть ли способ найти координаты, где линия пересекается с прямоугольником?

введите описание изображения здесь

Из предыдущего изображения: я пытаюсь заменить точки вне прямоугольника красными точками, пересекающими границы прямоугольника.

В двух словах я ищу способ обрезать линию внутри прямоугольника.

Это математический вопрос, но я хотел бы знать, как решить эту проблему, если возможно, используя фундамент.

После последних комментариев: кажется, что Core Graphics не может быть действительно полезной в этом процессе. Любые другие подсказки или формулы, которые я могу преобразовать в Swift?


person MatterGoal    schedule 09.02.2017    source источник
comment
Что ты пробовал? Какие исследования вы провели? Примеров этого уже должно быть бесчисленное множество. Найдите несколько, попробуйте перевести на свой любимый язык. Измените свой вопрос, используя то, что вы пробовали до сих пор.   -  person rmaddy    schedule 09.02.2017
comment
На самом деле я прошу помощи, потому что я не могу найти ничего похожего и (что также указано в моем вопросе) я прошу любое решение, которое использует базовую и базовую графику.   -  person MatterGoal    schedule 09.02.2017
comment
Вы можете быть чрезмерно ограничены за счет использования базовой и базовой графики. Специальной функции для этого нет. Вам нужно будет посчитать. Вы, конечно, можете вернуть CGPoint, и таким образом вы сможете использовать базовую графику. Но решение этой проблемы потребует решения нескольких линейных уравнений. Если вы уже знаете, как это сделать, все готово. Core Graphics здесь не предлагает ничего, кроме некоторых типов для работы (а Foundation на самом деле не предлагает ничего полезного для решения этой проблемы). Вам также необходимо определить, что произойдет, если линия пересечет несколько краев прямоугольника.   -  person Rob Napier    schedule 09.02.2017
comment
Правильно, я ищу способ решить эту проблему, используя что-то вроде CGRectIntersection. Я пытаюсь придумать точный способ обработки линии как прямоугольника и выполнить большую часть работы, не прибегая к математике ... Между прочим, несколько линейных уравнений для обрезки линии внутри прямоугольника не так просто Чтобы понять, любой намек с этой стороны тоже будет полезен.   -  person MatterGoal    schedule 09.02.2017
comment
На данный момент единственное решение, которое я вижу, - это разделить CGRect на 4 строки и проверить пересечение между линиями (вот хорошее руководство с математическим объяснением и кодом martin-thoma.com/how-to-check-if-two-line-segments-intersect / )   -  person MatterGoal    schedule 09.02.2017
comment
спасибо @slcott очень полезно! :)   -  person MatterGoal    schedule 10.02.2017


Ответы (1)


Что-то вроде этого (слегка протестировано) на основе Как определить, где пересекаются два отрезка линии?

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:
        // https://stackoverflow.com/a/1968345/97337

        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))
person Rob Napier    schedule 09.02.2017
comment
Кажется, он работает с одним пересечением, я пытаюсь понять, как заставить его работать также с линией, пересекающейся с двумя краями прямоугольника (завтра я пройду полную проверку). - person MatterGoal; 09.02.2017
comment
Главный вопрос будет в том, чтобы определить, каким вы хотите получить ответ. Обратите внимание на заявление о защите и комментарии в верхней части CGRect.intersection. В настоящее время он намеренно опускает этот случай. Если вы хотите получить полупроизвольное пересечение, просто удалите оператор защиты. - person Rob Napier; 09.02.2017
comment
Спасибо, этот код идеален. Вместо того, чтобы возвращать точку пересечения, с некоторыми изменениями я возвращаю линию, обрезанную внутри прямоугольника: - Я сохраняю охрану, возвращая исходную линию, когда обе точки содержатся в прямоугольнике. -Затем я проверяю все ребра, которые пересекаются с линией. - В зависимости от ребер я определяю, какая точка заменяется вычисленной точкой пересечения. Я обновлю свой вопрос, добавив этот код. - person MatterGoal; 10.02.2017
comment
Отлично, большое спасибо! - person aheze; 11.04.2021