ラベル Processing の投稿を表示しています。 すべての投稿を表示
ラベル Processing の投稿を表示しています。 すべての投稿を表示

2014年6月13日金曜日

Processingの起動時間

millis()という関数でint型の起動時間がミリ秒で帰るようです
これを使えば起動時間を最大20日程度まで計測できます

似た関数に second() minute() hour() day() month() year() がありますが、これはシステム時間を返す関数なので注意してください


ProcessingではframeCountという変数があり、これは1フレーム毎にインクリメントされていきます
またframeRateという変数もあり、起動時間 = frameCount / frameRateと計算できるのでは? と思うのですが そう簡単なものでもありません

というのは、frameRateを表示してみればわかると思いますが、このframeRate変数は定数ではありません

frameRate()関数で設定できるのはあくまで目標値であり、実際のフレームレートはPCの処理速度や負荷に応じて変動しています
frameRate変数で獲得できるフレームレートは実際に計測したフレームレートなので、frameCount / frameRateの計算をすると 負荷が大きい時には実際よりも長く起動していると計算されてしまいます


そこで、起動時間を計算する方法ですが
まず前提として「1.0 / frameRate = 前フレームから経過時間」という条件を作ります
実際のframeRateの計算方法を知らないのでこの条件が当てはまるかはわかりません

しかし、こうすることで非常に簡単に計算できるようになります
前フレームからの経過時間が分かれば、フレームごとに時間を積算すればいいだけです
ということで
float time = 0;
void draw() {
    time += 1.0 / frameRate;
}
とすれば稼働時間がわかります

浮動小数点演算の精度がきになる場合は
int second = 0; float millisecond = 0;
void draw() {
    if ((millisecond += 1.0 / frameRate) >= 1.0) { millisecond -= 1.0; second++; }
}
とでもしておけばmillisecondの最大値はせいぜい1.1以下におさまるはずなので、演算時の切り捨ては無視できると思います
(小数点以下の演算誤差は相変わらず存在します)


とりあえずこれで時間が扱えるようになりました
なんかの処理に使えるかも

言い訳をすると、day hour millis minute month second yearと並んでいるので、millisはシステム時間のミリ秒を返す関数かと思っていました
システム時間のミリ秒が欲しい場合はどうすればいいんだろう?

2014年5月27日火曜日

Processingでアナログ時計

Processingでアナログ時計を表示する関数です

void drawClock(String config) {
  String configs[] = split(config, ',');
  
  int x = width / 2, y = height / 2;
  int size = min(width, height);
  float hour   = 0; boolean isHour    = false; int hourColor   = #000000;
  float minute = 0; boolean isMinute  = false; int minuteColor = #000000;
  float sec    = 0; boolean isSec     = false; int secColor    = #000000;
  
  
  for (int i = 0; i < configs.length; i++) {
    if (configs[i] == null) { continue; }
    String[] val = split(configs[i], ' ');
    
    if (val.length < 1) { continue; }
    if (val[0] == null) { continue; }
    
    if (val.length < 2) { continue; }
    if (val[1] == null) { continue; }
    if (val[0].equals("h"))    { hour   = parseFloat(val[1]); isHour   = true; continue; }
    if (val[0].equals("m"))    { minute = parseFloat(val[1]); isMinute = true; continue; }
    if (val[0].equals("s"))    { sec    = parseFloat(val[1]); isSec    = true; continue; }
    if (val[0].equals("hc"))   { hourColor   = parseInt(val[1]); continue; }
    if (val[0].equals("mc"))   { minuteColor = parseInt(val[1]); continue; }
    if (val[0].equals("sc"))   { secColor    = parseInt(val[1]); continue; }
    if (val[0].equals("size")) { size = parseInt(val[1]); continue; }
    if (val[0].equals("x"))    { x = parseInt(val[1]); continue; }
    if (val[0].equals("y"))    { y = parseInt(val[1]); continue; }
  }
  
  pushMatrix();
  translate(x, y);
  
  noFill();
 
  sec    *=  6;
  minute *=  6; 
  hour   *= 30;
  
  hour   += minute / 12;
  minute += sec    / 60;
  
  ellipse(0, 0, size, size);
  
  {
    float eSize =  size * PI / 60 * 0.6;
    float len   = size / 2 * 0.95;
    for (int i = 0; i < 60; i++) {
      float px =  sin(radians(6 * i)) * len;
      float py = -cos(radians(6 * i)) * len;
      ellipse(px, py, eSize, eSize); 
    }
  }
  
  if (isSec) {
    float len = size / 2 * 0.75;
    float px =  sin(radians(sec)) * len;
    float py = -cos(radians(sec)) * len;
    stroke(secColor);
    line(px, py, 0, 0);
    line(-px * 0.25, -py * 0.25, 0, 0);
  }
  
  if (isMinute) {
    float len = size / 2 * 0.80;
    float px =  sin(radians(minute)) * len;
    float py = -cos(radians(minute)) * len;
    stroke(minuteColor);
    line(px, py, 0, 0);
    line(-px * 0.25, -py * 0.25, 0, 0);
  }
  
  if (isHour) {
    float len = size / 2 * 0.60;
    float px =  sin(radians(hour)) * len;
    float py = -cos(radians(hour)) * len;
    stroke(hourColor);
    line(px, py, 0, 0);
    line(-px * 0.25, -py * 0.25, 0, 0);
  }
  
  popMatrix();
}

