我试图找到旋转多边形以使特定侧面完全水平且在底部的角度。

例如这样的形状:


需要旋转,以便带有红色正方形的一侧在底部并且完全水平,例如:



到目前为止,我已经尝试了几种方法,但最终都遇到了角度不正确的奇怪边缘情况。

评论

欢迎使用计算机图形堆栈交换。您可以通过添加有关多边形和红色框的哪些信息来改善您的问题。你例如知道顶点的精确坐标,还是只有图像?另外,请描述您已经尝试执行的操作。这些信息将帮助我们写出好的答案,尤其是可以帮助您最好的答案。您可以在帮助中心的“如何提问”页面上找到更多提示。

#1 楼

推导角度并旋转该角度在2D中效果很好(请参见TLousky的文章)。这种策略不能很好地扩展到三维领域。我将提供一个替代解决方案,该解决方案显示适用于更多情况的通用策略。作为奖励,这无需考虑三角函数即可编码。


首先在参考空间中确定要转换为方向的向量。让我们将此向量称为$ \ vec {a} $或沿向量。

接下来,您将需要确定一个垂直于该向量的向量。让我们将此向量称为\\ vec {b} $,有时也称为上向量。在三维中,您需要3个向量,但是一旦确定了2个,就可以计算第三个向量。向量乘以$ 90 ^ {\ circ} $或简单地交换坐标并使x坐标为负(请参见1)。另外,您可以使用与3D中相同的过程。 {bmatrix}
\ cos(\ pi / 2)和-\ sin(\ pi / 2)\\
\ sin(\ pi / 2)和\ cos(\ pi / 2)
\ end {bmatrix}
\ begin {bmatrix}
x \\
y
\ end {bmatrix}
=
\ begin {bmatrix }
-y \\
x
\ end {bmatrix}
\ tag {1} $$

在3-D中,这通常需要一个招。由于每个三角形都是平坦的,因此可以利用这一优点。这给了我们一个垂直方向,现在我们仍然需要第三个方向,我们可以通过2个已知向量的叉积得到它。现在,您有3个向量。 >将向量归一化
使用这些向量可生成旋转矩阵(或仿射矩阵,您可以根据自己的意愿将点指定为零)。该矩阵表示旋转对象的局部坐标。
现在只需要反转此矩阵并将点矩阵乘以多边形角点矢量即可得到多边形的对齐版本。现在我看不到定义基本矩阵运算的任何要点,但是我看到了代码中的价值。因此,我将使用numpy。代码,并消除了多余的操作,但仅是一个演示,因此...


图像2:多边形,蓝色轴固定为绿色。

对于矫正,倾斜和其他问题(例如透视校正),同样的方法效果很好。您可以将其扩展为仿射矩阵,这样您就可以仅再多1行就将转换编码到计算中。本质上,您可以指定原点,然后再进行2次操作就可以定义旋转的原点。现在它只是绕原点旋转。

#2 楼

该算法基于此答案以找到向量之间的角度,并且此答案基于旋转多边形点。它是用Python编写的,并且假定您要使边缘与X轴(水平轴)对齐。如果要将其与Y轴对齐,请将下面代码中的xVec替换为yVec = [0,1]

编辑:添加了用于旋转多边形以及显示原始多边形和旋转多边形的代码。该脚本的结果显示在下图中:



 from PIL import Image, ImageDraw
from matplotlib import pyplot as plt
from math import radians, degrees, sqrt, acos, cos, sin

def dotproduct(v1, v2):
  return sum( (a*b) for a, b in zip(v1, v2) )

def length(v):
  return sqrt( dotproduct(v, v) )

def angle(v1, v2):
  return acos( dotproduct(v1, v2) / ( length(v1) * length(v2) ) )

def vec_subtraction( v1, v2 ):
    return [ e1 - e2 for e1, e2 in zip( v1, v2 ) ]

