📌  相关文章
📜  最大的鲁洛三角形内接于椭圆内的正方形内(1)

📅  最后修改于: 2023-12-03 15:10:35.687000             🧑  作者: Mango

最大的鲁洛三角形内接于椭圆内的正方形内

鲁洛三角形是指以任意三角形的三条边为边长,作三个外接圆,连接相邻两圆圆心的线段,得到的三角形。对于任意给定的三角形,我们都可以构造出一个唯一的鲁洛三角形。

现在考虑一个问题:如何找到一个最大的鲁洛三角形,使其内接于一个给定的椭圆内,且这个鲁洛三角形内接于一个正方形中?

解法

我们可以将问题转化为求三角形的最小外接圆。求三角形的最小外接圆是一个经典问题,解法有多种,这里给出一种基于旋转卡壳的求解方法:

  1. 选择一个三角形的内角最大的顶点,假设它是 $A$,将其作为最小圆的圆心。
  2. 找到三角形外一条边上与 $A$ 相邻的顶点 $B$。
  3. 在顶点 $B$ 上构造一个单位圆,并以顶点 $A$ 为起点沿着三角形的外接圆逆时针旋转。枚举到第一个与圆 $B$ 相交的位置 $C$,即可得到圆 $B$ 与三角形的外接圆相交的弧 $AC$。这个弧就是三角形的最小外接圆弧。
  4. 用枚举最大/最小值的方式找到顶点 $B$ 使得圆弧 $AC$ 能够刚好切到椭圆的外面,这个顶点就是最大圆的圆心。
  5. 将得到的最小圆心和最大圆心连线,连接相邻圆心得到的线段就是鲁洛三角形的三条边。

最后,我们可以将得到的鲁洛三角形内切于椭圆的最大正方形求出,作为鲁洛三角形内接于椭圆内的正方形。

代码实现

这里给出 Python 代码实现。需要注意的是,由于涉及到旋转卡壳算法,代码中需要用到复数,需要一定的数学功底。

def rot_calipers(polygon):
    def cross(a, b):
        return a.real * b.imag - b.real * a.imag

    def dis(a, b):
        return abs(a - b)

    def dist(p, a, b):
        return abs(cross(p - a, b - a)) / dis(a, b)

    n = len(polygon)
    p1 = min(polygon, key=lambda p: (p.real, p.imag))
    v1 = 1
    while dist(polygon[0], p1, polygon[v1]) == 0.0:
        v1 = (v1 + 1) % n

    ans1 = ans2 = ansd = None
    t = 0
    for i in range(n):
        while True:
            vt = (t + 1) % n
            if cross(polygon[vt] - polygon[i], polygon[i] - polygon[t]) > 0:
                t = vt
            else:
                break

        d = dist(polygon[t], polygon[i], polygon[(i + 1) % n])
        if ansd is None or d > ansd:
            ans1 = i
            ans2 = t
            ansd = d

    return ans1, ans2, ansd


def find_largest_luer_triangle(ellipse):
    vertices = [complex(x, y) for x, y in ellipse.vertices]
    ans1, ans2, _ = rot_calipers(vertices)
    ans3 = (ans2 + 1) % len(vertices)

    v1 = vertices[ans1]
    v2 = vertices[ans2]
    v3 = vertices[ans3]
    r1 = abs(v2 - v1)
    r2 = abs(v3 - v2)

    def get_circumcenter(a, b, c):
        d = 2 * cross(b - a, c - a)
        ua = abs(a * a - b * b)
        ub = abs(b * b - c * c)
        return ((ua * (c.imag - a.imag) + ub * (a.imag - b.imag)) / d,
            (ua * (c.real - a.real) + ub * (a.real - b.real)) / -d)

    c1 = (v2 + v3) / 2 + (v3 - v2) * 1j
    c2 = (v1 + v2) / 2 + (v2 - v1) * 1j
    c = get_circumcenter(v1, v2, v3)
    r = dist(c, v1, v2)

    if dist(c1, c, v2) < r:
        c = c1.real + c1.imag * 1j
        r = abs(v2 - c)

    if dist(c2, c, v2) < r:
        c = c2.real + c2.imag * 1j

    scale = 2 * r / (abs(v3 - v2) + abs(v2 - v1))
    v1 *= scale
    v2 *= scale
    v3 *= scale

    phi = math.atan2((v3 - v2).imag, (v3 - v2).real)
    c /= scale
    c *= math.cos(phi) + math.sin(phi) * 1j
    h = r * (math.cos(phi) - math.sin(phi) * 1j)
    v1 -= c
    v2 -= c
    v3 -= c
    v1 *= math.cos(phi) + math.sin(phi) * 1j
    v2 *= math.cos(phi) + math.sin(phi) * 1j
    v3 *= math.cos(phi) + math.sin(phi) * 1j
    x1 = v1.real + h.imag
    y1 = -v1.imag + h.real
    x2 = v3.real + h.imag
    y2 = -v3.imag + h.real
    return x1, y1, x2, y2

这段代码中用到了旋转卡壳的算法,以及求解三角形外接圆的函数 get_circumcenter,这里不再赘述。需要注意的是,代码中的输入椭圆是使用 vertices 表示的,该列表包含了四个顶点的坐标,依次是左上角、右上角、右下角、左下角。输出的是与椭圆相切的正方形的左上角和右下角坐标,依次是 x1、y1、x2、y2。

结论

本文介绍了求解最大的鲁洛三角形内接于椭圆内,且内接于正方形的方法,并给出了 Python 代码实现。程序员可以根据自己的需要进行修改和扩展。