39

I have input values of x, y coordinates in the following format:

[[1,1], [2,1], [2,2], [1,2], [0.5,1.5]]

I want to draw polygons, but I don't know how to draw them!

Thanks

Reblochon Masque
  • 35,405
  • 10
  • 55
  • 80
W.Fan
  • 407
  • 1
  • 4
  • 3

11 Answers11

54

Using matplotlib.pyplot

import matplotlib.pyplot as plt

coord = [[1,1], [2,1], [2,2], [1,2], [0.5,1.5]]
coord.append(coord[0]) #repeat the first point to create a 'closed loop'

xs, ys = zip(*coord) #create lists of x and y values

plt.figure()
plt.plot(xs,ys) 
plt.show() # if you need...
Julien
  • 13,986
  • 5
  • 29
  • 53
  • 2
    Maybe you add plt.show() to your code. Otherwise someone who just copy-paste it wouldn't see anything – Edmundo Del Gusto Jun 22 '18 at 09:30
  • Done. I use Spyder and `plt.show()` is not necessary, so I don't think about it... – Julien Jun 23 '18 at 09:51
  • 1
    If the polygon is a rectangle, it's a little easier/faster to add using `matplotlib.patches` as described [here](https://stackoverflow.com/questions/37435369/matplotlib-how-to-draw-a-rectangle-on-image) – Addison Klinke May 24 '19 at 18:13
  • Strictly speaking zip does not creates lists. It creates tuples. This is ok in the present application because pyplot is rather robust. – Chris Judge Sep 13 '22 at 22:17
18

Another way to draw a polygon is this:

import PIL.ImageDraw as ImageDraw
import PIL.Image as Image

image = Image.new("RGB", (640, 480))

draw = ImageDraw.Draw(image)

# points = ((1,1), (2,1), (2,2), (1,2), (0.5,1.5))
points = ((100, 100), (200, 100), (200, 200), (100, 200), (50, 150))
draw.polygon((points), fill=200)

image.show()

Note that you need to install the pillow library. Also, I scaled up your coordinates by the factor of 100 so that we can see the polygon on the 640 x 480 screen.

Hope this helps.

Nurjan
  • 5,889
  • 5
  • 34
  • 54
17
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon

y = np.array([[1,1], [2,1], [2,2], [1,2], [0.5,1.5]])

p = Polygon(y, facecolor = 'k')

fig,ax = plt.subplots()

ax.add_patch(p)
ax.set_xlim([0,3])
ax.set_ylim([0,3])
plt.show()
10

matplotlib.patches has a function called Polygon, it can be imported as from matplotlib.patches import Polygon. You can use the add_patch method of the axis object to plot the polygon.

from matplotlib.patches import Polygon
import matplotlib.pyplot as plt

polygon1 = Polygon([(0,5), (1,1), (3,0),])

fig, ax = plt.subplots(1,1)

ax.add_patch(polygon1)

plt.ylim(0,6)
plt.xlim(0,6)

enter image description here

Shrish
  • 109
  • 1
  • 4
  • Thanks, that seems very clean. But it is sad that the xlim and ylim are set independently, rather than from the existing data. I see discussion of bounding boxes in the documentation, but it isn't clear how to cleanly extract the bounding box to draw the figure (without manually iterating thru the points). Or, even better, how to combine bounding boxes for multiple polygons. – nealmcb Apr 18 '23 at 22:38
6

Also, if you're drawing on window, use this:

dots = [[1,1], [2,1], [2,2], [1,2], [0.5,1.5]]
from tkinter import Canvas
c = Canvas(width=750, height=750)
c.pack()
out = []
for x,y in dots:
    out += [x*250, y*250]
c.create_polygon(*out, fill='#aaffff')#fill with any color html or name you want, like fill='blue'
c.update()

or also you may use this:

dots = [[1,1], [2,1], [2,2], [1,2], [0.5,1.5]]
out = []
for x,y in dots:
    out.append([x*250, y*250])
import pygame, sys
from pygame.locals import *
pygame.init()
DISPLAYSURF = pygame.display.set_mode((750, 750), 0, 32)
pygame.display.set_caption('WindowName')
DISPLAYSURF.fill((255,255,255))#< ; \/ - colours
pygame.draw.polygon(DISPLAYSURF, (0, 255,0), out)
while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
    pygame.display.update()

