Skip to content

Commit a88aacf

Browse files
committed
More work on CartesianPlane
1 parent bcde014 commit a88aacf

File tree

5 files changed

+1124
-211
lines changed

5 files changed

+1124
-211
lines changed

examples/graphics/source/examples/CrossoverDemo.h

Lines changed: 64 additions & 211 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
#include <yup_dsp/yup_dsp.h>
2525
#include <yup_audio_formats/yup_audio_formats.h>
26+
#include <yup_audio_gui/yup_audio_gui.h>
2627

2728
#include <memory>
2829
#include <random>
@@ -31,213 +32,6 @@
3132

3233
//==============================================================================
3334

34-
class CrossoverFrequencyResponseDisplay : public yup::Component
35-
{
36-
public:
37-
void updateResponse (const std::vector<yup::Point<double>>& lowData,
38-
const std::vector<yup::Point<double>>& highData)
39-
{
40-
lowPassData = lowData;
41-
highPassData = highData;
42-
repaint();
43-
}
44-
45-
void setCrossoverFrequency (double freq)
46-
{
47-
crossoverFreq = freq;
48-
repaint();
49-
}
50-
51-
private:
52-
void paint (yup::Graphics& g) override
53-
{
54-
auto bounds = getLocalBounds();
55-
56-
// Background
57-
g.setFillColor (yup::Color (0xFF1E1E1E));
58-
g.fillRect (bounds);
59-
60-
// Reserve space for labels
61-
auto titleBounds = bounds.removeFromTop (25);
62-
titleBounds.removeFromLeft (5);
63-
auto bottomLabelSpace = bounds.removeFromBottom (20);
64-
auto leftLabelSpace = bounds.removeFromLeft (50);
65-
leftLabelSpace.removeFromRight (5);
66-
67-
// Grid
68-
g.setStrokeColor (yup::Color (0xFF333333));
69-
g.setStrokeWidth (1.0f);
70-
71-
// Frequency grid lines (logarithmic)
72-
for (double freq : { 20.0, 50.0, 100.0, 200.0, 500.0, 1000.0, 2000.0, 5000.0, 10000.0, 20000.0 })
73-
{
74-
float x = frequencyToX (freq, bounds);
75-
g.strokeLine ({ x, bounds.getY() }, { x, bounds.getBottom() });
76-
}
77-
78-
// dB grid lines
79-
for (double db : { -48.0, -36.0, -24.0, -12.0, -6.0, 0.0, 6.0 })
80-
{
81-
float y = dbToY (db, bounds);
82-
g.strokeLine ({ bounds.getX(), y }, { bounds.getRight(), y });
83-
}
84-
85-
// Zero line
86-
g.setStrokeColor (yup::Color (0xFF666666));
87-
g.setStrokeWidth (2.0f);
88-
float y0 = dbToY (0.0, bounds);
89-
g.strokeLine ({ bounds.getX(), y0 }, { bounds.getRight(), y0 });
90-
91-
// -6dB crossover line
92-
g.setStrokeColor (yup::Color (0xFF444444));
93-
g.setStrokeWidth (1.0f);
94-
float y6 = dbToY (-6.0, bounds);
95-
g.strokeLine ({ bounds.getX(), y6 }, { bounds.getRight(), y6 });
96-
97-
// Crossover frequency line
98-
if (crossoverFreq > 0)
99-
{
100-
g.setStrokeColor (yup::Color (0xFF888888));
101-
g.setStrokeWidth (1.0f);
102-
float xCross = frequencyToX (crossoverFreq, bounds);
103-
g.strokeLine ({ xCross, bounds.getY() }, { xCross, bounds.getBottom() });
104-
}
105-
106-
// Plot frequency responses
107-
if (! lowPassData.empty())
108-
{
109-
// Low pass in blue
110-
yup::Path lowPath;
111-
bool firstPoint = true;
112-
113-
g.setStrokeColor (yup::Color (0xFF4488FF));
114-
g.setStrokeWidth (2.0f);
115-
116-
for (const auto& point : lowPassData)
117-
{
118-
float x = frequencyToX (point.getX(), bounds);
119-
float y = dbToY (point.getY(), bounds);
120-
121-
if (firstPoint)
122-
{
123-
lowPath.startNewSubPath (x, y);
124-
firstPoint = false;
125-
}
126-
else
127-
{
128-
lowPath.lineTo (x, y);
129-
}
130-
}
131-
132-
g.strokePath (lowPath);
133-
}
134-
135-
if (! highPassData.empty())
136-
{
137-
// High pass in orange
138-
yup::Path highPath;
139-
bool firstPoint = true;
140-
141-
g.setStrokeColor (yup::Color (0xFFFF8844));
142-
g.setStrokeWidth (2.0f);
143-
144-
for (const auto& point : highPassData)
145-
{
146-
float x = frequencyToX (point.getX(), bounds);
147-
float y = dbToY (point.getY(), bounds);
148-
149-
if (firstPoint)
150-
{
151-
highPath.startNewSubPath (x, y);
152-
firstPoint = false;
153-
}
154-
else
155-
{
156-
highPath.lineTo (x, y);
157-
}
158-
}
159-
160-
g.strokePath (highPath);
161-
}
162-
163-
// Labels with smaller font
164-
g.setFillColor (yup::Colors::white);
165-
auto font = yup::ApplicationTheme::getGlobalTheme()->getDefaultFont().withHeight (10.0f);
166-
167-
// Title (centered, leaving space for legend)
168-
auto titleArea = titleBounds.removeFromLeft (titleBounds.getWidth() - 120);
169-
g.fillFittedText ("Crossover Frequency Response", font.withHeight (12.0f), titleArea, yup::Justification::centerLeft);
170-
171-
// Frequency labels
172-
for (double freq : { 100.0, 1000.0, 10000.0 })
173-
{
174-
float x = frequencyToX (freq, bounds);
175-
yup::String label;
176-
if (freq >= 1000.0)
177-
label = yup::String (freq / 1000.0, 0) + "k";
178-
else
179-
label = yup::String (static_cast<int> (freq));
180-
181-
auto labelBounds = yup::Rectangle<int> (static_cast<int> (x - 20),
182-
bottomLabelSpace.getY(),
183-
40,
184-
bottomLabelSpace.getHeight());
185-
g.fillFittedText (label, font, labelBounds, yup::Justification::center);
186-
}
187-
188-
// dB labels
189-
g.setFillColor (yup::Colors::gray);
190-
for (double db : { -24.0, -12.0, -6.0, 0.0 })
191-
{
192-
float y = dbToY (db, bounds);
193-
auto label = yup::String (static_cast<int> (db)) + " dB";
194-
g.fillFittedText (label, font, leftLabelSpace.withY (static_cast<int> (y - 8)).withHeight (16), yup::Justification::right);
195-
}
196-
197-
// Legend (on the right side of title area)
198-
auto legendBounds = titleBounds;
199-
legendBounds = legendBounds.withY (legendBounds.getY() + 4);
200-
201-
g.setFillColor (yup::Color (0xFF4488FF));
202-
auto lowRect = legendBounds.removeFromLeft (15).reduced (2);
203-
g.fillRect (lowRect);
204-
g.setFillColor (yup::Colors::white);
205-
g.fillFittedText ("Low", font, legendBounds.removeFromLeft (25), yup::Justification::left);
206-
207-
g.setFillColor (yup::Color (0xFFFF8844));
208-
auto highRect = legendBounds.removeFromLeft (15).reduced (2);
209-
g.fillRect (highRect);
210-
g.setFillColor (yup::Colors::white);
211-
g.fillFittedText ("High", font, legendBounds, yup::Justification::left);
212-
}
213-
214-
float frequencyToX (double freq, const yup::Rectangle<float>& bounds) const
215-
{
216-
if (freq <= 0)
217-
return bounds.getX();
218-
219-
const double minLog = std::log10 (20.0);
220-
const double maxLog = std::log10 (20000.0);
221-
const double logFreq = std::log10 (freq);
222-
const double normalised = (logFreq - minLog) / (maxLog - minLog);
223-
224-
return bounds.getX() + static_cast<float> (normalised * bounds.getWidth());
225-
}
226-
227-
float dbToY (double db, const yup::Rectangle<float>& bounds) const
228-
{
229-
const double minDb = -48.0;
230-
const double maxDb = 12.0;
231-
const double normalised = 1.0 - (db - minDb) / (maxDb - minDb);
232-
233-
return bounds.getY() + static_cast<float> (normalised * bounds.getHeight());
234-
}
235-
236-
std::vector<yup::Point<double>> lowPassData;
237-
std::vector<yup::Point<double>> highPassData;
238-
double crossoverFreq = 1000.0;
239-
};
240-
24135
//==============================================================================
24236

