Skip to content
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
3 changes: 2 additions & 1 deletion lib/bademagic_module/models/mode.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ enum Mode {
diamond('0x0B'), // Diamond animation mode
feet('0x0C'), // Feet animation mode
brokenhearts('0x0D'), // Broken Hearts animation mode
cupid('0x0E'); // Cupid animation mode
cupid('0x0E'), // Cupid animation mode
cycle('0x0F'); // Cycle animation mode

final String hexValue;
const Mode(this.hexValue);
Expand Down
321 changes: 321 additions & 0 deletions lib/badge_animation/ani_cycle.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,321 @@
import 'package:badgemagic/badge_animation/animation_abstract.dart';
import 'dart:math';

class CycleAnimation extends BadgeAnimation {
static const int badgeHeight = 11;
static const int badgeWidth = 44;
static const int cycleHeight = 11;
static const int cycleWidth = 20;
static const int framesPerCycle =
8; // Total frames for complete back-and-forth movement (4 frames each direction)
static const int previewFramesPerCycle =
64; // Much more frames for ultra-smooth preview animation with wheel bounce

// Bicycle pattern matrix - 11x20 grid representing a bicycle
// 1 = LED on, 0 = LED off
static final List<List<int>> cycleMatrix = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], // Row 0 - Top
[
0,
0,
0,
0,
0,
0,
1,
1,
1,
1,
0,
0,
0,
0,
1,
0,
1,
0,
0,
0
], // Row 1 - Handlebars
[
0,
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
0,
0,
1,
0,
0,
1,
0,
0
], // Row 2 - Upper frame
[
0,
0,
0,
0,
0,
0,
0,
1,
1,
1,
1,
0,
0,
1,
0,
0,
0,
0,
0,
0
], // Row 3 - Main frame
[
0,
0,
0,
1,
1,
1,
0,
0,
1,
0,
0,
0,
1,
0,
0,
1,
1,
1,
0,
0
], // Row 4 - Wheels & frame
[
0,
0,
1,
0,
0,
0,
1,
0,
0,
1,
0,
1,
0,
0,
1,
0,
0,
0,
1,
0
], // Row 5 - Wheel centers
[
0,
1,
0,
0,
1,
0,
0,
1,
0,
0,
1,
0,
0,
1,
0,
0,
1,
0,
0,
1
], // Row 6 - Lower wheels
[
0,
1,
0,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
0,
0,
1,
1,
1,
0,
1
], // Row 7 - Bottom frame
[
0,
1,
0,
0,
1,
0,
0,
1,
0,
1,
1,
0,
0,
1,
0,
0,
1,
0,
0,
1
], // Row 8 - Ground
[0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0],
[0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0]
];

// Transfer function for badge: generate 8 frames from the infinite animation
List<List<List<bool>>> transferFrames() {
List<List<List<bool>>> frames = [];

// Pick 8 frames from the infinite sequence for transfer
// Based on analysis:
// Left-to-right fully visible: frames 13-18 (positions 3-21)
// Right-to-left fully visible: frames 45-50 (positions 3-21)
// Frame 1-4: Cycle moving left to right (fully visible)
// Frame 5-8: Cycle moving right to left (fully visible, flipped)
List<int> transferFrameIndices = [12, 15, 17, 18, 45, 47, 49, 50];

for (int i = 0; i < transferFrameIndices.length; i++) {
int animationIndex = transferFrameIndices[i];

// Each frame is badgeHeight x badgeWidth
List<List<bool>> canvas =
List.generate(badgeHeight, (_) => List.filled(badgeWidth, false));

// Calculate position for this frame
int cycleX = _calculateCycleX(animationIndex);
int cycleY =
0 + _calculateWheelBounce(animationIndex); // Add wheel bounce effect

// Draw the cycle at the calculated position
_drawCycle(canvas, cycleY, cycleX,
flip: _shouldFlipCycle(animationIndex));

frames.add(canvas);
}
return frames;
}

@override
void processAnimation(
int badgeHeight,
int badgeWidth,
int animationIndex,
List<List<bool>> processGrid,
List<List<bool>> canvas,
) {
// Clear canvas
for (int y = 0; y < badgeHeight; y++) {
for (int x = 0; x < badgeWidth; x++) {
canvas[y][x] = false;
}
}

// Calculate cycle position for this frame (infinite back and forth movement)
int cycleX = _calculateCycleX(animationIndex);
int cycleY =
0 + _calculateWheelBounce(animationIndex); // Add wheel bounce effect

// Draw the cycle
_drawCycle(canvas, cycleY, cycleX, flip: _shouldFlipCycle(animationIndex));
}

