2017-02-09 5 views
1

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

enter image description here

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

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

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

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

+0

Что вы пробовали? Какие исследования вы сделали? Здесь должно быть множество примеров этого. Найдите несколько, сделайте попытку перевести на свой любимый язык. [Изменить] ваш вопрос с тем, что вы пробовали до сих пор. – rmaddy

+0

На самом деле я прошу о помощи, потому что я не могу найти ничего подобного и (как указано в моем вопросе). Я прошу о любом решении, которое использует базовую и основную графику. – MatterGoal

+1

Вы можете чрезмерно ограничить себя «использованием фундамента и основной графики». Это не делает никакой конкретной функции. Вам нужно будет сделать математику. Вы можете, конечно, вернуть 'CGPoint', и таким образом вы будете использовать основную графику. Но решение этой задачи потребует решения нескольких линейных уравнений. Если вы уже знаете, как это сделать, все готово. Core Graphics здесь ничего не дает, кроме некоторых типов для работы (и Foundation действительно не предлагает ничего полезного для этой проблемы). Вам также необходимо определить, что произойдет, если линия пересечет несколько ребер прямоугольника. –

ответ

1

Что-то вроде этого (слегка испытано), основанный на 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: 
     // 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)) 
+0

Кажется, что я работаю с одним пересечением, я пытаюсь выяснить, как заставить его работать также с линией, пересекающейся с двумя ребрами rect (я пойду через полную проверку завтра). – MatterGoal

+0

Главный вопрос будет определять, что вы хотите, чтобы этот ответ был. Обратите внимание на инструкцию и комментарии в верхней части 'CGRect.intersection'. В настоящее время он умышленно упускает этот случай. Если вы хотите получить полу-произвольное пересечение, просто удалите инструкцию охраны. –

+0

Спасибо, этот код совершенен. Вместо того, чтобы возвращать точку пересечения с несколькими изменениями, я возвращаю строку, которая обрезана внутри прямоугольника: - Я держу охрану, возвращая исходную строку, когда обе точки содержатся в прямоугольнике. -Тогда я проверяю все ребра, которые пересекаются с линией - В зависимости от краев я определяю, какую точку заменить на точку пересечения. Я обновлю свой вопрос, добавив этот код. – MatterGoal

Смежные вопросы