Code refactoring.
authorSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Mon, 11 Jul 2022 19:14:30 +0000 (22:14 +0300)
committerSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Mon, 11 Jul 2022 19:14:30 +0000 (22:14 +0300)
src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/ApplicationListPanel.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/Main.java
src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/MenuPanel.java [deleted file]
src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Cell.java [deleted file]
src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Main.java [deleted file]
src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Matrix.java [deleted file]
src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Star.java [deleted file]
src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Cell.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Main.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Matrix.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Star.java [new file with mode: 0644]

diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/ApplicationListPanel.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/ApplicationListPanel.java
new file mode 100644 (file)
index 0000000..2b3b643
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko. 
+ * This project is released under Creative Commons Zero (CC0) license.
+ *
+*/
+
+package eu.svjatoslav.sixth.e3d.examples.launcher;
+
+import eu.svjatoslav.sixth.e3d.examples.*;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+
+class ApplicationListPanel extends JPanel {
+    private static final long serialVersionUID = 2012721856427052560L;
+
+    ApplicationListPanel() {
+        final GroupLayout groupLayout = new GroupLayout(this);
+        GroupLayout.SequentialGroup sequentialGroup = groupLayout.createSequentialGroup();
+        sequentialGroup.addComponent(new JLabel("Choose an example to launch:"));
+        sequentialGroup.addComponent(new JButton(new ShowOctree()));
+        sequentialGroup.addComponent(new JButton(new ShowMathGraphs()));
+        sequentialGroup.addComponent(new JButton(new ShowPointCloud()));
+        sequentialGroup.addComponent(new JButton(new ShowRain()));
+        sequentialGroup.addComponent(new JButton(new ShowTextEditors()));
+        sequentialGroup.addComponent(new JButton(new ShowTextEditors2()));
+        sequentialGroup.addComponent(new JButton(new ShowGameOfLife()));
+        sequentialGroup.addComponent(new JButton(new ShowRandomPolygons()));
+    }
+
+    private static class ShowTextEditors extends AbstractAction {
+        ShowTextEditors() {
+            putValue(NAME, "Text editors");
+        }
+
+        @Override
+        public void actionPerformed(final ActionEvent e) {
+            TextEditorDemo.main(null);
+        }
+    }
+
+    private static class ShowTextEditors2 extends AbstractAction {
+        ShowTextEditors2() {
+            putValue(NAME, "Text editors city");
+        }
+
+        @Override
+        public void actionPerformed(final ActionEvent e) {
+            TextEditorDemo2.main(null);
+        }
+    }
+
+
+    private static class ShowRain extends AbstractAction {
+        ShowRain() {
+            putValue(NAME, "Raining numbers");
+        }
+
+        @Override
+        public void actionPerformed(final ActionEvent e) {
+            RainingNumbersDemo.main(null);
+        }
+    }
+
+    private static class ShowPointCloud extends AbstractAction {
+        ShowPointCloud() {
+            putValue(NAME, "Point cloud galaxy");
+        }
+
+        @Override
+        public void actionPerformed(final ActionEvent e) {
+            PointCloudDemo.main(null);
+        }
+    }
+
+    private static class ShowMathGraphs extends AbstractAction {
+        ShowMathGraphs() {
+            putValue(NAME, "Mathematical graphs");
+        }
+
+        @Override
+        public void actionPerformed(final ActionEvent e) {
+            GraphDemo.main(null);
+        }
+    }
+
+    private static class ShowRandomPolygons extends AbstractAction {
+        ShowRandomPolygons() {
+            putValue(NAME, "Random polygons");
+        }
+
+        @Override
+        public void actionPerformed(final ActionEvent e) {
+            RandomPolygonsDemo.main(null);
+        }
+    }
+
+    private static class ShowOctree extends AbstractAction {
+        ShowOctree() {
+            putValue(NAME, "Volumetric Octree");
+        }
+
+        @Override
+        public void actionPerformed(final ActionEvent e) {
+            OctreeDemo.main(null);
+        }
+    }
+
+    private static class ShowGameOfLife extends AbstractAction {
+        ShowGameOfLife() {
+            putValue(NAME, "Game of Life");
+        }
+
+        @Override
+        public void actionPerformed(final ActionEvent e) {
+            eu.svjatoslav.sixth.e3d.examples.life_demo.Main.main(null);
+        }
+    }
+
+}
index 4d090f8..07e5c39 100755 (executable)
@@ -9,29 +9,24 @@ package eu.svjatoslav.sixth.e3d.examples.launcher;
 import javax.swing.*;
 import java.awt.*;
 