int _calculateCycleX(int animationIndex) {
// Calculate x position for the cycle
// Move back and forth: left to right, then right to left (infinite)
int frame = animationIndex % previewFramesPerCycle;

if (frame < previewFramesPerCycle / 2) {
// First half: left to right
double progress = frame / (previewFramesPerCycle / 2 - 1); // 0.0 to 1.0
// Use smooth easing for more natural movement
double easedProgress = _easeInOut(progress);
int startX = -cycleWidth;
int endX = badgeWidth;
int cycleX = startX + (easedProgress * (endX - startX)).round();
return cycleX;
} else {
// Second half: right to left
double progress = (frame - previewFramesPerCycle / 2) /
(previewFramesPerCycle / 2 - 1); // 0.0 to 1.0
// Use smooth easing for more natural movement
double easedProgress = _easeInOut(progress);
int startX = badgeWidth;
int endX = -cycleWidth;
int cycleX = startX + (easedProgress * (endX - startX)).round();
return cycleX;
}
}

double _easeInOut(double t) {
// Smooth easing function for more natural movement
// This creates acceleration and deceleration instead of linear movement
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
}

bool _shouldFlipCycle(int animationIndex) {
// Determine if cycle should be flipped based on direction
int frame = animationIndex % previewFramesPerCycle;
return frame >= previewFramesPerCycle / 2; // Flip when moving right to left
}

int _calculateWheelBounce(int animationIndex) {
// Add subtle wheel bounce effect to simulate tire rolling
int frame = animationIndex % previewFramesPerCycle;
double progress = frame / previewFramesPerCycle;
// Create a gentle sine wave bounce effect
double bounce = sin(progress * 2 * pi) * 0.5;
return bounce.round();
}

void _drawCycle(List<List<bool>> canvas, int top, int left,
{bool flip = false}) {
// Draw the bicycle pattern
for (int y = 0; y < cycleHeight; y++) {
for (int x = 0; x < cycleWidth; x++) {
if (cycleMatrix[y][x] == 1) {
int drawX = flip ? (left + cycleWidth - 1 - x) : (left + x);
int drawY = top + y;
if (drawY >= 0 &&
drawY < badgeHeight &&
drawX >= 0 &&
drawX < badgeWidth) {
canvas[drawY][drawX] = true;
}
}
}
}
}
}
15 changes: 12 additions & 3 deletions lib/providers/animation_badge_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import 'package:badgemagic/badge_animation/ani_emergency.dart';
import 'package:badgemagic/badge_animation/ani_beating_hearts.dart';
import 'package:badgemagic/badge_animation/ani_fireworks.dart';
import 'package:badgemagic/badge_animation/ani_equalizer.dart'; // new import of EqualizerAnimation
import 'package:badgemagic/badge_animation/ani_cycle.dart';

Map<int, BadgeAnimation?> animationMap = {
0: LeftAnimation(),
Expand All @@ -57,6 +58,7 @@ Map<int, BadgeAnimation?> animationMap = {
18: BeatingHeartsAnimation(), // Beating Hearts
19: FireworksAnimation(), // Fireworks
20: EqualizerAnimation(), // Digital Rain
21: CycleAnimation(), // Cycle
};

Map<int, BadgeEffect> effectMap = {
Expand Down Expand Up @@ -86,8 +88,8 @@ class AnimationBadgeProvider extends ChangeNotifier {
// Helper: returns true if a special animation (custom) is selected
bool isSpecialAnimationSelected() {
int idx = getAnimationIndex() ?? 0;
// Add all special animation indices here (including Equalizer at 20):
return [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20].contains(idx);
// Add all special animation indices here (including Equalizer at 20 and Cycle at 20):
return [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21].contains(idx);
}

// Call this to reset to text animation (LeftAnimation)
Expand All @@ -99,7 +101,12 @@ class AnimationBadgeProvider extends ChangeNotifier {
void calculateDuration(int speed) {
int idx = getAnimationIndex() ?? 0;
int newSpeed;
if (idx == 9 || idx == 10 || idx == 11 || idx == 12 || idx == 20) {
if (idx == 9 ||
idx == 10 ||
idx == 11 ||
idx == 12 ||
idx == 20 ||
idx == 21) {
//added EqualizerAnimation
// Use slower mapping for custom animations
// (aniSpeedStrategy already uses the slower mapping if you want, or you can hardcode)
Expand Down Expand Up @@ -296,6 +303,8 @@ class AnimationBadgeProvider extends ChangeNotifier {
await transferFireworksAnimation(badgeData, selectedSpeed);
} else if (aniIndex == 20) {
await transferEqualizerAnimation(badgeData, selectedSpeed);
} else if (aniIndex == 21) {
await transferCycleAnimation(badgeData, selectedSpeed);
} else {
await badgeData.checkAndTransfer(
inlineImageProvider.getController().text,
Expand Down
Loading
Loading