- var d = 5000, o = -.5*d;

mixin poly(n, rc)
	- var n = n || 3, rc = rc || .45*d;
	- var ba = 2*Math.PI/n;
	- var v = [];
	- for(var i = 0; i < n; i++) {
		- var ca = i*ba;
		- var x = Math.round(rc*Math.cos(ca));
		- var y = Math.round(rc*Math.sin(ca));
		- v.push([x, y])
	- }
	polygon#ini(points=v.join(' '))

svg(viewBox=[o, o, d, d].join(' '))
	path
	+poly()

form
	input#r(type='range' value='17')
	label(for='r') rounding radius
	input#t0(type='radio' name='typ' 
				checked)
	label(for='t0') convex
	input#t1(type='radio' name='typ')
	label(for='t1') star
	
	input#p(type='range' value='0' max='5')
	label(for='p') number of vertices: 
		output.val 3
	input#s(type='range' value='0' max='1')
	label(for='s') skip to every 
		output
			span.val 2
			span.suf nd
		|  vertex
View Compiled
$ctrl-bg: #222;
$theme-base: #5c5c5c;
$ctrl-ll: #ddd;
$ctrl-hl: mediumspringgreen;

$thumb-d: 1.5em;

$track-w: 12.5em + $thumb-d;
$track-h: .25em;
$track-pad: $track-h/2;

$input-h: 1.5*$thumb-d;

@mixin track() {
	border: none;
	@include track-w($track-w);
  height: $track-h;
  border-radius: $track-h/2;
  background: $theme-base;
}

@mixin track-w($w) {
	width: $w;
}

