// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ScopeCanvas.h" #include "wx/wxprec.h" #ifndef WX_PRECOMP #include "wx/wx.h" #endif #if !wxUSE_GLCANVAS #error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library" #endif #include "CubicSDR.h" #include #include wxBEGIN_EVENT_TABLE(ScopeCanvas, wxGLCanvas) EVT_PAINT(ScopeCanvas::OnPaint) EVT_IDLE(ScopeCanvas::OnIdle) EVT_MOTION(ScopeCanvas::OnMouseMoved) EVT_LEFT_DOWN(ScopeCanvas::OnMouseDown) EVT_LEFT_UP(ScopeCanvas::OnMouseReleased) EVT_RIGHT_DOWN(ScopeCanvas::OnMouseRightDown) EVT_RIGHT_UP(ScopeCanvas::OnMouseRightReleased) EVT_LEAVE_WINDOW(ScopeCanvas::OnMouseLeftWindow) EVT_ENTER_WINDOW(ScopeCanvas::OnMouseEnterWindow) wxEND_EVENT_TABLE() ScopeCanvas::ScopeCanvas(wxWindow *parent, const wxGLAttributes& dispAttrs) : InteractiveCanvas(parent, dispAttrs), ppmMode(false), ctr(0), ctrTarget(0), dragAccel(0) { glContext = new ScopeContext(this, &wxGetApp().GetContext(this), wxGetApp().GetContextAttributes()); inputData->set_max_num_items(2); bgPanel.setFill(GLPanel::GLPANEL_FILL_GRAD_Y); bgPanel.setSize(1.0, 0.5f); bgPanel.setPosition(0.0, -0.5f); panelSpacing = 0.4f; parentPanel.addChild(&scopePanel); parentPanel.addChild(&spectrumPanel); parentPanel.setFill(GLPanel::GLPANEL_FILL_NONE); scopePanel.setSize(1.0,-1.0); spectrumPanel.setSize(1.0,-1.0); showDb = true; spectrumPanel.setShowDb(showDb); //dB offset is a RF value, has no meaning in audio, disable it. spectrumPanel.setUseDBOffset(false); } ScopeCanvas::~ScopeCanvas() = default; bool ScopeCanvas::scopeVisible() { float panelInterval = (2.0 + panelSpacing); ctrTarget = abs(round(ctr / panelInterval)); if (ctrTarget == 0 || dragAccel || (ctr != ctrTarget)) { return true; } return false; } bool ScopeCanvas::spectrumVisible() { float panelInterval = (2.0 + panelSpacing); ctrTarget = abs(round(ctr / panelInterval)); if (ctrTarget == 1 || dragAccel || (ctr != ctrTarget)) { return true; } return false; } void ScopeCanvas::setDeviceName(std::string device_name) { deviceName = device_name; deviceName.append(" "); } void ScopeCanvas::setPPMMode(bool ppmMode_in) { ppmMode = ppmMode_in; } bool ScopeCanvas::getPPMMode() const { return ppmMode; } void ScopeCanvas::setShowDb(bool show) { this->showDb = show; } bool ScopeCanvas::getShowDb() const { return showDb; } void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { // wxPaintDC dc(this); const wxSize ClientSize = GetClientSize() * GetContentScaleFactor(); ScopeRenderDataPtr avData; while (inputData->try_pop(avData)) { if (!avData->spectrum) { scopePanel.setMode(avData->mode); if (!avData->waveform_points.empty()) { scopePanel.setPoints(avData->waveform_points); } } else { if (!avData->waveform_points.empty()) { spectrumPanel.setPoints(avData->waveform_points); spectrumPanel.setFloorValue(avData->fft_floor); spectrumPanel.setCeilValue(avData->fft_ceil); spectrumPanel.setBandwidth((avData->sampleRate/2)*1000); spectrumPanel.setFreq((avData->sampleRate/4)*1000); spectrumPanel.setFFTSize(avData->fft_size); spectrumPanel.setShowDb(showDb); } } } glContext->SetCurrent(*this); initGLExtensions(); glViewport(0, 0, ClientSize.x, ClientSize.y); // TODO: find out why frontbuffer drawing has stopped working in wx 3.1.0? // if (scopePanel.getMode() == ScopePanel::SCOPE_MODE_XY && !spectrumVisible()) { // glDrawBuffer(GL_FRONT); // glContext->DrawBegin(false); // } else { // glDrawBuffer(GL_BACK); glContext->DrawBegin(); bgPanel.setFillColor(ThemeMgr::mgr.currentTheme->scopeBackground * 3.0, RGBA4f(0,0,0,1)); bgPanel.calcTransform(CubicVR::mat4::identity()); bgPanel.draw(); // } glMatrixMode(GL_PROJECTION); glLoadIdentity(); glLoadMatrixf(CubicVR::mat4::perspective(45.0, 1.0, 1.0, 1000.0).to_ptr()); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); CubicVR::mat4 modelView = CubicVR::mat4::lookat(0, 0, -1.205f, 0, 0, 0, 0, -1, 0); float panelWidth = 1.0; float panelInterval = (panelWidth * 2.0 + panelSpacing); if (!mouseTracker.mouseDown()) { ctrTarget = round(ctr / panelInterval); if (ctrTarget < -1.0) { ctrTarget = -1.0; } else if (ctrTarget > 0.0) { ctrTarget = 0.0; } ctrTarget *= panelInterval; if (!dragAccel) { if (ctr != ctrTarget) { ctr += (ctrTarget-ctr)*0.2; } if (abs(ctr - ctrTarget) < 0.001) { ctr=ctrTarget; } } else { dragAccel -= dragAccel * 0.1; if ((abs(dragAccel) < 0.2) || (ctr < (ctrTarget-panelInterval/2.0)) || (ctr > (ctrTarget+panelInterval/2.0)) ) { dragAccel = 0; } else { ctr += dragAccel; } } } float roty = 0; scopePanel.setPosition(ctr, 0); if (scopeVisible()) { scopePanel.contentsVisible = true; roty = atan2(scopePanel.pos[0],1.2); scopePanel.rot[1] = -(roty * (180.0 / M_PI)); } else { scopePanel.contentsVisible = false; } spectrumPanel.setPosition(panelInterval+ctr, 0); if (spectrumVisible()) { spectrumPanel.setFillColor(ThemeMgr::mgr.currentTheme->scopeBackground * 2.0, RGBA4f(0,0,0,1)); spectrumPanel.contentsVisible = true; roty = atan2(spectrumPanel.pos[0],1.2); spectrumPanel.rot[1] = -(roty * (180.0 / M_PI)); } else { spectrumPanel.contentsVisible = false; } parentPanel.calcTransform(modelView); parentPanel.draw(); if (spectrumVisible()) { spectrumPanel.drawChildren(); } glLoadMatrixf(scopePanel.transform.to_ptr()); if (!deviceName.empty()) { glContext->DrawDeviceName(deviceName); } glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glContext->DrawTunerTitles(ppmMode); glContext->DrawEnd(); // if (scopePanel.getMode() != ScopePanel::SCOPE_MODE_XY || spectrumVisible()) { SwapBuffers(); // } } void ScopeCanvas::OnIdle(wxIdleEvent &event) { Refresh(); event.RequestMore(); } ScopeRenderDataQueuePtr ScopeCanvas::getInputQueue() { return inputData; } void ScopeCanvas::OnMouseMoved(wxMouseEvent& event) { InteractiveCanvas::OnMouseMoved(event); if (mouseTracker.mouseDown()) { dragAccel = 4.0*mouseTracker.getDeltaMouseX(); ctr += dragAccel; } } void ScopeCanvas::OnMouseWheelMoved(wxMouseEvent& /* event */) { } void ScopeCanvas::OnMouseDown(wxMouseEvent& event) { InteractiveCanvas::OnMouseDown(event); } void ScopeCanvas::OnMouseReleased(wxMouseEvent& event) { InteractiveCanvas::OnMouseReleased(event); } void ScopeCanvas::OnMouseEnterWindow(wxMouseEvent& event) { InteractiveCanvas::OnMouseEnterWindow(event); if (!helpTip.empty()) { setStatusText(helpTip); } SetCursor(wxCURSOR_SIZEWE); } void ScopeCanvas::OnMouseLeftWindow(wxMouseEvent& event) { InteractiveCanvas::OnMouseLeftWindow(event); } void ScopeCanvas::setHelpTip(std::string tip) { helpTip = tip; } void ScopeCanvas::OnKeyDown(wxKeyEvent& event) { InteractiveCanvas::OnKeyDown(event); switch (event.GetKeyCode()) { case 'B': setShowDb(!getShowDb()); break; default: event.Skip(); } } void ScopeCanvas::OnKeyUp(wxKeyEvent& event) { InteractiveCanvas::OnKeyUp(event); }