AdMob + mediation の設定の話

 AdMob Mediation を iOS プロジェクトに導入しようと作業を行なっていましたが、手近なドキュメントをそのまま信じて随分と時間がかかってしまいました。

  1. Mediation(英語)https://developers.google.com/mobile-ads-sdk/docs/admob/mediation#ios
  2. AdMob(日本語)https://developers.google.com/mobile-ads-sdk/docs/ios/fundamentals?hl=ja
  3. AdMob(英語)https://developers.google.com/mobile-ads-sdk/docs/

 Mediation も AdMob もどちらも Google の製品でありまして、Mediation を導入すると AdMob も自動的に付いてくるようになっています。Mediation ID を設定すれば Mediation に、AdMob の広告 ID を設定すれば AdMob のみの広告となるようです。

 それで、上記リンクの1つめを信じて設定を行なっていたのですが、どうしても

Undefined symbols for architecture armv7: 
"_SKStoreProductParameterITunesItemIdentifier", referenced from: 
-[GADOpener openInAppStore:fallbackURLString:] in libGoogleAdMobAds.a(GADOpener.o) 
"_OBJC_CLASS_$_SKStoreProductViewController", referenced from: 
objc-class-ref in libGoogleAdMobAds.a(GADOpener.o)
 "_OBJC_CLASS_$_ASIdentifierManager", referenced from: 
objc-class-ref in libGoogleAdMobAds.a(GADIdentifierUtilities.o) 
objc-class-ref in libGoogleAdMobAds.a(GADGestureIdUtil.o)
 ld: symbol(s) not found for architecture armv7 
clang: error: linker command failed with exit code 1 (use -v to see invocation)

 というエラーが出てしまいます。

 ドキュメントとにらめっこしていましたが、解決せず。サイトを色々検索してやっと、framework が足りなさそう、という情報を得ました。そして英語版の AdMob のサイトに行ってみると、framework は

  • AudioToolbox
  • MessageUI
  • SystemConfiguration
  • CoreGraphics
  • AdSupport
  • StoreKit

を追加しましょうと書いてありました。後ろの2つのフレームワークはリンク1. と 2. では抜けていました。おいおい公式ドキュメント、と思いながら framework を追加するとビルドが通りました。やれやれ。

 他に忘れそうなことをついでに書いておくと

  • Build Setting > Other Linker Flag への “-ObjC” の追加
  • libGoogleAdMobAds.a は Link Binary With Libraries に追加(フレームワークのところ)
  • AdMob SDK version 6.2 から “-all_load” フラグは不要になった
  • armv7s(つまり iOS6 以上相当)に対応しているのは SDK version6.2 から
    (それより前の SDK を使用する場合はプロジェクト設定を修正する必要がある)

