- 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
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.