Skip to content

Commit

Permalink
Merge pull request #91 from botamochi6277/wink
Browse files Browse the repository at this point in the history
Add Right & Left Eye Open Ratio for Wink
  • Loading branch information
mongonta0716 committed Jun 2, 2024
2 parents af2bff6 + d35f146 commit 73210d6
Show file tree
Hide file tree
Showing 6 changed files with 241 additions and 121 deletions.
36 changes: 36 additions & 0 deletions examples/wink-demo/wink-demo.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include <Avatar.h>
#include <M5Unified.h>

m5avatar::Avatar avatar;
m5avatar::ColorPalette* palettes[2];

void setup() {
M5.begin();

palettes[0] = new m5avatar::ColorPalette();
palettes[1] = new m5avatar::ColorPalette();
palettes[1]->set(COLOR_PRIMARY, TFT_YELLOW);
palettes[1]->set(COLOR_BACKGROUND, TFT_DARKCYAN);

avatar.init(); // start drawing
}

void loop() {
M5.update();
if (M5.BtnA.wasPressed()) {
// switch right eye
avatar.setRightEyeOpenRatio(avatar.getRightEyeOpenRatio() > 0.5f ? 0.0f
: 1.0f);
}
if (M5.BtnB.wasPressed()) {
avatar.setIsAutoBlink(!avatar.getIsAutoBlink());
avatar.setColorPalette(
*palettes[static_cast<uint8_t>(!avatar.getIsAutoBlink())]);
}
if (M5.BtnC.wasPressed()) {
// switch left eye
avatar.setLeftEyeOpenRatio(avatar.getLeftEyeOpenRatio() > 0.5f ? 0.0f
: 1.0f);
}
delay(10);
}
183 changes: 107 additions & 76 deletions src/Avatar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,10 @@ unsigned int seed = 0;
#ifdef SDL_h_
#define TaskResult() return 0
#define TaskDelay(ms) lgfx::delay(ms)
long random(long howbig) {
return std::rand() % howbig;
}
long random(long howbig) { return std::rand() % howbig; }
#else
#define TaskResult() vTaskDelete(NULL)
#define TaskDelay(ms) vTaskDelay(ms/portTICK_PERIOD_MS)
#define TaskDelay(ms) vTaskDelay(ms / portTICK_PERIOD_MS)
#endif

