Initial commit

This commit is contained in:
alastaira
2023-09-06 16:45:07 +01:00
parent 8dfb4a48d7
commit 83fbe060b1
30 changed files with 2436 additions and 0 deletions

261
Animations.h Normal file
View File

@@ -0,0 +1,261 @@
/***************************************************
Copyright (c) 2023 Alastair Aitchison, Playful Technology, (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#ifndef _ANIMATIONS_h
#define _ANIMATIONS_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "arduino.h"
#else
#include "WProgram.h"
#endif
class IAnimation {
public:
virtual float GetValue() = 0;
virtual float GetValue(unsigned long overWriteMillis) = 0;
virtual unsigned long GetElapsed() = 0;
private:
virtual float Calculate(unsigned long elapsedMillis) = 0;
};
class AnimationBase : IAnimation {
public:
AnimationBase(unsigned long interval) : Interval(interval), StarTime(millis()) {}
unsigned long Interval;
unsigned long StarTime;
virtual void Restart() {
StarTime = millis();
}
float GetValue() override final {
return GetValue(GetElapsed());
}
float GetValue(unsigned long elapsedMillis) override final {
return Calculate(elapsedMillis);
}
unsigned long GetElapsed() override {
return static_cast<unsigned long> (millis() - StarTime);
}
protected:
float Calculate(unsigned long elapsedMillis) override { return 0.0; }
};
class DeltaAnimation : public AnimationBase {
public:
unsigned long StarTime;
DeltaAnimation(unsigned long interval) : AnimationBase(interval) {};
float Calculate(unsigned long elapsedMillis) {
if (elapsedMillis < Interval) {
return 0.0f;
}
else {
return 1.0f;
}
};
};
class StepAnimation : public AnimationBase {
public:
unsigned long Interval;
unsigned long StarTime;
bool IsActive = true;
StepAnimation(unsigned long interval) : AnimationBase(interval) {};
float Calculate(unsigned long elapsedMillis) {
if (elapsedMillis < Interval) {
return 0.0f;
}
return 1.0f;
};
};
class RampAnimation : public AnimationBase {
public:
unsigned long StarTime;
bool IsActive = true;
RampAnimation(unsigned long interval) : AnimationBase(interval) {};
float Calculate(unsigned long elapsedMillis) {
if (elapsedMillis < Interval) {
return static_cast<float>(elapsedMillis) / Interval;
}
return 1.0f;
};
};
class TriangleAnimation : public AnimationBase {
public:
TriangleAnimation(unsigned long interval) : AnimationBase(interval) {
_t0 = interval / 2;
_t1 = interval - _t0;
}
TriangleAnimation(unsigned long t0, unsigned long t1) : AnimationBase(t0 + t1) {
_t0 = t0;
_t1 = t1;
};
float Calculate(unsigned long elapsedMillis) {
if (elapsedMillis % Interval < _t0) {
return static_cast<float>(elapsedMillis % Interval) / _t0;
}
return 1.0f - (static_cast<float>(elapsedMillis % Interval) - _t0) / _t1;
};
unsigned long _t0;
unsigned long _t1;
};
class TrapeziumAnimation : public AnimationBase {
public:
TrapeziumAnimation(unsigned long t) : AnimationBase(t) {
_t0 = t / 3;
_t1 = _t0;
_t2 = t - _t0 - _t1;
};
TrapeziumAnimation(unsigned long t0, unsigned long t1, unsigned long t2) : AnimationBase(t0 + t1 + t2) {
_t0 = t0;
_t1 = t1;
_t2 = t2;
};
float Calculate(unsigned long elapsedMillis) override {
if (elapsedMillis > Interval) return 0.0;
if (elapsedMillis < _t0) {
return static_cast<float>(elapsedMillis) / _t0;
}
else if (elapsedMillis < _t0 + _t1) {
return 1.0f;
}
else {
return 1.0f - (static_cast<float>(elapsedMillis) - _t1 - _t0) / _t2;
}
};
unsigned long _t0;
unsigned long _t1;
unsigned long _t2;
};
class TrapeziumPulseAnimation : public AnimationBase {
public:
TrapeziumPulseAnimation(unsigned long t) : AnimationBase(t) {
_t0 = 0;
_t1 = t / 3;
_t2 = t - _t0 - _t0;
_t3 = _t1;
_t4 = 0;
};
TrapeziumPulseAnimation(unsigned long t0, unsigned long t1, unsigned long t2) : AnimationBase(t0 + t1 + t2) {
_t0 = 0;
_t1 = t0;
_t2 = t1;
_t3 = t2;
_t4 = 0;
};
TrapeziumPulseAnimation(unsigned long t0, unsigned long t1, unsigned long t2, unsigned long t3, unsigned long t4) : AnimationBase(t0 + t1 + t2 + t3 + t4) {
_t0 = t0;
_t1 = t1;
_t2 = t2;
_t3 = t3;
_t4 = t4;
};
float Calculate(unsigned long elapsedMillis) override {
unsigned long elapsed = elapsedMillis % Interval;
if (elapsed < _t0) {
return 0.0;
}
if (elapsed < _t0 + _t1) {
return static_cast<float>(elapsed - _t0) / _t1;
}
else if (elapsed < _t0 + _t1 + _t2) {
return 1.0f;
}
else if (elapsed < _t0 + _t1 + _t2 + _t3) {
return 1.0f - (static_cast<float>(elapsed) - _t2 - _t1 - _t0) / _t3;
}
return 0.0;
};
void SetInterval(uint16_t t) {
_t0 = 0;
_t1 = t / 3;
_t2 = t - _t0 - _t0;
_t3 = _t1;
_t4 = 0;
Interval = _t0 + _t1 + _t2 + _t3 + _t4;
}
void SetTriangle(uint16_t t, uint16_t delay) {
_t0 = 0;
_t1 = t / 2;
_t2 = 0;
_t3 = _t1;
_t4 = delay;
Interval = _t0 + _t1 + _t2 + _t3 + _t4;
}
void SetTriangleCuadrature(uint16_t t, uint16_t delay) {
_t0 = delay;
_t1 = t / 2;
_t2 = 0;
_t3 = _t1;
_t4 = 0;
Interval = _t0 + _t1 + _t2 + _t3 + _t4;
}
void SetPulse(uint16_t t, uint16_t delay) {
_t0 = 0;
_t1 = t / 3;
_t2 = t - _t0 - _t0;
_t3 = _t1;
_t4 = delay;
Interval = _t0 + _t1 + _t2 + _t3 + _t4;
}
void SetPulseCuadrature(uint16_t t, uint16_t delay) {
_t0 = delay;
_t1 = t / 3;
_t2 = t - _t0 - _t0;
_t3 = _t1;
_t4 = 0;
Interval = _t0 + _t1 + _t2 + _t3 + _t4;
}
void SetInterval(uint16_t t0, uint16_t t1, uint16_t t2, uint16_t t3, uint16_t t4) {
_t0 = t0;
_t1 = t1;
_t2 = t2;
_t3 = t3;
_t4 = t4;
Interval = _t0 + _t1 + _t2 + _t3 + _t4;
}
unsigned long _t0;
unsigned long _t1;
unsigned long _t2;
unsigned long _t3;
unsigned long _t4;
};
#endif

69
AsyncTimer.cpp Normal file
View File

@@ -0,0 +1,69 @@
/***************************************************
Copyright (c) 2023 Alastair Aitchison, Playful Technology, (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#include "AsyncTimer.h"
AsyncTimer::AsyncTimer(unsigned long millisInterval) : AsyncTimer(millisInterval, nullptr) {}
AsyncTimer::AsyncTimer(unsigned long millisInterval, AsyncTimerCallback onFinish) {
Interval = millisInterval;
OnFinish = onFinish;
}
void AsyncTimer::Start() {
Reset();
_isActive = true;
}
void AsyncTimer::Reset() {
_startTime = millis();
}
void AsyncTimer::Stop() {
_isActive = false;
}
bool AsyncTimer::Update() {
if (_isActive == false) return false;
_isExpired = false;
if (static_cast<unsigned long>(millis() - _startTime) >= Interval) {
_isExpired = true;
if (OnFinish != nullptr) OnFinish();
Reset();
}
return _isExpired;
}
void AsyncTimer::SetIntervalMillis(unsigned long interval) {
Interval = interval;
}
unsigned long AsyncTimer::GetStartTime() {
return _startTime;
}
unsigned long AsyncTimer::GetElapsedTime() {
return millis() - _startTime;
}
unsigned long AsyncTimer::GetRemainingTime() {
return Interval - millis() + _startTime;
}
bool AsyncTimer::IsActive() const {
return _isActive;
}
bool AsyncTimer::IsExpired() const{
return _isExpired;
}

52
AsyncTimer.h Normal file
View File

@@ -0,0 +1,52 @@
/***************************************************
Copyright (c) 2023 Alastair Aitchison, Playful Technology, (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#ifndef _ASYNCTIMER_h
#define _ASYNCTIMER_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "arduino.h"
#else
#include "WProgram.h"
#endif
typedef void(*AsyncTimerCallback)();
class AsyncTimer {
public:
AsyncTimer(unsigned long millisInterval);
AsyncTimer(unsigned long millisInterval, AsyncTimerCallback OnFinish);
void Start();
void Reset();
void Stop();
bool Update();
void SetIntervalMillis(unsigned long interval);
unsigned long GetStartTime();
unsigned long GetElapsedTime();
unsigned long GetRemainingTime();
bool IsActive() const;
bool IsExpired() const;
unsigned long Interval;
AsyncTimerCallback OnFinish;
private:
bool _isActive;
bool _isExpired;
unsigned long _startTime;
};
#endif

32
BlinkAssistant.cpp Normal file
View File

@@ -0,0 +1,32 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#include "BlinkAssistant.h"
#include "Face.h"
BlinkAssistant::BlinkAssistant(Face& face) : _face(face), Timer(3500) {
Timer.Start();
}
void BlinkAssistant::Update() {
Timer.Update();
if (Timer.IsExpired()) {
Blink();
}
}
void BlinkAssistant::Blink() {
_face.LeftEye.BlinkTransformation.Animation.Restart();
_face.RightEye.BlinkTransformation.Animation.Restart();
Timer.Reset();
}

40
BlinkAssistant.h Normal file
View File

@@ -0,0 +1,40 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#ifndef _BLINKASSISTANT_h
#define _BLINKASSISTANT_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "arduino.h"
#else
#include "WProgram.h"
#endif
#include "Animations.h"
#include "AsyncTimer.h"
class Face;
class BlinkAssistant {
protected:
Face& _face;
public:
BlinkAssistant(Face& face);
AsyncTimer Timer;
void Update();
void Blink();
};
#endif

8
Common.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef COMMON_h
#define COMMON_h
#include <U8g2lib.h>
extern U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2;
#endif

85
Eye.cpp Normal file
View File

@@ -0,0 +1,85 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#include "Eye.h"
Eye::Eye(Face& face) : _face(face) {
this->IsMirrored = false;
ChainOperators();
Variation1.Animation._t0 = 200;
Variation1.Animation._t1 = 200;
Variation1.Animation._t2 = 200;
Variation1.Animation._t3 = 200;
Variation1.Animation._t4 = 0;
Variation1.Animation.Interval = 800;
Variation2.Animation._t0 = 0;
Variation2.Animation._t1 = 200;
Variation2.Animation._t2 = 200;
Variation2.Animation._t3 = 200;
Variation2.Animation._t4 = 200;
Variation2.Animation.Interval = 800;
}
void Eye::ChainOperators() {
Transition.Origin = &Config;
Transformation.Input = &Config;
Variation1.Input = &(Transformation.Output);
Variation2.Input = &(Variation1.Output);
BlinkTransformation.Input = &(Variation2.Output);
FinalConfig = &(BlinkTransformation.Output);
}
void Eye::Update() {
Transition.Update();
Transformation.Update();
Variation1.Update();
Variation2.Update();
BlinkTransformation.Update();
}
void Eye::Draw() {
Update();
EyeDrawer::Draw(CenterX, CenterY, FinalConfig);
}
void Eye::ApplyPreset(const EyeConfig config) {
Config.OffsetX = this->IsMirrored ? -config.OffsetX : config.OffsetX;
Config.OffsetY = -config.OffsetY;
Config.Height = config.Height;
Config.Width = config.Width;
Config.Slope_Top = this->IsMirrored ? config.Slope_Top : -config.Slope_Top;
Config.Slope_Bottom = this->IsMirrored ? config.Slope_Bottom : -config.Slope_Bottom;
Config.Radius_Top = config.Radius_Top;
Config.Radius_Bottom = config.Radius_Bottom;
Config.Inverse_Radius_Top = config.Inverse_Radius_Top;
Config.Inverse_Radius_Bottom = config.Inverse_Radius_Bottom;
Transition.Animation.Restart();
}
void Eye::TransitionTo(const EyeConfig config) {
Transition.Destin.OffsetX = this->IsMirrored ? -config.OffsetX : config.OffsetX;
Transition.Destin.OffsetY = -config.OffsetY;
Transition.Destin.Height = config.Height;
Transition.Destin.Width = config.Width;
Transition.Destin.Slope_Top = this->IsMirrored ? config.Slope_Top : -config.Slope_Top;
Transition.Destin.Slope_Bottom = this->IsMirrored ? config.Slope_Bottom : -config.Slope_Bottom;
Transition.Destin.Radius_Top = config.Radius_Top;
Transition.Destin.Radius_Bottom = config.Radius_Bottom;
Transition.Destin.Inverse_Radius_Top = config.Inverse_Radius_Top;
Transition.Destin.Inverse_Radius_Bottom = config.Inverse_Radius_Bottom;
Transition.Animation.Restart();
}

62
Eye.h Normal file
View File

@@ -0,0 +1,62 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#ifndef _EYE_h
#define _EYE_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "arduino.h"
#else
#include "WProgram.h"
#endif
#include "Common.h"
#include "Animations.h"
#include "EyeConfig.h"
#include "EyeDrawer.h"
#include "EyeTransition.h"
#include "EyeTransformation.h"
#include "EyeVariation.h"
#include "EyeBlink.h"
#include "EyeVariation.h"
class Face;
class Eye {
protected:
Face& _face;
void Update();
void ChainOperators();
public:
Eye(Face& face);
uint16_t CenterX;
uint16_t CenterY;
bool IsMirrored = false;
EyeConfig Config;
EyeConfig* FinalConfig;
EyeTransition Transition;
EyeTransformation Transformation;
EyeVariation Variation1;
EyeVariation Variation2;
EyeBlink BlinkTransformation;
void ApplyPreset(const EyeConfig preset);
void TransitionTo(const EyeConfig preset);
void Draw();
};
#endif

41
EyeBlink.cpp Normal file
View File

@@ -0,0 +1,41 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#include "EyeBlink.h"
EyeBlink::EyeBlink() : Animation(40, 100, 40) { }
void EyeBlink::Update() {
auto t = Animation.GetValue();
if(Animation.GetElapsed() > Animation.Interval) t = 0.0;
Apply(t * t);
}
void EyeBlink::Apply(float t) {
Output.OffsetX = Input->OffsetX;
Output.OffsetY = Input->OffsetY;
Output.Width = (BlinkWidth - Input->Width) * t + Input->Width;
Output.Height = (BlinkHeight - Input->Height) * t + Input->Height;
Output.Slope_Top = Input->Slope_Top * (1.0 - t);
Output.Slope_Bottom = Input->Slope_Bottom * (1.0 - t);
Output.Radius_Top = Input->Radius_Top * (1.0 - t);
Output.Radius_Bottom = Input->Radius_Bottom * (1.0 - t);
Output.Inverse_Radius_Top = Input->Inverse_Radius_Top * (1.0 - t);
Output.Inverse_Radius_Bottom = Input->Inverse_Radius_Bottom * (1.0 - t);
Output.Inverse_Offset_Top = Input->Inverse_Offset_Top * (1.0 - t);
Output.Inverse_Offset_Bottom = Input->Inverse_Offset_Bottom * (1.0 - t);
}

43
EyeBlink.h Normal file
View File

@@ -0,0 +1,43 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#ifndef _EYEBLINK_h
#define _EYEBLINK_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "arduino.h"
#else
#include "WProgram.h"
#endif
#include "Animations.h"
#include "EyeConfig.h"
class EyeBlink {
protected:
public:
EyeBlink();
EyeConfig* Input;
EyeConfig Output;
TrapeziumAnimation Animation;
int32_t BlinkWidth = 60;
int32_t BlinkHeight = 2;
void Update();
void Apply(float t);
};
#endif

43
EyeConfig.h Normal file
View File

@@ -0,0 +1,43 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#ifndef _EYECONFIG_h
#define _EYECONFIG_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "arduino.h"
#else
#include "WProgram.h"
#endif
struct EyeConfig
{
int16_t OffsetX;
int16_t OffsetY;
int16_t Height;
int16_t Width;
float Slope_Top;
float Slope_Bottom;
int16_t Radius_Top;
int16_t Radius_Bottom;
int16_t Inverse_Radius_Top;
int16_t Inverse_Radius_Bottom;
int16_t Inverse_Offset_Top;
int16_t Inverse_Offset_Bottom;
};
#endif

216
EyeDrawer.h Normal file
View File

@@ -0,0 +1,216 @@
/***************************************************
Copyright (c) 2023 Alastair Aitchison, Playful Technology, (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#ifndef _EYEDRAWER_h
#define _EYEDRAWER_h
#include "Common.h"
#include "EyeConfig.h"
#if defined(ARDUINO) && ARDUINO >= 100
#include "arduino.h"
#else
#include "WProgram.h"
#endif
enum CornerType {T_R, T_L, B_L, B_R};
/**
* Contains all functions to draw eye based on supplied (expression-based) config
*/
class EyeDrawer {
public:
static void Draw(int16_t centerX, int16_t centerY, EyeConfig *config) {
// Amount by which corners will be shifted up/down based on requested "slope"
int32_t delta_y_top = config->Height * config->Slope_Top / 2.0;
int32_t delta_y_bottom = config->Height * config->Slope_Bottom / 2.0;
// Full extent of the eye, after accounting for slope added at top and bottom
auto totalHeight = config->Height + delta_y_top - delta_y_bottom;
// If the requested top/bottom radius would exceed the height of the eye, adjust them downwards
if (config->Radius_Bottom > 0 && config->Radius_Top > 0 && totalHeight - 1 < config->Radius_Bottom + config->Radius_Top) {
int32_t corrected_radius_top = (float)config->Radius_Top * (totalHeight - 1) / (config->Radius_Bottom + config->Radius_Top);
int32_t corrected_radius_bottom = (float)config->Radius_Bottom * (totalHeight - 1) / (config->Radius_Bottom + config->Radius_Top);
config->Radius_Top = corrected_radius_top;
config->Radius_Bottom = corrected_radius_bottom;
}
// Calculate _inside_ corners of eye (TL, TR, BL, and BR) before any slope or rounded corners are applied
int32_t TLc_y = centerY + config->OffsetY - config->Height/2 + config->Radius_Top - delta_y_top;
int32_t TLc_x = centerX + config->OffsetX - config->Width/2 + config->Radius_Top;
int32_t TRc_y = centerY + config->OffsetY - config->Height/2 + config->Radius_Top + delta_y_top;
int32_t TRc_x = centerX + config->OffsetX + config->Width/2 - config->Radius_Top;
int32_t BLc_y = centerY + config->OffsetY + config->Height/2 - config->Radius_Bottom - delta_y_bottom;
int32_t BLc_x = centerX + config->OffsetX - config->Width/2 + config->Radius_Bottom;
int32_t BRc_y = centerY + config->OffsetY + config->Height/2 - config->Radius_Bottom + delta_y_bottom;
int32_t BRc_x = centerX + config->OffsetX + config->Width/2 - config->Radius_Bottom;
// Calculate interior extents
int32_t min_c_x = min(TLc_x, BLc_x);
int32_t max_c_x = max(TRc_x, BRc_x);
int32_t min_c_y = min(TLc_y, TRc_y);
int32_t max_c_y = max(BLc_y, BRc_y);
// Fill eye centre
EyeDrawer::FillRectangle(min_c_x, min_c_y, max_c_x, max_c_y, 1);
// Fill eye outwards to meet edges of rounded corners
EyeDrawer::FillRectangle(TRc_x, TRc_y, BRc_x + config->Radius_Bottom, BRc_y, 1); // Right
EyeDrawer::FillRectangle(TLc_x - config->Radius_Top, TLc_y, BLc_x, BLc_y, 1); // Left
EyeDrawer::FillRectangle(TLc_x, TLc_y - config->Radius_Top, TRc_x, TRc_y, 1); // Top
EyeDrawer::FillRectangle(BLc_x, BLc_y, BRc_x, BRc_y + config->Radius_Bottom, 1); // Bottom
// Draw slanted edges at top of bottom of eyes
// +ve Slope_Top means eyes slope downwards towards middle of face
if(config->Slope_Top > 0) {
EyeDrawer::FillRectangularTriangle(TLc_x, TLc_y-config->Radius_Top, TRc_x, TRc_y-config->Radius_Top, 0);
EyeDrawer::FillRectangularTriangle(TRc_x, TRc_y-config->Radius_Top, TLc_x, TLc_y-config->Radius_Top, 1);
}
else if(config->Slope_Top < 0) {
EyeDrawer::FillRectangularTriangle(TRc_x, TRc_y-config->Radius_Top, TLc_x, TLc_y-config->Radius_Top, 0);
EyeDrawer::FillRectangularTriangle(TLc_x, TLc_y-config->Radius_Top, TRc_x, TRc_y-config->Radius_Top, 1);
}
// Draw slanted edges at bottom of eyes
if(config->Slope_Bottom > 0) {
EyeDrawer::FillRectangularTriangle(BRc_x+config->Radius_Bottom, BRc_y+config->Radius_Bottom, BLc_x-config->Radius_Bottom, BLc_y+config->Radius_Bottom, 0);
EyeDrawer::FillRectangularTriangle(BLc_x-config->Radius_Bottom, BLc_y+config->Radius_Bottom, BRc_x+config->Radius_Bottom, BRc_y+config->Radius_Bottom, 1);
}
else if (config->Slope_Bottom < 0) {
EyeDrawer::FillRectangularTriangle(BLc_x-config->Radius_Bottom, BLc_y+config->Radius_Bottom, BRc_x+config->Radius_Bottom, BRc_y+config->Radius_Bottom, 0);
EyeDrawer::FillRectangularTriangle(BRc_x+config->Radius_Bottom, BRc_y+config->Radius_Bottom, BLc_x-config->Radius_Bottom, BLc_y+config->Radius_Bottom, 1);
}
// Draw corners (which extend "outwards" towards corner of screen from supplied coordinate values)
if(config->Radius_Top > 0) {
EyeDrawer::FillEllipseCorner(T_L, TLc_x, TLc_y, config->Radius_Top, config->Radius_Top, 1);
EyeDrawer::FillEllipseCorner(T_R, TRc_x, TRc_y, config->Radius_Top, config->Radius_Top, 1);
}
if(config->Radius_Bottom > 0) {
EyeDrawer::FillEllipseCorner(B_L, BLc_x, BLc_y, config->Radius_Bottom, config->Radius_Bottom, 1);
EyeDrawer::FillEllipseCorner(B_R, BRc_x, BRc_y, config->Radius_Bottom, config->Radius_Bottom, 1);
}
}
// Draw rounded corners
static void FillEllipseCorner(CornerType corner, int16_t x0, int16_t y0, int32_t rx, int32_t ry, uint16_t color) {
if (rx < 2) return;
if (ry < 2) return;
int32_t x, y;
int32_t rx2 = rx * rx;
int32_t ry2 = ry * ry;
int32_t fx2 = 4 * rx2;
int32_t fy2 = 4 * ry2;
int32_t s;
if (corner == T_R) {
for(x = 0, y = ry, s = 2 * ry2 + rx2 * (1 - 2 * ry); ry2 * x <= rx2 * y; x++) {
u8g2.drawHLine(x0, y0 - y, x);
if(s >= 0) {
s += fx2 * (1 - y);
y--;
}
s += ry2 * ((4 * x) + 6);
}
for(x = rx, y = 0, s = 2 * rx2 + ry2 * (1 - 2 * rx); rx2 * y <= ry2 * x; y++) {
u8g2.drawHLine(x0, y0 - y, x);
if (s >= 0) {
s += fy2 * (1 - x);
x--;
}
s += rx2 * ((4 * y) + 6);
}
}
else if (corner == B_R) {
for (x = 0, y = ry, s = 2 * ry2 + rx2 * (1 - 2 * ry); ry2 * x <= rx2 * y; x++) {
u8g2.drawHLine(x0, y0 + y -1, x);
if (s >= 0) {
s += fx2 * (1 - y);
y--;
}
s += ry2 * ((4 * x) + 6);
}
for (x = rx, y = 0, s = 2 * rx2 + ry2 * (1 - 2 * rx); rx2 * y <= ry2 * x; y++) {
u8g2.drawHLine(x0, y0 + y -1, x);
if (s >= 0) {
s += fy2 * (1 - x);
x--;
}
s += rx2 * ((4 * y) + 6);
}
}
else if (corner == T_L) {
for (x = 0, y = ry, s = 2 * ry2 + rx2 * (1 - 2 * ry); ry2 * x <= rx2 * y; x++) {
u8g2.drawHLine(x0-x, y0 - y, x);
if (s >= 0) {
s += fx2 * (1 - y);
y--;
}
s += ry2 * ((4 * x) + 6);
}
for (x = rx, y = 0, s = 2 * rx2 + ry2 * (1 - 2 * rx); rx2 * y <= ry2 * x; y++) {
u8g2.drawHLine(x0-x, y0 - y, x);
if (s >= 0) {
s += fy2 * (1 - x);
x--;
}
s += rx2 * ((4 * y) + 6);
}
}
else if (corner == B_L) {
for (x = 0, y = ry, s = 2 * ry2 + rx2 * (1 - 2 * ry); ry2 * x <= rx2 * y; x++) {
u8g2.drawHLine(x0-x, y0 + y - 1, x);
if (s >= 0) {
s += fx2 * (1 - y);
y--;
}
s += ry2 * ((4 * x) + 6);
}
for (x = rx, y = 0, s = 2 * rx2 + ry2 * (1 - 2 * rx); rx2 * y <= ry2 * x; y++) {
u8g2.drawHLine(x0-x, y0 + y , x);
if (s >= 0) {
s += fy2 * (1 - x);
x--;
}
s += rx2 * ((4 * y) + 6);
}
}
}
// Fill a solid rectangle between specified coordinates
static void FillRectangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t color) {
// Always draw from TL->BR
int32_t l = min(x0, x1);
int32_t r = max(x0, x1);
int32_t t = min(y0, y1);
int32_t b = max(y0, y1);
int32_t w = r-l;
int32_t h = b-t;
u8g2.setDrawColor(color);
u8g2.drawBox(l, t, w, h);
u8g2.setDrawColor(1);
}
static void FillRectangularTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t color) {
u8g2.setDrawColor(color);
u8g2.drawTriangle(x0, y0, x1, y1, x1, y0);
u8g2.setDrawColor(1);
}
static void FillTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t color) {
u8g2.setDrawColor(color);
u8g2.drawTriangle(x0, y0, x1, y1, x2, y2);
u8g2.setDrawColor(1);
}
};
#endif

