【问题标题】:Calculate intersection of two lines using integers only仅使用整数计算两条线的交点
【发布时间】:2014-01-20 00:10:15
【问题描述】:

我可以很容易地计算出两条线的交点。如果我从两个顶点开始:

(x1,y1)
(x2,y2)

我可以通过(y1-y2)/(x1-x2)计算斜率,然后计算截距

y1 - slope * x1

然后再做一次,所以我必须设置斜率和截距,然后就这样做:

x = (intercept2 - intercept1) / (slope1 - slope2)
y = slope1 * x + intercept1
(免责声明:这甚至可能不起作用,但我已经得到了一些非常接近它的东西,它说明了我的一般技术)

BUT 仅适用于带小数或非整数的数据类型。假设顶点是:

(0,1)
(10,2)

计算斜率会得到(1-2)/(0-10),即-1/-10,不是1/10,而是0

如何获得仅使用整数产生有效结果的代码?

编辑:我不能使用花车根本!。没有铸造,什么都没有。此外,值的上限为 65535。而且一切都是无符号的。

【问题讨论】:

  • 将整数转换为浮点数、除法、舍入、转换回 int。
  • 什么是有效结果?在整数算术中,1/10 == 0 是正确的。使用整数准确表示斜率的唯一方法是创建具有分子和分母(分别为 1 和 10)的有理类型。否则,您将不得不使用浮点算术——这比实现和使用有理算术要容易得多。
  • @BWG 什么样的撒旦计算器不支持浮点数?
  • @BWG 将所有数字乘以 10(或 100),进行计算,然后将结果除以 10(或 100,或您选择的任何比例因子)。
  • “而且一切都是无符号的”——这个限制会让事情变得有趣。

标签: c++ integer line-intersection


【解决方案1】:

在高中减分数时,我们的老师教我们找到一个公分母

所以 1/4 - 1/6 = 3/12 - 2/12 = 1/12

对你的斜坡也这样做。

int slope1 = n1 / d1;  // numerator / denominator
int slope2 = n2 / d2;
// All divisions below should have 0 for remainder
int g = gcd( d1, d2 ); // gcd( 4, 6 ) = 2
int d = d1 * d2 / g; // common denominator (12 above)
int n = (d/d1) * n1 - (d/d2) * n2; // (1 in 1/12 above)
// n1/d1 - n2/d2 == n/d

我希望我是对的。

【讨论】:

  • 所以我将……我的斜率的分子和分母存储在不同的值中……然后以这种方式进行数学运算?
  • 请注意,它基本上仍然是“放大数字,计算,然后缩小结果”,但自动选择缩放因子。数学很棒!
【解决方案2】:

嗯..
(0,1)
(10,2)
和 (y1-y2)/(x1-x2)。嗯,这是一条线的描述,不是两条线的交点。
据我记得,线条以 x * v 的形式描述,其中 x 是一个 skalar,而 v 是一个
向量。然后是
x * (0,1) = v2 和
x * (10, 2) = v2.
因此,只有当两个方程都存在一个解时,这些线才会相交,
当存在无穷多个解时重叠,并且当它们平行时不相交。
http://www.gamedev.net/topic/647810-intersection-point-of-two-vectors/
解释基于点积的计算。

【讨论】:

  • 我只是使用这些顶点来说明整数如何不精确到无法返回的程度,而不是显示完整的问题。您最初是正确的,这是对一行的描述。我会有另一条线与之相交,但我的问题中没有显示。
【解决方案3】:

输入:L行经过(x1, y1)(x2, y2),M行经过(X1, Y1)(X2, Y2)

输出:两条线L和M的交点(x, y)

将 Wolfram Alpha 告知solve y = (y1-y2)/(x1-x2)*(x-x1)+y1 and y = (Y1-Y2)/(X1-X2)*(x-X1)+Y1 for x, y 以获得此解决方案:

但我不知道如何编写一个程序来为您的计算器实现上述解决方案,只使用uint16_t ALU。

【讨论】:

  • 其实这可以很好地工作。由于除法是发生的最后一步,因此不应损失精度。
【解决方案4】:

感谢 Graham Toal 的回答,下面是他们回答中链接 C 代码的原始 Rust 实现,修改为返回整条线的交点,而不是线段。它没有使用太多 Rust 特有的魔法,所以应该很容易移植到其他语言。

该函数返回一个PointLines 相交的地方,如果有的话,以及一个指示相交点是否位于两条相交线上(true)的标志(false)。

/// 2D integer point
struct Point {
    /// The x coordinate.
    pub x: i32,

    /// The y coordinate.
    pub y: i32,
}

/// Line primitive
struct Line {
    /// Start point
    pub start: Point,

    /// End point
    pub end: Point,
}

/// Check signs of two signed numbers
///
/// Fastest ASM output compared to other methods. See: https://godbolt.org/z/zVx9cD
fn same_signs(a: i32, b: i32) -> bool {
    a ^ b >= 0
}

/// Integer-only line segment intersection
///
/// If the point lies on both line segments, the second tuple argument will return `true`.
///
/// Inspired from https://stackoverflow.com/a/61485959/383609, which links to
/// https://webdocs.cs.ualberta.ca/~graphics/books/GraphicsGems/gemsii/xlines.c
fn intersection(l1: &Line, l2: &Line) -> Option<(Point, bool)> {
    let Point { x: x1, y: y1 } = l1.start;
    let Point { x: x2, y: y2 } = l1.end;
    let Point { x: x3, y: y3 } = l2.start;
    let Point { x: x4, y: y4 } = l2.end;

    // First line coefficients where "a1 x  +  b1 y  +  c1  =  0"
    let a1 = y2 - y1;
    let b1 = x1 - x2;
    let c1 = x2 * y1 - x1 * y2;

    // Second line coefficients
    let a2 = y4 - y3;
    let b2 = x3 - x4;
    let c2 = x4 * y3 - x3 * y4;

    let denom = a1 * b2 - a2 * b1;

    // Lines are colinear
    if denom == 0 {
        return None;
    }

    // Compute sign values
    let r3 = a1 * x3 + b1 * y3 + c1;
    let r4 = a1 * x4 + b1 * y4 + c1;

    // Sign values for second line
    let r1 = a2 * x1 + b2 * y1 + c2;
    let r2 = a2 * x2 + b2 * y2 + c2;

    // Flag denoting whether intersection point is on passed line segments. If this is false,
    // the intersection occurs somewhere along the two mathematical, infinite lines instead.
    //
    // Check signs of r3 and r4.  If both point 3 and point 4 lie on same side of line 1, the
    // line segments do not intersect.
    //
    // Check signs of r1 and r2.  If both point 1 and point 2 lie on same side of second line
    // segment, the line segments do not intersect.
    let is_on_segments = (r3 != 0 && r4 != 0 && same_signs(r3, r4))
        || (r1 != 0 && r2 != 0 && same_signs(r1, r2));

    // If we got here, line segments intersect. Compute intersection point using method similar
    // to that described here: http://paulbourke.net/geometry/pointlineplane/#i2l

    // The denom/2 is to get rounding instead of truncating. It is added or subtracted to the
    // numerator, depending upon the sign of the numerator.
    let offset = if denom < 0 { -denom / 2 } else { denom / 2 };

    let num = b1 * c2 - b2 * c1;
    let x = if num < 0 { num - offset } else { num + offset } / denom;

    let num = a2 * c1 - a1 * c2;
    let y = if num < 0 { num - offset } else { num + offset } / denom;

    Some((Point::new(x, y), is_on_segments))
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-07
    • 1970-01-01
    • 2011-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多