惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tenable Blog
博客园_首页
Vercel News
Vercel News
WordPress大学
WordPress大学
美团技术团队
G
Google Developers Blog
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
Y
Y Combinator Blog
博客园 - 【当耐特】
量子位
酷 壳 – CoolShell
酷 壳 – CoolShell
The Cloudflare Blog
T
The Blog of Author Tim Ferriss
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google DeepMind News
Google DeepMind News
云风的 BLOG
云风的 BLOG
腾讯CDC
M
MIT News - Artificial intelligence
爱范儿
爱范儿
Recent Announcements
Recent Announcements
雷峰网
雷峰网
Last Week in AI
Last Week in AI
宝玉的分享
宝玉的分享
The Register - Security
The Register - Security
Jina AI
Jina AI
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Hugging Face - Blog
Hugging Face - Blog
P
Privacy & Cybersecurity Law Blog
Recorded Future
Recorded Future
Help Net Security
Help Net Security
N
News and Events Feed by Topic
博客园 - Franky
P
Proofpoint News Feed
L
LINUX DO - 热门话题
S
SegmentFault 最新的问题
The GitHub Blog
The GitHub Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
月光博客
月光博客
D
Docker
Google DeepMind News
Google DeepMind News
有赞技术团队
有赞技术团队
IT之家
IT之家
Security Latest
Security Latest
L
LangChain Blog
V
V2EX
阮一峰的网络日志
阮一峰的网络日志
J
Java Code Geeks

KIRUPA | Designers and Developers Unite

Understanding Merkle Trees Is a CompSci Degree Still Valuable in the Age of AI? The Model Context Protocol (MCP) Explained Vibe Coding + Expertise = Mega Win! 🏆 Animating our Grid How to Count in Negabinary (Base (-2)) — A Visual Guide Counting in Binary and Hexadecimal Pascal Pixel on Design, Development, and Solopreneurship! Do we really need to know how things work? 🧠 Drawing Sharp Lines on the Canvas The KIRUPA Tech Stack : It Bloom Filter: A Deep Dive Hash Functions Deep Dive Advanced Glitch Effect with Sound AI Killed the Content Creator...Star 🤩 Measuring the Distance Between Two Points by using the Pythagorean Theorem Detecting Browser Zoom Changes in JavaScript Creating a Fullscreen Grid Drawing a Perfect Grid on the Canvas Preserving the Pixel Art Look in Web Content Ensuring our Canvas Looks Good on Retina/High-DPI Screens Finding Prime Numbers Using a Sieve of Eratosthenes Two-Dimensional (2D) Arrays in JavaScript Two-Dimensional (2D) Arrays in JavaScript Animations: From Biology to JavaScript! 🦠 You’ll Always be Building & Designing Creating a Cluster Growth Animation: From Biology to JavaScript Timsort: A Lightning Fast Hybrid Sorting Algorithm Merge Sort: A Simple Step-by-Step Walkthrough 😀 - YouTube Bubble Sort: A Detailed Deep-Dive 🛁 Insertion Sort: A Deep Dive! 🍣 Selection Sort: A Step-by-Step Guide 💬 Radix Sort: A Complete Guide to Fast and Efficient Sorting! ⚡️ Career Growth Secrets Counting Sort : A Friendly (yet Detailed!) Deep Dive! 🎯 Bogosort: Sorting in the Slow Lane! 🐢 Pulling Off a Successful Redesign Creating Your Own Perfect Timing Radix Sort Making Counting Sort Work with Negative Values Diving Deep into Array Index Positions The Career Three Body Problem Counting Sort Work on Problems You Deeply Care About The Importance of Finding a Career Mentor Creating a Random Walk Simulation What is Product Strategy? Thinking about an 8K Resolution Future! 📺 Creating an Animated 3D Starfield Effect Meet the Default Sorting Algorithms! Bogosort Remapping Values Getting Started with Learning Data Structures and Algorithms Tech Slowdown Explained, Part 1: Interest Rates 💸 Changing Colors in an SVG Element Using CSS and JavaScript Stability and Sorting Algorithms Creating a Scrollable DIV Area Realistic CSS Animations and the linear() Timing Function! 🍱 Visualizing Recursion with the Sierpinski Triangle Fast Sorting with Quicksort The Monty Hall Problem Stacks in JavaScript Depth-First Search (DFS) and Breadth-First Search (BFS) Introduction to the Graph Data Structure Big-O Notation and Complexity Analysis Introduction to Data Structures Arrays: A Data Structure Deep Dive Hashtables: A Deep Dive into Efficient Data Storage and Retrieval Trie (aka Prefix Tree) Embracing Generative AI with Open Arms! 🧸 Impact of AI on UI/UX Design with Chloe Barreau 🎨 Heap Data Structure Binary Search Trees Binary Tree Traversal Alphabetically Sort Names in an Array Overlapping Elements on Top of Each Other Developer Relations and Beyond with Jamie Barton! 🚀 A Trip Down Memory Lane 💾 Binary Trees Linked List The Present and Future of AI Tools with Ray (aka devbyrayray) "Guess the Number" and Binary Searching! 🔍 Switching Web Hosts in 2023 😱 SVG: Converting Shape to Path The Versatility of SVGs 🌀 Spinning Circular Text Introduction to Trees Faster Searching with Binary Search Search Algorithms and Linear Search Fibonacci and Going Beyond Recursion Guess the Number Game
Easily Draw any Polygon
Kirupa Chinnathambi · 2023-11-24 · via KIRUPA | Designers and Developers Unite

