Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed data/eye_outline_left_resized.jpg
Binary file not shown.
Binary file removed data/eye_outline_right_resized.jpg
Binary file not shown.
Binary file removed data/eye_pupil_left_resized.jpg
Binary file not shown.
Binary file removed data/eye_pupil_right_resized.jpg
Binary file not shown.
Binary file removed data/eye_reflex_left_resized.jpg
Binary file not shown.
Binary file removed data/eye_reflex_right_resized.jpg
Binary file not shown.
Binary file added data/kashiwagi_delighted.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
Binary file added data/kashiwagi_iris.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
Binary file added data/kashiwagi_troubled.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/kashiwagi_upperlid.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/kashiwagi_upperlid_ldown.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/kashiwagi_upperlid_rdown.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/normal_kxr_eyeball.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
Binary file added data/normal_kxr_iris_sleepy.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/normal_kxr_iris_surprised.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/normal_kxr_puil_surprised.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/normal_kxr_pupil.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/normal_kxr_pupil_sleepy.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/normal_kxr_reflex.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/normal_kxr_reflex_surp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
Binary file removed data/upperlid_happy_left.jpg
Diff not rendered.
51 changes: 51 additions & 0 deletions include/emotion.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include <math.h>
#include <functional>
#include <vector>
#include <map>
#include <array>
#include <string>
#include <iostream>

#include <Arduino.h>
#include <ArduinoJson.h>
#include <SPIFFS.h>

#include "eye_assets.hpp"

#if defined(STAMPS3)
#include <lgfx_round_lcd_stamp_s3.hpp>
#elif defined(STAMPC3)
#include <lgfx_round_lcd_stamp_c3.hpp>
#endif

class EmotionManager
{
private:
EyeManager& eyeManager; // EyeManager の参照を保持
int eye_status = -1;
int frame = 0;

public:
// コンストラクタで EyeManager の参照を受け取る
EmotionManager(EyeManager& manager) : eyeManager(manager) {}

// emotionに応じて画像セットを読み込む
void set_emotion(int received_eye_status = 0) {
if (eye_status != received_eye_status && eye_assets_map.find(received_eye_status) != eye_assets_map.end()) {
eye_status = received_eye_status;
eyeManager.set_picture_files_asset(eye_assets_map[eye_status]);
frame = 0;
}
}

int get_emotion(){
return eye_status;
}

void update_emotion() {
float upperlid_y = upperlid_position_map[eye_status][frame % upperlid_position_map[eye_status].size()];
eyeManager.set_upperlid_position(upperlid_y);
eyeManager.update();
frame ++;
}
};
229 changes: 50 additions & 179 deletions include/eye.hpp
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
#include <math.h>
#include <functional>
#include <vector>
#include <map>
#include <array>
#include <string>
#include <iostream>

#include <Arduino.h>
#include <ArduinoJson.h>
#include <SPIFFS.h>

#include "eye_assets.hpp"

#if defined(STAMPS3)
#include <lgfx_round_lcd_stamp_s3.hpp>
#elif defined(STAMPC3)
#include <lgfx_round_lcd_stamp_c3.hpp>
#endif

class Eye
class EyeManager
{
private:
LGFX_ROUND_LCD lcd;
Expand All @@ -24,14 +32,15 @@ class Eye
LGFX_Sprite sprite_upperlid;

float zoom_ratio;

int image_width;
int image_height;

public:
void init(const char *path_jpg_eyeball, const char *path_jpg_iris, const char *path_jpg_upperlid,
const int image_width, const int image_height, int rotation = 0)
float look_x = 0.0f;
float look_y = 0.0f;
float upperlid_y = 0.0f;

public:
void init(const int image_width, const int image_height, int rotation = 0)
{
this->image_width = image_width;
this->image_height = image_height;
Expand All @@ -42,34 +51,26 @@ class Eye
// 目の部位を描写するSpriteを準備
sprite_eye.createSprite(image_width, image_height);
sprite_eye.fillScreen(TFT_WHITE);
sprite_eye.drawJpgFile(SPIFFS, path_jpg_eyeball);

// 目玉を描写するSpriteを準備
sprite_eyeball.createSprite(image_width, image_height);
sprite_eyeball.fillScreen(TFT_WHITE);
sprite_eyeball.drawJpgFile(SPIFFS, path_jpg_eyeball);

// 上瞼を描写するSpriteを準備
sprite_upperlid.createSprite(image_width, image_height);
sprite_upperlid.fillScreen(TFT_WHITE);
const bool success_load_upperlid_image = sprite_upperlid.drawJpgFile(SPIFFS, path_jpg_upperlid);
Serial.println(success_load_upperlid_image); // 上瞼の画像がloadできていることを確認

// 虹彩を描写するSpriteを準備
sprite_iris.createSprite(image_width, image_height);
sprite_iris.fillScreen(TFT_WHITE);
const bool success_load_iris_image = sprite_iris.drawJpgFile(SPIFFS, path_jpg_iris);
Serial.println(success_load_iris_image);

// 瞳孔を描写するSpriteを準備
sprite_pupil.createSprite(image_width, image_height);
sprite_pupil.fillScreen(TFT_WHITE);
sprite_pupil.fillCircle(50, 50, 30, TFT_BLACK);

// 光の反射を描画するSpriteを準備
sprite_reflex.createSprite(image_width, image_height);
sprite_reflex.fillScreen(TFT_WHITE);
sprite_reflex.fillCircle(40, 40, 6, TFT_LIGHTGRAY);

// lcdを準備
lcd.setPivot(lcd.width() >> 1, lcd.height() >> 1);
Expand All @@ -85,191 +86,61 @@ class Eye
}
}