というところ(参考:http://adsense-ja.blogspot.jp/2012/10/ios-6-iphone-5-admob-sdk-62.html)。古い SDK だと UUID 取得処理のカンケイなどで、Apple Store のリジェクト理由にもなるそうです。なるべく新しい SDK を使用しましょう。

<追記>

 テスト用の表示については GADRequest の .testDevices に対して設定をするそうで、こちらも試してみました。しかし、Mediation では全く表示されません(AdMob の ID だとテスト用の広告が表示される)。

 どうなってんの?ということで調べてみると、公式掲示板(?)で情報発見。

https://groups.google.com/forum/#!topic/google-admob-ads-sdk/fq9VrToLeKs
「Mediation だとテスト用の広告が表示されないんだけど」
「返答遅れました。こちらの AdMob のプラグインの不備です。」
(2週間経過)
「その後、いかがでしょうか。」
「あいにく (Unfortunately) 最新版には反映できませんでした。次のリリースでは必ず直しますわ。」

  おいおい。

カテゴリー: tech | コメントする

PHP 設定 to OSX-Lion

 仕事で PHP をインストールというか設定したのですが、細かいところで詰まったのでその工程をメモしておこうと思います。細かい設定ややりかたなどは参考サイトなどを見てください。MySQL は入れてません。

 Lion には PHP も Apache も始めっから入っているのでインストールは必要なく、設定を行えば PHP は使えるようになります。

 XAMPP で一括でインストールするのも考えましたが、MAC バージョンは最終更新日が2010年だったのでやめました。MAMP というパッケージ型のものもあります。こちらの最終更新日は1週間ほど前だったのでこっちでもよかったかな。こちらは調べてません。とりあえず以下ではインストール無しで設定だけで PHP を動かす設定ということで。

 

=== root ユーザー ===

 これからの設定には権限の関係で root ユーザーというもので作業をする必要があります。すなわち一番権限が強いユーザー。root ユーザーでなければ一部のファイルの書換えができませんでした。Lion だけかな?あまり他のサイトには root でログインすべし、というのはなかったのですが。

 root ユーザーの設定とログインについては以下のアップルのページ参照。
http://support.apple.com/kb/HT1528?viewlocale=ja_JP

 

=== PHP 設定 ===

 root ユーザーでログインしてターミナルを起動。

sudo vi /etc/apache2/httpd.conf

と入力、パスワードを聞かれたらログインしているユーザーの、つまり root ユーザーのパスワードを叩いて return。(画面上に入力した文字は表示されません。)

 このコマンドでは vi というエディタを起動しているのですが、とりあえず
ESC : コマンドモードに切替え
コマンドモード時に i 入力 : insert モードに切替え
の2つを把握すべし。

 insert モードに切替え、矢印キーで下の方に移動していって(ホイールスクロールだと下に行ききらないので矢印キーで)、

#LoadModule php5_module libexec/apache2/libphp5.so

の行を探し、先頭の # を削除。ESC を押し、:wq! と入力(強制的に write + quit)。ここで root ユーザーでない場合には readonly なファイルで書換えはできません、と怒られました。

 もし入力中に間違ったりしたら ESC を押し、:q! と入力(変更を保存せずに終了)。間違った書き込みを保存してしまった、という場合でも httpd.conf ファイルの横に httpd.conf.default というファイルが置いてあるのでおそらくこれを利用すれば元に戻せると思われます(未確認)。

 無事編集して保存できたら root からログアウトし、PHP を使うユーザーに切り替えておきましょう。

 

=== Apache 設定 ===

  システム環境設定 –> 共有 –> Web 共有にチェック、で Apache が起動します。止めるときはチェックを外せば OK。かんた〜ん。

 この画面で「パーソナル Web サイトフォルダを作成」のボタンを押すと、ホームに「サイト」という名前のフォルダが作成され、このなかに index.html などが配置されます。このサイトフォルダが各ユーザーの localhost なURLで(なんという曖昧表現…)表示される場所になるようです。つまり http://localhost/~username/index.html
のアドレスですね。

 では index.html ファイルを同じ場所にコピーし、index.php と名前を変更。mi などのテキストエディタでこれを開き、中身を全て消去し、

<?php phpinfo(); ?>

と入力して保存。

 Apache を起動した状態で
http://localhost/~username/index.php
をブラウザで開いて PHP の状態が表示されたら OK ということになります。

 

 localhost/~username は各ユーザーのサイトフォルダになっていますが、これを変えるには httpd.conf ファイルの DocumentRoot の項目を変更するらしいのですが、とりあえず PHP が動いたのでここまでとします。

カテゴリー: tech | コメントする

実数だけを入力させる UITextField

というものを作る必要があって、コードを書いていたのですが結構面倒でした。数字だけなら良かったのですが、以下の様な条件がありました。

  • マイナスの値も入力可
  • 値は整数ではなく実数

 これだけみると大したことは無さそうですが、意外と面倒なんです。条件を整理すると以下のようになります。

  • マイナス記号は先頭だけに入力
  • 小数点が一番後ろについていたらゼロを付加
  • 小数点が先頭だったらその前にゼロを付加(マイナス記号がついても同様)
  • 小数点は複数入力させてはならない
  • マイナス記号は複数入力させてはならない

 最後の2つは厄介。なぜならキーボード入力のみであれば1文字1文字をチェックすればいいのですが、copy & paste という入力があるからです。

 マイナス記号を入力したかったのでキーボードのタイプは UIKeyboardTypeNumbersAndPunctuation の一択。

 UITextField の入力内容を修正するタイミングとしては2つ。ユーザーが入力を行ったとき、それから入力が終了した時。それぞれ以下のメソッド(セレクタ)を使います。delegate とかそういう話は他のサイトやなんかで拾ってきてください。

// 入力時
/**
 * @param textField 変更前の状態の textField
 * @param range     新しい文字列(string)が挿入される位置
 * @param string    range の位置に挿入される文字列
 *
 * @return    新しい文字列を挿入して良いか
 */
- (BOOL)textField:(UITextField *)textField 
    shouldChangeCharactersInRange:(NSRange)range 
                replacementString:(NSString *)string;

// 入力終了時
- (void)textFieldDidEndEditing:(UITextField *)textField;

// ついでに入力開始時
- (void)textFieldDidBeginEditing:(UITextField *)textField

  始めは正規表現が面倒で、if 文を書きなぐっていたのですが、やっぱり条件が複雑すぎて混沌としてしまいました。というわけで正規表現利用に切り替え。

 参考サイト:
http://stackoverflow.com/questions/1434568/how-to-verify-input-in-uitextfield-i-e-numeric-input
http://regexlib.com/DisplayPatterns.aspx?cattabindex=2&categoryId=3(正規表現全般)

 方針としては以下のようになりました。

  • 入力時に数字と記号類の並びは整える
  • 入力終了時に整数部先頭のゼロ連続を除去する(ex: 0012 → 12)
  • 入力終了時に入力値がゼロ度の場合は0.0に
  • 入力終了時に入力値が + – . 入力なしの場合は0.0に
  • -. は -0. に修正、+. は +0. に修正(ex: -.23 → -0.23)

 実際のコードを見てみましょう。まずは入力時。

- (BOOL)textField:(UITextField *)textField 
    shouldChangeCharactersInRange:(NSRange)range 
                replacementString:(NSString *)string
{
    // 入力後の文字列
    NSString *newString =
        [textField.text
            stringByReplacingCharactersInRange:range
                                    withString:string];

    NSString *expression = @"^[-+]?([0-9]*)?(\\.)?([0-9]*)?$";
    NSError *error = nil;

    NSRegularExpression *regex =
        [NSRegularExpression
            regularExpressionWithPattern:expression
            options:NSRegularExpressionCaseInsensitive 
            error:&error];

    NSUInteger numberOfMatches =
        [regex numberOfMatchesInString:newString
            options:0
            range:NSMakeRange(0, [newString length])];

    if (numberOfMatches == 0) return NO;

    return YES;
}

  正規表現の ^[-+]?([0-9]*)?(\\.)?([0-9]*)?$ について説明してみると、

  •  ^ : 先頭に
  • [-+]? : マイナスかプラスの符号が0個もしくは1個あり、
  • ([0-9]*)? : 続いて「数字が連続するカタマリ」が0個もしくは1個あり、
  • (\\.)? : 続いて小数点が0個もしくは1個あり、
  • ([0-9]*)? : 続いて「数字が連続するカタマリ」が0個もしくは1個あり、
  • $ : 最後に達する

となります。入力後の文字列がこの条件にあう文章なのであれば、入力してもよいよ(=return YES) となります。

 これでおおよそは整いました。少し困るのは00123のように整数部の先頭にゼロが付く場合、それから56.00のように小数部の末尾にゼロが付く場合。後者に関しては今回はよしとしました。正確性の明示という意味では悪くないという判断。めんどくさかったわけではありません。

 整数部の先頭のゼロは入力終了時に修正することにして、コードは以下のようになりました。ちなみに角度入力だったので入力開始時に角度記号の除去、入力終了時に角度記号の付加を行なっています。

// 入力開始時のメソッド
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    // 角度記号の除去
    textField.text =
        [textField.text substringToIndex:textField.text.length - 1];
}

