うつろぐ

文章

Processingできれいな歯車を描こう

あんまり難しいこと言わなくても書ける記事のネタができました~。某大学某学科のB1の皆さんはとくに今Processingでいろんな図形描きたいぞ~ってなってることもあるかと思うので良さげでは?と思って勢いで書いています

初級編 適当な歯車をかく

こんなのが描けます
f:id:Distorted_Unchi:20170701004400j:plain
手順としては、まず
f:id:Distorted_Unchi:20170701004537j:plain
このように二つ円を描き中心から2本線を延ばします
そうすると4つ交点ができる
そしてこれを一周分やるとこうなる
f:id:Distorted_Unchi:20170701005000j:plain
以下のように交点を順番に繋いでいけばなんとなく歯車っぽい図形が描けます かんたん
f:id:Distorted_Unchi:20170701005549j:plain
以下「こんなのが描けます」の歯車を描くコードです

float px, py;

void setup() {
  size(400, 400);
  px=width/2.0;
  py=height/2.0;
}

void draw() {
  background(-1);

  stroke(0);
  strokeWeight(1);
  noFill();
  beginShape();
  float div=12;
  for (int i=0; i<div; i++) {
    vertex(qPx(px, 150, 360*(i/div)-360/(div*4)), qPy(py, 150, 360*(i/div)-360/(div*4)));
    vertex(qPx(px, 180, 360*(i/div)-360/(div*4)), qPy(py, 180, 360*(i/div)-360/(div*4)));
    vertex(qPx(px, 180, 360*(i/div)+360/(div*4)), qPy(py, 180, 360*(i/div)+360/(div*4)));
    vertex(qPx(px, 150, 360*(i/div)+360/(div*4)), qPy(py, 150, 360*(i/div)+360/(div*4)));
  }
  endShape(CLOSE);
}

//---------------------------------------
//ある座標から極座標移動(長さ、角度を決めて移動)したときのx,y座標を計算する関数

float qPx(float _x, float _r, float _deg) { //x
  return _x+_r*cos(radians(_deg));
}

float qPy(float _y, float _r, float _deg) { //y
  return _y+_r*sin(radians(_deg));
}

どの辺が適当なのこれ

Googleで「歯車」と検索していちばん最初に出てきた画像がとりあえず
f:id:Distorted_Unchi:20170701011704j:plain
これなんですけど、さっき描いた歯車と見比べて気づいたことはないですか とくに歯のぶぶん

そう

f:id:Distorted_Unchi:20170701011447j:plain
これです これなのです
そもそも上>下だと歯車同士が噛み合わなくないですか 絶対噛み合わない うんち

(ここからは頻繁に「上」とか「上の円」とかそういう表現を使っていきたいと思います)

ちょっと上級編 なんとなく噛み合いそうな(上<下の)歯車を描く

解決法は色々ありそうなんですが僕が思いついたのはこれです
f:id:Distorted_Unchi:20170701012028j:plain
もう一つでかい円を描いて、その円の中心から延ばした線との交点と歯車の下の円の2点とで線をかいて、それと歯車の上の円との交点を上の歯の点にしようってやつです
でこれも一周する
f:id:Distorted_Unchi:20170701012632j:plain
これコードにすればいけるね!って感じなんですが、なんかそう簡単ではなかったっぽいです
さっきの場合は交点はぜんぶ円の中心からの距離が分かれば求められたけど、今回は本当に「円と線分の交点」を求めなくてはならないのです
これ、けっこうしんどいです なので僕は求めない方法を取っていきます
(勿論これが求められる方はあとはvertexすれば描けますぜ) (あとこれ以外にも楽な描き方はある筈なのでほんの一例としてって感じです)

ここからはPGraphicsが大活躍します ゆえにこれはProcessingでしかできない
PGraphicsってなにー?みたいなことはここでは省略させてください かなりの数の資料がある筈なのでこれは僕が説明するより調べたほうがわかりやすいからです

