npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

round-polygon

v0.6.7

Published

Small, typed, dependency free tool to round corners of 2d-polygon provided by an array of { x, y } points.

Downloads

3,123

Readme

round-polygon

Small, typed, dependency-free tool to round corners of any 2d-polygon provided by an array of { x, y } points.

The algorithm prevents rounding overlaps, so if you pass an oversized radius, it won't break the shape, but instead calculate the maximum radius of each point, just like you expect.

preview

Demo page

Installation

by npm:

npm i round-polygon

and then

import roundPolygon from "round-polygon"

or if you don't use npm you can import module from unpkg:

import roundPolygon from "https://unpkg.com/round-polygon@latest/dist/round-polygon.es.js"

or by script tag (with a link to IIFE module, which pollutes global scope with roundPolygon and getSegments functions) in your html page:

<script src="https://unpkg.com/round-polygon@latest/dist/round-polygon.iife.js"></script> 

Usage

Types

if you use TypeScript you can also import input type "InitPoint" and output type "RoundedPoint"

import roundPolygon, { InitPoint, RoundedPoint } from "round-polygon"

let polygonToRound: InitPoint[],
    roundedPolygon: RoundedPoint[],
    radius: number

Input

roundPolygon function takes two arguments: an array of initial points and a radius

polygonToRound = [
  { x: 100, y:   0 },
  { x:   0, y: 150 },
  { x: 200, y: 150 },
  { x: 200, y:   0 },
  { x: 150, y: 200 },
]

radius = 20

roundedPolygon = roundPolygon(polygonToRound, radius)

To set a certain radius for a certain point, just add "r" property to the initial point. The radius passed as the argument to the function won't affect these points. Keep in mind that the algorithm rounds these points with higher priority.

{ x: 100, y: 0, r: 50 }

Output

a rounded point is an object with provided properties:

{
  id: number, // index of the current point
  x: number, // x-coordinate of the initial point
  y: number, // y-coordinate of the initial point
  offset: number, // length between the initial point and the start or the end of a rounding arc
  angle: { // in radians
    main: number, // the angle between the previous point, the current one, and the next one
    prev: number, // the angle between prev-to-curr-line to x-Axis
    next: number, // the angle between next-to-curr-line to x-Axis
    bis: number, // the bisector angle to x-Axis
    dir: 1 | -1 // whether clockwise (1) or counter-clockwise (-1) is the main angle direction (from the "prev" to the "next" angle)
  },
  arc: {
    radius: number, // the rounding radius of the current point (might be less then provided as an argument (caused by rounding overlapping))
    x: number, // x-coordinate of rounding center
    y: number // y-coordinate of rounding center
  },
  in: {
    length: number, // the length of prev-to-curr-line
    x: number, // x-coordinate where the arc begins laying on prev-to-curr-line
    y: number // y-coordinate where the arc begins laying on prev-to-curr-line
  },
  out: {
    length: number, // the length of next-to-curr-line
    x: number, // x-coordinate where arc ends laying on next-to-curr-line
    y: number // y-coordinate where arc ends laying on next-to-curr-line
  },
  prev: {...}, // a getter, returns prev-indexed rounded point
  next: {...} // a getter, returns next-indexed rounded point
}

Segments

You might need the rounded polygon provided with only { x, y } points, so every arc should be divided by segments of stright lines. Given getSegments function does that, it calculates coordinates of segmented arcs points. It takes array of rounded points, type of arc division (by LENGTH of segments or by AMOUNT of points) and a value to divide by corresponded to this type. It returns an array of { x, y } points.

import roundPolygon, { getSegments, Point } from "round-polygon"

let segments: Point[]

roundedPolygon = roundPolygon(polygonToRound, radius)
segments = getSegments(roundedPolygon, "LENGTH", 20)

Summary

so the whole approach to draw a rounded shape using, for example, Canvas API looks like this:

// init
const
  canvas = document.querySelector("canvas"),
  ctx = canvas.getContext("2d"),
  polygonToRound = [
    { x: 100, y:   0, r: 60 },
    { x:   0, y: 150 },
    { x: 200, y: 150, r: 60 },
    { x: 200, y:   0 },
    { x: 150, y: 200 },
  ],
  radius = 1000,
  roundedPolygon = roundPolygon(polygonToRound, radius),
  segments = getSegments(roundedPolygon, "LENGTH", 10)

// draw rounded shape
ctx.beginPath()
roundedPolygon.forEach((p, i) => {
  !i && ctx.moveTo(p.in.x, p.in.y)
  ctx.arcTo(p.x, p.y, p.out.x, p.out.y, p.arc.radius)
  ctx.lineTo(p.next.in.x, p.next.in.y)
})
ctx.stroke()

// draw segments of rounded shape
segments.forEach((p) => {
  ctx.beginPath()
  ctx.arc(p.x, p.y, 2, 0, Math.PI*2)
  ctx.fill()
})

example

Changelog

v0.6.6

  • add getSegments utilite to calculate segments of rounded corner arc
  • cleaner code

v0.6.4

  • common radius doesn't affect init-points with "r" = 0
  • handle points overlapping, 0/PI radians main-angle point

v0.6.0

  • added ability to provide a certain radius to a certain Point
  • some bugs fixed

v0.5.9

  • performance improvement (clean code)

v0.5.1

  • first stable version

Upcoming

  • input and output might be SVG path
  • provide bezier curve estimations as an alternative to an arc output property