// 入力終了時メソッド
- (void)textFieldDidEndEditing:(UITextField *)textField
{   
    // 先頭のゼロの連続を除去
    // (整数部がゼロで少数がある場合は下流でゼロ付加)
    NSString *expression = @"^[-+]?(0+)";
    NSError *error = nil;

    NSRegularExpression *regex =
        [NSRegularExpression
            regularExpressionWithPattern:expression
            options:NSRegularExpressionCaseInsensitive
            error:&error];

    if (error) NSLog(@"error:%@", error);
    NSArray *list =
            [regex matchesInString:textField.text
                   options:0
                   range:NSMakeRange(0, textField.text.length)];

    if ([list count] > 0)
    {
        NSTextCheckingResult *res = [list objectAtIndex:0];

        // 見つかった範囲を出力してみる
        for (int i=0; i<res.numberOfRanges; i++)
        {
            NSRange range = [res rangeAtIndex:i];
            NSLog(@"range[%d] : %d - %d",
                  i , range.location, range.length);
        }

        // 正規表現で( )の箇所の範囲の1つめ
        NSRange range = [res rangeAtIndex:1];

        textField.text = [textField.text
            stringByReplacingCharactersInRange:range
                                    withString:@""];
    }

    // 小数点後の末尾のゼロの連続の修正 ---> 行わない

    // 最後のまとめ処理
    if ([textField.text isEqualToString:@""]
        || [textField.text isEqualToString:@"-"]
        || [textField.text isEqualToString:@"+"]
        || [textField.text isEqualToString:@"."]
        || [textField.text floatValue] == 0.0f)
    {
        textField.text = @"0.0";
    }
    else
    {
        // 符号と小数点がならんでいるならゼロを挿入
        textField.text = [textField.text
                stringByReplacingOccurrencesOfString:@"-."
                                          withString:@"-0."];
        textField.text = [textField.text
                stringByReplacingOccurrencesOfString:@"+."
                                          withString:@"+0."];

        NSRange dotSearch = [textField.text rangeOfString:@"."];

        // 一文字目が小数点なら先頭にゼロを付加
        if (dotSearch.location == 0) {
            textField.text = [[NSString stringWithString:@"0"]
                    stringByAppendingString:textField.text];
        }

        // 最後が小数点ならば末尾にゼロを1つ追加
        if (dotSearch.location == textField.text.length - 1) {
            textField.text =
                    [textField.text stringByAppendingString:@"0"];
        }
    }

    // 角度記号の追加
    textField.text = [textField.text stringByAppendingString:@"°"];
}

  ※エラー処理はちゃんとはしてません。正規表現がただしければ問題ないかな?

 インデント調整してみましたが、Objective-C はやっぱりメソッド名が長すぎる!長い名前はなれると読みやすいは読みやすいですが、こういうところでは悩ましい。もちろんいつもはこんな変なインデントしてませんよ。以上余談。

 これで 00123 になっていても終了時に 123° に修正されます。符号と小数点が並んでいてもOK。これでおおよそ大丈夫になったと思います。想定外の入力はもうないはず。。。

  もっと考えるならば、角度なので -180°〜180° の間にまるめてもよかったのですが、 360° とか 540° などの入力を制限したくなかったので、数値の範囲制限は無しとしました。

 以上。実数だけを入力させる方法の紹介でした。入力終了時の処理はもう少しコンパクトにまとまりそうですが、そこは宿題ということで。

 (追記)正規表現マッチ部の取得方法が今ひとつだった場所を修正しました。参考サイト:http://d.hatena.ne.jp/hypercrab/20111224/1324658693

 

 本記事内容についての正確性については保証しかねますので実際のところは各自リファレンスなどでご確認ください。

