【问题标题】:Fast way to enlarge a triangle快速放大三角形的方法
【发布时间】:2018-03-12 21:38:54
【问题描述】:

我有一个三角形,由它的 3 个(假设是整数)坐标定义,比如:

A(ax, ay) B(bx, by) C(cx, cy)

如何“放大”ABC 三角形以获得一个新的 (DEF),它是具有整数坐标的 最小(最小面积)三角形,其中包含以前的?

例如,如果我有三角形ABC,如何获得三角形DEF?

【问题讨论】:

  • 这是一个有趣的问题。你有没有在这个问题上做过任何尝试,你有任何代码要显示吗?您是否尝试过将每个顶点在 8 个主要方向(上、右、右等)中的每一个上移动一步并检查每个生成的三角形?
  • 也许检查如何find the area of overlap of two triangles 是一个好的开始。
  • 坐标是否可以保持不变?在您的示例中,如果 D 移动到 (-3, 0)、F=B 和 E=C,这是一个解决方案吗?
  • 我想你最好在Mathematics Stack Exchange问这个问题

标签: math geometry transform computational-geometry


【解决方案1】:

部分回答:

新三角形的顶点必须属于原三角形的外角(通过加长边得到)。

然后找到新的候选顶点作为当移动平行于一侧的线远离对角时遇到的第一个网格点。

我不确定这是否一定会给出最佳解决方案。为了安全起见,您可以尝试候选者的直接邻居,并检查是否有任何组合产生更小的区域。

【讨论】:

  • 我认为正确的解决方案在于这个方向;它与寻找包含一组点的最小凸包的算法是一致的。为了达到最佳效果,它可能需要一种通过三角形边长以某种方式对角区域的搜索进行加权的方法。
【解决方案2】:

python 2.7 中的以下方法可能是您的问题的公平解决方案。

from itertools import combinations, product
import matplotlib.pyplot as plt
import numpy as np

class Triangle(object):

    def __str__(self):
        return "Triangle: " + str(self.vertices)

    def __init__(self,A,B,C):
        """ Creates a Triangle of vertices A,B,C """
        self.A = A
        self.B = B
        self.C = C
        self.vertices = [A,B,C]

    def area(self):
        """ Area of the triangle """
        x1 = self.A[0]
        y1 = self.A[1]
        x2 = self.B[0]
        y2 = self.B[1]
        x3 = self.C[0]
        y3 = self.C[1]
        return abs((x1 * (y2 - y3) + x2 * (y3 - y1)
                + x3 * (y1 - y2)) / 2.0)

    def isInside(self,X):
        """ Function to check whether X lies inside the triangle """
        area = self.area()
        area1 = Triangle(X,self.B,self.C).area()
        area2 = Triangle(self.A,X,self.C).area()
        area3 = Triangle(self.A,self.B,X).area()
        # Check if sum of area1, area2 and area3 is same as area
        if(area == area1 + area2 + area3):
            return True
        else:
            return False

    def findPosibleIntegerTriangles(self):
        """ Find posible triangles moving vertices to next integer values (512 posible triangles) """
        posibleA = posiblePoints(self.A)
        posibleB = posiblePoints(self.B)
        posibleC = posiblePoints(self.C)
        listPosibleVertices =  list(product(*[posibleA, posibleB, posibleC]))
        posibleTriangles = [Triangle(A,B,C) for A,B,C in listPosibleVertices ]
        return posibleTriangles

    def findPosibleIncludingIntegerTriangles(self):
        """ Find those posible triangles that include the original triangle """
        posibleIncludingTriangles = list()
        posibleTriangles = self.findPosibleIntegerTriangles()
        for triangle in posibleTriangles:
            if self.isTriangleIncluded(triangle):
                posibleIncludingTriangles.append(triangle)
        return posibleIncludingTriangles

    def isTriangleIncluded(self,triangle):
        """ Checks if it's included in the triangle selected (DEF) """
        DNotInside = not self.isInside(triangle.A)
        ENotInside = not self.isInside(triangle.B)
        FNotInside = not self.isInside(triangle.C)
        D = triangle.A
        E = triangle.B
        F = triangle.C
        ABdoNotIntersectDE = not segmentsIntersect(self.A,self.B,D,E)
        ABdoNotIntersectEF = not segmentsIntersect(self.A,self.B,E,F)
        ABdoNotIntersectFD = not segmentsIntersect(self.A,self.B,F,D)
        BCdoNotIntersectDE = not segmentsIntersect(self.B,self.C,D,E)
        BCdoNotIntersectEF = not segmentsIntersect(self.B,self.C,E,F)
        BCdoNotIntersectFD = not segmentsIntersect(self.B,self.C,F,D)
        CAdoNotIntersectDE = not segmentsIntersect(self.C,self.A,D,E)
        CAdoNotIntersectEF = not segmentsIntersect(self.C,self.A,E,F)
        CAdoNotIntersectFD = not segmentsIntersect(self.C,self.A,F,D)
        return DNotInside and ENotInside and FNotInside and \
                ABdoNotIntersectDE and ABdoNotIntersectEF and ABdoNotIntersectFD and \
                BCdoNotIntersectDE and BCdoNotIntersectEF and BCdoNotIntersectFD and \
                CAdoNotIntersectDE and CAdoNotIntersectEF and CAdoNotIntersectFD

    def findMinimumIntegerTriangle(self):
        """ Find the minimum triangle enlarged by integer values that includes the original triangle """
        minimumIntegerTriangle = None
        area = None
        for triangle in self.findPosibleIncludingIntegerTriangles():
            if area == None:
                area = triangle.area()
                minimumIntegerTriangle = triangle
            else:
                if triangle.area() < area:
                    area = triangle.area
                    minimumIntegerTriangle = triangle

        return minimumIntegerTriangle

    def plot(self, color = "blue"):
        pol = plt.Polygon(np.array(self.vertices),color = color)
        plt.gca().add_patch(pol)
        plt.autoscale()