// TODO(meganetaaan): make read-only
Expand All @@ -41,6 +39,7 @@ TaskHandle_t drawTaskHandle;
TaskResult_t drawLoop(void *args) {
DriveContext *ctx = reinterpret_cast<DriveContext *>(args);
Avatar *avatar = ctx->getAvatar();
// update drawings in the display
while (avatar->isDrawing()) {
if (avatar->isDrawing()) {
avatar->draw();
Expand All @@ -51,7 +50,7 @@ TaskResult_t drawLoop(void *args) {
}

TaskResult_t facialLoop(void *args) {
int c = 0;
int count = 0;
DriveContext *ctx = reinterpret_cast<DriveContext *>(args);
Avatar *avatar = ctx->getAvatar();
uint32_t saccade_interval = 1000;
Expand All @@ -63,31 +62,35 @@ TaskResult_t facialLoop(void *args) {
float horizontal = 0.0f;
float breath = 0.0f;
init_rand();
// update facial internal state
while (avatar->isDrawing()) {

if ((lgfx::millis() - last_saccade_millis) > saccade_interval) {
vertical = _rand() / (RAND_MAX / 2.0) - 1;
horizontal = _rand() / (RAND_MAX / 2.0) - 1;
avatar->setGaze(vertical, horizontal);
avatar->setRightGaze(vertical, horizontal);
avatar->setLeftGaze(vertical, horizontal);
saccade_interval = 500 + 100 * random(20);
last_saccade_millis = lgfx::millis();
}

if ((lgfx::millis()- last_blink_millis) > blink_interval) {
if (eye_open) {
avatar->setEyeOpenRatio(1);
blink_interval = 2500 + 100 * random(20);
} else {
avatar->setEyeOpenRatio(0);
blink_interval = 300 + 10 * random(20);
if (avatar->getIsAutoBlink()) {
if ((lgfx::millis() - last_blink_millis) > blink_interval) {
if (eye_open) {
avatar->setEyeOpenRatio(1.0f);
blink_interval = 2500 + 100 * random(20);
} else {
avatar->setEyeOpenRatio(0.0f);
blink_interval = 300 + 10 * random(20);
}
eye_open = !eye_open;
last_blink_millis = lgfx::millis();
}
eye_open = !eye_open;
last_blink_millis = lgfx::millis();
}
c = (c + 1) % 100;
breath = sin(c * 2 * PI / 100.0);

count = (count + 1) % 100;
breath = sin(count * 2 * PI / 100.0);
avatar->setBreath(breath);
TaskDelay(33);
TaskDelay(33); // approx. 30fps
}
TaskResult();
}
Expand All @@ -99,31 +102,31 @@ Avatar::Avatar(Face *face)
_isDrawing{false},
expression{Expression::Neutral},
breath{0},
eyeOpenRatio{1},
leftEyeOpenRatio_{1.0f},
leftGazeH_{1.0f},
leftGazeV_{1.0f},
rightEyeOpenRatio_{1.0f},
rightGazeH_{1.0f},
rightGazeV_{1.0f},
isAutoBlink_{true},
mouthOpenRatio{0},
gazeV{0},
gazeH{0},
rotation{0},
scale{1},
palette{ColorPalette()},
speechText{""},
colorDepth{1},
batteryIconStatus{BatteryIconStatus::invisible}{}
batteryIconStatus{BatteryIconStatus::invisible} {}

Avatar::~Avatar() {
delete face;
}
Avatar::~Avatar() { delete face; }

void Avatar::setFace(Face *face) { this->face = face; }

Face *Avatar::getFace() const { return face; }

void Avatar::addTask(TaskFunction_t f
, const char* name
, const uint32_t stack_size
, UBaseType_t priority
, TaskHandle_t* const task_handle
, const BaseType_t core_id) {
void Avatar::addTask(TaskFunction_t f, const char *name,
const uint32_t stack_size, UBaseType_t priority,
TaskHandle_t *const task_handle,
const BaseType_t core_id) {
DriveContext *ctx = new DriveContext(this);
#ifdef SDL_h_
if (task_handle == NULL) {
Expand All @@ -133,13 +136,13 @@ void Avatar::addTask(TaskFunction_t f
}
#else
// TODO(meganetaaan): set a task handler
xTaskCreateUniversal(f, /* Function to implement the task */
name, /* Name of the task */
stack_size, /* Stack size in words */
ctx, /* Task input parameter */
priority, /* Priority of the task */
task_handle, /* Task handle. */
core_id); /* Core No*/
xTaskCreateUniversal(f, /* Function to implement the task */
name, /* Name of the task */
stack_size, /* Stack size in words */
ctx, /* Task input parameter */
priority, /* Priority of the task */
task_handle, /* Task handle. */
core_id); /* Core No*/
#endif
}

Expand All @@ -162,42 +165,46 @@ void Avatar::resume() {
#endif
}

void Avatar::start(int colorDepth) {
void Avatar::start(int colorDepth) {
// if the task already started, don't create another task;
if (_isDrawing) return;
_isDrawing = true;

this->colorDepth = colorDepth;
DriveContext *ctx = new DriveContext(this);
#ifdef SDL_h_
drawTaskHandle = SDL_CreateThreadWithStackSize(drawLoop, "drawLoop", 2048, ctx);
drawTaskHandle =
SDL_CreateThreadWithStackSize(drawLoop, "drawLoop", 2048, ctx);
SDL_CreateThreadWithStackSize(facialLoop, "facialLoop", 1024, ctx);
#else
// TODO(meganetaaan): keep handle of these tasks
xTaskCreateUniversal(drawLoop, /* Function to implement the task */
"drawLoop", /* Name of the task */
2048, /* Stack size in words */
ctx, /* Task input parameter */
1, /* Priority of the task */
&drawTaskHandle, /* Task handle. */
APP_CPU_NUM);

xTaskCreateUniversal(facialLoop, /* Function to implement the task */
"facialLoop", /* Name of the task */
1024, /* Stack size in words */
ctx, /* Task input parameter */
2, /* Priority of the task */
NULL, /* Task handle. */
APP_CPU_NUM);
xTaskCreateUniversal(drawLoop, /* Function to implement the task */
"drawLoop", /* Name of the task */
2048, /* Stack size in words */
ctx, /* Task input parameter */
1, /* Priority of the task */
&drawTaskHandle, /* Task handle. */
APP_CPU_NUM);

xTaskCreateUniversal(facialLoop, /* Function to implement the task */
"facialLoop", /* Name of the task */
1024, /* Stack size in words */
ctx, /* Task input parameter */
2, /* Priority of the task */
NULL, /* Task handle. */
APP_CPU_NUM);
#endif
}

void Avatar::draw() {
Gaze g = Gaze(this->gazeV, this->gazeH);
DrawContext *ctx = new DrawContext(this->expression, this->breath,
&this->palette, g, this->eyeOpenRatio,
this->mouthOpenRatio, this->speechText,
this->rotation, this->scale, this->colorDepth, this->batteryIconStatus, this->batteryLevel, this->speechFont);
Gaze rightGaze = Gaze(this->rightGazeV_, this->rightGazeV_);
Gaze leftGaze = Gaze(this->leftGazeV_, this->leftGazeH_);
DrawContext *ctx = new DrawContext(
this->expression, this->breath, &this->palette, rightGaze,
this->rightEyeOpenRatio_, leftGaze, this->leftEyeOpenRatio_,
this->mouthOpenRatio, this->speechText, this->rotation, this->scale,
this->colorDepth, this->batteryIconStatus, this->batteryLevel,
this->speechFont);
face->draw(ctx);
delete ctx;
}
Expand All @@ -210,15 +217,11 @@ void Avatar::setExpression(Expression expression) {
resume();
}

Expression Avatar::getExpression() {
return this->expression;
}
Expression Avatar::getExpression() { return this->expression; }

void Avatar::setBreath(float breath) { this->breath = breath; }

float Avatar::getBreath() {
return this->breath;
}
float Avatar::getBreath() { return this->breath; }

void Avatar::setRotation(float radian) { this->rotation = radian; }

Expand All @@ -234,16 +237,45 @@ ColorPalette Avatar::getColorPalette(void) const { return this->palette; }

void Avatar::setMouthOpenRatio(float ratio) { this->mouthOpenRatio = ratio; }

void Avatar::setEyeOpenRatio(float ratio) { this->eyeOpenRatio = ratio; }
void Avatar::setEyeOpenRatio(float ratio) {
setRightEyeOpenRatio(ratio);
setLeftEyeOpenRatio(ratio);
}

void Avatar::setLeftEyeOpenRatio(float ratio) {
this->leftEyeOpenRatio_ = ratio;
}

float Avatar::getLeftEyeOpenRatio() { return this->leftEyeOpenRatio_; }

void Avatar::setGaze(float vertical, float horizontal) {
this->gazeV = vertical;
this->gazeH = horizontal;
void Avatar::setRightEyeOpenRatio(float ratio) {
this->rightEyeOpenRatio_ = ratio;
}

void Avatar::getGaze(float *vertical, float *horizontal) {
*vertical = this->gazeV;
*horizontal = this->gazeH;
float Avatar::getRightEyeOpenRatio() { return this->rightEyeOpenRatio_; }

void Avatar::setIsAutoBlink(bool b) { this->isAutoBlink_ = b; }

bool Avatar::getIsAutoBlink() { return this->isAutoBlink_; }

void Avatar::setRightGaze(float vertical, float horizontal) {
this->rightGazeV_ = vertical;
this->rightGazeH_ = horizontal;
}

void Avatar::getRightGaze(float *vertical, float *horizontal) {
*vertical = this->rightGazeV_;
*horizontal = this->rightGazeH_;
}

void Avatar::setLeftGaze(float vertical, float horizontal) {
this->leftGazeV_ = vertical;
this->leftGazeH_ = horizontal;
}

void Avatar::getLeftGaze(float *vertical, float *horizontal) {
*vertical = this->leftGazeV_;
*horizontal = this->leftGazeH_;
}

void Avatar::setSpeechText(const char *speechText) {
Expand All @@ -267,11 +299,10 @@ void Avatar::setBatteryStatus(bool isCharging, int32_t batteryLevel) {
if (isCharging) {
this->batteryIconStatus = BatteryIconStatus::charging;
} else {
this->batteryIconStatus = BatteryIconStatus::discharging;
this->batteryIconStatus = BatteryIconStatus::discharging;
}
this->batteryLevel = batteryLevel;
}

}

} // namespace m5avatar
Loading

0 comments on commit 73210d6

Please sign in to comment.