The code in main prints a colored ribbon before the while and when I try to repeat this process inside the while after the user enters a 1, the constant adjusts the possible wavelengths (that are four) within the visible and assigns a character to the specified position of the array based on the o shape's wavelength, otherwise prints empty space for other colors.
It happens that after the user informs 1, the tape is repeated 3 more times after the first one, as in the print of the result above, only a ribbon should be printed. What makes it happen?
compile -lncursesw
environment xfce4-terminal
I think it's because of the wprintw(stdscr, "%lc", randomCharacter);
, I noticed that each new strip contains the randomCharacter
drawn in a different position corresponding to one of the four possible lengths of the adjustedConstant
, so this totals 4 strips instead of one.
i == index
it is a way of visualizing the colors associated with specific wavelengths within the visible light spectrum.
When the user enters a value other than -1, the program calculates the wavelength associated with the given input using the formula given by adjustedConstant
. If that wavelength is within the visible range (between 400 nm and 700 nm), the program determines the corresponding position in the array of colors (colors) and prints a random character with the color associated with that wavelength.
When pressed 1 ->
1n = 2, 2n = 3, Wavelength: 6.564696e-07 meters
1n = 2, 2n = 4, Wavelength: 4.862738e-07 meters
1n=2, 2n=5, Wavelength: 4.341730e-07 meters
1n=2, 2n=6, Wavelength: 4.102935e-07 meters
#include <stdio.h>
#include <stdlib.h>
#include <curses.h>
#include <locale.h>
#include <math.h>
typedef struct {
int r;
int g;
int b;
} RGB;
RGB newRGB(int r, int g, int b) {
RGB color;
color.r = r;
color.g = g;
color.b = b;
return color;
}
void calculateColor(RGB *colors, int totalColors, int currentIndex, float saturation, float luminosity);
double adjustedConstant() {
return 1.0973731568508E7 / (1 + 9.10938356E-31 / 1.67262192E-27);
}
int main() {
setlocale(LC_ALL, "");
int totalColors = 60;
RGB colors[totalColors];
float saturation = 1.0;
float luminosity = 0.3;
initscr();
start_color();
int end = totalColors - 18;
int start = end - 42;
for (int i = end; i >= start; i--) {
calculateColor(&colors[i], totalColors, i, saturation, luminosity);
// Convert RGB colors to curses default model
int r = colors[i].r * 1000 / 255;
int g = colors[i].g * 1000 / 255;
int b = colors[i].b * 1000 / 255;
// Set color in curses
init_color(i + 16, r, g, b); // needs to define the pair of colors i+16
init_pair(i + 16, COLOR_BLACK, i + 16);
// Print the color
attron(COLOR_PAIR(i + 16)); // needs to activate the pair of colors i+16
printw(" ");
attroff(COLOR_PAIR(i + 16));
}
// Vector of characters for drawing
int characters[] = {
9474, 9475, 10072, 10073, 12321
};
int numCharacters = sizeof(characters) / sizeof(characters[0]);
// Loop to print the colors on the screen
int wavelength;
while (1) {
int number;
printw("\n\nEnter 1 or -1 to exit: ");
scanw("%d", &number);
if (number == -1) {
break; // Exit the loop if the user enters -1
}
// Wavelength
int n1 = 1, n2 = 2;
double lambda_a = 0;
while (1) {
lambda_a = 1.0 / (adjustedConstant() * ((1.0 / (n1 * n1)) - (1.0 / (n2 * n2))));
if (lambda_a >= 400E-9 && lambda_a <= 700E-9) {
// Calculate the position in the colors array based on the wavelength
int index = (700 - lambda_a * 1e9) * (totalColors - 10.5) / (750 - 380);
// Print all colors
for (int i = end; i >= start; i--) {
if (i == index) {
// Activate the color pair for the corresponding wavelength color
attron(COLOR_PAIR(i + 16));
// Draw one of the characters randomly
int randomCharacter = characters[rand() % numCharacters];
wprintw(stdscr, "%lc", randomCharacter);
// Deactivate the color pair
attroff(COLOR_PAIR(i + 16));
} else {
// Draw empty spaces for the other colors
attron(COLOR_PAIR(i + 16));
printw(" ");
attroff(COLOR_PAIR(i + 16));
}
}
refresh(); // Update the screen
}
if (lambda_a > 700E-9) {
break; // Wavelength exceeded the visible range, end the loop
}
n2++; // Increment the value of 2n
if (n2 <= 6) {
continue;
}
n1++; // Increment the value of 1n and reset 2n to a value greater than 1n
n2 = n1 + 1;
}
}
getch(); // Wait for user input
endwin(); // End curses mode
return 0;
}
void calculateColor(RGB *colors, int totalColors, int currentIndex, float saturation, float luminosity) {
float hue = currentIndex * (360.0 / totalColors);
float chroma = (1 - fabs(2 * luminosity - 1)) * saturation;
float x = chroma * (1 - fabs(fmod(hue / 60, 2) - 1));
float m = luminosity - chroma / 2;
float r, g, b;
if (hue >= 0 && hue < 60) {
r = chroma;
g = x;
b = 0;
} else if (hue >= 60 && hue < 120) {
r = x;
g = chroma;
b = 0;
} else if (hue >= 120 && hue < 180) {
r = 0;
g = chroma;
b = x;
} else if (hue >= 180 && hue < 240) {
r = 0;
g = x;
b = chroma;
} else if (hue >= 240 && hue < 300) {
r = x;
g = 0;
b = chroma;
} else {
r = chroma;
g = 0;
b = x;
}
colors->r = (r + m) * 255;
colors->g = (g + m) * 255;
colors->b = (b + m) * 255;
}
The loop logic in the posted code is incorrect.
OP code has an outer loop that calculates lambda_a
values which presumably correspond with spectral absorption lines, and an inner loop that prints a color spectrum, either as blocks or as characters depending on the color index associated with the lambda_a
value. The inner loop prints a color spectrum and checks for a match with the index associated with lambda_a
, printing a character in that case, and then lambda_a
is modified in the outer loop. By this process, four values of lambda_a
appear in the visible spectrum range (lambda_a >= 400E-9 && lambda_a <= 700E-9
) so the spectrum is printed four times.
A better approach would be to calculate and cache color indexes which match an absorption line. Then when the color spectrum is printed once, if the color index matches a cached index a character is printed.
Here is a modified version of OP code that implements this. There is now a loop which populates line_idxs
with color indexes for absorption lines based on original OP code. A loop which prints a spectrum follows. Note that this code steps through line_idxs
in reverse because the printing order of colors is from highest index to lowest while the absorption lines were found in lowest to highest order.
#include <stdio.h>
#include <stdlib.h>
#include <curses.h>
#include <locale.h>
#include <math.h>
typedef struct {
int r;
int g;
int b;
} RGB;
RGB newRGB(int r, int g, int b) {
RGB color;
color.r = r;
color.g = g;
color.b = b;
return color;
}
void calculateColor(RGB *colors, int totalColors, int currentIndex, float saturation, float luminosity);
double adjustedConstant() {
return 1.0973731568508E7 / (1 + 9.10938356E-31 / 1.67262192E-27);
}
int main(void) {
setlocale(LC_ALL, "");
int totalColors = 60;
RGB colors[totalColors];
float saturation = 1.0;
float luminosity = 0.3;
initscr();
start_color();
int end = totalColors - 18;
int start = end - 42;
for (int i = end; i >= start; i--) {
calculateColor(&colors[i], totalColors, i, saturation, luminosity);
// Convert RGB colors to curses default model
int r = colors[i].r * 1000 / 255;
int g = colors[i].g * 1000 / 255;
int b = colors[i].b * 1000 / 255;
// Set color in curses
init_color(i + 16, r, g, b); // needs to define the pair of colors i+16
init_pair(i + 16, COLOR_BLACK, i + 16);
// Print the color
attron(COLOR_PAIR(i + 16)); // needs to activate the pair of colors i+16
printw(" ");
attroff(COLOR_PAIR(i + 16));
}
// Vector of characters for drawing
int characters[] = {
9474, 12321
};
int numCharacters = sizeof(characters) / sizeof(characters[0]);
// Loop to print the colors on the screen
// int wavelength;
while (1) {
int number;
printw("\n\nEnter 1 or -1 to exit: ");
scanw("%d", &number);
if (number == -1) {
break; // Exit the loop if the user enters -1
}
// Wavelength
int n1 = 1, n2 = 2;
int line_idxs[4] = { 0 }; // Save spectral line indices.
// Populate `line_idxs[]` with spectral lines.
for(size_t i = 0; i < 4;) {
double lambda_a = 0;
lambda_a =(1.0 / (adjustedConstant() * ((1.0 / (n1 * n1)) - (1.0 / (n2 * n2)))));
if (lambda_a >= 400E-9 && lambda_a <= 700E-9) {
line_idxs[i] = start + (700 - lambda_a * 1e9) * (totalColors - 10.5) / (750 - 380);
i++;
}
n2++;
if (n2 <= 6) continue;
n1++;
n2 = n1 + 1;
}
// Print spectrum.
for (int i = end, line_idx = 3; i >= start; i--) {
attron(COLOR_PAIR(i + 16));
if (line_idx >= 0 && i == line_idxs[line_idx]) {
// Print a random character.
int randomCharacter = characters[rand() % numCharacters];
wprintw(stdscr, "%lc", randomCharacter);
--line_idx;
} else {
printw(" ");
}
attroff(COLOR_PAIR(i + 16));
}
refresh(); // Update the screen
}
getch(); // Wait for user input
endwin(); // End curses mode
return 0;
}
void calculateColor(RGB *colors, int totalColors, int currentIndex, float saturation, float luminosity) {
float hue = currentIndex * (360.0 / totalColors);
float chroma = (1 - fabs(2 * luminosity - 1)) * saturation;
float x = chroma * (1 - fabs(fmod(hue / 60, 2) - 1));
float m = luminosity - chroma / 2;
float r, g, b;
if (hue >= 0 && hue < 60) {
r = chroma;
g = x;
b = 0;
} else if (hue >= 60 && hue < 120) {
r = x;
g = chroma;
b = 0;
} else if (hue >= 120 && hue < 180) {
r = 0;
g = chroma;
b = x;
} else if (hue >= 180 && hue < 240) {
r = 0;
g = x;
b = chroma;
} else if (hue >= 240 && hue < 300) {
r = x;
g = 0;
b = chroma;
} else {
r = chroma;
g = 0;
b = x;
}
colors->r = (r + m) * 255;
colors->g = (g + m) * 255;
colors->b = (b + m) * 255;
}
Here is the resulting display:
Note that the length of the second color spectrum is shorter than the first and the color blocks in the second spectrum are not aligned with the color blocks in the first. This is because the printed representation of the taller bar is narrower than the printed representation of the shorter bar. You can fix this by switching on randomCharacter
and printing an extra space for narrower characters. Here I have added some #define
s to make working with character codes easier. The switch
statement is used so that further characters may be easily introduced by adding additional cases:
/* ... */
#define TALLBAR 9474
#define SHORTBAR 12321
/* ... */
int characters[] = { TALLBAR, SHORTBAR };
/* ... */
// Print spectrum.
for (int i = end, line_idx = 3; i >= start; i--) {
attron(COLOR_PAIR(i + 16));
if (line_idx >= 0 && i == line_idxs[line_idx]) {
// Print a random character.
int randomCharacter = characters[rand() % numCharacters];
switch (randomCharacter) {
case TALLBAR:
wprintw(stdscr, "%lc ", randomCharacter);
break;
default:
wprintw(stdscr, "%lc", randomCharacter);
}
--line_idx;
} else {
printw(" ");
}
attroff(COLOR_PAIR(i + 16));
}
/* ... */
Of course, many further refinements are possible. Here is the resulting display after printing several spectra using the modified program: