Thymeleaf で isXxx プロパティを参照するとエラーになる

概要

Thymeleaf の変数式で ${aVariable.isXxx} とするとエラーになって困った時の記録です。

確認環境

  • Spring Boot 1.4.2
    • Spring 4.3.4
    • Thymeleaf 2.1.5, 3.0.0
  • Kotlin 1.0.5

解説

Kotlin で次のクラスを作成したとする。

data class User(
    val name: String,
    val age: Int,
    val address: Address,
    val isEnabled: Boolean)

Java ならば以下のようになる。

public class User {

    private final String name;
    private final int age;
    private final Address address;
    private final boolean isEnabled;

    public boolean isEnabled() {
        return isEnabled;
    }

    ... (setter/getter, toString, equals, hashCode メソッドと続くが省略)
}

Thymeleaf では User オブジェクトの isEnabled プロパティを参照するために以下のように書く。

<span th:text="${user.isEnabled}">どうなるでしょう?</span>

これはエラーになる。isEnabled プロパティが見つからないと。 isEnabled ではなく enabled とすることで参照できる。

<span th:text="${user.enabled}">参照できる</span>

何故エラーになるのかと思い、デバッガで確認した。

org.springframework.expression.spel.support.ReflectivePropertyAccessor クラスの findGetterForProperty メソッドでは、 propertyName=isEnabled を IsEnabled に変換した後に findMethodForProperty メソッドを呼ぶ。

findMethodForProperty メソッドでは、prefix=is と methodSuffix=IsEnabled を結合して isIsEnabled とした上でメソッド名と equals で比較。

プロパティ名を enabled とすれば、isEnabled となるため参照できる。

Thymeleaf は内部的に SpEL (非SpringではOGNL) を使って式を解釈している。 上記は SpEL の一部に該当する。

bootRun で Spring Boot を起動した時に、IntelliJ IDEA でデバッガを使う

概要

Spring Boot と IntelliJ IDEA の組み合わせでデバッガを使う方法です。 Terminal から bootRun 起動する場合に限ります。

確認環境

  • IntelliJ IDEA CE 2016.3
  • Spring Boot 1.4.2

参考情報

解説

Run > Edit Configurations を選択する。

Configuration を新規追加。

Remote を選択。(Socket で接続する。)

任意の名前に設定し、他はデフォルトでOK。

Terminal から起動する際に --debug-jvm を付けて bootRun を実行すると接続待ちになる。

作成した Configuration (例では、bootRun (debug)) でデバッグ実行を行うと、Socket が接続されて bootRun の起動が継続する。

以上で、デバッガを使用できる状態になる。

Thymeleaf snippet

概要

Thymeleaf の書き方を学習するために、チュートリアルの内容を一通り書いてみました。 その記録です。

確認環境

  • IntelliJ IDEA COMMUNITY 2016.3
  • Spring Boot 1.4.2
  • Thymeleaf 2.1.5

参考情報

Thymeleaf の使い方。日本語。

内容

github.com

html タグの xmlns:th="http://www.thymeleaf.org" は IDEA のエラー対策です。

雑感

IntelliJ IDEA ULTIMATE を使えば Thymeleaf でも補完できるらしい。 COMMUNITY は Thymeleaf の補完だけでなく JavaScript の整形、補完もできないので、開発で使うなら ULTIMATE を買わないとダメだなと思う。

FaceRig + 恋声 + Skype で映像と音声を変換して通話する

概要

FaceRig + Live2D Module を使えばイケメン・美少女キャラクターに成り替わることができます。 恋声を使えばマイク入力の音声を変換して女声・男声に替えることができます。 これらと Skype を組み合わせれば、姿と声を変えて通話することができます。

FaceRig + Live 2D Module は合わせても 2000 円弱、恋声は無料。 いずれも Windows 用のソフトウェアです。 MacBook Pro で試したいと思い、VMware Fusion 8 で動いている Windows 10 に環境構築しました。

確認環境

参考情報

解説

映像の設定、音声の設定、映像と音声を組み合わせて Skype に出力する手順で構築する。

仮想マシンのカメラを有効にする

  1. 仮想マシンをシャットダウン
  2. 設定からカメラを追加

以下、設定からカメラを追加する手順。

