Skip to main content
replaced http://stackoverflow.com/ with https://stackoverflow.com/
Source Link

This algorithm is based on this answer for finding the angle between vectorsthis answer for finding the angle between vectors, and this answer for rotating polygon pointsthis answer for rotating polygon points. It's written in Python, and assumes you want to align an edge with the X axis (horizontal axis). If you want to align it with the Y axis, replace the xVec in the code below with a yVec = [0,1].

This algorithm is based on this answer for finding the angle between vectors, and this answer for rotating polygon points. It's written in Python, and assumes you want to align an edge with the X axis (horizontal axis). If you want to align it with the Y axis, replace the xVec in the code below with a yVec = [0,1].

This algorithm is based on this answer for finding the angle between vectors, and this answer for rotating polygon points. It's written in Python, and assumes you want to align an edge with the X axis (horizontal axis). If you want to align it with the Y axis, replace the xVec in the code below with a yVec = [0,1].

Added code that positions aligned edge at bottom
Source Link
TLousky
  • 199
  • 1
  • 4
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 )

    # Draw rotated polygon
    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()

EDITED 2: Added some code to position the rotated polygon so that the aligned edge is on the floor (lines 90-91). Here's the result: enter image description here

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 )

    # Draw rotated polygon
    im = Image.new( 'RGB', (100,100) )
    angleTitle = "Angle: " + str( round( degrees(a), 2 ) )
    draw_and_plot_polygon( im, rotPol, color, angleTitle, i )

    i += 1

plt.show()
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()

EDITED 2: Added some code to position the rotated polygon so that the aligned edge is on the floor (lines 90-91). Here's the result: enter image description here

Refactoring for readability
Source Link
TLousky
  • 199
  • 1
  • 4

This algorithm is based on this answer for finding the angle between vectors, and this answer for rotating polygon points. It's written in Python, and assumes you want to align an edge with the X axis (horizontal axis). If you want to align it with the Y axis, replace the xVec in the code below with ana 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( polygonpoly, theta ):
    ''' Rotates the given polygon which consists of corners represented
        as (x,y), around the ORIGIN, clock-wise, theta degrees
    '''
    #theta = radians( theta )
    rotatedPolygon = []
    for corner in polygonpoly:
        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_polygondraw_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_polygondraw_and_plot_polygon( im, p, (255,255,255) )

plt.subplot( 3, 2, 1), plt.imshow( im ), plt.title( 'Original' ), plt.xticks([]), plt.yticks([])

# Draw point names (ABCD)
for i, c in enumerate( p['points'] ):
    plt.text( c[0], c[1], "ABCD"[i], color = 'white'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 )

    # Draw rotated polygon
    im = Image.new( 'RGB', (100,100) )
    draw_polygon( im, rotPol, color )
    plt.subplot( 3, 2, i ), plt.imshow( im )
    angleTitle = "Angle: " + str( round( degrees(a), 2 ) )
    plt.title( angleTitle ), plt.xticks([]), plt.yticks([])
 
    # Draw point names draw_and_plot_polygon(ABCD)
    for idxim, c in enumerate( rotPol['points'] ):
        plt.text( c[0]rotPol, c[1]color, "ABCD"[idx]angleTitle, color = 'white'i )

    i += 1

plt.show()

This algorithm is based on this answer for finding the angle between vectors, and this answer for rotating polygon points. It's written in Python, and assumes you want to align an edge with the X axis (horizontal axis). If you want to align it with the Y axis, replace the xVec in the code below with an 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( polygon, theta ):
    ''' Rotates the given polygon which consists of corners represented
        as (x,y), around the ORIGIN, clock-wise, theta degrees
    '''
    #theta = radians( theta )
    rotatedPolygon = []
    for corner in polygon:
        rotatedPolygon.append((
            corner[0] * cos( theta ) - corner[1] * sin( theta ), # x
            corner[0] * sin( theta ) + corner[1] * cos( theta )  # y
        ))

    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_polygon( im, poly, color ):
    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

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_polygon( im, p, (255,255,255) )

plt.subplot( 3, 2, 1), plt.imshow( im ), plt.title( 'Original' ), plt.xticks([]), plt.yticks([])

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

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 )

    # Draw rotated polygon
    im = Image.new( 'RGB', (100,100) )
    draw_polygon( im, rotPol, color )
    plt.subplot( 3, 2, i ), plt.imshow( im )
    angleTitle = "Angle: " + str( round( degrees(a), 2 ) )
    plt.title( angleTitle ), plt.xticks([]), plt.yticks([])
 
    # Draw point names (ABCD)
    for idx, c in enumerate( rotPol['points'] ):
        plt.text( c[0], c[1], "ABCD"[idx], color = 'white' )

    i += 1

plt.show()

This algorithm is based on this answer for finding the angle between vectors, and this answer for rotating polygon points. It's written in Python, and assumes you want to align an edge with the X axis (horizontal axis). If you want to align it with the Y axis, replace the xVec in the code below with a 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 )

    # Draw rotated polygon
    im = Image.new( 'RGB', (100,100) )
    angleTitle = "Angle: " + str( round( degrees(a), 2 ) )
    draw_and_plot_polygon( im, rotPol, color, angleTitle, i )

    i += 1

plt.show()
A bit more organized
Source Link
TLousky
  • 199
  • 1
  • 4
Loading
Fixed minor bug in edge point representation, improved polygon display
Source Link
TLousky
  • 199
  • 1
  • 4
Loading
Added code for rotating the polygon and displaying results
Source Link
TLousky
  • 199
  • 1
  • 4
Loading
Using same varnames for consistency
Source Link
TLousky
  • 199
  • 1
  • 4
Loading
Source Link
TLousky
  • 199
  • 1
  • 4
Loading