るいもの戯れ言
#257
2017/01/24 23:58

今日も来た。コメントformのURLを見ているわけではないようだ。

今度は、フォーム内のフィールドのname属性を変更してみた。あと承認待ちのコメントに名前を出さないようにしてみた。

コメントを書き込む
#227
2017/01/24 10:27

GTK(Cairo)での、線、矩形描画が思ったようにいかずに、少し悩んだのでメモ。

例えば以下のパラメータで矩形を描画した場合:

  • 始点: 2, 2
  • 終点: 8, 6
  • 線の太さ: 2
  • 線の色: 青
  • フィルの色: 赤

以下のように描画されるようだ。

線の太さは直径なので、2ピクセル未満とすると、サブピクセルになってしまうので、きっちりドットバイドットで表示したければ、最低でも2を指定する必要がある。また、GTKに再描画を依頼する場合、領域の指定は(2, 2) - (8, 6)にすると線の外周部分が漏れてしまうので、線の太さを考慮して、(1, 1) - (9, 7)を指定する必要がある。完全に1ピクセルの線を引きたかったら、線の太さを0にした矩形を描画した方が良さそう。

コメントを書き込む

作品15の2番目も、3部構成になっているが、今度はどちらも長調になっている。第3、4曲での極端なやり方に反省して、幻想的な雰囲気を壊さないように配慮したのかもしれない。

しかし、中間部の右手の多声進行は複雑だ。

エキエル版での謎の箇所として、まず出だし

そして中間部が終わった後の出だし。この2箇所でスラーのかかり方が微妙に違う。

これが意図したものなのか、単なる間違いなのか良く分からない。ちなみに全音だと、どの箇所も以下になっている。

みんな違うし ^^;

今回の演奏では、エキエル版の最初の出だしのスラーのかかり方を正としている。

楽譜引用は、エキエル版と全音版。

コメントを書き込む
#225
2017/01/24 04:01

コメント・スパムが来ているが、コメントは承認が必要なので全部削除。ただ、削除も面倒なので何か対策ができるか試し中。

とりあえずIPのblacklistをnginxに入れてみたが効果無し。これまでコメントスパムって、WordPressとかの有名どころのアプリケーションを使っているところに、機械的に投げ込んでいるのだと思っていたのだけど、うちのようにフルスクラッチのところにも来るというのは、どういう仕組みなのだろう。formのURLがpost comment的なところを見つけて送っているんだろうか。まさか人力でもあるまいし。

というわけでコメント投稿のURLを変更してみた。Play!だとroutesを変えるだけで良いのでラクチンだ。

コメントを書き込む

作品15も、9に引き続き3つで構成されている。作品15の最初の曲となるこの曲は、第3曲と同じで前後を穏かな長調、中間部を激しい短調のパッセージという形式になっている。ただ第3曲よりも洗練されて、全体に簡潔にまとめらている。

中間部、第2小節はペダルを踏む指示があるけど、スタカーティシモ。どのくらいペダルを踏み込むのかが演奏者に任されている感じ。

楽譜引用はエキエル版

コメントを書き込む

Rustで、Multimap(1つのキーに複数の値を登録できるMap)を実装してみる。後でキーをソート順の取り出したいので、BtreeMapを使って、値にVecを入れる。


use std::collections::BTreeMap;

struct Bag {
    map: BTreeMap<i32, Vec<i32>>
}

impl Bag {
    fn add(&mut self, key: i32, value: i32) {
        match self.map.get_mut(&key) {
            None => {
                self.map.insert(key, vec!(value));
            },
            Some(vec) => vec.push(value)
        }
    }
}

でも、これはコンパイルが通らない。


error[E0499]: cannot borrow `self.map` as mutable more than once at a time
  --> /Users/shanai/.emacs.d/rust-playground/at-2017-01-22-230525/snippet.rs:32:17
   |
