|
|
|
@@ -4,6 +4,7 @@
|
|
|
|
|
|
|
|
|
|
#include "theme_effects.h"
|
|
|
|
|
#include "low_spec.h"
|
|
|
|
|
#include "effect_clock.h"
|
|
|
|
|
#include "../schema/ui_schema.h"
|
|
|
|
|
#include "../layout.h"
|
|
|
|
|
#include <cmath>
|
|
|
|
@@ -47,7 +48,7 @@ ThemeEffects& ThemeEffects::instance() {
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
void ThemeEffects::beginFrame() {
|
|
|
|
|
if (!motion_frozen_) time_ = (float)ImGui::GetTime();
|
|
|
|
|
if (!motion_frozen_) time_ = ImGui::GetTime();
|
|
|
|
|
ImGuiViewport* vp = ImGui::GetMainViewport();
|
|
|
|
|
vpMinY_ = vp->WorkPos.y;
|
|
|
|
|
vpMaxY_ = vp->WorkPos.y + vp->WorkSize.y;
|
|
|
|
@@ -248,7 +249,7 @@ void ThemeEffects::loadFromTheme() {
|
|
|
|
|
ImU32 ThemeEffects::getAccentColor(float phaseOffset) const {
|
|
|
|
|
if (!enabled_ || !hue_cycle_.enabled) return IM_COL32(255, 218, 0, 255);
|
|
|
|
|
|
|
|
|
|
float hue = std::fmod(hue_cycle_.offset + time_ * hue_cycle_.speed + phaseOffset, 1.0f);
|
|
|
|
|
float hue = cyclePhase(time_, hue_cycle_.speed, (double)hue_cycle_.offset + phaseOffset);
|
|
|
|
|
if (hue < 0.0f) hue += 1.0f;
|
|
|
|
|
// Clamp to configured range
|
|
|
|
|
hue = hue_cycle_.offset + std::fmod(hue, std::max(0.01f, hue_cycle_.range));
|
|
|
|
@@ -293,7 +294,7 @@ void ThemeEffects::drawRainbowBorder(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax,
|
|
|
|
|
float rounding, float thickness) const {
|
|
|
|
|
if (!enabled_ || !rainbow_border_.enabled || rainbow_border_.stops.empty()) return;
|
|
|
|
|
|
|
|
|
|
float t = time_ * rainbow_border_.speed;
|
|
|
|
|
float t = cyclePhase(time_, rainbow_border_.speed);
|
|
|
|
|
|
|
|
|
|
// Draw the rainbow border as line segments around the rounded perimeter.
|
|
|
|
|
// Each segment gets a color sampled from the gradient at its position,
|
|
|
|
@@ -338,7 +339,7 @@ void ThemeEffects::drawShimmer(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax,
|
|
|
|
|
// Sweep position: band moves left to right across the panel
|
|
|
|
|
float period = 1.0f / std::max(0.01f, shimmer_.speed);
|
|
|
|
|
float totalTravel = w + shimmer_.width * 2.0f; // band enters and exits
|
|
|
|
|
float pos = std::fmod(time_ / period, 1.0f) * totalTravel - shimmer_.width;
|
|
|
|
|
float pos = cyclePhase(time_, 1.0 / period) * totalTravel - shimmer_.width;
|
|
|
|
|
|
|
|
|
|
// Band bounds (horizontal, ignoring angle for simplicity in this version)
|
|
|
|
|
float bandLeft = pMin.x + pos;
|
|
|
|
@@ -408,10 +409,10 @@ void ThemeEffects::drawSpecularGlare(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax,
|
|
|
|
|
float phaseSeed = seed + s * 3.14159265f;
|
|
|
|
|
|
|
|
|
|
// Slow Lissajous drift — each spot follows a unique orbit
|
|
|
|
|
float freqX = 2.0f * 3.14159265f * specular_glare_.speed;
|
|
|
|
|
float freqY = freqX * 1.618f; // golden ratio offset → non-repeating path
|
|
|
|
|
float driftX = std::sin(time_ * freqX + phaseSeed) * 0.3f + 0.5f;
|
|
|
|
|
float driftY = std::cos(time_ * freqY + phaseSeed * 1.3f) * 0.25f + 0.4f;
|
|
|
|
|
const double freqX = kEffectTwoPi * specular_glare_.speed;
|
|
|
|
|
const double freqY = freqX * 1.618; // golden ratio offset → non-repeating path
|
|
|
|
|
float driftX = radSin(time_, freqX, phaseSeed) * 0.3f + 0.5f;
|
|
|
|
|
float driftY = radCos(time_, freqY, phaseSeed * 1.3) * 0.25f + 0.4f;
|
|
|
|
|
|
|
|
|
|
float cx = pMin.x + w * driftX;
|
|
|
|
|
float cy = pMin.y + h * driftY;
|
|
|
|
@@ -484,7 +485,7 @@ void ThemeEffects::drawGlowPulse(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax,
|
|
|
|
|
if (!enabled_ || !glow_pulse_.enabled) return;
|
|
|
|
|
|
|
|
|
|
// Sinusoidal oscillation
|
|
|
|
|
float phase = std::sin(time_ * glow_pulse_.speed * 2.0f * 3.14159265f) * 0.5f + 0.5f;
|
|
|
|
|
float phase = cycleSin(time_, glow_pulse_.speed) * 0.5f + 0.5f;
|
|
|
|
|
float alpha = glow_pulse_.minAlpha + phase * (glow_pulse_.maxAlpha - glow_pulse_.minAlpha);
|
|
|
|
|
if (alpha <= 0.001f) return;
|
|
|
|
|
|
|
|
|
@@ -525,8 +526,7 @@ void ThemeEffects::drawGradientBorderShift(ImDrawList* dl, ImVec2 pMin, ImVec2 p
|
|
|
|
|
// Smooth sinusoidal oscillation between color A and color B.
|
|
|
|
|
// phaseOffset shifts where in the cycle this element sits so a wall of
|
|
|
|
|
// panels reads like veins at different depths rather than one pulse.
|
|
|
|
|
float phase = std::sin((time_ * gradient_border_.speed + phaseOffset)
|
|
|
|
|
* 2.0f * 3.14159265f) * 0.5f + 0.5f;
|
|
|
|
|
float phase = cycleSin(time_, gradient_border_.speed, phaseOffset) * 0.5f + 0.5f;
|
|
|
|
|
|
|
|
|
|
// Extract RGBA from both colors and lerp
|
|
|
|
|
RGB ca = unpackRGB(gradient_border_.colorA);
|
|
|
|
@@ -649,7 +649,7 @@ void ThemeEffects::drawEdgeTrace(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax,
|
|
|
|
|
float rounding) const {
|
|
|
|
|
if (!enabled_ || !edge_trace_.enabled) return;
|
|
|
|
|
|
|
|
|
|
float headPos = std::fmod(time_ * edge_trace_.speed, 1.0f);
|
|
|
|
|
float headPos = cyclePhase(time_, edge_trace_.speed);
|
|
|
|
|
float traceLen = edge_trace_.length;
|
|
|
|
|
|
|
|
|
|
RGB c = unpackRGB(edge_trace_.color);
|
|
|
|
@@ -705,12 +705,12 @@ void ThemeEffects::drawEmberRise(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax) const
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < ember_rise_.count; i++) {
|
|
|
|
|
// Golden-ratio spacing gives evenly distributed phases
|
|
|
|
|
float phase = std::fmod(time_ * ember_rise_.speed + i * 0.618033988f, 1.0f);
|
|
|
|
|
float phase = cyclePhase(time_, ember_rise_.speed, i * 0.618033988);
|
|
|
|
|
|
|
|
|
|
// Deterministic pseudo-random x position per particle
|
|
|
|
|
// Simple hash: sin of large prime multiples
|
|
|
|
|
float xHash = std::sin((float)(i + 1) * 127.1f) * 0.5f + 0.5f;
|
|
|
|
|
float xDrift = std::sin(time_ * 0.7f + i * 2.4f) * 4.0f * dp; // gentle sway
|
|
|
|
|
float xDrift = radSin(time_, 0.7, i * 2.4) * 4.0f * dp; // gentle sway
|
|
|
|
|
|
|
|
|
|
float x = pMin.x + w * xHash + xDrift;
|
|
|
|
|
float y = pMax.y - phase * (h + 8.0f); // rise from bottom past top
|
|
|
|
@@ -763,13 +763,13 @@ void ThemeEffects::drawViewportEmbers(ImDrawList* dl) const {
|
|
|
|
|
float vpAlpha = ember_rise_.alpha * 0.4f * bgOpacity_; // softer than panel embers
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < vpCount; i++) {
|
|
|
|
|
float phase = std::fmod(time_ * ember_rise_.speed * 0.6f + i * 0.618033988f, 1.0f);
|
|
|
|
|
float phase = cyclePhase(time_, ember_rise_.speed * 0.6, i * 0.618033988);
|
|
|
|
|
|
|
|
|
|
// Deterministic x position spread across viewport
|
|
|
|
|
float xHash = std::sin((float)(i + 1) * 127.1f) * 43758.5453f;
|
|
|
|
|
xHash = xHash - (int)xHash; // fractional part
|
|
|
|
|
if (xHash < 0) xHash += 1.0f;
|
|
|
|
|
float xDrift = std::sin(time_ * 0.5f + i * 1.7f) * 8.0f * dp;
|
|
|
|
|
float xDrift = radSin(time_, 0.5, i * 1.7) * 8.0f * dp;
|
|
|
|
|
|
|
|
|
|
float x = vpX + vpW * xHash + xDrift;
|
|
|
|
|
float y = vpY + vpH * (1.0f - phase); // rise from bottom to top
|
|
|
|
@@ -815,8 +815,7 @@ void ThemeEffects::drawSandstorm(ImDrawList* dl) const {
|
|
|
|
|
float windDy = std::sin(windRad); // vertical component (positive = downward)
|
|
|
|
|
|
|
|
|
|
// Global gust modulation: smoothly varies wind speed
|
|
|
|
|
float gust = 1.0f + sandstorm_.gustStrength *
|
|
|
|
|
std::sin(time_ * sandstorm_.gustSpeed * 2.0f * 3.14159265f);
|
|
|
|
|
float gust = 1.0f + sandstorm_.gustStrength * cycleSin(time_, sandstorm_.gustSpeed);
|
|
|
|
|
|
|
|
|
|
// Diagonal traversal distance (how far a particle travels across viewport)
|
|
|
|
|
float traversal = vpW + vpH * std::abs(windDy / std::max(0.01f, windDx));
|
|
|
|
@@ -844,14 +843,13 @@ void ThemeEffects::drawSandstorm(ImDrawList* dl) const {
|
|
|
|
|
|
|
|
|
|
// Phase: horizontal progress across viewport (0→1)
|
|
|
|
|
// Different particles have staggered starts via golden ratio
|
|
|
|
|
float phase = std::fmod(
|
|
|
|
|
time_ * sandstorm_.speed * speedMul * gust + i * 0.618033988f, 1.0f);
|
|
|
|
|
float phase = cyclePhase(time_, (double)sandstorm_.speed * speedMul * gust, i * 0.618033988);
|
|
|
|
|
|
|
|
|
|
// Y position: distributed across viewport with turbulent drift
|
|
|
|
|
float yBase = vpY + vpH * hash2;
|
|
|
|
|
float yTurb = std::sin(time_ * 0.8f + i * 3.7f) * vpH * 0.04f;
|
|
|
|
|
float yTurb = radSin(time_, 0.8, i * 3.7) * vpH * 0.04f;
|
|
|
|
|
// Additional gust-correlated vertical shift
|
|
|
|
|
float yGust = std::sin(time_ * sandstorm_.gustSpeed * 6.28318f + i * 1.3f) * vpH * 0.02f;
|
|
|
|
|
float yGust = radSin(time_, sandstorm_.gustSpeed * 6.28318, i * 1.3) * vpH * 0.02f;
|
|
|
|
|
|
|
|
|
|
// Position: particle blows right-to-left with downward drift
|
|
|
|
|
float x = vpX + vpW * (1.0f - phase) + phase * traversal * (1.0f - windDx) * 0.1f;
|
|
|
|
@@ -935,7 +933,7 @@ void ThemeEffects::drawPanelEffects(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax,
|
|
|
|
|
posKey = posKey - (int)posKey; // fractional 0..1
|
|
|
|
|
if (posKey < 0) posKey += 1.0f;
|
|
|
|
|
|
|
|
|
|
float headPos = std::fmod(time_ * edge_trace_.speed + posKey, 1.0f);
|
|
|
|
|
float headPos = cyclePhase(time_, edge_trace_.speed, posKey);
|
|
|
|
|
float traceLen = edge_trace_.length;
|
|
|
|
|
|
|
|
|
|
RGB c = unpackRGB(edge_trace_.color);
|
|
|
|
@@ -988,9 +986,9 @@ void ThemeEffects::drawPanelEffects(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax,
|
|
|
|
|
ImVec2(pMax.x + 2, pMax.y + 2), true);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < panelCount; i++) {
|
|
|
|
|
float phase = std::fmod(time_ * ember_rise_.speed + i * 0.618033988f + seed, 1.0f);
|
|
|
|
|
float phase = cyclePhase(time_, ember_rise_.speed, i * 0.618033988 + seed);
|
|
|
|
|
float xHash = std::sin((float)(i + 1) * 127.1f + seed * 100.0f) * 0.5f + 0.5f;
|
|
|
|
|
float xDrift = std::sin(time_ * 0.7f + i * 2.4f + seed) * 3.0f;
|
|
|
|
|
float xDrift = radSin(time_, 0.7, i * 2.4 + seed) * 3.0f;
|
|
|
|
|
|
|
|
|
|
float x = pMin.x + w * xHash + xDrift;
|
|
|
|
|
float y = pMax.y - phase * (h + 6.0f);
|
|
|
|
@@ -1038,7 +1036,7 @@ void ThemeEffects::drawViewportOverlay(ImDrawList* dl) const {
|
|
|
|
|
|
|
|
|
|
// Rotation: smoothly shift which color appears at which corner
|
|
|
|
|
if (viewport_overlay_.washRotateSpeed > 0.001f) {
|
|
|
|
|
float shift = std::fmod(time_ * viewport_overlay_.washRotateSpeed, 1.0f);
|
|
|
|
|
float shift = cyclePhase(time_, viewport_overlay_.washRotateSpeed);
|
|
|
|
|
ImU32 originals[4] = { corners[0], corners[1], corners[2], corners[3] };
|
|
|
|
|
|
|
|
|
|
for (int c = 0; c < 4; c++) {
|
|
|
|
@@ -1068,7 +1066,7 @@ void ThemeEffects::drawViewportOverlay(ImDrawList* dl) const {
|
|
|
|
|
// Smooth breathing via colour modulation (not alpha)
|
|
|
|
|
float colorMul = 1.0f;
|
|
|
|
|
if (viewport_overlay_.washPulseSpeed > 0.001f) {
|
|
|
|
|
float pulse = std::sin(time_ * viewport_overlay_.washPulseSpeed * 2.0f * 3.14159265f) * 0.5f + 0.5f;
|
|
|
|
|
float pulse = cycleSin(time_, viewport_overlay_.washPulseSpeed) * 0.5f + 0.5f;
|
|
|
|
|
colorMul = 1.0f - viewport_overlay_.washPulseDepth * (1.0f - pulse);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|