FaceRig をインストールする

  1. Windows 10 を起動
  2. Steam, The Ultimate Online Game Platform から Steam をダウンロードして、インストール
  3. Steam を起動して FaceRig と Live2D Module をインストール
  4. FaceRig を起動する
    • LAUNCH しようとするとエラーが表示されるので、OPTIONS から設定を変更 (DX11 ではなく DX9 を使用)
  5. アドバンス UI に変更する
  6. 高度なトラッキングの設定から、カメラデバイスの指定を変更
    • VMware Virtual USB Video Device
  7. アバターの設定を変更
  8. 自動調整を実行
    • 頭ポーズの(早い)自動調整
    • 表情の(早い)自動調整

以下、画像で手順を説明する。

Steam で FaceRig と Live2D Module をインストール。

FaceRig を起動。

アドバンス UI に変更。

カメラデバイスの指定。

アバター設定。

自動調整。

以上で映像の設定は完了。

仮想サウンドカードをインストールする

  1. NETDUETTO をダウンロードして、インストール

恋声 (無料ボイスチェンジャー) をインストール

  1. サービス終了のお知らせ をダウンロードして、インストール
  2. 恋声を起動
    • koigoe(x64).exe を実行
  3. バイス設定を変更
  4. 入力をマイクに設定
  5. 声質を調整
    • お好みに
  6. 起動したままにする

バイス設定を変更。

FaceRig のサウンド設定を変更

  1. 入力を仮想サウンドカードに変更

以上で音声の設定は完了。

Skype の設定

  1. FaceRig の仮想デバイスに映像を出力させる
    • ブロードキャストに切り替え
  2. 映像は FaceRig の仮想デバイスから入力、音声は仮想サウンドカードから入力にする

FaceRig の仮想デバイスに映像を出力させるには、ブロードキャストに切り替える。 リップシンクに切り替えると音声に合わせて口が動くようになる。

Skype の映像と音声の入力を変更する。

プログラム用のきれいな等幅フォント

2011.12.08: MacBook Air に環境構築。
2016.11.20: MacBook Pro 導入に伴って、再度インストールしました。内容は更新されています。

表題の件で何かないかと探したら、Ricty というフォントがあるそうな。早速入れてみました。

詳しいインストール方法は Ricty フォントのページに書かれています。

プログラミング用フォント Ricty

以下はインストール記録です。

前準備として、必要になるコマンドをインストールします。

Homebrew というパッケージ管理システムをインストールします。
インストール方法は Homebrew に書かれています。

```bash
$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
```

次に Homebrew を使って FontForge をインストールします。

```bash
$ brew install fontforge --use-clang
```

これで前準備は完了。次はフォントファイルと生成スクリプトプログラミング用フォント Ricty 内のリンクを辿ってダウンロード。

  • Inconsolata.otf
  • Inconsolata.zip
  • migu-1m-20150712.zip
  • ricty_generator.sh
  • ricty_discord_converter.pe

まず、ダウンロードしたファイルを一つのディレクトリに展開します。展開したディレクトリで ls コマンドを実行した結果です。

```bash
$ ls
Inconsolata-Bold.ttf migu-1m-regular.ttf
Inconsolata-Regular.ttf mplus-TESTFLIGHT-060
ipag00303 ricty_discord_converter.pe
migu-1m-bold.ttf ricty_generator.sh
```

続いて、Ricty 生成スクリプトを実行します。

```bash
$ bash ricty_generator.sh Inconsolata-Regular.ttf Inconsolata-Bold.ttf migu-1m-regular.ttf migu-1m-bold.ttf
```

実行が完了すると、Ricty ディレクトリに以下のファイルが生成されます。

  • Ricty-Bold.ttf
  • Ricty-Regular.ttf
  • RictyDiscord-Bold.ttf
  • RictyDiscord-Regular.ttf

これらのファイルを Finder でダブルクリックして、フォントをインストールします。

プログラミング用フォント Ricty に合わせて以下のフォントもインストールします。

  • Inconsolata-Regular.ttf
  • Inconsolata-Bold.ttf
  • migu-1m-regular.ttf
  • migu-1m-bold.ttf

以上で完了です。

参考:
http://knagayama.net/blog/2011/07/27/generate-ricty-font/
http://d.hatena.ne.jp/gar_sue/20111112/1321131923
http://d.hatena.ne.jp/hiroe_orz17/20111113/1321168274

libGDX 2D UI Library - Widget (Button)

概要

libGDX の 2D UI Library のうち、Button 系 Widget についてまとめます。

目次

確認環境

  • OS X El Capitan (10.11.6)
  • Xcode 8.0
  • Android Studio 2.2.2
    • Multi-OS Engine Plugin 1.2.1
    • Kotlin 1.0.4
    • buildToolVersion 25.0.0
    • compileSdkVersion 24
  • libGDX 1.9.5-SNAPSHOT