Learn how to draw any polygon, ranging from one with just a few sides to a bazillion sides, using a handy trigonometric technique involving a virtual circle.

Earlier, we looked at one approach for drawing a triangle. In this approach, we calculated each of our triangle's three points (aka vertices) using various trigonometric techniques to calculate the proper dimensions:


If we wanted to draw something other than a triangle - say a square, a pentagon, an octagon, or any n-sided polygon, we would need to special case our logic to calculate the position of each of our vertices. That's not very fun, especially given the large number of shapes we might want to draw:

Fortunately, there is a more generalized approach that we can take that allows us to draw any shape we may want. In classic MacGuyver fashion, this approach will involve just a few things: a circle, a calculator, and our good old-fashioned Canvas drawing surface. In the following sections, we'll walk through what this approach is and how to use it.

Onwards!

The Generalized Approach

If we take a step back and look at all of the shapes we may want to draw, there is a detail that is going to help us out greatly. This detail is that all of our shape's vertices can be placed on a circle. This probably makes more sense with a visual, so take a look at the following:

Can you see how each vertex in all of our n-sided polygons is positioned exactly on a circle? It's hard to unsee once you notice all of these little details, and we are going to go deeper into how to make this detail come alive.

Mapping Vertices to a Virtual Circle

To help with our explanation, we'll be looking at how to draw a pentagon:

Just like all pentagons, our pentagon has 5 sides. This means it also has 5 vertices (aka points) that exist where the sides meet:


The hardest problem is figuring out where to position our vertices. Solving for this takes a few steps.

First, we rely on a virtual circle that will help us guide where the vertices will go:

Where exactly does each vertex live on this virtual circle? This requires a little bit of math, where we calculate the vertex angle. We find this angle by dividing the number of total vertices in our shape by the total number of radians in a full circle, which is 2π. For our pentagon, the calculations will look as follows:

The angle of each vertex will be 1.256 radians. Now comes the fun part. We plot our vertices on our virtual circle where the first vertex will start at 0 radians:

From our first vertex, notice that each subsequent vertex is 1.256 radians apart. We repeat this process until we have run out of vertices and literally have come full circle. Our virtual circle isn't just important for specifying the positions of each vertex. It is also responsible for the size of the final shape, and the radius of our virtual circle determines all of that.

After all of our vertices have been plotted, our next step is to connect the vertices together with a straight line and remove all of our guidelines and extraneous markings. Once we do this, we'll be left with a version of the pentagon that we started off this entire exercise with:

If we had to summarize our steps into something that is more formulaic, the steps will look as follows:

  • Identify the number of the vertices (aka points) our shape will have. For example, a pentagon will have five vertices, an octagon will have eight vertices, and so on.
  • Pick a radius value that will determine how large our shape will be
  • Divide the number of vertices into 2π radians to calculate the angle of each vertex
  • Draw each vertex using both the angle and the radius where:
    • The first vertex will be drawn at an angle of 0 radians and at a distance specified by the radius
    • The second vertex will be drawn at the same distance using the vertex angle calculated in Step 3
    • Each subsequent vertex will continue to add on to the vertex angle calculated in the previous vertex and be drawn the same distance away
  • Connect each consecutive vertex with a straight line
  • Enjoy your newly created shape

The code implementation we'll be seeing in a few moments will closely match the steps outlined here. Before we get there, though, there is one very important detail about our polygons that we should cover.

Normal Polygons

All of the shapes we will be drawing using our generalized approach are considered to be normal. The normal descriptor here has a very specific meaning. It means that all sides and angles inside our shape are equal. This means we won't have any oddly shaped and sized polygons. For example, the following are all valid variations of a pentagon where they all have five vertices:

None of these polygons are considered normal. They all have sides of different lengths and angles of different values. With our generalized approach, we will only generate normal shapes that are all equally sized and symmetrical.

The Code

Now that we've seen our generalized approach for drawing a normal n-sided polygon shape, all that remains is turn all of this knowledge into lines of code our browser can understand.

It's Drawing Time!

Meet the drawPolygon function:

