近々移行します
ものっすごい久しぶりの更新ですが、近々このDirect2D 入門のブログを移転します。
というのも、この手のものを(外に公開するしない問わず)書くようなサービスを自分でつくったので、そちら側に移転したいと思っています。
まだ、デバッグが終わりきっていないのでもう少し先になりますが、移転完了後にはまたお知らせの記事を書きます。
また、移転後数ヶ月の期間をみて、こちら側は閉鎖させて頂きますので、よろしくお願いいたします。
ものっすごい久しぶりの更新ですが、近々このDirect2D 入門のブログを移転します。
というのも、この手のものを(外に公開するしない問わず)書くようなサービスを自分でつくったので、そちら側に移転したいと思っています。
まだ、デバッグが終わりきっていないのでもう少し先になりますが、移転完了後にはまたお知らせの記事を書きます。
また、移転後数ヶ月の期間をみて、こちら側は閉鎖させて頂きますので、よろしくお願いいたします。
しばらく更新しなくてごめんなさい
理由は、ちょっと別のプログラミングに興味を持ってやっていたというのと
冬はスノボーの季節なので土日が全部空いてないという残酷な状況だったからです
雪が無くなったら、ブログの更新を再開します
すっごくかわいい女の子の友達がいるんですよ。僕がまだ小さな時知り合った子です。
なんだかんだ、恋愛関係になることもなく、単に腐れ縁友達となって、今でも年に数回、二人で呑むことがあるんです。
しかしついこの間、呑みに行った帰りに、その子が、もっと遊ぼうって言ったんです。いつもなら呑んですぐに帰るんですけど。
僕が、どうしたの?って聞くと、今日は帰りたくないって、なんかドラマみたいな台詞を言うじゃないですか。
僕ももうドキドキで、ハアハアですよ。じ、時間も遅いし、ほ……ホテルでも行こうか、あ、ああもちろん何にもしないから!!
と言ったら、うん。って言うんですよ。ああ、やべーきたよこれキタ。とか思ってホテルにいく設定の夢を見ました。
今回は、前回の続き、ID2D1PathGeometryのAdd系関数についてみていこうと思います。
Add系関数は以下のようなものがありました。
/*円弧セグメントを追加する*/ AddArc /*ペジェ曲線を追加する*/ AddBezier /*ペジェ曲線(の配列)セグメントを追加する*/ AddBeziers /*簡易ペジェ曲線セグメントを追加する*/ AddQuadraticBezier /*簡易ペジェ曲線(の配列)セグメントを追加する*/ AddQuadraticBeziers /*直線セグメントを追加する*/ AddLine /*直線(の配列)セグメントを追加する*/ AddLines
このうち、後ろに「s」がついているやつは、一気にもそっと追加するバージョンなだけなので、はしょります。
ということで、覚えるべきAdd系関数はAddArc,AddBezier,AddQuadraticBezier,AddLineの4つです。
まずはAddLineについて。
void AddLine(D2D1_POINT_2F point);
言うことないですね。見たまんまです。
次にAddQuadraticBezierです。二次ペジェ曲線。
こいつは、簡単な曲線を書くためのものです。
void AddQuadraticBezier( const D2D1_QUADRATIC_BEZIER_SEGMENT &bezier );
D2D1_QUADRATIC_BEZIER_SEGMENTを引数にとっています。これがどうなっているかというと、
struct D2D1_QUADRATIC_BEZIER_SEGMENT { D2D1_POINT_2F point1; D2D1_POINT_2F point2; };
2つの点を取るだけです。
point1はコントロールポイント、いわゆる制御点というやつです。
point2は、エンドポイント、すなわち最終的に線が到達する場所、点です。
直前の点から、point2に指定した点まで線を引くが、point1の影響を受けて曲線になる、と言ったらわかりやすいでしょうか。
イメージとしては、直前の点から、point2までの線が、point1に指定したコントロールポイントに引っ張られたような曲線になるって感じです。
数学的な説明については、ここでかくのもあれだしネット上にたくさんの説明があるのでそちらを参照してください。
http://koujinz.cocolog-nifty.com/blog/2009/05/post-fd85.htmlなんかがわかりやすいです。
お次は、AddBezierです。三次ペジェ曲線。
これは、AddQuadraticBezierをもう少し複雑にしたもの、コントロールポイントが1つ増えたものです。
void AddBezier( const D2D1_BEZIER_SEGMENT &bezier );
形としてはAddQuadraticBezierとまったく同じですね。とる引数が違うだけです。
D2D1_BEZIER_SEGMENTがどうなっているかというと、想像通り……
struct D2D1_BEZIER_SEGMENT { D2D1_POINT_2F point1; D2D1_POINT_2F point2; D2D1_POINT_2F point3; };
点が1つ増えているだけです。
point1とpoint2がコントロールポイントとなり、point3をエンドポイントとした曲線を引きます。
イメージとしてもAddQuadraticBezierの時と同じで、直前の点とpoint3を結ぶ線を引くが、point1およびpoint2の方向に引っ張られたような曲線を描くことになります。
フォトショップやイラストレータを知っている人だったら、いわゆる「パスツール」である、といったらわかりやすいでしょうか。
上記したリンクに三次ペジェ曲線の数学的な解説も載っているので、一度目を通してみることをお勧めします。
(難しいものではないので、原理だけでも知っておけば、利用の手助けになると思います)
さて、最後にAddArcです。
これも曲線の一種です。名前からしてわかるとおり弧を描くためのものですが、これは今までのAdd系関数に比べて、ちょっとクセがあります。
もっとも基本的な動作としては、直前の点と、指定した点(エンドポイント)を直径とする半円が描かれます。
ただ、その他にもいろいろとパラメータをとることができ、この使い方にちょっとしたコツがいります。
まあとりあえず、プロトタイプを見てみましょう。
void AddArc( const D2D1_ARC_SEGMENT &arc );
AddArc自体は、他の関数と同じ、単純に対応する値を一つとるだけです。
D2D1_ARC_SEGMENTについてみてみましょう。
struct D2D1_ARC_SEGMENT { D2D1_POINT_2F point; D2D1_SIZE_F size; FLOAT rotationAngle; D2D1_SWEEP_DIRECTION sweepDirection; D2D1_ARC_SIZE arcSize; };
パラメータがいっぱいあります。眩暈がします。覚えたくないです。もうだめです。助けて……
しかし誰も助けてくれないので、理解するしかありません。だるい。
pointについては、まあわかると思いますが、エンドポイントです。最終的にこの点に線が到達します。
sizeは、xとyの半径を指定することができます。この値はちょっとクセがあります。
通常、直前の点からpointまでを直径とする正円を半分にぶった切った半円になるのですが、sizeに指定するx方向半径とy方向半径を調節することで、楕円っぽくすることができます。
しかしちょっと待った、直前の点と、pointで指定したエンドポイントが決められている以上、それら2つの点を必ず通過するような円にならなければなりません。
そうすっとsizeに指定するxとy(つまりx方向半径と、y方向半径)はどういう扱いになるのか?
これは、本当の半径以上の値を指定すれば、その値どおりになり、本当の半径以下の値を指定すると、比率になります。(みたいです。実験してわかったことです。)
つまり、例えば直前の点とエンドポイントの長さが100ある時、1,1を指定すると、直径を100とする正円を規則正しく半分にぶった切った半円が描かれることになります。
1,2を指定すれば、縦長になった楕円になります。
ちょっとわかりづらいです。(慣れる意味もこめて、少々値をいじくって実験してみるとよいです)
rotationAngleは、楕円の時に効果を発揮します。正円の時には効果は発揮しないようです。
楕円の時これを指定すると、時計回り方向に円がぐにゃってなります。ぐにゃって何?って言われても……
イメージとしては、風で飛ばされそうな風船みたいになります。
sweepDirectionは、半円の方向です。時計回りに書くか、反時計回りに書くか。
D2D1_SWEEP_DIRECTIONはenumで、以下の値をとり得ます。
typedef enum { D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE, D2D1_SWEEP_DIRECTION_CLOCKWISE } D2D1_SWEEP_DIRECTION;
まあ、見れば明らかですが、D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISEは反時計回り、D2D1_SWEEP_DIRECTION_CLOCKWISEは時計回りです。
例えば、x軸に水平になるように直前の点とエンドポイントをとり、かつ直前の点のほうがエンドポイントよりxの値が小さい場合(つまり左から右に線を引く場合)場合、半円を書くとするなら上のほうに弧ができるように書くか、下のほうに弧ができるように書くか、2通りの書き方ができると思います。
上側に弧ができるように書きたいのなら、時計回りに弧を描きますよね。逆にした側に弧ができるように書きたいのなら、反時計回りです。そういうことです。
そして最後に、arcSizeについてですが、こいつはD2D1_ARC_SIZE_SMALL, D2D1_ARC_SIZE_LARGEをとり得ます。
コレが何の意味があるかということですが、まず前提として、どうやらこれはsizeで指定した値が比率ではないとき、つまり実際の半径より大きい値を指定したときに効果があるようです。
そして、sizeの指定により楕円(正円含む)になった状態に対して効果があります。
ここまでの解説では、半径ありきで考えてきましたが、その考えを捨て去ってください。
端的に説明してしまうと、直前に指定した点と、今回指定したpointを結ぶ線、この線分よりもでっかい直径をもつ円の円周に両端が接するように、ぽこっと入れたとすれば、その線分で2分割される円の片割れは、ちいさいのとでっかいの2つができますよね?
まるいホールケーキを半分に切る時に直径で切れば、きれいに真っ二つになって美しいですが、俺のほうがいっぱい食べたいから直径からわざとずらして、彼女のぶんのケーキの量を減らすことに似ています。