First needs tkinter, second - pygame. First loads faster, second draws faster, if you put DISPLAYSURF.fill and than pygame.draw.polygon with a bit different coordinates into loop, it will work better than the same thing in tkinter. So if your polygon is flying and bouncing around, use second, but if it's just stable thing, use first. Also, in python2 use from Tkinter, not from tkinter. I've checked this code on raspberrypi3, it works.

------------EDIT------------

A little bit more about PIL and PYPLOT methods, see another answers:

matplotlib uses tkinter, maybe matplotlib is easier-to-use, but it's basically cooler tkinter window.

PIL in this case uses imagemagick, which is really good image editing tool

If you also need to apply effects on image, use PIL.

If you need more difficult math-figures, use matplotlib.pyplot.

For animation, use pygame.

For everything you don't know any better way to do something, use tkinter.

tkinter init is fast. pygame updates are fast. pyplot is just a geometry tool.

rizerphe
  • 1,340
  • 1
  • 14
  • 24
4

All the other answers seems veryhigh level, I guess that is my impression as a mechanical engineer. Here's a simple version of the code:

from numpy import *
from matplotlib.pyplot import *
x = ([1,2,2,1,0.5,1]) 
y = ([1,1,2,2,1.5,1])
plot(x,y)
show()
Nino Filiu
  • 16,660
  • 11
  • 54
  • 84
idiot
  • 127
  • 1
  • 3
  • 6
    Don't use wild card imports. It's not recommended, and you can see why. For a beginner like me, I have to google whether `plot` belongs to `numpy` or `matplotlib.pyplot`. Same with show. – Safwan Samsudeen Nov 16 '20 at 06:29
4

If you want to draw polygons on a matrix representing an image, scikit-image has 3 functions for you:

Correct me if your benchmark said the contrary, but I think these functions are quite fast.

Example

import numpy as np
from skimage.draw import polygon2mask, polygon, polygon_perimeter

shape = (10, 10)  # image shape
points = [(5, -1), (-1, 5), (5, 11), (10, 5)]  # polygon points

imgp2 = polygon2mask(shape, points).astype(str)  # astype() converts bools to strings
imgp2[imgp2 == "True"] = "O"
imgp2[imgp2 == "False"] = "."

imgp = np.full(shape, ".")  # fill a n*d matrix with '.'
imgpp = imgp.copy()
points = np.transpose(points)  # change format to ([5, -1, 5, 10], [-1, 5, 11, 5])

rr, cc = polygon(*points, shape=shape)
imgp[rr, cc] = "O"

rr, cc = polygon_perimeter(*points, shape=shape, clip=True)
imgpp[rr, cc] = "O"

print(imgp2, imgp, imgpp, sep="\n\n")

Result:

[['.' '.' '.' '.' 'O' 'O' '.' '.' '.' '.']
 ['.' '.' '.' 'O' 'O' 'O' 'O' '.' '.' '.']
 ['.' '.' 'O' 'O' 'O' 'O' 'O' 'O' '.' '.']
 ['.' 'O' 'O' 'O' 'O' 'O' 'O' 'O' 'O' '.']
 ['O' 'O' 'O' 'O' 'O' 'O' 'O' 'O' 'O' 'O']
 ['O' 'O' 'O' 'O' 'O' 'O' 'O' 'O' 'O' 'O']
 ['.' 'O' 'O' 'O' 'O' 'O' 'O' 'O' 'O' 'O']
 ['.' '.' 'O' 'O' 'O' 'O' 'O' 'O' 'O' '.']
 ['.' '.' '.' 'O' 'O' 'O' 'O' 'O' '.' '.']
 ['.' '.' '.' '.' 'O' 'O' 'O' '.' '.' '.']]

[['.' '.' '.' '.' 'O' 'O' '.' '.' '.' '.']
 ['.' '.' '.' 'O' 'O' 'O' 'O' '.' '.' '.']
 ['.' '.' 'O' 'O' 'O' 'O' 'O' 'O' '.' '.']
 ['.' 'O' 'O' 'O' 'O' 'O' 'O' 'O' 'O' '.']
 ['O' 'O' 'O' 'O' 'O' 'O' 'O' 'O' 'O' 'O']
 ['O' 'O' 'O' 'O' 'O' 'O' 'O' 'O' 'O' 'O']
 ['.' 'O' 'O' 'O' 'O' 'O' 'O' 'O' 'O' 'O']
 ['.' '.' 'O' 'O' 'O' 'O' 'O' 'O' 'O' '.']
 ['.' '.' '.' 'O' 'O' 'O' 'O' 'O' '.' '.']
 ['.' '.' '.' '.' 'O' 'O' 'O' '.' '.' '.']]

