2014-09-10 3 views
2

Я рисую большое количество связанных ребер на холсте, все из которых расположены таким образом, что некоторое конкретное расширение этих краев всегда дает точку пересечения с другим расширенным краем , Эта точка необходима для дальнейших вычислений - она ​​не актуальна в контексте того, что появляется на холсте. Мой код отлично подходит для общего случая, но если соответствующие расширения края выходят далеко, пересечения не всегда существуют (однако строки не параллельны) - по крайней мере, не в рамках этой программы. У кого-нибудь есть идеи улучшить точность этого кода? Я действительно работаю с BigDecimals позже, но для шагов построения я изначально думал, что достаточно использовать double.Далекие точки пересечения для почти параллельных линий

Этот расширяет край к обеим сторонам:

public Line2D.Double ExteLine(Point2D p, Point2D q){ 
    double slope, y3, y4; 
    slope = (q.getY() - p.getY())/(q.getX() - p.getX()); 
    y3 = (slope * (100000 - p.getX())) + p.getY(); 
    y4 = (slope * (-100000 - p.getX())) + p.getY(); 
    Point2D out1 = new Point2D.Double(100000, y3); 
    Point2D out2 = new Point2D.Double(-100000, y4); 
    Line2D.Double line = new Line2D.Double(out1, out2); 
    return line; } 

Это один находит пересечение:

public Point2D.Double getIntersectionPoint(Line2D.Double line1, Line2D.Double line2) { 
if (! line1.intersectsLine(line2)) { 
    System.out.println("No intersection"); 
    return null;} 
    double s1 = line1.getX1(), 
     sp2 = line1.getY1(), 
     rx = line1.getX2()-s1, 
     ry = line1.getY2()-sp2; 
    double qx = line2.getX1(), 
     qy = line2.getY1(), 
     sx = line2.getX2()-qx, 
     sy = line2.getY2()-qy; 
    double det = sx*ry - sy*rx; 
    if (det == 0) { System.out.println("Det = 0"); 
    return null;} 
    else { 
    double z = (sx*(qy-sp2)+sy*(s1-qx))/det; 
    if (z==0 || z==1) return null; 
    return new Point2D.Double(
     (double)(s1+z*rx), (double)(sp2+z*ry)); 
    } 
} 
+0

Являются ли края «истинными» линиями, которые расширяются до бесконечности в обоих направлениях (и просто проходят через сегмент линии, обозначаемый вашим Line2D.Doubles), или они являются сегментами линий, которые имеют разные начальные и конечные точки, представленные вашей Line2D. Удваивает? – paulk23

+1

. НИКОГДА не может зависеть от двух значений с плавающей запятой, вычисленных различными методами, которые ДОЛЖНЫ быть равными, чтобы быть в точности равными (для ==). Если вы должны использовать плавающую точку, вы должны сравнить ее с небольшим значением epsilon. То есть'if (abs (ab) <1.0E-6)' вместо 'if (a == b)' –

+0

@JimGarrison Это хороший момент и метод, который я использую на более позднем этапе, но я не достаточно посмотреть, как я могу включить это здесь (или даже использовать что-то еще, чем с плавающей запятой?) – Denor

ответ

1

Проективная геометрия, используя однородные координаты хороший подход для обработки строк, которые почти параллельно или даже полностью параллельной. Ниже приведен краткий курс:

  • Точка (x, y) в плоскости будет гомогенизирована (x, y, 1).
  • Умножения, представляющие одну и ту же точку, поэтому вы можете написать это как (2x, 2y, 2).
  • Линия ax + by + c = 0 представляется в виде (a, b, c).
  • Точка с однородными координатами p = (x, y, z) лежит на прямой g = (a, b, c) тогда и только тогда, когда их точечный продукт p · g = ax + на + cz равен нулю. (Вам, вероятно, это не понадобится, поскольку сравнение для равенства с удвоениями всегда сложно, но это объясняет, почему другие шаги работают так же, как и они.)
  • Линия, соединяющая две точки, может быть вычислена как перекрестное произведение их однородных координаты.
  • Точка пересечения между двумя линиями также может быть вычислена как поперечный разрез координат этих линий.
  • Поскольку вышеупомянутые шаги всегда умножаются, ваши двойные числа могут переполняться, если вы объедините несколько из них. Поэтому время от времени масштабируйте свои векторы, например. умножая их на некоторый скаляр, так что их запись с наибольшим абсолютным значением становится 1 или что-то еще.
  • Если вы хотите нарисовать точку (x, y, z) на холсте, вы вычислите (x/z, y/z) и нарисуете это.
  • Если z = 0, то предыдущий шаг невозможен. Это представляет собой точку «на бесконечности».
  • Если x = y = z = 0 или a = b = c = 0, то вектор не представляет собой точку или. а вместо этого - вырожденная ситуация. Например. вы пытались вычислить линию, соединяющую точку с самим собой.

Как вы можете видеть, это довольно просто реализовать, и что еще лучше: вам не нужны какие-либо различия в любом случае! Для фактического рисования вам может понадобиться пересечь строки с границами вашего холста, а затем использовать линии, соединяющие эти пересечения, в качестве определения ваших графических примитивов.

+0

Мне удалось повысить точность, работая с другим классом, но мне очень нравится эта идея, которая на самом деле хорошо вписывается в то, что я делаю, и я, надеюсь, обойдусь ее внедрением. – Denor

+0

Я знаю, что это может быть поздним продолжением, но в этом случае, как бы я вычислил поперечное отношение четырех коллинеарных точек, заданных в однородных координатах? Должен ли я сначала идентифицировать их с реальной проективной линией? – Denor

+0

@Denor: Обычно вы выбираете точку 'P' * not * в строке четырех точек' A, B, C, D', затем вычисляете 'CR (A, B; C, D) = ([ACP] [ BDP])/([ADP] [BCP]) 'где' [XYZ] 'является определителем однородных координат на плоскости, т. Е. Определителем 3 × 3. Но на самом деле это больше похоже на новый вопрос, который лучше всего задать на http://math.stackexchange.com/. В конце концов, мой ответ новее использовал перекрестные отношения. – MvG

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