About this Blog

This Blog has English posts and Japanese posts. About Mac, iOS, Objective-C, and so on.

2013年5月19日日曜日

自作ペイントソフトで筆のような効果を出す

学校の課題で簡単なペイントソフトを作りました。
1つオリジナルの味付けをとのことだったので、書道っぽくマウスの移動速度に応じて描く線の太さを変えるようにしました。
基本的には、一定時間ごとに検出されるマウスの位置情報から、移動量を速度とみなして、その逆数に比例した線の太さを設定します。
こうすると、ゆっくり動かした時には太い線が、早く動かした時には細い線が描けます。
が、1回ごとの移動量だけを元に計算すると、変動が大きく線がボコボコになってしまいます。
そこで、過去の移動量もストアしておき、平均移動量を使うことにしました。


計算としては、以下のようになります。
 最新の移動量 = { (1つ前のX座標 - 現在のX座標)の絶対値 + (1つ前のY座標 - 現在のY座標)の絶対値 } * 0.5
 新しい平均移動量 = { (過去の平均移動量)*重み + 現在の速度} / (重み+1)
 筆の太さ = 適切なパラメータ / 新しい平均移動量

2点間の距離の計算は、近似して計算の負荷を減らしています。


実行結果は以下の通り。左が平均化していないもの、右が平均化したものです。



ソースコードは以下の通り。Ruby + GTK2です。(課題自体はCだったのですが、コードが膨大だったので)
コード中では「移動量」ではなく「速度」(velocity, v)としています。厳密には正しくないですがまあ気にしないで。

calligraphy.rb
require 'gtk2'

# 筆に関するパラメータ
# いろいろ変えてみてください。
INIT_FUDE_VELOCITY = 2 # 筆の平均速度の初期値
FUDE_PARAM = 50        # 速度の逆数に乗ずる定数
WEIGHT = 5             # 平均速度算出のための重み

# 筆に関する変数を用意
$velocity = INIT_FUDE_VELOCITY
$x0 = 0
$y0 = 0
$x1 = 0
$y1 = 0

# ウィンドウとドローイングエリアの作成など
window = Gtk::Window.new
window.set_size_request(640, 640)
window.set_app_paintable(true)
window.set_events(Gdk::Event::BUTTON_PRESS_MASK | Gdk::Event::BUTTON_RELEASE_MASK | Gdk::Event::BUTTON_MOTION_MASK)
window.realize
drawable = window.window

# グラフィックコンテキスト(GC)の用意
gc = Gdk::GC.new(drawable)
black = Gdk::Color.new(0, 0, 0)
colormap = Gdk::Colormap.system
colormap.alloc_color(black, false, true)
gc.set_foreground(black)

window.signal_connect('motion_notify_event') do |win, evt| #マウスが動いた時
  $x1 = evt.x
  $y1 = evt.y
  v = (($x1-$x0).abs + ($y1-$y0).abs)/2.0             # 最新の速度を計算
  $velocity = (WEIGHT*$velocity + v)/(WEIGHT+1).to_f  # 平均速度を計算し直す
  fude_width = FUDE_PARAM/$velocity                   # 筆の太さを決定する
  gc.set_line_attributes(fude_width,Gdk::GC::LINE_SOLID,Gdk::GC::CAP_ROUND,Gdk::GC::JOIN_BEVEL) # 筆の太さを設定する
  drawable.draw_line(gc, $x0, $y0, $x1, $y1)
  $x0 = evt.x
  $y0 = evt.y
end

window.signal_connect('button_press_event') do |win, evt| #マウスボタンが押された時
  $x0 = evt.x
  $y0 = evt.y
end

window.signal_connect('button_release_event') do |win, evt| #マウスボタンが離された時
  $v = INIT_FUDE_VELOCITY
end

window.signal_connect('destroy') do
  Gtk.main_quit
end

window.show_all

Gtk.main


参考
 gtk2-tut - Ruby-GNOME2 Project Website
 Ruby/GTK - Ruby-GNOME2 Project Website
 ウインドウへの直接描画(Gdk::Drawable編) - Ruby-GNOME2 Project Website
 [Ruby] Ruby-GNOME2(Ruby/GTK2) 線を引く
 [Ruby] Ruby-GNOME2(Ruby/GTK2) マウスのイベントを拾う


ちなみに、GtkのRubyバインディングは
$sudo gem install gtk2
でインストールできます。(Ruby1.9.3で確認)