カテゴリー: tech | タグ: , , | コメントする

UIViewController でビューの管理

 OpenGLES のアプリを作成していても、設定画面はアップルが提供している UI を使うのが簡単だし、ユーザーとしてもわかりやすいからいいよね、というのは当然考えるところ。しかしながら、OpenGL 関連一辺倒でやってきたので UIなんたらController とか UINavigationController とか実はちゃんとはよくわかっていません。

 もう1つ。UINavigationController や UITableViewController などに関する情報は見つけられるのですが、大元の UIViewController を扱った情報がなかなか出てきません。じゃあやってみましょうか、というのがこの記事を書く理由ですね。

 本記事内容についての正確性については保証しかねますので実際のところは各自リファレンスなどでご確認ください。

<前提知識>

 まず、Ray Wenderlich のチュートリアルのうち、Beginning Storyboards in iOS 5 の Part 1 と Part2 を読みました。コードをよく見ていくと、delegate をどうやって使っているのかがよくわかりますね。個人的には View (+ ViewController) の扱いを Flash のディスプレイ表示リストになぞらえて捉えると理解がよく通りました。

 しかし、画面遷移に segue というモノを使っており、StoryBoards ありきの話になってしまっています。もちろんそこを目的としているチュートリアルなので問題ないのですが、個人的には

  • メインの GLKView (=OpenGL) 上のタップを検出して設定画面に遷移させたい
  • GLKView の表側には CocoaTouch 系のボタンなどは置きたくない
       (パフォーマンス+画面を広く使いたいから)
  • よってGlkView 上のタップ位置を検出して遷移させたい
  • メインの View の最初の表示は StoryBoard でつくったものを使う

という方向性がありました。よって segue 君は使えません。

 web 上をいろいろと探したのですが、良いチュートリアルが見つかりません。Apple 提供のドキュメント類、とくに「iOS View Controller プログラミングガイド」がよさそうったのですが、キーワードで中身を検索したら思っているような事柄が少ない感じ。だったら自分でコードを書いて確かめたほうが方が速いかな、ということで。

