I was having fun implementing some really basic stuff for 3D graphics. See what I came up with:Matrix.java:package net.coderodde.lib3d;/** * This class represents a matrix. *  * @author Rodion rodde Efremov * @version 1.6 */public class Matrix {    /**     * The actual elements.     */    private final double[][] matrix;    /**     * Constructs a zero matrix with <code>rows</code> rows and     * <code>columns</code> columns.     *      * @param rows    the amount of rows in the matrix.     * @param columns the amount of columns in the new matrix.     */    public Matrix(int rows, int columns) {        this.matrix = new double[rows][];        for (int i = 0; i < rows; ++i) {            this.matrix[i] = new double[columns];        }    }    /**     * Constructs a zero square matrix with <code>dimension</code> rows and     * columns.     *      * @param dimension the dimension of this square matrix.     */    public Matrix(int dimension) {        this(dimension, dimension);    }    /**     * Multiplies this matrix by vertex <code>v</code> and returns the result,     * which is a vertex too.     *      * @param  v the vertex to multiply by this matrix.     * @return the result vertex.     */    public Vertex product(Vertex v) {        double[] vector = new double[]{ v.x, v.y, v.z };        double[] vec = new double[vector.length];        for (int row = 0; row < matrix.length; ++row) {            for (int col = 0; col < matrix[row].length; ++col) {                vec[row] += vector[col] * matrix[row][col];            }        }        return new Vertex(vec[0], vec[1], vec[2]);    }    /**     * Returns the matrix for rotating around <tt>x</tt>-axis.     *      * @param  angle the rotation angle in radians.     * @return the rotation matrix.     */    public static Matrix getXRotationMatrix(double angle) {        final Matrix ret = new Matrix(3);        ret.matrix[0][0] = 1;        ret.matrix[0][1] = 0;        ret.matrix[0][2] = 0;        ret.matrix[1][0] = 0;        ret.matrix[2][0] = 0;        ret.matrix[1][1] = Math.cos(angle);        ret.matrix[1][2] = -Math.sin(angle);        ret.matrix[2][1] = Math.sin(angle);        ret.matrix[2][2] = Math.cos(angle);        return ret;    }    /**     * Returns the matrix for rotating around <tt>y</tt>-axis.     *      * @param  angle the rotation angle in radians.     * @return the rotation matrix.     */    public static Matrix getYRotationMatrix(double angle) {        final Matrix ret = new Matrix(3);        ret.matrix[0][1] = 0;        ret.matrix[1][0] = 0;        ret.matrix[1][2] = 0;        ret.matrix[2][1] = 0;        ret.matrix[1][1] = 1;        ret.matrix[0][0] = +Math.cos(angle);        ret.matrix[0][2] = +Math.sin(angle);        ret.matrix[2][0] = -Math.sin(angle);        ret.matrix[2][2] = +Math.cos(angle);        return ret;    }    /**     * Returns the matrix for rotating around <tt>z</tt>-axis.     *      * @param  angle the rotation angle in radians.     * @return the rotation matrix.     */    public static Matrix getZRotationMatrix(double angle) {        final Matrix ret = new Matrix(3);        ret.matrix[0][2] = 0;        ret.matrix[1][2] = 0;        ret.matrix[2][2] = 1;        ret.matrix[2][1] = 0;        ret.matrix[2][0] = 0;        ret.matrix[0][0] =  Math.cos(angle);        ret.matrix[0][1] = -Math.sin(angle);        ret.matrix[1][0] =  Math.sin(angle);        ret.matrix[1][1] =  Math.cos(angle);        return ret;    }}Vertex.java:package net.coderodde.lib3d;import java.awt.Color;import java.util.ArrayList;import java.util.Collections;import java.util.Iterator;import java.util.List;/** * This class implements a vertex that can be thought of as a vector. *  * @author Rodion rodde Efremov * @version 1.6 */public class Vertex implements Iterable<Vertex> {    public double x;    public double y;    public double z;    /**     * The list of neighbor vertices.     */    private final List<Vertex> neighborVertexList;    /**     * The list of colors.     */    private final List<Color> neighborColorList;    /**     * Constructs a new vertex.     *      * @param x the initial <tt>x</tt>-coordinate.     * @param y the initial <tt>y</tt>-coordinate.     * @param z the initial <tt>z</tt>-coordinate.     */    public Vertex(double x, double y, double z) {        this.x = x;        this.y = y;        this.z = z;        this.neighborVertexList = new ArrayList<>();        this.neighborColorList = new ArrayList<>();    }    /**     * Constructs a new vertex with the same coordinates as <code>other</code>.     *      * @param other the other vertex.     */    public Vertex(Vertex other) {        this(other.x, other.y, other.z);    }    /**     * Constructs a new vertex located at origo.     */    public Vertex() {        this(0.0, 0.0, 0.0);    }    /**     * Returns the view of colors of this vertex.     *      * @return the color view.     */    public List<Color> getColorList() {        return Collections.<Color>unmodifiableList(neighborColorList);    }    /**     * Returns the view of neighbor vertices of this vertex.     *      * @return the neighbor view.     */    public List<Vertex> getNeighborList() {        return Collections.<Vertex>unmodifiableList(neighborVertexList);    }    /**     * Adds a neighbor vertex to this vertex.     *      * @param neighbor the neighbor to add.     * @param color    the color of the edge from this vertex to      *                 <code>neighbor</code>.     */    public void addNeighbor(Vertex neighbor, Color color) {        neighborVertexList.add(neighbor);        neighborColorList.add(color);    }    /**     * Returns an iterator over neighbor vertices of this vertex.     *      * @return an iterator.     */    @Override    public Iterator<Vertex> iterator() {        return this.neighborVertexList.iterator();    }    /**     * Returns a string representation of this vertex.     *      * @return a string.     */    @Override    public String toString() {        return [Vertex ( + x + ,  + y + ,  + z + )];    }}SceneObject.java:package net.coderodde.lib3d;import java.util.ArrayList;import java.util.Collections;import java.util.Iterator;import java.util.List;/** * This class models an object in the scene. *  * @author Rodion rodde Efremov * @version 1.6 */public class SceneObject implements Iterable<Vertex> {    /**     * The list of vertices this object consists of. These are vector pointing     * from <code>location</code>.     */    private final List<Vertex> vertexList;    /**     * The location of this object.     */    private final Vertex location;    /**     * Constructs a new scene object with given location.     *      * @param x the <tt>x</tt>-coordinate of the location.     * @param y the <tt>y</tt>-coordinate of the location.     * @param z the <tt>z</tt>-coordinate of the location.     */    public SceneObject(double x, double y, double z) {        this.location = new Vertex(x, y, z);        this.vertexList = new ArrayList<>();    }    /**     * Constructs a new scene object with given location.     *      * @param vertex the initial location.     */    public SceneObject(Vertex vertex) {        this(vertex.x, vertex.y, vertex.y);    }    /**     * Constructs a new scene object with location at origo.     */    public SceneObject() {        this(0.0, 0.0, 0.0);    }    /**     * Returns the view of vertices belonging to this object.     *      * @return the vertex view.     */    public List<Vertex> getVertexList() {        return Collections.<Vertex>unmodifiableList(vertexList);    }    /**     * Returns the location of this scene object.     *      * @return the location.     */    public Vertex getLocation() {        return new Vertex(location);    }    /**     * Sets the location of this object.     *      * @param  v the new location.     */    public void setLocation(Vertex v) {        this.location.x = v.x;        this.location.y = v.y;        this.location.z = v.z;    }    /**     * Adds a vertex to this object.     *      * @param vertex the vertex to add.     */    public void add(Vertex vertex) {        this.vertexList.add(vertex);    }    /**     * Rotates this geometric object relative to the point     * <code>relative</code>.     *     * @param relative the relative point.     * @param angleAroundX the angle around the <tt>x</tt>-axis.     * @param angleAroundY the angle around the <tt>y</tt>-axis.     * @param angleAroundZ the angle around the <tt>z</tt>-axis.     */    public void rotate(Vertex relative,                       double angleAroundX,                       double angleAroundY,                       double angleAroundZ) {        rotateImpl(relative,                   location,                   angleAroundX,                   angleAroundY,                   angleAroundZ);        // Rotate the locatio vector of this geometric object.        final Vertex zero = new Vertex();        // Rotate the vertex vectors.        for (final Vertex vertex : vertexList) {            rotateImpl(zero,                       vertex,                       -angleAroundX,                       -angleAroundY,                       -angleAroundZ);        }    }    /**     * Implements the rotation routine.     *     * @param relative the relative location.     * @param target the vertex to rotate.     * @param angleAroundXAxis the angle around the <tt>x</tt>-axis.     * @param angleAroundY the angle around the <tt>y</tt>-axis.     * @param angleAroundZ the angle around the <tt>z</tt>-axis.     */    private void rotateImpl(Vertex relative,                            Vertex target,                            double angleAroundXAxis,                            double angleAroundYAxis,                            double angleAroundZAxis) {        final Matrix x = Matrix.getXRotationMatrix(angleAroundXAxis);        final Matrix y = Matrix.getYRotationMatrix(angleAroundYAxis);        final Matrix z = Matrix.getZRotationMatrix(angleAroundZAxis);        Vertex tmp = new Vertex(target.x - relative.x,                                target.y - relative.y,                                target.z - relative.z);        tmp = x.product(tmp);        tmp = y.product(tmp);        tmp = z.product(tmp);        target.x = relative.x + tmp.x;        target.y = relative.y + tmp.y;        target.z = relative.z + tmp.z;    }    @Override    public Iterator<Vertex> iterator() {        return this.vertexList.iterator();    }}SceneView.java:package net.coderodde.lib3d;import java.awt.Canvas;import java.awt.Color;import java.awt.Dimension;import java.awt.Graphics;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import java.util.ArrayList;import java.util.Arrays;import java.util.List;/** * This class implements a scene view displaying three-dimensional objects. * * @author Rodion rodde Efremov */public class SceneView extends Canvas implements KeyListener {    /**     * The list of scene objects in this view.     */    private final List<SceneObject> sceneObjectList;    /**     * The rotation source. All objects are rotated with respect to this point.     */    private final Vertex rotationSource;    /**     * Constructs this scene canvas.     *     * @param width the width of this canvas in pixels.     * @param height the height of this canvas in pixels.     */    public SceneView(int width,                      int height) {        setPreferredSize(new Dimension(width, height));        this.sceneObjectList = new ArrayList<>();        this.rotationSource = new Vertex(width / 2, height / 2, 0);        this.addKeyListener(this);        this.setBackground(Color.BLACK);    }    /**     * Draws this view.     *      * @param g the graphics device handle.     */    @Override    public void update(Graphics g) {        g.clearRect(0, 0, getWidth(), getHeight());        g.setColor(Color.red);        for (final SceneObject object : sceneObjectList) {            final Vertex objectOrigin = object.getLocation();            final List<Vertex> vertexList = object.getVertexList();            for (int i = 0; i < vertexList.size(); ++i) {                final Vertex v = vertexList.get(i);                final List<Color> colorList = v.getColorList();                final List<Vertex> neighborList = v.getNeighborList();                for (int j = 0; j < neighborList.size(); ++j) {                    final Vertex neighbor = neighborList.get(j);                    g.setColor(colorList.get(j));                    g.drawLine((int) Math.round(objectOrigin.x + v.x),                               (int) Math.round(objectOrigin.y + v.y),                               (int) Math.round(objectOrigin.x + neighbor.x),                               (int) Math.round(objectOrigin.y + neighbor.y));                }            }        }    }    /**     * Draws this view.     *      * @param g the graphics device handle.     */    @Override    public void paint(Graphics g) {        update(g);    }    /**     * Adds all the vectors in <code>vectors</code> to this scene view.     *     * @param objects the world objects to add to this view.     */    public void addWorldObject(SceneObject... objects) {        sceneObjectList.addAll(Arrays.asList(objects));    }    /**     * Responds to the event of a key being typed.     *      * @param e the key event.     */    @Override    public void keyTyped(KeyEvent e) {        switch (e.getExtendedKeyCode()) {            case KeyEvent.VK_A:                sceneObjectList.stream().forEach((o) -> {                    o.rotate(rotationSource, 0.0, -0.1, 0.0);                });                break;            case KeyEvent.VK_D:                sceneObjectList.stream().forEach((o) -> {                    o.rotate(rotationSource, 0.0, 0.1, 0.0);                });                break;            case KeyEvent.VK_W:                sceneObjectList.stream().forEach((o) -> {                    o.rotate(rotationSource, 0.1, 0.0, 0.0);                });                break;            case KeyEvent.VK_S:                sceneObjectList.stream().forEach((o) -> {                    o.rotate(rotationSource, -0.1, -0.0, 0.0);                });                break;            case KeyEvent.VK_Q:                sceneObjectList.stream().forEach((o) -> {                    o.rotate(rotationSource, 0.0, 0.0, -0.1);                });                break;            case KeyEvent.VK_E:                sceneObjectList.stream().forEach((o) -> {                    o.rotate(rotationSource, 0.0, 0.0, 0.1);                });                break;        }        repaint();    }    @Override    public void keyPressed(KeyEvent e) {}    @Override    public void keyReleased(KeyEvent e) {}}SceneFrame.java:package net.coderodde.lib3d;import java.awt.Dimension;import java.awt.Toolkit;import javax.swing.JFrame;/** * This class implements the frame containing a view. *  * @author Rodion rodde Efremov * @version 1.6 */public class SceneFrame extends JFrame {    /**     * The actual view component.     */    private final SceneView view;    /**     * Constructs a frame containing the view.     *      * @param width  the width of the frame in pixels.     * @param height the height of the frame in pixels.     */    SceneFrame(int width, int height) {        super(3D Cube);        add(view = new SceneView(width, height));        pack();        setResizable(false);        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        final Dimension screenDimension =                 Toolkit.getDefaultToolkit().getScreenSize();        // Center out the frame.        setLocation((screenDimension.width - getWidth()) / 2,                    (screenDimension.height - getHeight()) / 2);        setVisible(true);    }    /**     * Returns the scene view.     *      * @return the scene view.     */    public SceneView getSceneView() {        return view;    }}Demo.java:package net.coderodde.lib3d;import java.awt.Color;import javax.swing.SwingUtilities;public class Demo {    /**     * The entry point into a program.     * @param args the command line arguments.     */    public static void main(final String... args) {        SwingUtilities.invokeLater(() -> {             SceneFrame frame = new SceneFrame(600, 600);            // Let's build a wire cube.            SceneObject cube = new SceneObject(300, 300, 0);            // The vertices.            Vertex v000 = new Vertex(-100, -100, -100);            Vertex v001 = new Vertex(-100, -100, 100);            Vertex v010 = new Vertex(-100, 100, -100);            Vertex v011 = new Vertex(-100, 100, 100);            Vertex v100 = new Vertex(100, -100, -100);            Vertex v101 = new Vertex(100, -100, 100);            Vertex v110 = new Vertex(100, 100, -100);            Vertex v111 = new Vertex(100, 100, 100);            Color red = Color.red;            Color green = Color.green;            Color yellow = Color.yellow;            // Each vertex must know what other vertices it is linked to.            v000.addNeighbor(v001, red);            v000.addNeighbor(v010, red);            v000.addNeighbor(v100, yellow);            v001.addNeighbor(v101, yellow);            v001.addNeighbor(v011, red);            v001.addNeighbor(v000, red);            v010.addNeighbor(v110, yellow);            v010.addNeighbor(v000, red);            v010.addNeighbor(v011, red);            v011.addNeighbor(v111, yellow);            v011.addNeighbor(v001, red);            v011.addNeighbor(v010, red);            //            v100.addNeighbor(v000, yellow);            v100.addNeighbor(v110, green);            v100.addNeighbor(v101, green);            v101.addNeighbor(v001, yellow);            v101.addNeighbor(v111, green);            v101.addNeighbor(v100, green);            v110.addNeighbor(v010, yellow);            v110.addNeighbor(v100, green);            v110.addNeighbor(v111, green);            v111.addNeighbor(v011, yellow);            v111.addNeighbor(v101, green);            v111.addNeighbor(v110, green);            // Load the vertices to the cube.            cube.add(v000);            cube.add(v001);            cube.add(v010);            cube.add(v011);            cube.add(v100);            cube.add(v101);            cube.add(v110);            cube.add(v111);            cube.rotate(cube.getLocation(),                         0.0,                        Math.PI / 2.0,                        0.0);            // Add to the scene.            frame.getSceneView().addWorldObject(cube);        });    }}Any improvements I could do here?(For demonstration, click the window and use the keys Q, W, E, A, S, D to rotate the cube.)
Doing wire 3D cube without libraries
java;matrix;reinventing the wheel;graphics
I'll edit this answer again later when I have more time. I have a lot to say. Since I've only had a chance to play with SceneView.java that is what I'm going to mention.the update method is too large. It needs to get broken up into smaller pieces. Typically I personally like to grab everything inside a loop (be it a while loop, or a for loop) and put it into a new method. With your update method I made 2 more methods: updateSceneObject and updateVertex. I notice too that you switch back and forth with how you iterate through things. Be consistent. Here is a small look at my solution. Note that there is a small bug introduced with this method because your current mode of drawing relies on each matrix being drawn at a specific order./** * Draws this view. * * @param g the graphics device handle. */@Overridepublic void update(Graphics g) {    g.clearRect(0, 0, getWidth(), getHeight());    g.setColor(Color.red);    sceneObjectList.parallelStream().forEach(o->updateSceneObject(g,o));}private void updateSceneObject(Graphics g, SceneObject object) {    final Vertex objectOrigin = object.getLocation();    object.getVertexList().parallelStream().forEach(v->updateVertex(g,objectOrigin, v));}private void updateVertex(Graphics g, Vertex objectOrigin, Vertex v) {    final List<Color> colorList = v.getColorList();    final List<Vertex> neighborList = v.getNeighborList();    for (int j = 0; j < neighborList.size(); ++j) {        final Vertex neighbor = neighborList.get(j);        g.setColor(colorList.get(j));        g.drawLine((int) Math.round(objectOrigin.x + v.x),                (int) Math.round(objectOrigin.y + v.y),                (int) Math.round(objectOrigin.x + neighbor.x),                (int) Math.round(objectOrigin.y + neighbor.y));    }}Vertex.javahaving a list of neighbors and their colors (as a parallel list) is 2 parts wrong. 1 reason it is wrong is a parallel list. in other words you an array that depends on a very specific order. Should that order get disturbed some problems can arise. Usually you will want to encapsulate those values in another class and make a single list of those. the second part of why it is wrong is that I believe Vertex should simply be a data structure and nothing more. I can see your reasoning behind it but neighbor doesn't describe well enough that your line segment is a specific color. (Took me a little bit to figure that out) So to fix points one and two I believe if you make a LineSegment class that has 2 vertices and a single color your code will clean up nicely. Then you would just change Cube to have a list of LineSegments and update those when you rotate.TestsI'm a huge advocate of writing tests. First they act as a form of documentation, then also give you a safety net to change things around with less fear of breaking something. A good thing that should have tests around it is the rotate method for SceneObject. So for instancepublic class SceneObjectTest {    @Test    public void testRotate() throws Exception {        SceneObject object = new SceneObject(0,0,0);        final Vertex CENTER = new Vertex(0,0,0);        Vertex vertex0 = new Vertex(-10, 0, 0);        Vertex vertex1 = new Vertex(10, 0, 0);        object.add(vertex0);        object.add(vertex1);        object.rotate(CENTER, 1, 0, 0 );//rotate 1 degree on x        System.out.println(vertex0);        System.out.println(vertex1);        assertXYZ(vertex0, -10, 0, 0);//0        assertXYZ(vertex1, 10, 0, 0);//0    }    private void assertXYZ(Vertex vertex, double x, double y, double z) {        assertEquals(vertex.x, x, String.format(expected Vertex to have x coordinate of %f but found %f\r\nActual:%s, x, vertex.x, vertex));        assertEquals(vertex.y, y, String.format(expected Vertex to have y coordinate of %f but found %f\r\nActual:%s, y, vertex.y, vertex));        assertEquals(vertex.z, z, String.format(expected Vertex to have z coordinate of %f but found %f\r\nActual:%s, z, vertex.z, vertex));    }}this test takes 0.394 seconds to run and tells me that rotating a horizontal line on the x axis does nothing. Which is correct. So what happens if I rotate 1 degree on the z axis. (Which I can visualize easily in my head) I would expect the following test to pass (look at testRotateOnZAxis())public class SceneObjectTest {    final Vertex CENTER = new Vertex(0, 0, 0);    SceneObject object;    private Vertex vertex0;    private Vertex vertex1;    @BeforeTest    public void SetupBeforeTest() {        object = new SceneObject(0, 0, 0);        vertex0 = new Vertex(-10, 0, 0);        vertex1 = new Vertex(10, 0, 0);        object.add(vertex0);        object.add(vertex1);    }    @Test    public void testRotateOnXAxis() throws Exception {        object.rotate(CENTER, 1, 0, 0);//rotate 1 degree on x        System.out.println(vertex0);        System.out.println(vertex1);        assertXYZ(vertex0, -10, 0, 0);//0        assertXYZ(vertex1, 10, 0, 0);//0    }    @Test    public void testRotateOnZAxis() throws Exception {        object.rotate(CENTER, 0, 0, 1);//rotate 1 degree on z        System.out.println(vertex0);        System.out.println(vertex1);        //x' = x * cos(1); (-10 * 0.99984769515639123915701155881391), (10 * 0.99984769515639123915701155881391)        //y' = y * sin(1); (-10 * 0.01745240643728351281941897851632), (10 * 0.01745240643728351281941897851632)        assertXYZ(vertex0,-9.9984769515639123915701155881391,-0.1745240643728351281941897851632, 0);        assertXYZ(vertex1, 9.9984769515639123915701155881391, 0.1745240643728351281941897851632, 0);    }    private void assertXYZ(Vertex vertex, double x, double y, double z) {        assertEquals(vertex.x, x, String.format(expected Vertex to have x coordinate of %f but found %f\r\nActual:%s, x, vertex.x, vertex));        assertEquals(vertex.y, y, String.format(expected Vertex to have y coordinate of %f but found %f\r\nActual:%s, y, vertex.y, vertex));        assertEquals(vertex.z, z, String.format(expected Vertex to have z coordinate of %f but found %f\r\nActual:%s, z, vertex.z, vertex));    }}That test fails[Vertex (-5.403023058681398, 8.414709848078965, 0.0)][Vertex (5.403023058681398, -8.414709848078965, 0.0)]java.lang.AssertionError: expected Vertex to have x coordinate of -9.998477 but found -5.403023Actual:[Vertex (-5.403023058681398, 8.414709848078965, 0.0)] Expected :-9.998476951563912Actual   :-5.403023058681398Now it is possible that I'm asserting incorrectly, but that is what the tests do. They show us what you expect to happen given certain circumstances. Hope this helps some.