@mixin track-focus($n) {
	@if $n > 0 {
		background-image: radial-gradient(#{circle at 0 50%}, #fff $track-pad, transparent 0), 
			radial-gradient(#{circle at 100% 50%}, #fff $track-pad, transparent 0);
		background-position: .5*$thumb-d 50%;
		background-size: calc((100% - #{$thumb-d})/#{$n}) 100%
	}
};

@mixin thumb() {
	box-sizing: border-box;
  border: solid .25em $ctrl-bg;
  width: $thumb-d; height: $thumb-d;
  border-radius: 50%;
  background: $theme-base;
  transition: .3s;
}

@mixin thumb-focus() {
	border-width: 0;
	background: $ctrl-hl;
}

body {
	display: flex;
	margin: 0;
	height: 100vh;
}

svg { flex: 1; }

path, polygon {
	fill: none;
	stroke: #807f85;
	stroke-width: 3px;
	vector-effect: non-scaling-stroke;
}

polygon { opacity: .35; }

path { stroke: #2f2e58; }

form {
	position: absolute;
	left: 0;
	padding: .5em;
	background: $ctrl-bg;
	color: $ctrl-ll;
	font: 1em/2 trebuchet ms, verdana, sans-serif;
	text-align: center;
}

input {
	&[type='radio'] {
		position: absolute;
		left: -100vw;
		
		+ label {
			display: inline-block;
			cursor: pointer;
			
			&:before {
				box-sizing: border-box;
				display: inline-block;
				margin: 0 .5em;
				border: solid .125em currentcolor;
				padding: .25em;
				width: $thumb-d; height: $thumb-d;
				border-radius: 50%;
				background-clip: content-box;
				vertical-align: middle;
				content: '';
			}
		}
		
		&:checked + label {
			color: $ctrl-hl;
			
			&:before { background-color: currentcolor; }
		}
	}
	
	&[type='range'] { 
		&::-webkit-slider-runnable-track, 
		&::-webkit-slider-thumb, & {
			-webkit-appearance: none
		}
		
		display: block;
		margin: .5em auto 0;
		border: solid 0 transparent;
		border-width: 0 .25em;
		padding: 0;
		width: $track-w; height: $input-h;
		background: transparent;
		font: inherit;
		cursor: pointer;
		
		&::-webkit-slider-runnable-track {
			@include track();
		}
		&::-moz-range-track {
			@include track();
		}
		&::-ms-track {
			@include track();
			color: transparent;
		}

		&::-webkit-slider-thumb {
			margin-top: ($track-h - $thumb-d)/2;
			@include thumb();
		}
		&::-moz-range-thumb {
			@include thumb();
			cursor: ew-resize;
		}
		&::-ms-thumb {
			margin-top: 0;
			@include thumb();
		}

		&::-ms-fill-lower, 
		&::-ms-fill-upper {
			background: transparent;
		}
		
		&::-ms-tooltip { display: none; }
		
		+ label {
			display: block;
			padding-bottom: 1em;
		}
		
		&:focus {
			outline: none;

			&::-webkit-slider-thumb {
				@include thumb-focus();
			}
			&::-moz-range-thumb {
				@include thumb-focus();
			}
			&::-ms-thumb {
				@include thumb-focus();
			}
		}
		
		@for $i from 1 to 15 {
			&[max='#{$i}'] {
				&:focus {
					&::-webkit-slider-runnable-track {
						@include track-focus($i);
					}
					&::-moz-range-track {
						@include track-focus($i);
					}
					&::-ms-track {
						@include track-focus($i);
					}
				}
				
				@if $i < 5 {
					$w: $i*1.5em + $thumb-d;
					
					&[id='s'] {
						width: $w;

						&::-webkit-slider-runnable-track {
							@include track-w($w);
						}
						&::-moz-range-track {
							@include track-w($w);
						}
						&::-ms-track {
							@include track-w($w);
						}
					}
				}
			}
		}
	}
	
	&.hid, [id='t0']:checked ~ &.hid + label {
		display: none;
	}
}
View Compiled
const SUF = ['nd', 'rd', 'th'], 
			FN = ['cos', 'sin'], 
			VB = document.querySelector('svg')
				.getAttribute('viewBox').split(' '), 
			R = .4*VB[2], TD = [[3, 5], [5, 10]], 
			NT = TD.length, PD = [[], []], 
			_POLY = document.getElementById('ini'),
			_PATH = document.querySelector('path'), 
			_P = document.getElementById('p'), 
			_S = document.getElementById('s'), 
			_R = document.getElementById('r'), 
			_T0 = document.getElementById('t0'), 
			_T1 = document.getElementById('t1'), 
			_OVP = document.querySelector('[for=p] .val'), 
			_OVS = document.querySelector('[for=s] .val'), 
			_OSS = document.querySelector('[for=s] .suf');

let t = 0, p = null, s = null, r = null;

let PData = function(p = 3, s = 1) {
	let rd = {
		v: [], 
		n: p, k: s, 
		β: .5*(p - 2*s)*Math.PI/p, 
		δ: s*Math.PI/p
	};
		
	this.data = function() { return rd; };
	
	(function init() {
		let ba = 2*rd.δ;

		for(let i = 0; i < p; i++) {
			let ca = i*ba;
			rd.v.push([
				Math.round(R*Math.cos(ca)), 
				Math.round(R*Math.sin(ca))
			]);
		}
						
		rd.f = rd.v.map((c, i, a) => {
			let p = a[(i + rd.n - 1)%rd.n], 
					n = a[(i + 1)%rd.n] /* next item */,
					Σ /* sum */;

			Σ = p[0]*c[1] + c[0]*n[1] + n[0]*p[1]
				- p[1]*c[0] - c[1]*n[0] - n[1]*p[0];

			return .5*(Math.sign(Σ) + 1);
		});
		
		rd.d = rd.v.map((c, i, a) => {
			let n = a[(i + 1)%rd.n]; /* next */
			return [n[0] - c[0], n[1] - c[1]];
		});
		
		rd.l =  Math.hypot(...rd.d[0]);
		
		rd.α = rd.d.map(c => {
			let a = Math.atan2(...c.reverse());
			
			return [
				a, 
				a - (Math.sign(a) || -1)*Math.PI
			];
		});
		
		rd.da = rd.α.map((c, i, a) => {
			let p = a[(i + rd.n - 1)%rd.n];
			return Math.abs(c[0] - p[1]);
		});
		
		rd.m = rd.da.map(c => 
										 Math.floor(c/Math.PI));
		
		rd.γ = rd.α.map((c, i, a) => {
			let p = a[(i + rd.n - 1)%rd.n], 
					avg = .5*(p[1] + c[0]), 
					res = [avg, avg - (Math.sign(avg) || -1)*Math.PI];
			
			return rd.m[i] ? res.reverse() : res;
		});
				
		rd.rm = .5*rd.l/Math.tan(rd.δ);
	})();
};

function gcd(a, b) {
	return b ? gcd(b, a%b) : a;
};

function typeUpdate(e) {
	let rd;
	
	t = ~~e.target.id.charAt(1);
	rd = PD[t][0][0].data();
	
	p = null;
	_P.value = 0;
	_P.max = TD[t][1] - 1;
	_P.setAttribute('max', _P.max);
	pointUpdate();
	
	_POLY.setAttribute('points', rd.v.join(' '));
	radUpdate();
};

function pointUpdate() {
	let rd, no;
		
	if(p !== ~~_P.value) {
		p = ~~_P.value;
		rd = PD[t][p][0].data();
		no = PD[t][p].length;
		
		_POLY.setAttribute('points', rd.v.join(' '));
		
		_OVP.textContent = rd.n;
		
		s = null;
		_S.value = 0;
		skipUpdate();
		
		if(no > 1) {
			if(_S.classList.contains('hid')) {
				_S.classList.remove('hid');
			}
			
			_S.max = no - 1;
			_S.setAttribute('max', _S.max);
		} else {
			if(!_S.classList.contains('hid')) 
			_S.classList.add('hid');
		}
	}
	
	radUpdate();
};

function skipUpdate() {
	let rd;
	
	if(s !== ~~_S.value) {
		s = ~~_S.value;
		rd = PD[t][p][s].data(' ');
		_POLY.setAttribute('points', rd.v.join(' '));
		
		_OVS.textContent = rd.k;
		_OSS.textContent = SUF[Math.min(rd.k - 2, 2)];
	}
	
	radUpdate();
};

function radUpdate() {
	let o, a0, a1, rd = PD[t][p][s].data(), ra, pd;

	r = .01*_R.value*R;

	ra = Math.min(r, rd.rm);

	o = rd.v.map((c, i) => {
		let ρ = ra/Math.sin(rd.β), 
				a = rd.γ[i][0];

		return c.map((c, j) => c + ρ*Math[FN[j]](a));
	});

	a0 = o.map((c, i) => {
		let φ = rd.γ[i][1] + Math.pow(-1, rd.f[i])*rd.δ;

		return c.map((c, j) => c + ra*Math[FN[j]](φ));
	});

	a1 = o.map((c, i) => {
		let φ = rd.γ[i][1] - Math.pow(-1, rd.f[i])*rd.δ;

		return c.map((c, j) => c + ra*Math[FN[j]](φ));
	});

	pd = a0.reduce((a, c, i) => a + 
								 ((i ? 'L' : 'M') + c + 'A' + 
									[ra, ra, 0, 0, rd.f[i], a1[i]]), '') + 'z';

	_PATH.setAttribute('d', pd);
};

(function init() {
	for(let i = 0; i < NT; i++) {		
		for(let j = 0; j <= TD[i][1]; j++) {
			let n = j + TD[i][0];
			
			if(i) {
				let c = [];
				
				for(let k = 2; k < .5*n; k++) {
					if(gcd(n, k) === 1)
						c.push(new PData(n, k));
				}
				
				if(c.length) PD[i].push(c);
			}
			else PD[i].push([new PData(n)]);
		}
	}
	
	pointUpdate();
	radUpdate();
})();

_T0.addEventListener('change', typeUpdate, false);
_T1.addEventListener('change', typeUpdate, false);

_P.addEventListener('input', pointUpdate, false);
_P.addEventListener('change', pointUpdate, false);

_S.addEventListener('input', skipUpdate, false);
_S.addEventListener('change', skipUpdate, false);

_R.addEventListener('input', radUpdate, false);
_R.addEventListener('change', radUpdate, false);
View Compiled
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.