開発環境
・Xcode 4.3.1
・ARC ありき
・iPhone 5.1 simulator
参考サイト:http://d.hatena.ne.jp/glass-_-onion/20120601/1338477967

<プロジェクト作成>

File –> New –> Project –> Single View Application

screenshot_2012-06-05 211844

と、設定して Create。

 MainStoryBoard.storyboard を表示し、View の部分をクリックすると(少し灰色のアウトランが付く)、View の情報を確認できます。クラスはデフォルトの UIView になっています。

screenshot 2012-06-05 21.31.57

 次に、View の下部にある黒いバーを選択すると(アウトラインが青になる)、ViewController の情報が表示され、Project 生成時に自動的に作られたクラスが当てはめられいてます。

screenshot 2012-06-05 21.35.02

 この状態では RACViewController.h と RACViewController.m に書いた情報がこの View が持つ機能となります。逆に言うと、StoryBoard 上のビューに何らかの機能を持たせたければ、その機能を持ったクラスの名前をここに書けば良いことになりますね。

<ウインドウを追加する>

 さて、ではこの View にボタン (Round Rec Button) を追加しましょう。名前は change。画面を遷移させる=切り替えるボタンです。次に、このボタンが押された時の処理を RACViewController.h に宣言しましょう。(メソッドのネーミングが Apple 的でないかもしれませんね。。。)

// add to RACViewController.h
- (IBAction)changeToSecondView:(id)sender;

  StoryBoard にもどり、ボタンを Ctrl キーを押しながらドラッグしていって、下のバーの中の ViewController のアイコン(黄色の方)にドロップします。

screenshot 2012-06-05 21.53.37

 続いて黒いポップアップが出てくるので先ほど追加したメソッド changeToSccondView: を選択。

screenshot 2012-06-05 21.51.56

 これでボタンが押されると changeToSecondView: が呼ばれる、という関連付けがなされました。

 ではこのメソッドの内容を書いていきましょう。

  参考にした glass-_-onion さんのサイトを参考にして書くと以下のようになりました。

// add to RACViewController.m
- (IBAction)changeToSecondView:(id)sender
{
    NSLog(@"change");

    UIViewController *controller = [[UIViewController alloc] init];
    controller.view.backgroundColor = [UIColor blueColor];

    [self presentViewController:controller
                       animated:YES
                     completion:nil];
}

  新しいコントローラーを作成し、背景色を青に変更。そのコントローラーを present、というのが流れ。View の設定は必要ないみたいです。それからメソッド名にある “present” は「現在の」という意味かとおもってましたが、「出演させる」という意味もあるようで、そちらの方みたいですね。

 とにかく、ボタンを押すと新しい青い画面がアニメーション付きでせり上がってきます。

<ウインドウの除去と情報の取得>

  さて、この辺で UIViewController のリファレンスを確認してみましょう。関連するメソッド群としては

  • Presenting Another View Controller’s Content
  • Getting Other Related View Controllers

あたり。この中で present した View を除去するメソッド
– dismissViewControllerAnimated:completion:
について見ると、

The presenting view controller is responsible for dismissing the view controller it presented.
(present した view controller は、自身が present した view controller を始末する責任を負う。)

という記述が見られます。まどろっこしい和訳ですが、つまりAというViewController が presentViewController: メソッドで ViewController を追加したら、その追加したものについてはAがちゃんと始末しろよ、ということのようです。余談。

 実験実験。本当は追加する ViewController をサブクラス化するほうがよいのですが、面倒なのでタイマーでやってみます。まずは先程のメソッドを書き換え。といか最後の1行を追加。

// revise at RACViewController.m
- (IBAction)changeToSecondView:(id)sender
{
    NSLog(@"change");

    UIViewController *controller = [[UIViewController alloc] init];
    controller.view.backgroundColor = [UIColor blueColor];

    [self presentViewController:controller
                       animated:YES
                     completion:nil];

    [NSTimer scheduledTimerWithTimeInterval:0.5f
                                     target:self
                                   selector:@selector(onTime)
                                   userInfo:nil
                                    repeats:YES];
}

  それからタイマーで実行されるメソッドの追加。

