裏目小僧の部屋

js4つの歯車

<canvas id="CycCanvas2" width="300" height="300"></canvas> 
<label><input type="range" min=1 max=99 value=50 id="fSlider" >比率</label>
<label><input type="range" min=1 max=10 value=5  id="NSlider" >歯数N</label>
<label><input type="range" min=1 max=10 value=5  id="MSlider" >歯数M</label>
<script>
/* サイクロイド曲線描画  原作:裏目小僧*/
var cvs = document.getElementById("CycCanvas2");
var c = cvs.getContext("2d");
var X0 =cvs.width/2;
var Y0 =cvs.height/2;
var rotc=0;
var f =0.5;
var N = 1+Math.trunc(Math.random() * 5);
var fSlider = document.getElementById('fSlider');
    fSlider.value = f*100;
var NSlider = document.getElementById('NSlider')
var MSlider = document.getElementById('MSlider')
NSlider,value = N;
MSlider,value = N;
 
function cycDraw(n,x0,y0,r0,rot,f) {//一枚の歯車描画
let  NN=2;//分解能大きくするほど荒い
let  r1 = r0  * f;       //外転
let  r2 = r0  * (1 - f); //内転
r0 = r0*n;
let  lap = 2 * Math.PI * r0; //円周
let  psz = Math.trunc(lap/NN);

c.beginPath();
 for(let i = 0 ; i<psz ; i++){
  let d0 = i*NN / r0;
  let d1 = d0 * n;
      d0 = d0 + rot;
   while( d1 > 2 * Math.PI)  d1 = d1 - 2 * Math.PI;
   if ( d1 / (2 * Math.PI) <= f ){ //外転描画
     d2 = d0 + d1 / f;
     x1 = x0 + Math.cos(d0) * (r0 + r1) - Math.cos(d2) * (r1);
     y1 = y0 + Math.sin(d0) * (r0 + r1) - Math.sin(d2) * (r1);
   } else { //内転
     d2 = d0 - (d1 - 2 * Math.PI * f) / (1 - f);
     x1 = x0 + Math.cos(d0) * (r0 - r2) + Math.cos(d2) * (r2);
     y1 = y0 + Math.sin(d0) * (r0 - r2) + Math.sin(d2) * (r2);
   };
   if(i == 0) c.moveTo(x1, y1);
   else c.lineTo(x1, y1);
 };
 c.closePath();  //moveTo()で指定した始点に向けて線を引き、領域を閉じます。
 c.fill();  //stroke()では輪郭線を描き、fill()にすると中を塗りつぶします。
 return ;
};
let ra=0;
let L;
let M;
let r0;
let A=0;
let B=0;
let C=0;

clBac="#ffffff";

const QuDRAW = function(){
 X0 =cvs.width/2;
 Y0 =cvs.height/2;
  f = Math.round(fSlider.value)/100;
  N = Math.round(NSlider.value);
  M= Math.round(MSlider.value);
  r0 =((Y0 - 4) / ((N + M) + f * 2))*0.8;
  c.fillStyle=clBac;
  c.fillRect(0, 0, cvs.width,cvs.height);

  let oM=(M&1)*Math.PI;//奇数の時反転させる
  let x1=B+X0-r0*N;//左上
  let y1=C+Y0-r0*N;
  c.fillStyle="#0000AF";
  cycDraw(N,x1,y1,r0,(-360*(1-f/2)-rotc)  * Math.PI / 180/N,f);//N枚の歯車描画
 
   L=r0*N+r0*M;//歯車の中心の距離
      let x2=x1+L;//右上
      let y2=y1;
         c.fillStyle="rgba(" + [32, 0, 16, 0.7] + ")";;
    cycDraw(M,x2,y2,r0,((360*(-f/2)+rotc)  * Math.PI / 180+oM)/M ,1-f);//M枚の歯車描画


      A=Math.round(A);
  let H=(Math.sqrt(L*L-A*A));
  let x3=x1+A;//左下 A^2+
  let y3=y1+H;
   ra=(Math.asin(A/L)-Math.PI/2)*(M+N); 
  let q=2*Math.PI*(M-N)/(N);
  c.fillStyle="#502010";
  cycDraw(M,x3,y3,r0,((360*(-f/2)+rotc)  * Math.PI / 180+oM-ra)/M,1-f);//M枚の歯車 上と接続

      let x4=x3+L;//右下
      let y4=y3;
      c.fillStyle="rgba(" + [0, 0, 0, 0.3] + ")";
       let oN=((N^M)&1)*Math.PI;//歯数が偶奇の時反転させる
      cycDraw(N,x4,y4,r0,((-360*(1-f/2)-rotc)* Math.PI / 180+ra+oN)/N,f);//N枚の歯車 左と接続
      cycDraw(N,x4,y4,r0,((-360*(1-f/2)-rotc)* Math.PI / 180-ra+oN)/N,f);//N枚の歯車 上と接続
 
 rotc+=5;//回転角を進めてゆく
 if(rotc>360) rotc-=360;
};
let m = {T:false,On:false, x:0,y:0 };
function onTMoff(){clBac="#FFF";m.On=false;m.T=false;
let def=Math.sin(ra);
let C=Math.cos(ra);
  if(Math.abs(def)<0.3){
   A=A-C*def*r0/2;
   QuDRAW();
 }
}
 
function onMouseDown(e){clBac="#ECF";m.T=true;m.On=true;m.x=e.offsetX;m.y=e.offsetY;}
function onMouseMove(e){
if(m.T){
 let x=e.offsetX-m.x;
 let y=e.offsetY-m.y;
 if(e.offsetY<Y0) {B+=x; A-=x;}else A+=x;
  C+=y;
 };
 m.T=m.On;
 m.x=e.offsetX;m.y=e.offsetY;
};
function onTMove(e){
 e.preventDefault();
 let r= e.target.getBoundingClientRect();
 if(e.touches.length=1){
  let sx=e.touches[0].pageX;//座標関係なく差分だけで動かす
  let sy=e.touches[0].pageY-r.top + window.pageYOffset;
  if(m.T){
   let x=sx-m.x;
   let y=sy-m.y;
   if(sy<Y0) {B+=x; A-=x;}else A+=x;
   C+=y;
  };
 m.T=true;
 m.x=sx;m.y=sy;
 }else   m.T=false;//途中複数タッチになったら跳躍防止
};

var IntVal= setInterval(QuDRAW , 100);
 cvs.addEventListener('mousemove', onMouseMove);
 cvs.addEventListener('mousedown', onMouseDown);
 cvs.addEventListener('mouseup'    , onTMoff);
 cvs.addEventListener('touchmove', onTMove);
 cvs.addEventListener('touchend'   ,onTMoff); 
</script>
<INPUT TYPE="BUTTON" VALUE="表示幅を4割増" onClick=cvs.width*=1.4142;cvs.height*=1.4142;></INPUT><BR>

プライバシーポリシー本文は日本語以外に翻訳禁止