[Rod Stephens Books]
Index Books Python Examples About Rod Contact
[Mastodon] [Bluesky] [Facebook]
[Build Your Own Python Action Arcade!]

[Build Your Own Ray Tracer With Python]

[Beginning Database Design Solutions, Second Edition]

[Beginning Software Engineering, Second Edition]

[Essential Algorithms, Second Edition]

[The Modern C# Challenge]

[WPF 3d, Three-Dimensional Graphics with WPF and C#]

[The C# Helper Top 100]

[Interview Puzzles Dissected]

Title: Draw a polygon while snapping its vertices to a grid in Python

[This program snaps polygon vertices to a grid]

My example Calculate the area of a polygon in Python lets you click to draw a polygon. This example is similar except it snaps the polygon's vertices to a grid (and it doesn't calculate polygon areas). It only requires a few small changes.

Snapping to the Grid

The program uses two methods to track mouse movement. The mouse_down event handler fires when you click the mouse down to add a new point to the polygon. The mouse_move event handler executes when you move the mouse while you're in the process of drawing a polygon.

The code only needs one small change in those methods to handle the grid. Before it uses the mouse's current position, the code calls the following snap_point function to adjust the point to the nearest grid location.

def snap_point(x, y, row_hgt, col_wid): '''Snap the point to the grid.''' col = round(x / col_wid) row = round(y / row_hgt) return col * col_wid, row * row_hgt

This code divides the X coordinate by the grid's column width and rounds the result to get an integer. It then multiples the resulting column number by the column width to get the X coordinate of the nearest grid point. It performs similar steps to get the nearest grid Y coordinate and returns the adjusted point.

The following code shows how the mouse_move event handler uses snap_point (highlighted in blue).

def mouse_move(self, event): '''Update the last point in the selection polyline.''' # See if we're drawing. if self.new_points is None: return # Update the last point. x, y = snap_point(event.x, event.y, self.row_hgt, self.col_wid) self.new_points[-1] = (x, y) self.canvas.coords(self.new_polyline, self.new_points)

If we're not currently drawing, the method just returns. Otherwise, it passes the mouse's coordinates into snap_point and then updates the last point in the growing polygon to the new location. (Give the program a try and you'll see how it works.)

The mouse_down method is longer because it has more tasks to perform, but the snapping code is similar. It calls snap_point to adjust the mouse's current position and then does all of its work with the adjusted point.

Removing Duplicates

The other major change to the example is also in the mouse_down event handler. When you right-click, the method uses the following code to close the polygon.

elif event.num == 3: # Right mouse button. # See if we're drawing. if self.new_points is None: return # Delete the selection polyline. self.canvas.delete(self.new_polyline) # Delete the final point. self.new_points.pop() # Remove adjacent duplicates. no_dups = [] num_points = len(self.new_points) for i in range(num_points): j = (i + 1) % num_points if self.new_points[i] != self.new_points[j]: no_dups.append(self.new_points[i]) self.new_points = no_dups # See if we have at least three points. if len(self.new_points) >= 3: # Create the final polygon. self.canvas.create_polygon(self.new_points, outline='blue', fill='') # We are no longer drawing. self.new_points = self.new_polyline = None

First, if we're not drawing a polygon, the code simply returns. Otherwise, it deletes the polyline from the program's Canvas widget and removes the final point from the new_points list. That point showed where you could have put another segment if you had left-clicked. The program removes it and connects the last two points that you did click to close the polygon. (Again, give it a try and you'll see how it works.)

Because the points are being snapped to the grid, there's a much better chance that two adjacent points will be the same than in the previous example. This happens if you click the same point twice in a row or if you make the polygon's last point the same as its first point.

The blue code removes adjacent duplicates. To do that, it creates a new no_dups list and then loops through new_points. If a point is different from the one that follows it, the code adds it to no_dups.

After that, the code continues as before. If there are at least three points, the code draws the polygon.

The method finishes by setting new_points and new_polyline to None so we remember that we are no longer drawing a polygon.

Conclusion

Snapping vertices to a grid makes it easier to align multiple shapes. (It will also be helpful for a later example I plan to post about Pick's Theorem.)

Download the example to experiment with it and to see additional details.

© 2025 - 2026 Rocky Mountain Computer Consulting, Inc. All rights reserved.