diff options
author | Peter Wu <lekensteyn@gmail.com> | 2013-11-28 23:51:56 +0100 |
---|---|---|
committer | Peter Wu <lekensteyn@gmail.com> | 2013-11-28 23:51:56 +0100 |
commit | 41e1007da6101cfa7e5cc206d44027fbfe7052b6 (patch) | |
tree | 0e17299ad8df64e8f8effe5edf37dbce764e4262 | |
parent | c71ba8e4726bf9a904697f7acaf6602f7a2d2c8b (diff) | |
download | 2iv60-robots-41e1007da6101cfa7e5cc206d44027fbfe7052b6.tar.gz |
Refactor to remove magic numbers, deduplication
Added Colors class that labels the RGB color numbers. Introduce
helper function to draw beams that does not change scaling,
this allows the push/pop matrix function to be removed from drawTorso.
Remove duplicate code from drawLegs, drawArms, drawShoulders.
Remove drawStickFigures because it will interfere with animations
later, drawBeam will take care of drawing the skeleton if needed.
Joints are manually added during the transformations, that are possible
points for rotations later.
-rw-r--r-- | src/Colors.java | 25 | ||||
-rw-r--r-- | src/Direction.java | 6 | ||||
-rw-r--r-- | src/RobotRace.java | 482 |
3 files changed, 228 insertions, 285 deletions
diff --git a/src/Colors.java b/src/Colors.java new file mode 100644 index 0000000..fca191e --- /dev/null +++ b/src/Colors.java @@ -0,0 +1,25 @@ + +import java.awt.Color; + +/** + * A collection of colors. + */ +public class Colors { + + /** + * Made up color names. + */ + public static Color BLUEISH = new Color(100, 130, 255); + public static Color GRAYISH = new Color(100, 100, 100); + public static Color DIRTY_BLUE = new Color(130, 158, 174); + public static Color ARM_GRAY_COLOR = new Color(200, 200, 200); + + /** + * Names taken from Wikipedia. + * + * @see https://en.wikipedia.org/wiki/List_of_colors:_A%E2%80%93F + */ + public static Color LAVENDER = new Color(0xE6E6FA); + public static Color PALE_TURQOISE = new Color(0xAFEEEE); + public static Color CHOCOLATE = new Color(0x7B3F00); +} diff --git a/src/Direction.java b/src/Direction.java new file mode 100644 index 0000000..dccb87f --- /dev/null +++ b/src/Direction.java @@ -0,0 +1,6 @@ +/** + * Specifies the direction of a line segment. + */ +public enum Direction { + X, Y, Z +} diff --git a/src/RobotRace.java b/src/RobotRace.java index b1bf83a..d715a78 100644 --- a/src/RobotRace.java +++ b/src/RobotRace.java @@ -1,4 +1,5 @@ +import java.awt.Color; import java.awt.KeyEventDispatcher; import java.awt.KeyboardFocusManager; import java.awt.event.KeyEvent; @@ -136,7 +137,7 @@ public class RobotRace extends Base { gl.glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); gl.glBindTexture(GL_TEXTURE_2D, 0); } - + /** * Configures the viewing transform. */ @@ -228,11 +229,11 @@ public class RobotRace extends Base { * @param g Green color scale (0 to 1). * @param b Blue color scale (0 to 1). */ - private void drawColoredArrow(float r, float g, float b) { + private void drawColoredArrow(Color color) { gl.glPushMatrix(); // change color - gl.glColor3f(r, g, b); + setColor(color); // draw a thin line from the origin to the right. gl.glTranslatef(0.5f, 0, 0); @@ -258,16 +259,16 @@ public class RobotRace extends Base { */ public void drawAxisFrame() { // X-axis: normal orientation - drawColoredArrow(1, 0, 0); + drawColoredArrow(Color.RED); // Y-axis: rotate 90 degree clockwise in the Z-axis gl.glRotatef(90, 0, 0, 1); - drawColoredArrow(0, 1, 0); + drawColoredArrow(Color.GREEN); gl.glRotatef(-90, 0, 0, 1); // Z-axis: rotate 90 degree in the XY ais gl.glRotatef(-90, 0, 1, 0); - drawColoredArrow(0, 0, 1); + drawColoredArrow(Color.BLUE); gl.glRotatef(90, 0, 1, 0); // yellow sphere of 0.03m (with ten divisions) @@ -329,18 +330,38 @@ public class RobotRace extends Base { this.specular = specular; } } - + + /** + * Utility method to set color. + * @param color An AWT color. + */ + private void setColor(Color color) { + // contains four RGBA color components (floats in range 0 to 1) + float[] rgba = color.getRGBComponents(null); + gl.glColor3fv(rgba, 0); + } + /** * Represents a Robot, to be implemented according to the Assignments. */ private class Robot { - + private final Color boneColor = Colors.CHOCOLATE; + /** The material from which this robot is built. */ private final Material material; /** Relative lengths, widths and heights of robot model. */ private final float torsoHeight, torsoWidth, shoulderRadius, armLength, - legLength, armWidth, legWidth, neckHeight, headRadius, depth; + legLength, armWidth, legWidth, neckHeight, headRadius, depth, + footWidth, footHeight, footLength; + + /** Size of the bone for stick figures. */ + private final float boneSize; + + /** + * True if a skeleton should be drawn instead of a full body. + */ + private boolean asStickFigure; /** * Constructs the robot with initial parameters. @@ -357,6 +378,10 @@ public class RobotRace extends Base { this.legWidth = 0.06f; this.neckHeight = 0.15f; this.headRadius = 0.12f; + this.footWidth = legWidth; + this.footHeight = legWidth; + this.footLength = 2 * legWidth; + this.boneSize = 0.02f; this.depth = 0.24f; } @@ -364,336 +389,223 @@ public class RobotRace extends Base { * Draws this robot (as a {@code stickfigure} if specified). */ public void draw(boolean stickFigure) { + this.asStickFigure = stickFigure; // as the components are drawn with the torso as center, move it up gl.glPushMatrix(); gl.glTranslatef(0, 0, torsoHeight / 2 + legLength); - if (!stickFigure) { - // Draw the robot - drawTorso(); // Torso - drawShoulders(); // Shoulders - drawLegs(); // Legs and feet - drawArms(); // Arms and hands - drawHead(); // Neck and head - } else { - // Draw the stick figure - drawStickFigure(); + // These materials control the reflected light + gl.glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, material.diffuse, 0); + gl.glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, material.specular, 0); + + gl.glTranslatef(0, 0, footHeight); + + // Draw the robot, everything is relative to the center of torso. + // Static parts: + drawTorso(); + drawHead(); + // only draw the circles in normal, full body mode + if (!asStickFigure) { + drawShoulderTop(); } + // Parts that should be animated: + // draw left and right legs + drawLeg(false); + drawLeg(true); + // draw left and right arms + drawArm(false); + drawArm(true); + // restore position gl.glPopMatrix(); } - + /** - * Draws the stickfigure of the robot. + * Draws a 3d figure with given dimensions and color. If a stick figure + * must be drawn, then the figure will become a thin line in the X, Y + * or Z direction depending on the direction parameter. + * @param dir Direction of the line segment, relevant for stick figures. + * @param color If non-null, it becomes the color for this beam. Ignored + * when drawing a stick figure, in that case the boneColor constant will + * be used. */ - private void drawStickFigure() { - // Create all vectors based on the real position and the - // translations of the real robot. - Vector torsoLeftTop = new Vector(torsoWidth /2, 0f, torsoHeight /2); - Vector torsoRightTop = new Vector(-torsoWidth/2, 0f, torsoHeight/2); - Vector torsoLeftBottom = new Vector(torsoWidth / 2, 0f, - -torsoHeight /2); - Vector torsoRightBottom = new Vector(-torsoWidth /2 , 0f, - -torsoHeight /2); - - Vector leftShoulder = new Vector(torsoWidth / 2 + armWidth * 1.5f, - 0f, torsoHeight / 2 - 0.05f); - Vector rightShoulder = new Vector(-torsoWidth / 2 - armWidth * 1.5f, - 0f, torsoHeight / 2 - 0.05f); - - Vector leftHand = new Vector(torsoWidth / 2 + armWidth * 1.5f, - 0f, torsoHeight / 2 - 0.05f - armLength); - Vector rightHand = new Vector(-torsoWidth / 2 - armWidth * 1.5f, - 0f, torsoHeight / 2 - 0.05f - armLength); - - Vector neckStart = new Vector(0f, 0f, torsoHeight/2); - Vector neckEnd = new Vector(0f, 0f, torsoHeight/2 + neckHeight); - - Vector leftLegTop = new Vector(torsoWidth / 2 - legWidth * 2, 0f, - -torsoHeight / 2); - Vector leftLegBottom = new Vector(torsoWidth / 2 - legWidth * 2, - 0f, -torsoHeight / 2 - legLength); - - Vector rightLegTop = new Vector(-torsoWidth / 2 + legWidth * 2, 0f, - -torsoHeight / 2); - Vector rightLegBottom = new Vector(-torsoWidth / 2 + legWidth * 2, - 0f, -torsoHeight / 2 - legLength); - - /* - * For future, translate all vectors so they are positioned - * correctly and that they are rotated correctly. - * - * Arms and feet should be translated afterwards to represent - * the angle to simulate movement. - */ - - // Set the color to black - setColorRGB(0, 0, 0); - - // Connect all torso points with a line stip - gl.glBegin(GL_LINE_STRIP); - stickPoint(torsoLeftTop); - stickPoint(torsoRightTop); - stickPoint(torsoRightBottom); - stickPoint(torsoLeftBottom); - stickPoint(torsoLeftTop); - gl.glEnd(); - - // Draw head lines based on neck start and end - gl.glBegin(GL_LINES); - stickPoint(neckStart); - stickPoint(neckEnd); - gl.glEnd(); - - // Draw arm and leg lines, and connect arms with torso (shoulders) - gl.glBegin(GL_LINES); - stickPoint(leftLegTop); - stickPoint(leftLegBottom); - stickPoint(rightLegTop); - stickPoint(rightLegBottom); - stickPoint(leftShoulder); - stickPoint(leftHand); - stickPoint(rightShoulder); - stickPoint(rightHand); - stickPoint(rightShoulder); - stickPoint(torsoRightTop); - stickPoint(leftShoulder); - stickPoint(torsoLeftTop); - gl.glEnd(); - - // Set point size of the stickfigure - gl.glPointSize(7.5f); - - // Begin drawing all points - gl.glBegin(GL_POINTS); - - // First the torso points - stickPoint(torsoLeftTop); - stickPoint(torsoRightTop); - stickPoint(torsoRightBottom); - stickPoint(torsoLeftBottom); - - // Shoulders and the hands - stickPoint(leftShoulder); - stickPoint(rightShoulder); - stickPoint(leftHand); - stickPoint(rightHand); - - // The neck - stickPoint(neckStart); - stickPoint(neckEnd); - - // And finally the legs - stickPoint(leftLegTop); - stickPoint(leftLegBottom); - stickPoint(rightLegTop); - stickPoint(rightLegBottom); - - // Stop drawing points - gl.glEnd(); + private void drawBeam(float x, float y, float z, Direction dir, Color color) { + if (asStickFigure) { + // for a stick figure, draw a thin figure without colors + switch (dir) { + case X: + assert x != 0; + y = z = boneSize; + break; + case Y: + assert y != 0; + x = z = boneSize; + break; + case Z: + assert z != 0; + x = y = boneSize; + break; + default: + throw new AssertionError(dir.name()); + } + // stick figures always get a "bone" color + setColor(boneColor); + } else { + assert x != 0; + assert y != 0; + assert z != 0; + if (color != null) { + setColor(color); + } + } + gl.glScalef(x, y, z); + glut.glutSolidCube(1); + // return to previous scales + gl.glScalef(1 / x, 1 / y, 1 / z); } - + /** - * Draws a point with a given vector p - * @param p Point in space + * Draws a joint for stick figure model. */ - private void stickPoint(Vector p) { - gl.glVertex3f((float) p.x(), (float) p.y(), (float) p.z()); + private void drawJoint() { + if (asStickFigure) { + glut.glutSolidSphere(boneSize * 1.5, 10, 10); + } } - - + /** * Draws the torso of the robot. */ private void drawTorso() { - // Push matrix for returning to origin later - gl.glPushMatrix(); - // Scale the torso to specified values - gl.glScalef(torsoWidth, depth, torsoHeight); - - // Draw the torso itself with given color - setColorRGB(230, 230, 230); - glut.glutSolidCube(1f); - - // Pop matrix so we return to the origin - gl.glPopMatrix(); + drawBeam(torsoWidth, depth, torsoHeight, Direction.Z, Color.LIGHT_GRAY); + + if (asStickFigure) { + // draw the bone connecting the arms (visible for stick figure) + gl.glTranslatef(0, 0, torsoHeight / 2); + drawBeam(torsoWidth + armWidth * 3, boneSize, boneSize, Direction.X, null); + + gl.glTranslatef(0, 0, -torsoHeight); + // draw the bone connecting the legs (visible for stick figure) + drawBeam(.5f * torsoWidth + legWidth / 2, boneSize, boneSize, Direction.X, null); + + // return to torso center + gl.glTranslatef(0, 0, torsoHeight / 2); + } } /** * Draws both shoulders of the robot. */ - private void drawShoulders() { - // Push the translation matrix so we can return to the origin + private void drawShoulderTop() { + // save position gl.glPushMatrix(); - - // Translate to the left of the robot for left shoulder - gl.glTranslatef(torsoWidth / 2 + shoulderRadius / 1.5f, 0f, - torsoHeight / 2); - - // Set the drawing color and draw left shoulder - setColorRGB(100, 130, 255); - glut.glutSolidSphere(shoulderRadius, 10, 10); - - // Pop the matrix and then push it, so we are at the origin - gl.glPopMatrix(); - gl.glPushMatrix(); - - // Translate to the right of the robot for right shoulder - gl.glTranslatef(- torsoWidth / 2 - shoulderRadius / 1.5f, 0f, - torsoHeight / 2); - + + float shoulder_x = torsoWidth / 2 + shoulderRadius / 1.5f; + // Translate to the left of the robot for left shoulder + gl.glTranslatef(shoulder_x, 0f, torsoHeight / 2); + + setColor(Colors.BLUEISH); + // Set the drawing color and draw right shoulder - setColorRGB(100, 130, 255); glut.glutSolidSphere(shoulderRadius, 10, 10); - - // Pop the translation matrix so we are at the origin + // left shoulder + gl.glTranslatef(-2 * shoulder_x, 0, 0); + glut.glutSolidSphere(shoulderRadius, 10, 10); + + // restore position gl.glPopMatrix(); } /** - * Draws the legs and feet of the robot. + * Draws the upper and lower legs and feet of the robot. + * @param isRight True if at the robot's right (from the robot POV). */ - private void drawLegs() { - // Push the matrix for returning to origin + private void drawLeg(boolean isRight) { + // save center position gl.glPushMatrix(); - - // Translate and scale for the left leg - gl.glTranslatef(torsoWidth / 2 - legWidth * 2, 0f, - - legLength / 2 - torsoHeight / 2); - gl.glScalef(legWidth, legWidth, legLength); - - // Set the color and draw the left leg - setColorRGB(150, 150, 150); - glut.glutSolidCube(1f); - - // First scale and translate back, - // then new transformation for left foot - gl.glScalef(1/legWidth, 1/legWidth, 1/legLength); - gl.glTranslatef(0f, 0f, - legLength / 2); - gl.glTranslatef(0f, legWidth / 2, -legWidth / 2); - gl.glScalef(legWidth, legWidth * 2, legWidth); - - // Set the color and draw left foot - setColorRGB(100, 100, 100); - glut.glutSolidCube(1f); - - // Pop the matrix and then push, so we are at the origin - gl.glPopMatrix(); - gl.glPushMatrix(); - - // Translate and scale for the right leg - gl.glTranslatef(-torsoWidth / 2 + legWidth * 2, 0f, - - legLength / 2 - torsoHeight / 2); - gl.glScalef(legWidth, legWidth, legLength); - - // Set the color and draw right leg - setColorRGB(150, 150, 150); - glut.glutSolidCube(1f); - - // Scale and translate back, - // then translate and scale for right foot - gl.glScalef(1/legWidth, 1/legWidth, 1/legLength); - gl.glTranslatef(0f, 0f, - legLength / 2); - gl.glTranslatef(0f, legWidth / 2, -legWidth / 2); - gl.glScalef(legWidth, legWidth * 2, legWidth); - - // Set color and draw right foot - setColorRGB(100, 100, 100); - glut.glutSolidCube(1f); - - // Pop so we are at the origin + + // The legs are located on the first and third quarter + float leg_top_x = -torsoWidth / 4; + if (!isRight) { + leg_top_x += torsoWidth / 2; + } + gl.glTranslatef(leg_top_x, 0f, -torsoHeight / 2); + drawJoint(); + + // upper leg half + gl.glTranslatef(0, 0, -legLength / 4); + drawBeam(legWidth, legWidth, legLength / 2, Direction.Z, Color.DARK_GRAY); + + // knee + gl.glTranslatef(0, 0, -legLength / 4); + drawJoint(); + + // lower leg half + gl.glTranslatef(0, 0, -legLength / 4); + drawBeam(legWidth, legWidth, legLength / 2, Direction.Z, Color.DARK_GRAY); + + // draw feet! + if (!asStickFigure) { + gl.glTranslatef(0, legWidth / 2, -legLength / 4 - footHeight / 2); + drawBeam(footWidth, footLength, footHeight, Direction.Y, Colors.GRAYISH); + } + + // Restore position gl.glPopMatrix(); } /** * Draw both arms and both hands of the robot. + * @param isRight True if at the robot's right (from the robot POV). */ - private void drawArms() { + private void drawArm(boolean isRight) { // Push the translation matrix so we can return to the origin gl.glPushMatrix(); - - // Translate and scale for the left arm - gl.glTranslatef(torsoWidth / 2 + armWidth * 1.5f, 0f, - torsoHeight / 2 - armLength / 2 - 0.05f); - gl.glScalef(armWidth, armWidth, armLength); - - // Set the color and draw the arm itself - setColorRGB(200, 200, 200); - glut.glutSolidCube(1f); - - // Translate and scale for the left hand - gl.glScalef(1/armWidth, 1/armWidth, 1/armLength); - gl.glTranslatef(0f, 0f, - armLength / 2); - - // Set the color and draw the left hand - setColorRGB(130, 158, 174); - glut.glutSolidSphere(armWidth * 1.25f, 10, 10); - - // Pop the translation matrix and return to the origin - gl.glPopMatrix(); - gl.glPushMatrix(); - - // Translate and scale for the right arm - gl.glTranslatef(-(torsoWidth / 2 + armWidth * 1.5f), 0f, - torsoHeight / 2 - armLength / 2 - 0.05f); - gl.glScalef(armWidth, armWidth, armLength); - - // Set the color and draw the right arm - setColorRGB(200, 200, 200); - glut.glutSolidCube(1f); - - // Translate and scale for right hand - gl.glScalef(1/armWidth, 1/armWidth, 1/armLength); - gl.glTranslatef(0f, 0f, - armLength / 2); - - // Set color and draw the right hand - setColorRGB(130, 158, 174); - glut.glutSolidSphere(armWidth * 1.25f, 10, 10); - - // Pop the translation matrix so we are at the origin again + + // the arm is located outside the torso + float arm_x = torsoWidth / 2 + armWidth * 1.5f; + if (isRight) { + arm_x *= -1; + } + // arm starts next to the shoulder let's add a joint there... + gl.glTranslatef(arm_x, 0, torsoHeight / 2); + drawJoint(); + + // here is your arm (start drawing from the elbow) + gl.glTranslatef(0, 0, - armLength / 2); + drawBeam(armWidth, armWidth, armLength, Direction.Z, Colors.ARM_GRAY_COLOR); + + if (!asStickFigure) { + // Give me a big hand! + setColor(Colors.DIRTY_BLUE); + gl.glTranslatef(0f, 0f, - armLength / 2); + glut.glutSolidSphere(armWidth * 1.25f, 10, 10); + } + gl.glPopMatrix(); } /** - * Set the color for drawing specified with RGB values. - * - * @param r Red parameter (0 - 255) - * @param g Green parameter (0 - 255) - * @param b Blue parameter (0 - 255) - */ - private void setColorRGB(int r, int g, int b) { - gl.glColor3ub((byte) r, (byte) g, (byte) b); - } - - /** * Draw the head and the neck of the robot. */ private void drawHead() { // Push matrix so we can go to the origin afterwards gl.glPushMatrix(); - - // Translate and scale for the neck + + // position centered above the torso for the neck gl.glTranslatef(0f, 0f, torsoHeight / 2 + neckHeight / 2); - gl.glScalef(headRadius / 2.5f, headRadius / 2.5f, neckHeight); - - // Set color and draw neck - setColorRGB(230, 230, 230); - glut.glutSolidCube(1f); - - // Pop the matrix and push so we are at the origin again - gl.glPopMatrix(); - gl.glPushMatrix(); - - // Translate so we are above the neck for the head - gl.glTranslatef(0f, 0f, torsoHeight / 2 + neckHeight - + headRadius / 2); - + float neckRadius = headRadius / 2.5f; + drawBeam(neckRadius, neckRadius, neckHeight, Direction.Z, Colors.LAVENDER); + + // continue moving to the center of the head + gl.glTranslatef(0f, 0f, neckHeight / 2 + headRadius / 2); + // Set color and draw head - setColorRGB(190, 210, 220); + setColor(asStickFigure ? boneColor : Colors.PALE_TURQOISE); glut.glutSolidSphere(headRadius, 10, 10); - + // Pop so we are at the origin again gl.glPopMatrix(); } |