正多芒星

ベランダから見る夕日。 朝日や夕日がベランダから見えるようになるのが、俺の中での冬の始まり。
しばらく前 に css の三角関数と clip-path を組み合わせて正多角形を作ってみた。 隣り合う頂点を順番に繋ぐ単純なものだったが、計算結果ではなく計算式で記述できて、自分の中の満足度は高かった。 それを何に使うのかと訊かれたくは無いけれど。
沈む夕日を見ているうちにそんなことを思い出して、その流れで思いついた。 結ぶ頂点を一つ跳ばしにしたら星型になるのではないかと。
で、早速試してみた結果がこれ。 五芒星と六芒星。
html
<div class="polygon_base">
<div class="p5"></div>
</div>
<div class="polygon_base">
<div class="p6"></div>
</div>
css
.polygon_base {
width: 200px;
height: 200px;
border: 1px solid black;
border-radius: 50%;
padding: 4px;
}
.polygon_base div {
width: 100%;
height: 100%;
background: black;
}
.polygon_base .p5 {
clip-path: polygon(
calc( 50% + cos( 360deg / 5 * 0 - 90deg ) * 50% )
calc( 50% + sin( 360deg / 5 * 0 - 90deg ) * 50% ),
calc( 50% + cos( 360deg / 5 * 2 - 90deg ) * 50% )
calc( 50% + sin( 360deg / 5 * 2 - 90deg ) * 50% ),
calc( 50% + cos( 360deg / 5 * 4 - 90deg ) * 50% )
calc( 50% + sin( 360deg / 5 * 4 - 90deg ) * 50% ),
calc( 50% + cos( 360deg / 5 * 1 - 90deg ) * 50% )
calc( 50% + sin( 360deg / 5 * 1 - 90deg ) * 50% ),
calc( 50% + cos( 360deg / 5 * 3 - 90deg ) * 50% )
calc( 50% + sin( 360deg / 5 * 3 - 90deg ) * 50% )
);
}
.polygon_base .p6 {
clip-path: polygon(
calc( 50% + cos( 360deg / 6 * 0 - 90deg ) * 50% )
calc( 50% + sin( 360deg / 6 * 0 - 90deg ) * 50% ),
calc( 50% + cos( 360deg / 6 * 2 - 90deg ) * 50% )
calc( 50% + sin( 360deg / 6 * 2 - 90deg ) * 50% ),
calc( 50% + cos( 360deg / 6 * 4 - 90deg ) * 50% )
calc( 50% + sin( 360deg / 6 * 4 - 90deg ) * 50% ),
calc( 50% + cos( 360deg / 6 * 0 - 90deg ) * 50% )
calc( 50% + sin( 360deg / 6 * 0 - 90deg ) * 50% ),
calc( 50% + cos( 360deg / 6 * 1 - 90deg ) * 50% )
calc( 50% + sin( 360deg / 6 * 1 - 90deg ) * 50% ),
calc( 50% + cos( 360deg / 6 * 3 - 90deg ) * 50% )
calc( 50% + sin( 360deg / 6 * 3 - 90deg ) * 50% ),
calc( 50% + cos( 360deg / 6 * 5 - 90deg ) * 50% )
calc( 50% + sin( 360deg / 6 * 5 - 90deg ) * 50% ),
calc( 50% + cos( 360deg / 6 * 1 - 90deg ) * 50% )
calc( 50% + sin( 360deg / 6 * 1 - 90deg ) * 50% )
);
}
五芒星は、単純に一つ跳ばしで頂点を繋ぐだけ。
六芒星は、どうしようかちょっと迷って、閉じた三角形を二つ重ねてみたら上手くいった。
途中でパスが交差したり領域が重なり合ったりするが、最終的に最外周(?)となる領域でクリッピングしてくれる。 そしてクリッピングされた内側は、同じ一繋がりの領域として処理される。
五芒星と六芒星が出来ると、後は何芒星でも作れる。 頂点の数が奇数なら五芒星の、偶数なら六芒星の、単純な応用。 一つ跳ばしが二つ跳ばしや三つ跳ばしになっても、考え方は同じだからね。
ということで、前回同様ジェネレーターを作ってみた。
頂点の数と、その頂点を跳ばす数の指定に従って、12時の位置から clip-path の線を引く。
開始位置を一箇所で固定にすると、頂点の数と跳ばす数との組み合わせによって、頂点を網羅できない場合がある。 具体的には、頂点の数と跳ばす数(0始まりなので+1)が互いに素でない場合。 だから上の例の六芒星などは描けないのだが気にしない。
跳ばす数の最大値は、頂点の数の半分未満。 これは頂点の数の変更に連動させている。 跳ばす数として頂点の数の半分を超えた値を指定しても、描かれるのは半分未満を指定したパターンのどれかだからね。 無駄な指定をしなくて済む、俺に優しい設計。
以下、ソース。
html
<div class="polygon_base">
<div class="pN" id="p0"></div>
</div>
<div>
<label>
頂点の数
<input type="number" id="vtxCnt" value="3" min="3" max="21" />
</label>
<label>
跳ばす数
<input type="number" id="skpCnt" value="0" min="0" max="0" />
</label>
</div>
JavaScript
window.onload = () => {
const vtxElm = document.getElementById( "vtxCnt" );
const skpElm = document.getElementById( "skpCnt" );
const dsp = () => {
const vtxCnt = parseInt( vtxElm.value );
const skpCnt = parseInt( skpElm.value );
const clip = ( new Array( vtxCnt ) )
.fill( "" )
.map( ( e, i ) => `calc( 50% + cos( 360deg / ${ vtxCnt } * ${ i * ( skpCnt + 1 ) } - 90deg ) * 50% ) calc( 50% + sin( 360deg / ${ vtxCnt } * ${ i * ( skpCnt + 1 ) } - 90deg ) * 50% )` )
.join( "," );
document.getElementById( "p0" ).style.clipPath = `polygon( ${ clip } )`;
};
const modSkp = () => {
const curCnt = parseInt( skpElm.value );
const maxCnt = Math.floor( ( parseInt( vtxElm.value ) - 1 ) / 2 ) - 1;
skpElm.max = maxCnt;
skpElm.value = curCnt > maxCnt ? maxCnt : curCnt;
}
vtxElm.addEventListener( 'change', () => {
modSkp();
dsp();
}, false );
skpElm.addEventListener( 'change', () => {
dsp();
}, false );
}
そして今回も、これが何に使えるのかなんて考えない。