引数は文字列で渡します
時針 分針 秒針 の表示が可能です
試しに作っただけなので、あんまり使いやすくないですけども

2014年4月27日日曜日

Processingで2Dと3Dの混在

ProcessingではsizeでP3Dオプションをつけることで3次元を描くことができます
しかしそうした場合2Dで線や文字を書こうとすると3Dの奥に書かれてしまい、手前に表示させることができません

これを解決するにはhint(DISABLE_DEPTH_TEST);と関数を呼べばいいようです

本当にこの方法でいいのかは不明ですが
一応狙い通りの前後関係で表示できるようになりました



奥の四角を3Dで 手前の線と数字を2Dとして描いています

なお、2Dで描くべきモノをすべて書いたらhint(ENABLE_DEPTH_TEST);で3Dに戻す必要があります


2Dと3Dの位置関係はもっとうまいやり方が有るようなきがするのですが。。。

2014年4月24日木曜日

Processingで3D空間

Processingで3Dを使うときのサンプルというか 雛形みたいなもの

final float BoxSize = 120;
final float AxisLineLen = BoxSize / 1.5;
final float AutoRotateDegree = 0.5;

boolean IsCameraRotate = true;
float CameraXrot = radians( 45);
float CameraYrot = radians(-45);
float CameraZoom = 160;
boolean pmousePressed = false, pkeyPressed = false;

void setup() { 
  textFont(createFont("GulimChe", 20));
  textAlign(LEFT, TOP);
  size(1024, 768, P3D);
  noFill();
}

void draw() {
  keyboardInput();
  
  pushMatrix();
  background(127);
  MoveCamera();
  DrawBox();
  popMatrix();
  
  text("camR:" + (int)degrees(CameraXrot), 0, 20 * 0);
  text("camP:" + (int)degrees(CameraYrot), 0, 20 * 1);
  text("zoom:" + (int)CameraZoom, 0, 20 * 2);
  text("musX:" + mouseX, 0, 20 * 3);
  text("musY:" + mouseY, 0, 20 * 4);
  
  if (!pkeyPressed && keyPressed && key == 's')
  { save("./Processing_ss.png"); } 
  
  pmousePressed = mousePressed;
  pkeyPressed   = keyPressed;
}

void keyboardInput() {
  if (!pkeyPressed && keyPressed) {
    if (key == '1') { IsCameraRotate = false; CameraXrot = radians( 0); CameraYrot = radians( 0); }
    if (key == '2') { IsCameraRotate = false; CameraXrot = radians(90); CameraYrot = radians( 0); }
    if (key == '3') { IsCameraRotate = false; CameraXrot = radians(90); CameraYrot = radians(89.71); }
    if (key == '4') { IsCameraRotate =  true; CameraXrot = radians(45); CameraYrot = radians(-45); }
  }
}