30 |         match self.map.get_mut(&key) {
   |               -------- first mutable borrow occurs here
31 |             None => {
32 |                 self.map.insert(key, vec!(value));
   |                 ^^^^^^^^ second mutable borrow occurs here
...
35 |         }
   |         - first borrow ends here

mutable borrowが2回起きているという警告。まずここでself.mapに対するmutable参照を取得している。


        match self.map.get_mut(&key) {

で、このmatchが終了する前に、ここでもう一度self.mapに対するmutable参照を取得している(insertは、mutableなselfが必要)。


            None => {
                self.map.insert(key, vec!(value));
            },

つまりmutableな参照が、2つ存在してしまう瞬間がある。これはRustでは禁止されている。以下がエラーになるのと同じ。


    let mut i = 0;
    let pi = &mut i;
    let pi2 = &mut i;

スコープを分けて、mutable参照が2つ同時に存在する瞬間を無くしてしまえば良い。


impl Bag {
    fn add(&mut self, key: i32, value: i32) {
        {
            match self.map.get_mut(&key) {
                None => {},
                Some(vec) => {
                    vec.push(value);
                    return
                }
            }
        }
        
        self.map.insert(key, vec!(value));
    }
}

でも、BtreeMapにはentry()という素敵なメソッドがあるので、これを使うのが簡単。


impl Bag {
    fn add(&mut self, key: i32, value: i32) {
        self.map.entry(key).or_insert(vec!()).push(value)
    }
}

entryはキーを引数に取り、Entryというenumを返す。指定されたキーが無ければVacant、あればOccupiedが返る。or_insert()を呼び出すと、Vacantの場合は、引数に指定された値をmapに格納した上で、その値へのmutable参照を返す。Ocupiedの場合は、値へのmutable参照を返す。なので上のように書けば目的が達成できる。


fn main() {
    let mut bag = Bag { map: BTreeMap::new() };
    bag.add(1, 10);
    bag.add(1, 20);
    bag.add(2, 100);
    println!("map = {:?}", bag.map);
}


map = {1: [10, 20], 2: [100]}

コメントを書き込む

DrawingAreaは、デフォルトではマウスのイベントを拾えないようだ。発生するイベントの種類を増やすには、WidgetExt::add_event()というメソッドを使うらしい。


    let drawingArea: gtk::DrawingArea = builder.get_object("drawingarea1").unwrap();
    drawingArea.set_size_request(300, 300);
    drawingArea.add_events(gdk_sys::GDK_BUTTON_PRESS_MASK.bits() as i32);
    drawingArea.add_events(gdk_sys::GDK_BUTTON_RELEASE_MASK.bits() as i32);

JavaのSwingだと、clickedというイベントで同じ場所でマウスのボタンが押されて離されたという状態を取得できたが、どうやらGTK+には、そういうのが無いようだ。自分でPRESSの時の座標を覚えておいて、RELEASEの座標が同じだったらクリックと判定する必要がある模様。イベントハンドラの登録は、WidgetExt::connect_event()を使う。


        drawingArea.connect_event(|_, e| {
            let clone = e.clone();
            match e.get_event_type() {
                EventType::ButtonPress => {
                    let res: Result<EventButton, Event> = clone.downcast();
                    println!("pressed: {:?}", res.unwrap().get_position());
                },
                EventType::ButtonRelease => {
                    let res: Result<EventButton, Event> = clone.downcast();
                    println!("released: {:?}", res.unwrap().get_position());
                },
                _ => {}
            }
            return Inhibit(false);
        });

Event型で来るため、特定のイベントであるかをEventTypeで判定して、ダウンキャストするというなんとも汚ないコード。なぜイベント自体をenumにしなかったのかな。GTK+との関係で難しいのだろうか。

クロージャの引数は&Eventで、downcastの引数は、&selfではなくてselfそのものなので、そのままイベントを渡すとmoveが起きてしまってコンパイルエラーになる。なので、一度clone()している。

コードサンプルは、こちら

コメントを書き込む
ショパン・ノクターン第3曲 作品9-3

作品9は3つのノクターンで構成されていて、第3曲が最後。ショパンとしては、この曲は意欲作だったのではないかと思う。ショパンのノクターンは3部構成になっているものが多いけど、この曲は、その中間部の雰囲気をがらりと変えて大きな効果を得ることを狙ったのだと思う。ただ、残念ながら一般には作品9の中での知名度は最も低くなってしまった。まぁ普通の人からすると、え、何、なんでノクターンなのに、こんなに激しいの? みたいな違和感が大きかったんだろうな。まぁ、こんな具合に作曲者が空回りしてしまうケースというのは多い。

この曲は、スタカーティシモが多用されている。面白いのはタイの後の音にスタカーティシモが付いているケースがあって、これ、どう表現するか悩みどころ。実際の演奏会なら大げさに右手を跳ね上げたりするんだろうか。

中間部。Agitatoでガンガン行く。ショパン的には「どうだ!」って感じだったのだろうな。

最後のアルペジオは、ものすごく綺麗だけど、これppで弾くのすごく難しそうで禿げそう。

楽譜引用はエキエル版

コメントを書き込む

今回のお題は、2値(白黒)+alpha(透過付き)のgifファイルを読み込み、その中の黒を赤として描画したいというもの。要は「選択状態」の描画を赤色でしたい。だけど元の画像ファイルは2値(白黒)だよ。という状況。

Pixbufは、2値の画像読み込んだ場合でも、中ではRGBAそれぞれに8bitを割り当てているようだ。なので、ピクセル操作は思ったより簡単だった。


fn to_red(pixbuf: &Pixbuf) {
    assert!(
        pixbuf.get_colorspace() == gdk_pixbuf_sys::GDK_COLORSPACE_RGB,
        "Unsupported color space: {}", pixbuf.get_colorspace()
    );
    assert!(pixbuf.get_has_alpha(), "This image does not have alpha channel");
    let n_channels = pixbuf.get_n_channels();
    let w = pixbuf.get_width();
    let h = pixbuf.get_height();
    let rowstride = pixbuf.get_rowstride();
    let mut buf: &mut [u8] = unsafe {
        pixbuf.get_pixels()
    };

    for y in 0..h {
        for x in 0..w {
            let offset = (y * rowstride + x * n_channels) as usize;
            let r = buf[offset];
            let g = buf[offset + 1];
            let b = buf[offset + 2];
            let a = buf[offset + 3];
            if r == 0 && g == 0 && b == 0 {
                buf[offset] = 255;
            }
        }
    }
}

一応colorspaceと、alpha付きであることをチェック。


    assert!(
        pixbuf.get_colorspace() == gdk_pixbuf_sys::GDK_COLORSPACE_RGB,
        "Unsupported color space: {}", pixbuf.get_colorspace()
    );
    assert!(pixbuf.get_has_alpha(), "This image does not have alpha channel");

あと、注意としてパディングされている可能性があるので、rowstrideにyを掛けてやる必要があるようだ。get_pixelsは、&mut [u8]が返ってくるので、大胆にもそのまま中をいじれてしまう。RGB全てが0(黒)なら、Rだけ255にしてやる。


    for y in 0..h {
        for x in 0..w {
            let offset = (y * rowstride + x * n_channels) as usize;
            let r = buf[offset];
            let g = buf[offset + 1];
            let b = buf[offset + 2];
            let a = buf[offset + 3];
            if r == 0 && g == 0 && b == 0 {
                buf[offset] = 255;
            }
        }
    }

コードは、GuiHubに置いておいた。

コメントを書き込む
#199
2017/01/21 10:57

Rustで以下のようなJavaのenumを実現したい。


public enum SharpFlat {
    SHARP(1), DOUBLE_SHARP(2), NATURAL(0), NULL(0), FLAT(-1), DOUBLE_FLAT(-2);

    private final byte offset;

    private SharpFlat(int offset) {
        this.offset = (byte)offset;
    }

    public int getOffset() {
        return offset;
    }
}

RustのenumはJavaとかのenumとは考え方が違って直和型の定義のようだ

structで定義してもいいけど、enumになっていればexhaustive checkされるので、やはりenumの方がありがたい。試してみると、enumもtraitを実装できるので、以下のようにすることで解決できた。


enum SharpFlat {
    SHARP,
    FLAT,
}

trait HasOffset {
    fn offset(&self) -> i8;
}

impl HasOffset for SharpFlat {
    fn offset(&self) -> i8 {
        match *self {
            SharpFlat::SHARP => 1,
            SharpFlat::FLAT => -1,
        }
    }
}

fn foo(sharp_flat: Option<SharpFlat>) {
    match sharp_flat {
        None => {
            println!("none");
        },
        Some(SharpFlat::SHARP) => {
            println!("sharp {}", SharpFlat::SHARP.offset());
        },
        Some(SharpFlat::FLAT) => {
            println!("flat");
        },
    }
}

fn main() {
    foo(Some(SharpFlat::SHARP));
}

コメントを書き込む
1 / 5