-class Main extends javax.swing.JFrame {
-
-    private static final long serialVersionUID = -3679656169594556137L;
-
-    private Main() {
-        super();
-        initGUI();
-    }
+class Main {
 
     public static void main(final String[] args) {
-        SwingUtilities.invokeLater(() -> {
-            final Main inst = new Main();
-            final BorderLayout instLayout = new BorderLayout();
-            inst.setLocationRelativeTo(null);
-            inst.setVisible(true);
-            inst.getContentPane().setLayout(instLayout);
-        });
+        buildAndShowGuiWindow();
     }
 
-    private void initGUI() {
-        getContentPane().add(new MenuPanel());
-        pack();
-        setSize(390, 300);
+    private static void buildAndShowGuiWindow() {
+        JFrame frame = new JFrame("Sixth 3D engine demos");
+
+        // Keep application running until last frame is closed.
+        frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+
+        frame.getContentPane().setLayout(new BorderLayout());
+        frame.getContentPane().add(new ApplicationListPanel(), BorderLayout.CENTER);
+        frame.setSize(400, 300);
+
+        frame.setLocationRelativeTo(null); // center frame on screen
+        frame.setVisible(true);
     }
 
 }
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/MenuPanel.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/MenuPanel.java
deleted file mode 100644 (file)
index e620649..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Sixth 3D engine demos. Author: Svjatoslav Agejenko. 
- * This project is released under Creative Commons Zero (CC0) license.
- *
-*/
-
-package eu.svjatoslav.sixth.e3d.examples.launcher;
-
-import eu.svjatoslav.sixth.e3d.examples.*;
-
-import javax.swing.*;
-import java.awt.event.ActionEvent;
-
-class MenuPanel extends JPanel {
-    private static final long serialVersionUID = 2012721856427052560L;
-
-    MenuPanel() {
-        final GroupLayout groupLayout = new GroupLayout(this);
-        GroupLayout.SequentialGroup sequentialGroup = groupLayout.createSequentialGroup();
-        sequentialGroup.addComponent(new JLabel("Choose an example to launch:"));
-        sequentialGroup.addComponent(new JButton(new ShowOctree()));
-        sequentialGroup.addComponent(new JButton(new ShowMathGraphs()));
-        sequentialGroup.addComponent(new JButton(new ShowPointCloud()));
-        sequentialGroup.addComponent(new JButton(new ShowRain()));
-        sequentialGroup.addComponent(new JButton(new ShowTextEditors()));
-        sequentialGroup.addComponent(new JButton(new ShowTextEditors2()));
-        sequentialGroup.addComponent(new JButton(new ShowGameOfLife()));
-        sequentialGroup.addComponent(new JButton(new ShowRandomPolygons()));
-    }
-
-    private static class ShowTextEditors extends AbstractAction {
-        ShowTextEditors() {
-            putValue(NAME, "Text editors");
-        }
-
-        @Override
-        public void actionPerformed(final ActionEvent e) {
-            TextEditorDemo.main(null);
-        }
-    }
-
-    private static class ShowTextEditors2 extends AbstractAction {
-        ShowTextEditors2() {
-            putValue(NAME, "Text editors city");
-        }
-
-        @Override
-        public void actionPerformed(final ActionEvent e) {
-            TextEditorDemo2.main(null);
-        }
-    }
-
-
-    private static class ShowRain extends AbstractAction {
-        ShowRain() {
-            putValue(NAME, "Raining numbers");
-        }
-
-        @Override
-        public void actionPerformed(final ActionEvent e) {
-            RainingNumbersDemo.main(null);
-        }
-    }
-
-    private static class ShowPointCloud extends AbstractAction {
-        ShowPointCloud() {
-            putValue(NAME, "Pointcloud galaxy");
-        }
-
-        @Override
-        public void actionPerformed(final ActionEvent e) {
-            PointCloudDemo.main(null);
-        }
-    }
-
-    private static class ShowMathGraphs extends AbstractAction {
-        ShowMathGraphs() {
-            putValue(NAME, "Mathematical graphs");
-        }
-
-        @Override
-        public void actionPerformed(final ActionEvent e) {
-            GraphDemo.main(null);
-        }
-    }
-
-    private static class ShowRandomPolygons extends AbstractAction {
-        ShowRandomPolygons() {
-            putValue(NAME, "Random polygons");
-        }
-
-        @Override
-        public void actionPerformed(final ActionEvent e) {
-            RandomPolygonsDemo.main(null);
-        }
-    }
-
-    private static class ShowOctree extends AbstractAction {
-        ShowOctree() {
-            putValue(NAME, "Volumetric Octree");
-        }
-
-        @Override
-        public void actionPerformed(final ActionEvent e) {
-            OctreeDemo.main(null);
-        }
-    }
-
-    private static class ShowGameOfLife extends AbstractAction {
-        ShowGameOfLife() {
-            putValue(NAME, "Game of Life");
-        }
-
-        @Override
-        public void actionPerformed(final ActionEvent e) {
-            eu.svjatoslav.sixth.e3d.examples.life.Main.main(null);
-        }
-    }
-
-}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Cell.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Cell.java
deleted file mode 100755 (executable)
index fb78e4b..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-package eu.svjatoslav.sixth.e3d.examples.life;
-
-import eu.svjatoslav.sixth.e3d.geometry.Point3D;
-import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController;
-import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
-
-/**
- * This class corresponds to a single cell within matrix.
- */
-class Cell extends AbstractCompositeShape implements
-        MouseInteractionController {
-
-    /**
-     * cell visual size
-     */
-    static final int SIZE = 20;
-    /**
-     * Color of the active cell (R, G, B, A)
-     */
-    private static final Color ACTIVE_COLOR = new Color("A8FF");
-    /**
-     * Color of the active cell (R, G, B, A) while mouse is over it.
-     */
-    private static final Color ACTIVE_COLOR_MOUSE_OVER = new Color("F9FF");
-    /**
-     * Color of the inactive cell (R, G, B, A)
-     */
-    private static final Color INACTIVE_COLOR = new Color("55F8");
-    /**
-     * Color of the inactive cell (R, G, B, A) while mouse is over it.
-     */
-    private static final Color INACTIVE_COLOR_MOUSE_OVER = new Color("77F8");
-    /**
-     * A placeholder variable to help in next generation computation. Indicates
-     * whether cell is going to survive within next generation.
-     */
-    public boolean survives;
-    /**
-     * Indicates whether cell is currently active
-     */
-    private boolean active;
-    /**
-     * Indicates whether mouse pointer is currently over this cell.
-     */
-    private boolean isMouseOver = false;
-
-    public Cell(final Point3D center) {
-        super(center);
-
-        createCellShape();
-
-        // enable receiving of mouse events
-        setMouseInteractionController(this);
-    }
-
-    private void createCellShape() {
-        final double halfSize = SIZE / 2f;
-
-        // define 4 points corresponding to cell borders
-        final Point3D p1 = new Point3D(-halfSize, 0, -halfSize);
-
-        final Point3D p2 = new Point3D(+halfSize, 0, -halfSize);
-
-        final Point3D p3 = new Point3D(+halfSize, 0, +halfSize);
-
-        final Point3D p4 = new Point3D(-halfSize, 0, +halfSize);
-
-        // connect 4 points with 2 polygons
-        addShape(new SolidPolygon(p1, p2, p3, computeCellColor()));
-        addShape(new SolidPolygon(p1, p4, p3, computeCellColor()));
-    }
-
-    /**
-     * Compute cell color depending if cell is active and if mouse is over the
-     * cell.
-     */
-    private Color computeCellColor() {
-        if (active)
-            if (isMouseOver)
-                return ACTIVE_COLOR_MOUSE_OVER;
-            else
-                return ACTIVE_COLOR;
-        else if (isMouseOver)
-            return INACTIVE_COLOR_MOUSE_OVER;
-        else
-            return INACTIVE_COLOR;
-    }
-
-    public boolean isActive() {
-        return active;
-    }
-
-    public void setActive(final boolean active) {
-        this.active = active;
-        updateColor();
-    }
-
-    @Override
-    public boolean mouseClicked() {
-        setActive(!isActive());
-        return true;
-    }
-
-    @Override
-    public boolean mouseEntered() {
-        setMouseOver(true);
-        return true;
-    }
-
-    @Override
-    public boolean mouseExited() {
-        setMouseOver(false);
-        return true;
-    }
-
-    private void setMouseOver(final boolean isMouseOver) {
-        this.isMouseOver = isMouseOver;
-        updateColor();
-    }
-
-    /**
-     * This method is called when cell status is changed to update its color
-     * too.
-     */
-    private void updateColor() {
-        setColor(computeCellColor());
-    }
-
-}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Main.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Main.java
deleted file mode 100644 (file)
index afc0e1c..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-package eu.svjatoslav.sixth.e3d.examples.life;
-
-import eu.svjatoslav.sixth.e3d.geometry.Point3D;
-import eu.svjatoslav.sixth.e3d.geometry.Rectangle;
-import eu.svjatoslav.sixth.e3d.gui.Avatar;
-import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
-import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
-import eu.svjatoslav.sixth.e3d.gui.humaninput.WorldNavigationUserInputTracker;
-import eu.svjatoslav.sixth.e3d.math.Transform;
-import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
-import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid2D;
-
-import java.awt.event.KeyEvent;
-
-
-public class Main extends WorldNavigationUserInputTracker {
-
-    private static final Matrix MATRIX = new Matrix(
-            new Point3D() // position matrix in the center of the scene
-    );
-
-    public static void main(final String[] args) {
-        new Main().run();
-    }
-
-    /**
-     * Handle keyboard input.
-     */
-    @Override
-    public boolean keyPressed(final KeyEvent event, final ViewPanel viewPanel) {
-        switch (event.getKeyChar()) {
-            case ' ': // space key
-                MATRIX.evolve(false);
-                break;
-            case 10: // ENTER
-                MATRIX.evolve(true);
-                break;
-            case 'c': // reset matrix
-                MATRIX.clear();
-                break;
-            default:
-                return super.keyPressed(event, viewPanel);
-        }
-        return true;
-    }
-
-    private void run() {
-
-        // create application frame visible to the user
-        final ViewFrame viewFrame = new ViewFrame();
-
-        final ShapeCollection shapeCollection = viewFrame.getViewPanel()
-                .getRootShapeCollection();
-
-        // add matrix
-        shapeCollection.addShape(MATRIX);
-
-        // add wire-frame grid (optional)
-        shapeCollection.addShape(createGrid());
-
-        final ViewPanel viewPanel = viewFrame.getViewPanel();
-
-        setAvatarOrientation(viewPanel.getAvatar());
-
-        // enable receiving of keyboard events
-        viewPanel.getKeyboardFocusStack().pushFocusOwner(this);
-
-        // Done! World is built. So ensure screen is updated too.
-        viewPanel.repaintDuringNextViewUpdate();
-    }
-
-    /**
-     * Create pink wire-frame grid below (for decorative purposes).
-     */
-    private Grid2D createGrid() {
-        return new Grid2D(
-                new Transform(
-                        new Point3D( // Grid positioning:
-                                0, // center
-                                100, // below the main scene
-                                0), // center
-
-                        // Grid orientation:
-                        0, // no rotation along XZ axis
-                        Math.PI / 2), // face down
-
-                new Rectangle(800), // large enough, square grid
-
-                5, 5, // grid will be divided to 5x5 segments
-
-                new LineAppearance(3, // line thickness
-                        new Color("FF000050") // red and quite transparent
-                )
-        );
-    }
-
-    /**
-     * Set Avatar/Camera initial position in the world.
-     */
-    private void setAvatarOrientation(final Avatar avatar) {
-        avatar.setLocation(new Point3D(100, -50, -200));
-        avatar.setAngleXZ(0.2f);
-        avatar.setAngleYZ(-0.7f);
-    }
-
-}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Matrix.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Matrix.java
deleted file mode 100644 (file)
index ee7b0aa..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-package eu.svjatoslav.sixth.e3d.examples.life;
-
-import eu.svjatoslav.sixth.e3d.geometry.Point3D;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.SubShape;
-
-/**
- * This is our 2D game of life world. It contains 2D array of {@link Cell}'s.
- */
-class Matrix extends AbstractCompositeShape {
-
-    /**
-     * Empty space between cells.
-     */
-    private static final int BORDER = 5;
-
-    /**
-     * Matrix size X and Y. (Amount of cells.)
-     */
-    private static final int SIZE = 30;
-
-    /**
-     * Object marker in 3D scene. Allows to locate all stars from the scene.
-     */
-    private static final String GROUP_STARS = "stars";
-
-    /**
-     * Object marker in 3D scene. Allows to locate surface on the scene.
-     */
-    private static final String GROUP_SURFACE = "surface";
-
-    /**
-     * 2 dimensional matrix of cells
-     */
-    private final Cell[][] cells = new Cell[SIZE][];
-
-    public Matrix(final Point3D location) {
-        super(location);
-
-
-        for (int x = 0; x < SIZE; x++) {
-            cells[x] = new Cell[SIZE];
-
-            // init Y row
-            for (int z = 0; z < SIZE; z++) {
-                // create cell and register it
-                final Cell cell = new Cell(getCellLocation(x, z));
-                cells[x][z] = cell;
-                addShape(cell);
-            }
-        }
-
-        setGroupForUngrouped(GROUP_SURFACE);
-    }
-
-    /**
-     * Clear matrix.
-     */
-    public void clear() {
-
-        // mark every cell as inactive
-        for (int x = 0; x < SIZE; x++)
-            for (int y = 0; y < SIZE; y++)
-                cells[x][y].setActive(false);
-
-        // remove history stars
-        removeGroup(GROUP_STARS);
-    }
-
-    /**
-     * Compute survived cells based on the rules of Conway's Game of Life.
-     */
-    private void computeSurvivedCells() {
-
-        for (int y = 0; y < SIZE; y++)
-            for (int x = 0; x < SIZE; x++)
-                processCell(x, y);
-
-        for (int y = 0; y < SIZE; y++)
-            for (int x = 0; x < SIZE; x++)
-                cells[x][y].setActive(cells[x][y].survives);
-
-    }
-
-    private void processCell(int x, int y) {
-        int aliveNeighbours = countNeighbours(x, y);
-
-        if (cells[x][y].isActive()) {
-            cells[x][y].survives = ((aliveNeighbours == 2) || (aliveNeighbours == 3));
-        } else {
-            cells[x][y].survives = aliveNeighbours == 3;
-        }
-    }
-
-    private int countNeighbours(int x, int y) {
-        int result = 0;
-        for (int ny = y - 1; ny <= y + 1; ny++)
-            for (int nx = x - 1; nx <= x + 1; nx++)
-                if (isCellAlive(nx, ny)) result++;
-
-        if (isCellAlive(x, y)) result--;
-
-        return result;
-    }
-
-    private boolean isCellAlive(int x, int y) {
-        if (x < 0) return false;
-        if (x >= SIZE) return false;
-        if (y < 0) return false;
-        if (y >= SIZE) return false;
-        return cells[x][y].isActive();
-    }
-
-    /**
-     * Evolve matrix for one iteration
-     */
-    public void evolve(final boolean preserveHistory) {
-        if (preserveHistory)
-            markActiveCells();
-
-        shiftStarsUp();
-
-        computeSurvivedCells();
-    }
-
-    private Point3D getCellLocation(final int x, final int z) {
-        final int shift = -((SIZE / 2) * (Cell.SIZE + BORDER));
-
-        return new Point3D(
-                (x * (Cell.SIZE + BORDER)) + shift,
-                0,
-                (z * (Cell.SIZE + BORDER)) + shift);
-    }
-
-    /**
-     * Leave trail of active cells as stars above matrix.
-     */
-    private void markActiveCells() {
-        // mark survived cells
-        for (int x = 0; x < SIZE; x++)
-            for (int y = 0; y < SIZE; y++)
-                if (cells[x][y].isActive())
-                    addShape(new Star(getCellLocation(x, y)));
-
-        setGroupForUngrouped(GROUP_STARS);
-    }
-
-    /**
-     * Find all history tracking stars and shift them up.
-     */
-    private void shiftStarsUp() {
-
-        for (final SubShape subShape : getGroup(GROUP_STARS))
-            ((Star) subShape.getShape()).getLocation().translateY(-10);
-    }
-}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Star.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Star.java
deleted file mode 100644 (file)
index ad242d0..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-package eu.svjatoslav.sixth.e3d.examples.life;
-
-import eu.svjatoslav.sixth.e3d.geometry.Point3D;
-import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.GlowingPoint;
-
-import java.util.ArrayList;
-import java.util.List;
-
-class Star extends GlowingPoint {
-    public static final int STAR_SIZE = 10;
-
-    public static final int UNIQUE_STARS_COUNT = 30;
-
-    private static final List<Color> uniqueStarColors = new ArrayList<>();
-
-    /**
-     * A little hack to save RAM. We are going to have potentially lot of stars.
-     * Instead of creating new individual texture for each star, Sixth 3D engine
-     * uses internal optimization and reuses existing star textures, if star with
-     * identical color already exists. To take advantage ot such optimization
-     * we create here limited set of precomputed star colors and later reuse them.
-     */
-    static {
-        for (int i = 0; i < UNIQUE_STARS_COUNT; i++)
-            uniqueStarColors.add(
-                    new Color(
-                            Math.random() + 0.5,
-                            Math.random() + 0.5,
-                            Math.random() + 0.5,
-                            255));
-    }
-
-    public Star(Point3D location) {
-        super(location,
-                STAR_SIZE,
-                uniqueStarColors.get((int) (Math.random() * uniqueStarColors.size())) // pick random pre-generated color
-        );
-    }
-
-}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Cell.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Cell.java
new file mode 100755 (executable)
index 0000000..6b1ee8e
--- /dev/null
@@ -0,0 +1,131 @@
+package eu.svjatoslav.sixth.e3d.examples.life_demo;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
+
+/**
+ * This class corresponds to a single cell within matrix.
+ */
+class Cell extends AbstractCompositeShape implements
+        MouseInteractionController {
+
+    /**
+     * cell visual size
+     */
+    static final int SIZE = 20;
+    /**
+     * Color of the active cell (R, G, B, A)
+     */
+    private static final Color ACTIVE_COLOR = new Color("A8FF");
+    /**
+     * Color of the active cell (R, G, B, A) while mouse is over it.
+     */
+    private static final Color ACTIVE_COLOR_MOUSE_OVER = new Color("F9FF");
+    /**
+     * Color of the inactive cell (R, G, B, A)
+     */
+    private static final Color INACTIVE_COLOR = new Color("55F8");
+    /**
+     * Color of the inactive cell (R, G, B, A) while mouse is over it.
+     */
+    private static final Color INACTIVE_COLOR_MOUSE_OVER = new Color("77F8");
+    /**
+     * A placeholder variable to help in next generation computation. Indicates
+     * whether cell is going to survive within next generation.
+     */
+    public boolean survives;
+    /**
+     * Indicates whether cell is currently active
+     */
+    private boolean active;
+    /**
+     * Indicates whether mouse pointer is currently over this cell.
+     */
+    private boolean isMouseOver = false;
+
+    public Cell(final Point3D center) {
+        super(center);
+
+        createCellShape();
+
+        // enable receiving of mouse events
+        setMouseInteractionController(this);
+    }
+
+    private void createCellShape() {
+        final double halfSize = SIZE / 2f;
+
+        // define 4 points corresponding to cell borders
+        final Point3D p1 = new Point3D(-halfSize, 0, -halfSize);
+
+        final Point3D p2 = new Point3D(+halfSize, 0, -halfSize);
+
+        final Point3D p3 = new Point3D(+halfSize, 0, +halfSize);
+
+        final Point3D p4 = new Point3D(-halfSize, 0, +halfSize);
+
+        // connect 4 points with 2 polygons
+        addShape(new SolidPolygon(p1, p2, p3, computeCellColor()));
+        addShape(new SolidPolygon(p1, p4, p3, computeCellColor()));
+    }
+
+    /**
+     * Compute cell color depending if cell is active and if mouse is over the
+     * cell.
+     */
+    private Color computeCellColor() {
+        if (active)
+            if (isMouseOver)
+                return ACTIVE_COLOR_MOUSE_OVER;
+            else
+                return ACTIVE_COLOR;
+        else if (isMouseOver)
+            return INACTIVE_COLOR_MOUSE_OVER;
+        else
+            return INACTIVE_COLOR;
+    }
+
+    public boolean isActive() {
+        return active;
+    }
+
+    public void setActive(final boolean active) {
+        this.active = active;
+        updateColor();
+    }
+
+    @Override
+    public boolean mouseClicked() {
+        setActive(!isActive());
+        return true;
+    }
+
+    @Override
+    public boolean mouseEntered() {
+        setMouseOver(true);
+        return true;
+    }
+
+    @Override
+    public boolean mouseExited() {
+        setMouseOver(false);
+        return true;
+    }
+
+    private void setMouseOver(final boolean isMouseOver) {
+        this.isMouseOver = isMouseOver;
+        updateColor();
+    }
+
+    /**
+     * This method is called when cell status is changed to update its color
+     * too.
+     */
+    private void updateColor() {
+        setColor(computeCellColor());
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Main.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Main.java
new file mode 100644 (file)
index 0000000..cf47e7f
--- /dev/null
@@ -0,0 +1,108 @@
+package eu.svjatoslav.sixth.e3d.examples.life_demo;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.geometry.Rectangle;
+import eu.svjatoslav.sixth.e3d.gui.Avatar;
+import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
+import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
+import eu.svjatoslav.sixth.e3d.gui.humaninput.WorldNavigationUserInputTracker;
+import eu.svjatoslav.sixth.e3d.math.Transform;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid2D;
+
+import java.awt.event.KeyEvent;
+
+
+public class Main extends WorldNavigationUserInputTracker {
+
+    private static final Matrix MATRIX = new Matrix(
+            new Point3D() // position matrix in the center of the scene
+    );
+
+    public static void main(final String[] args) {
+        new Main().run();
+    }
+
+    /**
+     * Handle keyboard input.
+     */
+    @Override
+    public boolean keyPressed(final KeyEvent event, final ViewPanel viewPanel) {
+        switch (event.getKeyChar()) {
+            case ' ': // space key
+                MATRIX.evolve(false);
+                break;
+            case 10: // ENTER
+                MATRIX.evolve(true);
+                break;
+            case 'c': // reset matrix
+                MATRIX.clear();
+                break;
+            default:
+                return super.keyPressed(event, viewPanel);
+        }
+        return true;
+    }
+
+    private void run() {
+
+        // create application frame visible to the user
+        final ViewFrame viewFrame = new ViewFrame();
+
+        final ShapeCollection shapeCollection = viewFrame.getViewPanel()
+                .getRootShapeCollection();
+
+        // add matrix
+        shapeCollection.addShape(MATRIX);
+
+        // add wire-frame grid (optional)
+        shapeCollection.addShape(createGrid());
+
+        final ViewPanel viewPanel = viewFrame.getViewPanel();
+
+        setAvatarOrientation(viewPanel.getAvatar());
+
+        // enable receiving of keyboard events
+        viewPanel.getKeyboardFocusStack().pushFocusOwner(this);
+
+        // Done! World is built. So ensure screen is updated too.
+        viewPanel.repaintDuringNextViewUpdate();
+    }
+
+    /**
+     * Create pink wire-frame grid below (for decorative purposes).
+     */
+    private Grid2D createGrid() {
+        return new Grid2D(
+                new Transform(
+                        new Point3D( // Grid positioning:
+                                0, // center
+                                100, // below the main scene
+                                0), // center
+
+                        // Grid orientation:
+                        0, // no rotation along XZ axis
+                        Math.PI / 2), // face down
+
+                new Rectangle(800), // large enough, square grid
+
+                5, 5, // grid will be divided to 5x5 segments
+
+                new LineAppearance(3, // line thickness
+                        new Color("FF000050") // red and quite transparent
+                )
+        );
+    }
+
+    /**
+     * Set Avatar/Camera initial position in the world.
+     */
+    private void setAvatarOrientation(final Avatar avatar) {
+        avatar.setLocation(new Point3D(100, -50, -200));
+        avatar.setAngleXZ(0.2f);
+        avatar.setAngleYZ(-0.7f);
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Matrix.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Matrix.java
new file mode 100644 (file)
index 0000000..8736b96
--- /dev/null
@@ -0,0 +1,156 @@
+package eu.svjatoslav.sixth.e3d.examples.life_demo;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.SubShape;
+
+/**
+ * This is our 2D game of life world. It contains 2D array of {@link Cell}'s.
+ */
+class Matrix extends AbstractCompositeShape {
+
+    /**
+     * Empty space between cells.
+     */
+    private static final int BORDER = 5;
+
+    /**
+     * Matrix size X and Y. (Amount of cells.)
+     */
+    private static final int SIZE = 30;
+
+    /**
+     * Object marker in 3D scene. Allows to locate all stars from the scene.
+     */
+    private static final String GROUP_STARS = "stars";
+
+    /**
+     * Object marker in 3D scene. Allows to locate surface on the scene.
+     */
+    private static final String GROUP_SURFACE = "surface";
+
+    /**
+     * 2 dimensional matrix of cells
+     */
+    private final Cell[][] cells = new Cell[SIZE][];
+
+    public Matrix(final Point3D location) {
+        super(location);
+
+
+        for (int x = 0; x < SIZE; x++) {
+            cells[x] = new Cell[SIZE];
+
+            // init Y row
+            for (int z = 0; z < SIZE; z++) {
+                // create cell and register it
+                final Cell cell = new Cell(getCellLocation(x, z));
+                cells[x][z] = cell;
+                addShape(cell);
+            }
+        }
+
+        setGroupForUngrouped(GROUP_SURFACE);
+    }
+
+    /**
+     * Clear matrix.
+     */
+    public void clear() {
+
+        // mark every cell as inactive
+        for (int x = 0; x < SIZE; x++)
+            for (int y = 0; y < SIZE; y++)
+                cells[x][y].setActive(false);
+
+        // remove history stars
+        removeGroup(GROUP_STARS);
+    }
+
+    /**
+     * Compute survived cells based on the rules of Conway's Game of Life.
+     */
+    private void computeSurvivedCells() {
+
+        for (int y = 0; y < SIZE; y++)
+            for (int x = 0; x < SIZE; x++)
+                processCell(x, y);
+
+        for (int y = 0; y < SIZE; y++)
+            for (int x = 0; x < SIZE; x++)
+                cells[x][y].setActive(cells[x][y].survives);
+
+    }
+
+    private void processCell(int x, int y) {
+        int aliveNeighbours = countNeighbours(x, y);
+
+        if (cells[x][y].isActive()) {
+            cells[x][y].survives = ((aliveNeighbours == 2) || (aliveNeighbours == 3));
+        } else {
+            cells[x][y].survives = aliveNeighbours == 3;
+        }
+    }
+
+    private int countNeighbours(int x, int y) {
+        int result = 0;
+        for (int ny = y - 1; ny <= y + 1; ny++)
+            for (int nx = x - 1; nx <= x + 1; nx++)
+                if (isCellAlive(nx, ny)) result++;
+
+        if (isCellAlive(x, y)) result--;
+
+        return result;
+    }
+
+    private boolean isCellAlive(int x, int y) {
+        if (x < 0) return false;
+        if (x >= SIZE) return false;
+        if (y < 0) return false;
+        if (y >= SIZE) return false;
+        return cells[x][y].isActive();
+    }
+
+    /**
+     * Evolve matrix for one iteration
+     */
+    public void evolve(final boolean preserveHistory) {
+        if (preserveHistory)
+            markActiveCells();
+
+        shiftStarsUp();
+
+        computeSurvivedCells();
+    }
+
+    private Point3D getCellLocation(final int x, final int z) {
+        final int shift = -((SIZE / 2) * (Cell.SIZE + BORDER));
+
+        return new Point3D(
+                (x * (Cell.SIZE + BORDER)) + shift,
+                0,
+                (z * (Cell.SIZE + BORDER)) + shift);
+    }
+
+    /**
+     * Leave trail of active cells as stars above matrix.
+     */
+    private void markActiveCells() {
+        // mark survived cells
+        for (int x = 0; x < SIZE; x++)
+            for (int y = 0; y < SIZE; y++)
+                if (cells[x][y].isActive())
+                    addShape(new Star(getCellLocation(x, y)));
+
+        setGroupForUngrouped(GROUP_STARS);
+    }
+
+    /**
+     * Find all history tracking stars and shift them up.
+     */
+    private void shiftStarsUp() {
+
+        for (final SubShape subShape : getGroup(GROUP_STARS))
+            ((Star) subShape.getShape()).getLocation().translateY(-10);
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Star.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Star.java
new file mode 100644 (file)
index 0000000..6f5edaa
--- /dev/null
@@ -0,0 +1,41 @@
+package eu.svjatoslav.sixth.e3d.examples.life_demo;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.GlowingPoint;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class Star extends GlowingPoint {
+    public static final int STAR_SIZE = 10;
+
+    public static final int UNIQUE_STARS_COUNT = 30;
+
+    private static final List<Color> uniqueStarColors = new ArrayList<>();
+
+    /*
+     * A little hack to save RAM. We are going to have potentially lot of stars.
+     * Instead of creating new individual texture for each star, Sixth 3D engine
+     * uses internal optimization and reuses existing star textures, if star with
+     * identical color already exists. To take advantage ot such optimization
+     * we create here limited set of precomputed star colors and later reuse them.
+     */
+    static {
+        for (int i = 0; i < UNIQUE_STARS_COUNT; i++)
+            uniqueStarColors.add(
+                    new Color(
+                            Math.random() + 0.5,
+                            Math.random() + 0.5,
+                            Math.random() + 0.5,
+                            255));
+    }
+
+    public Star(Point3D location) {
+        super(location,
+                STAR_SIZE,
+                uniqueStarColors.get((int) (Math.random() * uniqueStarColors.size())) // pick random pre-generated color
+        );
+    }
+
+}