【Bラボデジタルサイネージ20】RModuleの影を実装

![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fuser-images.githubusercontent.com%2F29194025%2F137117015-af05fe4f-7c32-4e38-876f-e153ff2f2f7d.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&w=1400&fit=max&s=2bbf46fb5c1d8dec16f2fcb5d089fde2)

https://github.com/ohayota/BLabo-digital-signage

本記事は、[株式会社 函館ラボラトリ](http://www.hakolab.co.jp)が運営する「[Bラボ](http://www.hakolab.co.jp/b-lab/index.html)」における、大人向け「看板アプリ（デジタルサイネージ）を作るコース」用教材テキストです。

http://www.hakolab.co.jp/b-lab/index.html

1. [【Bラボデジタルサイネージ1】Processing+ラズパイでデジタルサイネージを作る！](https://qiita.com/ohayota/items/f4aa3e1b452060eb52ad)
2. [【Bラボデジタルサイネージ2】ラズパイ初期設定](https://qiita.com/ohayota/items/88fbcf177e6606311efb)
3. [【Bラボデジタルサイネージ3】準備プログラム作成＆機能実現の方針決定](https://qiita.com/ohayota/items/41a746d2258673726541)
4. [【Bラボデジタルサイネージ4】レイアウトの基準になるグリッドを表示する「GridModule」](https://qiita.com/ohayota/items/df704f523b5aa4dbb180)
5. [【Bラボデジタルサイネージ5】レイアウトの基準になる枠を表示する「PlaceholderModule」](https://qiita.com/ohayota/items/f52a10660009794afd9f)
6. [【Bラボデジタルサイネージ6】画像を全画面表示する「FullImageModule」](https://qiita.com/ohayota/items/4a9c8423459c251f4911)
7. [【Bラボデジタルサイネージ7】ページの自動切り替え](https://qiita.com/ohayota/items/9d2c28f9cbf2f559be31)
8. [【Bラボデジタルサイネージ8】設置されている場所の名前を表示する「LocationModule」](https://qiita.com/ohayota/items/046b361d945a709c4605)
9. [【Bラボデジタルサイネージ9】現在の時間を表示する「DateModule」](https://qiita.com/ohayota/items/6b80137e33bfb4172de6)
10. [【Bラボデジタルサイネージ10】ページ切り替えの時間が分かる「ProgressBarModule」](https://qiita.com/ohayota/items/9f3b6f9f32b7d93ef2a7)
11. [【Bラボデジタルサイネージ11】現在の表示中のページが分かる「PageControlModule」](https://qiita.com/ohayota/items/23acc993dca47286fffb)
12. [【Bラボデジタルサイネージ12】現在の天気を表示する「WeatherRModule」](https://qiita.com/ohayota/items/d9b1d21037dcedcfd78c)
13. [【Bラボデジタルサイネージ13】直近2件のバス時刻表を表示する「BusRModule」](https://qiita.com/ohayota/items/93d7c9b5328188b3c5b6)
14. [【Bラボデジタルサイネージ14】ごみ出しカレンダーを表示する「GomiRModule」](https://qiita.com/ohayota/items/40ed5db352a688637f83)
15. [【Bラボデジタルサイネージ15】（発展編）ツイートを表示する「TwitterRModule」](https://qiita.com/ohayota/items/6367fad7beaf9ad9ea1c)
16. [【Bラボデジタルサイネージ16】開店／閉店を表示する「OpenCloseRModule」](https://qiita.com/ohayota/items/284b5c7e7101f602016f)
17. [【Bラボデジタルサイネージ17】部屋の温度を表示する「TemperatureRModule」](https://qiita.com/ohayota/items/386c27a021601fb3ccfd)
18. [【Bラボデジタルサイネージ18】部屋の明るさを表示する「BrightnessRModule」](https://qiita.com/ohayota/items/76279927b4ef35eaf628)
19. [【Bラボデジタルサイネージ19】起動画面を表示する「LaunchingScreenModule」](https://qiita.com/ohayota/items/0b6bdeda68c36707f762)
20. **【Bラボデジタルサイネージ20】RModuleの影を実装（本記事）**

# 作るもの

今まで実装してきた「WeatherRModule」などの「RM」に分類されるモジュールの後ろに、薄い影を描画します。

影がある状態

![0041.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/afb4daa4-9ae8-ecb5-5d35-78d08df179ce.png)

影がない状態

![0017.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/96ea3e4f-b6db-cec0-45cf-45b6e6a6e2ee.png)

影がある状態

![0145.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/a499d198-f9db-8534-5a9c-172b4481be67.png)

影がない状態

![0021.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/c3d71701-275b-1c80-6721-d1a13fa05873.png)


# 影の作り方

RModuleのうしろに影を描画します。
影を作るには、RModuleの背景画像と同じように、PGraphicsを使います。
生成する影は下図のようなものです。

![211114_201043_124.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/556c615f-d3e9-d18b-16c9-de6eb35f2057.png)

この影は、灰色の角丸四角をぼかしたものです。
影の手前に、例としてTemperatureRModule用の背景を描画してみます。

![211114_202009_301.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/741a2cb2-1a58-3a76-d61e-3e9017e98baa.png)

影のほうが一回り大きいので、RModuleの背景のうしろから、うっすらと灰色がにじんでいます。

このようにして、各RModuleに影をつけます。


# 影の画像を生成

影に関連する変数を定義します。

``` java:DigitalSignage.pde
/* 略 */

// Shadow
final int SHADOW_ALPHA = 60;
final int SHADOW_PADDING = 20;
PGraphics moduleShadowS;
PGraphics moduleShadowM;
PGraphics moduleShadowL;

/* 略 */
```

影の透明度、RModuleの背景画像のまわりに確保するピクセル数、RModuleのサイズ別（S、M、L）の影画像用変数を定義しました。
`SHADOW_PADDING=20`なので、影の画像は縦横ともに、40ピクセル（20ピクセルを左右、または上下）を余分に確保することになります。

次に、影の初期化用関数`initializeShadow()`を新しくつくります。

``` java:Initialize.pde
/* 略 */

void initializeShadow() {
  Size size;
  int w;
  int h;
  
  // 影はモジュールよりも少し大きいサイズにする
  size = Size.S;
  w = moduleWidth(size) + SHADOW_PADDING * 2;
  h = moduleHeight(size) + SHADOW_PADDING * 2;
  
  // 影の画像を生成する
  moduleShadowS = createGraphics(w, h);
  moduleShadowS.beginDraw();
  moduleShadowS.colorMode(HSB, 360, 100, 100, 100);
  moduleShadowS.noStroke();
  moduleShadowS.fill(0, 0, 0, SHADOW_ALPHA);
  moduleShadowS.rect(SHADOW_PADDING, SHADOW_PADDING,
                     moduleWidth(size), moduleHeight(size),
                     MODULE_RECT_ROUND);
  moduleShadowS.filter(BLUR, 8); // にじませる
  moduleShadowS.endDraw();
  
  // 影はモジュールよりも少し大きいサイズにする
  size = Size.M;
  w = moduleWidth(size) + SHADOW_PADDING * 2;
  h = moduleHeight(size) + SHADOW_PADDING * 2;
  
  // 影の画像を生成する
  moduleShadowM = createGraphics(w, h);
  moduleShadowM.beginDraw();
  moduleShadowM.colorMode(HSB, 360, 100, 100, 100);
  moduleShadowM.noStroke();
  moduleShadowM.fill(0, 0, 0, SHADOW_ALPHA);
  moduleShadowM.rect(SHADOW_PADDING, SHADOW_PADDING,
                     moduleWidth(size), moduleHeight(size),
                     MODULE_RECT_ROUND);
  moduleShadowM.filter(BLUR, 8); // にじませる
  moduleShadowM.endDraw();
  
  // 影はモジュールよりも少し大きいサイズにする
  size = Size.L;
  w = moduleWidth(size) + SHADOW_PADDING * 2;
  h = moduleHeight(size) + SHADOW_PADDING * 2;
  
  // 影の画像を生成する
  moduleShadowL = createGraphics(w, h);
  moduleShadowL.beginDraw();
  moduleShadowL.colorMode(HSB, 360, 100, 100, 100);
  moduleShadowL.noStroke();
  moduleShadowL.fill(0, 0, 0, SHADOW_ALPHA);
  moduleShadowL.rect(SHADOW_PADDING, SHADOW_PADDING,
                     moduleWidth(size), moduleHeight(size),
                     MODULE_RECT_ROUND);
  moduleShadowL.filter(BLUR, 8); // にじませる
  moduleShadowL.endDraw();
}
```

PGraphicsを扱う記述のなかに出てきた下記の処理は、PGraphicsにぼかしを加えるための処理です。

``` java
moduleShadowS.filter(BLUR, 8); // にじませる
```

定義した`initializeShadow()`によって、RModuleの各サイズ（S、M、L）ごとに対応する影を生成します。
関数`initialize()`内で呼び出すようにしておきましょう。

``` java:Initialize.pde
void initialize() {
  initializeDate();
  isInitializedDates = true;
  
  initializeImage();
  initializeGrid();
  initializePlaceholder();
  initializeShadow(); /* 追加 */
  initializeRModuleBackground();
  isInitializedImages = true;
  
  isUpdatedWeather = updateWeather();
  isInitializedWeather = true;
  
  isUpdatedBus = updateBus();
  isInitializedBus = true;
  
  isUpdatedGomi = updateGomi();
  isInitializedGomi = true;
  
  isUpdatedTwitter = updateTwitter();
  isInitializedTwitter = true;
  
  isUpdatedOpenClose = updateOpenClose();
  isUpdatedTemperature = updateTemperature();
  isUpdatedBrightness = updateBrightness();
  
  updateNowPageID(true);
}

/* 略 */
```


# RModuleのサイズに対応した影を取得

RModuleの描画用関数内では、はじめにサイズを表す変数`size`を定義しています。

``` java:例
void drawWeatherRModule(Area area) {
  RModule module = RModule.Weather;
  Size size = moduleSize(module);
```

`size`には`Size.S`などの変数が入っています。
この変数を使い、すでに作成してある`moduleShadowS`などの影を取得できるようにしましょう。

関数`rmoduleShadowImage()`を定義します。
引数として取得した`size`ごとに、適した大きさの影を返す関数です。

``` java:DigitalSignage.pde
/* 略 */

PGraphics rmoduleShadowImage(Size size) {
  if (size == Size.S) return moduleShadowS;
  if (size == Size.M) return moduleShadowM;
  if (size == Size.L) return moduleShadowL;
  return null;
}
```

`rmoduleShadowImage(Size.S)`のようにして、影の画像を取得できるようになりました。


# RModuleのうしろに影を描画

今まで実装したRModuleの描画用関数に、影を描画する処理を追記します。
追記が必要な関数は、以下の7つです。

- drawBrightnessWeatherRModule()
- drawBusRModule()
- drawGomiRModule()
- drawOpenCloseRModule()
- drawTwitterRModule()
- drawTemperatureRModule()
- drawWeatherRModule()

追記する場所は、RModuleの幅や高さを取得する処理の直後です。

``` java:RM_Weather.pde
void drawWeatherRModule(Area area) {
  RModule module = RModule.Weather;
  Size size = moduleSize(module);
  
  int x = layoutGuideX(area);
  int y = layoutGuideY(area);
  int w = moduleWidth(size);
  int h = moduleHeight(size);
  
  image(rmoduleShadowImage(size), x-SHADOW_PADDING, y-SHADOW_PADDING); /* 追加 */

  /* 略 */
```

影はRModuleの背景より一回り大きいので、20ピクセル分を左上にずらして描画しています。
すべてのRModuleに対して描画処理を追記できたら、プログラムを実行してみてください。
各RModuleに影がついているはずです。

![0041.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/afb4daa4-9ae8-ecb5-5d35-78d08df179ce.png)

![0145.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/a499d198-f9db-8534-5a9c-172b4481be67.png)


# 最後に

**デジタルサイネージの実装は、これですべて終わりました。**

この記事までの20本では、実装する各処理はすべて関数を使って実現しました。
今までの関数を使った実装とは別バージョンで、**class**を使った実装をしたプログラムも公開しています。
もし別バージョンの実装に興味があれば、ソースコードを覗いてみてください。

https://github.com/ohayota/digital-signage-raspberry

お疲れ様でした！