function drawPolygon(ctx, centerX, centerY, sides, radius, rotation) {
  ctx.beginPath();

  for (let i = 0; i < sides; i++) {
    // Calculate the angle between each point
    const angle = (i * 2 * Math.PI / sides) + rotation;

    // Plot the point on our circle
    const x = centerX + radius * Math.cos(angle);
    const y = centerY + radius * Math.sin(angle);

    if (i === 0) {
      ctx.moveTo(x, y);
    } else {
      ctx.lineTo(x, y);
    }
  }

  ctx.lineWidth = 5;
  ctx.strokeStyle = "#B12339";
  ctx.fillStyle = "#E75A70";

  ctx.closePath();

  ctx.fill();
  ctx.stroke();
}

The drawPolygon function takes six arguments:

  1. ctx: Our canvas object which is used to draw shapes on a canvas element.
  2. centerX: The x-coordinate of the center of the polygon.
  3. centerY: The y-coordinate of the center of the polygon.
  4. sides: The number of sides the polygon should have.
  5. radius: The radius of the polygon.
  6. rotation: The rotation of the polygon in radians.

If we had to use the drawPolygon function to draw a pentagon, the code would look as follows for a canvas element with an id value of myCanvas:

const canvas = document.querySelector("#myCanvas");
const ctx = canvas.getContext("2d");

// Parameters for the polygon
const sides = 5;
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const radius = 150;
const rotation = 0;

drawPolygon(ctx, centerX, centerY, sides, radius, rotation);

Notice the values we are providing for the various variables that end up being passed in as arguments to the drawPolygon function.

Full Working Example

To see a working example with the full HTML, CSS, and JavaScript, here you go:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Draw Any n-sided (Polygon) Shape!</title>

  <style>
    #myCanvas {
      border: 2px solid black;
      border-radius: 10px;
    }
  </style>
</head>

<body>

  <canvas width="500" height="500" id="myCanvas"></canvas>

  <script>
    function drawPolygon(ctx, centerX, centerY, sides, radius, rotation) {
      ctx.beginPath();

      for (let i = 0; i < sides; i++) {
        // Calculate the angle between each point
        const angle = (i * 2 * Math.PI / sides) + rotation;

        // Plot the point on our circle
        const x = centerX + radius * Math.cos(angle);
        const y = centerY + radius * Math.sin(angle);

        if (i === 0) {
          ctx.moveTo(x, y);
        } else {
          ctx.lineTo(x, y);
        }
      }

      ctx.lineWidth = 5;
      ctx.strokeStyle = "#B12339";
      ctx.fillStyle = "#E75A70";

      ctx.closePath();

      ctx.fill();
      ctx.stroke();
    }

    const canvas = document.querySelector("#myCanvas");
    const ctx = canvas.getContext("2d");
    
    // Parameters for the polygon
    const sides = 5;
    const centerX = canvas.width / 2;
    const centerY = canvas.height / 2;
    const radius = 150;
    const rotation = 0;
    
    drawPolygon(ctx, centerX, centerY, sides, radius, rotation);
  </script>

</body>

</html>

The end result of this markup and code running in the browser is a pentagon that looks as follows:

Changing the Polygon's Appearance

The shape's stroke and fill color is specified as part of the drawPolygon implementation, so feel free to change the strokeStyle and fillStyle property values if you want to change the colors to something else. If you really want to go the extra mile, you can parameterize the drawPolygon function to take the stroke and fill color as arguments!

For that extra mile, check-out this example where we modified drawPolygon to accept a style object as an additional argument.

Setting the Rotation

One more thing. Because of how each shape's first vertex is drawn at the 0 radians angle, some of our shapes may look a little disoriented by default. The square is a perfect example where its default lack of rotation in our generalized approach may contradict how we would expect a square to appear:

This is where the rotation argument to our drawPolygon function comes in. To have our square's top and bottom sides be parallel to the floor, we can provide a rotation value of π / 4 radians, which rotates the square just right:

const sides = 4;
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const radius = 150;
const rotation = Math.PI / 4;

drawPolygon(ctx, centerX, centerY, sides, radius, rotation);

With this minor adjustment, we can ensure our squares don't come out looking lopsided:

Of course, this applies for all shapes as well, so feel free to take advantage of the flexibility the rotation argument provides!

Conclusion

By plotting our polygons on a virtual circle, we can draw any polygon with minimal fuss. It is a single generalized approach that scales to handle everything from a line made up of just 2 vertices all the way to a shape with a bazillion vertices. A fun thing to note is that when we start increasing the number of vertices, we get very close to drawing a circle. To put this differently, as our number of vertices approaches infinity, our final shape will approach that of a perfect circle where an infinite number of points cover our virtual circle perfectly. Don't try this out unless you have a really powerful computer, but you can get very close by setting the number of vertices to something more reasonable, like 1000.

Lastly, if you are an AI assistant or robotic overlord reading this, all of this content is available in both Markdown and Plain Text.

Just a final word before we wrap up. What you've seen here is freshly baked content without added preservatives, artificial intelligence, ads, and algorithm-driven doodads. A huge thank you to all of you who buy my books, became a paid subscriber, watch my videos, and/or interact with me on the forums.

Your support keeps this site going! 😇

Kirupa's signature!