399
EyePresets.h Normal file
View File

@@ -0,0 +1,399 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#ifndef _EYEPRESETS_h
#define _EYEPRESETS_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "arduino.h"
#else
#include "WProgram.h"
#endif
#include "EyeConfig.h"
static const EyeConfig Preset_Normal = {
.OffsetX = 0,
.OffsetY = 0,
.Height = 40,
.Width = 40,
.Slope_Top = 0,
.Slope_Bottom = 0,
.Radius_Top = 8,
.Radius_Bottom = 8,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Happy = {
.OffsetX = 0,
.OffsetY = 0,
.Height = 10,
.Width = 40,
.Slope_Top = 0,
.Slope_Bottom = 0,
.Radius_Top = 10,
.Radius_Bottom = 0,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Glee = {
.OffsetX = 0,
.OffsetY = 0,
.Height = 8,
.Width = 40,
.Slope_Top = 0,
.Slope_Bottom = 0,
.Radius_Top = 8,
.Radius_Bottom = 0,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 5,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Sad = {
.OffsetX = 0,
.OffsetY = 0,
.Height = 15,
.Width = 40,
.Slope_Top = -0.5,
.Slope_Bottom = 0,
.Radius_Top = 1,
.Radius_Bottom = 10,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Worried = {
.OffsetX = 0,
.OffsetY = 0,
.Height = 25,
.Width = 40,
.Slope_Top = -0.1,
.Slope_Bottom = 0,
.Radius_Top = 6,
.Radius_Bottom = 10,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Worried_Alt = {
.OffsetX = 0,
.OffsetY = 0,
.Height = 35,
.Width = 40,
.Slope_Top = -0.2,
.Slope_Bottom = 0,
.Radius_Top = 6,
.Radius_Bottom = 10,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Focused = {
.OffsetX = 0,
.OffsetY = 0,
.Height = 14,
.Width = 40,
.Slope_Top = 0.2,
.Slope_Bottom = 0,
.Radius_Top = 3,
.Radius_Bottom = 1,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Annoyed = {
.OffsetX = 0,
.OffsetY = 0,
.Height = 12,
.Width = 40,
.Slope_Top = 0,
.Slope_Bottom = 0,
.Radius_Top = 0,
.Radius_Bottom = 10,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Annoyed_Alt = {
.OffsetX = 0,
.OffsetY = 0,
.Height = 5,
.Width = 40,
.Slope_Top = 0,
.Slope_Bottom = 0,
.Radius_Top = 0,
.Radius_Bottom = 4,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Surprised = {
.OffsetX = -2,
.OffsetY = 0,
.Height = 45,
.Width = 45,
.Slope_Top = 0,
.Slope_Bottom = 0,
.Radius_Top = 16,
.Radius_Bottom = 16,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Skeptic = {
.OffsetX = 0,
.OffsetY = 0,
.Height = 40,
.Width = 40,
.Slope_Top = 0,
.Slope_Bottom = 0,
.Radius_Top = 10,
.Radius_Bottom = 10,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Skeptic_Alt = {
.OffsetX = 0,
.OffsetY = -6,
.Height = 26,
.Width = 40,
.Slope_Top = 0.3,
.Slope_Bottom = 0,
.Radius_Top = 1,
.Radius_Bottom = 10,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Fustrated = {
.OffsetX = 3,
.OffsetY = -5,
.Height = 12,
.Width = 40,
.Slope_Top = 0,
.Slope_Bottom = 0,
.Radius_Top = 0,
.Radius_Bottom = 10,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Unimpressed = {
.OffsetX = 3,
.OffsetY = 0,
.Height = 12,
.Width = 40,
.Slope_Top = 0,
.Slope_Bottom = 0,
.Radius_Top = 1,
.Radius_Bottom = 10,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Unimpressed_Alt = {
.OffsetX = 3,
.OffsetY = -3,
.Height = 22,
.Width = 40,
.Slope_Top = 0,
.Slope_Bottom = 0,
.Radius_Top = 1,
.Radius_Bottom = 16,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Sleepy = {
.OffsetX = 0,
.OffsetY = -2,
.Height = 14,
.Width = 40,
.Slope_Top = -0.5,
.Slope_Bottom = -0.5,
.Radius_Top = 3,
.Radius_Bottom = 3,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Sleepy_Alt = {
.OffsetX = 0,
.OffsetY = -2,
.Height = 8,
.Width = 40,
.Slope_Top = -0.5,
.Slope_Bottom = -0.5,
.Radius_Top = 3,
.Radius_Bottom = 3,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Suspicious = {
.OffsetX = 0,
.OffsetY = 0,
.Height = 22,
.Width = 40,
.Slope_Top = 0,
.Slope_Bottom = 0,
.Radius_Top = 8,
.Radius_Bottom = 3,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Suspicious_Alt = {
.OffsetX = 0,
.OffsetY = -3,
.Height = 16,
.Width = 40,
.Slope_Top = 0.2,
.Slope_Bottom = 0,
.Radius_Top = 6,
.Radius_Bottom = 3,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Squint = {
.OffsetX = -10,
.OffsetY = -3,
.Height = 35,
.Width = 35,
.Slope_Top = 0,
.Slope_Bottom = 0,
.Radius_Top = 8,
.Radius_Bottom = 8,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Squint_Alt = {
.OffsetX = 5,
.OffsetY = 0,
.Height = 20,
.Width = 20,
.Slope_Top = 0,
.Slope_Bottom = 0,
.Radius_Top = 5,
.Radius_Bottom = 5,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Angry = {
.OffsetX = -3,
.OffsetY = 0,
.Height = 20,
.Width = 40,
.Slope_Top = 0.3,
.Slope_Bottom = 0,
.Radius_Top = 2,
.Radius_Bottom = 12,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Furious = {
.OffsetX = -2,
.OffsetY = 0,
.Height = 30,
.Width = 40,
.Slope_Top = 0.4,
.Slope_Bottom = 0,
.Radius_Top = 2,
.Radius_Bottom = 8,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Scared = {
.OffsetX = -3,
.OffsetY = 0,
.Height = 40,
.Width = 40,
.Slope_Top = -0.1,
.Slope_Bottom = 0,
.Radius_Top = 12,
.Radius_Bottom = 8,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
static const EyeConfig Preset_Awe = {
.OffsetX = 2,
.OffsetY = 0,
.Height = 35,
.Width = 45,
.Slope_Top = -0.1,
.Slope_Bottom = 0.1,
.Radius_Top = 12,
.Radius_Bottom = 12,
.Inverse_Radius_Top = 0,
.Inverse_Radius_Bottom = 0,
.Inverse_Offset_Top = 0,
.Inverse_Offset_Bottom = 0
};
#endif

61
EyeTransformation.cpp Normal file
View File

@@ -0,0 +1,61 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#include "EyeTransformation.h"
EyeTransformation::EyeTransformation() : Animation(200)
{
}
void EyeTransformation::Update()
{
auto t = Animation.GetValue();
Current.MoveX = (Destin.MoveX - Origin.MoveX) * t + Origin.MoveX;
Current.MoveY = (Destin.MoveY - Origin.MoveY) * t + Origin.MoveY;
Current.ScaleX = (Destin.ScaleX - Origin.ScaleX) * t + Origin.ScaleX;
Current.ScaleY = (Destin.ScaleY - Origin.ScaleY) * t + Origin.ScaleY;
Apply();
}
void EyeTransformation::Apply()
{
Output.OffsetX = Input->OffsetX + Current.MoveX;
Output.OffsetY = Input->OffsetY - Current.MoveY;
Output.Width = Input->Width * Current.ScaleX;
Output.Height = Input->Height * Current.ScaleY;
Output.Slope_Top = Input->Slope_Top;
Output.Slope_Bottom = Input->Slope_Bottom;
Output.Radius_Top = Input->Radius_Top;
Output.Radius_Bottom = Input->Radius_Bottom;
Output.Inverse_Radius_Top = Input->Inverse_Radius_Top;
Output.Inverse_Radius_Bottom = Input->Inverse_Radius_Bottom;
Output.Inverse_Offset_Top = Input->Inverse_Offset_Top;
Output.Inverse_Offset_Bottom = Input->Inverse_Offset_Bottom;
}
void EyeTransformation::SetDestin(Transformation transformation)
{
Origin.MoveX = Current.MoveX;
Origin.MoveY = Current.MoveY;
Origin.ScaleX = Current.ScaleX;
Origin.ScaleY = Current.ScaleY;
Destin.MoveX = transformation.MoveX;
Destin.MoveY = transformation.MoveY;
Destin.ScaleX = transformation.ScaleX;
Destin.ScaleY = transformation.ScaleY;
}

53
EyeTransformation.h Normal file
View File

@@ -0,0 +1,53 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#ifndef _EYETRANSFORMATION_h
#define _EYETRANSFORMATION_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "arduino.h"
#else
#include "WProgram.h"
#endif
#include "Animations.h"
#include "EyeConfig.h"
struct Transformation
{
float MoveX = 0.0;
float MoveY = 0.0;
float ScaleX = 1.0;
float ScaleY = 1.0;
};
class EyeTransformation
{
public:
EyeTransformation();
EyeConfig* Input;
EyeConfig Output;
Transformation Origin;
Transformation Current;
Transformation Destin;
RampAnimation Animation;
void Update();
void Apply();
void SetDestin(Transformation transformation);
};
#endif

35
EyeTransition.cpp Normal file
View File

@@ -0,0 +1,35 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#include "EyeTransition.h"
EyeTransition::EyeTransition() : Animation(500){}
void EyeTransition::Update() {
float t = Animation.GetValue();
Apply(t);
}
void EyeTransition::Apply(float t) {
Origin->OffsetX = Origin->OffsetX * (1.0 - t) + Destin.OffsetX * t;
Origin->OffsetY = Origin->OffsetY * (1.0 - t) + Destin.OffsetY * t;
Origin->Height = Origin->Height * (1.0 - t) + Destin.Height * t;
Origin->Width = Origin->Width * (1.0 - t) + Destin.Width * t;
Origin->Slope_Top = Origin->Slope_Top * (1.0 - t) + Destin.Slope_Top * t;
Origin->Slope_Bottom = Origin->Slope_Bottom * (1.0 - t) + Destin.Slope_Bottom * t;
Origin->Radius_Top = Origin->Radius_Top * (1.0 - t) + Destin.Radius_Top * t;
Origin->Radius_Bottom = Origin->Radius_Bottom * (1.0 - t) + Destin.Radius_Bottom * t;
Origin->Inverse_Radius_Top = Origin->Inverse_Radius_Top * (1.0 - t) + Destin.Inverse_Radius_Top * t;
Origin->Inverse_Radius_Bottom = Origin->Inverse_Radius_Bottom * (1.0 - t) + Destin.Inverse_Radius_Bottom * t;
Origin->Inverse_Offset_Top = Origin->Inverse_Offset_Top * (1.0 - t) + Destin.Inverse_Offset_Top * t;
Origin->Inverse_Offset_Bottom = Origin->Inverse_Offset_Bottom * (1.0 - t) + Destin.Inverse_Offset_Bottom * t;
}

39
EyeTransition.h Normal file
View File

@@ -0,0 +1,39 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#ifndef _EYETRANSITION_h
#define _EYETRANSITION_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "arduino.h"
#else
#include "WProgram.h"
#endif
#include "Animations.h"
#include "EyeConfig.h"
class EyeTransition {
public:
EyeTransition();
EyeConfig* Origin;
EyeConfig Destin;
RampAnimation Animation;
void Update();
void Apply(float t);
};
#endif

50
EyeVariation.cpp Normal file
View File

@@ -0,0 +1,50 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#include "EyeVariation.h"
EyeVariation::EyeVariation() : Animation(0, 1000, 0, 1000, 0){}
void EyeVariation::Clear() {
Values.OffsetX = 0;
Values.OffsetY = 0;
Values.Height = 0;
Values.Width = 0;
Values.Slope_Top = 0;
Values.Slope_Bottom = 0;
Values.Radius_Top = 0;
Values.Radius_Bottom = 0;
Values.Inverse_Radius_Top = 0;
Values.Inverse_Radius_Bottom = 0;
Values.Inverse_Offset_Top = 0;
Values.Inverse_Offset_Bottom = 0;
}
void EyeVariation::Update() {
auto t = Animation.GetValue();
Apply(2.0 * t - 1.0);
}
void EyeVariation::Apply(float t) {
Output.OffsetX = Input->OffsetX + Values.OffsetX * t;
Output.OffsetY = Input->OffsetY + Values.OffsetY * t;
Output.Height = Input->Height + Values.Height * t;;
Output.Width = Input->Width + Values.Width * t;
Output.Slope_Top = Input->Slope_Top + Values.Slope_Top * t;
Output.Slope_Bottom = Input->Slope_Bottom + Values.Slope_Bottom * t;
Output.Radius_Top = Input->Radius_Top + Values.Radius_Top * t;
Output.Radius_Bottom = Input->Radius_Bottom + Values.Radius_Bottom * t;
Output.Inverse_Radius_Top = Input->Inverse_Radius_Top + Values.Inverse_Radius_Top * t;
Output.Inverse_Radius_Bottom = Input->Inverse_Radius_Bottom + Values.Inverse_Radius_Bottom * t;
Output.Inverse_Offset_Top = Input->Inverse_Offset_Top + Values.Inverse_Offset_Top * t;
Output.Inverse_Offset_Bottom = Input->Inverse_Offset_Bottom + Values.Inverse_Offset_Bottom * t;;
}

46
EyeVariation.h Normal file
View File

@@ -0,0 +1,46 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#ifndef _EYEVARIATION_h
#define _EYEVARIATION_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "arduino.h"
#else
#include "WProgram.h"
#endif
#include "Animations.h"
#include "EyeConfig.h"
class EyeVariation
{
public:
EyeVariation();
EyeConfig* Input;
EyeConfig Output;
TrapeziumPulseAnimation Animation;
EyeConfig Values;
void Clear();
void SetInterval(uint16_t t0, uint16_t t1, uint16_t t2, uint16_t t3, uint16_t t4);
void Update();
void Apply(float t);
};
#endif

96
Face.cpp Normal file
View File

@@ -0,0 +1,96 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#include "Face.h"
#include "Common.h"
Face::Face(uint16_t screenWidth, uint16_t screenHeight, uint16_t eyeSize)
: LeftEye(*this), RightEye(*this), Blink(*this), Look(*this), Behavior(*this), Expression(*this)
{
Width = screenWidth;
Height = screenHeight;
EyeSize = eyeSize;
CenterX = Width / 2;
CenterY = Height / 2;
LeftEye.IsMirrored = true;
Behavior.Timer.Start();
}
void Face::LookFront() {
Look.LookAt(0.0, 0.0);
}
void Face::LookRight() {
Look.LookAt(-1.0, 0.0);
}
void Face::LookLeft() {
Look.LookAt(1.0, 0.0);
}
void Face::LookTop() {
Look.LookAt(0.0, 1.0);
}
void Face::LookBottom() {
Look.LookAt(0.0, -1.0);
}
void Face::Wait(unsigned long milliseconds) {
unsigned long start;
start = millis();
while (millis() - start < milliseconds) {
Draw();
}
}
void Face::DoBlink() {
Blink.Blink();
}
void Face::Update() {
if(RandomBehavior) Behavior.Update();
if(RandomLook) Look.Update();
if(RandomBlink) Blink.Update();
Draw();
}
void Face::Draw() {
u8g2.clearBuffer();
// Only clear the section of the screen with the eyes - not the text underneath!
//u8g2.setDrawColor(0);
//u8g2.drawBox(20, 0, 64, 128);
//u8g2.setDrawColor(1);
LeftEye.CenterX = CenterX - EyeSize / 2 - EyeInterDistance;
LeftEye.CenterY = CenterY;
LeftEye.Draw();
RightEye.CenterX = CenterX + EyeSize / 2 + EyeInterDistance;
RightEye.CenterY = CenterY;
RightEye.Draw();
//u8g2.setDisplayRotation(U8G2_R3);
//_buffer.setPivot(_buffer.width(), 0);//_buffer.height()/2);
//_buffer.pushRotated(270, -1);
//_buffer.pushSprite(0, 0);
u8g2.sendBuffer(); // transfer internal memory to the display
//u8g2.setDisplayRotation(U8G2_R0);
}

69
Face.h Normal file
View File

@@ -0,0 +1,69 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#ifndef _FACE_h
#define _FACE_h
#include "config.h"
#if defined(ARDUINO) && ARDUINO >= 100
#include "arduino.h"
#else
#include "WProgram.h"
#endif
#include "Common.h"
#include "Animations.h"
#include "EyePresets.h"
#include "Eye.h"
#include "FaceExpression.h"
#include "FaceBehavior.h"
#include "LookAssistant.h"
#include "BlinkAssistant.h"
class Face {
public:
Face(uint16_t screenWidth, uint16_t screenHeight, uint16_t eyeSize);
uint16_t Width;
uint16_t Height;
uint16_t CenterX;
uint16_t CenterY;
uint16_t EyeSize;
uint16_t EyeInterDistance = 4;
Eye LeftEye;
Eye RightEye;
BlinkAssistant Blink;
LookAssistant Look;
FaceBehavior Behavior;
FaceExpression Expression;
void Update();
void DoBlink();
bool RandomBehavior = true;
bool RandomLook = true;
bool RandomBlink = true;
void LookLeft();
void LookRight();
void LookFront();
void LookTop();
void LookBottom();
void Wait(unsigned long milliseconds);
protected:
void Draw();
};
#endif

100
FaceBehavior.cpp Normal file
View File

@@ -0,0 +1,100 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#include "Face.h"
#include "FaceBehavior.h"
#include "FaceEmotions.hpp"
FaceBehavior::FaceBehavior(Face& face) : _face(face), Timer(500) {
Timer.Start();
Clear();
Emotions[(int)eEmotions::Normal] = 2.0;
Emotions[(int)eEmotions::Happy] = 1.0;
}
void FaceBehavior::SetEmotion(eEmotions emotion, float value) {
Emotions[emotion] = value;
}
float FaceBehavior::GetEmotion(eEmotions emotion) {
return Emotions[emotion];
}
void FaceBehavior::Clear() {
for (int emotion = 0; emotion < eEmotions::EMOTIONS_COUNT; emotion++) {
Emotions[emotion] = 0.0;
}
}
eEmotions FaceBehavior::GetRandomEmotion() {
float sum_of_weight = 0;
for (int emotion = 0; emotion < eEmotions::EMOTIONS_COUNT; emotion++) {
sum_of_weight += Emotions[emotion];
}
if (sum_of_weight == 0) {
return eEmotions::Normal;
}
float rand = random(0, 1000 * eEmotions::EMOTIONS_COUNT) / 1000.0;
float acc = 0;
for (int emotion = 0; emotion < eEmotions::EMOTIONS_COUNT; emotion++) {
if (Emotions[emotion] == 0) continue;
acc += Emotions[emotion] * (eEmotions::EMOTIONS_COUNT - 1) / sum_of_weight;
if (rand <= acc) {
return (eEmotions)emotion;
}
}
return eEmotions::Normal;
}
void FaceBehavior::Update() {
Timer.Update();
if (Timer.IsExpired()) {
Timer.Reset();
eEmotions newEmotion = GetRandomEmotion();
if (CurrentEmotion != newEmotion) {
GoToEmotion(newEmotion);
}
}
}
void FaceBehavior::GoToEmotion(eEmotions emotion) {
CurrentEmotion = emotion;
switch (CurrentEmotion) {
case eEmotions::Normal: _face.Expression.GoTo_Normal(); break;
case eEmotions::Angry: _face.Expression.GoTo_Angry(); break;
case eEmotions::Glee: _face.Expression.GoTo_Glee(); break;
case eEmotions::Happy: _face.Expression.GoTo_Happy(); break;
case eEmotions::Sad: _face.Expression.GoTo_Sad(); break;
case eEmotions::Worried: _face.Expression.GoTo_Worried(); break;
case eEmotions::Focused: _face.Expression.GoTo_Focused(); break;
case eEmotions::Annoyed: _face.Expression.GoTo_Annoyed(); break;
case eEmotions::Surprised: _face.Expression.GoTo_Surprised(); break;
case eEmotions::Skeptic: _face.Expression.GoTo_Skeptic(); break;
case eEmotions::Fustrated: _face.Expression.GoTo_Fustrated(); break;
case eEmotions::Unimpressed: _face.Expression.GoTo_Unimpressed(); break;
case eEmotions::Sleepy: _face.Expression.GoTo_Sleepy(); break;
case eEmotions::Suspicious: _face.Expression.GoTo_Suspicious(); break;
case eEmotions::Squint: _face.Expression.GoTo_Squint(); break;
case eEmotions::Furious: _face.Expression.GoTo_Furious(); break;
case eEmotions::Scared: _face.Expression.GoTo_Scared(); break;
case eEmotions::Awe: _face.Expression.GoTo_Awe(); break;
default: break;
}
}

52
FaceBehavior.h Normal file
View File

@@ -0,0 +1,52 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#ifndef _FACEBEHAVIOR_h
#define _FACEBEHAVIOR_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "arduino.h"
#else
#include "WProgram.h"
#endif
#include "FaceEmotions.hpp"
#include "AsyncTimer.h"
class Face;
class FaceBehavior
{
protected:
Face& _face;
public:
FaceBehavior(Face& face);
eEmotions CurrentEmotion;
float Emotions[eEmotions::EMOTIONS_COUNT];
AsyncTimer Timer;
void SetEmotion(eEmotions emotion, float value);
float GetEmotion(eEmotions emotion);
void Clear();
void Update();
eEmotions GetRandomEmotion();
void GoToEmotion(eEmotions emotion);
};
#endif

44
FaceEmotions.hpp Normal file
View File

@@ -0,0 +1,44 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#ifndef _FACEEMOTIONS_h
#define _FACEEMOTIONS_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "arduino.h"
#else
#include "WProgram.h"
#endif
enum eEmotions {
Normal=0,
Angry,
Glee,
Happy,
Sad,
Worried,
Focused,
Annoyed,
Surprised,
Skeptic,
Fustrated,
Unimpressed,
Sleepy,
Suspicious,
Squint,
Furious,
Scared,
Awe,
EMOTIONS_COUNT
};
#endif

181
FaceExpression.cpp Normal file
View File

@@ -0,0 +1,181 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#include "FaceExpression.h"
#include "Face.h"
FaceExpression::FaceExpression(Face& face) : _face(face)
{
}
void FaceExpression::ClearVariations()
{
_face.RightEye.Variation1.Clear();
_face.RightEye.Variation2.Clear();
_face.LeftEye.Variation1.Clear();
_face.LeftEye.Variation2.Clear();
_face.RightEye.Variation1.Animation.Restart();
_face.LeftEye.Variation1.Animation.Restart();
}
void FaceExpression::GoTo_Normal()
{
ClearVariations();
_face.RightEye.Variation1.Values.Height = 3;
_face.RightEye.Variation2.Values.Width = 1;
_face.LeftEye.Variation1.Values.Height = 2;
_face.LeftEye.Variation2.Values.Width = 2;
_face.RightEye.Variation1.Animation.SetTriangle(1000, 0);
_face.LeftEye.Variation1.Animation.SetTriangle(1000, 0);
_face.RightEye.TransitionTo(Preset_Normal);
_face.LeftEye.TransitionTo(Preset_Normal);
}
void FaceExpression::GoTo_Angry()
{
ClearVariations();
_face.RightEye.Variation1.Values.OffsetY = 2;
_face.LeftEye.Variation1.Values.OffsetY = 2;
_face.RightEye.Variation1.Animation.SetTriangle(300, 0);
_face.LeftEye.Variation1.Animation.SetTriangle(300, 0);
_face.RightEye.TransitionTo(Preset_Angry);
_face.LeftEye.TransitionTo(Preset_Angry);
}
void FaceExpression::GoTo_Glee()
{
ClearVariations();
_face.RightEye.Variation1.Values.OffsetY = 5;
_face.LeftEye.Variation1.Values.OffsetY = 5;
_face.RightEye.Variation1.Animation.SetTriangle(300, 0);
_face.LeftEye.Variation1.Animation.SetTriangle(300, 0);
_face.RightEye.TransitionTo(Preset_Glee);
_face.LeftEye.TransitionTo(Preset_Glee);
}
void FaceExpression::GoTo_Happy()
{
ClearVariations();
_face.RightEye.TransitionTo(Preset_Happy);
_face.LeftEye.TransitionTo(Preset_Happy);
}
void FaceExpression::GoTo_Sad()
{
ClearVariations();
_face.RightEye.TransitionTo(Preset_Sad);
_face.LeftEye.TransitionTo(Preset_Sad);
}
void FaceExpression::GoTo_Worried()
{
ClearVariations();
_face.RightEye.TransitionTo(Preset_Worried);
_face.LeftEye.TransitionTo(Preset_Worried_Alt);
}
void FaceExpression::GoTo_Focused()
{
ClearVariations();
_face.RightEye.TransitionTo(Preset_Focused);
_face.LeftEye.TransitionTo(Preset_Focused);
}
void FaceExpression::GoTo_Annoyed()
{
ClearVariations();
_face.RightEye.TransitionTo(Preset_Annoyed);
_face.LeftEye.TransitionTo(Preset_Annoyed_Alt);
}
void FaceExpression::GoTo_Surprised()
{
ClearVariations();
_face.RightEye.TransitionTo(Preset_Surprised);
_face.LeftEye.TransitionTo(Preset_Surprised);
}
void FaceExpression::GoTo_Skeptic()
{
ClearVariations();
_face.RightEye.TransitionTo(Preset_Skeptic);
_face.LeftEye.TransitionTo(Preset_Skeptic_Alt);
}
void FaceExpression::GoTo_Fustrated()
{
ClearVariations();
_face.RightEye.TransitionTo(Preset_Fustrated);
_face.LeftEye.TransitionTo(Preset_Fustrated);
}
void FaceExpression::GoTo_Unimpressed()
{
ClearVariations();
_face.RightEye.TransitionTo(Preset_Unimpressed);
_face.LeftEye.TransitionTo(Preset_Unimpressed_Alt);
}
void FaceExpression::GoTo_Sleepy()
{
ClearVariations();
_face.RightEye.TransitionTo(Preset_Sleepy);
_face.LeftEye.TransitionTo(Preset_Sleepy_Alt);
}
void FaceExpression::GoTo_Suspicious()
{
ClearVariations();
_face.RightEye.TransitionTo(Preset_Suspicious);
_face.LeftEye.TransitionTo(Preset_Suspicious_Alt);
}
void FaceExpression::GoTo_Squint()
{
ClearVariations();
_face.LeftEye.Variation1.Values.OffsetX = 6;
_face.LeftEye.Variation2.Values.OffsetY = 6;
_face.RightEye.TransitionTo(Preset_Squint);
_face.LeftEye.TransitionTo(Preset_Squint_Alt);
}
void FaceExpression::GoTo_Furious()
{
ClearVariations();
_face.RightEye.TransitionTo(Preset_Furious);
_face.LeftEye.TransitionTo(Preset_Furious);
}
void FaceExpression::GoTo_Scared()
{
ClearVariations();
_face.RightEye.TransitionTo(Preset_Scared);
_face.LeftEye.TransitionTo(Preset_Scared);
}
void FaceExpression::GoTo_Awe()
{
ClearVariations();
_face.RightEye.TransitionTo(Preset_Awe);
_face.LeftEye.TransitionTo(Preset_Awe);
}

53
FaceExpression.h Normal file
View File

@@ -0,0 +1,53 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#ifndef _FACEEXPRESSION_h
#define _FACEEXPRESSION_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "arduino.h"
#else
#include "WProgram.h"
#endif
class Face;
class FaceExpression {
protected:
Face& _face;
public:
FaceExpression(Face& face);
void ClearVariations();
void GoTo_Normal();
void GoTo_Angry();
void GoTo_Glee();
void GoTo_Happy();
void GoTo_Sad();
void GoTo_Worried();
void GoTo_Focused();
void GoTo_Annoyed();
void GoTo_Surprised();
void GoTo_Skeptic();
void GoTo_Fustrated();
void GoTo_Unimpressed();
void GoTo_Sleepy();
void GoTo_Suspicious();
void GoTo_Squint();
void GoTo_Furious();
void GoTo_Scared();
void GoTo_Awe();
};
#endif

68
LookAssistant.cpp Normal file
View File

@@ -0,0 +1,68 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#include "LookAssistant.h"
#include "Face.h"
#include "EyeTransformation.h"
LookAssistant::LookAssistant(Face& face) : _face(face), Timer(4000)
{
Timer.Start();
}
void LookAssistant::LookAt(float x, float y)
{
int16_t moveX_x;
int16_t moveY_x;
int16_t moveY_y;
float scaleY_x;
float scaleY_y;
moveX_x = -25 * x;
moveY_x = -3 * x;
moveY_y = 20 * y;
scaleY_x = 1.0 - x * 0.2;
scaleY_y = 1.0 - (y > 0 ? y : -y) * 0.4;
transformation.MoveX = moveX_x;
transformation.MoveY = moveY_y; //moveY_x + moveY_y;
transformation.ScaleX = 1.0;
transformation.ScaleY = scaleY_x * scaleY_y;
_face.RightEye.Transformation.SetDestin(transformation);
moveY_x = +3 * x;
scaleY_x = 1.0 + x * 0.2;
transformation.MoveX = moveX_x;
transformation.MoveY = + moveY_y; //moveY_x + moveY_y;
transformation.ScaleX = 1.0;
transformation.ScaleY = scaleY_x * scaleY_y;
_face.LeftEye.Transformation.SetDestin(transformation);
_face.RightEye.Transformation.Animation.Restart();
_face.LeftEye.Transformation.Animation.Restart();
}
void LookAssistant::Update()
{
Timer.Update();
if (Timer.IsExpired())
{
Timer.Reset();
auto x = random(-50, 50);
auto y = random(-50, 50);
LookAt((float)x / 100, (float)y / 100);
}
}

44
LookAssistant.h Normal file
View File

@@ -0,0 +1,44 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
#ifndef _LOOKASSISTANT_h
#define _LOOKASSISTANT_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "arduino.h"
#else
#include "WProgram.h"
#endif
#include "EyeTransformation.h"
#include "AsyncTimer.h"
class Face;
class LookAssistant
{
protected:
Face& _face;
public:
LookAssistant(Face& face);
Transformation transformation;
AsyncTimer Timer;
void LookAt(float x, float y);
void Update();
};
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

94
esp32-eyes.ino Normal file
View File

@@ -0,0 +1,94 @@
/***************************************************
Copyright (c) 2020 Luis Llamas
(www.luisllamas.es)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses
****************************************************/
// INCLUDES
#include "Face.h"
#include <Wire.h>
#include "Common.h"
// DEFINES
#define WIDTH 128 //180
#define HEIGHT 64 //240
#define EYE 40 //40
// GLOBALS
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ 4, /* data= */ 5);
Face *face;
String emotionName[] = {
"Normal",
"Angry",
"Glee",
"Happy",
"Sad",
"Worried",
"Focused",
"Annoyed",
"Surprised",
"Skeptic",
"Frustrated",
"Unimpressed",
"Sleepy",
"Suspicious",
"Squint",
"Furious",
"Scared",
"Awe"
};
void setup(void) {
Serial.begin(115200);
Serial.println(__FILE__ __DATE__);
face = new Face(WIDTH, HEIGHT, EYE);
// face->Expression.GoTo_Happy();
face->Behavior.Clear();
face->Behavior.SetEmotion(eEmotions::Glee, 1.0);
// Unlike almost every other Arduino application, I2C address scanner etc., u8g2 library
// requires 8-bit I2C address, so we shift the 7-bit address left by one.
u8g2.setI2CAddress(0x3C<<1);
u8g2.begin();
u8g2.clearBuffer(); // clear the internal memory
u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font
u8g2.drawStr(0,10,"Hello World!"); // write something to the internal memory
u8g2.sendBuffer(); // transfer internal memory to the display
}
void loop(){
static uint32_t counter = millis();
static uint8_t emotionno = 0;
static uint8_t emotionflag = 0;
static uint8_t n = 0;
if(millis() - counter > 6000) {
counter = millis();
emotionno = n++;
if(n >= EMOTIONS_COUNT) n = 0;
emotionflag = 1;
}
if(emotionflag) {
emotionflag = 0;
// Update the eyes' emotion
face->Behavior.Clear();
face->Behavior.SetEmotion((eEmotions)emotionno, 1.0);
// Draw a text string
//u8g2.setCursor((WIDTH-(emotionName[emotionno].length() * 5)) / 2, HEIGHT);
//u8g2.print(emotionName[emotionno]);
//u8g2.sendBuffer(); // transfer internal memory to the display
Serial.println(emotionName[emotionno]);
}
face->Update();
delay(10);
}