Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Right & Left Eye Open Ratio for Wink #91

Merged
merged 4 commits into from
Jun 2, 2024
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
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
Loading