def posiblePoints(P):
    """ Determine posible points to move to """
    posibleX = range(P[0]-1,P[0]+2)
    posibleY = range(P[1]-1,P[1]+2)
    posibleP = []
    for x in posibleX:
        for y in posibleY:
            if x!=P[0] or y!=P[1]:
                posibleP.append((x,y))
    return posibleP

def segmentsIntersect(A,B,C,D):
    """ Check if segment AB instersects with CD """
    def ccw(A,B,C):
        return (C[1]-A[1]) * (B[0]-A[0]) > (B[1]-A[1]) * (C[0]-A[0])
    return ccw(A,C,D) != ccw(B,C,D) and ccw(A,B,C) != ccw(A,B,D)

# Select the vertices of original triangle
A = (-2,0)
B = (4,0)
C = (-1,3)

# Define the triangle
T = Triangle(A,B,C)

# Find the minimum triangle enlarged by integer values that includes the original triangle and print it
Tmin = T.findMinimumIntegerTriangle()
print Tmin

# Plot the found minimum triangle and the original one
Tmin.plot("red")
T.plot("blue")
plt.axis('equal')
plt.grid()
plt.show()

代码有一个Triangle 类。 Triangle 对象由三个顶点定义,它们是元组,并定义了函数area 用于计算三角形面积,isInside 用于检查点是否在三角形内,findPosibleIntegerTriangles 用于查找可能的三角形,isTriangleIncluded检查它是否包含在某个三角形中,findMinimumIntegerTriangle 会找到它的“最小三角形”(如果有的话)。

要找到“最小三角形”,它会查找所有可能的“整数三角形”,这些“整数三角形”是通过将每个方向上的三个顶点移动一个单位(总共 512 种可能性)而产生的。然后从这些可能的三角形中检查顶点是否在原始三角形之外,并确保它的任何边都不与原始三角形的任何线段相交(如果两个条件都为真,则三角形将包括原始三角形)。最后从那些可能的“整数三角形”中找出面积最小的。

程序输出“最小三角形”的顶点坐标(通过打印作为解找到的Triangle 对象Tmin)并绘制“最小三角形”和原始三角形。

输出是:

>>> 
Triangle: [(-3, -1), (5, 0), (-1, 4)]

【讨论】:

  • 请注意,有些三角形没有对应的“最小三角形”。例如三角形[(-2, 2), (4, 0), (-1, 3)]。不确定他们应该具备哪种条件才能确保有解决方案。
  • 我还怀疑,随着三角形的“纵横比”更加严重,您可能需要查看的不仅仅是最近的整数点。例如,考虑 [(0,0), (999999999999999,2), (999999999999999,1)] ...在长轴附近看 2 个整数步长的微小长度扩展对总面积。
  • @lockcmpxchg8b,只是为了说明,对于你提到的三角形,我从我发布的程序中得到的“最小三角形”是:Triangle: [(1, 1), (1000000000000000L, 3), (999999999999998L, 2)]
  • @CedricCoppolo 呵呵。我实际上并没有想出一个反例,我只是想说明一般的想法。对于一个充分畸形的三角形,“最近的整数”可能不如“与最长轴的精确对齐”重要
【解决方案3】:

此答案适用于您想均匀缩放三角形。


让我们平移三角形,使顶点A(ax,ay) 变为原点(0,0)
现在平移的顶点是B'(bx-ax,by-ay)=(ux,uy)C'=(cx-ax,cy-ay)=(vx,vy)

当比例因子F 应用于坐标x'= x·F 时,我们有一个增量d= x'-x。所以我们也可以说x'= x+dF= x'/x = (x+d)/x = 1 + d/x

让我们对翻译后的B'C' 应用相同的比例:

F= 1 + d1/ux
F= 1 + d2/uy
F= 1 + d3/vx
F= 1 + d4/vy

因为F对于我们拥有的四个等式是一样的:

d2/uy = d1/ux   ==>>   d2·ux = d1·uy  ==>> d2·ux - d1·uy = 0
d3/vx = d1/ux   ==>>   d3·ux = d1·vx  ==>> d3·ux - d1·vx = 0
d4/vy = d1/ux   ==>>   d4·ux = d1·vy  ==>> d4·ux - d1·vy = 0

ax+by=0 形式的任何方程都有(-b,a) 解。所以:

d1 = ux
d2 = uy
d3 = vx
d4 = vy

如果ux,uy,vx,vy 有一个最大公约数g,那么我们找到最小的比例因子:
从第一个相等:

ux = ux'·g   ==>> g= ux/ux'
uy = uy'·g
d2·ux'·g - d1·uy'·g = 0   ==>>   d2·ux' - d1·uy' = 0

这意味着d1 = ux'也是一个解决方案。

比例因子为:

F= (ux + d1)/ux = 1 + ux'/ux  =  1 + 1/g

翻译回我们拥有的坐标,例如顶点B

b_scaled_x = (bx-ax) · F + ax
b_scaled_y = (by-ay) · F + ay

请注意,所有坐标和增量di 和最大公约数g 都是整数值。比例因子F 是一个有理数。因此,必须在“浮点数”而不是“整数”上进行微积分。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-06-30
    • 1970-01-01
    • 2014-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-19
    相关资源
    最近更新 更多