mirror of
https://github.com/VioletTides/KnockThru.git
synced 2026-10-27 23:00:18 +01:00
Recording logs one line with how each knock ended (counted, too soon, too light, other kind, ignored) instead of a line per knock, and the panel no longer logs frame timings. Canceling a recording logs the same summary. The learn step names the ring it asks people to wait for. The Saved banner says when the calibrated kind or KnockThru itself is turned off, since knocks would do nothing until it is back on. The calibration plan records the current decisions.
135 lines
5.5 KiB
C++
135 lines
5.5 KiB
C++
// Writes the dashboard panel in sample states to PNG files.
|
|
// Usage: panel_preview OUTPUT_DIRECTORY
|
|
// The calibration steps also get frame sequences in OUTPUT_DIRECTORY/frames, at 25
|
|
// frames a second, to review the animations.
|
|
#include "panel.hpp"
|
|
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
|
|
using namespace knockthru;
|
|
|
|
// Writes `seconds` of frames, starting at `start`, as NAME-000.png and up.
|
|
void frames(Panel& panel, const PanelState& state, const std::filesystem::path& out, const std::string& name,
|
|
double start, double seconds) {
|
|
std::filesystem::create_directories(out / "frames");
|
|
for (int i = 0; i < static_cast<int>(seconds * 25); ++i) {
|
|
std::ostringstream file;
|
|
file << name << "-" << std::setw(3) << std::setfill('0') << i << ".png";
|
|
panel.render(state, std::nullopt, start + i / 25.0).write_png(out / "frames" / file.str());
|
|
}
|
|
}
|
|
|
|
// Writes a frame at `t`, after one at `t` - 0.5 so a new screen has finished rising in.
|
|
void shot(Panel& panel, const PanelState& state, double t, const std::filesystem::path& path,
|
|
std::optional<std::pair<float, float>> hover = std::nullopt) {
|
|
panel.render(state, hover, t - 0.5);
|
|
panel.render(state, hover, t).write_png(path);
|
|
}
|
|
|
|
// Walks through the five calibration steps, as clicks and the app would.
|
|
void calibration(const std::filesystem::path& out, const PanelState& idle) {
|
|
Panel panel(KNOCKTHRU_FONTS);
|
|
panel.render(idle, std::nullopt, 10);
|
|
panel.click(1050, 620); // Calibrate on the main screen
|
|
shot(panel, idle, 10.6, out / "calibrate-1-pick.png", std::make_pair(600.0f, 400.0f));
|
|
frames(panel, idle, out, "pick", 20.8, 2.6);
|
|
panel.click(600, 400); // the surface card
|
|
shot(panel, idle, 30.6, out / "calibrate-2-learn.png");
|
|
frames(panel, idle, out, "learn", 40, 2);
|
|
|
|
PanelState state = idle;
|
|
state.message.clear();
|
|
CalibrationView view;
|
|
view.kind = KnockKind::Surface;
|
|
view.peaks = {92, 118, 76};
|
|
view.speeds = {1.1, 1.5, 0.8};
|
|
view.counted_t = 50;
|
|
view.knocked_t = 49.7;
|
|
view.seconds_left = 74;
|
|
state.calibration = view;
|
|
shot(panel, state, 50.6, out / "calibrate-3-record.png");
|
|
frames(panel, state, out, "record", 50, 1.2);
|
|
state.calibration->ignored = KnockKind::Together;
|
|
state.calibration->ignored_t = 51.5;
|
|
shot(panel, state, 51.8, out / "calibrate-3-record-hint.png");
|
|
// The get-ready ring after Start, with a knock that came too soon.
|
|
PanelState ready = state;
|
|
ready.calibration->peaks.clear();
|
|
ready.calibration->speeds.clear();
|
|
ready.calibration->started_t = 55;
|
|
ready.calibration->too_soon_t = 55.8;
|
|
shot(panel, ready, 56.2, out / "calibrate-3-record-ready.png");
|
|
|
|
// Robin's surface calibration on the Frame, 2026-10-15, with its one too-light knock raised to 64.
|
|
view.peaks = {80, 401, 115, 342, 446, 91, 57, 234, 64, 147};
|
|
view.speeds = {1.21, 0.85, 0.71, 0.81, 0.62, 0.77, 0.63, 0.53, 0.72, 1.71};
|
|
view.phase = CalibrationView::Phase::Trying;
|
|
state.calibration = view;
|
|
shot(panel, state, 70.6, out / "calibrate-4-try.png");
|
|
frames(panel, state, out, "try", 71, 2.6);
|
|
state.calibration->trial_passed = false;
|
|
state.calibration->trial_heard = "a triple surface knock";
|
|
state.calibration->trial_t = 74;
|
|
shot(panel, state, 74.6, out / "calibrate-4-try-miss.png");
|
|
state.calibration->trial_passed = true;
|
|
shot(panel, state, 75.6, out / "calibrate-4-try-pass.png");
|
|
|
|
state.calibration.reset();
|
|
state.calibration_saved = KnockKind::Surface;
|
|
shot(panel, state, 80, out / "calibrate-saved.png");
|
|
state.enabled[static_cast<int>(KnockKind::Surface)] = false;
|
|
shot(panel, state, 81, out / "calibrate-saved-kind-off.png");
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
const std::filesystem::path out = argc > 1 ? argv[1] : ".";
|
|
Panel panel(KNOCKTHRU_FONTS);
|
|
|
|
PanelState idle;
|
|
idle.room_view = false;
|
|
idle.opaque = false;
|
|
idle.app_key = "steam.app.620980";
|
|
idle.app_name = "Beat Saber";
|
|
idle.message = "Passthrough off (double together knock)";
|
|
panel.render(idle, std::make_pair(900.0f, 300.0f)).write_png(out / "panel-idle.png");
|
|
|
|
PanelState busy = idle;
|
|
busy.room_view = true;
|
|
busy.opaque = true;
|
|
busy.paused = true;
|
|
busy.app_blocked = true;
|
|
busy.enabled = {true, false, true};
|
|
panel.render(busy).write_png(out / "panel-paused.png");
|
|
|
|
PanelState broken;
|
|
broken.room_view_error = "Turn on CEF Remote Debugging in the Frame's Developer settings.";
|
|
broken.opaque = false;
|
|
panel.render(broken).write_png(out / "panel-broken.png");
|
|
|
|
calibration(out, idle);
|
|
|
|
PanelState none = idle;
|
|
none.enabled = {false, false, false};
|
|
Panel back_panel(KNOCKTHRU_FONTS);
|
|
back_panel.render(none).write_png(out / "panel-no-kinds.png");
|
|
|
|
// Opens the settings screen through its button, as a click would.
|
|
Panel settings_panel(KNOCKTHRU_FONTS);
|
|
settings_panel.render(idle);
|
|
settings_panel.click(460, 60);
|
|
settings_panel.render(idle, std::make_pair(600.0f, 200.0f)).write_png(out / "panel-settings.png");
|
|
idle.classic_bar_icon = true;
|
|
settings_panel.render(idle).write_png(out / "panel-settings-color.png");
|
|
|
|
Canvas icon(256, 256);
|
|
Panel::draw_icon(icon);
|
|
icon.write_png(out / "icon.png");
|
|
Panel::draw_icon(icon, IconStyle::BarWhite);
|
|
icon.write_png(out / "icon-dashboard.png");
|
|
Panel::draw_icon(icon, IconStyle::BarColor);
|
|
icon.write_png(out / "icon-dashboard-classic.png");
|
|
std::cout << "Wrote panel previews to " << out << "\n";
|
|
}
|