void DrawBox() {
  strokeWeight(2);
  stroke(255);     box(BoxSize / 2, BoxSize / 4, BoxSize);
  
  strokeWeight(1);
  stroke(#FF0000); line(0, 0, 0, 0, 0,  AxisLineLen); sphere(1, 0, 0, AxisLineLen);
  stroke(#7F0000); line(0, 0, 0, 0, 0, -AxisLineLen);
  stroke(#00FF00); line(0, 0, 0,  AxisLineLen, 0, 0); sphere(1, AxisLineLen, 0, 0);
  stroke(#007F00); line(0, 0, 0, -AxisLineLen, 0, 0);
  stroke(#0000FF); line(0, 0, 0, 0,  AxisLineLen, 0); sphere(1, 0, AxisLineLen, 0);
  stroke(#00007F); line(0, 0, 0, 0, -AxisLineLen, 0);
}

boolean MvCm_IsCameraMove = false;
void MoveCamera() {
  if (mousePressed) {
    CameraXrot += radians(pmouseX - mouseX) / 2;
    CameraYrot += radians(pmouseY - mouseY) / 2;
    MvCm_IsCameraMove = MvCm_IsCameraMove || pmouseX != mouseX || pmouseY != mouseY;
  } else {
    if (pmousePressed) 
    { IsCameraRotate = IsCameraRotate ? false : !MvCm_IsCameraMove; }
    if (IsCameraRotate) { CameraXrot += radians(AutoRotateDegree); }
    MvCm_IsCameraMove = false;
  }
  
  if (keyPressed && key == 'z') { CameraZoom++; }
  if (keyPressed && key == 'x') { CameraZoom--; }
  
  CameraXrot = (CameraXrot + TWO_PI) % TWO_PI;
  if (CameraYrot < -HALF_PI) { CameraYrot += PI; }
  if (CameraYrot >  HALF_PI) { CameraYrot -= PI; }
  
  camera(CameraXrot, CameraYrot, CameraZoom);
}

void camera(float X, float Y, float zoom) 
{ camera(cos(Y) * zoom * sin(X), sin(Y) * zoom, cos(Y) * zoom * cos(X), 0, 0, 0, 0, 1, 0); }

void sphere(float r, float x, float y, float z) 
{ pushMatrix(); translate(x, y, z); sphere(r); popMatrix(); }

やってることは
1カメラを移動
2箱と棒を表示
だけです
他にマウスのドラッグでカメラの位置を変えたりだとか、キーの入力に応じて視点を変更したりスクリーンショットを保存できます


カメラの位置変更はキーボードで行っています
一旦はマウスホイールでの動作も確認したのですが、あまりマウスホイールはどうさがよろしくなかったので

スクリーンショットの撮影をキー入力関数で行わないのは
キー入力:できるだけ早い段階でキーを読んでおきたい
スクリーンショット:すべての表示が終わった最後の段階で撮影したい
という違いによるものです

Processingは基本的に静的変数が使えないので
複数フレーム間で共有したい値はすべてグローバル変数にする必要があります
この辺りがちょっと面倒です

ちょっとした3Dの表示とかはProcessingだと簡単に作れるので
C#とかで作ったデータを何らかの方法でProcessingに渡すことができれば いろいろ応用が広がりそうです

2014年4月23日水曜日

Processingの3D空間で円弧

Processingにはarc関数があり、平面上で円弧を描きたい場合にはこれを使えばいいのですが、3Dで円弧を描くための関数は作られていないようです
ということで、間に合わせで作ってみました


void arc(float x, float y, float z, String vec, float start, float stop, float size) {
  final float lineDeg = radians(360 / 32);
  float px0, py0, pz0, px1, py1, pz1;
  boolean IsBreak = false;
  start = (start);
  stop  = (stop);
  
  do {
    float startRot = start;
    float stopRot  = startRot + lineDeg;
    if (stopRot > stop) { stopRot = stop; IsBreak = true; }
    
    px0 = px1 = py0 = py1 = pz0 = pz1 = 0;
    
    if (vec == "X") {
      pz0 = -sin(start)   * size;
      py0 = -cos(start)   * size;
      pz1 = -sin(stopRot) * size;
      py1 = -cos(stopRot) * size;
    }
    if (vec == "Y") {
      px0 =  sin(start)   * size;
      pz0 =  cos(start)   * size;
      px1 =  sin(stopRot) * size;
      pz1 =  cos(stopRot) * size;
    }
    if (vec == "Z") {
      px0 =  sin(start)   * size;
      py0 = -cos(start)   * size;
      px1 =  sin(stopRot) * size;
      py1 = -cos(stopRot) * size;
    }
    
    line(px0 + x, py0 + y, pz0 + z, px1 + x, py1 + y, pz1 + z);
    
    start = stopRot;
  } while (!IsBreak);
}

使い方は
  arc(0, 0, 0, "Z", radians(-45), radians(45), 30);
のような感じ
引数1-3に円の中心の位置を指定 
引数4に表示する面を文字列で指定 
引数5に開始点 引数6に終了点 をそれぞれラジアンで
引数6に円の半径を
与えます

そうすると
このように表示できます

基本的にlineのラッパー関数なので、線の色や太さ等はlineと同等に変更できます
言い換えればこの関数の前で色を変更した場合、lineなどに影響を与えるので注意してください

現在制限されていることですが
・任意の面で表示できない(X,Y,Zの3種類だけ)
・軸の正面から見て時計方向の回転だけ(start<=stopは許されない)
という点があります

1つ目については、個人的に使いたいのが3面で表示するだけで十分だったというのがあります
また、任意面で表示できるようにするのは面倒というのもあります

2つ目については、例えばstart:90 stop:0の90°の円弧を描くということはできません
例えばジャイロセンサの入力から回転を描く場合、おそらくdps等に応じて円弧の長さを変えると思いますが
startを0で固定し、ジャイロが負の方向に回転した場合はstopが負の値になる というような処理はできません
そのように使いたい場合は まず終了点を変数に保存し、終了点が負なら終了点をstartで与え、stopを0に それ以外の場合はstartを0にして終了点をstopとして与える といった判定処理が必要です

円の滑らかさは関数内lineDegを変更することで行います
Degと書きながら中身はRadianです 上記では360°を32分割しているので、正確には円ではなく32角形です
これが360角形とかになるとよほど拡大しない限りは問題なく滑らかになると思いますが、実際には32でも十分に滑らかです


また円を1周ぐるりと書く場合は、上記"start<=stop"が不可という制限から、start0 stop0ではなく、start0 stop6.283を与える必要があります(6.28は2piです)


(これくらいの関数 普通に実装されてるので実現できそうだなぁ…)

2014年4月22日火曜日

Processing

ふとしたことで、Processingを始めてみました(昔にちょっと触ってたけど)

バージョンが2になっていて、フォントが「Ubuntuっぽい感じの」ジワッとした感じになっていました

ProcessingはたぶんNexus7を買った時に少し触った程度だったと思います
なのでMacに入ってたProcessingはAndoroidoの設定のままでした(Winは新規で導入)

ProcessingはArduino以上にややこしい感じがしてあまり使っていませんでした
しかし3Dオブジェクトの表示などは簡単にできるので、便利かなーと思って始めた次第です



とりあえず、マイコンと接続するためにシリアルポートを使ってみました

import processing.serial.*;
Serial myPort = null;

void setup() {
  textFont(createFont("GulimChe", 20));
  size(1024, 768);
}

void draw() {
  if (myPort == null) { OpenSerialPortSelect(); }
}

void serialEvent(Serial myPort) {
  background(0);
  text(myPort.readStringUntil('\n'), 10, 20);
}

void OpenSerialPortSelect() {
  String[] SerialList = Serial.list();
  background(0);
  
  for (int i = 0; i < SerialList.length && i < 10; i++)
  { text((i + 1) + " : " + SerialList[i], 10, 20 + (i * 20)); }
  
  int SerialSelect = -1;
  if (keyPressed) { SerialSelect = key - '1'; }
  if (SerialSelect >= 0 && SerialSelect < SerialList.length) {
    myPort = new Serial(this, SerialList[SerialSelect], 115200);
    myPort.bufferUntil('\n');
  }
}

こんな感じです
まず接続するポートを選択する画面が表示されます
これはSerial.list()で得られた配列の最初9個を表示します
そして1-9のキーで選択したポートを115.2kbaudで開きます
あとは改行コードを受信した時点で割り込みが発生し、その文字列を表示します

ここまでできれば受信した文字列を配列に展開し、実数などに変換して棒グラフの長さを変えるなどすればぐらふぃかるに表示したり、いろいろできます

以前に缶サット用にPFD(Primary Flight Display)をC#で作りましたがProcessingを使えばこういうのももっと簡単に作れそうです