まずさっきのやつの外側の円と歯車の上の円以外をnoStrokeで白黒でPGraphicsに描画します
こんなかんじです PGraphicsのwidth及びheightは歯車の上の円の直径にしています
f:id:Distorted_Unchi:20170701014400p:plain
結論から言ってしまうとこれを上の円でちょん切るだけです これを実現するのがPGraphicsなんだぜということです
mask関数などを駆使してさっきのPGraphicsと同じサイズの円外枠を作ります こんなの
f:id:Distorted_Unchi:20170701014909p:plain
そしてこれを上からかぶせると
f:id:Distorted_Unchi:20170701015200p:plain
歯車の形のマスクができました あとはこれを無地なり画像なりにmaskして使って噛み合いそうな歯車の完成!
さっきと違って上の歯を直線じゃなく円形でちょん切ってますがそこはもう気にしない感じで…

たのしい
f:id:Distorted_Unchi:20170701020148j:plain

以下は上の「ね」の歯車を描くコードです
HagurumaTes.pde(本体)

PImage img;

void setup() {
  size(500, 500);
  img=loadImage("ね.jpg");
}

void draw() {
  background(-1);
  drawHag(width/2.0, height/2.0, 400);
}

fn_drawHag.pde(歯車を描く関数)

void drawHag(float px, float py, float size) {
  pushStyle();
  PGraphics hagBase, hagMask, cirFrameBase, cirFrameMask;
  float r1, r2, r3;
  r1=size/1.2; //下の円の直径
  r2=size; //上の円の直径
  r3=2.2*size/1.2; //外側のでかい円の直径

  cirFrameBase=createGraphics((int)r2, (int)r2, JAVA2D); //円外枠のベース
  cirFrameBase.beginDraw();
  cirFrameBase.noStroke();
  cirFrameBase.background(0);
  cirFrameBase.endDraw();

  cirFrameMask=createGraphics((int)r2, (int)r2, JAVA2D); //円外枠の切り抜き型
  cirFrameMask.beginDraw();
  cirFrameMask.noStroke();
  cirFrameMask.background(-1);
  cirFrameMask.fill(0);
  cirFrameMask.ellipse(r2/2.0, r2/2.0, r2, r2);
  cirFrameMask.endDraw();

  cirFrameBase.mask(cirFrameMask); //ここでcirFrameBaseが円外枠になる

  hagMask=createGraphics((int)r2, (int)r2, JAVA2D); //放射状のアレを描く
  hagMask.beginDraw();
  hagMask.background(0);
  hagMask.fill(-1);
  hagMask.noStroke();
  hagMask.ellipse(r2/2.0, r2/2.0, r1, r1);
  float div=12.0;
  for (int i=0; i<div; i++) {
    hagMask.beginShape();
    hagMask.vertex(qPx(r2/2.0, r3/2.0, 360*(i/div)), qPy(r2/2.0, r3/2.0, 360*(i/div)));
    hagMask.vertex(qPx(r2/2.0, r1/2.0, 360*(i/div)-360/(div*4)), qPy(r2/2.0, r1/2.0, 360*(i/div)-360/(div*4)));
    hagMask.vertex(qPx(r2/2.0, r1/2.0, 360*(i/div)+360/(div*4)), qPy(r2/2.0, r1/2.0, 360*(i/div)+360/(div*4)));
    hagMask.endShape(CLOSE);
  }
  hagMask.image(cirFrameBase, 0, 0); //円外枠を上から重ねる
  hagMask.endDraw(); //歯車のマスクが完成

  img.resize((int)r2, (int)r2);
  hagBase=createGraphics((int)r2, (int)r2, JAVA2D); //歯車のベース(maskする対象)
  hagBase.beginDraw();
  hagBase.image(img, 0, 0); //ね.jpgを描画
  hagBase.endDraw();
  hagBase.mask(hagMask); //歯車の形に切り抜けた!

  imageMode(CENTER);
  image(hagBase, px, py); //指定座標に描画

  popStyle();
}

//-----------------------------------

float qPx(float _x, float _r, float _deg) {
  return _x+_r*cos(radians(_deg));
}

float qPy(float _y, float _r, float _deg) {
  return _y+_r*sin(radians(_deg));
}

おわりに

PGraphicsは最高 最高だよ ありがとう