HansonServo/animation.cpp

221 lines
5.6 KiB
C++

#include "animation.h"
Animation::Animation() {
clear();
memcpy(header.magic, "ANIM", 4);
header.version = 1;
header.frameRate = FRAMES_PER_SECOND;
header.frameCount = MAX_FRAMES;
memset(header.reserved, 0, sizeof(header.reserved));
}
void Animation::setFrame(uint16_t frameIndex, uint16_t channel, uint16_t value) {
if (frameIndex < MAX_FRAMES && channel < NUM_CHANNELS) {
data[frameIndex][channel] = value;
}
}
uint16_t Animation::getFrame(uint16_t frameIndex, uint16_t channel) const {
if (frameIndex < MAX_FRAMES && channel < NUM_CHANNELS) {
return data[frameIndex][channel];
}
return 0;
}
bool Animation::getFramePositions(uint16_t frameIndex, uint16_t* outPositions) {
if (frameIndex >= header.frameCount) return false;
for (uint8_t ch = 0; ch < NUM_CHANNELS; ch++) {
outPositions[ch] = getFrame(frameIndex, ch);
}
return true;
}
void Animation::addKeyframe(uint8_t motorId, uint16_t frame, uint16_t position) {
keyframes.push_back({ motorId, frame, position });
}
const std::vector<Keyframe>& Animation::getKeyframes() const {
return keyframes;
}
void Animation::printKeyframes() {
const std::vector<Keyframe>& frames = getKeyframes();
Serial.println("Keyframes:");
for (size_t i = 0; i < frames.size(); ++i) {
const Keyframe& kf = frames[i];
Serial.print(" [");
Serial.print(i);
Serial.print("] Motor ID: ");
Serial.print(kf.motorId);
Serial.print(", Frame: ");
Serial.print(kf.frame);
Serial.print(", Position: ");
Serial.println(kf.position);
}
}
void Animation::clear() {
memset(data, 0, sizeof(data));
}
uint16_t* Animation::getRawData() {
return &data[0][0];
}
size_t Animation::getSize() const {
return sizeof(data);
}
uint16_t Animation::getFrameCount() const {
return header.frameCount;
}
bool Animation::saveToFile(const char* filename) {
// Auto-detect actual frame count
uint16_t lastFrame = 0;
for (uint16_t frame = 0; frame < MAX_FRAMES; frame++) {
for (uint8_t ch = 0; ch < NUM_CHANNELS; ch++) {
if (data[frame][ch] != 0) {
lastFrame = frame;
break;
}
}
}
header.frameCount = lastFrame + 1; // +1 because frame index starts at 0
File file = FFat.open(filename, FILE_WRITE);
if (!file) return false;
// Write header and motion data
file.write((uint8_t*)&header, sizeof(header));
file.write((uint8_t*)data, sizeof(data));
// Write keyframe count
uint16_t keyframeCount = keyframes.size();
file.write((uint8_t*)&keyframeCount, sizeof(keyframeCount));
// Write keyframes
for (const Keyframe& kf : keyframes) {
file.write(kf.motorId);
file.write((uint8_t*)&kf.frame, sizeof(kf.frame));
file.write((uint8_t*)&kf.position, sizeof(kf.position));
}
file.close();
return true;
}
bool Animation::loadFromFile(const char* filename) {
File file = FFat.open(filename, FILE_READ);
if (!file) return false;
// Read and validate header
AnimationHeader tempHeader;
if (file.read((uint8_t*)&tempHeader, sizeof(tempHeader)) != sizeof(tempHeader)) {
file.close();
return false;
}
if (strncmp(tempHeader.magic, "ANIM", 4) != 0 || tempHeader.version != 1) {
file.close();
return false;
}
header = tempHeader;
// Read motion data
size_t expectedSize = sizeof(data);
if (file.read((uint8_t*)data, expectedSize) != expectedSize) {
file.close();
return false;
}
// Read keyframe count
uint16_t keyframeCount;
if (file.read((uint8_t*)&keyframeCount, sizeof(keyframeCount)) != sizeof(keyframeCount)) {
file.close();
return false;
}
// Read keyframes
keyframes.clear();
for (uint16_t i = 0; i < keyframeCount; i++) {
Keyframe kf;
if (file.read(&kf.motorId, 1) != 1 ||
file.read((uint8_t*)&kf.frame, sizeof(kf.frame)) != sizeof(kf.frame) ||
file.read((uint8_t*)&kf.position, sizeof(kf.position)) != sizeof(kf.position)) {
file.close();
return false;
}
keyframes.push_back(kf);
}
file.close();
return true;
}
void Animation::createSampleSweep(uint8_t seconds) {
clear();
const uint16_t sweepFrames = FRAMES_PER_SECOND * seconds;
const uint16_t totalFrames = sweepFrames * 2; // Up and down
for (uint16_t frame = 0; frame < totalFrames; frame++) {
float progress;
if (frame < sweepFrames) {
// Sweep up: 0 → 1023
progress = (float)frame / (sweepFrames - 1);
} else {
// Sweep down: 1023 → 0
progress = 1.0f - ((float)(frame - sweepFrames) / (sweepFrames - 1));
}
uint16_t value = (uint16_t)(progress * 1023);
for (uint8_t ch = 0; ch < NUM_CHANNELS; ch++) {
setFrame(frame, ch, value);
}
}
header.frameCount = totalFrames;
// 🧩 Add keyframes to match the sweep motion
for (uint8_t ch = 0; ch < NUM_CHANNELS; ch++) {
addKeyframe(ch, 0, 0); // Start at 0
addKeyframe(ch, sweepFrames - 1, 1023); // Peak
addKeyframe(ch, totalFrames - 1, 0); // Return to 0
}
}
void Animation::createStaggeredSweep(uint8_t seconds) {
clear();
const uint16_t sweepFrames = FRAMES_PER_SECOND * seconds;
const uint16_t totalFrames = sweepFrames * 2; // Up and down
for (uint16_t frame = 0; frame < totalFrames; frame++) {
float progress;
if (frame < sweepFrames) {
progress = (float)frame / (sweepFrames - 1); // 0 → 1
} else {
progress = 1.0f - ((float)(frame - sweepFrames) / (sweepFrames - 1)); // 1 → 0
}
for (uint8_t ch = 0; ch < NUM_CHANNELS; ch++) {
float channelProgress = (ch % 2 == 0) ? progress : (1.0f - progress);
uint16_t value = (uint16_t)(channelProgress * 1023);
setFrame(frame, ch, value);
}
}
header.frameCount = totalFrames;
}