20#include <SFML/Graphics.hpp>
21#include <SFML/Graphics/Color.hpp>
22#include <SFML/System/Time.hpp>
23#include <SFML/Window/Keyboard.hpp>
35constexpr float fResidueW = 1 / (2 * fLedW - 2 * floor(fLedW) - 1);
55 static constexpr uint screenWidth = 1920;
56 static constexpr uint screenHeight = 1080;
57 static constexpr char title[] =
"lampda simulator";
59 static constexpr uint brightnessWidth = 480;
67 std::array<uint16_t, brightnessWidth> brightnessTracker {};
68 std::array<sf::RectangleShape, brightnessWidth> dots;
75 std::array<sf::RectangleShape, _LampTy::ledCount> shapes;
79 bool enableFont =
false;
80 bool enableText =
false;
81 if (font.loadFromFile(
"simulator/resources/DejaVuSansMono.ttf"))
87 fprintf(stderr,
"DejaVuSansMono.ttf not found, disabling text...");
91 sf::RenderWindow window(sf::VideoMode({screenWidth, screenHeight}), title);
93 sf::CircleShape buttonMask(simu.buttonSize);
94 sf::CircleShape indicator(simu.buttonSize + simu.buttonMargin);
95 buttonMask.setFillColor(sf::Color::Black);
97 sf::Vector2<int> mousePos = sf::Mouse::getPosition(window);
98 const float indCoordX = simu.buttonLeftPos;
99 const float indCoordY = (simu.ledSizePx + simu.ledPaddingPx) * (ledH + 3);
105 mock_registers::shouldStopThreads =
false;
110 start_electrical_mock();
123 uint64_t skipframe = 0;
124 while (window.isOpen())
126 uint64_t mouseClick = 0;
128 mousePos = sf::Mouse::getPosition(window);
129 enableText = enableFont && sf::Mouse::isButtonPressed(sf::Mouse::Button::Left);
130 enableText |= state.verbose;
134 while (window.pollEvent(event))
136 if (event.type == sf::Event::Closed)
142 if (event.type == sf::Event::KeyPressed)
144 auto kpressed =
event.key.code;
148 state.lastKeyPressed = 0;
150 case sf::Keyboard::Key::P:
151 state.lastKeyPressed =
'p';
153 case sf::Keyboard::Key::V:
154 state.lastKeyPressed =
'v';
156 case sf::Keyboard::Key::T:
157 state.lastKeyPressed =
't';
159 case sf::Keyboard::Key::H:
160 state.lastKeyPressed =
'h';
162 case sf::Keyboard::Key::G:
163 state.lastKeyPressed =
'g';
165 case sf::Keyboard::Key::J:
166 state.lastKeyPressed =
'j';
168 case sf::Keyboard::Key::K:
169 state.lastKeyPressed =
'k';
171 case sf::Keyboard::Key::D:
172 state.lastKeyPressed =
'd';
174 case sf::Keyboard::Key::U:
175 state.lastKeyPressed =
'u';
177 case sf::Keyboard::Key::I:
178 state.lastKeyPressed =
'i';
180 case sf::Keyboard::Key::R:
181 state.lastKeyPressed =
'r';
183 case sf::Keyboard::Key::C:
184 state.lastKeyPressed =
'c';
186 case sf::Keyboard::Key::Q:
187 state.lastKeyPressed =
'q';
194 if (event.type == sf::Event::KeyReleased)
197 if (state.lastKeyPressed ==
't')
199 state.paused =
false;
200 state.tickAndPause = 2;
204 if (state.lastKeyPressed ==
'p')
206 state.paused = !state.paused;
210 if (state.lastKeyPressed ==
'v')
212 state.verbose = !state.verbose;
216 if (state.lastKeyPressed ==
'g')
218 if (state.slowTimeFactor > 1.50f)
220 state.slowTimeFactor *= 0.95f;
222 else if (state.slowTimeFactor > 1.0f)
224 state.slowTimeFactor = ceil(state.slowTimeFactor * 20 - 1) / 20;
228 state.slowTimeFactor = std::max<float>(0.1f, state.slowTimeFactor - 0.05f);
230 fprintf(stderr,
"slower %f\n", state.slowTimeFactor);
234 if (state.lastKeyPressed ==
'h')
236 if (state.slowTimeFactor > 1.20f)
238 state.slowTimeFactor *= 1.05f;
240 else if (state.slowTimeFactor > 1.0f)
242 state.slowTimeFactor = ceil(state.slowTimeFactor * 20 + 1) / 20;
246 state.slowTimeFactor += 0.05f;
248 fprintf(stderr,
"faster %f\n", state.slowTimeFactor);
252 if (state.lastKeyPressed ==
'k')
255 if (fakeXorigin > ledW - 1)
257 fakeXend = std::max<int>(fakeXorigin - std::min<int>(4, ledW - 1 - fakeXorigin), 0);
261 if (state.lastKeyPressed ==
'j')
265 fakeXorigin = ledW - 1;
266 fakeXend = std::max<int>(fakeXorigin - std::min<int>(4, ledW - 1 - fakeXorigin), 0);
270 state.lastKeyPressed = 0;
274 if (event.type == sf::Event::MouseButtonPressed)
276 float dx = indCoordX - mousePos.x + simu.buttonSize;
277 float dy = indCoordY - mousePos.y + simu.buttonSize;
278 float norm = simu.buttonSize;
280 if (dx * dx + dy * dy < norm * norm)
289 state.isButtonPressed = sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Space);
293 state.isButtonPressed =
true;
296 if (state.tickAndPause > 0)
298 if (state.tickAndPause == 1)
302 state.tickAndPause -= 1;
311 if (mock_registers::isDeepSleep)
314 mock_registers::shouldStopThreads =
true;
322 mock_gpios::update_callbacks();
328 const bool isOutputEnabled = is_output_enabled();
331#ifdef LMBD_LAMP_TYPE__INDEXABLE
334 static curve_t brightnessCurve({curve_t::point_t {0, ::lampda::minimumAllowedBrightness_8},
335 curve_t::point_t {::lampda::brightness::absoluteMaximumBrightness, 255}});
338 const bool isVoltageHighEnough = mock_electrical::outputVoltage > 11.5;
341 state.colorBuffer[I] =
343 (isVoltageHighEnough ? ::lampda::user::_private::strip.getRawPixelColor(I) : 0xffffff) :
354 state.colorBuffer[I] = isOutputEnabled ? 0xffff00 : 0;
357 state.indicatorColor = mock_indicator::get_color();
360 const float ledSz = simu.ledSizePx;
361 const auto ledPadSz = simu.ledPaddingPx + simu.ledSizePx;
362 const auto ledOffset = ledSz / _LampTy::shiftPeriod;
364 const float Xbase = _LampTy::extraShiftTotal > 0 ? 0 : -_LampTy::extraShiftTotal;
365 const float Xextra = _LampTy::extraShiftTotal < 0 ? 0 : _LampTy::extraShiftTotal;
368 window.clear(sf::Color(13, 12, 11));
370 for (
int Ypos = 0; Ypos < ledH; ++Ypos)
372 int realRowSize = ledW;
373 if (_LampTy::allDeltaResiduesY[Ypos])
376 for (
int fXpos = -fakeXorigin; fXpos < realRowSize - fakeXend; ++fXpos)
384 Xpos = realRowSize + fXpos;
387 if (_LampTy::allDeltaResiduesY[Ypos])
392 auto& shape = shapes[I];
395 if (realPos.x != Xpos || realPos.y != Ypos)
398 shape.setSize({ledSz, ledSz});
400 int rXpos = fXpos + fakeXorigin + Xoff;
401 int rYpos = Ypos + Yoff;
402 int shiftR = _LampTy::extraShiftResiduesY[Ypos];
403 int shiftL = Ypos + shiftR + 1;
405 if (_LampTy::shiftResidue == 1 && _LampTy::shiftPerTurn < 0.1)
408 float shapeX = ledSz + rXpos * ledPadSz;
409 shapeX += ledOffset * Xbase;
410 shapeX += ledOffset * (shiftL % _LampTy::shiftPeriod);
411 shapeX -= (ledOffset - simu.ledPaddingPx / 2) * (Yoff - shiftR);
412 shapeX -= Xoff * simu.ledPaddingPx;
416 shapeX += simu.ledPaddingPx;
418 float shapeY = rYpos * ledPadSz;
419 shape.setPosition({shapeX, shapeY});
421 const uint32_t color = state.colorBuffer[I];
422 float b = (color & 0xff);
423 float g = ((color >> 8) & 0xff);
424 float r = ((color >> 16) & 0xff);
426#ifndef LMBD_DEBUG_SIMU_REALCOLORS
427 if (state.brightness != 255)
430 r = (uint16_t(r) * state.brightness + state.brightness) >> 8;
431 g = (uint16_t(g) * state.brightness + state.brightness) >> 8;
432 b = (uint16_t(b) * state.brightness + state.brightness) >> 8;
436 shape.setFillColor(sf::Color(r, g, b));
442 int MouseYpos = mousePos.y;
443 int MouseXpos = mousePos.x;
445 auto shapeXY = shape.getPosition();
446 float dx = shapeXY.x + ledSz / 2 - MouseXpos;
447 float dy = shapeXY.y + ledSz / 2 - MouseYpos;
450 if (abs(2 * dx) > ledPadSz || abs(2 * dy) > ledPadSz)
454 float largestX = ledSz + (ledW + 1 + fakeXorigin - fakeXend) * ledPadSz + ledOffset * (Xextra + Xbase);
455 float largestY = (ledH + Yoff) * ledPadSz + 8;
457 auto str =
"(" + std::to_string(Xpos) +
", " + std::to_string(Ypos) +
")";
459 sf::Text text(str, font, 12);
460 sf::Vector2f where(largestX, largestY);
461 text.setPosition(where);
467 auto str = std::to_string(Ypos);
468 if (_LampTy::allDeltaResiduesY[Ypos])
471 sf::Text text(str, font, 12);
472 sf::Vector2f where(largestX, shapeXY.y + ledSz / 4);
473 text.setPosition(where);
479 auto str = std::to_string(Xpos);
483 sf::Text text(str, font, 12);
484 sf::Vector2f where(shapeXY.x, largestY);
485 text.setPosition(where);
491 auto str =
"(" + std::to_string(
int(r)) +
", ";
492 str += std::to_string(
int(g)) +
", ";
493 str += std::to_string(
int(b)) +
")";
494 str +=
", " + std::to_string(I);
496 sf::Text text(str, font, 12);
497 sf::Vector2f where(indCoordX + simu.buttonSize * 3, indCoordY - 24.f);
498 text.setPosition(where);
509 const uint32_t indicatorColor = state.indicatorColor;
510 float b = (indicatorColor & 0xff);
511 float g = ((indicatorColor >> 8) & 0xff);
512 float r = ((indicatorColor >> 16) & 0xff);
513 indicator.setFillColor(sf::Color(r, g, b));
514 indicator.setPosition({indCoordX, indCoordY});
515 buttonMask.setPosition({indCoordX + simu.buttonMargin, indCoordY + simu.buttonMargin});
516 window.draw(indicator);
517 window.draw(buttonMask);
520 float now = (time.getElapsedTime().asMilliseconds() * simu.fps) / 1000.f;
521 uint32_t trackerPos = now / simu.brightnessRate;
523 trackerPos = trackerPos % brightnessTracker.size();
524 brightnessTracker[trackerPos] = state.brightness;
525 brightnessTracker[(trackerPos + 1) % brightnessTracker.size()] = 0;
527 float xDotsOrigin = indCoordX + simu.buttonSize * 2 + simu.buttonMargin * 2 + 16.f;
528 float yDotsOrigin = indCoordY + simu.brightnessScale;
530 for (
size_t I = 0; I < brightnessTracker.size(); ++I)
534 float v = (brightnessTracker[I] * simu.brightnessScale) / 256;
535 float x = xDotsOrigin + I;
536 float y = yDotsOrigin - v;
538 dot.setPosition({x, y});
539 dot.setFillColor(sf::Color(0, 0xff, 0));
547 stop_electrical_mock();
Given a set of points, will fit multiple linear segments to it.
Definition: curves.h:36
General electrical simulation of the Lampda board.
Define specific needed user functions.
Main input point of the whole program.
Handle the physical simulation paremeters of a real lamp.
brightness_t get_brightness()
Return the current brightness value (in range 0 - brightness::absoluteMaximumBrightness)....
Definition: brightness_handle.cpp:39
@ indexable
Equivalent to LMBD_LAMP_TYPE__INDEXABLE.
static constexpr uint16_t to_strip(uint16_t, uint16_t)
convert grid coordinates to strip index
Definition: lamp_type.hpp:1100
static constexpr XYTy strip_to_XY(uint16_t n)
convert strip index to grid coordinates
Definition: lamp_type.hpp:1132
void main_setup()
Setup of the program, call once on systel start.
Definition: global.cpp:82
void main_loop(const uint32_t addedDelay)
Run the main program loop.
Definition: global.cpp:228
GlobalSimStateTy state
Store the global simulation state.
Definition: simulator_state.cpp:7
Simulator dedicated namespace.
Definition: default_simulation.h:8
void read_and_update_parameters()
Read parameters from the parameter file, and update the simulation.
Definition: parameter_parser.h:24
Handle the modification of the simulation parameters by a file.
Contain some simulation state.
static constexpr float maxWidthFloat
Width as a precise floating point number, equal to stripXCoordinates.
Definition: lamp_type.hpp:369
static constexpr uint16_t maxOverflowHeight
Larger height, taken as the absolute maximum Y coordinate, overflowing.
Definition: lamp_type.hpp:393
static constexpr LampTypes flavor
Which lamp flavor is currently used by the implementation?
Definition: lamp_type.hpp:336
static constexpr uint16_t maxWidth
(indexable) Width of "pixel space" w/ lamp taken as a LED matrix
Definition: lamp_type.hpp:366
static constexpr uint16_t ledCount
(indexable) Count of indexable LEDs on the lamp
Definition: lamp_type.hpp:353
User defined constants, relative to specific lamp types.