参考情報

解説

Button

Table クラスのサブクラス。

Button (libgdx API)

設定項目 説明
child Actor 子の Actor。
style Button.ButtonStyle 背景の Drawable (up, down, checked, over, checkedOver, disabled)。child のオフセット (unpressedOffset[X|Y], pressedOffset[X|Y], checkedOffset[X|Y])。
Skin

状態遷移(一部)

サイズ

min pref max
width/height prefと同じ child のサイズ 0

実行例

up checked (offsetX = 100, offsetY = 100)

サンプルコード

    private val actor by lazy {
        val imageButton = ImageButton(
                SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply {
                    color = Color.RED
                }),
                SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply {
                    color = Color.BLUE
                }),
                SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply {
                    color = Color.GREEN
                })
        )
        Button(imageButton,
                Button.ButtonStyle().apply {
                    up = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply {
                        color = Color.YELLOW
                    })
                    down = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply {
                        color = Color.MAGENTA
                    })
                    checked = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply {
                        color = Color.CYAN
                    })
                    checkedOffsetX = 100f
                    checkedOffsetY = 100f
                }
        ).apply {
            width = this@MyGdxGame.stage.width
            height = this@MyGdxGame.stage.height
        }
    }

    override fun create() {
        ...
        stage.addActor(actor)
    }

ImageButton

Button クラスのサブクラス。

ImageButton (libgdx API)

設定項目 説明
style ImageButton.ImageButtonStyle imageUp, imageDown, imageChecked, imageOver, imageCheckedOver, imageDisabled。加えて、Button.ButtonStyle の項目。
Skin

サイズ

min pref max
width/height prefと同じ image のサイズ 0

実行例

up checked

サンプルコード

    private val actor by lazy {
        ImageButton(
                ImageButton.ImageButtonStyle().apply {
                    up = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply {
                        color = Color.YELLOW
                    })
                    imageUp = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply {
                        color = Color.RED
                    })
                    down = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply {
                        color = Color.MAGENTA
                    })
                    imageDown = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply {
                        color = Color.BLUE
                    })
                    checked = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply {
                        color = Color.CYAN
                    })
                    imageChecked = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply {
                        color = Color.GREEN
                    })
                    disabled = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply {
                        color = Color.GRAY
                    })
                    imageDisabled = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply {
                        color = Color.WHITE
                    })
                }
        ).apply {
            width = this@MyGdxGame.stage.width
            height = this@MyGdxGame.stage.height
            isDisabled = false
        }
    }

    override fun create() {
        ...
        stage.addActor(actor)
    }

TextButton

Button クラスのサブクラス。

TextButton (libgdx API)

設定項目 説明
text String ボタンテキスト。改行文字 ('\n') で改行される。
style TextButton.TextButtonStyle font, fontColor, downFontColor, checkedFontColor, overFontColor, checkedOverFontColor, disabledFontColor。加えて、Button.ButtonStyle の項目
Skin
label.* Label の設定項目。

サイズ

min pref max
width/height prefと同じ text が収まるサイズを算出 0

実行例

up checked

サンプルコード

    private val actor by lazy {
        TextButton("OK\n(Enter)",
                TextButton.TextButtonStyle().apply {
                    font = BitmapFont()
                    fontColor = Color.RED
                    downFontColor = Color.BLUE
                    checkedFontColor = Color.GREEN
                    disabledFontColor = Color.GRAY
                    up = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply {
                        color = Color.YELLOW
                    })
                    down = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply {
                        color = Color.MAGENTA
                    })
                    checked = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply {
                        color = Color.CYAN
                    })
                    disabled = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply {
                        color = Color.WHITE
                    })
                }
        ).apply {
            width = this@MyGdxGame.stage.width
            height = this@MyGdxGame.stage.height
            label.setFontScale(5f, 10f)
            isDisabled = false
        }
    }

    override fun create() {
        ...
        stage.addActor(actor)
    }

CheckBox

TextButton クラスのサブクラス。

CheckBox (libgdx API)

設定項目 説明
text String ボタンテキスト。改行文字 ('\n') で改行される。
style CheckBox.CheckBoxStyle Drawable (checkboxOff, checkboxOn, checkboxOffDisabled, checkboxOnDisabled, checkboxOver)。 加えて、TextButton.TextButtonStyle の項目。
Skin
label.* Label の設定項目。

サイズ

min pref max
width/height prefと同じ text と checkbox (Drawable) が収まるサイズを算出 0

