diff --git a/oscope-of/src/ofApp.cpp b/oscope-of/src/ofApp.cpp index 9ec10c3..606a849 100644 --- a/oscope-of/src/ofApp.cpp +++ b/oscope-of/src/ofApp.cpp @@ -319,41 +319,46 @@ void ofApp::drawPanelLabel(int x, int y, const char* title, } void ofApp::drawScope4(int W, int H) { - // 1) Tunnel fullscreen en fond — fréquences pilotent tile size / dir. + // 1) Tunnel fullscreen en fond — fréquences pilotent tile size / dir + // + curve. Le centre du tunnel reste pile au milieu de l'écran ; + // les uniformes uPan / uCurve modulent l'intérieur sans bouger le + // cadre, donc le polar central reste aligné avec la nef. tunnel_->draw(0, 0, W, H); const float cx = W * 0.5f; const float cy = H * 0.5f; const float minSide = static_cast(std::min(W, H)); - // 2) Polar VRAIMENT central, focal — taille = 70% du min(W,H), centré - // pile sur (cx, cy). Couleurs des pétales pilotées par les bands. - const int polarS = static_cast(minSide * 0.70f); + // 2) WAVEFORM circulaire FULLSCREEN — les CH1/CH2/slow sont des anneaux + // concentriques qui font le tour complet (timeline → angle 0..2π). + // Mode circular activé, le draw remplit tout l'écran derrière le + // polar et le spectro ring. + waveform_->setCircular(true); + waveform_->draw(0, 0, W, H); + + // 3) Polar central, focal, centré PILE sur le tunnel (cx, cy). + // Couleurs des pétales par bands (bass→bleu, lead→rouge). + const int polarS = static_cast(minSide * 0.55f); polar_->draw(static_cast(cx) - polarS / 2, static_cast(cy) - polarS / 2, polarS, polarS); - // 3) Spectrogramme circulaire en anneau juste à l'extérieur du Polar - // (PolarVis::draw utilise radius = polarS*0.4 pour ses pétales, - // soit 0.28 de minSide depuis le centre — l'anneau commence après). - const float ringInner = minSide * 0.32f; - const float ringOuter = minSide * 0.44f; + // 4) Spectrogramme circulaire en anneau autour du polar. + const float ringInner = minSide * 0.27f; + const float ringOuter = minSide * 0.36f; spectro_->drawCircular(cx, cy, ringInner, ringOuter); - // 4) Deux satellites (Waveform + Lissajous) en orbite à 180°, situés - // AU-DELÀ de l'anneau spectro pour ne pas le couper. - const float bpm = osc_.bpm(); - const float orbit = ofGetElapsedTimef() * (0.04f + bpm * 0.0003f); - const float orbitR = minSide * 0.54f; - const int satW = static_cast(minSide * 0.18f); - const int satH = static_cast(minSide * 0.14f); - oscope::Visualizer* sats[2] = { waveform_.get(), lissajous_.get() }; - for (int i = 0; i < 2; ++i) { - const float a = orbit + i * PI; - const int sx = static_cast(cx + std::cos(a) * orbitR - satW * 0.5f); - const int sy = static_cast(cy + std::sin(a) * orbitR - satH * 0.5f); - sats[i]->draw(sx, sy, satW, satH); - } + // 5) Lissajous en arrière-plan additif fullscreen pour le glow. + ofEnableBlendMode(OF_BLENDMODE_ADD); + ofPushStyle(); + ofSetColor(255, 255, 255, 90); + lissajous_->draw(0, 0, W, H); + ofPopStyle(); + ofDisableBlendMode(); + + // Reset le flag circular pour que le mode Waveform standard (touche 5) + // garde son rendu CRT rectangulaire. + waveform_->setCircular(false); } void ofApp::draw() { diff --git a/oscope-of/src/visualizers/WaveformVis.cpp b/oscope-of/src/visualizers/WaveformVis.cpp index 790d966..30e2a97 100644 --- a/oscope-of/src/visualizers/WaveformVis.cpp +++ b/oscope-of/src/visualizers/WaveformVis.cpp @@ -125,7 +125,63 @@ void WaveformVis::update(const VisFrame& frame) { } } +void WaveformVis::drawCircular(int cx, int cy, float baseR, float maxR) { + ofPushStyle(); + ofPushMatrix(); + ofTranslate(cx, cy); + + // Cercle de référence (timeline) — discret, pour donner une sensation + // d'horloge. + ofNoFill(); + ofSetColor(0, 80, 50, 80); + ofSetLineWidth(1); + ofDrawCircle(0, 0, baseR); + + auto plotRing = [&](const std::vector& trace, ofColor base, + float radius, float ampScale) { + const int n = static_cast(trace.size()); + if (n < 2) return; + // Glow phosphor 3 passes + for (int pass = 3; pass >= 1; --pass) { + ofColor c = base; + c.a = static_cast(40 * pass); + ofSetColor(c); + ofSetLineWidth(2.0f * (4 - pass)); + ofBeginShape(); + for (int i = 0; i < n; ++i) { + const float t = static_cast(i) / (n - 1); + const float a = t * TWO_PI - HALF_PI; + const float r = radius + trace[i] * ampScale; + ofVertex(std::cos(a) * r, std::sin(a) * r); + } + ofEndShape(true); // closed loop + } + }; + + const float band = (maxR - baseR); + // Ordre : slow trace en arrière-plan, puis CH1 puis CH2 par-dessus. + if (showSlowOverlay_ && !slowTrace_.empty()) { + plotRing(slowTrace_, ofColor(80, 160, 200), baseR + band * 0.05f, + band * 0.45f); + } + plotRing(trace1_, ofColor(0, 255, 140), baseR + band * 0.30f, + band * 0.20f); + plotRing(trace2_, ofColor(255, 200, 60), baseR + band * 0.65f, + band * 0.20f); + + ofPopMatrix(); + ofPopStyle(); +} + void WaveformVis::draw(int x, int y, int w, int h) { + if (circular_) { + const float cx = x + w * 0.5f; + const float cy = y + h * 0.5f; + const float maxR = 0.46f * std::min(w, h); + const float baseR = maxR * 0.50f; + drawCircular(static_cast(cx), static_cast(cy), baseR, maxR); + return; + } ofPushStyle(); ofPushMatrix(); diff --git a/oscope-of/src/visualizers/WaveformVis.h b/oscope-of/src/visualizers/WaveformVis.h index c2fa9f0..c3cac9f 100644 --- a/oscope-of/src/visualizers/WaveformVis.h +++ b/oscope-of/src/visualizers/WaveformVis.h @@ -24,6 +24,10 @@ public: void setShowSlowOverlay(bool b) { showSlowOverlay_ = b; } void setScrollSpeed(float v) { scrollSpeed_ = std::max(0.0f, std::min(1.0f, v)); } void setSampleRate(float hz) { if (hz > 1.0f) sampleRateHz_ = hz; } + void setCircular(bool b) { circular_ = b; } + /// Render circulaire fullscreen — timeline mappée à l'angle (0..2π), + /// CH1/CH2/slow chacun sur un anneau concentrique. Pour Scope4. + void drawCircular(int cx, int cy, float baseR, float maxR); private: int w_ = 0, h_ = 0; @@ -49,6 +53,7 @@ private: // mais avec une fenêtre bien plus longue, superposé au trace audio. float slowMsPerDiv_ = 50.0f; bool showSlowOverlay_ = true; + bool circular_ = false; // 0 = freeze (capture par trame), 1 = scroll continu (oscilloscope rolling). // Défaut = 1 pour que le trace soit live tant que le user ne touche à rien. float scrollSpeed_ = 1.0f;