--- /dev/null
+# Created by .ignore support plugin (hsz.mobi)
+/.idea/
+/target/
\ No newline at end of file
--- /dev/null
+<?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"><<span style="color: #bc6ec5; font-weight: bold;">dependencies</span>>
+ ...
+ <<span style="color: #bc6ec5; font-weight: bold;">dependency</span>>
+ <<span style="color: #bc6ec5; font-weight: bold;">groupId</span>>eu.svjatoslav</<span
+ style="color: #bc6ec5; font-weight: bold;">groupId</span>>
+ <<span style="color: #bc6ec5; font-weight: bold;">artifactId</span>>sixth-3d</<span
+ style="color: #bc6ec5; font-weight: bold;">artifactId</span>>
+ <<span style="color: #bc6ec5; font-weight: bold;">version</span>>1.0</<span
+ style="color: #bc6ec5; font-weight: bold;">version</span>>
+ </<span style="color: #bc6ec5; font-weight: bold;">dependency</span>>
+ ...
+</<span style="color: #bc6ec5; font-weight: bold;">dependencies</span>>
+
+<<span style="color: #bc6ec5; font-weight: bold;">repositories</span>>
+ ...
+ <<span style="color: #bc6ec5; font-weight: bold;">repository</span>>
+ <<span style="color: #bc6ec5; font-weight: bold;">id</span>>svjatoslav.eu</<span
+ style="color: #bc6ec5; font-weight: bold;">id</span>>
+ <<span style="color: #bc6ec5; font-weight: bold;">name</span>>Svjatoslav repository</<span
+ style="color: #bc6ec5; font-weight: bold;">name</span>>
+ <<span style="color: #bc6ec5; font-weight: bold;">url</span>>http://www2.svjatoslav.eu/maven/</<span
+ style="color: #bc6ec5; font-weight: bold;">url</span>>
+ </<span style="color: #bc6ec5; font-weight: bold;">repository</span>>
+ ...
+</<span style="color: #bc6ec5; font-weight: bold;">repositories</span>>
+</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>
--- /dev/null
+#+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]]
+
+
--- /dev/null
+<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
--- /dev/null
+<?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
--- /dev/null
+/*
+ * 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();
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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();
+ }
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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();
+ }
+
+}
--- /dev/null
+/*
+ * 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();
+
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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();
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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();
+
+}
--- /dev/null
+/*
+ * 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);
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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 == ' ';
+ }
+}
--- /dev/null
+/*
+ * 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);
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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();
+ }
+
+}
--- /dev/null
+/*
+ * 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();
+ }
+
+}
--- /dev/null
+/*
+ * 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();
+
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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.
+ */
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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());
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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];
+ */
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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.
+ */
--- /dev/null
+/*
+ * 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.
+ */
--- /dev/null
+/*
+ * 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();
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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>
+ */
--- /dev/null
+/*
+ * 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);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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));
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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();
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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));
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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);
+ //
+ // }
+}
--- /dev/null
+/*
+ * 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));
+ }
+
+ }
+
+ }
+
+}
--- /dev/null
+/*
+ * 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));
+ }
+
+ }
+ }
+}
--- /dev/null
+/*
+ * 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)));
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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++;
+ }
+
+ }
+
+}
--- /dev/null
+/*
+ * 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));
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+<?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>
--- /dev/null
+/*
+ * 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());
+ }
+
+}
--- /dev/null
+#!/bin/bash
+
+cd "${0%/*}"
+
+cd ..
+idea .
--- /dev/null
+#!/bin/bash
+
+cd "${0%/*}"
+
+cd ..
+
+cola
--- /dev/null
+#!/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