So this is getting pretty cool!
Added floating point parsing functionality, so I can pass in decimal points now. And I made it so the separate graphs will display in different colors, so you know which one's which. Removed the annoying exclamation point when it displays it in the equation window.
Further improvements that could be made:
- Allow for whitespace in the equation
- Expand the equation display screen if needed (right now it's a fixed size)
- More robust error handling - for now the parser assumes everything is valid input
- Make my "remove from char array" functions more flexible (removeCharFromEnd is probably kind of stupid)
- Most importantly, start refactoring the code. I hate long methods and that's exactly what I have. Also, all of this shouldn't be in one file. Some of my variable names could be better. I can probably hook up the colors together better - right now it's the programmer's responsibility to make sure my int declarations of colors (RED, ORANGE, GREEN, etc.) match up with the order in my colors matrix (ooh - dictionary).
Anyways, click
here to see my messy code:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string>
#include <vector>
#include <GL/glew.h>
#include <GL/glut.h>
#include <GL/gl.h>
#include <GL/glext.h>
#include "constants.h"
using namespace std;
/////////////////////////////////////
/////////////////////////////////////
int RED = 0, ORANGE = 1, YELLOW = 2, GREEN = 3, BLUE = 4, PURPLE = 5, WHITE = 6, LAST = 6;
float colors[7][3] = {
{1.0, 0.0, 0.0},
{1.0, 0.5, 0.0},
{1.0, 1.0, 0.0},
{0.0, 1.0, 0.0},
{0.0, 0.0, 1.0},
{1.0, 0.0, 1.0},
{1.0, 1.0, 1.0}
};
int PI_2 = 0, PI_3 = 1, PI_4 = 2;
int angle_index = PI_4;
string angles[3][11] = {
{"PI/2", "PI", "3PI/2", "2PI", "5PI/2", "3PI", "7PI/2", "4PI", "9PI/2", "5PI", "11PI/2"},
{"PI/3", "2PI/3", "PI", "4PI/3", "5PI/3", "2PI", "7PI/3", "10PI/3", "11PI/3", "4PI", "13PI/3"},
{"PI/4", "PI/2", "3PI/4", "PI", "9PI/4", "5PI/2", "11PI/4", "3PI", "13PI/4", "7PI/2", "15PI/4"}
};
vector<string> equations;
void initializeEquations() {
equations.push_back("sin(2*t)");
equations.push_back("0.85*cos(4*t)");
equations.push_back("sin(3*t)");
equations.push_back("2*sin(2*t)");
equations.push_back("2*0.85*cos(4*t)");
equations.push_back("2*sin(3*t)");
equations.push_back("1.1*sin(2*t)");
equations.push_back("1.1*0.85*cos(4*t)");
equations.push_back("1.1*sin(3*t)");
equations.push_back("1.5*sin(2*t)");
equations.push_back("1.5*0.85*cos(4*t)");
equations.push_back("1.5*sin(3*t)");
equations.push_back("0.5*sin(2*t)");
equations.push_back("0.5*0.85*cos(4*t)");
equations.push_back("0.5*sin(3*t)");
}
void prepareEquationsForParsing() {
for (size_t i = 0; i < equations.size(); i++) {
equations[i].append("!");
}
}
void remove(char s[], int remove) {
for (size_t i = 0; i < strlen(s); i++) {
s[i] = s[i + remove];
}
}
string removeCharFromEnd(string s) {
int len = s.size();
s[len - 1] = s[len];
return s;
}
float valueOfDigitSeq(char source[], float angle) {
if (source[0] == 't') {
remove(source, 1);
return angle;
}
float value = 0;
if (source[0] >= '0' && source[0] <= '9') {
value = (double)(source[0] - '0');
}
remove(source, 1);
while (source[0] >= '0' && source[0] <= '9') {
value *= 10;
value += (float)(source[0] - '0');
remove(source, 1);
}
double trailing = 0.0;
int placeHolder = 10;
if (source[0] == '.') {
remove(source, 1);
while (source[0] >= '0' && source[0] <= '9') {
double digit = (double)(source[0] - '0') / placeHolder;
placeHolder *= 10;
trailing += digit;
remove(source, 1);
}
}
return value + trailing;
}
float valueOfFactor(char source[], float angle) {
float value;
if (source[0] == '(' || source[0] == 's' || source[0] == 'c')
{
if (source[0] == '(')
{
remove(source, 1);
value = valueOfExpr(source, angle);
remove(source, 1);
}
else if (source[0] == 's')
{
remove(source, 3);
value = sin(valueOfFactor(source, angle));
}
else
{
remove(source, 3);
value = cos(valueOfExpr(source, angle));
}
}
else
{
value = valueOfDigitSeq(source, angle);
}
return value;
}
float valueOfTerm(char source[], float angle) {
float value = valueOfFactor(source, angle);
while (source[0] == '*' || source[0] == '/')
{
char mystery = source[0];
remove(source, 1);
if (mystery == '*')
{
value = value * valueOfTerm(source, angle);
}
else if (mystery == '/')
{
value = value / valueOfTerm(source, angle);
}
}
return value;
}
float valueOfExpr(char source[], float angle) {
float value = valueOfTerm(source, angle);
while (source[0] == '+' || source[0] == '-')
{
char mystery = source[0];
remove(source, 1);
if (mystery == '+')
{
value = value + valueOfTerm(source, angle);
}
else
{
value = value - valueOfTerm(source, angle);
}
}
return value;
}
void writeText(int xPos, int yPos, float size, string message, int color) {
color %= (LAST + 1);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, window_width, 0.0, window_height, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(xPos, yPos, 0);
glScalef(size, size, 0);
glColor3f(colors[color][0], colors[color][1], colors[color][2]);
for (size_t i = 0; i < message.length(); i++) {
glutStrokeCharacter(GLUT_STROKE_ROMAN, message[i]);
}
}
float determineAngleIncrement() {
float angle_increment;
switch (angle_index) {
case 0:
angle_increment = PI/2;
break;
case 1:
angle_increment = PI/3;
break;
case 2:
angle_increment = PI/4;
break;
}
return angle_increment;
}
void drawAngle(int i, int equation, int color) {
color %= LAST;
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glBegin(GL_POINTS);
glColor3f(colors[color][0], colors[color][1], colors[color][2]);
float angle_increment = determineAngleIncrement();
for (float angle = 0; angle < i * angle_increment + angle_increment; angle += .005) {
// refactor this out
char *a = new char[equations[equation].size() + 1];
a[equations[equation].size()] = 0;
memcpy(a, equations[equation].c_str(), equations[equation].size());
float r = valueOfExpr(a, angle);
float x = r * cos(angle);
float y = r * sin(angle);
glVertex3f(x, y, 0);
}
glEnd();
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
void displayGraphs()
{
glClear(GL_COLOR_BUFFER_BIT);
glColor4f(1,1,1,1);
// would it be possible to put all this in a struct?
float header_font_size = .2, text_size = .3;
int numOfSquares = 9, numOfCols = sqrt(numOfSquares);
int mask = 0x3;
int space = 10;
int yScale = window_height / numOfCols, xScale = window_width / numOfCols;
for (int i = 0; i < numOfSquares; i++) {
// would it be possible to put all of this into a struct as well?
int xToggle = (i % numOfCols) & mask, yToggle = (i / numOfCols) & mask;
int xMin = xToggle * xScale, yMin = yToggle * yScale;
int xCorrection = xToggle * (space / numOfCols), yCorrection = yToggle * (space / numOfCols);
int width = xScale - (space / numOfCols), height = yScale - (space / numOfCols);
glViewport(xMin + xCorrection, yMin + yCorrection, width, height);
for (size_t graph = 0; graph < equations.size(); graph++) {
int color = graph;
drawAngle(i, graph, color);
}
writeText(xPos, yPos, .3, angles[angle_index][i], WHITE);
}
glFlush();
}
void displayEquations() {
initializeEquations();
prepareEquationsForParsing();
for (size_t i = 0, yCorrection = 0; i < equations.size(); i++, yCorrection += 50) {
string equation = removeCharFromEnd(equations[i]);
writeText(xPos, yPos + yCorrection, .25, "r = " + equation, i);
}
}
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitWindowSize(window_width, window_height);
glutCreateWindow("polar graphs");
glutDisplayFunc(displayGraphs);
glutInitWindowSize(200, 200);
glutCreateWindow("equations");
glutPositionWindow(540, 40);
glutDisplayFunc(displayEquations);
glutMainLoop();
}
And my crazy picture:
I actually really like the first couple (PI/4 and PI/2). Of course it all looks pretty cool. But the beginning ones look.. wispy. I like that.