最後にもう1枚。

2013年5月15日水曜日

ruby-openglの立体をGithubに公開しました。

まだまだドキュメンテーションはじめ不備が多いですが、ひとまずお知らせ。
ykuramata/ruby-opengl-glut-shapes · GitHub

公開したのは、以下の通り。
・楕円体 (GLUT.Ellipsoid)
・回転角度によって半径を変えられる回転体 (GLUT.Cocoon)
・曲線周りの回転体 (GLUT.Macaroni)
・レゴブロック (GLUT.Legoblock)

一部は記事として紹介しています。
enthusiastick coding: ruby-openglで楕円体を描いてみた
[ruby-opengl] 回転体 Advanced(回転角度によって半径を変えられる)

2013年5月8日水曜日

Ubuntu+Ruby1.9.3+OpenGL+joystick(Xboxのコントローラ)

enthusiastick coding: Ubuntu+RubyでX-BOXのコントローラを使う(gem不使用)で接続したXboxのコントローラでの入力をOpenGLに反映させてみます。

参考にしたのは、
Pablotron: Joystick-Rubyで落としてきたtarボールに入っていたサンプルコードのxo.rb
require 'opengl'
require 'glut'
require 'joystick'
INTERVAL = 50
# 中略
$joy = Joystick::Device::open "/dev/input/js0" # 環境に応じて書き換えてください。
$foo = 0.0 # ジョイスティックからの入力を反映させたい変数。
timer_func = Proc.new{
  foo_diff = 0
  while $joy.pending?
    ev = $joy.next_event
    case ev.type
      when Joystick::Event::AXIS
        case ev.num
        when 0
          foo_diff = ev.value/1000.0 #AXISの値は-32767から32767
        end
      when Joystick::Event::BUTTON
        case ev.num
        when 0
          puts ev.value #=> ボタンが押された時: 1, ボタンが離された時: 0
          # ボタンに反応させたい時はここに具体的な処理を書いてください。
        end
      end
  end
  $foo += foo_diff
}
# 中略
GLUT.TimerFunc(INTERVAL, timer_func, 0)
# 後略
joystickからの入力値はバッファにためられるので、ただ一定時間ごとに値を取得してOpenGLの描画に反映させてもうまく動きません。
そこで、毎回バッファをクリアして最新の値を取得してするようにしています。
joystickの入力をトリガにするという手もありますが、今回はこれで。

2013年5月5日日曜日

Ubuntu+Ruby1.9.3でXboxのコントローラを使う(gem不使用)


といっても、それほど難しいことはしていないのですが。
Linuxではこういったデバイス(ゲームのコントローラなど)を汎用的にjoystickとして扱います。

コントローラを接続してから、
$ls /dev/input/
と打つと、js0やjs1といったデバイスが表示されます。

