168 lines
3.9 KiB
C++
168 lines
3.9 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::addCurveSegment(const CurveSegment& segment) {
|
||
if (segment.motorID < NUM_CHANNELS) {
|
||
curves[segment.motorID].push_back(segment);
|
||
}
|
||
}
|
||
|
||
void Animation::clearCurves(uint8_t motorID) {
|
||
if (motorID < NUM_CHANNELS) {
|
||
curves[motorID].clear();
|
||
}
|
||
}
|
||
|
||
void Animation::clearAllCurves() {
|
||
for (int i = 0; i < NUM_CHANNELS; ++i) {
|
||
curves[i].clear();
|
||
}
|
||
}
|
||
|
||
uint16_t Animation::getMotorPosition(uint8_t motorID, uint16_t timeCS) {
|
||
if (motorID >= NUM_CHANNELS) return 0;
|
||
|
||
for (const auto& seg : curves[motorID]) {
|
||
if (timeCS >= seg.startTime && timeCS <= seg.endTime) {
|
||
float t = float(timeCS - seg.startTime) / (seg.endTime - seg.startTime);
|
||
|
||
// Convert uint16_t to float in range -1 to 1
|
||
auto toFloat = [](uint16_t v) {
|
||
return (float(v) / 65535.0f) * 2.0f - 1.0f;
|
||
};
|
||
|
||
float p0 = toFloat(seg.startPoint);
|
||
float p1 = toFloat(seg.startHandle);
|
||
float p2 = toFloat(seg.endHandle);
|
||
float p3 = toFloat(seg.endPoint);
|
||
|
||
float u = 1.0f - t;
|
||
float value = u*u*u*p0 + 3*u*u*t*p1 + 3*u*t*t*p2 + t*t*t*p3;
|
||
|
||
// Remap back to 0–4095 for PWM
|
||
return constrain((value + 1.0f) * 2047.5f, 0, 4095);
|
||
}
|
||
}
|
||
|
||
return 2048; // Default center if no segment matches
|
||
}
|
||
|
||
|
||
|
||
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;
|
||
|
||
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));
|
||
|
||
// Count total curve segments
|
||
uint16_t curveCount = 0;
|
||
for (uint8_t ch = 0; ch < NUM_CHANNELS; ch++) {
|
||
curveCount += curves[ch].size();
|
||
}
|
||
|
||
// Write curve count
|
||
file.write((uint8_t*)&curveCount, sizeof(curveCount));
|
||
|
||
// Write all curve segments
|
||
for (uint8_t ch = 0; ch < NUM_CHANNELS; ch++) {
|
||
for (const CurveSegment& seg : curves[ch]) {
|
||
file.write((uint8_t*)&seg, sizeof(CurveSegment));
|
||
}
|
||
}
|
||
|
||
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 curve count
|
||
uint16_t curveCount;
|
||
if (file.read((uint8_t*)&curveCount, sizeof(curveCount)) != sizeof(curveCount)) {
|
||
file.close();
|
||
return false;
|
||
}
|
||
|
||
// Clear existing curves
|
||
clearAllCurves();
|
||
|
||
// Read curve segments
|
||
for (uint16_t i = 0; i < curveCount; i++) {
|
||
CurveSegment seg;
|
||
if (file.read((uint8_t*)&seg, sizeof(CurveSegment)) != sizeof(CurveSegment)) {
|
||
file.close();
|
||
return false;
|
||
}
|
||
|
||
if (seg.motorID < NUM_CHANNELS) {
|
||
curves[seg.motorID].push_back(seg);
|
||
}
|
||
}
|
||
|
||
file.close();
|
||
return true;
|
||
}
|
||
|