実行例

up checked

サンプルコード

    private val actor by lazy {
        CheckBox("On",
                CheckBox.CheckBoxStyle().apply {
                    font = BitmapFont()
                    checkboxOff = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply {
                        color = Color.RED
                    })
                    checkboxOn = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply {
                        color = Color.GREEN
                    })
                }
        ).apply {
            width = this@MyGdxGame.stage.width
            height = this@MyGdxGame.stage.height
            label.setFontScale(5f, 10f)
        }
    }

    override fun create() {
        ...
        stage.addActor(actor)
    }

libGDX Scene2d の基礎

概要

libGDX で 2D UI Widget (Button, Label, CheckBox, etc.) を使用するための基礎となる Stage, Viewport, Layout についてまとめます。 (ゲームライブラリとしてではなく、マルチプラットフォームGUI ライブラリとしての使用を模索中です。)

確認環境

  • OS X El Capitan (10.11.6)
  • Xcode 8.0
  • Android Studio 2.2.2
    • Multi-OS Engine Plugin 1.2.1
    • Kotlin 1.0.4
    • buildToolVersion 25.0.0
    • compileSdkVersion 24
  • libGDX 1.9.5-SNAPSHOT

参考情報

解説

Scene2d は基本的な 2D scene graph の機能を提供する。 Scene2d により以下が提供される。

  • アクター (actor)
    • 2D scene graph のノード。位置 (position)、サイズ (rectangular size)、原点 (origin)、スケール (scale)、回転 (rotation)、色 (color) を持つ。
  • グループ (group)
    • アクターのグループ。
  • 描画 (drawing)
    • 回転 (rotation)、スケール (scale) を考慮した描画を行う。
  • 当たり判定 (hit detection)
    • 回転 (rotation)、スケール (scale) を考慮した当たり判定を行う。
  • イベント (event)
    • 各アクターに入力イベントを分配する。
  • アクション (action)
    • 各アクターを時間に応じて変化させる。

Scene2d を基礎とした UI Widget が用意されている。 UI Widget 関連のクラスを図に示す。

Stage

Stage は 複数の Actor を子に持ち、Actor の root 要素になる。 Stage は InputProcessor であり、入力イベントを各 Actor に分配する。 Viewport により Stage を Screen に投影する方法を指定する。

基本的な使い方は以下のとおり。

class MyGdxGame : ApplicationAdapter() {

    private val stage by lazy {
        Stage()
    }

    override fun create() {
        Gdx.input.inputProcessor = stage
        Gdx.graphics.isContinuousRendering = false
        stage.addActor(Image(Texture("badlogic.jpg")))
    }

    override fun resize(width: Int, height: Int) {
        stage.viewport.update(width, height, true)
    }

    override fun render() {
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
        stage.act(Gdx.graphics.deltaTime)
        stage.draw()
    }

    override fun dispose() {
        stage.dispose()
    }
}

stage プロパティは var lateinit stage でも良いが、stage プロパティを変更不可としたいため val stage by lazy を使用している。 by lazy を使用することにより、最初の get 呼び出しでインスタンスが生成されるようになる。

Viewport

Viewport により Stage を Screen に投影する方法を指定する。 Viewport のクラス階層を以下に示す。

各 Viewport における、Screen サイズ (単位はピクセル) と Stage サイズ (単位はユニット) の関係を以下に示す。 Stage を構成する単位 (Stage の論理的な画素) を 1 ユニットとする。

  • ScreenViewport
    • Stage サイズ = Screen サイズ × 1 ピクセル当たりのユニット (unitsPerPixel)
    • デフォルトでは、1 ユニット = 1 ピクセル (unitsPerPixcel = 1)。
  • ScalingViewport
    • Stage サイズを指定し、Screen に合わせて Scale (拡大/縮小) して表示する。Stage サイズは指定したサイズ。
    • Scale 方法は以下のとおり。
      • none : 1 ユニット = 1 ピクセルとする。
      • fit : 縦横一方を Stage = Screen となるように、一方を Stage < Screen となるように、Stage のアスペクト比を変えずに拡大/縮小する。
      • fill : 縦横一方を Stage = Screen となるように、一方を Stage > Screen となるように、Stage のアスペクト比を変えずに拡大/縮小する。
      • stretch : 縦横共に Stage = Screen となるように、Stage のアスペクト比を変えて拡大/縮小する。
  • FitViewport
    • ScalingViewport の fit と同じ。
  • FillViewport
    • ScalingViewport の fill と同じ。
  • StretchViewport
    • ScalingViewport の stretch と同じ。
  • ExtendViewport
    • Stage サイズを指定し、Screen に合わせて拡大/縮小して表示する。拡大/縮小に合わせて Stage サイズを変更する。
    • FitViewport と同様に拡大/縮小した後に、Stage < Screen となっている辺の Stage サイズを変更する。
      • 最大サイズが指定されていない場合は、Stage = Screen となるように Stage サイズを変更する。
      • 最大サイズが指定されている場合は、指定した最大サイズに Stage サイズを変更する。