def calc_edge_vec( poly, edge ):
    p1 = poly['points'][ edge[0] ]
    p2 = poly['points'][ edge[1] ]

    return vec_subtraction( p2, p1 )

def rotatePolygon( poly, theta ):
    ''' Rotates the given polygon which consists of corners represented
        as (x,y), around the ORIGIN, clock-wise, theta degrees
    '''

    rotatedPolygon = []
    for corner in poly:
        rotatedPolygon.append((
            corner[0] * cos( theta ) - corner[1] * sin( theta ), # x
            corner[0] * sin( theta ) + corner[1] * cos( theta )  # y
        ))

    # Make sure rotated polygon coordinates are within image boundaries
    minX = min( [ c[0] for c in rotatedPolygon ] ) * -1
    minX = minX if minX > 0 else 0
    minY = min( [ c[1] for c in rotatedPolygon ] ) * -1
    minY = minY if minY > 0 else 0

    rotatedPolygon = [ ( x + minX, y + minY ) for x, y in rotatedPolygon ]

    return rotatedPolygon

def draw_and_plot_polygon( im, poly, color, plotTitle, plotIndex ):
    draw = ImageDraw.Draw( im )

    for e in poly['edges']:
        p1 = tuple( poly['points'][ e[0] ] )
        p2 = tuple( poly['points'][ e[1] ] )
        draw.line( [p1, p2], fill = color )

    del draw

    plt.subplot( 3, 2, plotIndex ),
    plt.imshow( im ),
    plt.title( plotTitle ),
    plt.xticks([]), plt.yticks([])

    # Draw point names (ABCD)
    for i, c in enumerate( poly['points'] ):
        plt.text( c[0], c[1], "ABCD"[i], color = 'white' )

# Define polygon as dictionary of point coordinates and edges
# each edge is a pair of point indices
p = {
    'points' : [ (50,10), (95,30), (80,20), (62,48) ],
    'edges'  : [ (3,0), (3,1), (1,2), (2,0) ]
}

xVec = [1,0] # Horizontal axis

# Draw original poly in white
im = Image.new( 'RGB', (100,100) )
draw_and_plot_polygon( im, p, (255,255,255), 'Original', 1 )

colors = [ (50,50,255), (255,0,0), (0,255,0), (200, 150, 50) ]

i = 2
for e, color in zip( p['edges'], colors ):
    edgeVec = calc_edge_vec( p, e )
    a       = angle( edgeVec, xVec )

    rotPol = p.copy()
    rotPol['points'] = rotatePolygon( p['points'], a )

    im = Image.new( 'RGB', (100,100) )

    # Place aligned edge at bottom
    alingedEdgeY = im.size[1] - rotPol['points'][ e[0] ][ 1 ] # Current edge height
    rotPol['points'] = [ ( p[0], p[1] + alingedEdgeY ) for p in rotPol['points'] ]

    # Draw rotated polygon
    angleTitle = "Angle: " + str( round( degrees(a), 2 ) )
    draw_and_plot_polygon( im, rotPol, color, angleTitle, i )

    i += 1

plt.show()
 


编辑2:添加了一些代码来定位旋转的多边形,以使对齐的边缘位于地板上(第90-91行)。结果如下:


评论


$ \ begingroup $
请注意,该问题还要求指定的边在底部。
$ \ endgroup $
– trichoplax
16年1月2日在23:40

$ \ begingroup $
@trichoplax,对。添加了那段代码。
$ \ endgroup $
–TLousky
16年1月3日,下午5:34

$ \ begingroup $
从问题的示例来看,我认为“在底部”是指水平边缘成为形状的基础,而不是将其移动到屏幕的底部。
$ \ endgroup $
– trichoplax
16年1月3日,14:14

$ \ begingroup $
例如,您的侧面AD位于图形的顶部,因此即使将其移动到屏幕底部,图形的其余部分仍位于屏幕下方,位于屏幕下方。
$ \ endgroup $
– trichoplax
16年1月3日,14:15