/** * Copyright (c) 2013, Mattias Andrée * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package v; import javax.swing.*; import java.awt.event.*; import java.awt.*; import java.util.*; import static v.VMaths.*; public class D3 { static final float DEGREES_PER_SECOND = 50f / 1000f; static final float DEGREES_PER_SECOND_PER_SECOND = DEGREES_PER_SECOND; static float[] xs = new float[8 + 6 + 3 + 3]; static float[] ys = new float[8 + 6 + 3 + 3]; static float[] zs = new float[8 + 6 + 3 + 3]; static float[] normal_xs = {0f, 0f, 1f, -1f, 0f, 0f}; static float[] normal_ys = {1f, 0f, 0f, 0f, 0f, -1f}; static float[] normal_zs = {0f, 1f, 0f, 0f, -1f, 0f}; static float[] rotation = {1f, 0f, 0f}; static float rotation_speed = 0f; static int rx = 0; static int ry = 0; static int rxy = 0; static int rg = 0; static int srx = 0; static int sry = 0; static int srxy = 0; static int zrx = 0; static int zry = 0; static int zrxy = 0; static float[][] Pm = UNIT; static float[][] Qm = UNIT; static boolean rreset = false; static boolean sreset = false; static boolean zreset = false; static boolean camera_to_rotation = false; static boolean rotation_to_camera = false; public static void main(String... args) throws InterruptedException { resetRotation(); xs[8] = +1; xs[11] = -1; ys[9] = +1; ys[12] = -1; zs[10] = +1; zs[13] = -1; xs[14] = +1; ys[15] = +1; zs[16] = +1; xs[17] = +1; ys[18] = +1; zs[19] = +1; final JFrame window = new JFrame(); final JPanel panel; window.pack(); window.setLayout(new BorderLayout()); window.add(panel = new JPanel() { float[] pxs = new float[8 + 7]; float[] pys = new float[8 + 7]; float[] pzs = new float[8 + 7]; float[] normal_pxs = new float[6]; float[] normal_pys = new float[6]; float[] normal_pzs = new float[6]; { this.setBackground(Color.BLACK); } @Override public void paint(Graphics g) { super.paint(g); /* Camera */ for (int i = 8; i < 8 + 6; i++) { pxs[i] = xs[i]; pys[i] = ys[i]; pzs[i] = zs[i]; } for (int i = 0; i < 8; i++) { float[] v = mul(Qm, mul(Pm, xs[i], ys[i], zs[i])); pxs[i] = v[0]; pys[i] = v[1]; pzs[i] = v[2]; } for (int i = 0; i < 6; i++) { float[] v = mul(Qm, mul(Pm, normal_xs[i], normal_ys[i], normal_zs[i])); normal_pxs[i] = v[0]; normal_pys[i] = v[1]; normal_pzs[i] = v[2]; } /* End camera */ /* */ int[] z = new int[6]; for (int i = 0; i < 6; i++) { int v = (int)(normal_pzs[i] * 1000f); z[i] = (v << 8) | i; } Arrays.sort(z); for (int i = 0; i < 6; i++) { int side = z[i] & 7; switch (side) { case 0: draw(188, 188, 188, 0, 1, 5, 4, g, normal_pzs[0]); break; case 1: draw(188, 0, 0, 0, 1, 3, 2, g, normal_pzs[1]); break; case 2: draw( 0, 188, 0, 0, 2, 6, 4, g, normal_pzs[2]); break; case 3: draw( 0, 0, 188, 1, 3, 7, 5, g, normal_pzs[3]); break; case 4: draw(188, 128, 0, 4, 5, 7, 6, g, normal_pzs[4]); break; case 5: draw(188, 188, 0, 2, 3, 7, 6, g, normal_pzs[5]); break; } } /* */ /* */ draw(new Color(255, 0, 0), 0, 1, g); draw(new Color( 0, 0, 255), 0, 2, g); draw(new Color(210, 0, 210), 0, 4, g); draw(new Color(255, 255, 0), 1, 3, g); draw(new Color(210, 128, 0), 1, 5, g); draw(new Color( 0, 255, 0), 2, 3, g); draw(new Color( 0, 210, 210), 2, 6, g); draw(new Color(188, 210, 0), 3, 7, g); draw(new Color(188, 0, 0), 4, 5, g); draw(new Color( 0, 0, 188), 4, 6, g); draw(new Color(188, 188, 0), 5, 7, g); draw(new Color( 0, 188, 0), 6, 7, g); /* */ /* */ draw(new Color(255, 0, 0), 8, 14, g); draw(new Color( 0, 255, 0), 9, 14, g); draw(new Color( 0, 0, 255), 10, 14, g); draw(new Color( 0, 255, 255), 11, 14, g); draw(new Color(255, 0, 255), 12, 14, g); draw(new Color(255, 255, 0), 13, 14, g); /* */ } private void draw(int R, int G, int B, int a, int b, int c, int d, Graphics g, float normal_z) { int A = (int)(255f * normal_z); if (A > 0) { A = A / 2 + 128; if (A > 255) A = 255; g.setColor(new Color(R, G, B, A)); g.fillPolygon(new int[] {Px(a), Px(b), Px(c), Px(d)}, new int[] {Py(a), Py(b), Py(c), Py(d)}, 4); } } private void draw(Color colour, int i, int j, Graphics g) { g.setColor(colour); g.drawLine(Px(i), Py(i), Px(j), Py(j)); } static final float SCREEN_Z = 50f; static final float MODEL_Z = 55f; static final float VIEWER_Z = 50f; static final float VIEWER_X = 0f; static final float VIEWER_Y = 0f; static final float CAMERA_Z = 10f; static final float CAMERA_X = 0f; static final float CAMERA_Y = 0f; private int Px(int i) { /* Orthogonal projection */ //float z = SCREEN_Z / (pzs[i] + MODEL_Z); //return (int)(pxs[i] * z * 300f + 400f); /* Perspective projection */ float model_z = pzs[i] + MODEL_Z; float model_x = pxs[i]; float p = VIEWER_Z / (model_z - CAMERA_Z) * (model_x - CAMERA_X) - VIEWER_X; return (int)(p * 300f + 400f); } private int Py(int i) { /* Orthogonal projection */ //float z = SCREEN_Z / (pzs[i] + MODEL_Z); //return (int)(pys[i] * z * 300f + 300f); /* Perspective projection */ float model_z = pzs[i] + MODEL_Z; float model_y = pys[i]; float p = VIEWER_Z / (model_z - CAMERA_Z) * (model_y - CAMERA_Y) - VIEWER_Y; return (int)(p * 300f + 300f); } }); window.setBackground(Color.BLACK); final Insets in = window.getInsets(); window.setSize(new Dimension(in.left + 800 + in.right, in.top + 600 + in.bottom)); window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); window.setVisible(true); final float SPEED = (float)(Math.sqrt(25.5f / 2f)) * DEGREES_PER_SECOND_PER_SECOND; final float SSPEED = 25.5f * DEGREES_PER_SECOND; window.addKeyListener(new KeyListener() { public void keyTyped(KeyEvent e) {} public void keyReleased(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_D: zrx = srx = rx = 0; break; case KeyEvent.VK_A: zrx = srx = rx = 0; break; case KeyEvent.VK_W: zry = sry = ry = 0; break; case KeyEvent.VK_S: zry = sry = ry = 0; break; case KeyEvent.VK_E: zrxy = srxy = rxy = 0; break; case KeyEvent.VK_Q: zrxy = srxy = rxy = 0; break; case KeyEvent.VK_F: rg = 0; break; case KeyEvent.VK_C: rg = 0; break; } } public void keyPressed(KeyEvent e) { boolean shift = (e.getModifiersEx() & KeyEvent.SHIFT_DOWN_MASK) != 0; boolean alt = (e.getModifiersEx() & KeyEvent.ALT_DOWN_MASK) != 0; boolean ctrl = (e.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0; int v = ctrl ? 2 : 1; if ((shift == false) && (alt == false)) switch (e.getKeyCode()) { case KeyEvent.VK_D: rx = +v; break; case KeyEvent.VK_A: rx = -v; break; case KeyEvent.VK_W: ry = +v; break; case KeyEvent.VK_S: ry = -v; break; case KeyEvent.VK_E: rxy = +v; break; case KeyEvent.VK_Q: rxy = -v; break; case KeyEvent.VK_F: rg = +1; break; case KeyEvent.VK_C: rg = -1; break; case KeyEvent.VK_X: camera_to_rotation = true; break; case KeyEvent.VK_R: if (ctrl) rreset = true; else rotation_speed = 0; break; } else if (shift && (alt == false)) switch (e.getKeyCode()) { case KeyEvent.VK_D: srx = +v; break; case KeyEvent.VK_A: srx = -v; break; case KeyEvent.VK_W: sry = +v; break; case KeyEvent.VK_S: sry = -v; break; case KeyEvent.VK_E: srxy = +v; break; case KeyEvent.VK_Q: srxy = -v; break; case KeyEvent.VK_X: rotation_to_camera = true; break; case KeyEvent.VK_R: sreset = true; break; } else if ((shift == false) && alt) switch (e.getKeyCode()) { case KeyEvent.VK_D: zrx = +v; break; case KeyEvent.VK_A: zrx = -v; break; case KeyEvent.VK_W: zry = +v; break; case KeyEvent.VK_S: zry = -v; break; case KeyEvent.VK_E: zrxy = +v; break; case KeyEvent.VK_Q: zrxy = -v; break; case KeyEvent.VK_R: zreset = true; break; } } }); for (;;) { Thread.sleep(50); if (camera_to_rotation) { transform(Pm); camera_to_rotation = false; sreset = true; } if (sreset) { Pm = UNIT; for (int i = 8; i < 8 + 6; i++) xs[i] = ys[i] = zs[i] = 0; xs[8] = +1; xs[11] = -1; ys[9] = +1; ys[12] = -1; zs[10] = +1; zs[13] = -1; sreset = false; } if (zreset) { Qm = UNIT; for (int i = 8 + 6 + 3; i < 8 + 6 + 3 + 3; i++) xs[i] = ys[i] = zs[i] = 0; xs[17] = +1; ys[18] = +1; zs[19] = +1; zreset = false; } if (rotation_to_camera) { stransform(resetRotation()); rotation_to_camera = false; } if (rreset) { resetRotation(); rreset = false; } if (rx * rx == 1) { float[] v = mul(rx * SPEED, createRotation("x", Pm)); v = add(mul(rotation_speed, rotation), v); rotation_speed = length(v); rotation = normalise(v); } if (ry * ry == 1) { float[] v = mul(ry * SPEED, createRotation("y", Pm)); v = add(mul(rotation_speed, rotation), v); rotation_speed = length(v); rotation = normalise(v); } if (rxy * rxy == 1) { float[] v = mul(rxy * SPEED, createRotation("xy", Pm)); v = add(mul(rotation_speed, rotation), v); rotation_speed = length(v); rotation = normalise(v); } if (rg == 1) rotation_speed *= 1.1f; else if (rg == -1) rotation_speed /= 1.1f; if (rotation_speed != 0) transform(createRotation(rotation, rotation_speed)); if (rx * rx == 4) transform(createRotation("x", 45 * rx / 2)); if (ry * ry == 4) transform(createRotation("y", 45 * ry / 2)); if (rxy * rxy == 4) transform(createRotation("xy", 45 * rxy / 2)); if (rx * rx == 4) rx = 0; if (ry * ry == 4) ry = 0; if (rxy * rxy == 4) rxy = 0; if (srx * srx == 1) stransform(createRotation("x", srx * SSPEED)); if (sry * sry == 1) stransform(createRotation("y", sry * SSPEED)); if (srxy * srxy == 1) stransform(createRotation("xy", srxy * SSPEED)); if (srx * srx == 4) stransform(createRotation("x", 45 * srx / 2)); if (sry * sry == 4) stransform(createRotation("y", 45 * sry / 2)); if (srxy * srxy == 4) stransform(createRotation("xy", 45 * srxy / 2)); if (srx * srx == 4) srx = 0; if (sry * sry == 4) sry = 0; if (srxy * srxy == 4) srxy = 0; if (zrx * zrx == 1) ztransform(createRotation("x", zrx * SSPEED)); if (zry * zry == 1) ztransform(createRotation("y", zry * SSPEED)); if (zrxy * zrxy == 1) ztransform(createRotation("xy", zrxy * SSPEED)); if (zrx * zrx == 4) ztransform(createRotation("x", 45 * zrx / 2)); if (zry * zry == 4) ztransform(createRotation("y", 45 * zry / 2)); if (zrxy * zrxy == 4) ztransform(createRotation("xy", 45 * zrxy / 2)); if (zrx * zrx == 4) zrx = 0; if (zry * zry == 4) zry = 0; if (zrxy * zrxy == 4) zrxy = 0; panel.repaint(); } } public static float[][] resetRotation() { float[][] r = new float[3][3]; r[0][0] = xs[14]; r[1][0] = ys[14]; r[2][0] = zs[14]; r[0][1] = xs[15]; r[1][1] = ys[15]; r[2][1] = zs[15]; r[0][2] = xs[16]; r[1][2] = ys[16]; r[2][2] = zs[16]; for (int i = 0; i < 8; i++) { xs[i] = ((i & 1) != 0 ? 1f : 0f) - 0.5f; ys[i] = ((i & 2) != 0 ? 1f : 0f) - 0.5f; zs[i] = ((i & 4) != 0 ? 1f : 0f) - 0.5f; } xs[14] = xs[15] = xs[16] = 0; ys[14] = ys[15] = ys[16] = 0; zs[14] = zs[15] = zs[16] = 0; xs[14] = ys[15] = zs[16] = 1; normal_xs = new float[] {0f, 0f, 1f, -1f, 0f, 0f}; normal_ys = new float[] {1f, 0f, 0f, 0f, 0f, -1f}; normal_zs = new float[] {0f, 1f, 0f, 0f, -1f, 0f}; return r; } public static void transform(float[][] m) { /* This is not a transformation of the object, but * rather its projection onto the screen. (This * program combines the object and the projection.) */ for (int i = 0; i < 8; i++) { float[] v = mul(m, xs[i], ys[i], zs[i]); xs[i] = v[0]; ys[i] = v[1]; zs[i] = v[2]; } for (int i = 8 + 6; i < 8 + 6 + 3; i++) { float[] v = mul(m, xs[i], ys[i], zs[i]); xs[i] = v[0]; ys[i] = v[1]; zs[i] = v[2]; } for (int i = 0; i < 6; i++) { float[] v = mul(m, normal_xs[i], normal_ys[i], normal_zs[i]); normal_xs[i] = v[0]; normal_ys[i] = v[1]; normal_zs[i] = v[2]; } } public static void stransform(float[][] m) { for (int i = 8; i < 8 + 6; i++) { float[] v = mul(m, xs[i], ys[i], zs[i]); xs[i] = v[0]; ys[i] = v[1]; zs[i] = v[2]; } if (Pm == UNIT) { Pm = new float[3][3]; for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) Pm[i][j] = UNIT[i][j]; } Pm[0][0] = xs[8]; Pm[1][0] = ys[8]; Pm[2][0] = zs[8]; Pm[0][1] = xs[9]; Pm[1][1] = ys[9]; Pm[2][1] = zs[9]; Pm[0][2] = xs[10]; Pm[1][2] = ys[10]; Pm[2][2] = zs[10]; } public static void ztransform(float[][] m) { for (int i = 8 + 6 + 3; i < 8 + 6 + 3 + 3; i++) { float[] v = mul(m, xs[i], ys[i], zs[i]); xs[i] = v[0]; ys[i] = v[1]; zs[i] = v[2]; } if (Qm == UNIT) { Qm = new float[3][3]; for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) Qm[i][j] = UNIT[i][j]; } Qm[0][0] = xs[17]; Qm[1][0] = ys[17]; Qm[2][0] = zs[17]; Qm[0][1] = xs[18]; Qm[1][1] = ys[18]; Qm[2][1] = zs[18]; Qm[0][2] = xs[19]; Qm[1][2] = ys[19]; Qm[2][2] = zs[19]; } }