// 通常の目を描画する準備
void ready_for_normal_eye(const char *path_jpg_iris = "/white.jpg", const char *path_jpg_upperlid = "/white.jpg")
{
sprite_iris.fillScreen(TFT_WHITE);
const bool success_load_iris_image = sprite_iris.drawJpgFile(SPIFFS, path_jpg_iris);
Serial.println(success_load_iris_image);

sprite_pupil.fillScreen(TFT_WHITE);
sprite_pupil.fillCircle(50, 50, 30, TFT_BLACK);

sprite_reflex.fillScreen(TFT_WHITE);
sprite_reflex.fillCircle(40, 40, 6, TFT_LIGHTGRAY);

sprite_upperlid.fillScreen(TFT_WHITE);
sprite_upperlid.drawJpgFile(SPIFFS, path_jpg_upperlid);

}

// 驚いた目を描画する準備
void ready_for_surprised_eye(const char *path_jpg_surprised_iris = "/white.jpg")
{
void draw_image_file(LGFX_Sprite& sprite, const char* filePath)
{
std::string pathStr(filePath);
std::string extension = pathStr.substr(pathStr.find_last_of('.') + 1);

if (extension == "jpg" || extension == "jpeg") {
sprite.drawJpgFile(SPIFFS, filePath);
} else if (extension == "png") {
sprite.drawPngFile(SPIFFS, filePath);
}
}

// 画像ファイルのセットを決定する
// 画像が変わらないときはこれは実行しない
void set_picture_files_asset(const EyeAsset& eye_asset)
{
sprite_eyeball.fillScreen(TFT_WHITE);
draw_image_file(sprite_eyeball, eye_asset.eyeball); // eyeball
sprite_iris.fillScreen(TFT_WHITE);
const bool success_load_iris_image = sprite_iris.drawJpgFile(SPIFFS, path_jpg_surprised_iris);
Serial.println(success_load_iris_image);

draw_image_file(sprite_iris, eye_asset.iris); // iris
sprite_pupil.fillScreen(TFT_WHITE);
sprite_pupil.fillCircle(image_height / 2, image_width / 2, 15, TFT_BLACK);

draw_image_file(sprite_pupil, eye_asset.pupil); // pupil
sprite_reflex.fillScreen(TFT_WHITE);
sprite_reflex.fillCircle(image_height / 2, image_width / 2, 7, TFT_LIGHTGRAY);
}

// 怒った目を描画する準備
void ready_for_angry_eye(const char *path_jpg_angry_upperlid = "/white.jpg")
{
sprite_upperlid.fillScreen(TFT_WHITE);
const bool success_load_angry_upperlid_image = sprite_upperlid.drawJpgFile(SPIFFS, path_jpg_angry_upperlid);
Serial.println("angry_eye");
Serial.println(success_load_angry_upperlid_image);
}

// 悲しい目を描画する準備
void ready_for_sad_eye(const char *path_jpg_sad_upperlid = "/white.jpg")
{
draw_image_file(sprite_reflex, eye_asset.reflex); // reflex
sprite_upperlid.fillScreen(TFT_WHITE);
const bool success_load_sad_upperlid_image = sprite_upperlid.drawJpgFile(SPIFFS, path_jpg_sad_upperlid);
Serial.println("sad_eye");
Serial.println(success_load_sad_upperlid_image);
draw_image_file(sprite_upperlid, eye_asset.upperlid); // upperlid
}

// 喜んでいる目を描画する準備
void ready_for_happy_eye(const char *path_jpg_happy_upperlid = "/white.jpg")
// 視線方向を変更(値を設定するだけ)
void set_gaze_direction(float look_x, float look_y)
{
sprite_upperlid.fillScreen(TFT_WHITE);
const bool success_load_happy_upperlid_image = sprite_upperlid.drawJpgFile(SPIFFS, path_jpg_happy_upperlid);
Serial.println("happy_eye");
Serial.println(success_load_happy_upperlid_image);
this->look_x = look_x;
this->look_y = look_y;
}

// 通常の目の描画
void update_look(float dx = 0.0, float dy = 0.0, float scale = 10.0, float random_scale = 5.0)
// まぶたの位置を変更(値を設定するだけ)
void set_upperlid_position(float upperlid_y)
{
long rx = (int)(random_scale * random(100) / 100);
long ry = (int)(random_scale * random(100) / 100);

sprite_eye.clear();
sprite_eye.fillScreen(TFT_WHITE);
sprite_eyeball.pushSprite(&sprite_eye, 0, 0, TFT_WHITE);
sprite_iris.pushSprite(&sprite_eye, 0, 0, TFT_WHITE);

sprite_pupil.pushSprite(&sprite_eye, (int)(scale * dx), (int)(scale * dy), TFT_WHITE); // 瞳孔をランダムに動かす
sprite_reflex.pushSprite(&sprite_eye, (int)(scale * dx) + rx, (int)(scale * dy) + ry, TFT_WHITE); // 光の反射をランダムに動かす
sprite_upperlid.pushSprite(&sprite_eye, 0, -130, TFT_WHITE);

sprite_eye.pushRotateZoom(&lcd, lcd.width() >> 1, lcd.height() >> 1, 0, zoom_ratio, zoom_ratio, TFT_WHITE); // sprite_eye をlcdに一括転送
this->upperlid_y = upperlid_y;
}

// 瞬きの描画
void blink_eye(float dx = 0.0, float dy = 0.0, int blink_level = 0 /*何コマ目か*/, float scale = 10.0, float random_scale = 5.0)
// 目の状態を更新する
void update(float scale = 10.0, float random_scale = 5.0)
{
int upperlid_y_arr[] = {-130, -130, 0, 0, -130, -130}; // 上瞼のコマ送り時のy座標の配列
long rx = (int)(random_scale * random(100) / 100);
long ry = (int)(random_scale * random(100) / 100);

sprite_eye.clear();
sprite_eye.fillScreen(TFT_WHITE);
sprite_eyeball.pushSprite(&sprite_eye, 0, 0, TFT_WHITE);
sprite_iris.pushSprite(&sprite_eye, 0, 0, TFT_WHITE);

sprite_pupil.pushSprite(&sprite_eye, (int)(scale * dx), (int)(scale * dy), TFT_WHITE); // 瞳孔をランダムに動かす
sprite_reflex.pushSprite(&sprite_eye, (int)(scale * dx) + rx, (int)(scale * dy) + ry, TFT_WHITE); // 光の反射をランダムに動かす

sprite_upperlid.pushSprite(&sprite_eye, 0, upperlid_y_arr[blink_level], TFT_WHITE); // 上瞼を動かす

sprite_eye.pushRotateZoom(&lcd, lcd.width() >> 1, lcd.height() >> 1, 0, zoom_ratio, zoom_ratio, TFT_WHITE); // sprite_eye をlcdに一括転送
}


// 驚きの目の描画
void surprised(float dx = 0.0, float dy = 0.0, int surprised_level = 0 /*何コマ目か*/, float scale = 10.0, float random_scale = 5.0)
{
int upperlid_y_arr[] = {- 130, - 130, -130, -130, 0, 0, -130, -130, -130, -130, 0, -130, -130, 0, -130, -130};
long rx = (int)(random_scale * random(100) / 100);
long ry = (int)(random_scale * random(100) / 100);

sprite_eye.clear();
sprite_eye.fillScreen(TFT_WHITE);
sprite_eyeball.pushSprite(&sprite_eye, 0, 0, TFT_WHITE);

sprite_iris.pushSprite(&sprite_eye, 0, 5, TFT_WHITE);
sprite_pupil.pushSprite(&sprite_eye, (int)(scale * dx), (int)(scale * dy), TFT_WHITE);
sprite_reflex.pushSprite(&sprite_eye, (int)(scale * dx) + rx, (int)(scale * dy) + ry, TFT_WHITE);
sprite_upperlid.pushSprite(&sprite_eye, 0, upperlid_y_arr[surprised_level], TFT_WHITE);

sprite_eye.pushRotateZoom(&lcd, lcd.width() >> 1, lcd.height() >> 1, 0, zoom_ratio, zoom_ratio, TFT_WHITE);
}

// 眠い目の描画
void sleepy(float dx = 0.0, float dy = 0.0, int sleepy_level = 0, float scale = 10.0, float random_scale = 5.0)
{
int upperlid_y_arr[] = {- 70, - 70, -60, 0, 0, 0, -60, -70, -70, - 70};
long rx = (int)(random_scale * random(100) / 100);
long ry = (int)(random_scale * random(100) / 100);

sprite_eye.clear();
sprite_eye.fillScreen(TFT_WHITE);
sprite_eyeball.pushSprite(&sprite_eye, 0, 0, TFT_WHITE);
sprite_iris.pushSprite(&sprite_eye, -10, 15, TFT_WHITE);

sprite_pupil.pushSprite(&sprite_eye, (int)(scale * dx) - 10, (int)(scale * dy) + 15, TFT_WHITE);
sprite_upperlid.pushSprite(&sprite_eye, 0, upperlid_y_arr[sleepy_level]);

sprite_eye.pushRotateZoom(&lcd, lcd.width() >> 1, lcd.height() >> 1, 0, zoom_ratio, zoom_ratio, TFT_WHITE);
}

// 怒った目の描画
void angry(float dx = 0.0, float dy = 0.0, int angry_level = 0, float scale = 10.0, float random_scale = 5.0)
{
long rx = (int)(random_scale * random(100) / 100);
long ry = (int)(random_scale * random(100) / 100);

sprite_eye.clear();
sprite_eye.fillScreen(TFT_WHITE);
sprite_eyeball.pushSprite(&sprite_eye, 0, 0, TFT_WHITE);
sprite_iris.pushSprite(&sprite_eye, 0, 0, TFT_WHITE);

sprite_pupil.pushSprite(&sprite_eye, (int)(scale * dx), (int)(scale * dy), TFT_WHITE); // 瞳孔をランダムに動かす
sprite_reflex.pushSprite(&sprite_eye, (int)(scale * dx) + rx, (int)(scale * dy) + ry + 10, TFT_WHITE); // 光の反射をランダムに動かす

sprite_upperlid.pushSprite(&sprite_eye, 0, -10, TFT_WHITE);

sprite_eye.pushRotateZoom(&lcd, lcd.width() >> 1, lcd.height() >> 1, 0, zoom_ratio, zoom_ratio, TFT_WHITE);
}

// 悲しい目の描画
void sad(float dx = 0.0, float dy = 0.0, int sad_level = 0, float scale = 10.0, float random_scale = 5.0)
{
long rx = (int)(random_scale * random(100) / 100);
long ry = (int)(random_scale * random(100) / 100);

sprite_eye.clear();
sprite_eye.fillScreen(TFT_WHITE);
sprite_eyeball.pushSprite(&sprite_eye, 0, 0, TFT_WHITE);
sprite_iris.pushSprite(&sprite_eye, 0, 0, TFT_WHITE);

sprite_pupil.pushSprite(&sprite_eye, (int)(scale * dx), (int)(scale * dy), TFT_WHITE); // 瞳孔をランダムに動かす
sprite_reflex.pushSprite(&sprite_eye, (int)(scale * dx) + rx, (int)(scale * dy) + ry + 10, TFT_WHITE); // 光の反射をランダムに動かす

sprite_upperlid.pushSprite(&sprite_eye, 0, 0, TFT_WHITE);

sprite_eye.pushRotateZoom(&lcd, lcd.width() >> 1, lcd.height() >> 1, 0, zoom_ratio, zoom_ratio, TFT_WHITE);
}

// 嬉しい目の描画
void happy(float dx = 0.0, float dy = 0.0, int happy_level = 0, float scale = 10.0, float random_scale = 5.0)
{
long rx = (int)(random_scale * random(100) / 100);
long ry = (int)(random_scale * random(100) / 100);

sprite_eye.clear();
sprite_eye.fillScreen(TFT_WHITE);
sprite_eyeball.pushSprite(&sprite_eye, 0, 0, TFT_WHITE);

sprite_upperlid.pushSprite(&sprite_eye, rx, ry, TFT_WHITE);
sprite_iris.pushSprite(&sprite_eye, look_x, look_y, TFT_WHITE);
sprite_pupil.pushSprite(&sprite_eye, look_x, look_y, TFT_WHITE);
sprite_reflex.pushSprite(&sprite_eye, look_x + rx, look_y + ry, TFT_WHITE);
sprite_upperlid.pushSprite(&sprite_eye, 0, upperlid_y, TFT_WHITE);

sprite_eye.pushRotateZoom(&lcd, lcd.width() >> 1, lcd.height() >> 1, 0, zoom_ratio, zoom_ratio, TFT_WHITE);
}
};
};
Loading