[['.' '.' '.' '.' 'O' 'O' 'O' '.' '.' '.']
 ['.' '.' '.' 'O' '.' '.' '.' 'O' '.' '.']
 ['.' '.' 'O' '.' '.' '.' '.' '.' 'O' '.']
 ['.' 'O' '.' '.' '.' '.' '.' '.' '.' 'O']
 ['O' '.' '.' '.' '.' '.' '.' '.' '.' 'O']
 ['O' '.' '.' '.' '.' '.' '.' '.' '.' 'O']
 ['O' '.' '.' '.' '.' '.' '.' '.' '.' 'O']
 ['.' 'O' 'O' '.' '.' '.' '.' '.' '.' 'O']
 ['.' '.' '.' 'O' '.' '.' '.' 'O' 'O' '.']
 ['.' '.' '.' '.' 'O' 'O' 'O' '.' '.' '.']]
Alexandre Huat
  • 806
  • 10
  • 16
3

Here's a one-liner (assuming you have matplotlib.pyplot imported and your polygon defined – if not then it's three lines^^):

import matplotlib.pyplot as plt
poly = [[1,1], [2,1], [2,2], [1,2], [0.5,1.5]]

plt.plot(*np.column_stack(poly+[poly[0]]));
2

tkinter canvas is very powerful, and allows to easily draw a variety of polygons with many built in properties that can be manipulated:

example (screenshot):

enter image description here

code for the example:

import tkinter as tk


def _scale_and_flip(point, offset):
    """
    scales the provided point and flips the y axis so it points upwards
    origin (0, 0) at the bottom left corner of the screen
    returns the point scaled and flipped
    """
    x, y = point
    ox, oy = offset
    return ((x+ox) * SCALE, HEIGHT - (y+oy) * SCALE)

def scale_and_flip(polygon, offset=(0, 0)):
    """
    scales the provided point and flips the y axis so it points upwards
    origin (0, 0) at the bottom left corner of the screen
    returns a sequence of scaled and flipped points representing the polygon ready to render
    """
    return [_scale_and_flip(point, offset) for point in polygon]


if __name__ == '__main__':
    WIDTH, HEIGHT = 500, 500
    SCALE = 100
    polygon_points = [[1, 1], [2, 1], [2, 2], [1, 2], [0.5, 1.5]]

    root = tk.Tk()
    canvas = tk.Canvas(root, width=WIDTH, height=HEIGHT, bg='cyan')
    canvas.pack()

    canvas.create_polygon(scale_and_flip(polygon_points), fill='beige', outline='black')
    canvas.create_polygon(scale_and_flip(polygon_points, (0, 2)), fill='beige', outline='black', smooth=True)
    canvas.create_polygon(scale_and_flip(polygon_points, (2, 0)), fill='beige', outline='black', dash=(1, 3))
    canvas.create_polygon(scale_and_flip(polygon_points, (2, 2)), fill='beige', outline='black', dash=(1, 3), smooth=True)

    root.mainloop()

More tk.Canvas.create_polygon options and properties can be found here

Reblochon Masque
  • 35,405
  • 10
  • 55
  • 80
1

Even though there have been a lot of answers, here is my approach using the Turtle module. The turtle module provides turtle graphics primitives in both object-oriented and procedure-oriented ways.

import turtle
  

t = turtle.Turtle()
  
# input for number of sides 
n_sides = int(input("Enter the sides of the polygon : "))
  
# length of the polygon
l_sides = int(input("Enter the length of the polygon : "))
  
  
for _ in range(n_sides):
    turtle.fd(l_sides)
    turtle.rt(360 / n_sides)
Kofi
  • 1,224
  • 1
  • 10
  • 21
0

This can do the trick. Scipy has some interesting solutions.

from scipy.spatial import ConvexHull

points = np.array([[36.129858, 35.908697],[36.130999, 35.913982],[36.127573, 35.913875],[36.127180, 35.919735],[36.122773, 35.924147],[36.121684, 35.920957],[36.125561, 35.916850],[36.124430, 35.914520]]) 

hull = ConvexHull(points)
        convexlist = hull.simplices
for simplex in hull.simplices:
            plt.plot(points[simplex, 0], points[simplex, 1], 'k-')
plt.show()
Max
  • 3
  • 2
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community May 25 '23 at 02:23