diff --git a/apps/desktop/src/protocol/paletteTiming.ts b/apps/desktop/src/protocol/paletteTiming.ts index fca30890..987daed0 100644 --- a/apps/desktop/src/protocol/paletteTiming.ts +++ b/apps/desktop/src/protocol/paletteTiming.ts @@ -14,7 +14,6 @@ const COLOR_PALETTE_EDITOR_RESET_UP_HOLD_MS = 1_500; const COLOR_PALETTE_EDITOR_RESET_LEFT_HOLD_MS = 3_000; const COLOR_PALETTE_EDITOR_MOVE_STEP_MS = 20; const COLOR_PALETTE_EDITOR_HUE_RESET_SETTLE_MS = 500; -const COLOR_PALETTE_EDITOR_DARK_VALUE_FINE_STEPS = 12; const BASIC_COLOR_GRID_ROWS = 7; const BASIC_COLOR_GRID_COLS = 12; const BASIC_COLOR_TAB_SETTLE_MS = 140; @@ -113,19 +112,6 @@ function rgbToHsv(red: number, green: number, blue: number): HsvColor { }; } -function splitPaletteValueDropSteps(valueDropSteps: number): { - coarseValueSteps: number; - fineValueSteps: number; -} { - const normalizedSteps = valueDropSteps < 0 ? 0 : valueDropSteps; - const fineValueSteps = Math.min(normalizedSteps, COLOR_PALETTE_EDITOR_DARK_VALUE_FINE_STEPS); - - return { - coarseValueSteps: normalizedSteps - fineValueSteps, - fineValueSteps, - }; -} - function timeoutMargin(options: PaletteTimingOptions): number { return options.includeTimeoutMargin ? PALETTE_CONFIG_TIMEOUT_MARGIN_MS : 0; } @@ -195,7 +181,6 @@ export function estimatePaletteConfigDurationMs( const hueSteps = Math.round(hueRatio * COLOR_PALETTE_EDITOR_HUE_STEP_COUNT); const saturationSteps = scaleChannelToSteps(hsv.saturation, COLOR_PALETTE_EDITOR_SATURATION_STEP_COUNT); const valueDropSteps = scaleChannelToSteps(1 - hsv.value, COLOR_PALETTE_EDITOR_VALUE_STEP_COUNT); - const { coarseValueSteps, fineValueSteps } = splitPaletteValueDropSteps(valueDropSteps); const generalPressMs = timing.buttonPressMs + timing.inputDelayMs; const menuPressMs = COLOR_PALETTE_MENU_PRESS_DURATION_MS + COLOR_PALETTE_MENU_INPUT_DELAY_MS; @@ -213,10 +198,7 @@ export function estimatePaletteConfigDurationMs( (saturationSteps > 0 ? saturationSteps * COLOR_PALETTE_EDITOR_MOVE_STEP_MS + timing.inputDelayMs : 0) + - (coarseValueSteps > 0 - ? coarseValueSteps * COLOR_PALETTE_EDITOR_MOVE_STEP_MS + timing.inputDelayMs - : 0) + - fineValueSteps * generalPressMs + + valueDropSteps * generalPressMs + 3 * menuPressMs + timing.inputDelayMs + timeoutMargin(options) diff --git a/apps/desktop/test/firmware-flash.test.ts b/apps/desktop/test/firmware-flash.test.ts index 7082a74e..5db6279d 100644 --- a/apps/desktop/test/firmware-flash.test.ts +++ b/apps/desktop/test/firmware-flash.test.ts @@ -287,9 +287,17 @@ test("controller firmware routes palette menu navigation through reliable input" new URL("../src/serial/sender.ts", import.meta.url), "utf8", ); + const paletteBody = /bool SwitchController::configurePaletteSlot[\s\S]*?(?=bool SwitchController::configureBasicPaletteSlot)/u + .exec(controllerSource)?.[0] ?? ""; assert.match(configSource, /COLOR_PALETTE_MENU_INPUT_DELAY_MS = 150/u); assert.match(senderSource, /from "\.\.\/protocol\/paletteTiming\.js"/u); + assert.doesNotMatch(paletteBody, /coarseValueSteps/u); + assert.doesNotMatch(paletteBody, /moveDirection\(\s*0,\s*1,/u); + assert.match( + paletteBody, + /for \(uint16_t step = 0; step < valueDropSteps; step \+= 1\)[\s\S]*ControllerButton::DpadDown/u, + ); assert.match( transportHeaderSource, /virtual bool pressButtonsReliable[\s\S]*return pressButtons\(buttonsMask, holdMs, settleMs\);[\s\S]*pressButtonReliable/u, diff --git a/apps/desktop/test/three-layer-fix.test.ts b/apps/desktop/test/three-layer-fix.test.ts index 18794fd4..6e5d1a88 100644 --- a/apps/desktop/test/three-layer-fix.test.ts +++ b/apps/desktop/test/three-layer-fix.test.ts @@ -12,6 +12,7 @@ import { pixelizeImage } from "../src/image/pixelize.js"; import { renderPreviewToBuffer } from "../src/image/renderPreview.js"; import { resizeImage } from "../src/image/resizeImage.js"; import { generateScanlineCommands } from "../src/path/scanline.js"; +import { estimatePaletteConfigDurationMs } from "../src/protocol/paletteTiming.js"; import { serializeCommands } from "../src/protocol/serializer.js"; import { SERIAL_OPEN_BOOT_TIMEOUT_MS, @@ -482,6 +483,10 @@ test("serial sender probes fresh ESP32 serial sessions before first sequenced co test("palette-config commands get enough timeout for calibrated custom colors", () => { const timing = { buttonPressMs: 100, inputDelayMs: 100, homeMs: 1800 }; + assert.equal(estimatePaletteConfigDurationMs(1, 0x4e, 0x32, 0x39, timing), 34_660); + assert.equal(estimatePaletteConfigDurationMs(2, 0x00, 0xff, 0x00, timing), 46_560); + assert.equal(estimatePaletteConfigDurationMs(0, 0x20, 0x20, 0x20, timing), 35_680); + assert.equal(estimatePaletteConfigDurationMs(0, 0x00, 0x00, 0x00, timing), 38_480); assert.equal(getAckTimeoutForCommand("PC 1 #4E3239", 20_000, timing), 90_000); assert.equal(getAckTimeoutForCommand("PC 2 #00FF00", 20_000, timing), 90_000); assert.equal(getAckTimeoutForCommand("PC 0 #202020", 20_000, timing), 90_000); @@ -491,6 +496,8 @@ test("palette-config commands get enough timeout for calibrated custom colors", test("palette-config timeout covers issue 74 custom colors", () => { const timing = { buttonPressMs: 65, inputDelayMs: 45, homeMs: 1800 }; + assert.equal(estimatePaletteConfigDurationMs(2, 0x93, 0x36, 0x43, timing), 23_845); + assert.equal(estimatePaletteConfigDurationMs(3, 0x5b, 0x2a, 0x33, timing), 26_065); assert.equal(getAckTimeoutForCommand("PC 2 #933643", 20_000, timing), 90_000); assert.equal(getAckTimeoutForCommand("PC 3 #5b2a33", 20_000, timing), 90_000); }); diff --git a/firmware/esp32/src/config.h b/firmware/esp32/src/config.h index 03f25176..662c0f34 100644 --- a/firmware/esp32/src/config.h +++ b/firmware/esp32/src/config.h @@ -23,7 +23,6 @@ constexpr uint16_t COLOR_PALETTE_EDITOR_RESET_UP_HOLD_MS = 1500; constexpr uint16_t COLOR_PALETTE_EDITOR_RESET_LEFT_HOLD_MS = 3000; constexpr uint16_t COLOR_PALETTE_EDITOR_MOVE_STEP_MS = 20; constexpr uint16_t COLOR_PALETTE_EDITOR_HUE_RESET_SETTLE_MS = 500; -constexpr uint8_t COLOR_PALETTE_EDITOR_DARK_VALUE_FINE_STEPS = 12; constexpr uint8_t BASIC_COLOR_GRID_ROWS = 7; constexpr uint8_t BASIC_COLOR_GRID_COLS = 12; constexpr uint16_t BASIC_COLOR_TAB_SETTLE_MS = 140; diff --git a/firmware/esp32/src/controller.cpp b/firmware/esp32/src/controller.cpp index c44fc155..79b45d29 100644 --- a/firmware/esp32/src/controller.cpp +++ b/firmware/esp32/src/controller.cpp @@ -293,10 +293,6 @@ bool SwitchController::configurePaletteSlot(int index, uint8_t red, uint8_t gree scaleChannelToSteps(hsv.saturation, COLOR_PALETTE_EDITOR_SATURATION_STEP_COUNT); const uint8_t valueDropSteps = scaleChannelToSteps(1.0f - hsv.value, COLOR_PALETTE_EDITOR_VALUE_STEP_COUNT); - const uint8_t fineValueSteps = valueDropSteps < COLOR_PALETTE_EDITOR_DARK_VALUE_FINE_STEPS - ? valueDropSteps - : COLOR_PALETTE_EDITOR_DARK_VALUE_FINE_STEPS; - const uint8_t coarseValueSteps = valueDropSteps - fineValueSteps; // Palette selection page. if (!pressPaletteMenuButton(transport_, ControllerButton::Y)) { @@ -359,16 +355,7 @@ bool SwitchController::configurePaletteSlot(int index, uint8_t red, uint8_t gree } } - if (coarseValueSteps > 0) { - if (!transport_.moveDirection( - 0, 1, static_cast(coarseValueSteps) * COLOR_PALETTE_EDITOR_MOVE_STEP_MS, inputDelayMs_)) { - return false; - } - } - - // Keep the last darkening steps discrete so near-black shades do not get - // flattened by the continuous analog hold. - for (uint16_t step = 0; step < fineValueSteps; step += 1) { + for (uint16_t step = 0; step < valueDropSteps; step += 1) { if (!transport_.pressButton(ControllerButton::DpadDown, buttonPressMs_, inputDelayMs_)) { return false; }