- (void)onTime
{
    NSString *className =
        NSStringFromClass([[self presentedViewController] class]);
    NSLog(@"my presented : %@", className);

    static BOOL flag = YES;
    static int count = 0;

    if (flag)
    {
        count++;
        if (count >= 10)
        {
            [self dismissViewControllerAnimated:YES completion:nil];
            flag = NO;
        }
    }
}

  5秒経過したら self が present した ViewController を破棄しています。ついでに0.5秒ごとに presentedViewController を表示してみています。青いウインドウが消えるまでは UIViewContollerと表示され、そのあとは nil と表示されます。

 ちなみにリファレンスによると present は複数回可能であり、dissmissViewController のメソッドでは preset されたものを全部破棄するようです。もし1つ1つ管理したいのであれば、リファレンスの “Managing Child View Controllers in a Custom Container” のメソッド群を利用するとよさそうです。ただし Available in iOS 5.0 and later だそうで。

< StoryBoard で作った ViewController を利用する>

 ここまではメッソドの中で自前で alloc + init でもって新しい ViewController を作っていましたが、画面は StoryBoard で作ってしまいたいですよね。さて、適当で良いのでもう1つ ViewController を StoryBoard 上に作成しましょう。

screenshot 2012-06-05 23.49.49

 新しいビューの下部にある黒いバーをクリック(青アウトラインが出る)。右上のウインドウで Identifier を入力しましょう。ここでは subView としました。

screenshot 2012-06-05 23.51.57

 ここからの手順は以下のサイトを参考にしました。意外と簡単ですね。
http://tech.caph.jp/2012/02/02/storyboard-nav-customize/

 再度、RACViewController.m を書き換えます。

- (IBAction)changeToSecondView:(id)sender
{
    NSLog(@"change");

    UIViewController *controller =
        [self.storyboard
            instantiateViewControllerWithIdentifier:@"subView"];

    [self presentViewController:controller
                       animated:YES
                     completion:nil];

    [NSTimer scheduledTimerWithTimeInterval:0.5f
                                     target:self
                                   selector:@selector(onTime)
                                   userInfo:nil
                                    repeats:YES];
}

 self が参照している StoryBoard から Identifier が “subView” というものを取ってきて、それを表示するという流れです。あとは一緒で5秒後に追加した View が消えます。

=== 以上 ===

 文章としては長くなってしまいましたが、ViewController の追加と除去についての整理がつきました。間違いなどありましたらご指摘ください。

カテゴリー: tech | タグ: | コメントする

GLKMatrix

 iPhone の GLKit で追加されたマトリクスについて書いてみようと思います。ちょっと特殊な感じがします。

 さて、お題としてはある点 (1,2) について、原点を中心としてx方向に3倍、y方向に5倍。そのあとでx方向に+100、y方向に+200してみましょう。

 拡大とオフセットをする変換行列を作り、これを利用して座標を移動させます。自然に書くと以下のようになると思います。(GLKVector3 でも良かったな)。

GLKVector4 vec = {1.0f, 2.0f, 0.0f, 1.0f};
GLKMatrix4 transform =
            GLKMatrix4MakeScale(3.0f, 5.0f, 1.0f);
transform = GLKMatrix4Translate(transform,
                                100.0f, 200.0f, 0.0f);

vec = GLKMatrix4MultiplyVector4(transform, vec);
NSLog(@"%@", NSStringFromGLKVector4(vec));
// out:{303, 1010, 0, 1}

 思惑が外れ、 (+100, +200) のオフセットがされた後で座標が拡大されてしまいました。MakeTranslate → scale 変換というように書き換えると拡大→オフセットになります。

 なんとなく直感的でないなあ、ということで行列の要素を調べてみました。

GLKMatrix4 scale = GLKMatrix4MakeScale(3.0f, 5.0f, 1.0f);
NSLog(@"%@", NSStringFromGLKMatrix4(scale));
// out:{
//    {3, 0, 0, 0}, 
//    {0, 5, 0, 0}, 
//    {0, 0, 1, 0}, 
//    {0, 0, 0, 1}}
GLKMatrix4 offset =
        GLKMatrix4MakeTranslation(100.0f, 200.0f, 0.0f);
NSLog(@"%@", NSStringFromGLKMatrix4(offset));
// out:{
//    {1,   0,   0, 0},
//    {0,   1,   0, 0},
//    {0,   0,   1, 0},
//    {100, 200, 0, 1}}

 さて、感の良い方なら4行目にオフセットの値があるのを見てあれれと思ったかもしれませんが、もう1つ。

