diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-04 23:56:56 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-04 23:56:56 +0100 |
| commit | 850932428ceea8422c9a0eef10f5e4df3be22c5d (patch) | |
| tree | 49e7d359a2feb755143e05650ea60675856f57ab /tools | |
| parent | dd9d3013d260f27f86b268c203a290f91431d8e5 (diff) | |
feat: Add Gantt chart visualization to seq_compiler
Implements ASCII Gantt chart generation for timeline debugging and visualization.
## New Feature
- Added --gantt=<output.txt> flag to seq_compiler
- Generates visual timeline showing sequences and effects on time axis
- Displays sequence priority, effect priority, and time ranges
- Shows explicit sequence end times with [END=...] markers
- Detects and warns about invalid time ranges (end < start)
## Usage
```bash
./build/seq_compiler assets/demo.seq src/generated/timeline.cc --gantt=timeline.txt
```
## Chart Format
- Time axis in seconds with 5-second markers
- Sequences shown as solid bars (█)
- Effects shown as shaded bars (▓) with sequence background (·)
- Labels include start/end times and priorities
- Legend and documentation at chart end
## Example Output
```
Time (s): 0 5 10 15 20 25 30
|----|----|----|----|----|----|----|
SEQ@0s [pri=0]
████████████████████████████████ (0-30s)
FlashEffect [pri=4]
▓▓·························· (0-1s)
HeptagonEffect [pri=0]
▓▓▓▓▓▓▓▓▓▓▓▓················ (0-10s)
```
## Benefits
- Visualize sequence overlap and layering
- Identify timing conflicts and gaps
- Verify effect priorities render in correct order
- Debug invalid time ranges
- Plan demo choreography visually
## Files Changed
- tools/seq_compiler.cc: Added generate_gantt_chart() function
- assets/demo.seq: Added usage documentation
- .gitignore: Exclude generated demo_timeline.txt
This debugging tool significantly improves timeline development workflow
by providing visual feedback on sequence and effect timing.
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/seq_compiler.cc | 141 |
1 files changed, 139 insertions, 2 deletions
diff --git a/tools/seq_compiler.cc b/tools/seq_compiler.cc index a4fd00c..548f467 100644 --- a/tools/seq_compiler.cc +++ b/tools/seq_compiler.cc @@ -32,6 +32,131 @@ std::string trim(const std::string& str) { return str.substr(first, (last - first + 1)); } +// Generate ASCII Gantt chart for timeline visualization +void generate_gantt_chart(const std::string& output_file, + const std::vector<SequenceEntry>& sequences, + float bpm, const std::string& demo_end_time) { + std::ofstream out(output_file); + if (!out.is_open()) { + std::cerr << "Warning: Could not open Gantt chart output file: " << output_file << "\n"; + return; + } + + // Find max time for the chart + float max_time = demo_end_time.empty() ? 0.0f : std::stof(demo_end_time); + for (const auto& seq : sequences) { + float seq_start = std::stof(seq.start_time); + for (const auto& eff : seq.effects) { + float eff_end = seq_start + std::stof(eff.end); + max_time = std::max(max_time, eff_end); + } + if (seq.end_time != "-1.0") { + max_time = std::max(max_time, seq_start + std::stof(seq.end_time)); + } + } + + // Chart configuration + const int chart_width = 100; + const float time_scale = chart_width / max_time; + + out << "Demo Timeline Gantt Chart\n"; + out << "==============================================================================\n"; + out << "BPM: " << bpm << ", Duration: " << max_time << "s"; + if (!demo_end_time.empty()) { + out << " (explicit end)"; + } + out << "\n\n"; + + // Time axis header + out << "Time (s): "; + for (int i = 0; i <= (int)max_time; i += 5) { + out << i; + int spacing = (i < 10) ? 4 : (i < 100) ? 3 : 2; + if (i + 5 <= max_time) { + for (int j = 0; j < spacing; ++j) out << " "; + } + } + out << "\n"; + out << " "; + for (int i = 0; i < chart_width; ++i) { + if (i % 5 == 0) out << "|"; + else out << "-"; + } + out << "\n\n"; + + // Draw sequences and effects + for (const auto& seq : sequences) { + float seq_start = std::stof(seq.start_time); + float seq_end = max_time; // Default: runs until end + + // Check if sequence has explicit end time + if (seq.end_time != "-1.0") { + seq_end = seq_start + std::stof(seq.end_time); + } else { + // Calculate implicit end from latest effect + for (const auto& eff : seq.effects) { + seq_end = std::max(seq_end, seq_start + std::stof(eff.end)); + } + } + + // Draw sequence bar + out << "SEQ@" << seq_start << "s [pri=" << seq.priority << "]"; + if (seq.end_time != "-1.0") { + out << " [END=" << seq_end << "s]"; + } + out << "\n"; + + int start_col = (int)(seq_start * time_scale); + int end_col = (int)(seq_end * time_scale); + out << " "; + for (int i = 0; i < chart_width; ++i) { + if (i >= start_col && i < end_col) out << "█"; + else out << " "; + } + out << " (" << seq_start << "-" << seq_end << "s)\n"; + + // Draw effects within sequence + for (const auto& eff : seq.effects) { + float eff_start = seq_start + std::stof(eff.start); + float eff_end = seq_start + std::stof(eff.end); + + // Truncate if sequence has explicit end time + if (seq.end_time != "-1.0") { + eff_end = std::min(eff_end, seq_end); + } + + out << " " << eff.class_name << " [pri=" << eff.priority << "]"; + if (eff_end < eff_start) { + out << " *** INVALID TIME RANGE ***"; + } + out << "\n"; + out << " "; + + int eff_start_col = (int)(eff_start * time_scale); + int eff_end_col = (int)(eff_end * time_scale); + + for (int i = 0; i < chart_width; ++i) { + if (i >= eff_start_col && i < eff_end_col) { + out << "▓"; + } else if (i >= start_col && i < end_col) { + out << "·"; // Show sequence background + } else { + out << " "; + } + } + out << " (" << eff_start << "-" << eff_end << "s)\n"; + } + out << "\n"; + } + + out << "==============================================================================\n"; + out << "Legend: █ Sequence ▓ Effect · Sequence background\n"; + out << "Priority: Higher numbers render later (on top)\n"; + + out.close(); + std::cout << "Gantt chart written to: " << output_file << "\n"; +} + // Convert beat notation to time in seconds // Supports: "64b" or "64" (beats), "32.0s" or "32.0" with decimal point (seconds) std::string convert_to_time(const std::string& value, float bpm) { @@ -63,13 +188,20 @@ std::string convert_to_time(const std::string& value, float bpm) { } int main(int argc, char* argv[]) { - if (argc != 3) { - std::cerr << "Usage: " << argv[0] << " <input.seq> <output.cc>\n"; + if (argc < 3 || argc > 4) { + std::cerr << "Usage: " << argv[0] << " <input.seq> <output.cc> [--gantt <gantt.txt>]\n"; std::cerr << "Example: " << argv[0] << " assets/demo.seq src/generated/timeline.cc\n"; + std::cerr << " " << argv[0] + << " assets/demo.seq src/generated/timeline.cc --gantt timeline.txt\n"; return 1; } + std::string gantt_output = ""; + if (argc == 4 && std::string(argv[3]).rfind("--gantt=", 0) == 0) { + gantt_output = std::string(argv[3]).substr(8); // Extract filename after --gantt= + } + std::ifstream in_file(argv[1]); if (!in_file.is_open()) { std::cerr << "Error: Could not open input file " << argv[1] << "\n"; @@ -248,5 +380,10 @@ int main(int argc, char* argv[]) { std::cout << "Successfully generated timeline with " << sequences.size() << " sequences.\n"; + // Generate Gantt chart if requested + if (!gantt_output.empty()) { + generate_gantt_chart(gantt_output, sequences, bpm, demo_end_time); + } + return 0; } |