で、それを使うためのRubyのライブラリもちゃんとあります。
自分の環境だとgem経由ではうまく動作しなかったので、
Pablotron: Joystick-Ruby
からダウンロードして
$ruby ./extconf.rb
$make
$sudo make install
しました。 ただ1点、joystick.cの77行目
if ((*fd = open(RSTRING(path)->ptr, O_RDONLY)) >= 0) { #=>Compile ERROR!
がRuby1.8向けの古い書き方なので、
if ((*fd = open(RSTRING_PTR(path), O_RDONLY)) >= 0) {
と修正しました。

使い方は、サンプルコードや
RDoc Documentation
を参考にしましょう。

# スティックから手を離した時にAxisの値が0に戻らないという謎の現象。
# そういうもんなのか、コントローラが壊れているのかは不明.. # 長らくしまいこんでたからな〜

[arduino]ミニ四駆のラップタイム計測

enthusiastick coding: [Arduino]光が遮られた回数をカウントの回路を流用して、コードの書き換えだけでミニ四駆用のラップタイム計測タイマーに改造してみます。

回路図

タクトスイッチを使わないので、左半分だけでいいのですが。

/* ミニ四駆用ラップタイマー */
const int CDS = 0;
const int LED = 13;
int cds_value = 0;
int old_cds_value = 0;
int lap_count = 0;
unsigned long lap_time = 0;
unsigned long start_time = 0;

void setup(){
  pinMode(LED, OUTPUT);
  Serial.begin(9600);
}

void loop(){
  /* CdSの入力値の低下を検出 */  
  cds_value = analogRead(CDS);
  if(cds_value < old_cds_value*0.8){//元の光の強さの0.8倍未満なら光が弱くなったと判断
    Serial.print('LAP: ');
    Serial.println(lap_count);//LAP: 0は無視してください. :P
    Serial.println(micros() - start_time);//マイクロ秒を検出. micors()は70分でオーバーフローする.
    start_time = micros();
    lap_count++;
  }
  old_cds_value = cds_value;
  delay(10);
}

[Arduino]光が遮られた回数をカウント

CdSの入力値を監視し、光が弱くなったことを検出します。
光が弱くなった回数をカウントし、タクトスイッチを押した時にLEDの点滅回数でそのカウントを出力します。
ただ、なにかがCdSの上を横切った、というようなときはうまくいきますが、
だんだん光が弱くなるような場合(指でCdSを押さえたときとか)は、それを複数回にカウントしてしまいます。


こんな感じ


回路図


スケッチ(コード)
/* 光が遮られた回数をカウントし、LED光で知らせるプログラム. */
const int CDS = 0;
const int SW = 8;
const int LED = 13;
int i;
int counter = 0;
int old_cds_value = 0;
int cds_value = 0;
int sw_value = 0;
int old_sw_value = 0;
int sw_state = 0;

void setup(){
  pinMode(LED, OUTPUT);
  pinMode(SW, INPUT);
}

void loop(){
  /* スイッチが押されたことを検出 */
  sw_value = digitalRead(SW);
  if(sw_value==HIGH && old_sw_value==LOW){
    sw_state = 1 - sw_state;
    delay(10);
  }
  if(sw_state==1){
    for(i=0;i<counter;i++){
      digitalWrite(LED, HIGH);
      delay(500);
      digitalWrite(LED, LOW);
      delay(500);
    }
    sw_state = 0;
  }
  old_sw_value = sw_value;
  /* CdSの入力値の低下を検出 */  
  cds_value = analogRead(CDS);
  if(cds_value < old_cds_value*0.8){//元の光の強さの0.8倍未満なら光が弱くなったと判断
    counter++;
  }
  old_cds_value = cds_value;
  delay(100);
}

2013年5月2日木曜日

Arduino UNOとポケコンの接続

エルチカ(LEDチカチカ)は終わったので、ポケコンとArduinoで通信してみました。
ポケコンの11ピンを使ってのシリアルパラレル通信。
部品の制限で2bit通信。0~3の値をポケコンに送ることができます。

こんな感じ。



回路図

ポケコンのGNDの抵抗を書き忘れました。補ってください。
抵抗はCdSにつないであるのが10kΩ,それ以外は1kΩです。
ポケコンのPIN番号は、一番上側を1,一番下側を11としています。3がGNDです。
参考: 8ビット制御練習基板

Arduinoのコード
const int SENSOR = 0; /* アナログINの0 */
const int POKECOM_1 = 12; /* デジタルIOの12 */
const int POKECOM_2 = 13; /* デジタルIOの13 */

int val = 0;

void setup(){
  pinMode(POKECOM_1, OUTPUT);
  pinMode(POKECOM_2, OUTPUT);
  Serial.begin(9600);
}
void loop(){
  /* CdSからの値を整形. 440~840くらいの値を取る(私の環境の場合)ので、それを0~3に直す.*/
  val = (analogRead(SENSOR) - 440)/100;
  /* 1bit目をPOKECOM_1から出力 */
  if(val%2==1){
    digitalWrite(POKECOM_1, HIGH);
  }else{
    digitalWrite(POKECOM_1, LOW);
  }
  /* 2bit目をPOKECOM_2から出力 */
  if(val/2==1){
    digitalWrite(POKECOM_2, HIGH);
  }else{
    digitalWrite(POKECOM_2, LOW);
  }
  delay(100);
}
パラレルといいつつ、あんまりパラレルじゃありません。
bit演算使えよ!といったツッコミも承知です。
部品を揃えたら8bit通信で作り直しますんでしばしお待ちください。

ポケコンのコード
10 WAIT 10
20 OPEN "PIO:"
30 CLS
40 X=0
50 Y=0
60 PIOSET 3
70 V=PIOGET
80 Y=30-Y
90 GCURSOR(X,Y)
110 GPRINT "1000"
120 X=X+1
130 GOTO 70
ポケコンを持っている方はマニュアルも持っているはずなので、そちらを参考にしてください。
4と5のピンをINPUTに設定し(PIOSET 3)、値を読み取ります。
読み取った値をグラフにして液晶に表示します。

Arduino UNOのメモ

自分用メモ.
  • クロックは16MHz
  • スイッチはない。通電したらプログラムが走り続ける。
  • void setup()内で、デバイス(主にpin)の初期化など。
  • リセットボタンを押すと、setupし直す。
  • 通電中はvoid loop()が実行され続ける。
  • 0~13のデジタルIOピン、0~5のアナログINピンを持つ。デジタルIOピンのうち3,5,6,9,10,11はアナログOUTとして使用可能.
  • デジタルIOピンは使い方を初期化する必要がある。
  • 初期化にはpinMode(pin_number, MODE);を使う.
  • MODEはINPUTまたはOUTPUT.
  • アナログINピンは初期化の必要なし.
  • 入出力値のdigital(2値)/analog(連続値)は関数で使い分ける。
  • analogWrite(pin_number, value); digitalRead(pin_number); digitalWrite(pin_number, value); はデジタルIOピンからの入出力.
  • analogRead(pin_number); はアナログINピンからの入力.
  • デジタルIOのvalueはHIGHまたはLOW.
  • 基本的にHIGHは5V、LOWはGND(グランド)0V.
  • アナログ入力は1024段階、アナログ出力は256段階.
  • pin_numberはconst int LED = 7;などとして、defineした値のように使うのがArduino流らしい。
  • delay(ms); ミリセカンド待機.
  • millis(); プログラムがスタートしてからの時間をミリセカンドで取得. 約50日でオーバーフロー.
  • micros(); プログラムがスタートしてからの時間をマイクロセカンドで取得. 約70分でオーバーフロー.
  • Serial.begin(9600); シリアルポートを開く。毎秒9600bit。setup()内で呼ぶ。
  • Serial.println(v); シリアルポートにデータを出力.

2013年5月1日水曜日

Arduinoはじめます。UbuntuにIDEその他インストール

いまさらですが、Arduinoを始めます(なんで自分ってこうアーリーアダプターじゃないんだろうと思いつつ)。
買ったのは、「Arduinoをはじめようキット」


やることその1 JDKをインストール
各所で言及されていますが、Ubuntuの標準でインストールされるJDKだとArduinoのIDEがうまく動作しません。
そこで、OracleのJDKをインストール。
$sudo add-apt-repository ppa:webupd8team/java
$sudo apt-get update
$sudo apt-get install oracle-java7-installer


やることその2 ArduinoをPCに接続
いきなり結線して大丈夫か?という心配そのものがArduinoには不要でした。
付属のUSBケーブルで接続したら、チカチカとLEDが光り出しました。


やることその3 ttyACM0のパーミッションを変更
Arduinoを接続すると
/dev/ttyACM0
というデバイスとして認識されているはずです。
が、デバイスにはスーパーユーザーしかアクセスできないので、このままでは、ArduinoのIDEからttyACM0にアクセスすることができません。
(具体的には、ツール→シリアルポートの項目が選択できない)
そこで、
$sudo chmod a+rw /dev/ttyACM0
として、デバイスへのアクセスを一般ユーザにも開放してやります。
追記:
ただこれ、USBの抜き差しのたびに権限を設定しなおさないといけないので、
コマンドラインから$sudo ./arduinoとしたほうが楽かもしれません。


やることその4 ArduinoのIDEをダウンロード
Arduino - Softwareから、
Linux用のIDEをダウンロード→解凍します。
その中のarduinoというファイルを実行してやるとIDEが起動します。


やることその5 基板上のLEDを点滅
本格的なことは次に回すとして、まずは基板上のLEDを点滅させてみましょう。
int led = 13;

void setup(){
  pinMode(led,OUTPUT);
}

void loop(){
  digitalWrite(led, HIGH);
  delay(1000);
  digitalWrite(led, LOW);
  delay(1000); 
}
IDEで「検証」して確かめてから、「マイコンボードに書き込む」を実行すると、基板上のLEDがそれっぽい間隔で点滅するはずです。


参考にしたページ:
Ubuntu 12.04 LTSにOracle JDK7をインストールする - Starlight -Little Programmer’s Diary-
ArduinoをUbuntuで動かす方法: 綺麗なコードが良い!ぶろぐ
UbuntuでUSBシリアルを認識してるのに、Arduino... - Arduino - FriendFeed
Blink