January 27, 2014

Polar Graphs part 2

I made a number of small improvements to my previous program.

Displayed the equation on the graph - and moved the generation of the equation earlier up in my code (string equation and the function implementation for float radius are closer together) to make it easier to sync between the two.  Optimally I would only have to define it once but that would be a hairy thing to try and implement now (would require a recursive descent parser).

I have a matrix of string angles, which will display how far the angle was drawn.  It's nice because I can easily switch between increments of PI/2, PI/3, and PI/4 by declaring angle_index to either 0, 1, 2.  I don't have to worry about changing the display or changing the increments because angle_index hooks up all of that.

You know, when taking my C class I never thought I would ever have to do bit manipulation but I actually use it in my display function.  It's not perfect yet, but I made it so I can display more squares (my below post could only support 4, but I can easily make it display 9 squares by changing numOfSquares to 9 instead of 4, and changing the mask to 0x3 instead of 0x1).  I've managed to parameterize out most of the math but I haven't figured out a mathematical equation to map numOfSquares to mask so it has to be hardcoded for now.  Basically the premise is to, with a single pass, manipulate the iterator so that it'll display all combinations of {000, 001, 010, ..., 100} which will put each square in a different spot on the screen.

So here's the code:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string>
#include <GL/glew.h>
#include <GL/glut.h>
#include <GL/gl.h>
#include <GL/glext.h>
using namespace std;

int header_space = 30;
int window_width = 500, window_height = 500 + header_space;
int xPos = 10, yPos = 10;
string equation = ".05 * (angle + 2)";

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"}
};
int angle_index = 2;

#define PI 3.1415926535897932384626433832795

float radius(float angle) {
return .05 * (angle + 2);
}

void writeText(int xPos, int yPos, float size, string message) {
  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(1, 1, 1);
  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) {

glMatrixMode (GL_MODELVIEW);
glPushMatrix ();
glLoadIdentity ();
glMatrixMode (GL_PROJECTION);
glPushMatrix ();
glLoadIdentity ();

glBegin(GL_POINTS);
float angle_increment = determineAngleIncrement();
for (float angle = 0; angle < i * angle_increment + angle_increment; angle += .005) {
float r = radius(angle);
float x = r * cos(angle);
float y = r * sin(angle);

glVertex3f(x, y, 0);
}
glEnd();

glPopMatrix ();
glMatrixMode (GL_MODELVIEW);
glPopMatrix ();
}

void display()
{
glClear(GL_COLOR_BUFFER_BIT);
glColor4f(1,1,1,1);

float header_font_size = .2, text_size = .3;

int numOfSquares = 9;
int numOfCols = sqrt(numOfSquares);
int mask = 0x3;

writeText(xPos, window_height - header_space, header_font_size, "r = " + equation);
int space = 10, yScale = (window_height - header_space) / numOfCols, xScale = window_width / numOfCols;

for (int i = 0; i < numOfSquares; i++) {
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);
drawAngle(i);
writeText(xPos, yPos, .3, angles[angle_index][i]);
}

glFlush();
}


///////////////////////////////////////////////////////////////

int main(int argc, char** argv)
{
  glutInit(&argc, argv);
  glutInitWindowSize(window_width, window_height);
  glutCreateWindow("polar equations graph");
  glutDisplayFunc(display);
  glutMainLoop();
}

With some pictures:

I'd like to note that if I were trying to teach this to someone my explanations would be a bit better.. I realize my explanations up top weren't completely intelligible.