initial commit
authorSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Wed, 3 Aug 2016 20:21:40 +0000 (23:21 +0300)
committerSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Wed, 3 Aug 2016 20:21:40 +0000 (23:21 +0300)
93 files changed:
.gitignore [new file with mode: 0644]
doc/index.html [new file with mode: 0644]
doc/index.org [new file with mode: 0644]
pom.xml [new file with mode: 0644]
sixth-3d.iml [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/geometry/Box.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/geometry/Circle.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/geometry/GeometryCoordinate.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/geometry/Orientation.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/geometry/Point2D.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/geometry/Point3D.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/geometry/Polygon.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/geometry/Rectangle.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/geometry/Transform.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/geometry/TransformPipe.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/gui/Avatar.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/gui/GuiComponent.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/gui/RenderingContext.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/gui/TextPointer.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/gui/UserRelativityTracker.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/gui/View.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewContext.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewFrame.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewListener.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewUpdateListener.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewUpdateTimerTask.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/KeyboardFocusTracker.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/MouseClick.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/MouseInteractionController.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/UserInputHandler.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/UserInputTracker.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/WorldNavigationTracker.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/Character.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/ColorConfig.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/KeyboardHelper.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/Page.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/TextEditComponent.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/TextLine.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/io/Connexion3D.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/OctreeVolume.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/package-info.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/Camera.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/CameraView.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/LightSource.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/Ray.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/RayHit.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/RayTracer.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/package-info.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/package-info.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/Color.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/RenderAggregator.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/ShapeCollection.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/package-info.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/AbstractCoordinateShape.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/AbstractShape.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/ForwardOrientedTexture.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/GlowingPoint.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/Line.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/LineAppearance.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/LineInterpolator.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/solidpolygon/LineInterpolator.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/solidpolygon/SolidPolygon.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/texturedpolygon/PolygonBorderInterpolator.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/texturedpolygon/TexturedPolygon.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/ForwardOrientedTextBlock.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/Galaxy.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/Graph.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/LightSourceMarker.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/TexturedRectangle.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/base/AbstractCompositeShape.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/base/SubShape.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/solid/SolidPolygonCube.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/solid/SolidPolygonRectangularBox.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/textcanvas/CanvasCharacter.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/textcanvas/RenderMode.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/textcanvas/TextCanvas.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/Grid2D.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/Grid3D.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeBox.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeCube.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeDrawing.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeSphere.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/slicer/BorderLine.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/slicer/PolygonCoordinate.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/slicer/Slicer.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/Texture.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/TextureBitmap.java [new file with mode: 0644]
src/main/resources/eu/svjatoslav/sixth/e3d/examples/hourglass.png [new file with mode: 0644]
src/main/resources/rebel.xml [new file with mode: 0644]
src/test/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/TextLineTest.java [new file with mode: 0644]
tools/open with IntelliJ IDEA [new file with mode: 0755]
tools/open with git-cola [new file with mode: 0755]
tools/update web site [new file with mode: 0755]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..d1f99ea
--- /dev/null
@@ -0,0 +1,3 @@
+# Created by .ignore support plugin (hsz.mobi)
+/.idea/
+/target/
\ No newline at end of file
diff --git a/doc/index.html b/doc/index.html
new file mode 100644 (file)
index 0000000..c73dfce
--- /dev/null
@@ -0,0 +1,426 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+    <!-- 2016-08-03 Wed 23:15 -->
+    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
+    <meta name="viewport" content="width=device-width, initial-scale=1"/>
+    <title>Sixth 3D - 3D engine</title>
+    <meta name="generator" content="Org-mode"/>
+    <meta name="author" content="Svjatoslav Agejenko"/>
+    <style type="text/css">
+        <!-- /*--><![CDATA[/*><!--*/
+        .title {
+            text-align: center;
+            margin-bottom: .2em;
+        }
+
+        .subtitle {
+            text-align: center;
+            font-size: medium;
+            font-weight: bold;
+            margin-top: 0;
+        }
+
+        .todo {
+            font-family: monospace;
+            color: red;
+        }
+
+        .done {
+            font-family: monospace;
+            color: green;
+        }
+
+        .priority {
+            font-family: monospace;
+            color: orange;
+        }
+
+        .tag {
+            background-color: #eee;
+            font-family: monospace;
+            padding: 2px;
+            font-size: 80%;
+            font-weight: normal;
+        }
+
+        .timestamp {
+            color: #bebebe;
+        }
+
+        .timestamp-kwd {
+            color: #5f9ea0;
+        }
+
+        .org-right {
+            margin-left: auto;
+            margin-right: 0px;
+            text-align: right;
+        }
+
+        .org-left {
+            margin-left: 0px;
+            margin-right: auto;
+            text-align: left;
+        }
+
+        .org-center {
+            margin-left: auto;
+            margin-right: auto;
+            text-align: center;
+        }
+
+        .underline {
+            text-decoration: underline;
+        }
+
+        #postamble p, #preamble p {
+            font-size: 90%;
+            margin: .2em;
+        }
+
+        p.verse {
+            margin-left: 3%;
+        }
+
+        pre {
+            border: 1px solid #ccc;
+            box-shadow: 3px 3px 3px #eee;
+            padding: 8pt;
+            font-family: monospace;
+            overflow: auto;
+            margin: 1.2em;
+        }
+
+        pre.src {
+            position: relative;
+            overflow: visible;
+            padding-top: 1.2em;
+        }
+
+        pre.src:before {
+            display: none;
+            position: absolute;
+            background-color: white;
+            top: -10px;
+            right: 10px;
+            padding: 3px;
+            border: 1px solid black;
+        }
+
+        pre.src:hover:before {
+            display: inline;
+        }
+
+        pre.src-sh:before {
+            content: 'sh';
+        }
+
+        pre.src-bash:before {
+            content: 'sh';
+        }
+
+        pre.src-emacs-lisp:before {
+            content: 'Emacs Lisp';
+        }
+
+        pre.src-R:before {
+            content: 'R';
+        }
+
+        pre.src-perl:before {
+            content: 'Perl';
+        }
+
+        pre.src-java:before {
+            content: 'Java';
+        }
+
+        pre.src-sql:before {
+            content: 'SQL';
+        }
+
+        table {
+            border-collapse: collapse;
+        }
+
+        caption.t-above {
+            caption-side: top;
+        }
+
+        caption.t-bottom {
+            caption-side: bottom;
+        }
+
+        td, th {
+            vertical-align: top;
+        }
+
+        th.org-right {
+            text-align: center;
+        }
+
+        th.org-left {
+            text-align: center;
+        }
+
+        th.org-center {
+            text-align: center;
+        }
+
+        td.org-right {
+            text-align: right;
+        }
+
+        td.org-left {
+            text-align: left;
+        }
+
+        td.org-center {
+            text-align: center;
+        }
+
+        dt {
+            font-weight: bold;
+        }
+
+        .footpara {
+            display: inline;
+        }
+
+        .footdef {
+            margin-bottom: 1em;
+        }
+
+        .figure {
+            padding: 1em;
+        }
+
+        .figure p {
+            text-align: center;
+        }
+
+        .inlinetask {
+            padding: 10px;
+            border: 2px solid gray;
+            margin: 10px;
+            background: #ffffcc;
+        }
+
+        #org-div-home-and-up {
+            text-align: right;
+            font-size: 70%;
+            white-space: nowrap;
+        }
+
+        textarea {
+            overflow-x: auto;
+        }
+
+        .linenr {
+            font-size: smaller
+        }
+
+        .code-highlighted {
+            background-color: #ffff00;
+        }
+
+        .org-info-js_info-navigation {
+            border-style: none;
+        }
+
+        #org-info-js_console-label {
+            font-size: 10px;
+            font-weight: bold;
+            white-space: nowrap;
+        }
+
+        .org-info-js_search-highlight {
+            background-color: #ffff00;
+            color: #000000;
+            font-weight: bold;
+        }
+
+        /*]]>*/
+        -->
+    </style>
+    <link rel="stylesheet" type="text/css" href="http://thomasf.github.io/solarized-css/solarized-dark.min.css"/>
+    <script type="text/javascript">
+        /*
+         @licstart  The following is the entire license notice for the
+         JavaScript code in this tag.
+
+         Copyright (C) 2012-2013 Free Software Foundation, Inc.
+
+         The JavaScript code in this tag is free software: you can
+         redistribute it and/or modify it under the terms of the GNU
+         General Public License (GNU GPL) as published by the Free Software
+         Foundation, either version 3 of the License, or (at your option)
+         any later version.  The code is distributed WITHOUT ANY WARRANTY;
+         without even the implied warranty of MERCHANTABILITY or FITNESS
+         FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
+
+         As additional permission under GNU GPL version 3 section 7, you
+         may distribute non-source (e.g., minimized or compacted) forms of
+         that code without the copy of the GNU GPL normally required by
+         section 4, provided you include this license notice and a URL
+         through which recipients can access the Corresponding Source.
+
+
+         @licend  The above is the entire license notice
+         for the JavaScript code in this tag.
+         */
+        <!--/*--><![CDATA[/*><!--*/
+        function CodeHighlightOn(elem, id) {
+            var target = document.getElementById(id);
+            if (null != target) {
+                elem.cacheClassElem = elem.className;
+                elem.cacheClassTarget = target.className;
+                target.className = "code-highlighted";
+                elem.className = "code-highlighted";
+            }
+        }
+        function CodeHighlightOff(elem, id) {
+            var target = document.getElementById(id);
+            if (elem.cacheClassElem)
+                elem.className = elem.cacheClassElem;
+            if (elem.cacheClassTarget)
+                target.className = elem.cacheClassTarget;
+        }
+        /*]]>*///-->
+    </script>
+</head>
+<body>
+<div id="content">
+    <h1 class="title">Sixth 3D - 3D engine</h1>
+    <div id="table-of-contents">
+        <h2>Table of Contents</h2>
+        <div id="text-table-of-contents">
+            <ul>
+                <li><a href="#orgheadline1">1. Project description</a></li>
+                <li><a href="#orgheadline2">2. Software development</a></li>
+            </ul>
+        </div>
+    </div>
+    <hr/>
+    <ul class="org-ul">
+        <li>This is a subproject of <a href="http://www2.svjatoslav.eu/gitbrowse/sixth/doc/index.html">Sixth</a></li>
+
+        <li><a href="http://www2.svjatoslav.eu/gitweb/?p=sixth.git;a=snapshot;h=HEAD;sf=tgz">download latest
+            snapshot</a></li>
+
+        <li>This program is free software; you can redistribute it and/or modify it under
+            the terms of version 3 of the <a href="https://www.gnu.org/licenses/lgpl.html">GNU Lesser General Public
+                License</a> or later as
+            published by the Free Software Foundation.
+        </li>
+
+        <li>Program author:
+            <ul class="org-ul">
+                <li>Svjatoslav Agejenko</li>
+                <li>Homepage: <a href="http://svjatoslav.eu/">http://svjatoslav.eu/</a></li>
+                <li>Email: <a href="mailto://svjatoslav@svjatoslav.eu/">mailto://svjatoslav@svjatoslav.eu/</a></li>
+            </ul>
+        </li>
+
+        <li><a href="http://svjatoslav.eu/programs.jsp">other applications hosted at svjatoslav.eu</a></li>
+    </ul>
+
+
+    <ul class="org-ul">
+        <li>In software, pure Java realtime 3D rendering engine. With the final goal of
+            becoming a platform for buildng 3D user interfaces.
+        </li>
+    </ul>
+
+    <div id="outline-container-orgheadline1" class="outline-2">
+        <h2 id="orgheadline1"><span class="section-number-2">1</span> Project description</h2>
+        <div class="outline-text-2" id="text-1">
+            <p>
+                System is implemented in Java because:
+            </p>
+            <ul class="org-ul">
+                <li>It scales well to handle great complexity.</li>
+                <li>It is easy to refactor and experiment with.</li>
+                <li>It is fast enough thanks to Java virtual machine just-in-time compiler.</li>
+                <li>Easy to run on various hardware platforms and operating systems.</li>
+            </ul>
+
+            <p>
+                3D rendering is done in software, 100% pure Java on CPU. At least for now.
+                Modern CPU cores count keeps growing and therefore rendering by CPU is not as
+                expensive as it used to be for the old single core systems.
+            </p>
+
+            <p>
+                CPU rendering performance is already good enough to implement usable 3D UI at
+                sufficient detail level, resolution and frame rate.
+            </p>
+
+            <p>
+                Pure Java also means easy portability and installation. No need to deal with
+                platform specific dependencies.
+            </p>
+
+            <p>
+                Also CPU rendering allows to easily test different rendering algorithms and
+                retains complete control of every rendered pixel.
+            </p>
+        </div>
+    </div>
+
+
+    <div id="outline-container-orgheadline2" class="outline-2">
+        <h2 id="orgheadline2"><span class="section-number-2">2</span> Software development</h2>
+        <div class="outline-text-2" id="text-2">
+            <p>
+                Instructions to embed Sixth-3D in your project as a library. Maven *pom.xml*
+                file snippet:
+            </p>
+            <div class="org-src-container">
+
+<pre class="src src-xml">&lt;<span style="color: #bc6ec5; font-weight: bold;">dependencies</span>&gt;
+    ...
+    &lt;<span style="color: #bc6ec5; font-weight: bold;">dependency</span>&gt;
+        &lt;<span style="color: #bc6ec5; font-weight: bold;">groupId</span>&gt;eu.svjatoslav&lt;/<span
+            style="color: #bc6ec5; font-weight: bold;">groupId</span>&gt;
+        &lt;<span style="color: #bc6ec5; font-weight: bold;">artifactId</span>&gt;sixth-3d&lt;/<span
+            style="color: #bc6ec5; font-weight: bold;">artifactId</span>&gt;
+        &lt;<span style="color: #bc6ec5; font-weight: bold;">version</span>&gt;1.0&lt;/<span
+            style="color: #bc6ec5; font-weight: bold;">version</span>&gt;
+    &lt;/<span style="color: #bc6ec5; font-weight: bold;">dependency</span>&gt;
+    ...
+&lt;/<span style="color: #bc6ec5; font-weight: bold;">dependencies</span>&gt;
+
+&lt;<span style="color: #bc6ec5; font-weight: bold;">repositories</span>&gt;
+    ...
+    &lt;<span style="color: #bc6ec5; font-weight: bold;">repository</span>&gt;
+        &lt;<span style="color: #bc6ec5; font-weight: bold;">id</span>&gt;svjatoslav.eu&lt;/<span
+            style="color: #bc6ec5; font-weight: bold;">id</span>&gt;
+        &lt;<span style="color: #bc6ec5; font-weight: bold;">name</span>&gt;Svjatoslav repository&lt;/<span
+            style="color: #bc6ec5; font-weight: bold;">name</span>&gt;
+        &lt;<span style="color: #bc6ec5; font-weight: bold;">url</span>&gt;http://www2.svjatoslav.eu/maven/&lt;/<span
+            style="color: #bc6ec5; font-weight: bold;">url</span>&gt;
+    &lt;/<span style="color: #bc6ec5; font-weight: bold;">repository</span>&gt;
+    ...
+&lt;/<span style="color: #bc6ec5; font-weight: bold;">repositories</span>&gt;
+</pre>
+            </div>
+
+            <p>
+                <a href="http://www2.svjatoslav.eu/projects/sixth/codegraphs/">Auto-generated graphs for parts of
+                    Sixth-3D code/architecture</a> using <a
+                    href="http://www2.svjatoslav.eu/gitbrowse/javainspect/doc/index.html">this tool</a>
+            </p>
+        </div>
+    </div>
+</div>
+<div id="postamble" class="status">
+    <p class="author">Author: Svjatoslav Agejenko</p>
+    <p class="date">Created: 2016-08-03 Wed 23:15</p>
+    <p class="validation"><a href="http://validator.w3.org/check?uri=referer">Validate</a></p>
+</div>
+</body>
+</html>
diff --git a/doc/index.org b/doc/index.org
new file mode 100644 (file)
index 0000000..31a2be0
--- /dev/null
@@ -0,0 +1,71 @@
+#+TITLE: Sixth 3D - 3D engine
+
+-----
+- This is a subproject of [[http://www2.svjatoslav.eu/gitbrowse/sixth/doc/index.html][Sixth]]
+
+- [[http://www2.svjatoslav.eu/gitweb/?p=sixth.git;a=snapshot;h=HEAD;sf=tgz][download latest snapshot]]
+
+- This program is free software; you can redistribute it and/or modify it under
+  the terms of version 3 of the [[https://www.gnu.org/licenses/lgpl.html][GNU Lesser General Public License]] or later as
+  published by the Free Software Foundation.
+
+- Program author:
+  - Svjatoslav Agejenko
+  - Homepage: http://svjatoslav.eu
+  - Email: mailto://svjatoslav@svjatoslav.eu
+
+- [[http://svjatoslav.eu/programs.jsp][other applications hosted at svjatoslav.eu]]
+
+
+- In software, pure Java realtime 3D rendering engine. With the final goal of
+  becoming a platform for buildng 3D user interfaces.
+  
+* Project description
+System is implemented in Java because:
+- It scales well to handle great complexity.
+- It is easy to refactor and experiment with.
+- It is fast enough thanks to Java virtual machine just-in-time compiler.
+- Easy to run on various hardware platforms and operating systems.
+
+3D rendering is done in software, 100% pure Java on CPU. At least for now.
+Modern CPU cores count keeps growing and therefore rendering by CPU is not as
+expensive as it used to be for the old single core systems.
+
+CPU rendering performance is already good enough to implement usable 3D UI at
+sufficient detail level, resolution and frame rate.
+
+Pure Java also means easy portability and installation. No need to deal with
+platform specific dependencies.
+
+Also CPU rendering allows to easily test different rendering algorithms and
+retains complete control of every rendered pixel.
+
+
+* Software development
+Instructions to embed Sixth-3D in your project as a library. Maven *pom.xml*
+file snippet:
+#+BEGIN_SRC xml
+<dependencies>
+    ...
+    <dependency>
+        <groupId>eu.svjatoslav</groupId>
+        <artifactId>sixth-3d</artifactId>
+        <version>1.0</version>
+    </dependency>
+    ...
+</dependencies>
+
+<repositories>
+    ...
+    <repository>
+        <id>svjatoslav.eu</id>
+        <name>Svjatoslav repository</name>
+        <url>http://www2.svjatoslav.eu/maven/</url>
+    </repository>
+    ...
+</repositories>
+#+END_SRC
+
+[[http://www2.svjatoslav.eu/projects/sixth/codegraphs/][Auto-generated graphs for parts of Sixth-3D code/architecture]] using [[http://www2.svjatoslav.eu/gitbrowse/javainspect/doc/index.html][this tool]]
+
+
diff --git a/pom.xml b/pom.xml
new file mode 100644 (file)
index 0000000..87e3a1c
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,125 @@
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>eu.svjatoslav</groupId>
+    <artifactId>sixth-3d</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <name>Sixth 3D</name>
+    <description>3D engine</description>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    </properties>
+
+    <organization>
+        <name>svjatoslav.eu</name>
+        <url>http://svjatoslav.eu</url>
+    </organization>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.8.1</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>eu.svjatoslav</groupId>
+            <artifactId>javainspect</artifactId>
+            <version>1.5-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>2.3.2</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                    <optimize>true</optimize>
+                    <encoding>UTF-8</encoding>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <version>2.2.1</version>
+                <executions>
+                    <execution>
+                        <id>attach-sources</id>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>2.9</version>
+                <executions>
+                    <execution>
+                        <id>attach-javadocs</id>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-resources-plugin</artifactId>
+                <version>2.4.3</version>
+                <configuration>
+                    <encoding>UTF-8</encoding>
+                </configuration>
+            </plugin>
+
+        </plugins>
+
+        <extensions>
+            <extension>
+                <groupId>org.apache.maven.wagon</groupId>
+                <artifactId>wagon-ssh-external</artifactId>
+                <version>2.6</version>
+            </extension>
+        </extensions>
+    </build>
+
+
+    <distributionManagement>
+        <snapshotRepository>
+            <id>svjatoslav.eu</id>
+            <name>svjatoslav.eu</name>
+            <url>scpexe://svjatoslav.eu/var/www/svjatoslav.eu/maven</url>
+        </snapshotRepository>
+        <repository>
+            <id>svjatoslav.eu</id>
+            <name>svjatoslav.eu</name>
+            <url>scpexe://svjatoslav.eu/var/www/svjatoslav.eu/maven</url>
+        </repository>
+    </distributionManagement>
+
+    <repositories>
+        <repository>
+            <id>svjatoslav.eu</id>
+            <name>Svjatoslav repository</name>
+            <url>http://www2.svjatoslav.eu/maven/</url>
+        </repository>
+    </repositories>
+
+    <scm>
+        <connection>scm:git:ssh://git@svjatoslav.eu/home/git/repositories/sixth-3d.git</connection>
+        <developerConnection>scm:git:ssh://git@svjatoslav.eu/home/git/repositories/sixth-3d.git</developerConnection>
+    </scm>
+
+</project>
\ No newline at end of file
diff --git a/sixth-3d.iml b/sixth-3d.iml
new file mode 100644 (file)
index 0000000..2cb0c21
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="false">
+    <output url="file://$MODULE_DIR$/target/classes" />
+    <output-test url="file://$MODULE_DIR$/target/test-classes" />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+      <excludeFolder url="file://$MODULE_DIR$/target" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.8.1" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: eu.svjatoslav:javainspect:1.5-SNAPSHOT" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: eu.svjatoslav:svjatoslavcommons:1.5-SNAPSHOT" level="project" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Box.java b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Box.java
new file mode 100644 (file)
index 0000000..abfe554
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.geometry;
+
+/**
+ * Same as: 3D rectangle, rectangular box, rectangular parallelopiped, cuboid,
+ * rhumboid, hexahedron, rectangular prism.
+ */
+public class Box implements Cloneable {
+
+    public final Point3D p1;
+    public final Point3D p2;
+
+    public Box() {
+        p1 = new Point3D();
+        p2 = new Point3D();
+    }
+
+    public Box(final Point3D p1, final Point3D p2) {
+        this.p1 = p1;
+        this.p2 = p2;
+    }
+
+    public Box addBorder(final double border) {
+
+        if (p1.x < p2.x) {
+            p1.translateX(-border);
+            p2.translateX(border);
+        } else {
+            p1.translateX(border);
+            p2.translateX(-border);
+        }
+
+        if (p1.y < p2.y) {
+            p1.translateY(-border);
+            p2.translateY(border);
+        } else {
+            p1.translateY(border);
+            p2.translateY(-border);
+        }
+
+        if (p1.z < p2.z) {
+            p1.translateZ(-border);
+            p2.translateZ(border);
+        } else {
+            p1.translateZ(border);
+            p2.translateZ(-border);
+        }
+
+        return this;
+    }
+
+    @Override
+    public Box clone() {
+        return new Box(p1.clone(), p2.clone());
+    }
+
+    public double getDepth() {
+        return Math.abs(p1.z - p2.z);
+    }
+
+    public double getHeight() {
+        return Math.abs(p1.y - p2.y);
+    }
+
+    public double getWidth() {
+        return Math.abs(p1.x - p2.x);
+    }
+
+    public void setSizeCentered(final Point3D size) {
+        p2.clone(size).scaleDown(2);
+        p1.clone(p2).invert();
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Circle.java b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Circle.java
new file mode 100644 (file)
index 0000000..adb685b
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.geometry;
+
+public class Circle {
+
+    Point2D center;
+    double radius;
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/geometry/GeometryCoordinate.java b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/GeometryCoordinate.java
new file mode 100644 (file)
index 0000000..2820c74
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.geometry;
+
+import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
+
+public class GeometryCoordinate {
+
+    public Point3D coordinate;
+    public Point3D transformedCoordinate;
+    public Point2D onScreenCoordinate;
+
+    int lastTransformedFrame;
+
+    public GeometryCoordinate() {
+        coordinate = new Point3D();
+        transformedCoordinate = new Point3D();
+        onScreenCoordinate = new Point2D();
+    }
+
+    public GeometryCoordinate(final Point3D location) {
+        coordinate = location;
+        transformedCoordinate = new Point3D();
+        onScreenCoordinate = new Point2D();
+    }
+
+    public void transform(final TransformPipe transforms,
+                          final RenderingContext renderContext) {
+
+        if (lastTransformedFrame == renderContext.frameNumber)
+            return;
+
+        lastTransformedFrame = renderContext.frameNumber;
+
+        transforms.transform(coordinate, transformedCoordinate);
+
+        onScreenCoordinate.x = ((transformedCoordinate.x / transformedCoordinate.z) * renderContext.zoom)
+                + renderContext.xCenter;
+        onScreenCoordinate.y = ((transformedCoordinate.y / transformedCoordinate.z) * renderContext.zoom)
+                + renderContext.yCenter;
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Orientation.java b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Orientation.java
new file mode 100644 (file)
index 0000000..c38e3cd
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+
+package eu.svjatoslav.sixth.e3d.geometry;
+
+public class Orientation implements Cloneable {
+
+    private double s1, c1, s2, c2;
+
+    private double angleXZ = 0;
+    private double angleYZ = 0;
+
+    public Orientation() {
+        computeMultipliers();
+    }
+
+    public Orientation(final double angleXZ, final double angleYZ) {
+        this.angleXZ = angleXZ;
+        this.angleYZ = angleYZ;
+        computeMultipliers();
+    }
+
+    @Override
+    public Orientation clone() {
+        return new Orientation(angleXZ, angleYZ);
+    }
+
+    private void computeMultipliers() {
+        s1 = Math.sin(angleXZ);
+        c1 = Math.cos(angleXZ);
+
+        s2 = Math.sin(angleYZ);
+        c2 = Math.cos(angleYZ);
+    }
+
+    public void rotate(final Point3D point3d) {
+        final double z1 = (point3d.z * c1) - (point3d.x * s1);
+        point3d.x = (point3d.z * s1) + (point3d.x * c1);
+
+        point3d.z = (z1 * c2) - (point3d.y * s2);
+        point3d.y = (z1 * s2) + (point3d.y * c2);
+    }
+
+    public void rotate(final double angleXZ, final double angleYZ) {
+        this.angleXZ += angleXZ;
+        this.angleYZ += angleYZ;
+        computeMultipliers();
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Point2D.java b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Point2D.java
new file mode 100755 (executable)
index 0000000..c767ffd
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.geometry;
+
+public class Point2D implements Cloneable {
+
+    public double x, y;
+
+    public Point2D() {
+    }
+
+    public Point2D(final double x, final double y) {
+        this.x = x;
+        this.y = y;
+    }
+
+    public Point2D(final Point2D source) {
+        x = source.x;
+        y = source.y;
+    }
+
+    public Point2D add(final Point2D direction) {
+        x += direction.x;
+        y += direction.y;
+        return this;
+    }
+
+    @Override
+    public Point2D clone() {
+        return new Point2D(this);
+    }
+
+    public void clone(final Point2D source) {
+        x = source.x;
+        y = source.y;
+    }
+
+    public Point2D computeMiddlePoint(final Point2D p1, final Point2D p2) {
+        x = (p1.x + p2.x) / 2d;
+        y = (p1.y + p2.y) / 2d;
+        return this;
+    }
+
+    public double getAngleXY(final Point2D anotherPoint) {
+        return Math.atan2(x - anotherPoint.x, y - anotherPoint.y);
+    }
+
+    public double getDistanceTo(final Point2D anotherPoint) {
+        final double xDiff = x - anotherPoint.x;
+        final double yDiff = y - anotherPoint.y;
+
+        return Math.sqrt(((xDiff * xDiff) + (yDiff * yDiff)));
+    }
+
+    public Point2D invert() {
+        x = -x;
+        y = -y;
+        return this;
+    }
+
+    public void roundToInteger() {
+        x = (int) x;
+        y = (int) y;
+    }
+
+    public Point2D subtract(final Point2D direction) {
+        x -= direction.x;
+        y -= direction.y;
+        return this;
+    }
+
+    public Point3D to3D() {
+        return new Point3D(x, y, 0);
+    }
+
+    public Point2D zero() {
+        x = 0;
+        y = 0;
+        return this;
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Point3D.java b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Point3D.java
new file mode 100755 (executable)
index 0000000..aff475e
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.geometry;
+
+/**
+ * Used to represent point in a 3D space, vector or speed.
+ */
+
+public class Point3D extends Point2D implements Cloneable {
+
+    public static final Point3D ZERO = new Point3D();
+    public double z;
+
+    public Point3D() {
+    }
+
+    public Point3D(final double x, final double y, final double z) {
+        this.x = x;
+        this.y = y;
+        this.z = z;
+    }
+
+    public Point3D(final float x, final float y, final float z) {
+        this.x = x;
+        this.y = y;
+        this.z = z;
+    }
+
+    public Point3D(final int x, final int y, final int z) {
+        this.x = x;
+        this.y = y;
+        this.z = z;
+    }
+
+    public Point3D(final Point3D parentPoint) {
+        x = parentPoint.x;
+        y = parentPoint.y;
+        z = parentPoint.z;
+    }
+
+    public Point3D add(final Point3D direction) {
+        super.add(direction);
+        z += direction.z;
+        return this;
+    }
+
+    @Override
+    public Point3D clone() {
+        return new Point3D(this);
+    }
+
+    public Point3D clone(final Point3D source) {
+        x = source.x;
+        y = source.y;
+        z = source.z;
+        return this;
+    }
+
+    public Point3D computeMiddlePoint(final Point3D p1, final Point3D p2) {
+        x = (p1.x + p2.x) / 2d;
+        y = (p1.y + p2.y) / 2d;
+        z = (p1.z + p2.z) / 2d;
+        return this;
+    }
+
+    public double getAngleXZ(final Point3D anotherPoint) {
+        return Math.atan2(x - anotherPoint.x, z - anotherPoint.z);
+    }
+
+    public double getAngleYZ(final Point3D anotherPoint) {
+        return Math.atan2(y - anotherPoint.y, z - anotherPoint.z);
+    }
+
+    /**
+     * Compute distance to another point.
+     */
+    public double getDistanceTo(final Point3D anotherPoint) {
+        final double xDelta = x - anotherPoint.x;
+        final double yDelta = y - anotherPoint.y;
+        final double zDelta = z - anotherPoint.z;
+
+        return Math
+                .sqrt(((xDelta * xDelta) + (yDelta * yDelta) + (zDelta * zDelta)));
+    }
+
+    @Override
+    public Point3D invert() {
+        x = -x;
+        y = -y;
+        z = -z;
+        return this;
+    }
+
+    public void rotate(final Point3D center, final double angleXZ,
+                       final double angleYZ) {
+        final double s1 = Math.sin(angleXZ);
+        final double c1 = Math.cos(angleXZ);
+
+        final double s2 = Math.sin(angleYZ);
+        final double c2 = Math.cos(angleYZ);
+
+        x -= center.x;
+        y -= center.y;
+        z -= center.z;
+
+        final double y1 = (z * s2) + (y * c2);
+        final double z1 = (z * c2) - (y * s2);
+
+        final double x1 = (z1 * s1) + (x * c1);
+        final double z2 = (z1 * c1) - (x * s1);
+
+        x = x1 + center.x;
+        y = y1 + center.y;
+        z = z2 + center.z;
+    }
+
+    @Override
+    public void roundToInteger() {
+        x = (int) x;
+        y = (int) y;
+        z = (int) z;
+    }
+
+    public Point3D scaleDown(final double factor) {
+        x /= factor;
+        y /= factor;
+        z /= factor;
+        return this;
+    }
+
+    public Point3D scaleUp(final double factor) {
+        x *= factor;
+        y *= factor;
+        z *= factor;
+        return this;
+    }
+
+    public void setValues(final double x, final double y, final double z) {
+        this.x = x;
+        this.y = y;
+        this.z = z;
+    }
+
+    public Point3D subtract(final Point3D direction) {
+        super.subtract(direction);
+        z -= direction.z;
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        return "x:" + x + " y:" + y + " z:" + z;
+    }
+
+    public Point3D translateX(final double xIncrement) {
+        x += xIncrement;
+        return this;
+    }
+
+    public Point3D translateY(final double yIncrement) {
+        y += yIncrement;
+        return this;
+    }
+
+    public Point3D translateZ(final double zIncrement) {
+        z += zIncrement;
+        return this;
+    }
+
+    public boolean withinDrawingLimits() {
+
+        if (z > 0)
+            return true;
+
+        // if ((z > 0) && (x > -1000) && (y > -1000) && (x < 2000) && (y <
+        // 2000))
+        // return true;
+
+        return false;
+    }
+
+    @Override
+    public Point3D zero() {
+        super.zero();
+        z = 0;
+        return this;
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Polygon.java b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Polygon.java
new file mode 100644 (file)
index 0000000..ac1f54d
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.geometry;
+
+public class Polygon {
+
+    private static boolean intersectsLine(final Point2D point, Point2D p1,
+                                          Point2D p2) {
+
+        if (p1.y > p2.y) {
+            final Point2D tmp = p1;
+            p1 = p2;
+            p2 = tmp;
+        }
+
+        if (point.y < p1.y)
+            return false;
+        if (point.y > p2.y)
+            return false;
+
+        final double xp = p2.x - p1.x;
+        final double yp = p2.y - p1.y;
+
+        final double yp2 = point.y - p1.y;
+
+        final double crossX = p1.x + ((xp * yp2) / yp);
+
+        return point.x >= crossX;
+
+    }
+
+    public static boolean pointWithinPolygon(final Point2D point,
+                                             final Point2D p1, final Point2D p2, final Point2D p3) {
+
+        int intersectionCount = 0;
+
+        if (intersectsLine(point, p1, p2))
+            intersectionCount++;
+
+        if (intersectsLine(point, p2, p3))
+            intersectionCount++;
+
+        if (intersectsLine(point, p3, p1))
+            intersectionCount++;
+
+        return intersectionCount == 1;
+
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Rectangle.java b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Rectangle.java
new file mode 100644 (file)
index 0000000..9d426c5
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+
+package eu.svjatoslav.sixth.e3d.geometry;
+
+public class Rectangle {
+
+    public Point2D p1, p2;
+
+    public Rectangle(final double size) {
+        p2 = new Point2D(size / 2, size / 2);
+        p1 = p2.clone().invert();
+    }
+
+    public Rectangle(final Point2D p1, final Point2D p2) {
+        this.p1 = p1;
+        this.p2 = p2;
+    }
+
+    public double getHeight() {
+        return Math.abs(p1.y - p2.y);
+    }
+
+    public double getLowerX() {
+        if (p1.x < p2.x)
+            return p1.x;
+        return p2.x;
+    }
+
+    public double getLowerY() {
+        if (p1.y < p2.y)
+            return p1.y;
+        return p2.y;
+    }
+
+    public double getWidth() {
+        return Math.abs(p1.x - p2.x);
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Transform.java b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Transform.java
new file mode 100755 (executable)
index 0000000..adcec0d
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.geometry;
+
+public class Transform implements Cloneable {
+
+    private final Point3D translation;
+    private final Orientation orientation;
+
+    public Transform() {
+        translation = new Point3D();
+        orientation = new Orientation();
+    }
+
+    public Transform(final Point3D translation) {
+        this.translation = translation;
+        orientation = new Orientation();
+    }
+
+    public Transform(final Point3D translation, final double angleXZ,
+                     final double angleYZ) {
+
+        this.translation = translation;
+        orientation = new Orientation(angleXZ, angleYZ);
+    }
+
+    public Transform(final Point3D translation, final Orientation orientation) {
+        this.translation = translation;
+        this.orientation = orientation;
+    }
+
+    @Override
+    public Transform clone() {
+        return new Transform(translation, orientation);
+    }
+
+    public Orientation getOrientation() {
+        return orientation;
+    }
+
+    public Point3D getTranslation() {
+        return translation;
+    }
+
+    public void transform(final Point3D point) {
+        orientation.rotate(point);
+        point.add(translation);
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/geometry/TransformPipe.java b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/TransformPipe.java
new file mode 100644 (file)
index 0000000..413876b
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.geometry;
+
+public class TransformPipe {
+
+    Transform[] transforms = new Transform[100];
+
+    private int transformsCount = 0;
+
+    public void addTransform(final Transform transform) {
+        transforms[transformsCount] = transform;
+        transformsCount++;
+    }
+
+    public void clear() {
+        transformsCount = 0;
+    }
+
+    public void dropTransform() {
+        transformsCount--;
+    }
+
+    public void transform(final Point3D source, final Point3D destination) {
+
+        destination.clone(source);
+
+        for (int i = transformsCount - 1; i >= 0; i--)
+            transforms[i].transform(destination);
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/Avatar.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/Avatar.java
new file mode 100755 (executable)
index 0000000..ea90687
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.gui;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+
+public class Avatar implements ViewUpdateListener {
+
+    public static final double SPEED_LIMIT = 30;
+    /**
+     * Just in case we want to adjust global speed for some reason.
+     */
+    private static final double SPEED_MULTIPLIER = .02d;
+    /**
+     * Avatar movement speed, relative to avatar itself. When avatar coordinates
+     * are updated within the world, avatar orientation relative to the world is
+     * taken into account.
+     */
+    private final Point3D movementDirection = new Point3D();
+    public double avatarAcceleration = 0.1;
+    /**
+     * Avatar location within the 3D world.
+     */
+    private Point3D location = new Point3D();
+
+    /**
+     * Avatar orientation on the X-Z plane. It changes when turning left or
+     * right.
+     */
+    private double orientationXZ;
+
+    /**
+     * Avatar orientation on the Y-Z plane. It changes when looking up or down.
+     */
+    private double orientationYZ;
+
+    public Avatar() {
+    }
+
+    public Avatar(final Avatar sourceView) {
+        setLocation(new Point3D(sourceView.location));
+        setAngleXZ(sourceView.getAngleXZ());
+        setAngleYZ(sourceView.getAngleYZ());
+    }
+
+    public Avatar(final Point3D location) {
+        setLocation(location);
+    }
+
+    public Avatar(final Point3D location, final float angleXZ,
+                  final float angleYZ) {
+        setLocation(location);
+        setAngleXZ(angleXZ);
+        setAngleYZ(angleYZ);
+    }
+
+    @Override
+    public boolean beforeViewUpdate(final ViewContext viewContext,
+                                    final int millisecondsSinceLastFrame) {
+
+        final Point3D locationBeforeUpdate = new Point3D(location);
+        updateLocation(millisecondsSinceLastFrame);
+
+        final double distanceMoved = location
+                .getDistanceTo(locationBeforeUpdate);
+
+        return distanceMoved > 0.03;
+
+    }
+
+    public void enforceSpeedLimit() {
+        final double currentSpeed = movementDirection
+                .getDistanceTo(Point3D.ZERO);
+
+        if (currentSpeed <= SPEED_LIMIT)
+            return;
+
+        movementDirection.scaleDown(currentSpeed / SPEED_LIMIT);
+    }
+
+    public double getAngleXZ() {
+        return orientationXZ;
+    }
+
+    public void setAngleXZ(final double angleXZ) {
+        orientationXZ = angleXZ;
+    }
+
+    public double getAngleYZ() {
+        return orientationYZ;
+    }
+
+    public void setAngleYZ(final double angleYZ) {
+        orientationYZ = angleYZ;
+    }
+
+    public Point3D getLocation() {
+        return location;
+    }
+
+    public void setLocation(final Point3D location) {
+        this.location = location;
+    }
+
+    public Point3D getMovementDirection() {
+        return movementDirection;
+    }
+
+    public double getMovementSpeed() {
+        return movementDirection.getDistanceTo(Point3D.ZERO);
+    }
+
+    /**
+     * Update camera location based on current speed
+     */
+    public void updateLocation(final int millisecondsPassedSinceLastFrame) {
+
+        // translate user coordinates based on avatar movement speed, and avatar
+        // orientation in the world
+        {
+            location.x -= (float) Math.sin(getAngleXZ())
+                    * getMovementDirection().z * SPEED_MULTIPLIER
+                    * millisecondsPassedSinceLastFrame;
+            location.z += (float) Math.cos(getAngleXZ())
+                    * getMovementDirection().z * SPEED_MULTIPLIER
+                    * millisecondsPassedSinceLastFrame;
+
+            location.x += (float) Math.cos(getAngleXZ())
+                    * getMovementDirection().x * SPEED_MULTIPLIER
+                    * millisecondsPassedSinceLastFrame;
+            location.z += (float) Math.sin(getAngleXZ())
+                    * getMovementDirection().x * SPEED_MULTIPLIER
+                    * millisecondsPassedSinceLastFrame;
+
+            location.y += getMovementDirection().y * SPEED_MULTIPLIER
+                    * millisecondsPassedSinceLastFrame;
+        }
+
+        final double millisecondFriction = 1.005;
+        // apply friction to progressively slow movement
+        for (int i = 0; i < millisecondsPassedSinceLastFrame; i++) {
+            getMovementDirection().x = getMovementDirection().x
+                    / millisecondFriction;
+            getMovementDirection().y = getMovementDirection().y
+                    / millisecondFriction;
+            getMovementDirection().z = getMovementDirection().z
+                    / millisecondFriction;
+        }
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/GuiComponent.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/GuiComponent.java
new file mode 100644 (file)
index 0000000..06b87b7
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.gui;
+
+import eu.svjatoslav.sixth.e3d.geometry.Box;
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.geometry.Transform;
+import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController;
+import eu.svjatoslav.sixth.e3d.gui.humaninput.UserInputHandler;
+import eu.svjatoslav.sixth.e3d.gui.textEditorComponent.KeyboardHelper;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.WireframeBox;
+
+import java.awt.event.KeyEvent;
+
+public class GuiComponent extends AbstractCompositeShape implements
+        UserInputHandler, MouseInteractionController {
+
+    private static final String GROUP_GUI_FOCUS = "gui.focus";
+    public final ViewContext viewContext;
+    Box containingBox = new Box();
+    private WireframeBox borders = null;
+
+    private boolean borderShown = false;
+
+    public GuiComponent(final Transform transform,
+                        final ViewContext viewContext, final Point3D size) {
+        super(transform);
+        this.viewContext = viewContext;
+        setDimensions(size);
+    }
+
+    @Override
+    public boolean beforeViewUpdate(final ViewContext viewContext,
+                                    final int millisecondsSinceLastFrame) {
+        return false;
+    }
+
+    private WireframeBox createBorder() {
+        final LineAppearance appearance = new LineAppearance(10,
+                new eu.svjatoslav.sixth.e3d.renderer.raster.Color(255, 0, 0, 100));
+
+        final double borderSize = 10;
+
+        final Box borderArea = containingBox.clone().addBorder(borderSize);
+
+        return new WireframeBox(borderArea, appearance);
+    }
+
+    @Override
+    public void focusLost(final ViewContext viewContext) {
+        hideBorder();
+    }
+
+    @Override
+    public void focusReceived(final ViewContext viewContext) {
+        showBorder();
+    }
+
+    public WireframeBox getBorders() {
+        if (borders == null)
+            borders = createBorder();
+        return borders;
+    }
+
+    public int getDepth() {
+        return (int) containingBox.getDepth();
+    }
+
+    public int getHeight() {
+        return (int) containingBox.getHeight();
+    }
+
+    public int getWidth() {
+        return (int) containingBox.getWidth();
+    }
+
+    public void hideBorder() {
+        if (!borderShown)
+            return;
+        borderShown = false;
+        removeGroup(GROUP_GUI_FOCUS);
+    }
+
+    @Override
+    public void keyPressed(final KeyEvent event, final ViewContext viewContext) {
+        if (event.getKeyChar() == KeyboardHelper.ESC)
+            viewContext.getKeyboardFocusTracker().popFocusOwner();
+    }
+
+    @Override
+    public void keyReleased(final KeyEvent event, final ViewContext viewContext) {
+    }
+
+    @Override
+    public void mouseClicked() {
+        viewContext.getKeyboardFocusTracker().setFocusOwner(this);
+    }
+
+    @Override
+    public void mouseEntered() {
+    }
+
+    @Override
+    public void mouseExited() {
+    }
+
+    private void setDimensions(final Point3D size) {
+        containingBox.setSizeCentered(size);
+    }
+
+    public void showBorder() {
+        if (borderShown)
+            return;
+        borderShown = true;
+        addShape(getBorders(), GROUP_GUI_FOCUS);
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/RenderingContext.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/RenderingContext.java
new file mode 100644 (file)
index 0000000..a16c2d5
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.gui;
+
+import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseClick;
+import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferByte;
+import java.awt.image.WritableRaster;
+
+public class RenderingContext {
+
+    public static final int bufferedImageType = BufferedImage.TYPE_4BYTE_ABGR;
+
+    public final BufferedImage bufferedImage;
+
+    public final Graphics2D graphics;
+
+    public final byte[] bytes;
+
+    public final int width;
+    public final int height;
+
+    public final int xCenter;
+    public final int yCenter;
+
+    public final double zoom;
+
+    public int frameNumber = 0;
+
+    /**
+     * Used to signal that actual rendering should be performed. It is useful to
+     * skip rendering when we only want to detect mouse clicks intersections
+     * without actually updating rendered frame.
+     */
+    public boolean doRender = true; // TODO: make use of the variable
+
+    public MouseClick mouseClick;
+
+    public MouseInteractionController clickedItem;
+
+    public RenderingContext(final int width, final int height,
+                            final RenderingContext oldContext) {
+
+        this.width = width;
+        this.height = height;
+
+        this.xCenter = width / 2;
+        this.yCenter = height / 2;
+
+        this.zoom = width / 3d;
+
+        bufferedImage = new BufferedImage(width, height, bufferedImageType);
+
+        final WritableRaster raster = bufferedImage.getRaster();
+        final DataBufferByte dbi = (DataBufferByte) raster.getDataBuffer();
+        bytes = dbi.getData();
+
+        graphics = (Graphics2D) bufferedImage.getGraphics();
+
+        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+                RenderingHints.VALUE_ANTIALIAS_ON);
+
+        graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
+                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+
+        if (oldContext != null) {
+            mouseClick = oldContext.mouseClick;
+            clickedItem = oldContext.clickedItem;
+        }
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/TextPointer.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/TextPointer.java
new file mode 100755 (executable)
index 0000000..e78640e
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.gui;
+
+public class TextPointer implements Comparable<TextPointer> {
+
+    public int row;
+    public int column;
+
+    public TextPointer() {
+        this(0, 0);
+    }
+
+    public TextPointer(final int row, final int column) {
+        this.row = row;
+        this.column = column;
+    }
+
+    public TextPointer(final TextPointer parent) {
+        this(parent.row, parent.column);
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (o == null) return false;
+
+        return o instanceof TextPointer && compareTo((TextPointer) o) == 0;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = row;
+        result = 31 * result + column;
+        return result;
+    }
+
+    @Override
+    public int compareTo(final TextPointer textPointer) {
+
+        if (textPointer.row > row)
+            return -1;
+        if (textPointer.row < row)
+            return 1;
+
+        if (textPointer.column > column)
+            return -1;
+        if (textPointer.column < column)
+            return 1;
+
+        return 0;
+    }
+
+    public boolean isBetween(final TextPointer start, final TextPointer end) {
+
+        if (start == null)
+            return false;
+
+        if (end == null)
+            return false;
+
+        TextPointer smaller;
+        TextPointer bigger;
+
+        if (end.compareTo(start) >= 0) {
+            smaller = start;
+            bigger = end;
+        } else {
+            smaller = end;
+            bigger = start;
+        }
+
+        return (compareTo(smaller) >= 0) && (bigger.compareTo(this) > 0);
+
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/UserRelativityTracker.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/UserRelativityTracker.java
new file mode 100644 (file)
index 0000000..f50f6d9
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+
+package eu.svjatoslav.sixth.e3d.gui;
+
+import eu.svjatoslav.sixth.e3d.geometry.GeometryCoordinate;
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.geometry.TransformPipe;
+
+public class UserRelativityTracker {
+
+    public final static int minimalSliceFactor = 5;
+    public GeometryCoordinate center = new GeometryCoordinate();
+    public GeometryCoordinate right;
+    public GeometryCoordinate down;
+
+    public UserRelativityTracker() {
+
+    }
+
+    public void analyze(final TransformPipe transformPipe,
+                        final RenderingContext renderingContext) {
+
+        center.transform(transformPipe, renderingContext);
+
+        if (right != null) {
+            right.transform(transformPipe, renderingContext);
+            down.transform(transformPipe, renderingContext);
+        }
+    }
+
+    public void enableOrientationTracking() {
+        right = new GeometryCoordinate(new Point3D(10, 0, 0));
+        down = new GeometryCoordinate(new Point3D(0, 10, 0));
+    }
+
+    public double getAngleXY() {
+        return center.transformedCoordinate
+                .getAngleXY(down.transformedCoordinate);
+    }
+
+    public double getAngleXZ() {
+        return center.transformedCoordinate
+                .getAngleXZ(right.transformedCoordinate);
+    }
+
+    public double getAngleYZ() {
+        return center.transformedCoordinate
+                .getAngleYZ(down.transformedCoordinate);
+    }
+
+    public double getDistanceToUser() {
+        return center.transformedCoordinate
+                .getDistanceTo(Point3D.ZERO);
+    }
+
+    public double proposeSliceFactor() {
+        final double distanceToCamera = getDistanceToUser();
+
+        double proposedSliceFactor = distanceToCamera / 5;
+
+        if (proposedSliceFactor < minimalSliceFactor)
+            proposedSliceFactor = minimalSliceFactor;
+
+        return proposedSliceFactor;
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/View.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/View.java
new file mode 100755 (executable)
index 0000000..ecd0e9d
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.gui;
+
+import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Timer;
+
+public class View extends JPanel implements ComponentListener {
+
+    private static final long serialVersionUID = 1683277888885045387L;
+    private final List<ViewListener> viewListeners = new ArrayList<>();
+    private final List<ViewUpdateListener> viewUpdateListeners = new ArrayList<>();
+    private final ViewContext context = new ViewContext(this);
+    /**
+     * Last time this view was updated
+     */
+    long lastUpdateMillis = 0;
+    private Timer canvasUpdateTimer;
+    private ViewUpdateTimerTask canvasUpdateTimerTask;
+    private RenderingContext renderingContext = new RenderingContext(1, 1, null);
+    private MouseInteractionController currentMouseOverComponent;
+    /**
+     * Currently target FPS for this view. It might change at runtime.
+     */
+    private int targetFramerate = 30;
+    private boolean repaintDuringNextViewUpdate = true;
+
+    public View() {
+        viewUpdateListeners.add(context.getAvatar());
+
+        // initialize input tracker
+        context.getUserInputTracker().bind(this);
+        viewUpdateListeners.add(context.getUserInputTracker());
+
+        initializePanelLayout();
+
+        setFrameRate(targetFramerate);
+
+        addComponentListener(this);
+    }
+
+    public void addViewListener(final ViewListener listener) {
+        getViewListeners().add(listener);
+    }
+
+    public void addViewUpdateListener(final ViewUpdateListener listener) {
+        viewUpdateListeners.add(listener);
+    }
+
+    @Override
+    public void componentHidden(final ComponentEvent e) {
+
+    }
+
+    @Override
+    public void componentMoved(final ComponentEvent e) {
+
+    }
+
+    @Override
+    public void componentResized(final ComponentEvent e) {
+        repaintDuringNextViewUpdate = true;
+    }
+
+    @Override
+    public void componentShown(final ComponentEvent e) {
+        repaintDuringNextViewUpdate = true;
+    }
+
+    public ViewContext getContext() {
+        return context;
+    }
+
+    @Override
+    public Dimension getMaximumSize() {
+        return getPreferredSize();
+    }
+
+    @Override
+    public Dimension getMinimumSize() {
+        return getPreferredSize();
+    }
+
+    @Override
+    public java.awt.Dimension getPreferredSize() {
+        return new java.awt.Dimension(640, 480);
+    }
+
+    public RenderingContext getRenderBuffer() {
+        return renderingContext;
+    }
+
+    public RenderingContext getRenderingContext() {
+        return renderingContext;
+    }
+
+    public List<ViewListener> getViewListeners() {
+        return viewListeners;
+    }
+
+    private void handleDetectedComponentMouseEvents() {
+        if (renderingContext.clickedItem != null) {
+            if (renderingContext.mouseClick.button == 0) {
+                // mouse over
+                if (currentMouseOverComponent == null) {
+                    currentMouseOverComponent = renderingContext.clickedItem;
+                    currentMouseOverComponent.mouseEntered();
+                    repaintDuringNextViewUpdate = true;
+                } else if (currentMouseOverComponent != renderingContext.clickedItem) {
+                    currentMouseOverComponent.mouseExited();
+                    currentMouseOverComponent = renderingContext.clickedItem;
+                    currentMouseOverComponent.mouseEntered();
+                    repaintDuringNextViewUpdate = true;
+                }
+            } else {
+                // mouse click
+                renderingContext.clickedItem.mouseClicked();
+                repaintDuringNextViewUpdate = true;
+            }
+        } else if (currentMouseOverComponent != null) {
+            currentMouseOverComponent.mouseExited();
+            repaintDuringNextViewUpdate = true;
+            currentMouseOverComponent = null;
+        }
+    }
+
+    private void initializePanelLayout() {
+        setFocusCycleRoot(true);
+        setOpaque(true);
+        setFocusable(true);
+        setDoubleBuffered(false);
+        setVisible(true);
+        requestFocusInWindow();
+    }
+
+    public void renderFrame() {
+        // build new render buffer if needed, this happens when window was just
+        // created or resized
+        if ((renderingContext == null)
+                || (renderingContext.width != getWidth())
+                || (renderingContext.height != getHeight()))
+            renderingContext = new RenderingContext(getWidth(), getHeight(),
+                    renderingContext);
+
+        // clear drawing area
+        {
+            renderingContext.graphics.setColor(Color.BLACK);
+            renderingContext.graphics.fillRect(0, 0, getWidth(), getHeight());
+        }
+
+        // paint root geometry collection to the offscreen render buffer
+        context.getRootShapeCollection().paint(context, renderingContext);
+
+        // draw rendered offscreen image to visible screen
+        final Graphics graphics = getGraphics();
+        if (graphics != null)
+            graphics.drawImage(renderingContext.bufferedImage, 0, 0, null);
+    }
+
+    /**
+     * Calling this methods tells 3D engine that current 3D view needs to be
+     * repainted on first opportunity.
+     */
+    public void repaintDuringNextViewUpdate() {
+        repaintDuringNextViewUpdate = true;
+    }
+
+    public void setFrameRate(final int frameRate) {
+        if (canvasUpdateTimerTask != null) {
+            canvasUpdateTimerTask.cancel();
+            canvasUpdateTimerTask = null;
+
+            canvasUpdateTimer.cancel();
+            canvasUpdateTimer = null;
+        }
+
+        targetFramerate = frameRate;
+
+        if (frameRate > 0) {
+            canvasUpdateTimer = new Timer();
+            canvasUpdateTimerTask = new ViewUpdateTimerTask(this);
+
+            canvasUpdateTimer.schedule(canvasUpdateTimerTask, 0,
+                    1000 / frameRate);
+        }
+    }
+
+    public void stop() {
+        if (canvasUpdateTimerTask != null) {
+            canvasUpdateTimerTask.cancel();
+            canvasUpdateTimerTask = null;
+        }
+
+        if (canvasUpdateTimer != null) {
+            canvasUpdateTimer.cancel();
+            canvasUpdateTimer = null;
+        }
+    }
+
+    /**
+     * This method is executed by periodic timer task, in frequency according to
+     * defined frame rate.
+     * <p>
+     * It tells view to update itself. View can decide if actual re-rendering of
+     * graphics is needed.
+     */
+    public void updateView() {
+        renderingContext.mouseClick = null;
+        renderingContext.clickedItem = null;
+
+        // compute time passed since last view update
+        final long currentTime = System.currentTimeMillis();
+
+        if (lastUpdateMillis == 0) {
+            lastUpdateMillis = currentTime;
+            return;
+        }
+
+        final int millisecondsPassedSinceLastUpdate = (int) (currentTime - lastUpdateMillis);
+        lastUpdateMillis = currentTime;
+
+        // notify update listeners
+        boolean rerenderView = false;
+
+        for (final ViewUpdateListener listener : viewUpdateListeners)
+            if (listener.beforeViewUpdate(context,
+                    millisecondsPassedSinceLastUpdate))
+                rerenderView = true;
+
+        // abort rendering if window size is invalid
+        if ((getWidth() <= 0) || (getHeight() <= 0))
+            return;
+
+        if (repaintDuringNextViewUpdate) {
+            repaintDuringNextViewUpdate = false;
+            rerenderView = true;
+        }
+
+        if (rerenderView) {
+            renderFrame();
+            handleDetectedComponentMouseEvents();
+        }
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewContext.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewContext.java
new file mode 100644 (file)
index 0000000..b3e66e3
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.gui;
+
+import eu.svjatoslav.sixth.e3d.gui.humaninput.KeyboardFocusTracker;
+import eu.svjatoslav.sixth.e3d.gui.humaninput.UserInputTracker;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+
+public class ViewContext {
+
+    private final UserInputTracker userInputTracker = new UserInputTracker(this);
+
+    private final KeyboardFocusTracker keyboardFocusTracker = new KeyboardFocusTracker(
+            this);
+
+    private final Avatar avatar = new Avatar();
+
+    private final View view;
+
+    private final ShapeCollection rootShapeCollection = new ShapeCollection();
+
+    public ViewContext(final View view) {
+        this.view = view;
+    }
+
+    public Avatar getAvatar() {
+        return avatar;
+    }
+
+    public KeyboardFocusTracker getKeyboardFocusTracker() {
+        return keyboardFocusTracker;
+    }
+
+    public ShapeCollection getRootShapeCollection() {
+        return rootShapeCollection;
+    }
+
+    public UserInputTracker getUserInputTracker() {
+        return userInputTracker;
+    }
+
+    public View getView() {
+        return view;
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewFrame.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewFrame.java
new file mode 100755 (executable)
index 0000000..3959a30
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.gui;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowListener;
+
+public class ViewFrame extends JFrame implements ViewListener, WindowListener {
+
+    private static final long serialVersionUID = -7037635097739548470L;
+
+    private final View view;
+
+    public ViewFrame() {
+        setTitle("3D engine");
+
+        addWindowListener(new java.awt.event.WindowAdapter() {
+            @Override
+            public void windowClosing(final java.awt.event.WindowEvent e) {
+                exit();
+            }
+        });
+
+        view = new View();
+
+        add(getView());
+
+        getView().addViewListener(this);
+
+        setSize(800, 600);
+
+        // maximize window
+        setExtendedState(JFrame.MAXIMIZED_BOTH);
+        setVisible(true);
+
+        addResizeListener();
+        addWindowListener(this);
+    }
+
+    public void addResizeListener() {
+        addComponentListener(new ComponentListener() {
+            // This method is called after the component's size changes
+            @Override
+            public void componentHidden(final ComponentEvent e) {
+            }
+
+            @Override
+            public void componentMoved(final ComponentEvent e) {
+            }
+
+            @Override
+            public void componentResized(final ComponentEvent evt) {
+
+                final Component c = (Component) evt.getSource();
+
+                // Get new size
+                final Dimension newSize = c.getSize();
+
+                boolean sizeFixed = false;
+
+                if (newSize.width < 400) {
+                    newSize.width = 400;
+                    sizeFixed = true;
+                }
+
+                if (newSize.height < 400) {
+                    newSize.height = 400;
+                    sizeFixed = true;
+                }
+
+                if (sizeFixed)
+                    setSize(newSize);
+
+            }
+
+            @Override
+            public void componentShown(final ComponentEvent e) {
+                view.repaintDuringNextViewUpdate();
+            }
+
+        });
+    }
+
+    @Override
+    public void exit() {
+        if (getView() != null) {
+            getView().stop();
+            getView().setEnabled(false);
+            getView().setVisible(false);
+        }
+        dispose();
+    }
+
+    @Override
+    public java.awt.Dimension getPreferredSize() {
+        return new java.awt.Dimension(640, 480);
+    }
+
+    /**
+     * @return the view
+     */
+    public View getView() {
+        return view;
+    }
+
+    @Override
+    public void windowActivated(final WindowEvent e) {
+        view.repaintDuringNextViewUpdate();
+    }
+
+    @Override
+    public void windowClosed(final WindowEvent e) {
+    }
+
+    @Override
+    public void windowClosing(final WindowEvent e) {
+    }
+
+    @Override
+    public void windowDeactivated(final WindowEvent e) {
+    }
+
+    @Override
+    public void windowDeiconified(final WindowEvent e) {
+        view.repaintDuringNextViewUpdate();
+    }
+
+    @Override
+    public void windowIconified(final WindowEvent e) {
+    }
+
+    @Override
+    public void windowOpened(final WindowEvent e) {
+        view.repaintDuringNextViewUpdate();
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewListener.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewListener.java
new file mode 100644 (file)
index 0000000..4cb062d
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.gui;
+
+public interface ViewListener {
+
+    void exit();
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewUpdateListener.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewUpdateListener.java
new file mode 100644 (file)
index 0000000..e31bd2e
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.gui;
+
+public interface ViewUpdateListener {
+
+    /**
+     * @return <code>true</code> if underlying view shall be re-rendered. If at
+     * least one of the view update listeners returns <code>true</code>,
+     * view is re-rendered.
+     */
+    boolean beforeViewUpdate(ViewContext viewContext,
+                             final int millisecondsSinceLastFrame);
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewUpdateTimerTask.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewUpdateTimerTask.java
new file mode 100755 (executable)
index 0000000..1dba36a
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.gui;
+
+public class ViewUpdateTimerTask extends java.util.TimerTask {
+
+    public View view;
+
+    public ViewUpdateTimerTask(final View view) {
+        this.view = view;
+    }
+
+    @Override
+    public void run() {
+
+        // update and possibly render view
+        view.updateView();
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/KeyboardFocusTracker.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/KeyboardFocusTracker.java
new file mode 100644 (file)
index 0000000..e4690d4
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.gui.humaninput;
+
+import eu.svjatoslav.sixth.e3d.gui.ViewContext;
+
+import java.util.Stack;
+
+public class KeyboardFocusTracker {
+
+    private final ViewContext viewContext;
+    WorldNavigationTracker defaultInputHandler = new WorldNavigationTracker();
+    Stack<UserInputHandler> inputHandlers = new Stack<>();
+    private UserInputHandler currentUserInputHandler;
+
+    public KeyboardFocusTracker(final ViewContext viewContext) {
+        this.viewContext = viewContext;
+        setFocusOwner(defaultInputHandler);
+    }
+
+    public UserInputHandler getCurrentFocusOwner() {
+        return currentUserInputHandler;
+    }
+
+    public void popFocusOwner() {
+        if (currentUserInputHandler == null)
+            return;
+
+        if (inputHandlers.isEmpty())
+            return;
+
+        currentUserInputHandler.focusLost(viewContext);
+
+        currentUserInputHandler = inputHandlers.pop();
+
+        currentUserInputHandler.focusReceived(viewContext);
+    }
+
+    public void setFocusOwner(final UserInputHandler inputHandler) {
+        if (currentUserInputHandler == inputHandler)
+            return;
+
+        if (currentUserInputHandler != null) {
+            currentUserInputHandler.focusLost(viewContext);
+            inputHandlers.push(currentUserInputHandler);
+        }
+
+        currentUserInputHandler = inputHandler;
+
+        currentUserInputHandler.focusReceived(viewContext);
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/MouseClick.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/MouseClick.java
new file mode 100644 (file)
index 0000000..be20a82
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.gui.humaninput;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point2D;
+
+public class MouseClick {
+
+    public Point2D coordinate;
+
+    /**
+     * Indicates pressed mouse button. Except 0 that simply means mouse over
+     * given region.
+     */
+    public int button;
+
+    public MouseClick(final int x, final int y, final int button) {
+        this(new Point2D(x, y), button);
+    }
+
+    public MouseClick(final Point2D coordinate, final int button) {
+        this.coordinate = coordinate;
+        this.button = button;
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/MouseInteractionController.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/MouseInteractionController.java
new file mode 100644 (file)
index 0000000..36913ec
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.gui.humaninput;
+
+public interface MouseInteractionController {
+
+    /**
+     * Called when mouse is clicked on component
+     */
+    void mouseClicked();
+
+    /**
+     * Called when mouse gets over given component.
+     */
+    void mouseEntered();
+
+    /**
+     * Called when mouse leaves screen area occupied by component.
+     */
+    void mouseExited();
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/UserInputHandler.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/UserInputHandler.java
new file mode 100644 (file)
index 0000000..72e5d38
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.gui.humaninput;
+
+import eu.svjatoslav.sixth.e3d.gui.ViewContext;
+import eu.svjatoslav.sixth.e3d.gui.ViewUpdateListener;
+
+import java.awt.event.KeyEvent;
+
+public interface UserInputHandler extends ViewUpdateListener {
+
+    void focusLost(ViewContext viewContext);
+
+    void focusReceived(ViewContext viewContext);
+
+    void keyPressed(KeyEvent event, ViewContext viewContext);
+
+    void keyReleased(KeyEvent event, ViewContext viewContext);
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/UserInputTracker.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/UserInputTracker.java
new file mode 100755 (executable)
index 0000000..4a1ab34
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.gui.humaninput;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point2D;
+import eu.svjatoslav.sixth.e3d.gui.Avatar;
+import eu.svjatoslav.sixth.e3d.gui.View;
+import eu.svjatoslav.sixth.e3d.gui.ViewContext;
+import eu.svjatoslav.sixth.e3d.gui.ViewUpdateListener;
+
+import javax.swing.*;
+import java.awt.event.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class UserInputTracker
+        implements MouseMotionListener, KeyListener, MouseListener, MouseWheelListener, ViewUpdateListener {
+
+    /**
+     * <pre>
+     * Key is keyboard key code.
+     * Value is system milliseconds when key was pressed.
+     *
+     * So by reading the map one can determine currently pressed keys as well as duration.
+     * </pre>
+     */
+    private final Map<Integer, Long> pressedKeysToPressedTimeMap = new HashMap<>();
+    private final List<MouseClick> detectedMouseClicks = new ArrayList<>();
+    private final List<KeyEvent> detectedKeyEvents = new ArrayList<>();
+    public int wheelMovedDirection = 0;
+    Point2D mouseDraggedDirection = new Point2D();
+    Point2D oldMouseCoordinatesWhenDragging;
+    ViewContext viewContext;
+    Point2D currentMouseLocation;
+    boolean mouseMoved;
+    private boolean mouseWithinWindow = false;
+
+    public UserInputTracker(final ViewContext viewContext) {
+        this.viewContext = viewContext;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean beforeViewUpdate(final ViewContext viewContext, final int millisecondsSinceLastFrame) {
+
+        boolean viewUpdateNeeded = handleDetectedMouseClicks(viewContext.getView());
+
+        viewUpdateNeeded |= handleDetectedKeyEvents();
+
+        viewContext.getKeyboardFocusTracker().getCurrentFocusOwner().beforeViewUpdate(viewContext,
+                millisecondsSinceLastFrame);
+        viewUpdateNeeded |= trackMouse();
+
+        return viewUpdateNeeded;
+    }
+
+    public void bind(final JPanel panel) {
+        panel.addMouseMotionListener(this);
+
+        panel.addKeyListener(this);
+
+        panel.addMouseListener(this);
+
+        panel.addMouseWheelListener(this);
+    }
+
+    public boolean handleDetectedKeyEvents() {
+        boolean keyEventsHandled = false;
+
+        final UserInputHandler currentFocusOwner = viewContext.getKeyboardFocusTracker().getCurrentFocusOwner();
+
+        synchronized (detectedKeyEvents) {
+            if (currentFocusOwner == null) {
+                detectedKeyEvents.clear();
+                return false;
+            }
+
+            while (!detectedKeyEvents.isEmpty()) {
+                final KeyEvent keyEvent = detectedKeyEvents.remove(0);
+
+                switch (keyEvent.getID()) {
+                    case KeyEvent.KEY_PRESSED:
+                        currentFocusOwner.keyPressed(keyEvent, viewContext);
+                        keyEventsHandled = true;
+                        break;
+
+                    case KeyEvent.KEY_RELEASED:
+                        currentFocusOwner.keyReleased(keyEvent, viewContext);
+                        keyEventsHandled = true;
+                        break;
+                }
+            }
+        }
+
+        return keyEventsHandled;
+    }
+
+    /**
+     * Returns <code>true</code> if mouse events are detected and view needs to
+     * be repainted.
+     */
+    public synchronized boolean handleDetectedMouseClicks(final View view) {
+        if (detectedMouseClicks.isEmpty()) {
+
+            if (currentMouseLocation != null)
+                view.getRenderingContext().mouseClick = new MouseClick(currentMouseLocation, 0);
+
+            if (mouseMoved) {
+                mouseMoved = false;
+                return true;
+            } else
+                return false;
+        }
+
+        view.getRenderingContext().mouseClick = detectedMouseClicks.remove(0);
+
+        return true;
+    }
+
+    public boolean isKeyPressed(final int keyCode) {
+        return pressedKeysToPressedTimeMap.containsKey(keyCode);
+    }
+
+    @Override
+    public void keyPressed(final KeyEvent evt) {
+        synchronized (detectedKeyEvents) {
+            pressedKeysToPressedTimeMap.put(evt.getKeyCode(), System.currentTimeMillis());
+            detectedKeyEvents.add(evt);
+            viewContext.getView().repaintDuringNextViewUpdate();
+        }
+    }
+
+    @Override
+    public void keyReleased(final KeyEvent evt) {
+        synchronized (detectedKeyEvents) {
+            pressedKeysToPressedTimeMap.remove(evt.getKeyCode());
+            detectedKeyEvents.add(evt);
+            viewContext.getView().repaintDuringNextViewUpdate();
+        }
+    }
+
+    @Override
+    public void keyTyped(final KeyEvent e) {
+    }
+
+    @Override
+    public void mouseClicked(final MouseEvent e) {
+        synchronized (detectedMouseClicks) {
+            detectedMouseClicks.add(new MouseClick(e.getX(), e.getY(), e.getButton()));
+        }
+    }
+
+    @Override
+    public void mouseDragged(final java.awt.event.MouseEvent evt) {
+        final Point2D mouseLocation = new Point2D(evt.getX(), evt.getY());
+
+        if (oldMouseCoordinatesWhenDragging == null) {
+            oldMouseCoordinatesWhenDragging = mouseLocation;
+            return;
+        }
+
+        mouseDraggedDirection.add(mouseLocation.clone().subtract(oldMouseCoordinatesWhenDragging));
+
+        oldMouseCoordinatesWhenDragging = mouseLocation;
+    }
+
+    @Override
+    public void mouseEntered(final MouseEvent e) {
+        mouseWithinWindow = true;
+    }
+
+    @Override
+    public synchronized void mouseExited(final MouseEvent e) {
+        mouseWithinWindow = false;
+        currentMouseLocation = null;
+    }
+
+    @Override
+    public synchronized void mouseMoved(final MouseEvent e) {
+        currentMouseLocation = new Point2D(e.getX(), e.getY());
+        mouseMoved = true;
+    }
+
+    @Override
+    public void mousePressed(final MouseEvent e) {
+    }
+
+    @Override
+    public void mouseReleased(final java.awt.event.MouseEvent evt) {
+        oldMouseCoordinatesWhenDragging = null;
+    }
+
+    @Override
+    public void mouseWheelMoved(final java.awt.event.MouseWheelEvent evt) {
+        wheelMovedDirection += evt.getWheelRotation();
+    }
+
+    /**
+     * Interpret mouse movement
+     */
+    public boolean trackMouse() {
+        final Avatar avatar = viewContext.getAvatar();
+
+        // track mouse dragging
+
+        // TODO: need to detect whether user moved mouse or touch screen
+
+        // for mouse
+        avatar.setAngleXZ(avatar.getAngleXZ() - ((float) mouseDraggedDirection.x / 50));
+        avatar.setAngleYZ(avatar.getAngleYZ() - ((float) mouseDraggedDirection.y / 50));
+
+        // for touch screen
+        // avatar.setAngleXZ(avatar.getAngleXZ() + ((float)
+        // mouseDraggedDirection.x / 50));
+        // avatar.setAngleYZ(avatar.getAngleYZ() + ((float)
+        // mouseDraggedDirection.y / 50));
+
+        // track mouse wheel movements
+        final double actualAcceleration = 50 * avatar.avatarAcceleration * (1 + (avatar.getMovementSpeed() / 10));
+
+        avatar.getMovementDirection().y += (wheelMovedDirection * actualAcceleration);
+        avatar.enforceSpeedLimit();
+
+        // check if view shall be repainted
+        boolean repaintNeeded;
+        repaintNeeded = (mouseDraggedDirection.x != 0) || (mouseDraggedDirection.y != 0) || (wheelMovedDirection != 0);
+
+        // reset movement counters
+        wheelMovedDirection = 0;
+        mouseDraggedDirection.zero();
+
+        return repaintNeeded;
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/WorldNavigationTracker.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/WorldNavigationTracker.java
new file mode 100644 (file)
index 0000000..34fd37e
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.gui.humaninput;
+
+import eu.svjatoslav.sixth.e3d.gui.Avatar;
+import eu.svjatoslav.sixth.e3d.gui.ViewContext;
+import eu.svjatoslav.sixth.e3d.gui.textEditorComponent.KeyboardHelper;
+
+import java.awt.event.KeyEvent;
+
+public class WorldNavigationTracker implements UserInputHandler {
+
+    @Override
+    public boolean beforeViewUpdate(final ViewContext viewContext,
+                                    final int millisecondsSinceLastFrame) {
+
+        trackKeys(millisecondsSinceLastFrame, viewContext);
+        return false;
+    }
+
+    @Override
+    public void focusLost(final ViewContext viewContext) {
+    }
+
+    @Override
+    public void focusReceived(final ViewContext viewContext) {
+    }
+
+    @Override
+    public void keyPressed(final KeyEvent event, final ViewContext viewContext) {
+    }
+
+    @Override
+    public void keyReleased(final KeyEvent event, final ViewContext viewContext) {
+    }
+
+    /**
+     * interpret currently pressed keys
+     */
+    public void trackKeys(final long millisecondsSinceLastFrame,
+                          final ViewContext viewContext) {
+
+        final UserInputTracker inputTracker = viewContext.getUserInputTracker();
+
+        final Avatar avatar = viewContext.getAvatar();
+
+        final double actualAcceleration = millisecondsSinceLastFrame
+                * avatar.avatarAcceleration
+                * (1 + (avatar.getMovementSpeed() / 10));
+
+        if (inputTracker.isKeyPressed(KeyboardHelper.UP))
+            avatar.getMovementDirection().z += actualAcceleration;
+
+        if (inputTracker.isKeyPressed(KeyboardHelper.DOWN))
+            avatar.getMovementDirection().z -= actualAcceleration;
+
+        if (inputTracker.isKeyPressed(KeyboardHelper.RIGHT))
+            avatar.getMovementDirection().x += actualAcceleration;
+
+        if (inputTracker.isKeyPressed(KeyboardHelper.LEFT))
+            avatar.getMovementDirection().x -= actualAcceleration;
+
+        avatar.enforceSpeedLimit();
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/Character.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/Character.java
new file mode 100644 (file)
index 0000000..0756d1c
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.gui.textEditorComponent;
+
+public class Character {
+
+    char value;
+
+    // TODO: background and foreground colors
+
+    public Character(final char value) {
+        this.value = value;
+    }
+
+    boolean isEmpty() {
+        return value == ' ';
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/ColorConfig.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/ColorConfig.java
new file mode 100644 (file)
index 0000000..623d7dc
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.gui.textEditorComponent;
+
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+
+public class ColorConfig {
+
+    public Color normalText = new Color(255, 255, 255);
+    public Color normalBack = new Color(20, 20, 20, 255);
+
+    public Color tabulatorBack = new Color(25, 25, 25, 255);
+
+    public Color cursorText = new Color(255, 255, 255);
+    public Color cursorBack = new Color(255, 0, 0);
+
+    public Color selectedText = new Color(255, 255, 255);
+    public Color selectedBack = new Color(0, 80, 80);
+
+    public Color pageEnd = new Color(70, 70, 0);
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/KeyboardHelper.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/KeyboardHelper.java
new file mode 100644 (file)
index 0000000..4edd809
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.gui.textEditorComponent;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class KeyboardHelper {
+
+    public static final int TAB = 9;
+    public static final int DOWN = 40;
+    public static final int UP = 38;
+    public static final int RIGHT = 39;
+    public static final int LEFT = 37;
+    public static final int PGDOWN = 34;
+    public static final int PGUP = 33;
+    public static final int HOME = 36;
+    public static final int END = 35;
+    public static final int DEL = 127;
+    public static final int ENTER = 10;
+    public static final int BACKSPACE = 8;
+    public static final int ESC = 27;
+    public static final int SHIFT = 16;
+
+    private static final Set<Integer> nonText;
+
+    static {
+        nonText = new HashSet<>();
+        nonText.add(DOWN);
+        nonText.add(UP);
+        nonText.add(LEFT);
+        nonText.add(RIGHT);
+
+        nonText.add(SHIFT);
+        nonText.add(ESC);
+    }
+
+    public static boolean isAlt(final int modifiers) {
+        return (modifiers | 8) == modifiers;
+    }
+
+    public static boolean isCtrl(final int modifiers) {
+        return (modifiers | 2) == modifiers;
+    }
+
+    public static boolean isShift(final int modifiers) {
+        return (modifiers | 1) == modifiers;
+    }
+
+    public static boolean isText(final int keyCode) {
+        return !nonText.contains(keyCode);
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/Page.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/Page.java
new file mode 100644 (file)
index 0000000..8757439
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.gui.textEditorComponent;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Page {
+
+    public List<TextLine> lines = new ArrayList<>();
+
+    public void ensureMaxTextLine(final int row) {
+        while (lines.size() <= row)
+            lines.add(new TextLine());
+    }
+
+    public char getChar(final int row, final int column) {
+        if (lines.size() <= row)
+            return ' ';
+        return lines.get(row).getCharForLocation(column);
+    }
+
+    public TextLine getLine(final int row) {
+        ensureMaxTextLine(row);
+        return lines.get(row);
+    }
+
+    public int getLineLength(final int row) {
+        if (lines.size() <= row)
+            return 0;
+        return lines.get(row).getLength();
+    }
+
+    public int getLinesCount() {
+        pack();
+        return lines.size();
+    }
+
+    public String getText() {
+        pack();
+
+        final StringBuilder result = new StringBuilder();
+        for (final TextLine textLine : lines) {
+            if (result.length() > 0)
+                result.append("\n");
+            result.append(textLine.toString());
+        }
+        return result.toString();
+    }
+
+    public void insertCharacter(final int row, final int col, final char value) {
+        getLine(row).insertCharacter(col, value);
+    }
+
+    public void insertLine(final int row, final TextLine textLine) {
+        lines.add(row, textLine);
+    }
+
+    private void pack() {
+        int newLength = 0;
+
+        for (int i = lines.size() - 1; i >= 0; i--)
+            if (!lines.get(i).isEmpty()) {
+                newLength = i + 1;
+                break;
+            }
+
+        if (newLength == lines.size())
+            return;
+
+        lines = lines.subList(0, newLength);
+    }
+
+    public void removeCharacter(final int row, final int col) {
+        if (lines.size() <= row)
+            return;
+        getLine(row).removeCharacter(col);
+    }
+
+    public void removeLine(final int row) {
+        if (lines.size() <= row)
+            return;
+        lines.remove(row);
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/TextEditComponent.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/TextEditComponent.java
new file mode 100755 (executable)
index 0000000..d81da31
--- /dev/null
@@ -0,0 +1,634 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.gui.textEditorComponent;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point2D;
+import eu.svjatoslav.sixth.e3d.geometry.Transform;
+import eu.svjatoslav.sixth.e3d.gui.GuiComponent;
+import eu.svjatoslav.sixth.e3d.gui.TextPointer;
+import eu.svjatoslav.sixth.e3d.gui.ViewContext;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas.TextCanvas;
+
+import java.awt.*;
+import java.awt.datatransfer.*;
+import java.awt.event.KeyEvent;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+public class TextEditComponent extends GuiComponent implements ClipboardOwner {
+
+    private static final long serialVersionUID = -7118833957783600630L;
+    // lines that need to be repainted
+    private final Set<Integer> dirtyRows = new HashSet<>();
+    private final TextCanvas textCanvas;
+    public int scrolledCharacters = 0, scrolledLines = 0;
+    public boolean selecting = false;
+    public TextPointer selectionStart = new TextPointer(0, 0);
+    public TextPointer selectionEnd = new TextPointer(0, 0);
+    public TextPointer cursorLocation;
+    Page page = new Page();
+    ColorConfig colorConfig = new ColorConfig();
+    boolean repaintPage = false;
+
+    public TextEditComponent(final Transform transform,
+                             final ViewContext viewContext, final Point2D size) {
+        super(transform, viewContext, size.to3D());
+
+        cursorLocation = new TextPointer(0, 0);
+
+        // initialize visual panel
+
+        final int columns = (int) (size.x / TextCanvas.FONT_CHAR_WIDTH);
+        final int rows = (int) (size.y / TextCanvas.FONT_CHAR_HEIGHT);
+
+        textCanvas = new TextCanvas(new Transform(), new TextPointer(rows,
+                columns), Color.WHITE, colorConfig.normalBack);
+
+        textCanvas.setMouseInteractionController(this);
+
+        repaintPage();
+        addShape(textCanvas);
+    }
+
+    private void checkCursorBoundaries() {
+        if (cursorLocation.column < 0)
+            cursorLocation.column = 0;
+        if (cursorLocation.row < 0)
+            cursorLocation.row = 0;
+
+        // ensure chat cursor stays within vertical editor boundaries by
+        // vertical scrolling
+        if ((cursorLocation.row - scrolledLines) < 0)
+            scroll(0, cursorLocation.row - scrolledLines);
+
+        if ((((cursorLocation.row - scrolledLines) + 1)) > textCanvas.getSize().row)
+            scroll(0,
+                    ((((((cursorLocation.row - scrolledLines) + 1) - textCanvas
+                            .getSize().row)))));
+
+        // ensure chat cursor stays within horizontal editor boundaries by
+        // horizontal scrolling
+        if ((cursorLocation.column - scrolledCharacters) < 0)
+            scroll(cursorLocation.column - scrolledCharacters, 0);
+
+        if ((((cursorLocation.column - scrolledCharacters) + 1)) > textCanvas
+                .getSize().column)
+            scroll((((((cursorLocation.column - scrolledCharacters) + 1) - textCanvas
+                    .getSize().column))), 0);
+    }
+
+    /**
+     * Clear text selection.
+     */
+    public void clearSelection() {
+        selectionEnd = new TextPointer(selectionStart);
+        repaintPage = true;
+    }
+
+    /**
+     * Copies selected text to the clipboard.
+     */
+    public void copyToClipboard() {
+        if (selectionStart.compareTo(selectionEnd) == 0)
+            return;
+        // System.out.println("Copy action.");
+        final StringBuilder msg = new StringBuilder();
+
+        ensureSelectionOrder();
+
+        for (int row = selectionStart.row; row <= selectionEnd.row; row++) {
+            final TextLine textLine = page.getLine(row);
+
+            if (row == selectionStart.row) {
+                if (row == selectionEnd.row)
+                    msg.append(textLine.getSubString(selectionStart.column,
+                            selectionEnd.column + 1));
+                else
+                    msg.append(textLine.getSubString(selectionStart.column,
+                            textLine.getLength()));
+            } else {
+                msg.append('\n');
+                if (row == selectionEnd.row)
+                    msg.append(textLine
+                            .getSubString(0, selectionEnd.column + 1));
+                else
+                    msg.append(textLine.toString());
+            }
+        }
+
+        setClipboardContents(msg.toString());
+    }
+
+    public void cutToClipboard() {
+        copyToClipboard();
+        deleteSelection();
+        repaintPage();
+    }
+
+    public void deleteSelection() {
+        ensureSelectionOrder();
+        int ym = 0;
+
+        for (int line = selectionStart.row; line <= selectionEnd.row; line++) {
+            final TextLine currentLine = page.getLine(line - ym);
+
+            if (line == selectionStart.row) {
+                if (line == selectionEnd.row)
+
+                    currentLine.cutSubString(selectionStart.column,
+                            selectionEnd.column);
+                else if (selectionStart.column == 0) {
+                    page.removeLine(line - ym);
+                    ym++;
+                } else
+                    currentLine.cutSubString(selectionStart.column,
+                            currentLine.getLength() + 1);
+            } else if (line == selectionEnd.row)
+                currentLine.cutSubString(0, selectionEnd.column);
+            else {
+                page.removeLine(line - ym);
+                ym++;
+            }
+        }
+
+        clearSelection();
+        cursorLocation = new TextPointer(selectionStart);
+    }
+
+    /**
+     * Ensures that {@link #selectionStart} is smaller than
+     * {@link #selectionEnd}.
+     */
+    public void ensureSelectionOrder() {
+        if (selectionStart.compareTo(selectionEnd) > 0) {
+            final TextPointer temp = selectionEnd;
+            selectionEnd = selectionStart;
+            selectionStart = temp;
+        }
+    }
+
+    public String getClipboardContents() {
+        String result = "";
+        final Clipboard clipboard = Toolkit.getDefaultToolkit()
+                .getSystemClipboard();
+        // odd: the Object param of getContents is not currently used
+        final Transferable contents = clipboard.getContents(null);
+        final boolean hasTransferableText = (contents != null)
+                && contents.isDataFlavorSupported(DataFlavor.stringFlavor);
+        if (hasTransferableText)
+            try {
+                result = (String) contents
+                        .getTransferData(DataFlavor.stringFlavor);
+            } catch (final UnsupportedFlavorException | IOException ex) {
+                // highly unlikely since we are using a standard DataFlavor
+                System.out.println(ex);
+            }
+        // System.out.println(result);
+        return result;
+    }
+
+    /**
+     * Place string into system clipboard so that it can be pasted into other
+     * applications.
+     */
+    public void setClipboardContents(final String contents) {
+        final StringSelection stringSelection = new StringSelection(contents);
+        final Clipboard clipboard = Toolkit.getDefaultToolkit()
+                .getSystemClipboard();
+        clipboard.setContents(stringSelection, stringSelection);
+    }
+
+    public void goToLine(final int Line) {
+        // markNavigationLocation(Line);
+        scrolledLines = Line + 1;
+        cursorLocation.row = Line + 1;
+        cursorLocation.column = 0;
+        repaintPage();
+    }
+
+    public void insertText(final String txt) {
+        if (txt == null)
+            return;
+
+        for (final char c : txt.toCharArray()) {
+
+            if (c == KeyboardHelper.DEL) {
+                processDel();
+                continue;
+            }
+
+            if (c == KeyboardHelper.ENTER) {
+                processEnter();
+                continue;
+            }
+
+            if (c == KeyboardHelper.BACKSPACE) {
+                processBackspace();
+                continue;
+            }
+
+            // type character
+            if (KeyboardHelper.isText(c)) {
+                page.insertCharacter(cursorLocation.row, cursorLocation.column,
+                        c);
+                cursorLocation.column++;
+            }
+        }
+    }
+
+    /**
+     * Parse key presses.
+     */
+    @Override
+    public void keyPressed(final KeyEvent event, final ViewContext viewContext) {
+        super.keyPressed(event, viewContext);
+
+        processKeyEvent(event);
+
+        markRowDirty();
+
+        checkCursorBoundaries();
+
+        repaintWhatNeeded();
+    }
+
+    /**
+     * Empty implementation of the ClipboardOwner interface.
+     */
+    @Override
+    public void lostOwnership(final Clipboard aClipboard,
+                              final Transferable aContents) {
+        // do nothing
+    }
+
+    public void markRowDirty() {
+        dirtyRows.add(cursorLocation.row);
+    }
+
+    public void pasteFromClipboard() {
+        insertText(getClipboardContents());
+    }
+
+    private void processBackspace() {
+        if (selectionStart.compareTo(selectionEnd) == 0) {
+            // erase single character
+            if (cursorLocation.column > 0) {
+                cursorLocation.column--;
+                page.removeCharacter(cursorLocation.row, cursorLocation.column);
+                // System.out.println(lines.get(currentCursor.line).toString());
+            } else if (cursorLocation.row > 0) {
+                cursorLocation.row--;
+                final int currentLineLength = page
+                        .getLineLength(cursorLocation.row);
+                cursorLocation.column = currentLineLength;
+                page.getLine(cursorLocation.row)
+                        .insertTextLine(currentLineLength,
+                                page.getLine(cursorLocation.row + 1));
+                page.removeLine(cursorLocation.row + 1);
+                repaintPage = true;
+            }
+        } else {
+            // dedent multiple lines
+            ensureSelectionOrder();
+            // scan if enough space exists
+            for (int y = selectionStart.row; y < selectionEnd.row; y++)
+                if (page.getLine(y).getIdent() < 4)
+                    return;
+
+            for (int y = selectionStart.row; y < selectionEnd.row; y++)
+                page.getLine(y).cutFromBeginning(4);
+
+            repaintPage = true;
+        }
+    }
+
+    private void processCtrlCombinations(final int keyCode) {
+
+        if ((char) keyCode == 'A') { // CTRL + A -- select all
+            final int lastLineIndex = page.getLinesCount() - 1;
+            selectionStart = new TextPointer(0, 0);
+            selectionEnd = new TextPointer(lastLineIndex,
+                    page.getLineLength(lastLineIndex));
+            repaintPage();
+        }
+
+        // CTRL + X -- cut
+        if ((char) keyCode == 'X')
+            cutToClipboard();
+
+        // CTRL + C -- copy
+        if ((char) keyCode == 'C')
+            copyToClipboard();
+
+        // CTRL + V -- paste
+        if ((char) keyCode == 'V')
+            pasteFromClipboard();
+
+        if (keyCode == 39) { // RIGHT
+            // skip to the beginning of the next word
+
+            for (int x = cursorLocation.column; x < (page
+                    .getLineLength(cursorLocation.row) - 1); x++)
+                if ((page.getChar(cursorLocation.row, x) == ' ')
+                        && (page.getChar(cursorLocation.row, x + 1) != ' ')) {
+                    // beginning of the next word is found
+                    cursorLocation.column = x + 1;
+                    return;
+                }
+
+            cursorLocation.column = page.getLineLength(cursorLocation.row);
+            return;
+        }
+
+        if (keyCode == 37) { // Left
+
+            // skip to the beginning of the previous word
+            for (int x = cursorLocation.column - 2; x >= 0; x--)
+                if ((page.getChar(cursorLocation.row, x) == ' ')
+                        & (page.getChar(cursorLocation.row, x + 1) != ' ')) {
+                    cursorLocation.column = x + 1;
+                    return;
+                }
+
+            cursorLocation.column = 0;
+            return;
+        }
+    }
+
+    public void processDel() {
+        if (selectionStart.compareTo(selectionEnd) == 0) {
+            // is there still some text right to the cursor ?
+            if (cursorLocation.column < page.getLineLength(cursorLocation.row))
+                page.removeCharacter(cursorLocation.row, cursorLocation.column);
+            else {
+                page.getLine(cursorLocation.row).insertTextLine(
+                        cursorLocation.column,
+                        page.getLine(cursorLocation.row + 1));
+                page.removeLine(cursorLocation.row + 1);
+                repaintPage = true;
+            }
+        } else {
+            deleteSelection();
+            repaintPage = true;
+        }
+    }
+
+    private void processEnter() {
+        final TextLine currentLine = page.getLine(cursorLocation.row);
+        // move everything right to the cursor into new line
+        final TextLine newLine = currentLine.getSubLine(cursorLocation.column,
+                currentLine.getLength());
+        page.insertLine(cursorLocation.row + 1, newLine);
+
+        // trim existing line
+        page.getLine(cursorLocation.row).cutUntilEnd(cursorLocation.column);
+        repaintPage = true;
+
+        cursorLocation.row++;
+        cursorLocation.column = 0;
+    }
+
+    private void processKeyEvent(final KeyEvent event) {
+        final int modifiers = event.getModifiers();
+
+        final int keyCode = event.getKeyCode();
+        final char keyChar = event.getKeyChar();
+
+        // System.out.println("Keycode:" + keyCode s+ ", keychar:" + keyChar);
+
+        if (KeyboardHelper.isAlt(modifiers))
+            return;
+
+        if (KeyboardHelper.isCtrl(modifiers)) {
+            processCtrlCombinations(keyCode);
+            return;
+        }
+
+        if (keyCode == KeyboardHelper.TAB) {
+            processTab(modifiers);
+            return;
+        }
+
+        clearSelection();
+
+        if (KeyboardHelper.isText(keyCode)) {
+            insertText(String.valueOf(keyChar));
+            return;
+        }
+
+        // System.out.println("Co:" + String.valueOf(code) + "  Ch:" +
+        // String.valueOf(keyChar));
+
+        if (KeyboardHelper.isShift(modifiers)) {
+            if (!selecting)
+                attemptSelectionStart:{
+
+                    if (keyChar == 65535)
+                        if (keyCode == 16)
+                            break attemptSelectionStart;
+                    if (((keyChar >= 32) & (keyChar <= 128)) | (keyChar == 10)
+                            | (keyChar == 8) | (keyChar == 9))
+                        break attemptSelectionStart;
+
+                    // System.out.println("Selection started:" + keyChar + " "
+                    // + keyCode);
+
+                    selectionStart = new TextPointer(cursorLocation);
+                    selectionEnd = selectionStart;
+                    selecting = true;
+                    repaintPage();
+                }
+        } else
+            selecting = false;
+
+        if (keyCode == KeyboardHelper.HOME) {
+            cursorLocation.column = 0;
+            return;
+        }
+        if (keyCode == KeyboardHelper.END) {
+            cursorLocation.column = page.getLineLength(cursorLocation.row);
+            return;
+        }
+
+        // process cursor keys
+        if (keyCode == KeyboardHelper.DOWN) {
+            markRowDirty();
+            cursorLocation.row++;
+            return;
+        }
+
+        if (keyCode == KeyboardHelper.UP) {
+            markRowDirty();
+            cursorLocation.row--;
+            return;
+        }
+
+        if (keyCode == KeyboardHelper.RIGHT) {
+            cursorLocation.column++;
+            return;
+        }
+
+        if (keyCode == KeyboardHelper.LEFT) {
+            cursorLocation.column--;
+            return;
+        }
+
+        if (keyCode == KeyboardHelper.PGDOWN) {
+            cursorLocation.row += textCanvas.getSize().row;
+            repaintPage();
+            return;
+        }
+
+        if (keyCode == KeyboardHelper.PGUP) {
+            cursorLocation.row -= textCanvas.getSize().row;
+            repaintPage = true;
+            return;
+        }
+
+    }
+
+    private void processTab(final int modifiers) {
+        if (KeyboardHelper.isShift(modifiers)) {
+            if (selectionStart.compareTo(selectionEnd) != 0) {
+                // dedent multiple lines
+                ensureSelectionOrder();
+
+                identSelection:
+                {
+                    // check that identation is possible
+                    for (int y = selectionStart.row; y < selectionEnd.row; y++) {
+                        final TextLine textLine = page.getLine(y);
+
+                        if (!textLine.isEmpty())
+                            if (textLine.getIdent() < 4)
+                                break identSelection;
+                    }
+
+                    for (int y = selectionStart.row; y < selectionEnd.row; y++)
+                        page.getLine(y).cutFromBeginning(4);
+                }
+            } else {
+                // dedent current line
+                final TextLine textLine = page.getLine(cursorLocation.row);
+
+                if (cursorLocation.column >= 4)
+                    if (textLine.isEmpty())
+                        cursorLocation.column -= 4;
+                    else if (textLine.getIdent() >= 4) {
+                        cursorLocation.column -= 4;
+                        textLine.cutFromBeginning(4);
+                    }
+
+            }
+
+            repaintPage();
+
+        } else if (selectionStart.compareTo(selectionEnd) != 0) {
+            // ident multiple lines
+            ensureSelectionOrder();
+            for (int y = selectionStart.row; y < selectionEnd.row; y++)
+                page.getLine(y).addIdent(4);
+
+            repaintPage();
+        }
+    }
+
+    public void repaintPage() {
+
+        final int chXe = textCanvas.getSize().column + 2;
+        final int chYe = textCanvas.getSize().row + 2;
+
+        for (int cy = 0; cy < chYe; cy++)
+            for (int cx = 0; cx < chXe; cx++) {
+                final boolean isTabMargin = ((cx + scrolledCharacters) % 4) == 0;
+
+                if ((cx == (cursorLocation.column - scrolledCharacters))
+                        & (cy == (cursorLocation.row - scrolledLines))) {
+                    // cursor
+                    textCanvas.setBackgroundColor(colorConfig.cursorBack);
+                    textCanvas.setForegroundColor(colorConfig.cursorText);
+                } else if (new TextPointer(cy + scrolledLines, cx).isBetween(
+                        selectionStart, selectionEnd)) {
+                    // selected text
+                    textCanvas.setBackgroundColor(colorConfig.selectedBack);
+                    textCanvas.setForegroundColor(colorConfig.selectedText);
+                } else {
+                    // normal text
+                    textCanvas.setBackgroundColor(colorConfig.normalBack);
+                    textCanvas.setForegroundColor(colorConfig.normalText);
+
+                    if (isTabMargin)
+                        textCanvas
+                                .setBackgroundColor(colorConfig.tabulatorBack);
+
+                }
+
+                final char charUnderCursor = page.getChar(cy + scrolledLines,
+                        cx + scrolledCharacters);
+
+                textCanvas.putChar(cy, cx, charUnderCursor);
+            }
+
+    }
+
+    public void repaintRow(final int rowNumber) {
+        // TODO: fix this
+        repaintPage();
+    }
+
+    private void repaintWhatNeeded() {
+        if (repaintPage) {
+            dirtyRows.clear();
+            repaintPage();
+            return;
+        }
+
+        dirtyRows.forEach(this::repaintRow);
+        dirtyRows.clear();
+    }
+
+    // public void setCaret(final int x, final int y) {
+    // selecting = false;
+    // cursorLocation.column = (x / characterWidth) + scrolledCharacters;
+    // cursorLocation.row = (y / characterHeight) + scrolledLines;
+    // repaintPage();
+    // }
+
+    /**
+     * Scroll full page to given amount of lines or charancters.
+     */
+    public void scroll(final int charactersToScroll, final int linesToScroll) {
+        scrolledLines += linesToScroll;
+        scrolledCharacters += charactersToScroll;
+
+        if (scrolledLines < 0)
+            scrolledLines = 0;
+
+        if (scrolledCharacters < 0)
+            scrolledCharacters = 0;
+
+        repaintPage = true;
+    }
+
+    public void setText(final String text) {
+        // System.out.println("Set text:" + text);
+        cursorLocation = new TextPointer(0, 0);
+        scrolledCharacters = 0;
+        scrolledLines = 0;
+        selectionStart = new TextPointer(0, 0);
+        selectionEnd = new TextPointer(0, 0);
+        page = new Page();
+        insertText(text);
+        repaintPage();
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/TextLine.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/TextLine.java
new file mode 100755 (executable)
index 0000000..d3cf315
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.gui.textEditorComponent;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TextLine {
+
+    private List<Character> chars = new ArrayList<>();
+
+    public TextLine() {
+    }
+
+    public TextLine(final List<Character> value) {
+        chars = value;
+        pack();
+    }
+
+    public TextLine(final String value) {
+        setValue(value);
+    }
+
+    public void addIdent(final int amount) {
+        if (isEmpty())
+            return;
+
+        for (int i = 0; i < amount; i++)
+            chars.add(0, new Character(' '));
+    }
+
+    public String copySubString(final int from, final int until) {
+        final StringBuilder result = new StringBuilder();
+
+        ensureLength(until);
+
+        for (int i = from; i < until; i++)
+            result.append(chars.remove(from).value);
+
+        pack();
+        return result.toString();
+    }
+
+    public void cutFromBeginning(int charactersToCut) {
+
+        if (charactersToCut > chars.size())
+            charactersToCut = chars.size();
+
+        if (charactersToCut == 0)
+            return;
+
+        chars = chars.subList(charactersToCut, chars.size());
+    }
+
+    public String cutSubString(final int from, final int until) {
+        final StringBuilder result = new StringBuilder();
+
+        final List<Character> reminder = new ArrayList<>();
+
+        ensureLength(until);
+
+        for (int i = 0; i < chars.size(); i++)
+            if ((i >= from) && (i < until))
+                result.append(chars.get(i).value);
+            else
+                reminder.add(chars.get(i));
+
+        chars = reminder;
+
+        pack();
+        return result.toString();
+    }
+
+    public void cutUntilEnd(final int col) {
+        if (col >= chars.size())
+            return;
+
+        chars = chars.subList(0, col);
+    }
+
+    private void ensureLength(final int length) {
+        while (chars.size() < length)
+            chars.add(new Character(' '));
+    }
+
+    public char getCharForLocation(final int col) {
+
+        if (col >= chars.size())
+            return ' ';
+
+        return chars.get(col).value;
+    }
+
+    public List<Character> getChars() {
+        return chars;
+    }
+
+    public int getIdent() {
+        if (isEmpty())
+            return 0;
+
+        for (int i = 0; i < chars.size(); i++)
+            if (!chars.get(i).isEmpty())
+                return i;
+
+        throw new RuntimeException("This code shall never execute");
+    }
+
+    public int getLength() {
+        return chars.size();
+    }
+
+    public TextLine getSubLine(final int from, final int until) {
+        final List<Character> result = new ArrayList<>();
+
+        for (int i = from; i < until; i++) {
+            if (i >= chars.size())
+                break;
+            result.add(chars.get(i));
+        }
+
+        return new TextLine(result);
+    }
+
+    public String getSubString(final int from, final int until) {
+        final StringBuilder result = new StringBuilder();
+
+        for (int i = from; i < until; i++)
+            result.append(getCharForLocation(i));
+
+        return result.toString();
+    }
+
+    public void insertCharacter(final int col, final char value) {
+        ensureLength(col);
+        chars.add(col, new Character(value));
+        pack();
+    }
+
+    public void insertString(final int col, final String value) {
+        ensureLength(col);
+        int i = 0;
+        for (final char c : value.toCharArray()) {
+            chars.add(col + i, new Character(c));
+            i++;
+        }
+        pack();
+    }
+
+    public void insertTextLine(final int col, final TextLine textLine) {
+        ensureLength(col);
+        int i = 0;
+        for (final Character c : textLine.getChars()) {
+            chars.add(col + i, c);
+            i++;
+        }
+        pack();
+    }
+
+    public boolean isEmpty() {
+        return chars.isEmpty();
+    }
+
+    private void pack() {
+        int newLength = 0;
+
+        for (int i = chars.size() - 1; i >= 0; i--)
+            if (!chars.get(i).isEmpty()) {
+                newLength = i + 1;
+                break;
+            }
+
+        if (newLength == chars.size())
+            return;
+
+        chars = chars.subList(0, newLength);
+    }
+
+    public void removeCharacter(final int col) {
+        if (col >= chars.size())
+            return;
+
+        chars.remove(col);
+    }
+
+    public void setValue(final String string) {
+        chars.clear();
+        for (final char c : string.toCharArray())
+            chars.add(new Character(c));
+
+        pack();
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder buffer = new StringBuilder();
+
+        for (final Character character : chars)
+            buffer.append(character.value);
+
+        return buffer.toString();
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/io/Connexion3D.java b/src/main/java/eu/svjatoslav/sixth/e3d/io/Connexion3D.java
new file mode 100644 (file)
index 0000000..20fd41a
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.io;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+
+/**
+ * 3D Connexion mouse adapter.
+ * <p>
+ * Idea is to read Linux device file and interpret resulting numbers.
+ * <p>
+ * TODO: unfinished
+ */
+
+public class Connexion3D {
+
+    public static void main(final String[] args) throws IOException {
+
+        final BufferedReader in = new BufferedReader(new FileReader(
+                "/dev/hidraw4"));
+
+
+        while (true) {
+            System.out.print(in.read() + " ");
+            System.out.println("\n");
+        }
+
+        // in.close();
+
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/OctreeVolume.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/OctreeVolume.java
new file mode 100755 (executable)
index 0000000..4b483e0
--- /dev/null
@@ -0,0 +1,1007 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.octree;
+
+import eu.svjatoslav.sixth.e3d.renderer.octree.raytracer.Ray;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+
+/**
+ * <pre>
+ * There are 3 cell types:
+ *
+ *      UNUSED
+ *
+ *      SOLID
+ *          contains:
+ *              original color
+ *              visible color, after being illuminated by nearby light sources
+ *
+ *      CLUSTER
+ *          contains pointers to 8 sub cells
+ * </pre>
+ */
+
+public class OctreeVolume {
+
+    public static final int TRACE_NO_HIT = -1;
+    /**
+     * Single solid color.
+     */
+    private static final int CELL_STATE_SOLID = -2;
+    private static final int CELL_STATE_UNUSED = -1;
+    final LineAppearance factory = new LineAppearance();
+    public int ce1[];
+    public int ce2[];
+    public int ce3[];
+    public int ce4[];
+    public int ce5[];
+    public int ce6[];
+    public int ce7[];
+    public int ce8[];
+    public int cellAllocationPointer = 0;
+    public int usedCellsCount = 0;
+    public int masterCellSize;
+
+    public OctreeVolume() {
+        initWorld(1500000, 256 * 64);
+    }
+
+    public void breakSolidCell(final int pointer) {
+        final int color = getCellColor(pointer);
+        final int illumination = getCellIllumination(pointer);
+
+        ce1[pointer] = makeNewCell(color, illumination);
+        ce2[pointer] = makeNewCell(color, illumination);
+        ce3[pointer] = makeNewCell(color, illumination);
+        ce4[pointer] = makeNewCell(color, illumination);
+        ce5[pointer] = makeNewCell(color, illumination);
+        ce6[pointer] = makeNewCell(color, illumination);
+        ce7[pointer] = makeNewCell(color, illumination);
+        ce8[pointer] = makeNewCell(color, illumination);
+    }
+
+    public void clearCell(final int pointer) {
+        ce1[pointer] = 0;
+        ce2[pointer] = 0;
+        ce3[pointer] = 0;
+        ce4[pointer] = 0;
+
+        ce5[pointer] = 0;
+        ce6[pointer] = 0;
+        ce7[pointer] = 0;
+        ce8[pointer] = 0;
+    }
+
+    public void deleteCell(final int cellPointer) {
+        clearCell(cellPointer);
+        ce1[cellPointer] = CELL_STATE_UNUSED;
+        usedCellsCount--;
+    }
+
+    public int doesIntersect(final int cubeX, final int cubeY, final int cubeZ,
+                             final int cubeSize, final Ray r) {
+
+        // ray starts inside the cube
+        if ((cubeX - cubeSize) < r.x)
+            if ((cubeX + cubeSize) > r.x)
+                if ((cubeY - cubeSize) < r.y)
+                    if ((cubeY + cubeSize) > r.y)
+                        if ((cubeZ - cubeSize) < r.z)
+                            if ((cubeZ + cubeSize) > r.z) {
+                                r.hitX = r.x;
+                                r.hitY = r.y;
+                                r.hitZ = r.z;
+                                return 1;
+                            }
+        // back face
+        if (r.zp > 0)
+            if ((cubeZ - cubeSize) > r.z) {
+                final double mult = ((cubeZ - cubeSize) - r.z) / r.zp;
+                final double hitX = (r.xp * mult) + r.x;
+                if ((cubeX - cubeSize) < hitX)
+                    if ((cubeX + cubeSize) > hitX) {
+                        final double hitY = (r.yp * mult) + r.y;
+                        if ((cubeY - cubeSize) < hitY)
+                            if ((cubeY + cubeSize) > hitY) {
+                                r.hitX = hitX;
+                                r.hitY = hitY;
+                                r.hitZ = cubeZ - cubeSize;
+                                return 2;
+                            }
+                    }
+            }
+
+        // up face
+        if (r.yp > 0)
+            if ((cubeY - cubeSize) > r.y) {
+                final double mult = ((cubeY - cubeSize) - r.y) / r.yp;
+                final double hitX = (r.xp * mult) + r.x;
+                if ((cubeX - cubeSize) < hitX)
+                    if ((cubeX + cubeSize) > hitX) {
+                        final double hitZ = (r.zp * mult) + r.z;
+                        if ((cubeZ - cubeSize) < hitZ)
+                            if ((cubeZ + cubeSize) > hitZ) {
+                                r.hitX = hitX;
+                                r.hitY = cubeY - cubeSize;
+                                r.hitZ = hitZ;
+                                return 3;
+                            }
+                    }
+            }
+
+        // left face
+        if (r.xp > 0)
+            if ((cubeX - cubeSize) > r.x) {
+                final double mult = ((cubeX - cubeSize) - r.x) / r.xp;
+                final double hitY = (r.yp * mult) + r.y;
+                if ((cubeY - cubeSize) < hitY)
+                    if ((cubeY + cubeSize) > hitY) {
+                        final double hitZ = (r.zp * mult) + r.z;
+                        if ((cubeZ - cubeSize) < hitZ)
+                            if ((cubeZ + cubeSize) > hitZ) {
+                                r.hitX = cubeX - cubeSize;
+                                r.hitY = hitY;
+                                r.hitZ = hitZ;
+                                return 4;
+                            }
+                    }
+            }
+
+        // front face
+        if (r.zp < 0)
+            if ((cubeZ + cubeSize) < r.z) {
+                final double mult = ((cubeZ + cubeSize) - r.z) / r.zp;
+                final double hitX = (r.xp * mult) + r.x;
+                if ((cubeX - cubeSize) < hitX)
+                    if ((cubeX + cubeSize) > hitX) {
+                        final double hitY = (r.yp * mult) + r.y;
+                        if ((cubeY - cubeSize) < hitY)
+                            if ((cubeY + cubeSize) > hitY) {
+                                r.hitX = hitX;
+                                r.hitY = hitY;
+                                r.hitZ = cubeZ + cubeSize;
+                                return 5;
+                            }
+                    }
+            }
+
+        // down face
+        if (r.yp < 0)
+            if ((cubeY + cubeSize) < r.y) {
+                final double mult = ((cubeY + cubeSize) - r.y) / r.yp;
+                final double hitX = (r.xp * mult) + r.x;
+                if ((cubeX - cubeSize) < hitX)
+                    if ((cubeX + cubeSize) > hitX) {
+                        final double hitZ = (r.zp * mult) + r.z;
+                        if ((cubeZ - cubeSize) < hitZ)
+                            if ((cubeZ + cubeSize) > hitZ) {
+                                r.hitX = hitX;
+                                r.hitY = cubeY + cubeSize;
+                                r.hitZ = hitZ;
+                                return 6;
+                            }
+                    }
+            }
+
+        // right face
+        if (r.xp < 0)
+            if ((cubeX + cubeSize) < r.x) {
+                final double mult = ((cubeX + cubeSize) - r.x) / r.xp;
+                final double hitY = (r.yp * mult) + r.y;
+                if ((cubeY - cubeSize) < hitY)
+                    if ((cubeY + cubeSize) > hitY) {
+                        final double hitZ = (r.zp * mult) + r.z;
+                        if ((cubeZ - cubeSize) < hitZ)
+                            if ((cubeZ + cubeSize) > hitZ) {
+                                r.hitX = cubeX + cubeSize;
+                                r.hitY = hitY;
+                                r.hitZ = hitZ;
+                                return 7;
+                            }
+                    }
+            }
+        return 0;
+    }
+
+    /**
+     * Fill 3D rectangle.
+     */
+    public void fillRect3D(int x1, int y1, int z1, int x2, int y2, int z2,
+                           final Color color) {
+        int t;
+        if (x1 > x2) {
+            t = x1;
+            x1 = x2;
+            x2 = t;
+        }
+
+        if (y1 > y2) {
+            t = y1;
+            y1 = y2;
+            y2 = t;
+        }
+
+        if (z1 > z2) {
+            t = z1;
+            z1 = z2;
+            z2 = t;
+        }
+
+        for (int x = x1; x <= x2; x++)
+            for (int y = y1; y <= y2; y++)
+                for (int z = z1; z <= z2; z++)
+                    putCell(x, y, z, 0, 0, 0, masterCellSize, 0, color);
+
+    }
+
+    public int getCellColor(final int pointer) {
+        return ce2[pointer];
+    }
+
+    public int getCellIllumination(final int pointer) {
+        return ce3[pointer];
+    }
+
+    public void initWorld(final int bufferLength, final int masterCellSize) {
+        // System.out.println("Initializing new world");
+
+        // initialize world storage buffer
+        this.masterCellSize = masterCellSize;
+
+        ce1 = new int[bufferLength];
+        ce2 = new int[bufferLength];
+        ce3 = new int[bufferLength];
+        ce4 = new int[bufferLength];
+
+        ce5 = new int[bufferLength];
+        ce6 = new int[bufferLength];
+        ce7 = new int[bufferLength];
+        ce8 = new int[bufferLength];
+
+        for (int i = 0; i < bufferLength; i++)
+            ce1[i] = CELL_STATE_UNUSED;
+
+        // initialize master cell
+        clearCell(0);
+    }
+
+    public boolean isCellSolid(final int pointer) {
+        return ce1[pointer] == CELL_STATE_SOLID;
+    }
+
+    public int makeNewCell() {
+        for (; ; ) {
+            if (cellAllocationPointer >= ce1.length)
+                cellAllocationPointer = 0;
+
+            if (ce1[cellAllocationPointer] == CELL_STATE_UNUSED) {
+
+                clearCell(cellAllocationPointer);
+
+                usedCellsCount++;
+                return cellAllocationPointer;
+            } else
+                cellAllocationPointer++;
+        }
+    }
+
+    public int makeNewCell(final int color, final int illumination) {
+        final int pointer = makeNewCell();
+        markCellAsSolid(pointer);
+        setCellColor(pointer, color);
+        setCellIllumination(pointer, illumination);
+        return pointer;
+    }
+
+    public void markCellAsSolid(final int pointer) {
+        ce1[pointer] = CELL_STATE_SOLID;
+    }
+
+    public void putCell(final int x, final int y, final int z, final Color color) {
+        putCell(x, y, z, 0, 0, 0, masterCellSize, 0, color);
+    }
+
+    private void putCell(final int x, final int y, final int z,
+                         final int cellX, final int cellY, final int cellZ,
+                         final int cellSize, final int cellPointer, final Color color) {
+
+        if (cellSize > 1) {
+
+            // if case of big cell
+            if (isCellSolid(cellPointer)) {
+
+                // if cell is already a needed color, do notheing
+                if (getCellColor(cellPointer) == color.toInt())
+                    return;
+
+                // otherwise break cell up
+                breakSolidCell(cellPointer);
+
+                // continue, as if it is cluster now
+            }
+
+            // decide witch subcube to use
+            int subCubeArray[];
+            int subX, subY, subZ;
+
+            if (x > cellX) {
+                subX = (cellSize / 2) + cellX;
+                if (y > cellY) {
+                    subY = (cellSize / 2) + cellY;
+                    if (z > cellZ) {
+                        subZ = (cellSize / 2) + cellZ;
+                        // 7
+                        subCubeArray = ce7;
+                    } else {
+                        subZ = (-cellSize / 2) + cellZ;
+                        // 3
+                        subCubeArray = ce3;
+                    }
+                } else {
+                    subY = (-cellSize / 2) + cellY;
+                    if (z > cellZ) {
+                        subZ = (cellSize / 2) + cellZ;
+                        // 6
+                        subCubeArray = ce6;
+                    } else {
+                        subZ = (-cellSize / 2) + cellZ;
+                        // 2
+                        subCubeArray = ce2;
+                    }
+                }
+            } else {
+                subX = (-cellSize / 2) + cellX;
+                if (y > cellY) {
+                    subY = (cellSize / 2) + cellY;
+                    if (z > cellZ) {
+                        subZ = (cellSize / 2) + cellZ;
+                        // 8
+                        subCubeArray = ce8;
+                    } else {
+                        subZ = (-cellSize / 2) + cellZ;
+                        // 4
+                        subCubeArray = ce4;
+                    }
+                } else {
+                    subY = (-cellSize / 2) + cellY;
+                    if (z > cellZ) {
+                        subZ = (cellSize / 2) + cellZ;
+                        // 5
+                        subCubeArray = ce5;
+                    } else {
+                        subZ = (-cellSize / 2) + cellZ;
+                        // 1
+                        subCubeArray = ce1;
+                    }
+                }
+            }
+
+            int subCubePointer;
+            if (subCubeArray[cellPointer] == 0) {
+                // create empty cluster
+                subCubePointer = makeNewCell();
+                subCubeArray[cellPointer] = subCubePointer;
+            } else
+                subCubePointer = subCubeArray[cellPointer];
+
+            putCell(x, y, z, subX, subY, subZ, cellSize / 2, subCubePointer,
+                    color);
+        } else {
+            ce1[cellPointer] = CELL_STATE_SOLID;
+            ce2[cellPointer] = color.toInt();
+            ce3[cellPointer] = CELL_STATE_UNUSED;
+            // System.out.println("Cell written!");
+        }
+    }
+
+    public void setCellColor(final int pointer, final int color) {
+        ce2[pointer] = color;
+    }
+
+    public void setCellIllumination(final int pointer, final int illumination) {
+        ce3[pointer] = illumination;
+    }
+
+    /**
+     * @return intersecting cell pointer or -1 if no cell in intersecting this
+     * ray.
+     */
+    public int traceCell(final int cellX, final int cellY, final int cellZ,
+                         final int cellSize, final int pointer, final Ray ray) {
+        if (isCellSolid(pointer)) {
+            // solid cell
+            if (doesIntersect(cellX, cellY, cellZ, cellSize, ray) != 0) {
+                ray.hitCellSize = cellSize;
+                ray.hitCellX = cellX;
+                ray.hitCellY = cellY;
+                ray.hitCellZ = cellZ;
+                return pointer;
+            }
+            return TRACE_NO_HIT;
+        } else // cluster
+            if (doesIntersect(cellX, cellY, cellZ, cellSize, ray) != 0) {
+                final int halfOfCellSize = cellSize / 2;
+                int rayIntersectionResult;
+
+                if (ray.x > cellX) {
+                    if (ray.y > cellY) {
+                        if (ray.z > cellZ) {
+                            // 7
+                            // 6 8 3 5 2 4 1
+
+                            if (ce7[pointer] != 0) {
+                                rayIntersectionResult = traceCell(cellX
+                                                + halfOfCellSize, cellY + halfOfCellSize,
+                                        cellZ + halfOfCellSize, halfOfCellSize,
+                                        ce7[pointer], ray);
+                                if (rayIntersectionResult >= 0)
+                                    return rayIntersectionResult;
+                            }
+                            if (ce6[pointer] != 0) {
+                                rayIntersectionResult = traceCell(cellX
+                                                + halfOfCellSize, cellY - halfOfCellSize,
+                                        cellZ + halfOfCellSize, halfOfCellSize,
+                                        ce6[pointer], ray);
+                                if (rayIntersectionResult >= 0)
+                                    return rayIntersectionResult;
+                            }
+                            if (ce8[pointer] != 0) {
+                                rayIntersectionResult = traceCell(cellX
+                                                - halfOfCellSize, cellY + halfOfCellSize,
+                                        cellZ + halfOfCellSize, halfOfCellSize,
+                                        ce8[pointer], ray);
+                                if (rayIntersectionResult >= 0)
+                                    return rayIntersectionResult;
+                            }
+                            if (ce3[pointer] != 0) {
+                                rayIntersectionResult = traceCell(cellX
+                                                + halfOfCellSize, cellY + halfOfCellSize,
+                                        cellZ - halfOfCellSize, halfOfCellSize,
+                                        ce3[pointer], ray);
+                                if (rayIntersectionResult >= 0)
+                                    return rayIntersectionResult;
+                            }
+
+                            if (ce2[pointer] != 0) {
+                                rayIntersectionResult = traceCell(cellX
+                                                + halfOfCellSize, cellY - halfOfCellSize,
+                                        cellZ - halfOfCellSize, halfOfCellSize,
+                                        ce2[pointer], ray);
+                                if (rayIntersectionResult >= 0)
+                                    return rayIntersectionResult;
+                            }
+                            if (ce4[pointer] != 0) {
+                                rayIntersectionResult = traceCell(cellX
+                                                - halfOfCellSize, cellY + halfOfCellSize,
+                                        cellZ - halfOfCellSize, halfOfCellSize,
+                                        ce4[pointer], ray);
+                                if (rayIntersectionResult >= 0)
+                                    return rayIntersectionResult;
+                            }
+
+                            if (ce5[pointer] != 0) {
+                                rayIntersectionResult = traceCell(cellX
+                                                - halfOfCellSize, cellY - halfOfCellSize,
+                                        cellZ + halfOfCellSize, halfOfCellSize,
+                                        ce5[pointer], ray);
+                                if (rayIntersectionResult >= 0)
+                                    return rayIntersectionResult;
+                            }
+
+                            if (ce1[pointer] != 0) {
+                                rayIntersectionResult = traceCell(cellX
+                                                - halfOfCellSize, cellY - halfOfCellSize,
+                                        cellZ - halfOfCellSize, halfOfCellSize,
+                                        ce1[pointer], ray);
+                                if (rayIntersectionResult >= 0)
+                                    return rayIntersectionResult;
+                            }
+
+                        } else {
+                            // 3
+                            // 2 4 7 1 6 8 5
+                            if (ce3[pointer] != 0) {
+                                rayIntersectionResult = traceCell(cellX
+                                                + halfOfCellSize, cellY + halfOfCellSize,
+                                        cellZ - halfOfCellSize, halfOfCellSize,
+                                        ce3[pointer], ray);
+                                if (rayIntersectionResult >= 0)
+                                    return rayIntersectionResult;
+                            }
+
+                            if (ce2[pointer] != 0) {
+                                rayIntersectionResult = traceCell(cellX
+                                                + halfOfCellSize, cellY - halfOfCellSize,
+                                        cellZ - halfOfCellSize, halfOfCellSize,
+                                        ce2[pointer], ray);
+                                if (rayIntersectionResult >= 0)
+                                    return rayIntersectionResult;
+                            }
+                            if (ce4[pointer] != 0) {
+                                rayIntersectionResult = traceCell(cellX
+                                                - halfOfCellSize, cellY + halfOfCellSize,
+                                        cellZ - halfOfCellSize, halfOfCellSize,
+                                        ce4[pointer], ray);
+                                if (rayIntersectionResult >= 0)
+                                    return rayIntersectionResult;
+                            }
+
+                            if (ce7[pointer] != 0) {
+                                rayIntersectionResult = traceCell(cellX
+                                                + halfOfCellSize, cellY + halfOfCellSize,
+                                        cellZ + halfOfCellSize, halfOfCellSize,
+                                        ce7[pointer], ray);
+                                if (rayIntersectionResult >= 0)
+                                    return rayIntersectionResult;
+                            }
+                            if (ce6[pointer] != 0) {
+                                rayIntersectionResult = traceCell(cellX
+                                                + halfOfCellSize, cellY - halfOfCellSize,
+                                        cellZ + halfOfCellSize, halfOfCellSize,
+                                        ce6[pointer], ray);
+                                if (rayIntersectionResult >= 0)
+                                    return rayIntersectionResult;
+                            }
+                            if (ce8[pointer] != 0) {
+                                rayIntersectionResult = traceCell(cellX
+                                                - halfOfCellSize, cellY + halfOfCellSize,
+                                        cellZ + halfOfCellSize, halfOfCellSize,
+                                        ce8[pointer], ray);
+                                if (rayIntersectionResult >= 0)
+                                    return rayIntersectionResult;
+                            }
+
+                            if (ce1[pointer] != 0) {
+                                rayIntersectionResult = traceCell(cellX
+                                                - halfOfCellSize, cellY - halfOfCellSize,
+                                        cellZ - halfOfCellSize, halfOfCellSize,
+                                        ce1[pointer], ray);
+                                if (rayIntersectionResult >= 0)
+                                    return rayIntersectionResult;
+                            }
+
+                            if (ce5[pointer] != 0) {
+                                rayIntersectionResult = traceCell(cellX
+                                                - halfOfCellSize, cellY - halfOfCellSize,
+                                        cellZ + halfOfCellSize, halfOfCellSize,
+                                        ce5[pointer], ray);
+                                if (rayIntersectionResult >= 0)
+                                    return rayIntersectionResult;
+                            }
+
+                        }
+                    } else if (ray.z > cellZ) {
+                        // 6
+                        // 5 2 7 8 1 3 4
+                        if (ce6[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            + halfOfCellSize, cellY - halfOfCellSize, cellZ
+                                            + halfOfCellSize, halfOfCellSize, ce6[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+
+                        if (ce7[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            + halfOfCellSize, cellY + halfOfCellSize, cellZ
+                                            + halfOfCellSize, halfOfCellSize, ce7[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+                        if (ce2[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            + halfOfCellSize, cellY - halfOfCellSize, cellZ
+                                            - halfOfCellSize, halfOfCellSize, ce2[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+                        if (ce5[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            - halfOfCellSize, cellY - halfOfCellSize, cellZ
+                                            + halfOfCellSize, halfOfCellSize, ce5[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+                        if (ce8[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            - halfOfCellSize, cellY + halfOfCellSize, cellZ
+                                            + halfOfCellSize, halfOfCellSize, ce8[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+                        if (ce3[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            + halfOfCellSize, cellY + halfOfCellSize, cellZ
+                                            - halfOfCellSize, halfOfCellSize, ce3[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+
+                        if (ce1[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            - halfOfCellSize, cellY - halfOfCellSize, cellZ
+                                            - halfOfCellSize, halfOfCellSize, ce1[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+                        if (ce4[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            - halfOfCellSize, cellY + halfOfCellSize, cellZ
+                                            - halfOfCellSize, halfOfCellSize, ce4[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+
+                    } else {
+                        // 2
+                        // 1 3 6 5 4 7 8
+                        if (ce2[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            + halfOfCellSize, cellY - halfOfCellSize, cellZ
+                                            - halfOfCellSize, halfOfCellSize, ce2[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+
+                        if (ce3[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            + halfOfCellSize, cellY + halfOfCellSize, cellZ
+                                            - halfOfCellSize, halfOfCellSize, ce3[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+
+                        if (ce1[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            - halfOfCellSize, cellY - halfOfCellSize, cellZ
+                                            - halfOfCellSize, halfOfCellSize, ce1[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+                        if (ce6[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            + halfOfCellSize, cellY - halfOfCellSize, cellZ
+                                            + halfOfCellSize, halfOfCellSize, ce6[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+
+                        if (ce7[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            + halfOfCellSize, cellY + halfOfCellSize, cellZ
+                                            + halfOfCellSize, halfOfCellSize, ce7[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+                        if (ce5[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            - halfOfCellSize, cellY - halfOfCellSize, cellZ
+                                            + halfOfCellSize, halfOfCellSize, ce5[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+                        if (ce4[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            - halfOfCellSize, cellY + halfOfCellSize, cellZ
+                                            - halfOfCellSize, halfOfCellSize, ce4[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+                        if (ce8[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            - halfOfCellSize, cellY + halfOfCellSize, cellZ
+                                            + halfOfCellSize, halfOfCellSize, ce8[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+
+                    }
+                } else if (ray.y > cellY) {
+                    if (ray.z > cellZ) {
+                        // 8
+                        // 5 7 4 1 6 3 2
+
+                        if (ce8[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            - halfOfCellSize, cellY + halfOfCellSize, cellZ
+                                            + halfOfCellSize, halfOfCellSize, ce8[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+                        if (ce7[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            + halfOfCellSize, cellY + halfOfCellSize, cellZ
+                                            + halfOfCellSize, halfOfCellSize, ce7[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+                        if (ce5[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            - halfOfCellSize, cellY - halfOfCellSize, cellZ
+                                            + halfOfCellSize, halfOfCellSize, ce5[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+                        if (ce4[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            - halfOfCellSize, cellY + halfOfCellSize, cellZ
+                                            - halfOfCellSize, halfOfCellSize, ce4[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+
+                        if (ce3[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            + halfOfCellSize, cellY + halfOfCellSize, cellZ
+                                            - halfOfCellSize, halfOfCellSize, ce3[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+
+                        if (ce1[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            - halfOfCellSize, cellY - halfOfCellSize, cellZ
+                                            - halfOfCellSize, halfOfCellSize, ce1[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+                        if (ce6[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            + halfOfCellSize, cellY - halfOfCellSize, cellZ
+                                            + halfOfCellSize, halfOfCellSize, ce6[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+
+                        if (ce2[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            + halfOfCellSize, cellY - halfOfCellSize, cellZ
+                                            - halfOfCellSize, halfOfCellSize, ce2[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+
+                    } else {
+                        // 4
+                        // 1 3 8 5 7 2 6
+
+                        if (ce4[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            - halfOfCellSize, cellY + halfOfCellSize, cellZ
+                                            - halfOfCellSize, halfOfCellSize, ce4[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+
+                        if (ce8[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            - halfOfCellSize, cellY + halfOfCellSize, cellZ
+                                            + halfOfCellSize, halfOfCellSize, ce8[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+                        if (ce3[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            + halfOfCellSize, cellY + halfOfCellSize, cellZ
+                                            - halfOfCellSize, halfOfCellSize, ce3[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+
+                        if (ce1[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            - halfOfCellSize, cellY - halfOfCellSize, cellZ
+                                            - halfOfCellSize, halfOfCellSize, ce1[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+                        if (ce7[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            + halfOfCellSize, cellY + halfOfCellSize, cellZ
+                                            + halfOfCellSize, halfOfCellSize, ce7[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+                        if (ce5[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            - halfOfCellSize, cellY - halfOfCellSize, cellZ
+                                            + halfOfCellSize, halfOfCellSize, ce5[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+
+                        if (ce2[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            + halfOfCellSize, cellY - halfOfCellSize, cellZ
+                                            - halfOfCellSize, halfOfCellSize, ce2[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+                        if (ce6[pointer] != 0) {
+                            rayIntersectionResult = traceCell(cellX
+                                            + halfOfCellSize, cellY - halfOfCellSize, cellZ
+                                            + halfOfCellSize, halfOfCellSize, ce6[pointer],
+                                    ray);
+                            if (rayIntersectionResult >= 0)
+                                return rayIntersectionResult;
+                        }
+
+                    }
+                } else if (ray.z > cellZ) {
+                    // 5
+                    // 1 6 8 4 2 7 3
+
+                    if (ce5[pointer] != 0) {
+                        rayIntersectionResult = traceCell(cellX - halfOfCellSize,
+                                cellY - halfOfCellSize, cellZ + halfOfCellSize,
+                                halfOfCellSize, ce5[pointer], ray);
+                        if (rayIntersectionResult >= 0)
+                            return rayIntersectionResult;
+                    }
+
+                    if (ce1[pointer] != 0) {
+                        rayIntersectionResult = traceCell(cellX - halfOfCellSize,
+                                cellY - halfOfCellSize, cellZ - halfOfCellSize,
+                                halfOfCellSize, ce1[pointer], ray);
+                        if (rayIntersectionResult >= 0)
+                            return rayIntersectionResult;
+                    }
+
+                    if (ce6[pointer] != 0) {
+                        rayIntersectionResult = traceCell(cellX + halfOfCellSize,
+                                cellY - halfOfCellSize, cellZ + halfOfCellSize,
+                                halfOfCellSize, ce6[pointer], ray);
+                        if (rayIntersectionResult >= 0)
+                            return rayIntersectionResult;
+                    }
+
+                    if (ce8[pointer] != 0) {
+                        rayIntersectionResult = traceCell(cellX - halfOfCellSize,
+                                cellY + halfOfCellSize, cellZ + halfOfCellSize,
+                                halfOfCellSize, ce8[pointer], ray);
+                        if (rayIntersectionResult >= 0)
+                            return rayIntersectionResult;
+                    }
+
+                    if (ce4[pointer] != 0) {
+                        rayIntersectionResult = traceCell(cellX - halfOfCellSize,
+                                cellY + halfOfCellSize, cellZ - halfOfCellSize,
+                                halfOfCellSize, ce4[pointer], ray);
+                        if (rayIntersectionResult >= 0)
+                            return rayIntersectionResult;
+                    }
+
+                    if (ce7[pointer] != 0) {
+                        rayIntersectionResult = traceCell(cellX + halfOfCellSize,
+                                cellY + halfOfCellSize, cellZ + halfOfCellSize,
+                                halfOfCellSize, ce7[pointer], ray);
+                        if (rayIntersectionResult >= 0)
+                            return rayIntersectionResult;
+                    }
+
+                    if (ce2[pointer] != 0) {
+                        rayIntersectionResult = traceCell(cellX + halfOfCellSize,
+                                cellY - halfOfCellSize, cellZ - halfOfCellSize,
+                                halfOfCellSize, ce2[pointer], ray);
+                        if (rayIntersectionResult >= 0)
+                            return rayIntersectionResult;
+                    }
+
+                    if (ce3[pointer] != 0) {
+                        rayIntersectionResult = traceCell(cellX + halfOfCellSize,
+                                cellY + halfOfCellSize, cellZ - halfOfCellSize,
+                                halfOfCellSize, ce3[pointer], ray);
+                        if (rayIntersectionResult >= 0)
+                            return rayIntersectionResult;
+                    }
+
+                } else {
+                    // 1
+                    // 5 2 4 8 6 3 7
+
+                    if (ce1[pointer] != 0) {
+                        rayIntersectionResult = traceCell(cellX - halfOfCellSize,
+                                cellY - halfOfCellSize, cellZ - halfOfCellSize,
+                                halfOfCellSize, ce1[pointer], ray);
+                        if (rayIntersectionResult >= 0)
+                            return rayIntersectionResult;
+                    }
+
+                    if (ce5[pointer] != 0) {
+                        rayIntersectionResult = traceCell(cellX - halfOfCellSize,
+                                cellY - halfOfCellSize, cellZ + halfOfCellSize,
+                                halfOfCellSize, ce5[pointer], ray);
+                        if (rayIntersectionResult >= 0)
+                            return rayIntersectionResult;
+                    }
+                    if (ce2[pointer] != 0) {
+                        rayIntersectionResult = traceCell(cellX + halfOfCellSize,
+                                cellY - halfOfCellSize, cellZ - halfOfCellSize,
+                                halfOfCellSize, ce2[pointer], ray);
+                        if (rayIntersectionResult >= 0)
+                            return rayIntersectionResult;
+                    }
+
+                    if (ce4[pointer] != 0) {
+                        rayIntersectionResult = traceCell(cellX - halfOfCellSize,
+                                cellY + halfOfCellSize, cellZ - halfOfCellSize,
+                                halfOfCellSize, ce4[pointer], ray);
+                        if (rayIntersectionResult >= 0)
+                            return rayIntersectionResult;
+                    }
+
+                    if (ce6[pointer] != 0) {
+                        rayIntersectionResult = traceCell(cellX + halfOfCellSize,
+                                cellY - halfOfCellSize, cellZ + halfOfCellSize,
+                                halfOfCellSize, ce6[pointer], ray);
+                        if (rayIntersectionResult >= 0)
+                            return rayIntersectionResult;
+                    }
+
+                    if (ce8[pointer] != 0) {
+                        rayIntersectionResult = traceCell(cellX - halfOfCellSize,
+                                cellY + halfOfCellSize, cellZ + halfOfCellSize,
+                                halfOfCellSize, ce8[pointer], ray);
+                        if (rayIntersectionResult >= 0)
+                            return rayIntersectionResult;
+                    }
+
+                    if (ce3[pointer] != 0) {
+                        rayIntersectionResult = traceCell(cellX + halfOfCellSize,
+                                cellY + halfOfCellSize, cellZ - halfOfCellSize,
+                                halfOfCellSize, ce3[pointer], ray);
+                        if (rayIntersectionResult >= 0)
+                            return rayIntersectionResult;
+                    }
+                    if (ce7[pointer] != 0) {
+                        rayIntersectionResult = traceCell(cellX + halfOfCellSize,
+                                cellY + halfOfCellSize, cellZ + halfOfCellSize,
+                                halfOfCellSize, ce7[pointer], ray);
+                        if (rayIntersectionResult >= 0)
+                            return rayIntersectionResult;
+                    }
+                }
+            }
+        return TRACE_NO_HIT;
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/package-info.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/package-info.java
new file mode 100755 (executable)
index 0000000..7494166
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.octree;
+
+/**
+ * Realtime voxel renderer (in software).
+ * Uses octree for data compression.
+ */
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/Camera.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/Camera.java
new file mode 100755 (executable)
index 0000000..07a7909
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.octree.raytracer;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.geometry.Transform;
+import eu.svjatoslav.sixth.e3d.gui.Avatar;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.TexturedRectangle;
+import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.net.URL;
+
+public class Camera extends TexturedRectangle {
+
+    public static final int CAMERA_SIZE = 100;
+    public static final int IMAGE_SIZE = 500;
+    private final CameraView cameraView;
+    private Point3D camCenter;
+
+    public Camera(final Avatar avatar, final double zoom) {
+        super(new Transform(avatar.getLocation().clone()));
+        cameraView = new CameraView(avatar, zoom);
+
+        computeCameraCoordinates(avatar);
+
+        addWaitNotification(getTexture());
+    }
+
+    private void addWaitNotification(final Texture texture) {
+        // add hourglass icon
+        try {
+            final BufferedImage sprite = getSprite("eu/svjatoslav/sixth/e3d/examples/hourglass.png");
+            texture.graphics.drawImage(sprite, IMAGE_SIZE / 2,
+                    (IMAGE_SIZE / 2) - 30, null);
+        } catch (final Exception ignored) {
+        }
+
+        // add "Please wait..." message
+        texture.graphics.setColor(java.awt.Color.WHITE);
+        texture.graphics.setFont(new Font("Monospaced", Font.PLAIN, 10));
+        texture.graphics.drawString("Please wait...", (IMAGE_SIZE / 2) - 20,
+                (IMAGE_SIZE / 2) + 30);
+    }
+
+    private void computeCameraCoordinates(final Avatar avatar) {
+        initialize(CAMERA_SIZE, CAMERA_SIZE, IMAGE_SIZE, IMAGE_SIZE, 3);
+
+        camCenter = Point3D.ZERO.clone();
+
+        topLeft.setValues(camCenter.x, camCenter.y, camCenter.z + CAMERA_SIZE);
+
+        topRight.clone(topLeft);
+        bottomLeft.clone(topLeft);
+        bottomRight.clone(topLeft);
+
+        final float viewAngle = (float) .6;
+
+        topLeft.rotate(camCenter, -viewAngle, -viewAngle);
+        topRight.rotate(camCenter, viewAngle, -viewAngle);
+        bottomLeft.rotate(camCenter, -viewAngle, viewAngle);
+        bottomRight.rotate(camCenter, viewAngle, viewAngle);
+
+        topLeft.rotate(camCenter, -avatar.getAngleXZ(), -avatar.getAngleYZ());
+        topRight.rotate(camCenter, -avatar.getAngleXZ(), -avatar.getAngleYZ());
+        bottomLeft
+                .rotate(camCenter, -avatar.getAngleXZ(), -avatar.getAngleYZ());
+        bottomRight.rotate(camCenter, -avatar.getAngleXZ(),
+                -avatar.getAngleYZ());
+
+        final Color cameraColor = new Color(255, 255, 0, 255);
+        final LineAppearance appearance = new LineAppearance(2, cameraColor);
+
+        // addShape(appearance.getLine(camCenter, upLeft));
+        // addShape(appearance.getLine(camCenter, upRight));
+        // addShape(appearance.getLine(camCenter, downLeft));
+        // addShape(appearance.getLine(camCenter, downRight));
+
+        addShape(appearance.getLine(topLeft, topRight));
+        addShape(appearance.getLine(bottomLeft, bottomRight));
+        addShape(appearance.getLine(topLeft, bottomLeft));
+        addShape(appearance.getLine(topRight, bottomRight));
+
+    }
+
+    public CameraView getCameraView() {
+        return cameraView;
+    }
+
+    public BufferedImage getSprite(final String ref) throws IOException {
+        final URL url = this.getClass().getClassLoader().getResource(ref);
+        return ImageIO.read(url);
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/CameraView.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/CameraView.java
new file mode 100644 (file)
index 0000000..f5fc65e
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.octree.raytracer;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.gui.Avatar;
+
+public class CameraView {
+
+    Point3D camCenter;
+    Point3D upLeft;
+    Point3D upRight;
+    Point3D downLeft;
+    Point3D downRight;
+
+    public CameraView(final Avatar avatar, final double zoom) {
+        computeCameraCoordinates(avatar, zoom);
+    }
+
+    private void computeCameraCoordinates(final Avatar avatar, final double zoom) {
+        camCenter = new Point3D(avatar.getLocation()).scaleDown(zoom);
+
+        upLeft = new Point3D(camCenter.x, camCenter.y, camCenter.z
+                + Camera.CAMERA_SIZE);
+        upRight = new Point3D(upLeft);
+        downLeft = new Point3D(upLeft);
+        downRight = new Point3D(upLeft);
+
+        final float viewAngle = (float) .6;
+
+        upLeft.rotate(camCenter, -viewAngle, -viewAngle);
+        upRight.rotate(camCenter, viewAngle, -viewAngle);
+        downLeft.rotate(camCenter, -viewAngle, viewAngle);
+        downRight.rotate(camCenter, viewAngle, viewAngle);
+
+        upLeft.rotate(camCenter, -avatar.getAngleXZ(), -avatar.getAngleYZ());
+        upRight.rotate(camCenter, -avatar.getAngleXZ(), -avatar.getAngleYZ());
+        downLeft.rotate(camCenter, -avatar.getAngleXZ(), -avatar.getAngleYZ());
+        downRight.rotate(camCenter, -avatar.getAngleXZ(), -avatar.getAngleYZ());
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/LightSource.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/LightSource.java
new file mode 100755 (executable)
index 0000000..fcf93a4
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.octree.raytracer;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+
+public class LightSource {
+
+    public int x, y, z;
+
+    public Color color;
+
+    public float brightness;
+
+    public LightSource(final Point3D location, final Color color,
+                       final float Brightness) {
+        x = (int) location.x;
+        y = (int) location.y;
+        z = (int) location.z;
+
+        this.color = color;
+        brightness = Brightness;
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/Ray.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/Ray.java
new file mode 100755 (executable)
index 0000000..93c53f6
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.octree.raytracer;
+
+public class Ray {
+
+    public double x, y, z;
+
+    public double xp, yp, zp;
+
+    public double hitX, hitY, hitZ;
+
+    public int hitCellSize;
+
+    public int hitCellX, hitCellY, hitCellZ;
+
+       /*
+     * public int orientation;
+        * 
+        * public static final int ORIENTATION_RIGHT = 0;
+        * 
+        * public static final int ORIENTATION_LEFT = 1;
+        * 
+        * public static final int ORIENTATION_DOWN = 2;
+        * 
+        * public static final int ORIENTATION_UP = 3;
+        * 
+        * public static final int ORIENTATION_FORWARD = 4;
+        * 
+        * public static final int ORIENTATION_BACK = 5;
+        * 
+        * public static final String orientations[] = { "RIGHT", "LEFT", "DOWN",
+        * "UP", "FORWARD", "BACK" };
+        */
+
+    public Ray(final double X, final double Y, final double Z, final double Xp,
+               final double Yp, final double Zp) {
+        x = X;
+        y = Y;
+        z = Z;
+        xp = Xp;
+        yp = Yp;
+        zp = Zp;
+        // calculateOrientation();
+    }
+
+    /*
+     * public void calculateOrientation() { float axp = Math.abs(xp); float ayp
+     * = Math.abs(yp); float azp = Math.abs(zp);
+     *
+     * if (axp > ayp) { if (axp > azp) { if (xp > 0) { orientation =
+     * ORIENTATION_RIGHT; } else { orientation = ORIENTATION_LEFT; } } else { if
+     * (zp > 0) { orientation = ORIENTATION_FORWARD; } else { orientation =
+     * ORIENTATION_BACK; } } } else { if (ayp > azp) { if (yp > 0) { orientation
+     * = ORIENTATION_DOWN; } else { orientation = ORIENTATION_UP; } } else { if
+     * (zp > 0) { orientation = ORIENTATION_FORWARD; } else { orientation =
+     * ORIENTATION_BACK; } } }
+     *
+     *
+     * }
+     */
+    @Override
+    public String toString() {
+        return "Ray \n" + "    x " + x + "\n" + "      y " + y + "\n" + "      z " + z
+                + "\n" + "     xp " + xp + "\n" + "    yp " + yp + "\n" + "    zp " + zp
+                + "\n"; /*
+                         * + " orientation " + orientations[orientation];
+                                                */
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/RayHit.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/RayHit.java
new file mode 100755 (executable)
index 0000000..9f5348b
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.octree.raytracer;
+
+public class RayHit {
+
+    float x, y, z;
+
+    int cellPointer;
+
+    public RayHit(final float x, final float y, final float z,
+                  final int cellPointer) {
+        this.x = x;
+        this.y = y;
+        this.z = z;
+        this.cellPointer = cellPointer;
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/RayTracer.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/RayTracer.java
new file mode 100755 (executable)
index 0000000..47b3bd3
--- /dev/null
@@ -0,0 +1,306 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.octree.raytracer;
+
+import eu.svjatoslav.sixth.e3d.gui.View;
+import eu.svjatoslav.sixth.e3d.renderer.octree.OctreeVolume;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture;
+
+import java.util.Vector;
+
+public class RayTracer implements Runnable {
+
+    private static final int PROGRESS_UPDATE_FREQUENCY_MILLIS = 1000;
+    private final Camera camera;
+    private final Texture texture;
+    private final View view;
+    private OctreeVolume octreeVolume;
+    private Vector<LightSource> lights;
+    private int computedLights;
+
+    public RayTracer(final Texture texture, final OctreeVolume octreeVolume,
+                     final Vector<LightSource> lights, final Camera camera,
+                     final View view) {
+
+        this.texture = texture;
+        this.octreeVolume = octreeVolume;
+        this.lights = lights;
+        this.camera = camera;
+        this.view = view;
+    }
+
+    @Override
+    public void run() {
+        computedLights = 0;
+
+        // create camera
+
+        // Camera cam = new Camera(camCenter, upLeft, upRight, downLeft,
+        // downRight);
+
+        // add camera to the raytracing point
+        // Main.mainWorld.geometryCollection.addObject(cam);
+        // Main.mainWorld.compiledGeometry.compileGeometry(Main.mainWorld.geometryCollection);
+
+        final int width = texture.primaryBitmap.width;
+        final int height = texture.primaryBitmap.height;
+
+        final CameraView cameraView = camera.getCameraView();
+
+        // calculate vertical vectors
+        final double x1p = cameraView.downLeft.x - cameraView.upLeft.x;
+        final double y1p = cameraView.downLeft.y - cameraView.upLeft.y;
+        final double z1p = cameraView.downLeft.z - cameraView.upLeft.z;
+
+        final double x2p = cameraView.downRight.x - cameraView.upRight.x;
+        final double y2p = cameraView.downRight.y - cameraView.upRight.y;
+        final double z2p = cameraView.downRight.z - cameraView.upRight.z;
+
+        long nextBitmapUpdate = System.currentTimeMillis()
+                + PROGRESS_UPDATE_FREQUENCY_MILLIS;
+
+        for (int y = 0; y < height; y++) {
+            final double cx1 = cameraView.upLeft.x + ((x1p * y) / height);
+            final double cy1 = cameraView.upLeft.y + ((y1p * y) / height);
+            final double cz1 = cameraView.upLeft.z + ((z1p * y) / height);
+
+            final double cx2 = cameraView.upRight.x + ((x2p * y) / height);
+            final double cy2 = cameraView.upRight.y + ((y2p * y) / height);
+            final double cz2 = cameraView.upRight.z + ((z2p * y) / height);
+
+            // calculate horisontal vector
+            final double x3p = cx2 - cx1;
+            final double y3p = cy2 - cy1;
+            final double z3p = cz2 - cz1;
+
+            for (int x = 0; x < width; x++) {
+                final double cx3 = cx1 + ((x3p * x) / width);
+                final double cy3 = cy1 + ((y3p * x) / width);
+                final double cz3 = cz1 + ((z3p * x) / width);
+
+                final Ray r = new Ray(cameraView.camCenter.x,
+                        cameraView.camCenter.y, cameraView.camCenter.z, cx3
+                        - cameraView.camCenter.x, cy3
+                        - cameraView.camCenter.y, cz3
+                        - cameraView.camCenter.z);
+                final int c = traceRay(r);
+
+                final Color color = new Color(c);
+                texture.primaryBitmap.drawPixel(x, y, color);
+            }
+
+            if (System.currentTimeMillis() > nextBitmapUpdate) {
+                nextBitmapUpdate = System.currentTimeMillis()
+                        + PROGRESS_UPDATE_FREQUENCY_MILLIS;
+                texture.resetResampledBitmapCache();
+                view.repaintDuringNextViewUpdate();
+            }
+        }
+
+        texture.resetResampledBitmapCache();
+        view.repaintDuringNextViewUpdate();
+        // System.out.println("Raytracing done.");
+        // System.out.println("New lights computed:" + computedLights);
+    }
+
+    public int traceLight(final LightSource l, final int cubeX,
+                          final int cubeY, final int cubeZ) {
+        return 0;
+    }
+
+    public int traceRay(final Ray r) {
+
+        final int re = octreeVolume.traceCell(0, 0, 0,
+                octreeVolume.masterCellSize, 0, r);
+
+        if (re != -1) {
+            // if lightening not computed, compute it
+            if (octreeVolume.ce3[re] == -1)
+                // if cell is larger than 1
+                if (r.hitCellSize > 1) {
+                    // break it up
+                    octreeVolume.breakSolidCell(re);
+                    return traceRay(r);
+                } else {
+                    computedLights++;
+                    float red = 30, green = 30, blue = 30;
+
+                    for (final LightSource l : lights) {
+                        final int xDist = (l.x - r.hitCellX);
+                        final int yDist = (l.y - r.hitCellY);
+                        final int zDist = (l.z - r.hitCellZ);
+
+                        double newRed = 0, newGreen = 0, newBlue = 0;
+                        double tempRed, tempGreen, tempBlue;
+
+                        double distance = Math.sqrt((xDist * xDist)
+                                + (yDist * yDist) + (zDist * zDist));
+                        distance = (distance / 3) + 1;
+
+                        final Ray r1 = new Ray(r.hitCellX, r.hitCellY
+                                - (float) 1.5, r.hitCellZ,
+
+                                (float) l.x - (float) r.hitCellX, l.y
+                                - (r.hitCellY - (float) 1.5), (float) l.z
+                                - (float) r.hitCellZ);
+
+                        final int rt1 = octreeVolume.traceCell(0, 0, 0,
+                                octreeVolume.masterCellSize, 0, r1);
+
+                        if (rt1 == -1) {
+                            newRed = (l.color.r * l.brightness) / distance;
+                            newGreen = (l.color.g * l.brightness) / distance;
+                            newBlue = (l.color.b * l.brightness) / distance;
+                        }
+
+                        final Ray r2 = new Ray(r.hitCellX - (float) 1.5,
+                                r.hitCellY, r.hitCellZ,
+
+                                l.x - (r.hitCellX - (float) 1.5), (float) l.y
+                                - (float) r.hitCellY, (float) l.z
+                                - (float) r.hitCellZ);
+
+                        final int rt2 = octreeVolume.traceCell(0, 0, 0,
+                                octreeVolume.masterCellSize, 0, r2);
+
+                        if (rt2 == -1) {
+                            tempRed = (l.color.r * l.brightness) / distance;
+                            tempGreen = (l.color.g * l.brightness) / distance;
+                            tempBlue = (l.color.b * l.brightness) / distance;
+
+                            if (tempRed > newRed)
+                                newRed = tempRed;
+                            if (tempGreen > newGreen)
+                                newGreen = tempGreen;
+                            if (tempBlue > newBlue)
+                                newBlue = tempBlue;
+                        }
+
+                        final Ray r3 = new Ray(r.hitCellX, r.hitCellY,
+                                r.hitCellZ - (float) 1.5,
+
+                                (float) l.x - (float) r.hitCellX, (float) l.y
+                                - (float) r.hitCellY, l.z
+                                - (r.hitCellZ - (float) 1.5));
+
+                        final int rt3 = octreeVolume.traceCell(0, 0, 0,
+                                octreeVolume.masterCellSize, 0, r3);
+
+                        if (rt3 == -1) {
+                            tempRed = (l.color.r * l.brightness) / distance;
+                            tempGreen = (l.color.g * l.brightness) / distance;
+                            tempBlue = (l.color.b * l.brightness) / distance;
+                            if (tempRed > newRed)
+                                newRed = tempRed;
+                            if (tempGreen > newGreen)
+                                newGreen = tempGreen;
+                            if (tempBlue > newBlue)
+                                newBlue = tempBlue;
+                        }
+
+                        final Ray r4 = new Ray(r.hitCellX, r.hitCellY
+                                + (float) 1.5, r.hitCellZ,
+
+                                (float) l.x - (float) r.hitCellX, l.y
+                                - (r.hitCellY + (float) 1.5), (float) l.z
+                                - (float) r.hitCellZ);
+
+                        final int rt4 = octreeVolume.traceCell(0, 0, 0,
+                                octreeVolume.masterCellSize, 0, r4);
+
+                        if (rt4 == -1) {
+                            tempRed = (l.color.r * l.brightness) / distance;
+                            tempGreen = (l.color.g * l.brightness) / distance;
+                            tempBlue = (l.color.b * l.brightness) / distance;
+                            if (tempRed > newRed)
+                                newRed = tempRed;
+                            if (tempGreen > newGreen)
+                                newGreen = tempGreen;
+                            if (tempBlue > newBlue)
+                                newBlue = tempBlue;
+                        }
+
+                        final Ray r5 = new Ray(r.hitCellX + (float) 1.5,
+                                r.hitCellY, r.hitCellZ,
+
+                                l.x - (r.hitCellX + (float) 1.5), (float) l.y
+                                - (float) r.hitCellY, (float) l.z
+                                - (float) r.hitCellZ);
+
+                        final int rt5 = octreeVolume.traceCell(0, 0, 0,
+                                octreeVolume.masterCellSize, 0, r5);
+
+                        if (rt5 == -1) {
+                            tempRed = (l.color.r * l.brightness) / distance;
+                            tempGreen = (l.color.g * l.brightness) / distance;
+                            tempBlue = (l.color.b * l.brightness) / distance;
+                            if (tempRed > newRed)
+                                newRed = tempRed;
+                            if (tempGreen > newGreen)
+                                newGreen = tempGreen;
+                            if (tempBlue > newBlue)
+                                newBlue = tempBlue;
+                        }
+
+                        final Ray r6 = new Ray(r.hitCellX, r.hitCellY,
+                                r.hitCellZ + (float) 1.5,
+
+                                (float) l.x - (float) r.hitCellX, (float) l.y
+                                - (float) r.hitCellY, l.z
+                                - (r.hitCellZ + (float) 1.5));
+
+                        final int rt6 = octreeVolume.traceCell(0, 0, 0,
+                                octreeVolume.masterCellSize, 0, r6);
+
+                        if (rt6 == -1) {
+                            tempRed = (l.color.r * l.brightness) / distance;
+                            tempGreen = (l.color.g * l.brightness) / distance;
+                            tempBlue = (l.color.b * l.brightness) / distance;
+                            if (tempRed > newRed)
+                                newRed = tempRed;
+                            if (tempGreen > newGreen)
+                                newGreen = tempGreen;
+                            if (tempBlue > newBlue)
+                                newBlue = tempBlue;
+                        }
+                        red += newRed;
+                        green += newGreen;
+                        blue += newBlue;
+
+                    }
+
+                    final int cellColor = octreeVolume.ce2[re];
+
+                    red = (red * ((cellColor & 0xFF0000) >> 16)) / 255;
+                    green = (green * ((cellColor & 0xFF00) >> 8)) / 255;
+                    blue = (blue * (cellColor & 0xFF)) / 255;
+
+                    if (red > 255)
+                        red = 255;
+                    if (green > 255)
+                        green = 255;
+                    if (blue > 255)
+                        blue = 255;
+
+                    octreeVolume.ce3[re] = (((int) red) << 16)
+                            + (((int) green) << 8) + ((int) blue);
+
+                }
+            if (octreeVolume.ce3[re] == 0)
+                return octreeVolume.ce2[re];
+            return octreeVolume.ce3[re];
+        }
+
+        // return (200 << 16) + (200 << 8) + 255;
+        return 0;
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/package-info.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/package-info.java
new file mode 100755 (executable)
index 0000000..a801ead
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.octree.raytracer;
+
+/**
+ * Raytracer through voxel data.
+ */
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/package-info.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/package-info.java
new file mode 100755 (executable)
index 0000000..32d1160
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer;
+
+/**
+ * Various 3D renderers utilizing different rendering approaches.
+ */
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/Color.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/Color.java
new file mode 100644 (file)
index 0000000..125ef6e
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster;
+
+public final class Color {
+
+    public static final Color RED = new Color(255, 0, 0, 255);
+    public static final Color GREEN = new Color(0, 255, 0, 255);
+    public static final Color BLUE = new Color(0, 0, 255, 255);
+    public static final Color YELLOW = new Color(255, 255, 0, 255);
+    public static final Color WHITE = new Color(255, 255, 255, 255);
+    public static final Color BLACK = new Color(0, 0, 0, 255);
+    public static final Color PURPLE = new Color(255, 0, 255, 255);
+    public static final Color TRANSPARENT = new Color(0, 0, 0, 0);
+
+    public final int r, g, b;
+
+    /**
+     * <pre>
+     * 255 is opaque.
+     * 0 is transparent.
+     * </pre>
+     */
+    public int a;
+
+    public Color(final Color parentColor) {
+        r = parentColor.r;
+        g = parentColor.g;
+        b = parentColor.b;
+        a = parentColor.a;
+    }
+
+    public Color(final double r, final double g, final double b, final double a) {
+        this.r = ensureByteLimit((int) (r * 255d));
+        this.g = ensureByteLimit((int) (g * 255d));
+        this.b = ensureByteLimit((int) (b * 255d));
+        this.a = ensureByteLimit((int) (a * 255d));
+    }
+
+    /**
+     * <pre>
+     *     Supported formats are:
+     *
+     *     RGB
+     *     RGBA
+     *     RRGGBB
+     *     RRGGBBAA
+     * </pre>
+     */
+    public Color(String colorHexCode) {
+        switch (colorHexCode.length()) {
+            case 3:
+                r = parseHexSegment(colorHexCode, 0, 1) * 16;
+                g = parseHexSegment(colorHexCode, 1, 1) * 16;
+                b = parseHexSegment(colorHexCode, 2, 1) * 16;
+                a = 255;
+                return;
+
+            case 4:
+                r = parseHexSegment(colorHexCode, 0, 1) * 16;
+                g = parseHexSegment(colorHexCode, 1, 1) * 16;
+                b = parseHexSegment(colorHexCode, 2, 1) * 16;
+                a = parseHexSegment(colorHexCode, 3, 1) * 16;
+                return;
+
+            case 6:
+                r = parseHexSegment(colorHexCode, 0, 2);
+                g = parseHexSegment(colorHexCode, 2, 2);
+                b = parseHexSegment(colorHexCode, 4, 2);
+                a = 255;
+                return;
+
+            case 8:
+                r = parseHexSegment(colorHexCode, 0, 2);
+                g = parseHexSegment(colorHexCode, 2, 2);
+                b = parseHexSegment(colorHexCode, 4, 2);
+                a = parseHexSegment(colorHexCode, 6, 2);
+                return;
+            default:
+                throw new IllegalArgumentException("Unsupported color code: " + colorHexCode);
+        }
+    }
+
+    public Color(final int rgb) {
+        r = (rgb & 0xFF0000) >> 16;
+        g = (rgb & 0xFF00) >> 8;
+        b = rgb & 0xFF;
+        a = 255;
+    }
+
+    public Color(final int r, final int g, final int b) {
+        this(r, g, b, 255);
+    }
+
+    public Color(final int r, final int g, final int b, final int a) {
+        this.r = ensureByteLimit(r);
+        this.g = ensureByteLimit(g);
+        this.b = ensureByteLimit(b);
+        this.a = ensureByteLimit(a);
+    }
+
+    private int parseHexSegment(String hexString, int start, int length) {
+        return Integer.parseInt(hexString.substring(start, start + length), 16);
+    }
+
+    /**
+     * Ensure that color values are within allowed limits of 0 to 255.
+     */
+    private int ensureByteLimit(final int value) {
+        if (value < 0)
+            return 0;
+
+        if (value > 255)
+            return 255;
+
+        return value;
+    }
+
+    public boolean isTransparent() {
+        return a == 0;
+    }
+
+    public java.awt.Color toAwtColor() {
+        return new java.awt.Color(r, g, b, a);
+    }
+
+    public int toInt() {
+        return toAwtColor().getRGB();
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/RenderAggregator.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/RenderAggregator.java
new file mode 100644 (file)
index 0000000..74340c9
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster;
+
+import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractCoordinateShape;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+public class RenderAggregator {
+
+    ArrayList<AbstractCoordinateShape> shapes = new ArrayList<>();
+    ShapesComparator comparator = new ShapesComparator();
+
+    public void paint(final RenderingContext renderBuffer) {
+
+        Collections.sort(shapes, comparator);
+
+        for (final AbstractCoordinateShape shape : shapes)
+            shape.paint(renderBuffer);
+
+    }
+
+    public void queueShapeForRendering(final AbstractCoordinateShape shape) {
+        shapes.add(shape);
+    }
+
+    public void reset() {
+        shapes.clear();
+    }
+
+    static class ShapesComparator implements Comparator<AbstractCoordinateShape>, Serializable {
+
+        @Override
+        public int compare(final AbstractCoordinateShape o1, final AbstractCoordinateShape o2) {
+            if (o1.getZ() < o2.getZ())
+                return 1;
+            else if (o1.getZ() > o2.getZ())
+                return -1;
+
+            return Integer.compare(o1.shapeId, o2.shapeId);
+        }
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/ShapeCollection.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/ShapeCollection.java
new file mode 100755 (executable)
index 0000000..8864f74
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.geometry.Transform;
+import eu.svjatoslav.sixth.e3d.geometry.TransformPipe;
+import eu.svjatoslav.sixth.e3d.gui.Avatar;
+import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
+import eu.svjatoslav.sixth.e3d.gui.ViewContext;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractShape;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class ShapeCollection {
+
+    private final RenderAggregator aggregator = new RenderAggregator();
+    private final TransformPipe transformPipe = new TransformPipe();
+    private final List<AbstractShape> shapes = new ArrayList<>();
+
+    public synchronized void addShape(final AbstractShape shape) {
+        shapes.add(shape);
+    }
+
+    public Collection<AbstractShape> getShapes() {
+        return shapes;
+    }
+
+    public void clear() {
+        shapes.clear();
+    }
+
+    public synchronized void paint(final ViewContext viewContext,
+                                   final RenderingContext renderingContext) {
+
+        renderingContext.frameNumber++;
+
+        aggregator.reset();
+        transformPipe.clear();
+
+        // translate scene according to avatar current location
+        final Avatar avatar = viewContext.getAvatar();
+
+        // rotate scene according to avatar looking direction
+        transformPipe.addTransform(new Transform(new Point3D(), avatar
+                .getAngleXZ(), avatar.getAngleYZ()));
+
+        // translate scene according to avatar location in the world
+        final Point3D translation = new Point3D(-avatar.getLocation().x,
+                -avatar.getLocation().y, -avatar.getLocation().z);
+
+        transformPipe.addTransform(new Transform(translation, 0, 0));
+
+        for (final AbstractShape shape : shapes)
+            shape.transform(transformPipe, aggregator, renderingContext);
+
+        aggregator.paint(renderingContext);
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/package-info.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/package-info.java
new file mode 100755 (executable)
index 0000000..980cd12
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster;
+
+/**
+ * <pre>
+ * Realtime renderer (in software) using traditional approaches:
+ *      Wireframe
+ *      Textured polygons
+ *      Z-Buffer
+ * </pre>
+ */
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/AbstractCoordinateShape.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/AbstractCoordinateShape.java
new file mode 100644 (file)
index 0000000..1a5bf0a
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes;
+
+import eu.svjatoslav.sixth.e3d.geometry.GeometryCoordinate;
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.geometry.TransformPipe;
+import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
+import eu.svjatoslav.sixth.e3d.renderer.raster.RenderAggregator;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+public abstract class AbstractCoordinateShape extends AbstractShape {
+
+    public static final AtomicInteger lastShapeId = new AtomicInteger();
+    public final int shapeId;
+    public double onScreenZ;
+    public GeometryCoordinate[] coordinates;
+
+    public AbstractCoordinateShape(final int pointsCount) {
+        coordinates = new GeometryCoordinate[pointsCount];
+        for (int i = 0; i < pointsCount; i++)
+            coordinates[i] = new GeometryCoordinate();
+
+        shapeId = lastShapeId.getAndIncrement();
+    }
+
+    public AbstractCoordinateShape(final Point3D... locationPoints) {
+        coordinates = new GeometryCoordinate[locationPoints.length];
+
+        for (int i = 0; i < locationPoints.length; i++)
+            coordinates[i] = new GeometryCoordinate(locationPoints[i]);
+
+        shapeId = lastShapeId.getAndIncrement();
+    }
+
+    public double getZ() {
+        return onScreenZ;
+    }
+
+    public abstract void paint(RenderingContext renderBuffer);
+
+    @Override
+    public void transform(final TransformPipe transforms,
+                          final RenderAggregator aggregator,
+                          final RenderingContext renderingContext) {
+
+        double accumulatedZ = 0;
+        boolean paint = true;
+
+        for (final GeometryCoordinate geometryPoint : coordinates) {
+            geometryPoint.transform(transforms, renderingContext);
+
+            accumulatedZ += geometryPoint.transformedCoordinate.z;
+
+            if (!geometryPoint.transformedCoordinate.withinDrawingLimits())
+                paint = false;
+        }
+
+        if (paint) {
+            onScreenZ = accumulatedZ / coordinates.length;
+            aggregator.queueShapeForRendering(this);
+        }
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/AbstractShape.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/AbstractShape.java
new file mode 100644 (file)
index 0000000..d2db2bf
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes;
+
+import eu.svjatoslav.sixth.e3d.geometry.TransformPipe;
+import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
+import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController;
+import eu.svjatoslav.sixth.e3d.renderer.raster.RenderAggregator;
+
+public abstract class AbstractShape {
+
+    public MouseInteractionController mouseInteractionController;
+
+    public void setMouseInteractionController(
+            final MouseInteractionController mouseInteractionController) {
+        this.mouseInteractionController = mouseInteractionController;
+    }
+
+    public abstract void transform(final TransformPipe transforms,
+                                   final RenderAggregator aggregator,
+                                   final RenderingContext renderingContext);
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/ForwardOrientedTexture.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/ForwardOrientedTexture.java
new file mode 100644 (file)
index 0000000..49af48c
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point2D;
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractCoordinateShape;
+import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture;
+import eu.svjatoslav.sixth.e3d.renderer.raster.texture.TextureBitmap;
+
+public class ForwardOrientedTexture extends AbstractCoordinateShape {
+
+    public static final double SIZE_MULTIPLIER = 0.005;
+    public Texture texture;
+    private double scale;
+
+    public ForwardOrientedTexture(final Point3D point, final double scale,
+                                  final Texture texture) {
+        super(point);
+        this.texture = texture;
+        setScale(scale);
+    }
+
+    @Override
+    public void paint(final RenderingContext targetRenderingArea) {
+
+        final double z = coordinates[0].transformedCoordinate.z;
+
+        final double visibleHorizontalDistanceFromCenter = (targetRenderingArea.width
+                * scale * texture.primaryBitmap.width)
+                / z;
+
+        final double visibleVerticalDistanceFromCenter = (targetRenderingArea.width
+                * scale * texture.primaryBitmap.height)
+                / z;
+
+        // compute visible pixel density, and get appropriate bitmap
+        final double zoom = (visibleHorizontalDistanceFromCenter * 2)
+                / texture.primaryBitmap.width;
+
+        final TextureBitmap textureBitmap = texture.getZoomedBitmap(zoom);
+
+        final Point2D onScreenLocation = coordinates[0].onScreenCoordinate;
+
+        // compute Y
+        final int initialYStart = (int) (onScreenLocation.y - visibleVerticalDistanceFromCenter);
+        final int initialYEnd = (int) (onScreenLocation.y + visibleVerticalDistanceFromCenter);
+        final int maxYDistance = initialYEnd - initialYStart;
+
+        int yStart = initialYStart;
+        int yEnd = initialYEnd;
+
+        if (yStart < 0)
+            yStart = 0;
+
+        if (yEnd > targetRenderingArea.height)
+            yEnd = targetRenderingArea.height;
+
+        // compute X
+        final int initialXStart = (int) (onScreenLocation.x - visibleHorizontalDistanceFromCenter);
+        final int initialXEnd = (int) (onScreenLocation.x + visibleHorizontalDistanceFromCenter);
+        final int maxXDistance = initialXEnd - initialXStart;
+
+        int xStart = initialXStart;
+        int xEnd = initialXEnd;
+
+        if (xStart < 0)
+            xStart = 0;
+
+        if (xEnd > targetRenderingArea.width)
+            xEnd = targetRenderingArea.width;
+
+        final byte[] targetRenderingAreaBytes = targetRenderingArea.bytes;
+
+        final int textureWidth = textureBitmap.width;
+
+        for (int y = yStart; y < yEnd; y++) {
+
+            final int relativeTextureOffset = ((textureBitmap.height * (y - initialYStart)) / maxYDistance)
+                    * textureWidth;
+
+            int targetRenderingAreaOffset = ((y * targetRenderingArea.width) + xStart) * 4;
+
+            for (int x = xStart; x < xEnd; x++) {
+
+                final int textureOffset = (relativeTextureOffset + ((textureWidth * (x - initialXStart)) / maxXDistance)) * 4;
+
+                textureBitmap.drawPixel(textureOffset,
+                        targetRenderingAreaBytes, targetRenderingAreaOffset);
+
+                targetRenderingAreaOffset += 4;
+            }
+        }
+    }
+
+    public void setScale(final double scale) {
+        this.scale = scale * SIZE_MULTIPLIER;
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/GlowingPoint.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/GlowingPoint.java
new file mode 100644 (file)
index 0000000..29cad63
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture;
+
+public class GlowingPoint extends ForwardOrientedTexture {
+
+    private static final int TEXTURE_SIZE = 50;
+    private final Color color;
+
+    public GlowingPoint(final Point3D point, final double pointSize,
+                        final Color color) {
+        super(point, pointSize, getTexture(color));
+        this.color = color;
+
+        getTexture(color);
+
+    }
+
+    public static Texture getTexture(final Color color) {
+        final Texture texture = new Texture(TEXTURE_SIZE, TEXTURE_SIZE, 1);
+        for (int x = 0; x < TEXTURE_SIZE; x++)
+            for (int y = 0; y < TEXTURE_SIZE; y++) {
+                int address = texture.primaryBitmap.getAddress(x, y);
+
+                final int distance = (int) Math
+                        .sqrt((((TEXTURE_SIZE / 2) - x) * ((TEXTURE_SIZE / 2) - x))
+                                + (((TEXTURE_SIZE / 2) - y) * ((TEXTURE_SIZE / 2) - y)));
+
+                int alpha = 255 - ((270 * distance) / (TEXTURE_SIZE / 2));
+                if (alpha < 0)
+                    alpha = 0;
+
+                texture.primaryBitmap.bytes[address] = (byte) alpha;
+                address++;
+                texture.primaryBitmap.bytes[address] = (byte) color.b;
+                address++;
+                texture.primaryBitmap.bytes[address] = (byte) color.g;
+                address++;
+                texture.primaryBitmap.bytes[address] = (byte) color.r;
+            }
+
+        return texture;
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/Line.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/Line.java
new file mode 100644 (file)
index 0000000..490e825
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point2D;
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractCoordinateShape;
+
+public class Line extends AbstractCoordinateShape {
+
+    private static final double MINIMUM_WIDTH_THRESHOLD = 1;
+
+    private static final double LINE_WIDTH_MULTIPLIER = 0.2d;
+
+    public final double width;
+    final LineInterpolator[] li = new LineInterpolator[4];
+    public Color color;
+
+    public Line(final Line parentLine) {
+        this(parentLine.coordinates[0].coordinate.clone(),
+                parentLine.coordinates[1].coordinate.clone(), new Color(
+                        parentLine.color), parentLine.width);
+    }
+
+    public Line(final Point3D point1, final Point3D point2, final Color color,
+                final double width) {
+
+        super(point1, point2);
+
+        this.color = color;
+        this.width = width;
+
+        for (int i = 0; i < li.length; i++)
+            li[i] = new LineInterpolator();
+
+    }
+
+    public void drawHorizontalLine(final LineInterpolator line1,
+                                   final LineInterpolator line2, final int y,
+                                   final RenderingContext renderBuffer) {
+
+        int x1 = line1.getX(y);
+        int x2 = line2.getX(y);
+
+        double d1 = line1.getD();
+        double d2 = line2.getD();
+
+        if (x1 > x2) {
+            final int tmp = x1;
+            x1 = x2;
+            x2 = tmp;
+
+            final double tmp2 = d1;
+            d1 = d2;
+            d2 = tmp2;
+        }
+
+        final int unclippedWidth = x2 - x1;
+        final double dinc = (d2 - d1) / unclippedWidth;
+
+        if (x1 < 0) {
+            d1 += (dinc * (-x1));
+            x1 = 0;
+        }
+
+        if (x2 >= renderBuffer.width)
+            x2 = renderBuffer.width - 1;
+
+        final int drawnWidth = x2 - x1;
+
+        int offset = ((y * renderBuffer.width) + x1) * 4;
+        final byte[] offSreenBufferBytes = renderBuffer.bytes;
+
+        final int lineAlpha = color.a;
+
+        final int colorB = color.b;
+        final int colorG = color.g;
+        final int colorR = color.r;
+
+        for (int i = 0; i < drawnWidth; i++) {
+
+            final double alphaMultiplier = 1d - Math.abs(d1);
+
+            final int realLineAlpha = (int) (lineAlpha * alphaMultiplier);
+            final int backgroundAlpha = 255 - realLineAlpha;
+
+            offSreenBufferBytes[offset] = (byte) 255;
+            offset++;
+            offSreenBufferBytes[offset] = (byte) ((((offSreenBufferBytes[offset] & 0xff) * backgroundAlpha) + (colorB * realLineAlpha)) / 256);
+            offset++;
+            offSreenBufferBytes[offset] = (byte) ((((offSreenBufferBytes[offset] & 0xff) * backgroundAlpha) + (colorG * realLineAlpha)) / 256);
+            offset++;
+            offSreenBufferBytes[offset] = (byte) ((((offSreenBufferBytes[offset] & 0xff) * backgroundAlpha) + (colorR * realLineAlpha)) / 256);
+            offset++;
+
+            d1 += dinc;
+        }
+
+    }
+
+    public void drawSinglePixelHorizontalLine(final RenderingContext buffer,
+                                              final int alpha) {
+
+        final Point2D onScreenPoint1 = coordinates[0].onScreenCoordinate;
+        final Point2D onScreenPoint2 = coordinates[1].onScreenCoordinate;
+
+        int xStart = (int) onScreenPoint1.x;
+        int xEnd = (int) onScreenPoint2.x;
+
+        int lineHeight;
+        int yBase;
+
+        if (xStart > xEnd) {
+            final int tmp = xStart;
+            xStart = xEnd;
+            xEnd = tmp;
+            lineHeight = (int) (onScreenPoint1.y - onScreenPoint2.y);
+            yBase = (int) onScreenPoint2.y;
+        } else {
+            yBase = (int) onScreenPoint1.y;
+            lineHeight = (int) (onScreenPoint2.y - onScreenPoint1.y);
+        }
+
+        final int lineWidth = xEnd - xStart;
+        if (lineWidth == 0)
+            return;
+
+        final byte[] offSreenBufferBytes = buffer.bytes;
+        final int backgroundAlpha = 255 - alpha;
+
+        final int blueWithAlpha = color.b * alpha;
+        final int greenWithAplha = color.g * alpha;
+        final int redWithAlpha = color.r * alpha;
+
+        for (int relativeX = 0; relativeX <= lineWidth; relativeX++) {
+            final int x = xStart + relativeX;
+
+            if ((x >= 0) && (x < buffer.width)) {
+
+                final int y = yBase + ((relativeX * lineHeight) / lineWidth);
+                if ((y >= 0) && (y < buffer.height)) {
+                    int ramOffset = ((y * buffer.width) + x) * 4;
+
+                    offSreenBufferBytes[ramOffset] = (byte) 255;
+                    ramOffset++;
+                    offSreenBufferBytes[ramOffset] = (byte) ((((offSreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + blueWithAlpha) / 256);
+                    ramOffset++;
+                    offSreenBufferBytes[ramOffset] = (byte) ((((offSreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + greenWithAplha) / 256);
+                    ramOffset++;
+                    offSreenBufferBytes[ramOffset] = (byte) ((((offSreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + redWithAlpha) / 256);
+                }
+            }
+        }
+
+    }
+
+    public void drawSinglePixelVerticalLine(final RenderingContext buffer,
+                                            final int alpha) {
+
+        final Point2D onScreenPoint1 = coordinates[0].onScreenCoordinate;
+        final Point2D onScreenPoint2 = coordinates[1].onScreenCoordinate;
+
+        int yStart = (int) onScreenPoint1.y;
+        int yEnd = (int) onScreenPoint2.y;
+
+        int lineWidth;
+        int xBase;
+
+        if (yStart > yEnd) {
+            final int tmp = yStart;
+            yStart = yEnd;
+            yEnd = tmp;
+            lineWidth = (int) (onScreenPoint1.x - onScreenPoint2.x);
+            xBase = (int) onScreenPoint2.x;
+        } else {
+            xBase = (int) onScreenPoint1.x;
+            lineWidth = (int) (onScreenPoint2.x - onScreenPoint1.x);
+        }
+
+        final int lineHeight = yEnd - yStart;
+        if (lineHeight == 0)
+            return;
+
+        final byte[] offSreenBufferBytes = buffer.bytes;
+        final int backgroundAlpha = 255 - alpha;
+
+        final int blueWithAlpha = color.b * alpha;
+        final int greenWithAplha = color.g * alpha;
+        final int redWithAlpha = color.r * alpha;
+
+        for (int relativeY = 0; relativeY <= lineHeight; relativeY++) {
+            final int y = yStart + relativeY;
+
+            if ((y >= 0) && (y < buffer.height)) {
+
+                final int x = xBase + ((relativeY * lineWidth) / lineHeight);
+                if ((x >= 0) && (x < buffer.width)) {
+                    int ramOffset = ((y * buffer.width) + x) * 4;
+
+                    offSreenBufferBytes[ramOffset] = (byte) 255;
+                    ramOffset++;
+                    offSreenBufferBytes[ramOffset] = (byte) ((((offSreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + blueWithAlpha) / 256);
+                    ramOffset++;
+                    offSreenBufferBytes[ramOffset] = (byte) ((((offSreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + greenWithAplha) / 256);
+                    ramOffset++;
+                    offSreenBufferBytes[ramOffset] = (byte) ((((offSreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + redWithAlpha) / 256);
+                }
+            }
+        }
+    }
+
+    public int getLineInterpolator(final int startPointer, final int y) {
+
+        for (int i = startPointer; i < li.length; i++)
+            if (li[i].containsY(y))
+                return i;
+        return -1;
+    }
+
+    @Override
+    public void paint(final RenderingContext buffer) {
+
+        final Point2D onScreenPoint1 = coordinates[0].onScreenCoordinate;
+        final Point2D onScreenPoint2 = coordinates[1].onScreenCoordinate;
+
+        final double xp = onScreenPoint2.x - onScreenPoint1.x;
+        final double yp = onScreenPoint2.y - onScreenPoint1.y;
+
+        final double point1radius = (buffer.width * LINE_WIDTH_MULTIPLIER * width)
+                / coordinates[0].transformedCoordinate.z;
+        final double point2radius = (buffer.width * LINE_WIDTH_MULTIPLIER * width)
+                / coordinates[1].transformedCoordinate.z;
+
+        if ((point1radius < MINIMUM_WIDTH_THRESHOLD)
+                || (point2radius < MINIMUM_WIDTH_THRESHOLD)) {
+
+            double averageRadius = (point1radius + point2radius) / 2;
+
+            if (averageRadius > 1)
+                averageRadius = 1;
+
+            final int alpha = (int) (color.a * averageRadius);
+            if (alpha < 2)
+                return;
+
+            if (Math.abs(xp) > Math.abs(yp))
+                drawSinglePixelHorizontalLine(buffer, alpha);
+            else
+                drawSinglePixelVerticalLine(buffer, alpha);
+            return;
+        }
+
+        final double lineLength = Math.sqrt((xp * xp) + (yp * yp));
+
+        final double yinc1 = (point1radius * xp) / lineLength;
+        final double yinc2 = (point2radius * xp) / lineLength;
+
+        final double xdec1 = (point1radius * yp) / lineLength;
+        final double xdec2 = (point2radius * yp) / lineLength;
+
+        final double p1x1 = onScreenPoint1.x - xdec1;
+        final double p1y1 = onScreenPoint1.y + yinc1;
+
+        final double p1x2 = onScreenPoint1.x + xdec1;
+        final double p1y2 = onScreenPoint1.y - yinc1;
+
+        final double p2x1 = onScreenPoint2.x - xdec2;
+        final double p2y1 = onScreenPoint2.y + yinc2;
+
+        final double p2x2 = onScreenPoint2.x + xdec2;
+        final double p2y2 = onScreenPoint2.y - yinc2;
+
+        li[0].setPoints(p1x1, p1y1, 1d, p2x1, p2y1, 1d);
+        li[1].setPoints(p1x2, p1y2, -1d, p2x2, p2y2, -1d);
+
+        li[2].setPoints(p1x1, p1y1, 1d, p1x2, p1y2, -1d);
+        li[3].setPoints(p2x1, p2y1, 1d, p2x2, p2y2, -1d);
+
+        double ymin = p1y1;
+        if (p1y2 < ymin)
+            ymin = p1y2;
+        if (p2y1 < ymin)
+            ymin = p2y1;
+        if (p2y2 < ymin)
+            ymin = p2y2;
+        if (ymin < 0)
+            ymin = 0;
+
+        double ymax = p1y1;
+        if (p1y2 > ymax)
+            ymax = p1y2;
+        if (p2y1 > ymax)
+            ymax = p2y1;
+        if (p2y2 > ymax)
+            ymax = p2y2;
+        if (ymax >= buffer.height)
+            ymax = buffer.height - 1;
+
+        for (int y = (int) ymin; y <= ymax; y++) {
+            final int li1 = getLineInterpolator(0, y);
+            if (li1 != -1) {
+                final int li2 = getLineInterpolator(li1 + 1, y);
+                if (li2 != -1)
+                    drawHorizontalLine(li[li1], li[li2], y, buffer);
+            }
+        }
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/LineAppearance.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/LineAppearance.java
new file mode 100644 (file)
index 0000000..d2d5b6a
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+
+public class LineAppearance {
+
+    private final double lineWidth;
+
+    private Color color = new Color(100, 100, 255, 255);
+
+    public LineAppearance() {
+        lineWidth = 1;
+    }
+
+    public LineAppearance(final double lineWidth) {
+        this.lineWidth = lineWidth;
+    }
+
+    public LineAppearance(final double lineWidth, final Color color) {
+        this.lineWidth = lineWidth;
+        this.color = color;
+    }
+
+    public Line getLine(final Point3D point1, final Point3D point2) {
+        return new Line(point1, point2, color, lineWidth);
+    }
+
+    public Line getLine(final Point3D point1, final Point3D point2,
+                        final Color color) {
+        return new Line(point1, point2, color, lineWidth);
+    }
+
+    public double getLineWidth() {
+        return lineWidth;
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/LineInterpolator.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/LineInterpolator.java
new file mode 100644 (file)
index 0000000..65d67e4
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line;
+
+public class LineInterpolator {
+
+    private double x1, y1, d1, x2, y2, d2;
+
+    private double d;
+    private int height;
+    private int width;
+    private double dinc;
+
+    public boolean containsY(final int y) {
+
+        if (y1 < y2) {
+            if (y >= y1)
+                if (y <= y2)
+                    return true;
+        } else if (y >= y2)
+            if (y <= y1)
+                return true;
+
+        return false;
+    }
+
+    public double getD() {
+        return d;
+    }
+
+    public int getX(final int y) {
+        if (height == 0)
+            return (int) (x2 + x1) / 2;
+
+        final int distanceFromY1 = y - (int) y1;
+
+        d = d1 + ((dinc * distanceFromY1) / height);
+
+        return (int) x1 + ((width * distanceFromY1) / height);
+    }
+
+    public void setPoints(final double x1, final double y1, final double d1,
+                          final double x2, final double y2, final double d2) {
+
+        this.x1 = x1;
+        this.y1 = y1;
+        this.d1 = d1;
+
+        this.x2 = x2;
+        this.y2 = y2;
+        this.d2 = d2;
+
+        height = (int) y2 - (int) y1;
+        width = (int) x2 - (int) x1;
+
+        dinc = d2 - d1;
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/solidpolygon/LineInterpolator.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/solidpolygon/LineInterpolator.java
new file mode 100644 (file)
index 0000000..ffd417d
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point2D;
+
+public class LineInterpolator implements Comparable<LineInterpolator> {
+
+    Point2D p1;
+    Point2D p2;
+    private int height;
+    private int width;
+    private int absoluteHeight;
+
+    @Override
+    public boolean equals(final Object o) {
+        if (o == null) return false;
+
+        return o instanceof LineInterpolator && compareTo((LineInterpolator) o) == 0;
+    }
+
+    @Override
+    public int compareTo(final LineInterpolator o) {
+        if (absoluteHeight < o.absoluteHeight)
+            return 1;
+        if (absoluteHeight > o.absoluteHeight)
+            return -1;
+
+        if (width < o.width)
+            return 1;
+        if (width > o.width)
+            return -1;
+
+        return 0;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = width;
+        result = 31 * result + absoluteHeight;
+        return result;
+    }
+
+    public boolean containsY(final int y) {
+
+        if (p1.y <= p2.y) {
+            if (y >= p1.y)
+                if (y <= p2.y)
+                    return true;
+        } else if (y >= p2.y)
+            if (y <= p1.y)
+                return true;
+
+        return false;
+    }
+
+    public int getX(final int y) {
+
+        if (height == 0)
+            return (int) (p2.x + p1.x) / 2;
+
+        return (int) (p1.x + ((width * (y - p1.y)) / height));
+    }
+
+    public void setPoints(final Point2D p1, final Point2D p2) {
+        this.p1 = p1;
+        this.p2 = p2;
+        height = (int) (p2.y - p1.y);
+        width = (int) (p2.x - p1.x);
+
+        absoluteHeight = Math.abs(height);
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/solidpolygon/SolidPolygon.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/solidpolygon/SolidPolygon.java
new file mode 100644 (file)
index 0000000..d235606
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point2D;
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.geometry.Polygon;
+import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
+import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractCoordinateShape;
+
+public class SolidPolygon extends AbstractCoordinateShape {
+
+    final static LineInterpolator polygonBoundary1 = new LineInterpolator();
+    final static LineInterpolator polygonBoundary2 = new LineInterpolator();
+    final static LineInterpolator polygonBoundary3 = new LineInterpolator();
+    private Color color;
+
+    public SolidPolygon(final Point3D point1, final Point3D point2,
+                        final Point3D point3, final Color color) {
+        super(point1, point2, point3);
+        this.color = color;
+    }
+
+    public static void drawHorizontalLine(final LineInterpolator line1,
+                                          final LineInterpolator line2, final int y,
+                                          final RenderingContext renderBuffer, final Color color) {
+
+        int x1 = line1.getX(y);
+        int x2 = line2.getX(y);
+
+        if (x1 > x2) {
+            final int tmp = x1;
+            x1 = x2;
+            x2 = tmp;
+        }
+
+        if (x1 < 0)
+            x1 = 0;
+
+        if (x2 >= renderBuffer.width)
+            x2 = renderBuffer.width - 1;
+
+        final int width = x2 - x1;
+
+        int offset = ((y * renderBuffer.width) + x1) * 4;
+        final byte[] offSreenBufferBytes = renderBuffer.bytes;
+
+        final int polygonAlpha = color.a;
+        final int b = color.b;
+        final int g = color.g;
+        final int r = color.r;
+
+        if (polygonAlpha == 255)
+            for (int i = 0; i < width; i++) {
+                offSreenBufferBytes[offset] = (byte) 255;
+                offset++;
+                offSreenBufferBytes[offset] = (byte) b;
+                offset++;
+                offSreenBufferBytes[offset] = (byte) g;
+                offset++;
+                offSreenBufferBytes[offset] = (byte) r;
+                offset++;
+            }
+        else {
+            final int backgroundAlpha = 255 - polygonAlpha;
+
+            final int blueWithAlpha = b * polygonAlpha;
+            final int greenWithAlpha = g * polygonAlpha;
+            final int redWithAlpha = r * polygonAlpha;
+
+            for (int i = 0; i < width; i++) {
+                offSreenBufferBytes[offset] = (byte) 255;
+                offset++;
+                offSreenBufferBytes[offset] = (byte) ((((offSreenBufferBytes[offset] & 0xff) * backgroundAlpha) + blueWithAlpha) / 256);
+                offset++;
+                offSreenBufferBytes[offset] = (byte) ((((offSreenBufferBytes[offset] & 0xff) * backgroundAlpha) + greenWithAlpha) / 256);
+                offset++;
+                offSreenBufferBytes[offset] = (byte) ((((offSreenBufferBytes[offset] & 0xff) * backgroundAlpha) + redWithAlpha) / 256);
+                offset++;
+            }
+
+        }
+
+    }
+
+    public static void drawPolygon(final RenderingContext context,
+                                   final Point2D onScreenPoint1, final Point2D onScreenPoint2,
+                                   final Point2D onScreenPoint3,
+                                   final MouseInteractionController mouseInteractionController,
+                                   final Color color) {
+
+        onScreenPoint1.roundToInteger();
+        onScreenPoint2.roundToInteger();
+        onScreenPoint3.roundToInteger();
+
+        if (mouseInteractionController != null)
+            if (context.mouseClick != null)
+                if (Polygon.pointWithinPolygon(context.mouseClick.coordinate,
+                        onScreenPoint1, onScreenPoint2, onScreenPoint3))
+                    context.clickedItem = mouseInteractionController;
+
+        if (color.isTransparent())
+            return;
+
+        // find top-most point
+        int yTop = (int) onScreenPoint1.y;
+
+        if (onScreenPoint2.y < yTop)
+            yTop = (int) onScreenPoint2.y;
+
+        if (onScreenPoint3.y < yTop)
+            yTop = (int) onScreenPoint3.y;
+
+        if (yTop < 0)
+            yTop = 0;
+
+        // find bottom-most point
+        int yBottom = (int) onScreenPoint1.y;
+
+        if (onScreenPoint2.y > yBottom)
+            yBottom = (int) onScreenPoint2.y;
+
+        if (onScreenPoint3.y > yBottom)
+            yBottom = (int) onScreenPoint3.y;
+
+        if (yBottom >= context.height)
+            yBottom = context.height - 1;
+
+        // paint
+        polygonBoundary1.setPoints(onScreenPoint1, onScreenPoint2);
+        polygonBoundary2.setPoints(onScreenPoint1, onScreenPoint3);
+        polygonBoundary3.setPoints(onScreenPoint2, onScreenPoint3);
+
+        final LineInterpolator[] is = new LineInterpolator[3];
+        is[0] = polygonBoundary1;
+        is[1] = polygonBoundary2;
+        is[2] = polygonBoundary3;
+
+        java.util.Arrays.sort(is);
+
+        for (int y = yTop; y < yBottom; y++)
+            if (is[0].containsY(y)) {
+
+                if (is[1].containsY(y))
+                    drawHorizontalLine(is[0], is[1], y, context, color);
+                else if (is[2].containsY(y))
+                    drawHorizontalLine(is[0], is[2], y, context, color);
+            } else if (is[1].containsY(y))
+                if (is[2].containsY(y))
+                    drawHorizontalLine(is[1], is[2], y, context, color);
+    }
+
+    public Color getColor() {
+        return color;
+    }
+
+    public void setColor(final Color color) {
+        this.color = color;
+    }
+
+    @Override
+    public void paint(final RenderingContext renderBuffer) {
+
+        final Point2D onScreenPoint1 = coordinates[0].onScreenCoordinate;
+        final Point2D onScreenPoint2 = coordinates[1].onScreenCoordinate;
+        final Point2D onScreenPoint3 = coordinates[2].onScreenCoordinate;
+
+        drawPolygon(renderBuffer, onScreenPoint1, onScreenPoint2,
+                onScreenPoint3, mouseInteractionController, color);
+
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/texturedpolygon/PolygonBorderInterpolator.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/texturedpolygon/PolygonBorderInterpolator.java
new file mode 100644 (file)
index 0000000..a1dc9fc
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.texturedpolygon;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point2D;
+
+public class PolygonBorderInterpolator implements
+        Comparable<PolygonBorderInterpolator> {
+
+    // on-screen coordinates
+    Point2D p1;
+    Point2D p2;
+    double distanceFromY1;
+    private int height;
+    private int width;
+    private int absoluteHeight;
+    private double twidth;
+    private double theight;
+    // texture coordinates
+    private Point2D tp1;
+    private Point2D tp2;
+
+
+    @Override
+    public boolean equals(final Object o) {
+        if (o == null) return false;
+
+        return o instanceof PolygonBorderInterpolator && compareTo((PolygonBorderInterpolator) o) == 0;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = width;
+        result = 31 * result + absoluteHeight;
+        return result;
+    }
+
+    @Override
+    public int compareTo(final PolygonBorderInterpolator o) {
+        if (absoluteHeight < o.absoluteHeight)
+            return 1;
+        if (absoluteHeight > o.absoluteHeight)
+            return -1;
+
+        if (width < o.width)
+            return 1;
+        if (width > o.width)
+            return -1;
+
+        return 0;
+    }
+
+    public boolean containsY(final int y) {
+
+        if (p1.y < p2.y) {
+            if (y >= p1.y)
+                if (y <= p2.y)
+                    return true;
+        } else if (y >= p2.y)
+            if (y <= p1.y)
+                return true;
+
+        return false;
+    }
+
+    public double getTX() {
+
+        if (height == 0)
+            return (tp2.x + tp1.x) / 2d;
+
+        return tp1.x + ((twidth * distanceFromY1) / height);
+    }
+
+    public double getTY() {
+
+        if (height == 0)
+            return (tp2.y + tp1.y) / 2d;
+
+        return tp1.y + ((theight * distanceFromY1) / height);
+    }
+
+    public int getX() {
+
+        if (height == 0)
+            return (int) ((p2.x + p1.x) / 2d);
+
+        return (int) (p1.x + ((width * distanceFromY1) / height));
+    }
+
+    public void setCurrentY(final int y) {
+        distanceFromY1 = y - p1.y;
+    }
+
+    public void setPoints(final Point2D p1, final Point2D p2,
+                          final Point2D tp1, final Point2D tp2) {
+
+        this.p1 = p1;
+        this.p2 = p2;
+        this.tp1 = tp1;
+        this.tp2 = tp2;
+
+        height = (int) (p2.y - p1.y);
+        width = (int) (p2.x - p1.x);
+        absoluteHeight = Math.abs(height);
+
+        twidth = tp2.x - tp1.x;
+        theight = tp2.y - tp1.y;
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/texturedpolygon/TexturedPolygon.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/texturedpolygon/TexturedPolygon.java
new file mode 100644 (file)
index 0000000..ff900d8
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.texturedpolygon;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point2D;
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.geometry.Polygon;
+import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractCoordinateShape;
+import eu.svjatoslav.sixth.e3d.renderer.raster.slicer.PolygonCoordinate;
+import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture;
+import eu.svjatoslav.sixth.e3d.renderer.raster.texture.TextureBitmap;
+
+import java.awt.*;
+
+public class TexturedPolygon extends AbstractCoordinateShape {
+
+    public final Texture texture;
+    final PolygonBorderInterpolator[] is = new PolygonBorderInterpolator[]{
+            new PolygonBorderInterpolator(), new PolygonBorderInterpolator(),
+            new PolygonBorderInterpolator()};
+    /**
+     * Polygon texture coordinates.
+     */
+    public Point2D texturePoint1, texturePoint2, texturePoint3;
+    private boolean showBorders = false;
+    private double totalTextureDistance = -1;
+
+    public TexturedPolygon(final Point3D p1, final Point3D p2,
+                           final Point3D p3, final Point2D tp1, final Point2D tp2,
+                           final Point2D tp3, final Texture texture) {
+
+        super(p1, p2, p3);
+
+        texturePoint1 = tp1;
+        texturePoint2 = tp2;
+        texturePoint3 = tp3;
+
+        this.texture = texture;
+    }
+
+    public TexturedPolygon(final PolygonCoordinate pc1,
+                           final PolygonCoordinate pc2, final PolygonCoordinate pc3,
+                           final Texture texture) {
+
+        this(pc1.space, pc2.space, pc3.space, pc1.texture, pc2.texture,
+                pc3.texture, texture);
+    }
+
+    private void computeTotalTextureDistance() {
+        // compute total texture distance
+        totalTextureDistance = texturePoint1.getDistanceTo(texturePoint2);
+        totalTextureDistance += texturePoint1.getDistanceTo(texturePoint3);
+        totalTextureDistance += texturePoint2.getDistanceTo(texturePoint3);
+    }
+
+    private void drawHorizontalLine(final PolygonBorderInterpolator line1,
+                                    final PolygonBorderInterpolator line2, final int y,
+                                    final RenderingContext renderBuffer,
+                                    final TextureBitmap textureBitmap) {
+
+        line1.setCurrentY(y);
+        line2.setCurrentY(y);
+
+        int x1 = line1.getX();
+        int x2 = line2.getX();
+
+        final double tx2, ty2;
+        final double tx1, ty1;
+
+        if (x1 <= x2) {
+
+            tx1 = line1.getTX() * textureBitmap.multiplicationFactor;
+            ty1 = line1.getTY() * textureBitmap.multiplicationFactor;
+
+            tx2 = line2.getTX() * textureBitmap.multiplicationFactor;
+            ty2 = line2.getTY() * textureBitmap.multiplicationFactor;
+
+        } else {
+            final int tmp = x1;
+            x1 = x2;
+            x2 = tmp;
+
+            tx1 = line2.getTX() * textureBitmap.multiplicationFactor;
+            ty1 = line2.getTY() * textureBitmap.multiplicationFactor;
+
+            tx2 = line1.getTX() * textureBitmap.multiplicationFactor;
+            ty2 = line1.getTY() * textureBitmap.multiplicationFactor;
+        }
+
+        final double realWidth = x2 - x1;
+        final double realX1 = x1;
+
+        if (x1 < 0)
+            x1 = 0;
+
+        if (x2 >= renderBuffer.width)
+            x2 = renderBuffer.width - 1;
+
+        int renderBufferOffset = ((y * renderBuffer.width) + x1) * 4;
+        final byte[] renderBufferBytes = renderBuffer.bytes;
+
+        final double twidth = tx2 - tx1;
+        final double theight = ty2 - ty1;
+
+        for (int x = x1; x < x2; x++) {
+
+            final double distance = x - realX1;
+
+            final double tx = tx1 + ((twidth * distance) / realWidth);
+            final double ty = ty1 + ((theight * distance) / realWidth);
+
+            final int textureOffset = textureBitmap.getAddress((int) tx,
+                    (int) ty);
+
+            textureBitmap.drawPixel(textureOffset, renderBufferBytes,
+                    renderBufferOffset);
+
+            renderBufferOffset += 4;
+        }
+
+    }
+
+    @Override
+    public void paint(final RenderingContext renderBuffer) {
+
+        final Point2D projectedPoint1 = coordinates[0].onScreenCoordinate;
+        final Point2D projectedPoint2 = coordinates[1].onScreenCoordinate;
+        final Point2D projectedPoint3 = coordinates[2].onScreenCoordinate;
+
+        projectedPoint1.roundToInteger();
+        projectedPoint2.roundToInteger();
+        projectedPoint3.roundToInteger();
+
+        if (mouseInteractionController != null)
+            if (renderBuffer.mouseClick != null)
+                if (Polygon.pointWithinPolygon(
+                        renderBuffer.mouseClick.coordinate, projectedPoint1,
+                        projectedPoint2, projectedPoint3))
+                    renderBuffer.clickedItem = mouseInteractionController;
+
+        // Show polygon boundaries (for debugging)
+        if (showBorders)
+            showBorders(renderBuffer);
+
+        // find top-most point
+        int yTop = (int) projectedPoint1.y;
+
+        if (projectedPoint2.y < yTop)
+            yTop = (int) projectedPoint2.y;
+
+        if (projectedPoint3.y < yTop)
+            yTop = (int) projectedPoint3.y;
+
+        if (yTop < 0)
+            yTop = 0;
+
+        // find bottom-most point
+        int yBottom = (int) projectedPoint1.y;
+
+        if (projectedPoint2.y > yBottom)
+            yBottom = (int) projectedPoint2.y;
+
+        if (projectedPoint3.y > yBottom)
+            yBottom = (int) projectedPoint3.y;
+
+        if (yBottom >= renderBuffer.height)
+            yBottom = renderBuffer.height - 1;
+
+        // paint
+        double totalVisibleDistance = projectedPoint1
+                .getDistanceTo(projectedPoint2);
+        totalVisibleDistance += projectedPoint1.getDistanceTo(projectedPoint3);
+        totalVisibleDistance += projectedPoint2.getDistanceTo(projectedPoint3);
+
+        if (totalTextureDistance == -1)
+            computeTotalTextureDistance();
+        final double scaleFactor = (totalVisibleDistance / totalTextureDistance) * 1.2d;
+
+        final TextureBitmap zoomedBitmap = texture.getZoomedBitmap(scaleFactor);
+
+        is[0].setPoints(projectedPoint1, projectedPoint2, texturePoint1,
+                texturePoint2);
+        is[1].setPoints(projectedPoint1, projectedPoint3, texturePoint1,
+                texturePoint3);
+        is[2].setPoints(projectedPoint2, projectedPoint3, texturePoint2,
+                texturePoint3);
+
+        java.util.Arrays.sort(is);
+
+        for (int y = yTop; y < yBottom; y++)
+            if (is[0].containsY(y)) {
+
+                if (is[1].containsY(y))
+                    drawHorizontalLine(is[0], is[1], y, renderBuffer,
+                            zoomedBitmap);
+                else if (is[2].containsY(y))
+                    drawHorizontalLine(is[0], is[2], y, renderBuffer,
+                            zoomedBitmap);
+            } else if (is[1].containsY(y))
+                if (is[2].containsY(y))
+                    drawHorizontalLine(is[1], is[2], y, renderBuffer,
+                            zoomedBitmap);
+
+    }
+
+    private void showBorders(final RenderingContext renderBuffer) {
+
+        final Point2D projectedPoint1 = coordinates[0].onScreenCoordinate;
+        final Point2D projectedPoint2 = coordinates[1].onScreenCoordinate;
+        final Point2D projectedPoint3 = coordinates[2].onScreenCoordinate;
+
+        renderBuffer.graphics.setColor(Color.YELLOW);
+        renderBuffer.graphics.drawLine((int) projectedPoint1.x,
+                (int) projectedPoint1.y, (int) projectedPoint2.x,
+                (int) projectedPoint2.y);
+        renderBuffer.graphics.drawLine((int) projectedPoint3.x,
+                (int) projectedPoint3.y, (int) projectedPoint2.x,
+                (int) projectedPoint2.y);
+        renderBuffer.graphics.drawLine((int) projectedPoint1.x,
+                (int) projectedPoint1.y, (int) projectedPoint3.x,
+                (int) projectedPoint3.y);
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/ForwardOrientedTextBlock.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/ForwardOrientedTextBlock.java
new file mode 100644 (file)
index 0000000..49cd9e8
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.ForwardOrientedTexture;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas.TextCanvas;
+import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture;
+
+public class ForwardOrientedTextBlock extends ForwardOrientedTexture {
+
+    public ForwardOrientedTextBlock(final Point3D point, final double scale,
+                                    final int maxUpscaleFactor, final String text,
+                                    final eu.svjatoslav.sixth.e3d.renderer.raster.Color textColor) {
+        super(point, scale, getTexture(text, maxUpscaleFactor, textColor));
+
+    }
+
+    public static Texture getTexture(final String text,
+                                     final int maxUpscaleFactor,
+                                     final eu.svjatoslav.sixth.e3d.renderer.raster.Color textColor) {
+
+        final Texture texture = new Texture(text.length()
+                * TextCanvas.FONT_CHAR_WIDTH, TextCanvas.FONT_CHAR_HEIGHT,
+                maxUpscaleFactor);
+
+        // texture.graphics.setColor(Color.BLUE);
+        // texture.graphics.fillRect(0, 0, texture.primaryBitmap.width,
+        // texture.primaryBitmap.width);
+
+        texture.graphics.setFont(TextCanvas.FONT);
+        texture.graphics.setColor(textColor.toAwtColor());
+
+        for (int c = 0; c < text.length(); c++)
+            texture.graphics.drawChars(new char[]{text.charAt(c),}, 0, 1,
+                    (c * TextCanvas.FONT_CHAR_WIDTH) - 0,
+                    (0 * TextCanvas.FONT_CHAR_HEIGHT) + 11);
+
+        return texture;
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/Galaxy.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/Galaxy.java
new file mode 100755 (executable)
index 0000000..231aeee
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.geometry.Transform;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.ForwardOrientedTexture;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.GlowingPoint;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
+import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture;
+
+import java.util.Random;
+import java.util.Vector;
+
+public class Galaxy extends AbstractCompositeShape {
+
+    private Vector<Texture> textures;
+
+    public Galaxy(final int size, final int tailCount, final int starsCount,
+                  Transform transform) {
+
+        super(transform);
+
+        Point3D location = new Point3D();
+
+        createReusableTextures();
+
+        final double angle1 = Math.random() * 10;
+        final double angle2 = Math.random() * 10;
+
+        final double angleSin1 = Math.sin(angle1);
+        final double angleCos1 = Math.cos(angle1);
+        final double angleSin2 = Math.sin(angle2);
+        final double angleCos2 = Math.cos(angle2);
+
+        Random random = new Random();
+
+        for (int i = 1; i < starsCount; i++) {
+            final double b = Math.random() * 10;
+
+            final double s = (b * b) / 30;
+
+            final double v1 = (Math.random() * (11.5 - b)) / 3;
+            final double v1p = v1 / 2;
+
+            final double ane = ((Math.random() * (s / 2)) / tailCount) * 2;
+            final double sba = ((2 * Math.PI) / tailCount)
+                    * random.nextInt(tailCount);
+
+            final double x = (((Math.sin((b - sba) + ane) * s) + (Math.random() * v1)) - v1p)
+                    * size;
+            final double z = (((Math.cos((b - sba) + ane) * s) + (Math.random() * v1)) - v1p)
+                    * size;
+            final double y = ((Math.random() * v1) - v1p) * size;
+
+            final double x1 = (x * angleCos1) + (z * angleSin1);
+            final double z1 = (z * angleCos1) - (x * angleSin1);
+
+            final double y1 = (y * angleCos2) + (z1 * angleSin2);
+            final double z2 = (z1 * angleCos2) - (y * angleSin2);
+
+            final Point3D point3d = new Point3D(x1 + location.x, y1
+                    + location.y, z2 + location.z);
+
+            addStar(point3d);
+        }
+    }
+
+    public void addStar(final Point3D point3d) {
+
+        final Texture texture = textures.get((int) (Math.random() * textures
+                .size()));
+
+        addShape(new ForwardOrientedTexture(point3d, 10, texture));
+    }
+
+    public void createReusableTextures() {
+        textures = new Vector<>();
+
+        for (int i = 0; i < 30; i++) {
+            final GlowingPoint glowingPoint = new GlowingPoint(null, 1,
+                    new Color(Math.random() + 0.5, Math.random() + 0.5,
+                            Math.random() + 0.5, 255));
+
+            textures.add(glowingPoint.texture);
+
+        }
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/Graph.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/Graph.java
new file mode 100644 (file)
index 0000000..38dbf27
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point2D;
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.geometry.Transform;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.Line;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas.TextCanvas;
+
+import java.io.IOException;
+import java.util.List;
+
+public class Graph extends AbstractCompositeShape {
+
+    private final double width;
+    private final double yMin;
+    private final double yMax;
+    private final double horizontalStep;
+    private final double verticalStep;
+    private final Color gridColor;
+    private final double lineWidth;
+    private final Color plotColor;
+
+    public Graph(final double scale, final List<Point2D> data,
+                 final String label, final Point3D location) throws IOException {
+        super(location);
+
+        width = 20;
+
+        yMin = -2;
+        yMax = 2;
+
+        horizontalStep = 0.5;
+        verticalStep = 0.5;
+
+        gridColor = new Color(100, 100, 250, 100);
+
+        lineWidth = 0.1 * scale;
+        plotColor = new Color(255, 0, 0, 100);
+
+        addVerticalLines(scale);
+        addXlabels(scale);
+        addHorizontalLinesAndLabels(scale);
+        plotData(scale, data);
+
+        final Point3D labelLocation = new Point3D(width / 2, yMax + 0.5, 0)
+                .scaleUp(scale);
+
+        final TextCanvas labelCanvas = new TextCanvas(new Transform(
+                labelLocation), label, Color.WHITE, Color.TRANSPARENT);
+
+        addShape(labelCanvas);
+    }
+
+    public void addHorizontalLinesAndLabels(final double scale)
+            throws IOException {
+        for (double y = yMin; y <= yMax; y += verticalStep) {
+
+            final Point3D p1 = new Point3D(0, y, 0).scaleUp(scale);
+
+            final Point3D p2 = new Point3D(width, y, 0).scaleUp(scale);
+
+            final Line line = new Line(p1, p2, gridColor, lineWidth);
+
+            addShape(line);
+
+            final Point3D labelLocation = new Point3D(-0.5, y, 0)
+                    .scaleUp(scale);
+
+            final TextCanvas label = new TextCanvas(
+                    new Transform(labelLocation), String.valueOf(y),
+                    Color.WHITE, Color.TRANSPARENT);
+
+            addShape(label);
+
+        }
+    }
+
+    public void addVerticalLines(final double scale) {
+        for (double x = 0; x <= width; x += horizontalStep) {
+
+            final Point3D p1 = new Point3D(x, yMin, 0).scaleUp(scale);
+            final Point3D p2 = new Point3D(x, yMax, 0).scaleUp(scale);
+
+            final Line line = new Line(p1, p2, gridColor, lineWidth);
+
+            addShape(line);
+
+        }
+    }
+
+    public void addXlabels(final double scale) throws IOException {
+        for (double x = 0; x <= width; x += horizontalStep * 2) {
+            final Point3D labelLocation = new Point3D(x, yMin - 0.4, 0)
+                    .scaleUp(scale);
+
+            final TextCanvas label = new TextCanvas(
+                    new Transform(labelLocation), String.valueOf(x),
+                    Color.WHITE, Color.TRANSPARENT);
+
+            addShape(label);
+        }
+    }
+
+    public void plotData(final double scale, final List<Point2D> data) {
+        Point3D previousPoint = null;
+        for (final Point2D point : data) {
+
+            final Point3D p3d = new Point3D(point.x, point.y, 0).scaleUp(scale);
+
+            if (previousPoint != null) {
+
+                final Line line = new Line(previousPoint, p3d, plotColor,
+                        0.4 * scale);
+
+                addShape(line);
+            }
+
+            previousPoint = p3d;
+        }
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/LightSourceMarker.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/LightSourceMarker.java
new file mode 100755 (executable)
index 0000000..ef1f4f3
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
+
+public class LightSourceMarker extends AbstractCompositeShape {
+
+    public LightSourceMarker(final Point3D location, final Color color) {
+        super(location);
+        final LineAppearance appearance = new LineAppearance(10);
+
+        final int markerSize = 10;
+
+        addShape(appearance.getLine(new Point3D(0, -markerSize, 0),
+                new Point3D(markerSize, markerSize, -markerSize), color));
+        addShape(appearance.getLine(new Point3D(0, -markerSize, 0),
+                new Point3D(-markerSize, markerSize, -markerSize), color));
+        addShape(appearance.getLine(new Point3D(0, -markerSize, 0),
+                new Point3D(0, markerSize, markerSize), color));
+
+        addShape(appearance.getLine(new Point3D(-markerSize, markerSize,
+                        -markerSize), new Point3D(markerSize, markerSize, -markerSize),
+                color));
+        addShape(appearance.getLine(new Point3D(-markerSize, markerSize,
+                -markerSize), new Point3D(0, markerSize, markerSize), color));
+        addShape(appearance.getLine(new Point3D(0, markerSize, markerSize),
+                new Point3D(markerSize, markerSize, -markerSize), color));
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/TexturedRectangle.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/TexturedRectangle.java
new file mode 100644 (file)
index 0000000..8612f0b
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point2D;
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.geometry.Transform;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.texturedpolygon.TexturedPolygon;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
+import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture;
+
+public class TexturedRectangle extends AbstractCompositeShape {
+
+    public Point3D topLeft;
+    public Point3D topRight;
+    public Point3D bottomRight;
+    public Point3D bottomLeft;
+    public Point2D textureTopLeft;
+    public Point2D textureTopRight;
+    public Point2D textureBottomRight;
+    public Point2D textureBottomLeft;
+    private Texture texture;
+
+    public TexturedRectangle(final Transform transform) {
+        super(transform);
+    }
+
+    public TexturedRectangle(final Transform transform, final int width,
+                             final int height, final int maxTextureUpscale) {
+        this(transform, width, height, width, height, maxTextureUpscale);
+    }
+
+    public TexturedRectangle(final Transform transform, final int width,
+                             final int height, final int textureWidth, final int textureHeight,
+                             final int maxTextureUpscale) {
+
+        super(transform);
+
+        initialize(width, height, textureWidth, textureHeight,
+                maxTextureUpscale);
+    }
+
+    public Texture getTexture() {
+        return texture;
+    }
+
+    public void initialize(final double width, final double height,
+                           final int textureWidth, final int textureHeight,
+                           final int maxTextureUpscale) {
+
+        topLeft = new Point3D(-width / 2, -height / 2, 0);
+        topRight = new Point3D(width / 2, -height / 2, 0);
+        bottomRight = new Point3D(width / 2, height / 2, 0);
+        bottomLeft = new Point3D(-width / 2, height / 2, 0);
+
+        texture = new Texture(textureWidth, textureHeight, maxTextureUpscale);
+
+        textureTopRight = new Point2D(textureWidth, 0);
+        textureTopLeft = new Point2D(0, 0);
+        textureBottomRight = new Point2D(textureWidth, textureHeight);
+        textureBottomLeft = new Point2D(0, textureHeight);
+
+        final TexturedPolygon texturedPolygon1 = new TexturedPolygon(topLeft,
+                topRight, bottomRight, textureTopLeft, textureTopRight,
+                textureBottomRight, texture);
+
+        texturedPolygon1
+                .setMouseInteractionController(mouseInteractionController);
+
+        final TexturedPolygon texturedPolygon2 = new TexturedPolygon(topLeft,
+                bottomLeft, bottomRight, textureTopLeft, textureBottomLeft,
+                textureBottomRight, texture);
+
+        texturedPolygon2
+                .setMouseInteractionController(mouseInteractionController);
+
+        addShape(texturedPolygon1);
+        addShape(texturedPolygon2);
+    }
+
+    public void initialize(final int width, final int height,
+                           final int maxTextureUpscale) {
+        initialize(width, height, width, height, maxTextureUpscale);
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/base/AbstractCompositeShape.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/base/AbstractCompositeShape.java
new file mode 100644 (file)
index 0000000..dd8b784
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.geometry.Transform;
+import eu.svjatoslav.sixth.e3d.geometry.TransformPipe;
+import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
+import eu.svjatoslav.sixth.e3d.gui.UserRelativityTracker;
+import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.RenderAggregator;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractShape;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.Line;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.texturedpolygon.TexturedPolygon;
+import eu.svjatoslav.sixth.e3d.renderer.raster.slicer.Slicer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * In order to get perspective correct textures, large textured polygons are
+ * sliced into smaller ones.
+ */
+
+public class AbstractCompositeShape extends AbstractShape {
+    private final List<SubShape> originalSubShapes = new ArrayList<>();
+    private final UserRelativityTracker relativityTracker;
+    double currentSliceFactor = 5;
+    private List<AbstractShape> renderedSubShapes = new ArrayList<>();
+    private boolean slicingOutdated = true;
+    private Transform transform;
+
+    public AbstractCompositeShape() {
+        this(new Transform());
+    }
+
+    public AbstractCompositeShape(final Point3D location) {
+        this(new Transform(location));
+    }
+
+    public AbstractCompositeShape(final Transform transform) {
+        this.transform = transform;
+        relativityTracker = new UserRelativityTracker();
+    }
+
+    public void addShape(final AbstractShape shape) {
+        addShape(shape, null);
+    }
+
+    public void addShape(final AbstractShape shape, final String groupId) {
+        final SubShape subShape = new SubShape(shape);
+        subShape.setGroup(groupId);
+        subShape.setVisible(true);
+        originalSubShapes.add(subShape);
+        slicingOutdated = true;
+    }
+
+    /**
+     * This method should be overridden by anyone wanting to customize shape
+     * before it is rendered.
+     */
+    public void beforeTransformHook(final TransformPipe transformPipe,
+                                    final RenderingContext context) {
+    }
+
+    public Point3D getLocation() {
+        return transform.getTranslation();
+    }
+
+    public List<SubShape> getOriginalSubShapes() {
+        return originalSubShapes;
+    }
+
+    public UserRelativityTracker getRelativityTracker() {
+        return relativityTracker;
+    }
+
+    public void hideGroup(final String groupIdentifier) {
+        originalSubShapes.stream().filter(subShape -> subShape.matchesGroup(groupIdentifier)).forEach(subShape -> {
+            subShape.setVisible(false);
+            slicingOutdated = true;
+        });
+    }
+
+    private boolean isReslicingNeeded(double sliceFactor1, double sliceFactor2) {
+
+        if (slicingOutdated)
+            return true;
+
+        if (sliceFactor1 > sliceFactor2) {
+            final double tmp = sliceFactor1;
+            sliceFactor1 = sliceFactor2;
+            sliceFactor2 = tmp;
+        }
+
+        return (sliceFactor2 / sliceFactor1) > 1.5d;
+
+    }
+
+    public void removeGroup(final String groupIdentifier) {
+        final java.util.Iterator<SubShape> iterator = originalSubShapes
+                .iterator();
+
+        while (iterator.hasNext()) {
+            final SubShape subShape = iterator.next();
+            if (subShape.matchesGroup(groupIdentifier)) {
+                iterator.remove();
+                slicingOutdated = true;
+            }
+        }
+    }
+
+    private void resliceIfNeeded() {
+
+        final double proposedSliceFactor = relativityTracker
+                .proposeSliceFactor();
+
+        if (isReslicingNeeded(proposedSliceFactor, currentSliceFactor)) {
+            currentSliceFactor = proposedSliceFactor;
+            slice();
+        }
+    }
+
+    /**
+     * Paint solid elements of this composite shape into given color.
+     */
+    public void setColor(final Color color) {
+        for (final SubShape subShape : getOriginalSubShapes()) {
+            final AbstractShape shape = subShape.getShape();
+
+            if (shape instanceof SolidPolygon)
+                ((SolidPolygon) shape).setColor(color);
+
+            if (shape instanceof Line)
+                ((Line) shape).color = color;
+        }
+    }
+
+    public void setGroupForUngrouped(final String groupIdentifier) {
+        originalSubShapes.stream().filter(subShape -> subShape.isUngrouped()).forEach(subShape -> subShape.setGroup(groupIdentifier));
+    }
+
+    @Override
+    public void setMouseInteractionController(
+            final MouseInteractionController mouseInteractionController) {
+        super.setMouseInteractionController(mouseInteractionController);
+
+        for (final SubShape subShape : originalSubShapes)
+            subShape.getShape().setMouseInteractionController(
+                    mouseInteractionController);
+
+        slicingOutdated = true;
+
+    }
+
+    public void setTransform(final Transform transform) {
+        this.transform = transform;
+    }
+
+    public void showGroup(final String groupIdentifier) {
+        originalSubShapes.stream().filter(subShape -> subShape.matchesGroup(groupIdentifier)).forEach(subShape -> {
+            subShape.setVisible(true);
+            slicingOutdated = true;
+        });
+    }
+
+    private void slice() {
+        slicingOutdated = false;
+
+        final List<AbstractShape> result = new ArrayList<>();
+
+        final Slicer slicer = new Slicer(currentSliceFactor);
+        originalSubShapes.stream().filter(subShape -> subShape.isVisible()).forEach(subShape -> {
+            if (subShape.getShape() instanceof TexturedPolygon)
+                slicer.slice((TexturedPolygon) subShape.getShape());
+            else
+                result.add(subShape.getShape());
+        });
+
+        result.addAll(slicer.getResult());
+
+        renderedSubShapes = result;
+    }
+
+    @Override
+    public void transform(final TransformPipe transformPipe,
+                          final RenderAggregator aggregator, final RenderingContext context) {
+
+        // add current composite shape transform to the end of the transform
+        // pipeline
+        transformPipe.addTransform(transform);
+
+        relativityTracker.analyze(transformPipe, context);
+
+        beforeTransformHook(transformPipe, context);
+
+        // hack, to get somewhat perspective correct textures
+        resliceIfNeeded();
+
+        // transform rendered subshapes
+        for (final AbstractShape shape : renderedSubShapes)
+            shape.transform(transformPipe, aggregator, context);
+
+        transformPipe.dropTransform();
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/base/SubShape.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/base/SubShape.java
new file mode 100644 (file)
index 0000000..4b1ded6
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base;
+
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractShape;
+
+public class SubShape {
+
+    private final AbstractShape shape;
+    private boolean visible = true;
+    private String groupIdentifier;
+
+    public SubShape(AbstractShape shape) {
+        this.shape = shape;
+    }
+
+    public boolean isUngrouped() {
+        return groupIdentifier == null;
+    }
+
+    public boolean matchesGroup(final String groupIdentifier) {
+        if (this.groupIdentifier == null)
+            return groupIdentifier == null;
+
+        return this.groupIdentifier.equals(groupIdentifier);
+    }
+
+    public void setGroup(final String groupIdentifier) {
+        this.groupIdentifier = groupIdentifier;
+    }
+
+    public AbstractShape getShape() {
+        return shape;
+    }
+
+    public boolean isVisible() {
+        return visible;
+    }
+
+    public void setVisible(boolean visible) {
+        this.visible = visible;
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/solid/SolidPolygonCube.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/solid/SolidPolygonCube.java
new file mode 100755 (executable)
index 0000000..a768008
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+
+public class SolidPolygonCube extends SolidPolygonRectangularBox {
+
+    public SolidPolygonCube(final Point3D center, final double size,
+                            final Color color) {
+        super(new Point3D(center.x - size, center.y - size, center.z - size),
+                new Point3D(center.x + size, center.y + size, center.z + size),
+                color);
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/solid/SolidPolygonRectangularBox.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/solid/SolidPolygonRectangularBox.java
new file mode 100755 (executable)
index 0000000..1f149f4
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2015, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+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;
+
+public class SolidPolygonRectangularBox extends AbstractCompositeShape {
+
+    /**
+     * Draws solid rectangular box between using center and size.
+     *
+     * @param center center of the box
+     * @param size   size of the box
+     * @param color  box color
+     */
+    public SolidPolygonRectangularBox(final Point3D center, final double size, final Color color) {
+        this(//
+                new Point3D(center.x - (size / 2), center.y - (size / 2), center.z - (size / 2)), //
+                new Point3D(center.x + (size / 2), center.y + (size / 2), center.z + (size / 2)), //
+                color);
+    }
+
+    /**
+     * Draws solid rectangular box between 2 given points in 3D space.
+     */
+    public SolidPolygonRectangularBox(final Point3D p1, final Point3D p7, final Color color) {
+        super();
+
+        final Point3D p2 = new Point3D(p7.x, p1.y, p1.z);
+        final Point3D p3 = new Point3D(p7.x, p1.y, p7.z);
+        final Point3D p4 = new Point3D(p1.x, p1.y, p7.z);
+
+        final Point3D p5 = new Point3D(p1.x, p7.y, p1.z);
+        final Point3D p6 = new Point3D(p7.x, p7.y, p1.z);
+        final Point3D p8 = new Point3D(p1.x, p7.y, p7.z);
+
+        addShape(new SolidPolygon(p1, p2, p3, color));
+        addShape(new SolidPolygon(p1, p4, p3, color));
+
+        addShape(new SolidPolygon(p5, p6, p7, color));
+        addShape(new SolidPolygon(p5, p8, p7, color));
+
+        addShape(new SolidPolygon(p4, p3, p8, color));
+        addShape(new SolidPolygon(p3, p7, p8, color));
+
+        addShape(new SolidPolygon(p1, p5, p4, color));
+        addShape(new SolidPolygon(p4, p5, p8, color));
+
+        addShape(new SolidPolygon(p1, p2, p5, color));
+        addShape(new SolidPolygon(p2, p6, p5, color));
+
+        addShape(new SolidPolygon(p2, p6, p3, color));
+        addShape(new SolidPolygon(p6, p7, p3, color));
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/textcanvas/CanvasCharacter.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/textcanvas/CanvasCharacter.java
new file mode 100644 (file)
index 0000000..863b362
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point2D;
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractCoordinateShape;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon;
+
+import java.awt.*;
+
+public class CanvasCharacter extends AbstractCoordinateShape {
+
+    public static final double SIZE_MULTIPLIER = 0.005;
+    private static final int MAX_FONT_SIZE = 500;
+    private static final Font[] fonts = new Font[MAX_FONT_SIZE];
+    private String value;
+
+    private eu.svjatoslav.sixth.e3d.renderer.raster.Color foregroundColor;
+    private Color foregroundAwtColor;
+
+    private eu.svjatoslav.sixth.e3d.renderer.raster.Color backgroundColor;
+
+    public CanvasCharacter(final Point3D point, final char character,
+                           final eu.svjatoslav.sixth.e3d.renderer.raster.Color foregroundColor,
+                           final eu.svjatoslav.sixth.e3d.renderer.raster.Color backgroundColor) {
+
+        super(5);
+        coordinates[0].coordinate = point;
+
+        value = String.valueOf(character);
+
+        this.foregroundColor = foregroundColor;
+        foregroundAwtColor = foregroundColor.toAwtColor();
+
+        this.backgroundColor = backgroundColor;
+
+        // set corner coordinates (for drawing background)
+        {
+            final double widthHalf = TextCanvas.FONT_CHAR_WIDTH / 2d;
+            final double heightHalf = TextCanvas.FONT_CHAR_HEIGHT / 2d;
+
+            // upper left
+            coordinates[1].coordinate = point.clone().translateX(-widthHalf)
+                    .translateY(-heightHalf);
+
+            // upper right
+            coordinates[2].coordinate = point.clone().translateX(widthHalf)
+                    .translateY(-heightHalf);
+
+            // lower right
+            coordinates[3].coordinate = point.clone().translateX(widthHalf)
+                    .translateY(heightHalf);
+
+            // lower left
+            coordinates[4].coordinate = point.clone().translateX(-widthHalf)
+                    .translateY(heightHalf);
+
+        }
+    }
+
+    public static Font getFont(final int size) {
+        if (fonts[size] != null)
+            return fonts[size];
+
+        final Font font = new Font("Courier", Font.BOLD, size);
+        fonts[size] = font;
+        return font;
+    }
+
+    public eu.svjatoslav.sixth.e3d.renderer.raster.Color getBackgroundColor() {
+        return backgroundColor;
+    }
+
+    public void setBackgroundColor(
+            final eu.svjatoslav.sixth.e3d.renderer.raster.Color backgroundColor) {
+        this.backgroundColor = backgroundColor;
+    }
+
+    public eu.svjatoslav.sixth.e3d.renderer.raster.Color getForegroundColor() {
+        return foregroundColor;
+    }
+
+    public void setForegroundColor(
+            final eu.svjatoslav.sixth.e3d.renderer.raster.Color foregroundColor) {
+        this.foregroundColor = foregroundColor;
+        foregroundAwtColor = foregroundColor.toAwtColor();
+    }
+
+    @Override
+    public void paint(final RenderingContext renderingContext) {
+
+        SolidPolygon.drawPolygon(renderingContext,
+                coordinates[1].onScreenCoordinate,
+                coordinates[2].onScreenCoordinate,
+                coordinates[3].onScreenCoordinate, mouseInteractionController,
+                backgroundColor);
+
+        SolidPolygon.drawPolygon(renderingContext,
+                coordinates[1].onScreenCoordinate,
+                coordinates[3].onScreenCoordinate,
+                coordinates[4].onScreenCoordinate, mouseInteractionController,
+                backgroundColor);
+
+        final int size = (int) ((renderingContext.width * 4.5) / onScreenZ);
+
+        // do not render too large characters
+        if (size >= MAX_FONT_SIZE)
+            return;
+
+        final Point2D onScreenLocation = coordinates[0].onScreenCoordinate;
+
+        // screen borders check
+        if (onScreenLocation.x < 0)
+            return;
+        if (onScreenLocation.y < 0)
+            return;
+
+        if (onScreenLocation.x > renderingContext.width)
+            return;
+        if (onScreenLocation.y > renderingContext.height)
+            return;
+
+        renderingContext.graphics.setFont(getFont(size));
+        renderingContext.graphics.setColor(foregroundAwtColor);
+        renderingContext.graphics.drawString(value, (int) onScreenLocation.x
+                - (int) (size / 3.2), (int) onScreenLocation.y
+                + (int) (size / 2.5));
+
+    }
+
+    public void setValue(final char value) {
+        this.value = String.valueOf(value);
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/textcanvas/RenderMode.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/textcanvas/RenderMode.java
new file mode 100644 (file)
index 0000000..ecb07e8
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas;
+
+public enum RenderMode {
+    TEXTURE, CHARACTERS
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/textcanvas/TextCanvas.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/textcanvas/TextCanvas.java
new file mode 100644 (file)
index 0000000..5c594f2
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.geometry.Transform;
+import eu.svjatoslav.sixth.e3d.geometry.TransformPipe;
+import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
+import eu.svjatoslav.sixth.e3d.gui.TextPointer;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.TexturedRectangle;
+
+import java.awt.*;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+
+public class TextCanvas extends TexturedRectangle {
+
+    public static final int FONT_CHAR_WIDTH = 8;
+    public static final int FONT_CHAR_HEIGHT = 16;
+    public static final Font FONT = CanvasCharacter.getFont(15);
+    private static final String GROUP_TEXTURE = "texture";
+    private static final String GROUP_CHARACTERS = "characters";
+    private final TextPointer size;
+    private final TextPointer cursorLocation = new TextPointer();
+    CanvasCharacter lines[][];
+    private RenderMode renderMode = null;
+    private Color backgroundColor = Color.BLACK;
+
+    private Color foregroundColor = Color.WHITE;
+
+    public TextCanvas(final Transform location, final String text,
+                      final Color foregroundColor, final Color backgroundColor)
+            throws IOException {
+        this(location, getTextDimensions(text), foregroundColor,
+                backgroundColor);
+        setText(text);
+    }
+
+    public TextCanvas(final Transform location, final TextPointer dimensions,
+                      final Color foregroundColor, final Color backgroundColor) {
+        super(location);
+        getRelativityTracker().enableOrientationTracking();
+
+        size = dimensions;
+        final int columns = dimensions.column;
+        final int rows = dimensions.row;
+
+        this.backgroundColor = backgroundColor;
+        this.foregroundColor = foregroundColor;
+
+        // initialize underlying textured rectangle
+        initialize(columns * FONT_CHAR_WIDTH, rows * FONT_CHAR_HEIGHT, 0);
+
+        getTexture().primaryBitmap.fillColor(backgroundColor);
+        getTexture().resetResampledBitmapCache();
+
+        setGroupForUngrouped(GROUP_TEXTURE);
+
+        lines = new CanvasCharacter[rows][];
+        for (int row = 0; row < rows; row++) {
+            lines[row] = new CanvasCharacter[columns];
+
+            for (int column = 0; column < columns; column++) {
+                final Point3D characterCoordinate = getCharLocation(row, column);
+
+                final CanvasCharacter character = new CanvasCharacter(
+                        characterCoordinate, ' ', foregroundColor,
+                        backgroundColor);
+                addShape(character);
+                lines[row][column] = character;
+            }
+
+        }
+
+        setGroupForUngrouped(GROUP_CHARACTERS);
+
+        setRenderMode(RenderMode.TEXTURE);
+    }
+
+    public static TextPointer getTextDimensions(final String text)
+            throws IOException {
+
+        final BufferedReader reader = new BufferedReader(new StringReader(text));
+
+        int rows = 0;
+        int columns = 0;
+
+        while (true) {
+            final String line = reader.readLine();
+
+            if (line == null)
+                return new TextPointer(rows, columns);
+
+            rows++;
+            columns = Math.max(columns, line.length());
+        }
+    }
+
+    @Override
+    public void beforeTransformHook(final TransformPipe transformPipe,
+                                    final RenderingContext context) {
+
+        final double textRelativeSize = context.width
+                / getRelativityTracker().getDistanceToUser();
+
+        if (textRelativeSize < 2d) {
+            if (renderMode == RenderMode.CHARACTERS)
+                setRenderMode(RenderMode.TEXTURE);
+            return;
+        }
+
+        final double piHalf = Math.PI / 2;
+
+        final double deviation = Math.abs(getRelativityTracker().getAngleXZ()
+                + piHalf)
+                + Math.abs(getRelativityTracker().getAngleYZ() + piHalf);
+
+        final double maxDeviation = 0.5;
+
+        if (deviation > maxDeviation) {
+            if (renderMode == RenderMode.CHARACTERS)
+                setRenderMode(RenderMode.TEXTURE);
+        } else if (renderMode == RenderMode.TEXTURE)
+            setRenderMode(RenderMode.CHARACTERS);
+    }
+
+    public void cls() {
+        for (final CanvasCharacter[] line : lines)
+            for (final CanvasCharacter character : line) {
+                character.setValue(' ');
+                character.setBackgroundColor(backgroundColor);
+                character.setForegroundColor(foregroundColor);
+            }
+
+        // set background color
+        getTexture().primaryBitmap.fillColor(backgroundColor);
+        getTexture().resetResampledBitmapCache();
+    }
+
+    private void drawCharToTexture(final int row, final int column,
+                                   final char character, final Color background, final Color foreground) {
+        final Graphics2D graphics = getTexture().graphics;
+
+        getTexture().primaryBitmap.drawRectangle(column * FONT_CHAR_WIDTH, row
+                        * FONT_CHAR_HEIGHT, (column * FONT_CHAR_WIDTH)
+                        + FONT_CHAR_WIDTH, (row * FONT_CHAR_HEIGHT) + FONT_CHAR_HEIGHT,
+                backgroundColor);
+
+        graphics.setFont(FONT);
+        graphics.setColor(foreground.toAwtColor());
+        graphics.drawChars(new char[]{character,}, 0, 1,
+                (column * FONT_CHAR_WIDTH) - 0, (row * FONT_CHAR_HEIGHT) + 13);
+
+        getTexture().resetResampledBitmapCache();
+    }
+
+    public Point3D getCharLocation(final int row, final int column) {
+        final Point3D coordinate = topLeft.clone();
+
+        coordinate.translateY((row * FONT_CHAR_HEIGHT)
+                + ((FONT_CHAR_HEIGHT / 2) - 3));
+
+        coordinate.translateX((column * FONT_CHAR_WIDTH)
+                + (FONT_CHAR_WIDTH / 2));
+
+        return coordinate;
+    }
+
+    public TextPointer getSize() {
+        return size;
+    }
+
+    public void locate(final int row, final int column) {
+        cursorLocation.row = row;
+        cursorLocation.column = column;
+    }
+
+    public void print(final String text) {
+        for (final char c : text.toCharArray())
+            putChar(c);
+    }
+
+    public void putChar(final char character) {
+        putChar(cursorLocation, character);
+
+        cursorLocation.column++;
+        if (cursorLocation.column >= size.column) {
+            cursorLocation.column = 0;
+            cursorLocation.row++;
+        }
+    }
+
+    public void putChar(final int row, final int column, final char character) {
+        if ((row >= lines.length) || (row < 0))
+            return;
+
+        final CanvasCharacter[] line = lines[row];
+
+        if ((column >= line.length) || (column < 0))
+            return;
+
+        final CanvasCharacter canvasCharacter = line[column];
+        canvasCharacter.setValue(character);
+        canvasCharacter.setBackgroundColor(backgroundColor);
+        canvasCharacter.setForegroundColor(foregroundColor);
+        drawCharToTexture(row, column, character, backgroundColor,
+                foregroundColor);
+    }
+
+    public void putChar(final TextPointer location, final char character) {
+        putChar(location.row, location.column, character);
+    }
+
+    public void setBackgroundColor(
+            final eu.svjatoslav.sixth.e3d.renderer.raster.Color backgroundColor) {
+        this.backgroundColor = backgroundColor;
+    }
+
+    public void setForegroundColor(
+            final eu.svjatoslav.sixth.e3d.renderer.raster.Color foregroundColor) {
+        this.foregroundColor = foregroundColor;
+    }
+
+    private void setRenderMode(final RenderMode mode) {
+        if (mode == renderMode)
+            return;
+
+        switch (mode) {
+            case CHARACTERS:
+                hideGroup(GROUP_TEXTURE);
+                showGroup(GROUP_CHARACTERS);
+                break;
+            case TEXTURE:
+                hideGroup(GROUP_CHARACTERS);
+                showGroup(GROUP_TEXTURE);
+                break;
+        }
+
+        renderMode = mode;
+    }
+
+    public void setText(final String text) throws IOException {
+        final BufferedReader reader = new BufferedReader(new StringReader(text));
+
+        int row = 0;
+
+        while (true) {
+            final String line = reader.readLine();
+
+            if (line == null)
+                return;
+
+            int column = 0;
+            for (final char c : line.toCharArray()) {
+                putChar(row, column, c);
+                column++;
+            }
+            row++;
+        }
+    }
+
+    public void setTextColor(final Color color) {
+        for (final CanvasCharacter[] line : lines)
+            for (final CanvasCharacter character : line)
+                character.setForegroundColor(color);
+    }
+
+    // @Override
+    // public void transform(final TransformPipe transformPipe,
+    // final RenderAggregator aggregator, final RenderingContext buffer) {
+    //
+    // super.transform(transformPipe, aggregator, buffer);
+    //
+    // }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/Grid2D.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/Grid2D.java
new file mode 100644 (file)
index 0000000..3e685d1
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.geometry.Rectangle;
+import eu.svjatoslav.sixth.e3d.geometry.Transform;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
+
+public class Grid2D extends AbstractCompositeShape {
+
+    public Grid2D(final Transform transform, final Rectangle rectangle,
+                  final int divisionsX, final int divisionsY,
+                  final LineAppearance appearance) {
+
+        super(transform);
+
+        final double stepY = rectangle.getHeight() / divisionsY;
+        final double stepX = rectangle.getWidth() / divisionsX;
+
+        for (int yslice = 0; yslice <= divisionsY; yslice++) {
+            final double y = (yslice * stepY) + rectangle.getLowerY();
+
+            for (int xslice = 0; xslice <= divisionsX; xslice++) {
+                final double x = (xslice * stepX) + rectangle.getLowerX();
+
+                final Point3D p1 = new Point3D(x, y, 0);
+                final Point3D p2 = new Point3D(x + stepX, y, 0);
+                final Point3D p3 = new Point3D(x, y + stepY, 0);
+
+                if (xslice < divisionsX)
+                    addShape(appearance.getLine(p1, p2));
+
+                if (yslice < divisionsY)
+                    addShape(appearance.getLine(p1, p3));
+            }
+
+        }
+
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/Grid3D.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/Grid3D.java
new file mode 100755 (executable)
index 0000000..c84141b
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
+
+public class Grid3D extends AbstractCompositeShape {
+
+    public Grid3D(final Point3D p1t, final Point3D p2t, final double step,
+                  final LineAppearance appearance) {
+
+        super();
+
+        final Point3D p1 = new Point3D(p1t);
+        final Point3D p2 = new Point3D(p2t);
+
+        if (p1.x > p2.x) {
+            final double tmp = p1.x;
+            p1.x = p2.x;
+            p2.x = tmp;
+        }
+
+        if (p1.y > p2.y) {
+            final double tmp = p1.y;
+            p1.y = p2.y;
+            p2.y = tmp;
+        }
+
+        if (p1.z > p2.z) {
+            final double tmp = p1.z;
+            p1.z = p2.z;
+            p2.z = tmp;
+        }
+
+        for (double x = p1.x; x <= p2.x; x += step)
+            for (double y = p1.y; y <= p2.y; y += step)
+                for (double z = p1.z; z <= p2.z; z += step) {
+
+                    final Point3D p3 = new Point3D(x, y, z);
+
+                    if ((x + step) <= p2.x) {
+                        final Point3D point3d2 = new Point3D(x + step, y, z);
+                        addShape(appearance.getLine(p3, point3d2));
+                    }
+
+                    if ((y + step) <= p2.y) {
+                        final Point3D point3d3 = new Point3D(x, y + step, z);
+                        addShape(appearance.getLine(p3, point3d3));
+                    }
+
+                    if ((z + step) <= p2.z) {
+                        final Point3D point3d4 = new Point3D(x, y, z + step);
+                        addShape(appearance.getLine(p3, point3d4));
+                    }
+
+                }
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeBox.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeBox.java
new file mode 100755 (executable)
index 0000000..fa8f8c8
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe;
+
+import eu.svjatoslav.sixth.e3d.geometry.Box;
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
+
+public class WireframeBox extends AbstractCompositeShape {
+
+    public WireframeBox(final Box box,
+                        final LineAppearance appearance) {
+
+        this(box.p1, box.p2, appearance);
+    }
+
+    public WireframeBox(final Point3D p1, final Point3D p2,
+                        final LineAppearance appearance) {
+        super();
+
+        addShape(appearance.getLine(new Point3D(p1.x, p1.y, p1.z), new Point3D(
+                p2.x, p1.y, p1.z)));
+        addShape(appearance.getLine(new Point3D(p1.x, p2.y, p1.z), new Point3D(
+                p2.x, p2.y, p1.z)));
+        addShape(appearance.getLine(new Point3D(p1.x, p1.y, p1.z), new Point3D(
+                p1.x, p2.y, p1.z)));
+        addShape(appearance.getLine(new Point3D(p2.x, p1.y, p1.z), new Point3D(
+                p2.x, p2.y, p1.z)));
+
+        addShape(appearance.getLine(new Point3D(p1.x, p1.y, p2.z), new Point3D(
+                p2.x, p1.y, p2.z)));
+        addShape(appearance.getLine(new Point3D(p1.x, p2.y, p2.z), new Point3D(
+                p2.x, p2.y, p2.z)));
+        addShape(appearance.getLine(new Point3D(p1.x, p1.y, p2.z), new Point3D(
+                p1.x, p2.y, p2.z)));
+        addShape(appearance.getLine(new Point3D(p2.x, p1.y, p2.z), new Point3D(
+                p2.x, p2.y, p2.z)));
+
+        addShape(appearance.getLine(new Point3D(p1.x, p1.y, p1.z), new Point3D(
+                p1.x, p1.y, p2.z)));
+        addShape(appearance.getLine(new Point3D(p1.x, p2.y, p1.z), new Point3D(
+                p1.x, p2.y, p2.z)));
+        addShape(appearance.getLine(new Point3D(p2.x, p1.y, p1.z), new Point3D(
+                p2.x, p1.y, p2.z)));
+        addShape(appearance.getLine(new Point3D(p2.x, p2.y, p1.z), new Point3D(
+                p2.x, p2.y, p2.z)));
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeCube.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeCube.java
new file mode 100755 (executable)
index 0000000..3881110
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+
+public class WireframeCube extends WireframeBox {
+
+    public WireframeCube(final Point3D center, final double size,
+                         final LineAppearance appearance) {
+        super(new Point3D(center.x - size, center.y - size, center.z - size),
+                new Point3D(center.x + size, center.y + size, center.z + size),
+                appearance);
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeDrawing.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeDrawing.java
new file mode 100755 (executable)
index 0000000..0ed7a05
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.Line;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
+
+public class WireframeDrawing extends AbstractCompositeShape {
+
+    final private LineAppearance lineAppearance;
+    Point3D currentPoint;
+
+    public WireframeDrawing(final LineAppearance lineAppearance) {
+        super();
+        this.lineAppearance = lineAppearance;
+    }
+
+    public void addPoint(final Point3D point3d) {
+        if (currentPoint != null) {
+            final Line line = lineAppearance.getLine(currentPoint, point3d);
+            addShape(line);
+        }
+
+        currentPoint = new Point3D(point3d);
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeSphere.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeSphere.java
new file mode 100755 (executable)
index 0000000..bc3e827
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
+
+import java.util.ArrayList;
+
+public class WireframeSphere extends AbstractCompositeShape {
+
+    ArrayList<Point3D> previousRing = new ArrayList<>();
+
+    public WireframeSphere(final Point3D location, final float radius,
+                           final LineAppearance lineFactory) {
+        super(location);
+
+        final double step = Math.PI / 10;
+
+        final Point3D center = new Point3D();
+
+        int ringIndex = 0;
+
+        for (double j = 0d; j <= (Math.PI * 2); j += step) {
+
+            Point3D oldPoint = null;
+            int pointIndex = 0;
+
+            for (double i = 0; i <= (Math.PI * 2); i += step) {
+                final Point3D newPoint = new Point3D(0, 0, radius);
+                newPoint.rotate(center, i, j);
+
+                if (oldPoint != null)
+                    addShape(lineFactory.getLine(newPoint, oldPoint));
+
+                if (ringIndex > 0) {
+                    final Point3D previousRingPoint = previousRing
+                            .get(pointIndex);
+                    addShape(lineFactory.getLine(newPoint, previousRingPoint));
+
+                    previousRing.set(pointIndex, newPoint);
+                } else
+                    previousRing.add(newPoint);
+
+                oldPoint = newPoint;
+                pointIndex++;
+            }
+
+            ringIndex++;
+        }
+
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/slicer/BorderLine.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/slicer/BorderLine.java
new file mode 100644 (file)
index 0000000..70693f7
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.slicer;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point2D;
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+
+import java.util.Comparator;
+
+public class BorderLine implements Comparator<BorderLine> {
+
+    public final int count;
+    final PolygonCoordinate c1;
+    final PolygonCoordinate c2;
+
+    public BorderLine(final PolygonCoordinate c1, final PolygonCoordinate c2,
+                      final int count) {
+        this.c1 = c1;
+        this.c2 = c2;
+        this.count = count;
+    }
+
+    @Override
+    public int compare(final BorderLine o1, final BorderLine o2) {
+        return Double.compare(o1.getLength(), o2.getLength());
+    }
+
+    public double getLength() {
+        return c1.space.getDistanceTo(c2.space);
+    }
+
+    public PolygonCoordinate getMiddlePoint() {
+        return new PolygonCoordinate(new Point3D().computeMiddlePoint(c1.space,
+                c2.space), new Point2D().computeMiddlePoint(c1.texture,
+                c2.texture));
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/slicer/PolygonCoordinate.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/slicer/PolygonCoordinate.java
new file mode 100644 (file)
index 0000000..e338145
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.slicer;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point2D;
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+
+public class PolygonCoordinate {
+
+    public Point3D space;
+    public Point2D texture;
+
+    public PolygonCoordinate(final Point3D space, final Point2D texture) {
+        this.space = space;
+        this.texture = texture;
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/slicer/Slicer.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/slicer/Slicer.java
new file mode 100644 (file)
index 0000000..edfba1c
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.slicer;
+
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.texturedpolygon.TexturedPolygon;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class Slicer {
+
+    private final double maxDistance;
+    private final List<TexturedPolygon> result = new ArrayList<>();
+
+    public Slicer(final double maxDistance) {
+        this.maxDistance = maxDistance;
+    }
+
+    private void considerSlicing(final PolygonCoordinate c1,
+                                 final PolygonCoordinate c2, final PolygonCoordinate c3,
+                                 final TexturedPolygon originalPolygon) {
+
+        final BorderLine lines[] = new BorderLine[]{
+                new BorderLine(c1, c2, 1), new BorderLine(c2, c3, 2),
+                new BorderLine(c3, c1, 3)};
+
+        Arrays.sort(lines, lines[0]);
+
+        final BorderLine longestLine = lines[2];
+
+        if (longestLine.getLength() < maxDistance) {
+            final TexturedPolygon polygon = new TexturedPolygon(c1, c2, c3,
+                    originalPolygon.texture);
+
+            polygon.setMouseInteractionController(originalPolygon.mouseInteractionController);
+
+            getResult().add(polygon);
+            return;
+        }
+
+        final PolygonCoordinate middle = longestLine.getMiddlePoint();
+
+        switch (longestLine.count) {
+            case 1:
+                considerSlicing(c1, middle, c3, originalPolygon);
+                considerSlicing(middle, c2, c3, originalPolygon);
+                return;
+            case 2:
+                considerSlicing(c1, c2, middle, originalPolygon);
+                considerSlicing(middle, c3, c1, originalPolygon);
+                return;
+            case 3:
+                considerSlicing(c1, c2, middle, originalPolygon);
+                considerSlicing(middle, c2, c3, originalPolygon);
+        }
+
+    }
+
+    public List<TexturedPolygon> getResult() {
+        return result;
+    }
+
+    public void slice(final TexturedPolygon originalPolygon) {
+
+        final PolygonCoordinate pc1 = new PolygonCoordinate(
+                originalPolygon.coordinates[0].coordinate,
+                originalPolygon.texturePoint1);
+
+        final PolygonCoordinate pc2 = new PolygonCoordinate(
+                originalPolygon.coordinates[1].coordinate,
+                originalPolygon.texturePoint2);
+
+        final PolygonCoordinate pc3 = new PolygonCoordinate(
+                originalPolygon.coordinates[2].coordinate,
+                originalPolygon.texturePoint3);
+
+        considerSlicing(pc1, pc2, pc3, originalPolygon);
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/Texture.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/Texture.java
new file mode 100644 (file)
index 0000000..d5041f3
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.texture;
+
+import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferByte;
+import java.awt.image.WritableRaster;
+
+public class Texture {
+
+    public final TextureBitmap primaryBitmap;
+    public final java.awt.Graphics2D graphics;
+    TextureBitmap[] upSampled;
+    TextureBitmap[] downSampled = new TextureBitmap[8];
+
+    public Texture(final int width, final int height, final int maxUpscale) {
+        upSampled = new TextureBitmap[maxUpscale];
+
+        final BufferedImage bufferedImage = new BufferedImage(width, height,
+                RenderingContext.bufferedImageType);
+
+        final WritableRaster raster = bufferedImage.getRaster();
+        final DataBufferByte dbi = (DataBufferByte) raster.getDataBuffer();
+        graphics = (Graphics2D) bufferedImage.getGraphics();
+
+        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+                RenderingHints.VALUE_ANTIALIAS_ON);
+
+        graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
+                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+
+        primaryBitmap = new TextureBitmap(width, height, dbi.getData(), 1);
+    }
+
+    public int detectDownscaleFactorForZoom(final double zoom) {
+        double size = 1;
+        for (int i = 0; i < downSampled.length; i++) {
+            size = size / 2;
+            if (size < zoom)
+                return i;
+        }
+
+        return downSampled.length - 1;
+    }
+
+    public int detectUpscaleFactorForZoom(final double zoom) {
+        double size = 2;
+        for (int i = 0; i < upSampled.length; i++) {
+            size = size * 2;
+            if (size > zoom)
+                return i;
+        }
+
+        return upSampled.length - 1;
+    }
+
+    public TextureBitmap downscaleBitmap(final TextureBitmap originalBitmap) {
+        int newWidth = originalBitmap.width / 2;
+        int newHeight = originalBitmap.height / 2;
+
+        // Enforce minimum width and height
+        if (newWidth < 1)
+            newWidth = 1;
+        if (newHeight < 1)
+            newHeight = 1;
+
+        final TextureBitmap downScaled = new TextureBitmap(newWidth, newHeight,
+                originalBitmap.multiplicationFactor / 2d);
+
+        final ColorAccumulator accumulator = new ColorAccumulator();
+
+        for (int y = 0; y < newHeight; y++)
+            for (int x = 0; x < newWidth; x++) {
+                accumulator.reset();
+                accumulator.accumulate(originalBitmap, x * 2, y * 2);
+                accumulator.accumulate(originalBitmap, (x * 2) + 1, y * 2);
+                accumulator.accumulate(originalBitmap, x * 2, (y * 2) + 1);
+                accumulator
+                        .accumulate(originalBitmap, (x * 2) + 1, (y * 2) + 1);
+                accumulator.storeResult(downScaled, x, y);
+            }
+
+        return downScaled;
+    }
+
+    public TextureBitmap getDownscaledBitmap(final int scaleFactor) {
+        if (downSampled[scaleFactor] == null) {
+
+            TextureBitmap largerBitmap;
+            if (scaleFactor == 0)
+                largerBitmap = primaryBitmap;
+            else
+                largerBitmap = getDownscaledBitmap(scaleFactor - 1);
+
+            downSampled[scaleFactor] = downscaleBitmap(largerBitmap);
+        }
+
+        return downSampled[scaleFactor];
+    }
+
+    public TextureBitmap getUpscaledBitmap(final int scaleFactor) {
+        if (upSampled[scaleFactor] == null) {
+
+            TextureBitmap smallerBitmap;
+            if (scaleFactor == 0)
+                smallerBitmap = primaryBitmap;
+            else
+                smallerBitmap = getUpscaledBitmap(scaleFactor - 1);
+
+            upSampled[scaleFactor] = upscaleBitmap(smallerBitmap);
+        }
+
+        return upSampled[scaleFactor];
+    }
+
+    public TextureBitmap getZoomedBitmap(final double zoomLevel) {
+
+        if (zoomLevel < 1) {
+            final int downscaleFactor = detectDownscaleFactorForZoom(zoomLevel);
+            return getDownscaledBitmap(downscaleFactor);
+        } else if (zoomLevel > 2) {
+            final int upscaleFactor = detectUpscaleFactorForZoom(zoomLevel);
+
+            if (upscaleFactor < 0)
+                return primaryBitmap;
+
+            return getUpscaledBitmap(upscaleFactor);
+        }
+
+        // System.out.println(zoomLevel);
+        return primaryBitmap;
+    }
+
+    public void resetResampledBitmapCache() {
+        for (int i = 0; i < upSampled.length; i++)
+            upSampled[i] = null;
+
+        for (int i = 0; i < downSampled.length; i++)
+            downSampled[i] = null;
+    }
+
+    public TextureBitmap upscaleBitmap(final TextureBitmap originalBitmap) {
+        final int newWidth = originalBitmap.width * 2;
+        final int newHeight = originalBitmap.height * 2;
+
+        final TextureBitmap upScaled = new TextureBitmap(newWidth, newHeight,
+                originalBitmap.multiplicationFactor * 2d);
+
+        final ColorAccumulator accumulator = new ColorAccumulator();
+
+        for (int y = 0; y < originalBitmap.height; y++)
+            for (int x = 0; x < originalBitmap.width; x++) {
+                accumulator.reset();
+                accumulator.accumulate(originalBitmap, x, y);
+                accumulator.storeResult(upScaled, x * 2, y * 2);
+
+                accumulator.reset();
+                accumulator.accumulate(originalBitmap, x, y);
+                accumulator.accumulate(originalBitmap, x + 1, y);
+                accumulator.storeResult(upScaled, (x * 2) + 1, y * 2);
+
+                accumulator.reset();
+                accumulator.accumulate(originalBitmap, x, y);
+                accumulator.accumulate(originalBitmap, x, y + 1);
+                accumulator.storeResult(upScaled, x * 2, (y * 2) + 1);
+
+                accumulator.reset();
+                accumulator.accumulate(originalBitmap, x, y);
+                accumulator.accumulate(originalBitmap, x + 1, y);
+                accumulator.accumulate(originalBitmap, x, y + 1);
+                accumulator.accumulate(originalBitmap, x + 1, y + 1);
+                accumulator.storeResult(upScaled, (x * 2) + 1, (y * 2) + 1);
+            }
+
+        return upScaled;
+    }
+
+    public class ColorAccumulator {
+        public int r, g, b, a;
+        public int pixelCount = 0;
+
+        public void accumulate(final TextureBitmap bitmap, final int x,
+                               final int y) {
+            int address = bitmap.getAddress(x, y);
+
+            a += bitmap.bytes[address] & 0xff;
+            address++;
+
+            b += bitmap.bytes[address] & 0xff;
+            address++;
+
+            g += bitmap.bytes[address] & 0xff;
+            address++;
+
+            r += bitmap.bytes[address] & 0xff;
+
+            pixelCount++;
+        }
+
+        public void reset() {
+            a = 0;
+            r = 0;
+            g = 0;
+            b = 0;
+            pixelCount = 0;
+        }
+
+        public void storeResult(final TextureBitmap bitmap, final int x,
+                                final int y) {
+            int address = bitmap.getAddress(x, y);
+
+            bitmap.bytes[address] = (byte) (a / pixelCount);
+            address++;
+
+            bitmap.bytes[address] = (byte) (b / pixelCount);
+            address++;
+
+            bitmap.bytes[address] = (byte) (g / pixelCount);
+            address++;
+
+            bitmap.bytes[address] = (byte) (r / pixelCount);
+        }
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/TextureBitmap.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/TextureBitmap.java
new file mode 100644 (file)
index 0000000..2fbd824
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.renderer.raster.texture;
+
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+
+public class TextureBitmap {
+
+    /**
+     * Byte order: ABGR
+     */
+    public final byte[] bytes;
+
+    public final int width;
+
+    public final int height;
+
+    public double multiplicationFactor;
+
+    public TextureBitmap(final int width, final int height, final byte[] bytes,
+                         final double multiplicationFactor) {
+
+        this.width = width;
+        this.height = height;
+        this.bytes = bytes;
+        this.multiplicationFactor = multiplicationFactor;
+    }
+
+    public TextureBitmap(final int width, final int height,
+                         final double multiplicationFactor) {
+
+        this(width, height, new byte[width * height * 4], multiplicationFactor);
+    }
+
+    public void drawPixel(int textureOffset,
+                          final byte[] targetRenderingAreaBytes, int targetRenderingAreaOffset) {
+
+        final int textureAlpha = bytes[textureOffset] & 0xff;
+
+        if (textureAlpha == 0)
+            return;
+
+        if (textureAlpha == 255) {
+            // skip reading of background for fully opaque pixels
+            targetRenderingAreaBytes[targetRenderingAreaOffset] = (byte) 255;
+
+            targetRenderingAreaOffset++;
+            textureOffset++;
+            targetRenderingAreaBytes[targetRenderingAreaOffset] = bytes[textureOffset];
+
+            targetRenderingAreaOffset++;
+            textureOffset++;
+            targetRenderingAreaBytes[targetRenderingAreaOffset] = bytes[textureOffset];
+
+            targetRenderingAreaOffset++;
+            textureOffset++;
+            targetRenderingAreaBytes[targetRenderingAreaOffset] = bytes[textureOffset];
+            return;
+        }
+
+        final int backgroundAlpha = 255 - textureAlpha;
+        textureOffset++;
+
+        targetRenderingAreaBytes[targetRenderingAreaOffset] = (byte) 255;
+        targetRenderingAreaOffset++;
+
+        targetRenderingAreaBytes[targetRenderingAreaOffset] = (byte) ((((targetRenderingAreaBytes[targetRenderingAreaOffset] & 0xff) * backgroundAlpha) + ((bytes[textureOffset] & 0xff) * textureAlpha)) / 256);
+        textureOffset++;
+        targetRenderingAreaOffset++;
+
+        targetRenderingAreaBytes[targetRenderingAreaOffset] = (byte) ((((targetRenderingAreaBytes[targetRenderingAreaOffset] & 0xff) * backgroundAlpha) + ((bytes[textureOffset] & 0xff) * textureAlpha)) / 256);
+        textureOffset++;
+        targetRenderingAreaOffset++;
+
+        targetRenderingAreaBytes[targetRenderingAreaOffset] = (byte) ((((targetRenderingAreaBytes[targetRenderingAreaOffset] & 0xff) * backgroundAlpha) + ((bytes[textureOffset] & 0xff) * textureAlpha)) / 256);
+    }
+
+    public void drawPixel(final int x, final int y, final Color color) {
+        int address = getAddress(x, y);
+
+        bytes[address] = (byte) color.a;
+
+        address++;
+        bytes[address] = (byte) color.b;
+
+        address++;
+        bytes[address] = (byte) color.g;
+
+        address++;
+        bytes[address] = (byte) color.r;
+    }
+
+    public void drawRectangle(int x1, final int y1, int x2, final int y2,
+                              final Color color) {
+
+        if (x1 > x2) {
+            final int tmp = x1;
+            x1 = x2;
+            x2 = tmp;
+        }
+
+        if (y1 > y2) {
+            final int tmp = x1;
+            x1 = x2;
+            x2 = tmp;
+        }
+
+        for (int y = y1; y < y2; y++)
+            for (int x = x1; x < x2; x++)
+                drawPixel(x, y, color);
+    }
+
+    public void fillColor(final Color color) {
+        int address = 0;
+        while (address < bytes.length) {
+            bytes[address] = (byte) color.a;
+            address++;
+
+            bytes[address] = (byte) color.b;
+            address++;
+
+            bytes[address] = (byte) color.g;
+            address++;
+
+            bytes[address] = (byte) color.r;
+            address++;
+        }
+    }
+
+    public int getAddress(int x, int y) {
+        if (x < 0)
+            x = 0;
+
+        if (x >= width)
+            x = width - 1;
+
+        if (y < 0)
+            y = 0;
+
+        if (y >= height)
+            y = height - 1;
+
+        return ((y * width) + x) * 4;
+    }
+}
diff --git a/src/main/resources/eu/svjatoslav/sixth/e3d/examples/hourglass.png b/src/main/resources/eu/svjatoslav/sixth/e3d/examples/hourglass.png
new file mode 100644 (file)
index 0000000..47a1638
Binary files /dev/null and b/src/main/resources/eu/svjatoslav/sixth/e3d/examples/hourglass.png differ
diff --git a/src/main/resources/rebel.xml b/src/main/resources/rebel.xml
new file mode 100644 (file)
index 0000000..5f75318
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" generated-by="intellij"
+             xmlns="http://www.zeroturnaround.com"
+             xsi:schemaLocation="http://www.zeroturnaround.com http://update.zeroturnaround.com/jrebel/rebel-2_1.xsd">
+
+    <classpath>
+        <dir name="/home/n0/workspace/svjatoslav/sixth/target/classes">
+        </dir>
+    </classpath>
+
+</application>
diff --git a/src/test/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/TextLineTest.java b/src/test/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/TextLineTest.java
new file mode 100644 (file)
index 0000000..51e1de4
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Sixth - System for data storage, computation, exploration and interaction.
+ * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.sixth.e3d.gui.textEditorComponent;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class TextLineTest {
+
+    @Test
+    public void testAddIdent() {
+        TextLine textLine = new TextLine("test");
+        textLine.addIdent(4);
+        assertEquals("    test", textLine.toString());
+
+        textLine = new TextLine();
+        textLine.addIdent(4);
+        assertEquals("", textLine.toString());
+    }
+
+    @Test
+    public void testCutFromBeginning() {
+        TextLine textLine = new TextLine("test");
+        textLine.cutFromBeginning(2);
+        assertEquals("st", textLine.toString());
+
+        textLine = new TextLine("test");
+        textLine.cutFromBeginning(4);
+        assertEquals("", textLine.toString());
+
+        textLine = new TextLine("test");
+        textLine.cutFromBeginning(5);
+        assertEquals("", textLine.toString());
+
+        textLine = new TextLine("test");
+        textLine.cutFromBeginning(100);
+        assertEquals("", textLine.toString());
+    }
+
+    @Test
+    public void testCutSubString() {
+        TextLine textLine = new TextLine("test");
+        assertEquals("es", textLine.cutSubString(1, 3));
+        assertEquals("tt", textLine.toString());
+
+        textLine = new TextLine("test");
+        assertEquals("st ", textLine.cutSubString(2, 5));
+        assertEquals("te", textLine.toString());
+    }
+
+    @Test
+    public void testGetCharForLocation() {
+        final TextLine textLine = new TextLine("test");
+        assertEquals('s', textLine.getCharForLocation(2));
+        assertEquals('t', textLine.getCharForLocation(3));
+        assertEquals(' ', textLine.getCharForLocation(4));
+    }
+
+    @Test
+    public void testGetIdent() {
+        final TextLine textLine = new TextLine("   test");
+        assertEquals(3, textLine.getIdent());
+    }
+
+    @Test
+    public void testGetLength() {
+        final TextLine textLine = new TextLine("test");
+        assertEquals(4, textLine.getLength());
+    }
+
+    @Test
+    public void testInsertCharacter() {
+        TextLine textLine = new TextLine("test");
+        textLine.insertCharacter(1, 'o');
+        assertEquals("toest", textLine.toString());
+
+        textLine = new TextLine("test");
+        textLine.insertCharacter(5, 'o');
+        assertEquals("test o", textLine.toString());
+
+    }
+
+    @Test
+    public void testIsEmpty() {
+        TextLine textLine = new TextLine("");
+        assertEquals(true, textLine.isEmpty());
+
+        textLine = new TextLine("     ");
+        assertEquals(true, textLine.isEmpty());
+
+        textLine = new TextLine("l");
+        assertEquals(false, textLine.isEmpty());
+    }
+
+    @Test
+    public void testRemoveCharacter() {
+        TextLine textLine = new TextLine("test");
+        textLine.removeCharacter(0);
+        assertEquals("est", textLine.toString());
+
+        textLine = new TextLine("test");
+        textLine.removeCharacter(3);
+        assertEquals("tes", textLine.toString());
+
+        textLine = new TextLine("test");
+        textLine.removeCharacter(4);
+        assertEquals("test", textLine.toString());
+    }
+
+}
diff --git a/tools/open with IntelliJ IDEA b/tools/open with IntelliJ IDEA
new file mode 100755 (executable)
index 0000000..1f82875
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+cd "${0%/*}"
+
+cd ..
+idea .
diff --git a/tools/open with git-cola b/tools/open with git-cola
new file mode 100755 (executable)
index 0000000..8655908
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+cd "${0%/*}"
+
+cd ..
+
+cola
diff --git a/tools/update web site b/tools/update web site
new file mode 100755 (executable)
index 0000000..8f63084
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/bash
+cd "${0%/*}"; if [ "$1" != "T" ]; then xterm -e "'$0' T"; exit; fi;
+
+#
+# TODO: needs updating
+#
+# (
+#     cd ..
+
+#     mvn clean
+
+#     mkdir -p target/website/codegraphs
+
+#     mvn test package -e exec:java -Dexec.mainClass="eu.svjatoslav.sixth.DataGraph" -Dexec.classpathScope="test"
+
+#     (
+#         cd target/website/codegraphs/
+#         meviz index -t Sixth
+#     )
+
+#     cp target/sixth.jar target/website/
+
+#     rsync -avz --delete target/website/ n0@svjatoslav.eu:/var/www/svjatoslav.eu/projects/sixth
+# )
+
+echo "Script finished. Press ENTER to close this terminal"
+read