24337
class CrossoverDemo : public yup::Component
@@ -518,7 +312,7 @@ class CrossoverDemo : public yup::Component
518312
freqSlider.onValueChanged = [this] (float value)
519313
{
520314
crossoverFreq.setTargetValue (value);
521-
frequencyDisplay.setCrossoverFrequency (value);
315+
setCrossoverFrequency (value);
522316
};
523317
addAndMakeVisible (freqSlider);
524318

@@ -554,7 +348,8 @@ class CrossoverDemo : public yup::Component
554348
};
555349
addAndMakeVisible (highGainSlider);
556350

557-
// Frequency display
351+
// Configure frequency display (CartesianPlane)
352+
setupFrequencyDisplay();
558353
addAndMakeVisible (frequencyDisplay);
559354

560355
// Initialize frequency response
@@ -616,7 +411,60 @@ class CrossoverDemo : public yup::Component
616411
highResponse.emplace_back (freq, highDb);
617412
}
618413

619-
frequencyDisplay.updateResponse (lowResponse, highResponse);
414+
// Update signals on the CartesianPlane
415+
frequencyDisplay.updateSignalData (lowPassSignalIndex, lowResponse);
416+
frequencyDisplay.updateSignalData (highPassSignalIndex, highResponse);
417+
}
418+
419+
void setupFrequencyDisplay()
420+
{
421+
// Configure the CartesianPlane for frequency response display
422+
frequencyDisplay.setTitle ("Crossover Frequency Response");
423+
424+
// Set logarithmic X axis (frequency) and linear Y axis (dB)
425+
frequencyDisplay.setXRange (20.0, 20000.0);
426+
frequencyDisplay.setXScaleType (yup::CartesianPlane::AxisScaleType::logarithmic);
427+
frequencyDisplay.setYRange (-48.0, 12.0);
428+
frequencyDisplay.setYScaleType (yup::CartesianPlane::AxisScaleType::linear);
429+
430+
// Set margins
431+
frequencyDisplay.setMargins (25, 50, 20, 20);
432+
433+
// Add vertical grid lines (frequency)
434+
frequencyDisplay.setVerticalGridLines ({ 20.0, 50.0, 100.0, 200.0, 500.0, 1000.0, 2000.0, 5000.0, 10000.0, 20000.0 });
435+
436+
// Add horizontal grid lines (dB)
437+
frequencyDisplay.setHorizontalGridLines ({ -48.0, -36.0, -24.0, -12.0, -6.0, 0.0, 6.0 });
438+
439+
// Emphasize special lines
440+
frequencyDisplay.addHorizontalGridLine (0.0, yup::Color (0xFF666666), 2.0f, true); // 0dB line
441+
frequencyDisplay.addHorizontalGridLine (-6.0, yup::Color (0xFF444444), 1.0f, true); // -6dB crossover line
442+
443+
// Add axis labels
444+
frequencyDisplay.setXAxisLabels ({ 100.0, 1000.0, 10000.0 });
445+
frequencyDisplay.setYAxisLabels ({ -24.0, -12.0, -6.0, 0.0 });
446+
447+
// Add signals
448+
lowPassSignalIndex = frequencyDisplay.addSignal ("Low", yup::Color (0xFF4488FF), 2.0f);
449+
highPassSignalIndex = frequencyDisplay.addSignal ("High", yup::Color (0xFFFF8844), 2.0f);
450+
451+
// Configure legend
452+
frequencyDisplay.setLegendVisible (true);
453+
frequencyDisplay.setLegendPosition ({ 0.9f, 0.1f });
454+
455+
// Set initial crossover frequency line
456+
setCrossoverFrequency (1000.0);
457+
}
458+
459+
void setCrossoverFrequency (double freq)
460+
{
461+
currentCrossoverFreq = freq;
462+
463+
// Update crossover frequency line
464+
frequencyDisplay.clearVerticalGridLines();
465+
frequencyDisplay.setVerticalGridLines ({ 20.0, 50.0, 100.0, 200.0, 500.0, 1000.0, 2000.0, 5000.0, 10000.0, 20000.0 });
466+
if (freq > 0)
467+
frequencyDisplay.addVerticalGridLine (freq, yup::Color (0xFF888888), 1.0f, true);
620468
}
621469

622470
// Audio
@@ -645,5 +493,10 @@ class CrossoverDemo : public yup::Component
645493
yup::Slider lowGainSlider;
646494
yup::Label highGainLabel;
647495
yup::Slider highGainSlider;
648-
CrossoverFrequencyResponseDisplay frequencyDisplay;
496+
yup::CartesianPlane frequencyDisplay;
497+
498+
// Signal indices for CartesianPlane
499+
int lowPassSignalIndex = -1;
500+
int highPassSignalIndex = -1;
501+
double currentCrossoverFreq = 1000.0;
649502
};

0 commit comments

Comments
 (0)