言葉では分かりづらいため図示すると以下のようになる。 赤は Screen サイズ (単位はピクセル)。 黒は Stage サイズ (単位はユニット)。

ScreenViewport (unitsPerPixcel = 1) ScreenViewport (unitsPerPixcel = 2)

ScalingViewport (none, 640, 480) ScalingViewport (fit, 640, 480)
ScalingViewport (fill, 640, 480) ScalingViewport (stretch, 640, 480)

ExtendViewport (640, 480) ExtendViewport (640, 480, 800, 480) ExtendViewport (640, 480, 800, 640)

表示例を得るために以下のソースコードを使用した。

class MyGdxGame : ApplicationAdapter() {

    companion object {
        private val TAG = MyGdxGame::class.java.name
    }

    private val stage by lazy {
        Stage(ScreenViewport())
        /*
        Stage(ScreenViewport().apply {
            unitsPerPixel = 2f
        })
        */
        //Stage(ScalingViewport(Scaling.none, 640f, 480f))
        //Stage(ScalingViewport(Scaling.fit, 640f, 480f))
        //Stage(FitViewport(640f, 480f))
        //Stage(ScalingViewport(Scaling.fill, 640f, 480f))
        //Stage(FillViewport(640f, 480f))
        //Stage(ScalingViewport(Scaling.stretch, 640f, 480f))
        //Stage(StretchViewport(640f, 480f))
        //Stage(ExtendViewport(640f, 480f))
        //Stage(ExtendViewport(640f, 480f, 800f, 480f))
        //Stage(ExtendViewport(640f, 480f, 800f, 640f))
    }

    override fun create() {
        Gdx.app.logLevel = Application.LOG_DEBUG
        Gdx.app.debug(TAG, "create")
        Gdx.graphics.isContinuousRendering = false
        stage.addActor(object : Actor() {
            override fun draw(batch: Batch, parentAlpha: Float) {
                batch.end()
                ShapeRenderer().run {
                    projectionMatrix = batch.projectionMatrix
                    begin(ShapeRenderer.ShapeType.Filled)
                    color = Color.RED
                    rect(0f, 0f, stage.width, stage.height)
                    Gdx.app.debug(TAG, "${stage.width} ${stage.height}")
                    end()
                }
                batch.begin()
            }
        })
        stage.addActor(Image(Texture("badlogic.jpg")))
    }

    override fun resize(width: Int, height: Int) {
        Gdx.app.debug(TAG, "resize $width $height")
        stage.viewport.update(width, height, true)
    }

    override fun render() {
        Gdx.app.debug(TAG, "render")
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
        stage.draw()
    }

    override fun dispose() {
        Gdx.app.debug(TAG, "dispose")
        stage.dispose()
    }
}

レイアウト

UI Widget は自身のサイズと位置を決定しない。 親の UI Widget が子の UI Widget のサイズと位置を決定する。 UI Widget は親がサイズと位置を決定するためのヒントとなる最小サイズ、推奨サイズ、最大サイズを提供する。

UI Widget が描画 (draw) されるとき、最初に validate メソッドが呼ばれる。 UI Widget のレイアウトが invalid のとき、描画の前に layout メソッドが呼ばれる。 描画においては、layout によりキャッシュされたレイアウト情報 (サイズと位置) を用いる。 invalidate, invalidateHierarchy メソッドを呼ぶことで、レイアウトを invalid にできる。

UI Widget の状態が変化してキャッシュされたレイアウト情報を再計算する場合には invalidate メソッドを呼ぶ。 但し、最小サイズ、推奨サイズ、最大サイズに影響がない場合に限る。 サイズが変化せず、親 Widget が影響を受けない場合に使用する。

UI Widget の状態が変化して最小サイズ、推奨サイズ、最大サイズに影響が及ぶ場合には invalidateHierarcy メソッドを呼ぶ。 親 Widget のレイアウトに影響が及ぶため、root までの全ての親 Widgetinvalidate メソッドが呼ばれる。