このとき、でっかいほうのケーキを要求するのがD2D1_ARC_SIZE_LARGEで、小さいほうのケーキで我慢するのがD2D1_ARC_SIZE_SMALLです。
ま、僕は甘いものなんて好きじゃないから、小さいほうでいいよ。っていって彼女に大きいほうを上げて、喜ばせて、だけど彼女はケーキを食べ切れなくて、余ったケーキを、しょうがないなあ、といいながら食べてあげて、結局僕のほうがいっぱいケーキ食べることになるってことはあらかじめ計算していてあえて大きいほうをあげておき、さらに彼女は僕のさりげない(計算された)優しさを知ることになりそのあとふたりは(ry
というのが、arcSizeとなります。
Add系関数は、概念としては非常に簡単で、ただ点を追加していくだけ、ただその途中にどういうふうに線を曲線とするか、という程度のものなので、いくつか図形をつくって描画してみて、感覚をつかんでみてください。
次回は、PathGeometry(GeometrySink)を使う上での、いくらかの細かいフラグ関係についてみてみます。
ええ。雑記です。
Direct2Dとはなんの関係もありません。
Direct2D入門ってサイトなのに、それはどうよ?
そう思うかもしれません。しかし書かずにはいられませんでした。
今は反省していません。
ASIO(Audio Streaming Input Output)でオーディオを低レイテンシで入力するプログラムを趣味で作ろうと思ってASIO SDKをダウンロードしてきて、SDKはコードが全部あるのでざっと読んだのですが、これちょっとひどいですね。
ASIO対応のオーディオインターフェイスはそこそこのお値段しますが、その中でも、まあそこそこな機器を持ってるですよ。昔Cubase SXでDTMやろうと興奮気味に買ったのですが、3曲くらいつくって飽きて、いまや押入れに眠るようになってしまった幻の機器が。
ひさしぶりに(エロ本を探すため)押し入れをあさっていたら発見して、これはもったいない、ちょっとこいつで遊んでみようとおもって手を出したのです。
WindowsにおけるASIOプログラミングは、SDKの内部的にはレジストリにあるASIOドライバのエントリを列挙して、そこにかいてあるCLSIDからCOMで提供されているASIOドライバへアクセスするインターフェイスをロードする形になってるみたいですが、SDKにおけるその使い方というかライブラリがひでえ。
コードは簡素で読みやすいのですが、お行儀が悪すぎる。
グローバル変数つかいまくりんぐなのはまだよしとしても、newしたオブジェクトをdeleteしないとか、クラスのコンストラクタでCOMの初期化を勝手にするとか。
ドライバロードするのに、SDK中のcppにある関数のプロトタイプがヘッダに書いてないから、自分で使いたい場面でプロトタイプ宣言して使うとか、グローバル変数をexternして使うとか。
マルチスレッドのことまったく考慮してないのはいいにしてもCOMが内部に隠蔽された状態で勝手にSTAで初期化されるから、ASIO SDKをスレッドセーフに使ったとしてもCOM的に不正になるとか。
それ以外にもコードを追っていくと判明する数々の柔軟性の無さにファーーーーーーーック!!!って叫んでしまいそうになります
C言語とC++が絶妙に混ざり合ったなんともいえない仕上がりになっています。まあ、そもそもこのSDKってサポートすらしないってものらしいので、ようはSDKという名のサンプルコードなんでしょうね。
じゃあ最初からサンプルコードってことで公開しろよ……
かといってASIO自体は魅力的なので、しょうがないからSDKをまるごと作り直しています。
本当ならこんなめんどくさいことはしたくないのですが、どうしょうもないから、やるしかありません。
理不尽なことはやめて、C++ベースにして、newしたらdeleteするようにして(笑)、マルチスレッドでもCOMを扱えるようにして……
まるごと作り直しです。修正というレベルではなく、完全に作り直し。
普通SDKと名乗るならこれくらいやっておいてくれてもいいのにさ。
ASIOドライバを使ってるソフトウェアを作ってる会社(や人)は、みんなつくってるんだろうな……
まあ救いはASIOドライバの低レベルなインターフェイスはいたってシンプルなことです。
それでもめんどくさい……
でもまあ、そこも含めて、人があまりやらないニッチなジャンルに手を出すときの醍醐味かなとおもって納得しています
なんにせよ、初めてやる分野については、オラ、わくわくすっぞ!
なので、楽しいです。
でもめんどくさい……