GLKMatrix4 transform = GLKMatrix4MakeScale(3.0f, 5.0f, 1.0f);
transform = GLKMatrix4Translate(transform, 100.0f, 200.0f, 0.0f);

// 以上は以下と同義
transform = GLKMatrix4Multiply(scale, offset);

 GLKMatrix4Translate() というのは実はただの行列の掛け算だったと。さて、この transform のマトリックスを NSLog で表示してみると。

// out:{
//    {3,   0,    0, 0}, 
//    {0,   5,    0, 0}, 
//    {0,   0,    1, 0},
//    {300, 1000, 0, 1}}

となっています。そこで各マトリックスの要素を付きあわせて考えると GLKMatrix4Multiply(scale, offset) の箇所は実際には offset * scale という順序で行列の掛け算が行われていることがわかります。scale * offset ではありません(交換法則は成り立たない)。

  ちなみに GLKMatrix4MultiplyVector4(transform, vec) の箇所についても実際には vec * transform という順序の計算が行われています。

 アップルさんそれはわかりづらいでしょう。と言いたいところですが、最終的に GLKVector4 型の座標を変換することなどを考えるとこちらの形(transform * vec ではなく vec * transform)のほうが処理速度の面からベター、というのをどこかで見かけました。

 でもやっぱりわかりずらい。リファレンスを見てみると

GLKMatrix4 GLKMatrix4Multiply (
   GLKMatrix4 matrixLeft,
   GLKMatrix4 matrixRight
);

Parameters
  matrixLeft    The multiplicand. // 被乗数(掛け算の前の数)の意味
  matrixRight   The multiplier.   // 乗数(掛け算の後ろの数)の意味

と、なっています。でも実際の要素を見ると左右が逆なわけで、意味合いとしても乗数と被乗数も逆ではありませんか、という話になります。

 というわけでリファレンスの Left と Right を鵜呑みにすると痛い目を見そうですね、という話でした。Multiply を使わないマトリックスの変換では、実際に変換する順序とは逆から値を設定していくようにするとうまく行きそうです。

 アップルさん、箸を持つほうが右ですよ!え、何?左利き?そ、そうですか・・・。

カテゴリー: tech | タグ: , | コメントする

[WordPress] 行頭スペースの自動削除をキャンセル

 HTML といっても口語体の文章であれば、やっぱり行頭にインデントがあったほうが読みやすいですよね。しかし WordPress のデフォルト設定では行頭のスペース(半角も全角も)投稿時に自動的に削除されてしまいます。

 http://notnil-creative.com/blog/archives/846 で書かれているような設定もありますが、ブラウザによっては効果がないようです。解決方法としては WordPress のプラグイン PS Disable Auto Formatting を入れると無事に行頭のスペースを使うことができました。

 ただしもう1点。エディタで1行空けて書いても、その空白行が投稿時に勝手に消されてしまいます。上記プラグインを入れると半分だけ解決します。

 どういうことかと言いますと、投稿1回目完了の時点ではその空白行は消されずに投稿に反映されます。しかし再度この記事を編集しようとすると、この空白行がなくなってしまいます。1回書いたらそれ以上は絶対編集しないという方なら問題ありませんが。

 解決方法としては、その空白行に「スペース」と「shift + enter の改行」を入れておくことです。すなわち、

 (←スペースを1つ打って、shift + enter で改行)
(←ここにも1行入ることになる)

とすると、自由に1行空けることができます。スペースは全角半角どちらでも OK でした。厳密には1行ちょうどの幅にはなりません。実際の入力は以下のようになります。

 

 (ほぼ)1行分空きましたね。この空白行を Drag で選択すると半角スペースが見えると思います。エディタで再編集の画面を開いてもこの空白行が消えることはありません。というわけで泥臭くはありますが、お手軽な方法としてご紹介でした。

 それから何となく気づいた(思い違いかもしれません)のですが、記事を書く → 上記プラグインを導入 → 昔の記事を再度編集といった場合に、昔の記事に対してプラグインの整形機能が働くことがあるようで、昔の投稿が思わぬ形に崩れる可能性があります。プラグインを入れるなら WordPress 導入時がよいのかもしれない、という印象です。

 というわけで、やっとこさ設定が終わりました。OpenGL や iPhone のテクニカルな話題など書いていこうと思いますのでお付き合いいただければと存じます。

カテゴリー: tech | コメントする