From: Svjatoslav Agejenko Date: Wed, 3 Aug 2016 20:21:40 +0000 (+0300) Subject: initial commit X-Git-Tag: sixth-3d-1.2~67 X-Git-Url: http://www2.svjatoslav.eu/gitweb/?a=commitdiff_plain;h=6213716671ccab6b7256de41838e1f5401ce173c;p=sixth-3d.git initial commit --- 6213716671ccab6b7256de41838e1f5401ce173c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d1f99ea --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Created by .ignore support plugin (hsz.mobi) +/.idea/ +/target/ \ No newline at end of file diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000..c73dfce --- /dev/null +++ b/doc/index.html @@ -0,0 +1,426 @@ + + + + + + + + Sixth 3D - 3D engine + + + + + + + +
+

Sixth 3D - 3D engine

+
+

Table of Contents

+ +
+
+ + + + + +
+

1 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. +

+
+
+ + +
+

2 Software development

+
+

+ Instructions to embed Sixth-3D in your project as a library. Maven *pom.xml* + file snippet: +

+
+ +
<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>
+
+
+ +

+ Auto-generated graphs for parts of + Sixth-3D code/architecture using this tool +

+
+
+
+
+

Author: Svjatoslav Agejenko

+

Created: 2016-08-03 Wed 23:15

+

Validate

+
+ + diff --git a/doc/index.org b/doc/index.org new file mode 100644 index 0000000..31a2be0 --- /dev/null +++ b/doc/index.org @@ -0,0 +1,71 @@ +#+TITLE: Sixth 3D - 3D engine + +----- +- This is a subproject of [[http://www2.svjatoslav.eu/gitbrowse/sixth/doc/index.html][Sixth]] + +- [[http://www2.svjatoslav.eu/gitweb/?p=sixth.git;a=snapshot;h=HEAD;sf=tgz][download latest snapshot]] + +- This program is free software; you can redistribute it and/or modify it under + the terms of version 3 of the [[https://www.gnu.org/licenses/lgpl.html][GNU Lesser General Public License]] or later as + published by the Free Software Foundation. + +- Program author: + - Svjatoslav Agejenko + - Homepage: http://svjatoslav.eu + - Email: mailto://svjatoslav@svjatoslav.eu + +- [[http://svjatoslav.eu/programs.jsp][other applications hosted at svjatoslav.eu]] + + +- In software, pure Java realtime 3D rendering engine. With the final goal of + becoming a platform for buildng 3D user interfaces. + +* Project description +System is implemented in Java because: +- It scales well to handle great complexity. +- It is easy to refactor and experiment with. +- It is fast enough thanks to Java virtual machine just-in-time compiler. +- Easy to run on various hardware platforms and operating systems. + +3D rendering is done in software, 100% pure Java on CPU. At least for now. +Modern CPU cores count keeps growing and therefore rendering by CPU is not as +expensive as it used to be for the old single core systems. + +CPU rendering performance is already good enough to implement usable 3D UI at +sufficient detail level, resolution and frame rate. + +Pure Java also means easy portability and installation. No need to deal with +platform specific dependencies. + +Also CPU rendering allows to easily test different rendering algorithms and +retains complete control of every rendered pixel. + + +* Software development +Instructions to embed Sixth-3D in your project as a library. Maven *pom.xml* +file snippet: +#+BEGIN_SRC xml + + ... + + eu.svjatoslav + sixth-3d + 1.0 + + ... + + + + ... + + svjatoslav.eu + Svjatoslav repository + http://www2.svjatoslav.eu/maven/ + + ... + +#+END_SRC + +[[http://www2.svjatoslav.eu/projects/sixth/codegraphs/][Auto-generated graphs for parts of Sixth-3D code/architecture]] using [[http://www2.svjatoslav.eu/gitbrowse/javainspect/doc/index.html][this tool]] + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..87e3a1c --- /dev/null +++ b/pom.xml @@ -0,0 +1,125 @@ + + 4.0.0 + eu.svjatoslav + sixth-3d + 1.0-SNAPSHOT + Sixth 3D + 3D engine + + + UTF-8 + UTF-8 + + + + svjatoslav.eu + http://svjatoslav.eu + + + + + junit + junit + 4.8.1 + test + + + + eu.svjatoslav + javainspect + 1.5-SNAPSHOT + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.8 + 1.8 + true + UTF-8 + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9 + + + attach-javadocs + + jar + + + + + + + org.apache.maven.plugins + maven-resources-plugin + 2.4.3 + + UTF-8 + + + + + + + + org.apache.maven.wagon + wagon-ssh-external + 2.6 + + + + + + + + svjatoslav.eu + svjatoslav.eu + scpexe://svjatoslav.eu/var/www/svjatoslav.eu/maven + + + svjatoslav.eu + svjatoslav.eu + scpexe://svjatoslav.eu/var/www/svjatoslav.eu/maven + + + + + + svjatoslav.eu + Svjatoslav repository + http://www2.svjatoslav.eu/maven/ + + + + + scm:git:ssh://git@svjatoslav.eu/home/git/repositories/sixth-3d.git + scm:git:ssh://git@svjatoslav.eu/home/git/repositories/sixth-3d.git + + + \ No newline at end of file diff --git a/sixth-3d.iml b/sixth-3d.iml new file mode 100644 index 0000000..2cb0c21 --- /dev/null +++ b/sixth-3d.iml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Box.java b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Box.java new file mode 100644 index 0000000..abfe554 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Box.java @@ -0,0 +1,82 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.geometry; + +/** + * Same as: 3D rectangle, rectangular box, rectangular parallelopiped, cuboid, + * rhumboid, hexahedron, rectangular prism. + */ +public class Box implements Cloneable { + + public final Point3D p1; + public final Point3D p2; + + public Box() { + p1 = new Point3D(); + p2 = new Point3D(); + } + + public Box(final Point3D p1, final Point3D p2) { + this.p1 = p1; + this.p2 = p2; + } + + public Box addBorder(final double border) { + + if (p1.x < p2.x) { + p1.translateX(-border); + p2.translateX(border); + } else { + p1.translateX(border); + p2.translateX(-border); + } + + if (p1.y < p2.y) { + p1.translateY(-border); + p2.translateY(border); + } else { + p1.translateY(border); + p2.translateY(-border); + } + + if (p1.z < p2.z) { + p1.translateZ(-border); + p2.translateZ(border); + } else { + p1.translateZ(border); + p2.translateZ(-border); + } + + return this; + } + + @Override + public Box clone() { + return new Box(p1.clone(), p2.clone()); + } + + public double getDepth() { + return Math.abs(p1.z - p2.z); + } + + public double getHeight() { + return Math.abs(p1.y - p2.y); + } + + public double getWidth() { + return Math.abs(p1.x - p2.x); + } + + public void setSizeCentered(final Point3D size) { + p2.clone(size).scaleDown(2); + p1.clone(p2).invert(); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Circle.java b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Circle.java new file mode 100644 index 0000000..adb685b --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Circle.java @@ -0,0 +1,17 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.geometry; + +public class Circle { + + Point2D center; + double radius; + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/geometry/GeometryCoordinate.java b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/GeometryCoordinate.java new file mode 100644 index 0000000..2820c74 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/GeometryCoordinate.java @@ -0,0 +1,49 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.geometry; + +import eu.svjatoslav.sixth.e3d.gui.RenderingContext; + +public class GeometryCoordinate { + + public Point3D coordinate; + public Point3D transformedCoordinate; + public Point2D onScreenCoordinate; + + int lastTransformedFrame; + + public GeometryCoordinate() { + coordinate = new Point3D(); + transformedCoordinate = new Point3D(); + onScreenCoordinate = new Point2D(); + } + + public GeometryCoordinate(final Point3D location) { + coordinate = location; + transformedCoordinate = new Point3D(); + onScreenCoordinate = new Point2D(); + } + + public void transform(final TransformPipe transforms, + final RenderingContext renderContext) { + + if (lastTransformedFrame == renderContext.frameNumber) + return; + + lastTransformedFrame = renderContext.frameNumber; + + transforms.transform(coordinate, transformedCoordinate); + + onScreenCoordinate.x = ((transformedCoordinate.x / transformedCoordinate.z) * renderContext.zoom) + + renderContext.xCenter; + onScreenCoordinate.y = ((transformedCoordinate.y / transformedCoordinate.z) * renderContext.zoom) + + renderContext.yCenter; + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Orientation.java b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Orientation.java new file mode 100644 index 0000000..c38e3cd --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Orientation.java @@ -0,0 +1,57 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + + +package eu.svjatoslav.sixth.e3d.geometry; + +public class Orientation implements Cloneable { + + private double s1, c1, s2, c2; + + private double angleXZ = 0; + private double angleYZ = 0; + + public Orientation() { + computeMultipliers(); + } + + public Orientation(final double angleXZ, final double angleYZ) { + this.angleXZ = angleXZ; + this.angleYZ = angleYZ; + computeMultipliers(); + } + + @Override + public Orientation clone() { + return new Orientation(angleXZ, angleYZ); + } + + private void computeMultipliers() { + s1 = Math.sin(angleXZ); + c1 = Math.cos(angleXZ); + + s2 = Math.sin(angleYZ); + c2 = Math.cos(angleYZ); + } + + public void rotate(final Point3D point3d) { + final double z1 = (point3d.z * c1) - (point3d.x * s1); + point3d.x = (point3d.z * s1) + (point3d.x * c1); + + point3d.z = (z1 * c2) - (point3d.y * s2); + point3d.y = (z1 * s2) + (point3d.y * c2); + } + + public void rotate(final double angleXZ, final double angleYZ) { + this.angleXZ += angleXZ; + this.angleYZ += angleYZ; + computeMultipliers(); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Point2D.java b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Point2D.java new file mode 100755 index 0000000..c767ffd --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Point2D.java @@ -0,0 +1,89 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.geometry; + +public class Point2D implements Cloneable { + + public double x, y; + + public Point2D() { + } + + public Point2D(final double x, final double y) { + this.x = x; + this.y = y; + } + + public Point2D(final Point2D source) { + x = source.x; + y = source.y; + } + + public Point2D add(final Point2D direction) { + x += direction.x; + y += direction.y; + return this; + } + + @Override + public Point2D clone() { + return new Point2D(this); + } + + public void clone(final Point2D source) { + x = source.x; + y = source.y; + } + + public Point2D computeMiddlePoint(final Point2D p1, final Point2D p2) { + x = (p1.x + p2.x) / 2d; + y = (p1.y + p2.y) / 2d; + return this; + } + + public double getAngleXY(final Point2D anotherPoint) { + return Math.atan2(x - anotherPoint.x, y - anotherPoint.y); + } + + public double getDistanceTo(final Point2D anotherPoint) { + final double xDiff = x - anotherPoint.x; + final double yDiff = y - anotherPoint.y; + + return Math.sqrt(((xDiff * xDiff) + (yDiff * yDiff))); + } + + public Point2D invert() { + x = -x; + y = -y; + return this; + } + + public void roundToInteger() { + x = (int) x; + y = (int) y; + } + + public Point2D subtract(final Point2D direction) { + x -= direction.x; + y -= direction.y; + return this; + } + + public Point3D to3D() { + return new Point3D(x, y, 0); + } + + public Point2D zero() { + x = 0; + y = 0; + return this; + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Point3D.java b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Point3D.java new file mode 100755 index 0000000..aff475e --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Point3D.java @@ -0,0 +1,196 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.geometry; + +/** + * Used to represent point in a 3D space, vector or speed. + */ + +public class Point3D extends Point2D implements Cloneable { + + public static final Point3D ZERO = new Point3D(); + public double z; + + public Point3D() { + } + + public Point3D(final double x, final double y, final double z) { + this.x = x; + this.y = y; + this.z = z; + } + + public Point3D(final float x, final float y, final float z) { + this.x = x; + this.y = y; + this.z = z; + } + + public Point3D(final int x, final int y, final int z) { + this.x = x; + this.y = y; + this.z = z; + } + + public Point3D(final Point3D parentPoint) { + x = parentPoint.x; + y = parentPoint.y; + z = parentPoint.z; + } + + public Point3D add(final Point3D direction) { + super.add(direction); + z += direction.z; + return this; + } + + @Override + public Point3D clone() { + return new Point3D(this); + } + + public Point3D clone(final Point3D source) { + x = source.x; + y = source.y; + z = source.z; + return this; + } + + public Point3D computeMiddlePoint(final Point3D p1, final Point3D p2) { + x = (p1.x + p2.x) / 2d; + y = (p1.y + p2.y) / 2d; + z = (p1.z + p2.z) / 2d; + return this; + } + + public double getAngleXZ(final Point3D anotherPoint) { + return Math.atan2(x - anotherPoint.x, z - anotherPoint.z); + } + + public double getAngleYZ(final Point3D anotherPoint) { + return Math.atan2(y - anotherPoint.y, z - anotherPoint.z); + } + + /** + * Compute distance to another point. + */ + public double getDistanceTo(final Point3D anotherPoint) { + final double xDelta = x - anotherPoint.x; + final double yDelta = y - anotherPoint.y; + final double zDelta = z - anotherPoint.z; + + return Math + .sqrt(((xDelta * xDelta) + (yDelta * yDelta) + (zDelta * zDelta))); + } + + @Override + public Point3D invert() { + x = -x; + y = -y; + z = -z; + return this; + } + + public void rotate(final Point3D center, final double angleXZ, + final double angleYZ) { + final double s1 = Math.sin(angleXZ); + final double c1 = Math.cos(angleXZ); + + final double s2 = Math.sin(angleYZ); + final double c2 = Math.cos(angleYZ); + + x -= center.x; + y -= center.y; + z -= center.z; + + final double y1 = (z * s2) + (y * c2); + final double z1 = (z * c2) - (y * s2); + + final double x1 = (z1 * s1) + (x * c1); + final double z2 = (z1 * c1) - (x * s1); + + x = x1 + center.x; + y = y1 + center.y; + z = z2 + center.z; + } + + @Override + public void roundToInteger() { + x = (int) x; + y = (int) y; + z = (int) z; + } + + public Point3D scaleDown(final double factor) { + x /= factor; + y /= factor; + z /= factor; + return this; + } + + public Point3D scaleUp(final double factor) { + x *= factor; + y *= factor; + z *= factor; + return this; + } + + public void setValues(final double x, final double y, final double z) { + this.x = x; + this.y = y; + this.z = z; + } + + public Point3D subtract(final Point3D direction) { + super.subtract(direction); + z -= direction.z; + return this; + } + + @Override + public String toString() { + return "x:" + x + " y:" + y + " z:" + z; + } + + public Point3D translateX(final double xIncrement) { + x += xIncrement; + return this; + } + + public Point3D translateY(final double yIncrement) { + y += yIncrement; + return this; + } + + public Point3D translateZ(final double zIncrement) { + z += zIncrement; + return this; + } + + public boolean withinDrawingLimits() { + + if (z > 0) + return true; + + // if ((z > 0) && (x > -1000) && (y > -1000) && (x < 2000) && (y < + // 2000)) + // return true; + + return false; + } + + @Override + public Point3D zero() { + super.zero(); + z = 0; + return this; + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Polygon.java b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Polygon.java new file mode 100644 index 0000000..ac1f54d --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Polygon.java @@ -0,0 +1,57 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.geometry; + +public class Polygon { + + private static boolean intersectsLine(final Point2D point, Point2D p1, + Point2D p2) { + + if (p1.y > p2.y) { + final Point2D tmp = p1; + p1 = p2; + p2 = tmp; + } + + if (point.y < p1.y) + return false; + if (point.y > p2.y) + return false; + + final double xp = p2.x - p1.x; + final double yp = p2.y - p1.y; + + final double yp2 = point.y - p1.y; + + final double crossX = p1.x + ((xp * yp2) / yp); + + return point.x >= crossX; + + } + + public static boolean pointWithinPolygon(final Point2D point, + final Point2D p1, final Point2D p2, final Point2D p3) { + + int intersectionCount = 0; + + if (intersectsLine(point, p1, p2)) + intersectionCount++; + + if (intersectsLine(point, p2, p3)) + intersectionCount++; + + if (intersectsLine(point, p3, p1)) + intersectionCount++; + + return intersectionCount == 1; + + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Rectangle.java b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Rectangle.java new file mode 100644 index 0000000..9d426c5 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Rectangle.java @@ -0,0 +1,47 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + + +package eu.svjatoslav.sixth.e3d.geometry; + +public class Rectangle { + + public Point2D p1, p2; + + public Rectangle(final double size) { + p2 = new Point2D(size / 2, size / 2); + p1 = p2.clone().invert(); + } + + public Rectangle(final Point2D p1, final Point2D p2) { + this.p1 = p1; + this.p2 = p2; + } + + public double getHeight() { + return Math.abs(p1.y - p2.y); + } + + public double getLowerX() { + if (p1.x < p2.x) + return p1.x; + return p2.x; + } + + public double getLowerY() { + if (p1.y < p2.y) + return p1.y; + return p2.y; + } + + public double getWidth() { + return Math.abs(p1.x - p2.x); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Transform.java b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Transform.java new file mode 100755 index 0000000..adcec0d --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Transform.java @@ -0,0 +1,57 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.geometry; + +public class Transform implements Cloneable { + + private final Point3D translation; + private final Orientation orientation; + + public Transform() { + translation = new Point3D(); + orientation = new Orientation(); + } + + public Transform(final Point3D translation) { + this.translation = translation; + orientation = new Orientation(); + } + + public Transform(final Point3D translation, final double angleXZ, + final double angleYZ) { + + this.translation = translation; + orientation = new Orientation(angleXZ, angleYZ); + } + + public Transform(final Point3D translation, final Orientation orientation) { + this.translation = translation; + this.orientation = orientation; + } + + @Override + public Transform clone() { + return new Transform(translation, orientation); + } + + public Orientation getOrientation() { + return orientation; + } + + public Point3D getTranslation() { + return translation; + } + + public void transform(final Point3D point) { + orientation.rotate(point); + point.add(translation); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/geometry/TransformPipe.java b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/TransformPipe.java new file mode 100644 index 0000000..413876b --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/TransformPipe.java @@ -0,0 +1,38 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.geometry; + +public class TransformPipe { + + Transform[] transforms = new Transform[100]; + + private int transformsCount = 0; + + public void addTransform(final Transform transform) { + transforms[transformsCount] = transform; + transformsCount++; + } + + public void clear() { + transformsCount = 0; + } + + public void dropTransform() { + transformsCount--; + } + + public void transform(final Point3D source, final Point3D destination) { + + destination.clone(source); + + for (int i = transformsCount - 1; i >= 0; i--) + transforms[i].transform(destination); + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/Avatar.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/Avatar.java new file mode 100755 index 0000000..ea90687 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/Avatar.java @@ -0,0 +1,157 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.gui; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; + +public class Avatar implements ViewUpdateListener { + + public static final double SPEED_LIMIT = 30; + /** + * Just in case we want to adjust global speed for some reason. + */ + private static final double SPEED_MULTIPLIER = .02d; + /** + * Avatar movement speed, relative to avatar itself. When avatar coordinates + * are updated within the world, avatar orientation relative to the world is + * taken into account. + */ + private final Point3D movementDirection = new Point3D(); + public double avatarAcceleration = 0.1; + /** + * Avatar location within the 3D world. + */ + private Point3D location = new Point3D(); + + /** + * Avatar orientation on the X-Z plane. It changes when turning left or + * right. + */ + private double orientationXZ; + + /** + * Avatar orientation on the Y-Z plane. It changes when looking up or down. + */ + private double orientationYZ; + + public Avatar() { + } + + public Avatar(final Avatar sourceView) { + setLocation(new Point3D(sourceView.location)); + setAngleXZ(sourceView.getAngleXZ()); + setAngleYZ(sourceView.getAngleYZ()); + } + + public Avatar(final Point3D location) { + setLocation(location); + } + + public Avatar(final Point3D location, final float angleXZ, + final float angleYZ) { + setLocation(location); + setAngleXZ(angleXZ); + setAngleYZ(angleYZ); + } + + @Override + public boolean beforeViewUpdate(final ViewContext viewContext, + final int millisecondsSinceLastFrame) { + + final Point3D locationBeforeUpdate = new Point3D(location); + updateLocation(millisecondsSinceLastFrame); + + final double distanceMoved = location + .getDistanceTo(locationBeforeUpdate); + + return distanceMoved > 0.03; + + } + + public void enforceSpeedLimit() { + final double currentSpeed = movementDirection + .getDistanceTo(Point3D.ZERO); + + if (currentSpeed <= SPEED_LIMIT) + return; + + movementDirection.scaleDown(currentSpeed / SPEED_LIMIT); + } + + public double getAngleXZ() { + return orientationXZ; + } + + public void setAngleXZ(final double angleXZ) { + orientationXZ = angleXZ; + } + + public double getAngleYZ() { + return orientationYZ; + } + + public void setAngleYZ(final double angleYZ) { + orientationYZ = angleYZ; + } + + public Point3D getLocation() { + return location; + } + + public void setLocation(final Point3D location) { + this.location = location; + } + + public Point3D getMovementDirection() { + return movementDirection; + } + + public double getMovementSpeed() { + return movementDirection.getDistanceTo(Point3D.ZERO); + } + + /** + * Update camera location based on current speed + */ + public void updateLocation(final int millisecondsPassedSinceLastFrame) { + + // translate user coordinates based on avatar movement speed, and avatar + // orientation in the world + { + location.x -= (float) Math.sin(getAngleXZ()) + * getMovementDirection().z * SPEED_MULTIPLIER + * millisecondsPassedSinceLastFrame; + location.z += (float) Math.cos(getAngleXZ()) + * getMovementDirection().z * SPEED_MULTIPLIER + * millisecondsPassedSinceLastFrame; + + location.x += (float) Math.cos(getAngleXZ()) + * getMovementDirection().x * SPEED_MULTIPLIER + * millisecondsPassedSinceLastFrame; + location.z += (float) Math.sin(getAngleXZ()) + * getMovementDirection().x * SPEED_MULTIPLIER + * millisecondsPassedSinceLastFrame; + + location.y += getMovementDirection().y * SPEED_MULTIPLIER + * millisecondsPassedSinceLastFrame; + } + + final double millisecondFriction = 1.005; + // apply friction to progressively slow movement + for (int i = 0; i < millisecondsPassedSinceLastFrame; i++) { + getMovementDirection().x = getMovementDirection().x + / millisecondFriction; + getMovementDirection().y = getMovementDirection().y + / millisecondFriction; + getMovementDirection().z = getMovementDirection().z + / millisecondFriction; + } + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/GuiComponent.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/GuiComponent.java new file mode 100644 index 0000000..06b87b7 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/GuiComponent.java @@ -0,0 +1,127 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.gui; + +import eu.svjatoslav.sixth.e3d.geometry.Box; +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.geometry.Transform; +import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController; +import eu.svjatoslav.sixth.e3d.gui.humaninput.UserInputHandler; +import eu.svjatoslav.sixth.e3d.gui.textEditorComponent.KeyboardHelper; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.WireframeBox; + +import java.awt.event.KeyEvent; + +public class GuiComponent extends AbstractCompositeShape implements + UserInputHandler, MouseInteractionController { + + private static final String GROUP_GUI_FOCUS = "gui.focus"; + public final ViewContext viewContext; + Box containingBox = new Box(); + private WireframeBox borders = null; + + private boolean borderShown = false; + + public GuiComponent(final Transform transform, + final ViewContext viewContext, final Point3D size) { + super(transform); + this.viewContext = viewContext; + setDimensions(size); + } + + @Override + public boolean beforeViewUpdate(final ViewContext viewContext, + final int millisecondsSinceLastFrame) { + return false; + } + + private WireframeBox createBorder() { + final LineAppearance appearance = new LineAppearance(10, + new eu.svjatoslav.sixth.e3d.renderer.raster.Color(255, 0, 0, 100)); + + final double borderSize = 10; + + final Box borderArea = containingBox.clone().addBorder(borderSize); + + return new WireframeBox(borderArea, appearance); + } + + @Override + public void focusLost(final ViewContext viewContext) { + hideBorder(); + } + + @Override + public void focusReceived(final ViewContext viewContext) { + showBorder(); + } + + public WireframeBox getBorders() { + if (borders == null) + borders = createBorder(); + return borders; + } + + public int getDepth() { + return (int) containingBox.getDepth(); + } + + public int getHeight() { + return (int) containingBox.getHeight(); + } + + public int getWidth() { + return (int) containingBox.getWidth(); + } + + public void hideBorder() { + if (!borderShown) + return; + borderShown = false; + removeGroup(GROUP_GUI_FOCUS); + } + + @Override + public void keyPressed(final KeyEvent event, final ViewContext viewContext) { + if (event.getKeyChar() == KeyboardHelper.ESC) + viewContext.getKeyboardFocusTracker().popFocusOwner(); + } + + @Override + public void keyReleased(final KeyEvent event, final ViewContext viewContext) { + } + + @Override + public void mouseClicked() { + viewContext.getKeyboardFocusTracker().setFocusOwner(this); + } + + @Override + public void mouseEntered() { + } + + @Override + public void mouseExited() { + } + + private void setDimensions(final Point3D size) { + containingBox.setSizeCentered(size); + } + + public void showBorder() { + if (borderShown) + return; + borderShown = true; + addShape(getBorders(), GROUP_GUI_FOCUS); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/RenderingContext.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/RenderingContext.java new file mode 100644 index 0000000..a16c2d5 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/RenderingContext.java @@ -0,0 +1,82 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.gui; + +import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseClick; +import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferByte; +import java.awt.image.WritableRaster; + +public class RenderingContext { + + public static final int bufferedImageType = BufferedImage.TYPE_4BYTE_ABGR; + + public final BufferedImage bufferedImage; + + public final Graphics2D graphics; + + public final byte[] bytes; + + public final int width; + public final int height; + + public final int xCenter; + public final int yCenter; + + public final double zoom; + + public int frameNumber = 0; + + /** + * Used to signal that actual rendering should be performed. It is useful to + * skip rendering when we only want to detect mouse clicks intersections + * without actually updating rendered frame. + */ + public boolean doRender = true; // TODO: make use of the variable + + public MouseClick mouseClick; + + public MouseInteractionController clickedItem; + + public RenderingContext(final int width, final int height, + final RenderingContext oldContext) { + + this.width = width; + this.height = height; + + this.xCenter = width / 2; + this.yCenter = height / 2; + + this.zoom = width / 3d; + + bufferedImage = new BufferedImage(width, height, bufferedImageType); + + final WritableRaster raster = bufferedImage.getRaster(); + final DataBufferByte dbi = (DataBufferByte) raster.getDataBuffer(); + bytes = dbi.getData(); + + graphics = (Graphics2D) bufferedImage.getGraphics(); + + graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + + graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + + if (oldContext != null) { + mouseClick = oldContext.mouseClick; + clickedItem = oldContext.clickedItem; + } + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/TextPointer.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/TextPointer.java new file mode 100755 index 0000000..e78640e --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/TextPointer.java @@ -0,0 +1,83 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.gui; + +public class TextPointer implements Comparable { + + public int row; + public int column; + + public TextPointer() { + this(0, 0); + } + + public TextPointer(final int row, final int column) { + this.row = row; + this.column = column; + } + + public TextPointer(final TextPointer parent) { + this(parent.row, parent.column); + } + + @Override + public boolean equals(final Object o) { + if (o == null) return false; + + return o instanceof TextPointer && compareTo((TextPointer) o) == 0; + } + + @Override + public int hashCode() { + int result = row; + result = 31 * result + column; + return result; + } + + @Override + public int compareTo(final TextPointer textPointer) { + + if (textPointer.row > row) + return -1; + if (textPointer.row < row) + return 1; + + if (textPointer.column > column) + return -1; + if (textPointer.column < column) + return 1; + + return 0; + } + + public boolean isBetween(final TextPointer start, final TextPointer end) { + + if (start == null) + return false; + + if (end == null) + return false; + + TextPointer smaller; + TextPointer bigger; + + if (end.compareTo(start) >= 0) { + smaller = start; + bigger = end; + } else { + smaller = end; + bigger = start; + } + + return (compareTo(smaller) >= 0) && (bigger.compareTo(this) > 0); + + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/UserRelativityTracker.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/UserRelativityTracker.java new file mode 100644 index 0000000..f50f6d9 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/UserRelativityTracker.java @@ -0,0 +1,75 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + + +package eu.svjatoslav.sixth.e3d.gui; + +import eu.svjatoslav.sixth.e3d.geometry.GeometryCoordinate; +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.geometry.TransformPipe; + +public class UserRelativityTracker { + + public final static int minimalSliceFactor = 5; + public GeometryCoordinate center = new GeometryCoordinate(); + public GeometryCoordinate right; + public GeometryCoordinate down; + + public UserRelativityTracker() { + + } + + public void analyze(final TransformPipe transformPipe, + final RenderingContext renderingContext) { + + center.transform(transformPipe, renderingContext); + + if (right != null) { + right.transform(transformPipe, renderingContext); + down.transform(transformPipe, renderingContext); + } + } + + public void enableOrientationTracking() { + right = new GeometryCoordinate(new Point3D(10, 0, 0)); + down = new GeometryCoordinate(new Point3D(0, 10, 0)); + } + + public double getAngleXY() { + return center.transformedCoordinate + .getAngleXY(down.transformedCoordinate); + } + + public double getAngleXZ() { + return center.transformedCoordinate + .getAngleXZ(right.transformedCoordinate); + } + + public double getAngleYZ() { + return center.transformedCoordinate + .getAngleYZ(down.transformedCoordinate); + } + + public double getDistanceToUser() { + return center.transformedCoordinate + .getDistanceTo(Point3D.ZERO); + } + + public double proposeSliceFactor() { + final double distanceToCamera = getDistanceToUser(); + + double proposedSliceFactor = distanceToCamera / 5; + + if (proposedSliceFactor < minimalSliceFactor) + proposedSliceFactor = minimalSliceFactor; + + return proposedSliceFactor; + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/View.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/View.java new file mode 100755 index 0000000..ecd0e9d --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/View.java @@ -0,0 +1,258 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.gui; + +import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.util.ArrayList; +import java.util.List; +import java.util.Timer; + +public class View extends JPanel implements ComponentListener { + + private static final long serialVersionUID = 1683277888885045387L; + private final List viewListeners = new ArrayList<>(); + private final List 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 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. + *

+ * It tells view to update itself. View can decide if actual re-rendering of + * graphics is needed. + */ + public void updateView() { + renderingContext.mouseClick = null; + renderingContext.clickedItem = null; + + // compute time passed since last view update + final long currentTime = System.currentTimeMillis(); + + if (lastUpdateMillis == 0) { + lastUpdateMillis = currentTime; + return; + } + + final int millisecondsPassedSinceLastUpdate = (int) (currentTime - lastUpdateMillis); + lastUpdateMillis = currentTime; + + // notify update listeners + boolean rerenderView = false; + + for (final ViewUpdateListener listener : viewUpdateListeners) + if (listener.beforeViewUpdate(context, + millisecondsPassedSinceLastUpdate)) + rerenderView = true; + + // abort rendering if window size is invalid + if ((getWidth() <= 0) || (getHeight() <= 0)) + return; + + if (repaintDuringNextViewUpdate) { + repaintDuringNextViewUpdate = false; + rerenderView = true; + } + + if (rerenderView) { + renderFrame(); + handleDetectedComponentMouseEvents(); + } + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewContext.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewContext.java new file mode 100644 index 0000000..b3e66e3 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewContext.java @@ -0,0 +1,53 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.gui; + +import eu.svjatoslav.sixth.e3d.gui.humaninput.KeyboardFocusTracker; +import eu.svjatoslav.sixth.e3d.gui.humaninput.UserInputTracker; +import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; + +public class ViewContext { + + private final UserInputTracker userInputTracker = new UserInputTracker(this); + + private final KeyboardFocusTracker keyboardFocusTracker = new KeyboardFocusTracker( + this); + + private final Avatar avatar = new Avatar(); + + private final View view; + + private final ShapeCollection rootShapeCollection = new ShapeCollection(); + + public ViewContext(final View view) { + this.view = view; + } + + public Avatar getAvatar() { + return avatar; + } + + public KeyboardFocusTracker getKeyboardFocusTracker() { + return keyboardFocusTracker; + } + + public ShapeCollection getRootShapeCollection() { + return rootShapeCollection; + } + + public UserInputTracker getUserInputTracker() { + return userInputTracker; + } + + public View getView() { + return view; + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewFrame.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewFrame.java new file mode 100755 index 0000000..3959a30 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewFrame.java @@ -0,0 +1,148 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.gui; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; + +public class ViewFrame extends JFrame implements ViewListener, WindowListener { + + private static final long serialVersionUID = -7037635097739548470L; + + private final View view; + + public ViewFrame() { + setTitle("3D engine"); + + addWindowListener(new java.awt.event.WindowAdapter() { + @Override + public void windowClosing(final java.awt.event.WindowEvent e) { + exit(); + } + }); + + view = new View(); + + add(getView()); + + getView().addViewListener(this); + + setSize(800, 600); + + // maximize window + setExtendedState(JFrame.MAXIMIZED_BOTH); + setVisible(true); + + addResizeListener(); + addWindowListener(this); + } + + public void addResizeListener() { + addComponentListener(new ComponentListener() { + // This method is called after the component's size changes + @Override + public void componentHidden(final ComponentEvent e) { + } + + @Override + public void componentMoved(final ComponentEvent e) { + } + + @Override + public void componentResized(final ComponentEvent evt) { + + final Component c = (Component) evt.getSource(); + + // Get new size + final Dimension newSize = c.getSize(); + + boolean sizeFixed = false; + + if (newSize.width < 400) { + newSize.width = 400; + sizeFixed = true; + } + + if (newSize.height < 400) { + newSize.height = 400; + sizeFixed = true; + } + + if (sizeFixed) + setSize(newSize); + + } + + @Override + public void componentShown(final ComponentEvent e) { + view.repaintDuringNextViewUpdate(); + } + + }); + } + + @Override + public void exit() { + if (getView() != null) { + getView().stop(); + getView().setEnabled(false); + getView().setVisible(false); + } + dispose(); + } + + @Override + public java.awt.Dimension getPreferredSize() { + return new java.awt.Dimension(640, 480); + } + + /** + * @return the view + */ + public View getView() { + return view; + } + + @Override + public void windowActivated(final WindowEvent e) { + view.repaintDuringNextViewUpdate(); + } + + @Override + public void windowClosed(final WindowEvent e) { + } + + @Override + public void windowClosing(final WindowEvent e) { + } + + @Override + public void windowDeactivated(final WindowEvent e) { + } + + @Override + public void windowDeiconified(final WindowEvent e) { + view.repaintDuringNextViewUpdate(); + } + + @Override + public void windowIconified(final WindowEvent e) { + } + + @Override + public void windowOpened(final WindowEvent e) { + view.repaintDuringNextViewUpdate(); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewListener.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewListener.java new file mode 100644 index 0000000..4cb062d --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewListener.java @@ -0,0 +1,16 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.gui; + +public interface ViewListener { + + void exit(); + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewUpdateListener.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewUpdateListener.java new file mode 100644 index 0000000..e31bd2e --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewUpdateListener.java @@ -0,0 +1,21 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.gui; + +public interface ViewUpdateListener { + + /** + * @return true if underlying view shall be re-rendered. If at + * least one of the view update listeners returns true, + * view is re-rendered. + */ + boolean beforeViewUpdate(ViewContext viewContext, + final int millisecondsSinceLastFrame); +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewUpdateTimerTask.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewUpdateTimerTask.java new file mode 100755 index 0000000..1dba36a --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewUpdateTimerTask.java @@ -0,0 +1,27 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.gui; + +public class ViewUpdateTimerTask extends java.util.TimerTask { + + public View view; + + public ViewUpdateTimerTask(final View view) { + this.view = view; + } + + @Override + public void run() { + + // update and possibly render view + view.updateView(); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/KeyboardFocusTracker.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/KeyboardFocusTracker.java new file mode 100644 index 0000000..e4690d4 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/KeyboardFocusTracker.java @@ -0,0 +1,59 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.gui.humaninput; + +import eu.svjatoslav.sixth.e3d.gui.ViewContext; + +import java.util.Stack; + +public class KeyboardFocusTracker { + + private final ViewContext viewContext; + WorldNavigationTracker defaultInputHandler = new WorldNavigationTracker(); + Stack inputHandlers = new Stack<>(); + private UserInputHandler currentUserInputHandler; + + public KeyboardFocusTracker(final ViewContext viewContext) { + this.viewContext = viewContext; + setFocusOwner(defaultInputHandler); + } + + public UserInputHandler getCurrentFocusOwner() { + return currentUserInputHandler; + } + + public void popFocusOwner() { + if (currentUserInputHandler == null) + return; + + if (inputHandlers.isEmpty()) + return; + + currentUserInputHandler.focusLost(viewContext); + + currentUserInputHandler = inputHandlers.pop(); + + currentUserInputHandler.focusReceived(viewContext); + } + + public void setFocusOwner(final UserInputHandler inputHandler) { + if (currentUserInputHandler == inputHandler) + return; + + if (currentUserInputHandler != null) { + currentUserInputHandler.focusLost(viewContext); + inputHandlers.push(currentUserInputHandler); + } + + currentUserInputHandler = inputHandler; + + currentUserInputHandler.focusReceived(viewContext); + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/MouseClick.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/MouseClick.java new file mode 100644 index 0000000..be20a82 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/MouseClick.java @@ -0,0 +1,33 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.gui.humaninput; + +import eu.svjatoslav.sixth.e3d.geometry.Point2D; + +public class MouseClick { + + public Point2D coordinate; + + /** + * Indicates pressed mouse button. Except 0 that simply means mouse over + * given region. + */ + public int button; + + public MouseClick(final int x, final int y, final int button) { + this(new Point2D(x, y), button); + } + + public MouseClick(final Point2D coordinate, final int button) { + this.coordinate = coordinate; + this.button = button; + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/MouseInteractionController.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/MouseInteractionController.java new file mode 100644 index 0000000..36913ec --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/MouseInteractionController.java @@ -0,0 +1,29 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.gui.humaninput; + +public interface MouseInteractionController { + + /** + * Called when mouse is clicked on component + */ + void mouseClicked(); + + /** + * Called when mouse gets over given component. + */ + void mouseEntered(); + + /** + * Called when mouse leaves screen area occupied by component. + */ + void mouseExited(); + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/UserInputHandler.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/UserInputHandler.java new file mode 100644 index 0000000..72e5d38 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/UserInputHandler.java @@ -0,0 +1,27 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.gui.humaninput; + +import eu.svjatoslav.sixth.e3d.gui.ViewContext; +import eu.svjatoslav.sixth.e3d.gui.ViewUpdateListener; + +import java.awt.event.KeyEvent; + +public interface UserInputHandler extends ViewUpdateListener { + + void focusLost(ViewContext viewContext); + + void focusReceived(ViewContext viewContext); + + void keyPressed(KeyEvent event, ViewContext viewContext); + + void keyReleased(KeyEvent event, ViewContext viewContext); + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/UserInputTracker.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/UserInputTracker.java new file mode 100755 index 0000000..4a1ab34 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/UserInputTracker.java @@ -0,0 +1,246 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.gui.humaninput; + +import eu.svjatoslav.sixth.e3d.geometry.Point2D; +import eu.svjatoslav.sixth.e3d.gui.Avatar; +import eu.svjatoslav.sixth.e3d.gui.View; +import eu.svjatoslav.sixth.e3d.gui.ViewContext; +import eu.svjatoslav.sixth.e3d.gui.ViewUpdateListener; + +import javax.swing.*; +import java.awt.event.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class UserInputTracker + implements MouseMotionListener, KeyListener, MouseListener, MouseWheelListener, ViewUpdateListener { + + /** + *

+     * 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.
+     * 
+ */ + private final Map pressedKeysToPressedTimeMap = new HashMap<>(); + private final List detectedMouseClicks = new ArrayList<>(); + private final List 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 true if mouse events are detected and view needs to + * be repainted. + */ + public synchronized boolean handleDetectedMouseClicks(final View view) { + if (detectedMouseClicks.isEmpty()) { + + if (currentMouseLocation != null) + view.getRenderingContext().mouseClick = new MouseClick(currentMouseLocation, 0); + + if (mouseMoved) { + mouseMoved = false; + return true; + } else + return false; + } + + view.getRenderingContext().mouseClick = detectedMouseClicks.remove(0); + + return true; + } + + public boolean isKeyPressed(final int keyCode) { + return pressedKeysToPressedTimeMap.containsKey(keyCode); + } + + @Override + public void keyPressed(final KeyEvent evt) { + synchronized (detectedKeyEvents) { + pressedKeysToPressedTimeMap.put(evt.getKeyCode(), System.currentTimeMillis()); + detectedKeyEvents.add(evt); + viewContext.getView().repaintDuringNextViewUpdate(); + } + } + + @Override + public void keyReleased(final KeyEvent evt) { + synchronized (detectedKeyEvents) { + pressedKeysToPressedTimeMap.remove(evt.getKeyCode()); + detectedKeyEvents.add(evt); + viewContext.getView().repaintDuringNextViewUpdate(); + } + } + + @Override + public void keyTyped(final KeyEvent e) { + } + + @Override + public void mouseClicked(final MouseEvent e) { + synchronized (detectedMouseClicks) { + detectedMouseClicks.add(new MouseClick(e.getX(), e.getY(), e.getButton())); + } + } + + @Override + public void mouseDragged(final java.awt.event.MouseEvent evt) { + final Point2D mouseLocation = new Point2D(evt.getX(), evt.getY()); + + if (oldMouseCoordinatesWhenDragging == null) { + oldMouseCoordinatesWhenDragging = mouseLocation; + return; + } + + mouseDraggedDirection.add(mouseLocation.clone().subtract(oldMouseCoordinatesWhenDragging)); + + oldMouseCoordinatesWhenDragging = mouseLocation; + } + + @Override + public void mouseEntered(final MouseEvent e) { + mouseWithinWindow = true; + } + + @Override + public synchronized void mouseExited(final MouseEvent e) { + mouseWithinWindow = false; + currentMouseLocation = null; + } + + @Override + public synchronized void mouseMoved(final MouseEvent e) { + currentMouseLocation = new Point2D(e.getX(), e.getY()); + mouseMoved = true; + } + + @Override + public void mousePressed(final MouseEvent e) { + } + + @Override + public void mouseReleased(final java.awt.event.MouseEvent evt) { + oldMouseCoordinatesWhenDragging = null; + } + + @Override + public void mouseWheelMoved(final java.awt.event.MouseWheelEvent evt) { + wheelMovedDirection += evt.getWheelRotation(); + } + + /** + * Interpret mouse movement + */ + public boolean trackMouse() { + final Avatar avatar = viewContext.getAvatar(); + + // track mouse dragging + + // TODO: need to detect whether user moved mouse or touch screen + + // for mouse + avatar.setAngleXZ(avatar.getAngleXZ() - ((float) mouseDraggedDirection.x / 50)); + avatar.setAngleYZ(avatar.getAngleYZ() - ((float) mouseDraggedDirection.y / 50)); + + // for touch screen + // avatar.setAngleXZ(avatar.getAngleXZ() + ((float) + // mouseDraggedDirection.x / 50)); + // avatar.setAngleYZ(avatar.getAngleYZ() + ((float) + // mouseDraggedDirection.y / 50)); + + // track mouse wheel movements + final double actualAcceleration = 50 * avatar.avatarAcceleration * (1 + (avatar.getMovementSpeed() / 10)); + + avatar.getMovementDirection().y += (wheelMovedDirection * actualAcceleration); + avatar.enforceSpeedLimit(); + + // check if view shall be repainted + boolean repaintNeeded; + repaintNeeded = (mouseDraggedDirection.x != 0) || (mouseDraggedDirection.y != 0) || (wheelMovedDirection != 0); + + // reset movement counters + wheelMovedDirection = 0; + mouseDraggedDirection.zero(); + + return repaintNeeded; + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/WorldNavigationTracker.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/WorldNavigationTracker.java new file mode 100644 index 0000000..34fd37e --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/WorldNavigationTracker.java @@ -0,0 +1,72 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.gui.humaninput; + +import eu.svjatoslav.sixth.e3d.gui.Avatar; +import eu.svjatoslav.sixth.e3d.gui.ViewContext; +import eu.svjatoslav.sixth.e3d.gui.textEditorComponent.KeyboardHelper; + +import java.awt.event.KeyEvent; + +public class WorldNavigationTracker implements UserInputHandler { + + @Override + public boolean beforeViewUpdate(final ViewContext viewContext, + final int millisecondsSinceLastFrame) { + + trackKeys(millisecondsSinceLastFrame, viewContext); + return false; + } + + @Override + public void focusLost(final ViewContext viewContext) { + } + + @Override + public void focusReceived(final ViewContext viewContext) { + } + + @Override + public void keyPressed(final KeyEvent event, final ViewContext viewContext) { + } + + @Override + public void keyReleased(final KeyEvent event, final ViewContext viewContext) { + } + + /** + * interpret currently pressed keys + */ + public void trackKeys(final long millisecondsSinceLastFrame, + final ViewContext viewContext) { + + final UserInputTracker inputTracker = viewContext.getUserInputTracker(); + + final Avatar avatar = viewContext.getAvatar(); + + final double actualAcceleration = millisecondsSinceLastFrame + * avatar.avatarAcceleration + * (1 + (avatar.getMovementSpeed() / 10)); + + if (inputTracker.isKeyPressed(KeyboardHelper.UP)) + avatar.getMovementDirection().z += actualAcceleration; + + if (inputTracker.isKeyPressed(KeyboardHelper.DOWN)) + avatar.getMovementDirection().z -= actualAcceleration; + + if (inputTracker.isKeyPressed(KeyboardHelper.RIGHT)) + avatar.getMovementDirection().x += actualAcceleration; + + if (inputTracker.isKeyPressed(KeyboardHelper.LEFT)) + avatar.getMovementDirection().x -= actualAcceleration; + + avatar.enforceSpeedLimit(); + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/Character.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/Character.java new file mode 100644 index 0000000..0756d1c --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/Character.java @@ -0,0 +1,25 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.gui.textEditorComponent; + +public class Character { + + char value; + + // TODO: background and foreground colors + + public Character(final char value) { + this.value = value; + } + + boolean isEmpty() { + return value == ' '; + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/ColorConfig.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/ColorConfig.java new file mode 100644 index 0000000..623d7dc --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/ColorConfig.java @@ -0,0 +1,29 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.gui.textEditorComponent; + +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; + +public class ColorConfig { + + public Color normalText = new Color(255, 255, 255); + public Color normalBack = new Color(20, 20, 20, 255); + + public Color tabulatorBack = new Color(25, 25, 25, 255); + + public Color cursorText = new Color(255, 255, 255); + public Color cursorBack = new Color(255, 0, 0); + + public Color selectedText = new Color(255, 255, 255); + public Color selectedBack = new Color(0, 80, 80); + + public Color pageEnd = new Color(70, 70, 0); + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/KeyboardHelper.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/KeyboardHelper.java new file mode 100644 index 0000000..4edd809 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/KeyboardHelper.java @@ -0,0 +1,61 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.gui.textEditorComponent; + +import java.util.HashSet; +import java.util.Set; + +public class KeyboardHelper { + + public static final int TAB = 9; + public static final int DOWN = 40; + public static final int UP = 38; + public static final int RIGHT = 39; + public static final int LEFT = 37; + public static final int PGDOWN = 34; + public static final int PGUP = 33; + public static final int HOME = 36; + public static final int END = 35; + public static final int DEL = 127; + public static final int ENTER = 10; + public static final int BACKSPACE = 8; + public static final int ESC = 27; + public static final int SHIFT = 16; + + private static final Set nonText; + + static { + nonText = new HashSet<>(); + nonText.add(DOWN); + nonText.add(UP); + nonText.add(LEFT); + nonText.add(RIGHT); + + nonText.add(SHIFT); + nonText.add(ESC); + } + + public static boolean isAlt(final int modifiers) { + return (modifiers | 8) == modifiers; + } + + public static boolean isCtrl(final int modifiers) { + return (modifiers | 2) == modifiers; + } + + public static boolean isShift(final int modifiers) { + return (modifiers | 1) == modifiers; + } + + public static boolean isText(final int keyCode) { + return !nonText.contains(keyCode); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/Page.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/Page.java new file mode 100644 index 0000000..8757439 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/Page.java @@ -0,0 +1,93 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.gui.textEditorComponent; + +import java.util.ArrayList; +import java.util.List; + +public class Page { + + public List lines = new ArrayList<>(); + + public void ensureMaxTextLine(final int row) { + while (lines.size() <= row) + lines.add(new TextLine()); + } + + public char getChar(final int row, final int column) { + if (lines.size() <= row) + return ' '; + return lines.get(row).getCharForLocation(column); + } + + public TextLine getLine(final int row) { + ensureMaxTextLine(row); + return lines.get(row); + } + + public int getLineLength(final int row) { + if (lines.size() <= row) + return 0; + return lines.get(row).getLength(); + } + + public int getLinesCount() { + pack(); + return lines.size(); + } + + public String getText() { + pack(); + + final StringBuilder result = new StringBuilder(); + for (final TextLine textLine : lines) { + if (result.length() > 0) + result.append("\n"); + result.append(textLine.toString()); + } + return result.toString(); + } + + public void insertCharacter(final int row, final int col, final char value) { + getLine(row).insertCharacter(col, value); + } + + public void insertLine(final int row, final TextLine textLine) { + lines.add(row, textLine); + } + + private void pack() { + int newLength = 0; + + for (int i = lines.size() - 1; i >= 0; i--) + if (!lines.get(i).isEmpty()) { + newLength = i + 1; + break; + } + + if (newLength == lines.size()) + return; + + lines = lines.subList(0, newLength); + } + + public void removeCharacter(final int row, final int col) { + if (lines.size() <= row) + return; + getLine(row).removeCharacter(col); + } + + public void removeLine(final int row) { + if (lines.size() <= row) + return; + lines.remove(row); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/TextEditComponent.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/TextEditComponent.java new file mode 100755 index 0000000..d81da31 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/TextEditComponent.java @@ -0,0 +1,634 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.gui.textEditorComponent; + +import eu.svjatoslav.sixth.e3d.geometry.Point2D; +import eu.svjatoslav.sixth.e3d.geometry.Transform; +import eu.svjatoslav.sixth.e3d.gui.GuiComponent; +import eu.svjatoslav.sixth.e3d.gui.TextPointer; +import eu.svjatoslav.sixth.e3d.gui.ViewContext; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas.TextCanvas; + +import java.awt.*; +import java.awt.datatransfer.*; +import java.awt.event.KeyEvent; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +public class TextEditComponent extends GuiComponent implements ClipboardOwner { + + private static final long serialVersionUID = -7118833957783600630L; + // lines that need to be repainted + private final Set dirtyRows = new HashSet<>(); + private final TextCanvas textCanvas; + public int scrolledCharacters = 0, scrolledLines = 0; + public boolean selecting = false; + public TextPointer selectionStart = new TextPointer(0, 0); + public TextPointer selectionEnd = new TextPointer(0, 0); + public TextPointer cursorLocation; + Page page = new Page(); + ColorConfig colorConfig = new ColorConfig(); + boolean repaintPage = false; + + public TextEditComponent(final Transform transform, + final ViewContext viewContext, final Point2D size) { + super(transform, viewContext, size.to3D()); + + cursorLocation = new TextPointer(0, 0); + + // initialize visual panel + + final int columns = (int) (size.x / TextCanvas.FONT_CHAR_WIDTH); + final int rows = (int) (size.y / TextCanvas.FONT_CHAR_HEIGHT); + + textCanvas = new TextCanvas(new Transform(), new TextPointer(rows, + columns), Color.WHITE, colorConfig.normalBack); + + textCanvas.setMouseInteractionController(this); + + repaintPage(); + addShape(textCanvas); + } + + private void checkCursorBoundaries() { + if (cursorLocation.column < 0) + cursorLocation.column = 0; + if (cursorLocation.row < 0) + cursorLocation.row = 0; + + // ensure chat cursor stays within vertical editor boundaries by + // vertical scrolling + if ((cursorLocation.row - scrolledLines) < 0) + scroll(0, cursorLocation.row - scrolledLines); + + if ((((cursorLocation.row - scrolledLines) + 1)) > textCanvas.getSize().row) + scroll(0, + ((((((cursorLocation.row - scrolledLines) + 1) - textCanvas + .getSize().row))))); + + // ensure chat cursor stays within horizontal editor boundaries by + // horizontal scrolling + if ((cursorLocation.column - scrolledCharacters) < 0) + scroll(cursorLocation.column - scrolledCharacters, 0); + + if ((((cursorLocation.column - scrolledCharacters) + 1)) > textCanvas + .getSize().column) + scroll((((((cursorLocation.column - scrolledCharacters) + 1) - textCanvas + .getSize().column))), 0); + } + + /** + * Clear text selection. + */ + public void clearSelection() { + selectionEnd = new TextPointer(selectionStart); + repaintPage = true; + } + + /** + * Copies selected text to the clipboard. + */ + public void copyToClipboard() { + if (selectionStart.compareTo(selectionEnd) == 0) + return; + // System.out.println("Copy action."); + final StringBuilder msg = new StringBuilder(); + + ensureSelectionOrder(); + + for (int row = selectionStart.row; row <= selectionEnd.row; row++) { + final TextLine textLine = page.getLine(row); + + if (row == selectionStart.row) { + if (row == selectionEnd.row) + msg.append(textLine.getSubString(selectionStart.column, + selectionEnd.column + 1)); + else + msg.append(textLine.getSubString(selectionStart.column, + textLine.getLength())); + } else { + msg.append('\n'); + if (row == selectionEnd.row) + msg.append(textLine + .getSubString(0, selectionEnd.column + 1)); + else + msg.append(textLine.toString()); + } + } + + setClipboardContents(msg.toString()); + } + + public void cutToClipboard() { + copyToClipboard(); + deleteSelection(); + repaintPage(); + } + + public void deleteSelection() { + ensureSelectionOrder(); + int ym = 0; + + for (int line = selectionStart.row; line <= selectionEnd.row; line++) { + final TextLine currentLine = page.getLine(line - ym); + + if (line == selectionStart.row) { + if (line == selectionEnd.row) + + currentLine.cutSubString(selectionStart.column, + selectionEnd.column); + else if (selectionStart.column == 0) { + page.removeLine(line - ym); + ym++; + } else + currentLine.cutSubString(selectionStart.column, + currentLine.getLength() + 1); + } else if (line == selectionEnd.row) + currentLine.cutSubString(0, selectionEnd.column); + else { + page.removeLine(line - ym); + ym++; + } + } + + clearSelection(); + cursorLocation = new TextPointer(selectionStart); + } + + /** + * Ensures that {@link #selectionStart} is smaller than + * {@link #selectionEnd}. + */ + public void ensureSelectionOrder() { + if (selectionStart.compareTo(selectionEnd) > 0) { + final TextPointer temp = selectionEnd; + selectionEnd = selectionStart; + selectionStart = temp; + } + } + + public String getClipboardContents() { + String result = ""; + final Clipboard clipboard = Toolkit.getDefaultToolkit() + .getSystemClipboard(); + // odd: the Object param of getContents is not currently used + final Transferable contents = clipboard.getContents(null); + final boolean hasTransferableText = (contents != null) + && contents.isDataFlavorSupported(DataFlavor.stringFlavor); + if (hasTransferableText) + try { + result = (String) contents + .getTransferData(DataFlavor.stringFlavor); + } catch (final UnsupportedFlavorException | IOException ex) { + // highly unlikely since we are using a standard DataFlavor + System.out.println(ex); + } + // System.out.println(result); + return result; + } + + /** + * Place string into system clipboard so that it can be pasted into other + * applications. + */ + public void setClipboardContents(final String contents) { + final StringSelection stringSelection = new StringSelection(contents); + final Clipboard clipboard = Toolkit.getDefaultToolkit() + .getSystemClipboard(); + clipboard.setContents(stringSelection, stringSelection); + } + + public void goToLine(final int Line) { + // markNavigationLocation(Line); + scrolledLines = Line + 1; + cursorLocation.row = Line + 1; + cursorLocation.column = 0; + repaintPage(); + } + + public void insertText(final String txt) { + if (txt == null) + return; + + for (final char c : txt.toCharArray()) { + + if (c == KeyboardHelper.DEL) { + processDel(); + continue; + } + + if (c == KeyboardHelper.ENTER) { + processEnter(); + continue; + } + + if (c == KeyboardHelper.BACKSPACE) { + processBackspace(); + continue; + } + + // type character + if (KeyboardHelper.isText(c)) { + page.insertCharacter(cursorLocation.row, cursorLocation.column, + c); + cursorLocation.column++; + } + } + } + + /** + * Parse key presses. + */ + @Override + public void keyPressed(final KeyEvent event, final ViewContext viewContext) { + super.keyPressed(event, viewContext); + + processKeyEvent(event); + + markRowDirty(); + + checkCursorBoundaries(); + + repaintWhatNeeded(); + } + + /** + * Empty implementation of the ClipboardOwner interface. + */ + @Override + public void lostOwnership(final Clipboard aClipboard, + final Transferable aContents) { + // do nothing + } + + public void markRowDirty() { + dirtyRows.add(cursorLocation.row); + } + + public void pasteFromClipboard() { + insertText(getClipboardContents()); + } + + private void processBackspace() { + if (selectionStart.compareTo(selectionEnd) == 0) { + // erase single character + if (cursorLocation.column > 0) { + cursorLocation.column--; + page.removeCharacter(cursorLocation.row, cursorLocation.column); + // System.out.println(lines.get(currentCursor.line).toString()); + } else if (cursorLocation.row > 0) { + cursorLocation.row--; + final int currentLineLength = page + .getLineLength(cursorLocation.row); + cursorLocation.column = currentLineLength; + page.getLine(cursorLocation.row) + .insertTextLine(currentLineLength, + page.getLine(cursorLocation.row + 1)); + page.removeLine(cursorLocation.row + 1); + repaintPage = true; + } + } else { + // dedent multiple lines + ensureSelectionOrder(); + // scan if enough space exists + for (int y = selectionStart.row; y < selectionEnd.row; y++) + if (page.getLine(y).getIdent() < 4) + return; + + for (int y = selectionStart.row; y < selectionEnd.row; y++) + page.getLine(y).cutFromBeginning(4); + + repaintPage = true; + } + } + + private void processCtrlCombinations(final int keyCode) { + + if ((char) keyCode == 'A') { // CTRL + A -- select all + final int lastLineIndex = page.getLinesCount() - 1; + selectionStart = new TextPointer(0, 0); + selectionEnd = new TextPointer(lastLineIndex, + page.getLineLength(lastLineIndex)); + repaintPage(); + } + + // CTRL + X -- cut + if ((char) keyCode == 'X') + cutToClipboard(); + + // CTRL + C -- copy + if ((char) keyCode == 'C') + copyToClipboard(); + + // CTRL + V -- paste + if ((char) keyCode == 'V') + pasteFromClipboard(); + + if (keyCode == 39) { // RIGHT + // skip to the beginning of the next word + + for (int x = cursorLocation.column; x < (page + .getLineLength(cursorLocation.row) - 1); x++) + if ((page.getChar(cursorLocation.row, x) == ' ') + && (page.getChar(cursorLocation.row, x + 1) != ' ')) { + // beginning of the next word is found + cursorLocation.column = x + 1; + return; + } + + cursorLocation.column = page.getLineLength(cursorLocation.row); + return; + } + + if (keyCode == 37) { // Left + + // skip to the beginning of the previous word + for (int x = cursorLocation.column - 2; x >= 0; x--) + if ((page.getChar(cursorLocation.row, x) == ' ') + & (page.getChar(cursorLocation.row, x + 1) != ' ')) { + cursorLocation.column = x + 1; + return; + } + + cursorLocation.column = 0; + return; + } + } + + public void processDel() { + if (selectionStart.compareTo(selectionEnd) == 0) { + // is there still some text right to the cursor ? + if (cursorLocation.column < page.getLineLength(cursorLocation.row)) + page.removeCharacter(cursorLocation.row, cursorLocation.column); + else { + page.getLine(cursorLocation.row).insertTextLine( + cursorLocation.column, + page.getLine(cursorLocation.row + 1)); + page.removeLine(cursorLocation.row + 1); + repaintPage = true; + } + } else { + deleteSelection(); + repaintPage = true; + } + } + + private void processEnter() { + final TextLine currentLine = page.getLine(cursorLocation.row); + // move everything right to the cursor into new line + final TextLine newLine = currentLine.getSubLine(cursorLocation.column, + currentLine.getLength()); + page.insertLine(cursorLocation.row + 1, newLine); + + // trim existing line + page.getLine(cursorLocation.row).cutUntilEnd(cursorLocation.column); + repaintPage = true; + + cursorLocation.row++; + cursorLocation.column = 0; + } + + private void processKeyEvent(final KeyEvent event) { + final int modifiers = event.getModifiers(); + + final int keyCode = event.getKeyCode(); + final char keyChar = event.getKeyChar(); + + // System.out.println("Keycode:" + keyCode s+ ", keychar:" + keyChar); + + if (KeyboardHelper.isAlt(modifiers)) + return; + + if (KeyboardHelper.isCtrl(modifiers)) { + processCtrlCombinations(keyCode); + return; + } + + if (keyCode == KeyboardHelper.TAB) { + processTab(modifiers); + return; + } + + clearSelection(); + + if (KeyboardHelper.isText(keyCode)) { + insertText(String.valueOf(keyChar)); + return; + } + + // System.out.println("Co:" + String.valueOf(code) + " Ch:" + + // String.valueOf(keyChar)); + + if (KeyboardHelper.isShift(modifiers)) { + if (!selecting) + attemptSelectionStart:{ + + if (keyChar == 65535) + if (keyCode == 16) + break attemptSelectionStart; + if (((keyChar >= 32) & (keyChar <= 128)) | (keyChar == 10) + | (keyChar == 8) | (keyChar == 9)) + break attemptSelectionStart; + + // System.out.println("Selection started:" + keyChar + " " + // + keyCode); + + selectionStart = new TextPointer(cursorLocation); + selectionEnd = selectionStart; + selecting = true; + repaintPage(); + } + } else + selecting = false; + + if (keyCode == KeyboardHelper.HOME) { + cursorLocation.column = 0; + return; + } + if (keyCode == KeyboardHelper.END) { + cursorLocation.column = page.getLineLength(cursorLocation.row); + return; + } + + // process cursor keys + if (keyCode == KeyboardHelper.DOWN) { + markRowDirty(); + cursorLocation.row++; + return; + } + + if (keyCode == KeyboardHelper.UP) { + markRowDirty(); + cursorLocation.row--; + return; + } + + if (keyCode == KeyboardHelper.RIGHT) { + cursorLocation.column++; + return; + } + + if (keyCode == KeyboardHelper.LEFT) { + cursorLocation.column--; + return; + } + + if (keyCode == KeyboardHelper.PGDOWN) { + cursorLocation.row += textCanvas.getSize().row; + repaintPage(); + return; + } + + if (keyCode == KeyboardHelper.PGUP) { + cursorLocation.row -= textCanvas.getSize().row; + repaintPage = true; + return; + } + + } + + private void processTab(final int modifiers) { + if (KeyboardHelper.isShift(modifiers)) { + if (selectionStart.compareTo(selectionEnd) != 0) { + // dedent multiple lines + ensureSelectionOrder(); + + identSelection: + { + // check that identation is possible + for (int y = selectionStart.row; y < selectionEnd.row; y++) { + final TextLine textLine = page.getLine(y); + + if (!textLine.isEmpty()) + if (textLine.getIdent() < 4) + break identSelection; + } + + for (int y = selectionStart.row; y < selectionEnd.row; y++) + page.getLine(y).cutFromBeginning(4); + } + } else { + // dedent current line + final TextLine textLine = page.getLine(cursorLocation.row); + + if (cursorLocation.column >= 4) + if (textLine.isEmpty()) + cursorLocation.column -= 4; + else if (textLine.getIdent() >= 4) { + cursorLocation.column -= 4; + textLine.cutFromBeginning(4); + } + + } + + repaintPage(); + + } else if (selectionStart.compareTo(selectionEnd) != 0) { + // ident multiple lines + ensureSelectionOrder(); + for (int y = selectionStart.row; y < selectionEnd.row; y++) + page.getLine(y).addIdent(4); + + repaintPage(); + } + } + + public void repaintPage() { + + final int chXe = textCanvas.getSize().column + 2; + final int chYe = textCanvas.getSize().row + 2; + + for (int cy = 0; cy < chYe; cy++) + for (int cx = 0; cx < chXe; cx++) { + final boolean isTabMargin = ((cx + scrolledCharacters) % 4) == 0; + + if ((cx == (cursorLocation.column - scrolledCharacters)) + & (cy == (cursorLocation.row - scrolledLines))) { + // cursor + textCanvas.setBackgroundColor(colorConfig.cursorBack); + textCanvas.setForegroundColor(colorConfig.cursorText); + } else if (new TextPointer(cy + scrolledLines, cx).isBetween( + selectionStart, selectionEnd)) { + // selected text + textCanvas.setBackgroundColor(colorConfig.selectedBack); + textCanvas.setForegroundColor(colorConfig.selectedText); + } else { + // normal text + textCanvas.setBackgroundColor(colorConfig.normalBack); + textCanvas.setForegroundColor(colorConfig.normalText); + + if (isTabMargin) + textCanvas + .setBackgroundColor(colorConfig.tabulatorBack); + + } + + final char charUnderCursor = page.getChar(cy + scrolledLines, + cx + scrolledCharacters); + + textCanvas.putChar(cy, cx, charUnderCursor); + } + + } + + public void repaintRow(final int rowNumber) { + // TODO: fix this + repaintPage(); + } + + private void repaintWhatNeeded() { + if (repaintPage) { + dirtyRows.clear(); + repaintPage(); + return; + } + + dirtyRows.forEach(this::repaintRow); + dirtyRows.clear(); + } + + // public void setCaret(final int x, final int y) { + // selecting = false; + // cursorLocation.column = (x / characterWidth) + scrolledCharacters; + // cursorLocation.row = (y / characterHeight) + scrolledLines; + // repaintPage(); + // } + + /** + * Scroll full page to given amount of lines or charancters. + */ + public void scroll(final int charactersToScroll, final int linesToScroll) { + scrolledLines += linesToScroll; + scrolledCharacters += charactersToScroll; + + if (scrolledLines < 0) + scrolledLines = 0; + + if (scrolledCharacters < 0) + scrolledCharacters = 0; + + repaintPage = true; + } + + public void setText(final String text) { + // System.out.println("Set text:" + text); + cursorLocation = new TextPointer(0, 0); + scrolledCharacters = 0; + scrolledLines = 0; + selectionStart = new TextPointer(0, 0); + selectionEnd = new TextPointer(0, 0); + page = new Page(); + insertText(text); + repaintPage(); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/TextLine.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/TextLine.java new file mode 100755 index 0000000..d3cf315 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/TextLine.java @@ -0,0 +1,211 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.gui.textEditorComponent; + +import java.util.ArrayList; +import java.util.List; + +public class TextLine { + + private List chars = new ArrayList<>(); + + public TextLine() { + } + + public TextLine(final List 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 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 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 result = new ArrayList<>(); + + for (int i = from; i < until; i++) { + if (i >= chars.size()) + break; + result.add(chars.get(i)); + } + + return new TextLine(result); + } + + public String getSubString(final int from, final int until) { + final StringBuilder result = new StringBuilder(); + + for (int i = from; i < until; i++) + result.append(getCharForLocation(i)); + + return result.toString(); + } + + public void insertCharacter(final int col, final char value) { + ensureLength(col); + chars.add(col, new Character(value)); + pack(); + } + + public void insertString(final int col, final String value) { + ensureLength(col); + int i = 0; + for (final char c : value.toCharArray()) { + chars.add(col + i, new Character(c)); + i++; + } + pack(); + } + + public void insertTextLine(final int col, final TextLine textLine) { + ensureLength(col); + int i = 0; + for (final Character c : textLine.getChars()) { + chars.add(col + i, c); + i++; + } + pack(); + } + + public boolean isEmpty() { + return chars.isEmpty(); + } + + private void pack() { + int newLength = 0; + + for (int i = chars.size() - 1; i >= 0; i--) + if (!chars.get(i).isEmpty()) { + newLength = i + 1; + break; + } + + if (newLength == chars.size()) + return; + + chars = chars.subList(0, newLength); + } + + public void removeCharacter(final int col) { + if (col >= chars.size()) + return; + + chars.remove(col); + } + + public void setValue(final String string) { + chars.clear(); + for (final char c : string.toCharArray()) + chars.add(new Character(c)); + + pack(); + } + + @Override + public String toString() { + final StringBuilder buffer = new StringBuilder(); + + for (final Character character : chars) + buffer.append(character.value); + + return buffer.toString(); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/io/Connexion3D.java b/src/main/java/eu/svjatoslav/sixth/e3d/io/Connexion3D.java new file mode 100644 index 0000000..20fd41a --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/io/Connexion3D.java @@ -0,0 +1,40 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.io; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; + +/** + * 3D Connexion mouse adapter. + *

+ * Idea is to read Linux device file and interpret resulting numbers. + *

+ * TODO: unfinished + */ + +public class Connexion3D { + + public static void main(final String[] args) throws IOException { + + final BufferedReader in = new BufferedReader(new FileReader( + "/dev/hidraw4")); + + + while (true) { + System.out.print(in.read() + " "); + System.out.println("\n"); + } + + // in.close(); + + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/OctreeVolume.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/OctreeVolume.java new file mode 100755 index 0000000..4b483e0 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/OctreeVolume.java @@ -0,0 +1,1007 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.octree; + +import eu.svjatoslav.sixth.e3d.renderer.octree.raytracer.Ray; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance; + +/** + *

+ * 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
+ * 
+ */ + +public class OctreeVolume { + + public static final int TRACE_NO_HIT = -1; + /** + * Single solid color. + */ + private static final int CELL_STATE_SOLID = -2; + private static final int CELL_STATE_UNUSED = -1; + final LineAppearance factory = new LineAppearance(); + public int ce1[]; + public int ce2[]; + public int ce3[]; + public int ce4[]; + public int ce5[]; + public int ce6[]; + public int ce7[]; + public int ce8[]; + public int cellAllocationPointer = 0; + public int usedCellsCount = 0; + public int masterCellSize; + + public OctreeVolume() { + initWorld(1500000, 256 * 64); + } + + public void breakSolidCell(final int pointer) { + final int color = getCellColor(pointer); + final int illumination = getCellIllumination(pointer); + + ce1[pointer] = makeNewCell(color, illumination); + ce2[pointer] = makeNewCell(color, illumination); + ce3[pointer] = makeNewCell(color, illumination); + ce4[pointer] = makeNewCell(color, illumination); + ce5[pointer] = makeNewCell(color, illumination); + ce6[pointer] = makeNewCell(color, illumination); + ce7[pointer] = makeNewCell(color, illumination); + ce8[pointer] = makeNewCell(color, illumination); + } + + public void clearCell(final int pointer) { + ce1[pointer] = 0; + ce2[pointer] = 0; + ce3[pointer] = 0; + ce4[pointer] = 0; + + ce5[pointer] = 0; + ce6[pointer] = 0; + ce7[pointer] = 0; + ce8[pointer] = 0; + } + + public void deleteCell(final int cellPointer) { + clearCell(cellPointer); + ce1[cellPointer] = CELL_STATE_UNUSED; + usedCellsCount--; + } + + public int doesIntersect(final int cubeX, final int cubeY, final int cubeZ, + final int cubeSize, final Ray r) { + + // ray starts inside the cube + if ((cubeX - cubeSize) < r.x) + if ((cubeX + cubeSize) > r.x) + if ((cubeY - cubeSize) < r.y) + if ((cubeY + cubeSize) > r.y) + if ((cubeZ - cubeSize) < r.z) + if ((cubeZ + cubeSize) > r.z) { + r.hitX = r.x; + r.hitY = r.y; + r.hitZ = r.z; + return 1; + } + // back face + if (r.zp > 0) + if ((cubeZ - cubeSize) > r.z) { + final double mult = ((cubeZ - cubeSize) - r.z) / r.zp; + final double hitX = (r.xp * mult) + r.x; + if ((cubeX - cubeSize) < hitX) + if ((cubeX + cubeSize) > hitX) { + final double hitY = (r.yp * mult) + r.y; + if ((cubeY - cubeSize) < hitY) + if ((cubeY + cubeSize) > hitY) { + r.hitX = hitX; + r.hitY = hitY; + r.hitZ = cubeZ - cubeSize; + return 2; + } + } + } + + // up face + if (r.yp > 0) + if ((cubeY - cubeSize) > r.y) { + final double mult = ((cubeY - cubeSize) - r.y) / r.yp; + final double hitX = (r.xp * mult) + r.x; + if ((cubeX - cubeSize) < hitX) + if ((cubeX + cubeSize) > hitX) { + final double hitZ = (r.zp * mult) + r.z; + if ((cubeZ - cubeSize) < hitZ) + if ((cubeZ + cubeSize) > hitZ) { + r.hitX = hitX; + r.hitY = cubeY - cubeSize; + r.hitZ = hitZ; + return 3; + } + } + } + + // left face + if (r.xp > 0) + if ((cubeX - cubeSize) > r.x) { + final double mult = ((cubeX - cubeSize) - r.x) / r.xp; + final double hitY = (r.yp * mult) + r.y; + if ((cubeY - cubeSize) < hitY) + if ((cubeY + cubeSize) > hitY) { + final double hitZ = (r.zp * mult) + r.z; + if ((cubeZ - cubeSize) < hitZ) + if ((cubeZ + cubeSize) > hitZ) { + r.hitX = cubeX - cubeSize; + r.hitY = hitY; + r.hitZ = hitZ; + return 4; + } + } + } + + // front face + if (r.zp < 0) + if ((cubeZ + cubeSize) < r.z) { + final double mult = ((cubeZ + cubeSize) - r.z) / r.zp; + final double hitX = (r.xp * mult) + r.x; + if ((cubeX - cubeSize) < hitX) + if ((cubeX + cubeSize) > hitX) { + final double hitY = (r.yp * mult) + r.y; + if ((cubeY - cubeSize) < hitY) + if ((cubeY + cubeSize) > hitY) { + r.hitX = hitX; + r.hitY = hitY; + r.hitZ = cubeZ + cubeSize; + return 5; + } + } + } + + // down face + if (r.yp < 0) + if ((cubeY + cubeSize) < r.y) { + final double mult = ((cubeY + cubeSize) - r.y) / r.yp; + final double hitX = (r.xp * mult) + r.x; + if ((cubeX - cubeSize) < hitX) + if ((cubeX + cubeSize) > hitX) { + final double hitZ = (r.zp * mult) + r.z; + if ((cubeZ - cubeSize) < hitZ) + if ((cubeZ + cubeSize) > hitZ) { + r.hitX = hitX; + r.hitY = cubeY + cubeSize; + r.hitZ = hitZ; + return 6; + } + } + } + + // right face + if (r.xp < 0) + if ((cubeX + cubeSize) < r.x) { + final double mult = ((cubeX + cubeSize) - r.x) / r.xp; + final double hitY = (r.yp * mult) + r.y; + if ((cubeY - cubeSize) < hitY) + if ((cubeY + cubeSize) > hitY) { + final double hitZ = (r.zp * mult) + r.z; + if ((cubeZ - cubeSize) < hitZ) + if ((cubeZ + cubeSize) > hitZ) { + r.hitX = cubeX + cubeSize; + r.hitY = hitY; + r.hitZ = hitZ; + return 7; + } + } + } + return 0; + } + + /** + * Fill 3D rectangle. + */ + public void fillRect3D(int x1, int y1, int z1, int x2, int y2, int z2, + final Color color) { + int t; + if (x1 > x2) { + t = x1; + x1 = x2; + x2 = t; + } + + if (y1 > y2) { + t = y1; + y1 = y2; + y2 = t; + } + + if (z1 > z2) { + t = z1; + z1 = z2; + z2 = t; + } + + for (int x = x1; x <= x2; x++) + for (int y = y1; y <= y2; y++) + for (int z = z1; z <= z2; z++) + putCell(x, y, z, 0, 0, 0, masterCellSize, 0, color); + + } + + public int getCellColor(final int pointer) { + return ce2[pointer]; + } + + public int getCellIllumination(final int pointer) { + return ce3[pointer]; + } + + public void initWorld(final int bufferLength, final int masterCellSize) { + // System.out.println("Initializing new world"); + + // initialize world storage buffer + this.masterCellSize = masterCellSize; + + ce1 = new int[bufferLength]; + ce2 = new int[bufferLength]; + ce3 = new int[bufferLength]; + ce4 = new int[bufferLength]; + + ce5 = new int[bufferLength]; + ce6 = new int[bufferLength]; + ce7 = new int[bufferLength]; + ce8 = new int[bufferLength]; + + for (int i = 0; i < bufferLength; i++) + ce1[i] = CELL_STATE_UNUSED; + + // initialize master cell + clearCell(0); + } + + public boolean isCellSolid(final int pointer) { + return ce1[pointer] == CELL_STATE_SOLID; + } + + public int makeNewCell() { + for (; ; ) { + if (cellAllocationPointer >= ce1.length) + cellAllocationPointer = 0; + + if (ce1[cellAllocationPointer] == CELL_STATE_UNUSED) { + + clearCell(cellAllocationPointer); + + usedCellsCount++; + return cellAllocationPointer; + } else + cellAllocationPointer++; + } + } + + public int makeNewCell(final int color, final int illumination) { + final int pointer = makeNewCell(); + markCellAsSolid(pointer); + setCellColor(pointer, color); + setCellIllumination(pointer, illumination); + return pointer; + } + + public void markCellAsSolid(final int pointer) { + ce1[pointer] = CELL_STATE_SOLID; + } + + public void putCell(final int x, final int y, final int z, final Color color) { + putCell(x, y, z, 0, 0, 0, masterCellSize, 0, color); + } + + private void putCell(final int x, final int y, final int z, + final int cellX, final int cellY, final int cellZ, + final int cellSize, final int cellPointer, final Color color) { + + if (cellSize > 1) { + + // if case of big cell + if (isCellSolid(cellPointer)) { + + // if cell is already a needed color, do notheing + if (getCellColor(cellPointer) == color.toInt()) + return; + + // otherwise break cell up + breakSolidCell(cellPointer); + + // continue, as if it is cluster now + } + + // decide witch subcube to use + int subCubeArray[]; + int subX, subY, subZ; + + if (x > cellX) { + subX = (cellSize / 2) + cellX; + if (y > cellY) { + subY = (cellSize / 2) + cellY; + if (z > cellZ) { + subZ = (cellSize / 2) + cellZ; + // 7 + subCubeArray = ce7; + } else { + subZ = (-cellSize / 2) + cellZ; + // 3 + subCubeArray = ce3; + } + } else { + subY = (-cellSize / 2) + cellY; + if (z > cellZ) { + subZ = (cellSize / 2) + cellZ; + // 6 + subCubeArray = ce6; + } else { + subZ = (-cellSize / 2) + cellZ; + // 2 + subCubeArray = ce2; + } + } + } else { + subX = (-cellSize / 2) + cellX; + if (y > cellY) { + subY = (cellSize / 2) + cellY; + if (z > cellZ) { + subZ = (cellSize / 2) + cellZ; + // 8 + subCubeArray = ce8; + } else { + subZ = (-cellSize / 2) + cellZ; + // 4 + subCubeArray = ce4; + } + } else { + subY = (-cellSize / 2) + cellY; + if (z > cellZ) { + subZ = (cellSize / 2) + cellZ; + // 5 + subCubeArray = ce5; + } else { + subZ = (-cellSize / 2) + cellZ; + // 1 + subCubeArray = ce1; + } + } + } + + int subCubePointer; + if (subCubeArray[cellPointer] == 0) { + // create empty cluster + subCubePointer = makeNewCell(); + subCubeArray[cellPointer] = subCubePointer; + } else + subCubePointer = subCubeArray[cellPointer]; + + putCell(x, y, z, subX, subY, subZ, cellSize / 2, subCubePointer, + color); + } else { + ce1[cellPointer] = CELL_STATE_SOLID; + ce2[cellPointer] = color.toInt(); + ce3[cellPointer] = CELL_STATE_UNUSED; + // System.out.println("Cell written!"); + } + } + + public void setCellColor(final int pointer, final int color) { + ce2[pointer] = color; + } + + public void setCellIllumination(final int pointer, final int illumination) { + ce3[pointer] = illumination; + } + + /** + * @return intersecting cell pointer or -1 if no cell in intersecting this + * ray. + */ + public int traceCell(final int cellX, final int cellY, final int cellZ, + final int cellSize, final int pointer, final Ray ray) { + if (isCellSolid(pointer)) { + // solid cell + if (doesIntersect(cellX, cellY, cellZ, cellSize, ray) != 0) { + ray.hitCellSize = cellSize; + ray.hitCellX = cellX; + ray.hitCellY = cellY; + ray.hitCellZ = cellZ; + return pointer; + } + return TRACE_NO_HIT; + } else // cluster + if (doesIntersect(cellX, cellY, cellZ, cellSize, ray) != 0) { + final int halfOfCellSize = cellSize / 2; + int rayIntersectionResult; + + if (ray.x > cellX) { + if (ray.y > cellY) { + if (ray.z > cellZ) { + // 7 + // 6 8 3 5 2 4 1 + + if (ce7[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY + halfOfCellSize, + cellZ + halfOfCellSize, halfOfCellSize, + ce7[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce6[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY - halfOfCellSize, + cellZ + halfOfCellSize, halfOfCellSize, + ce6[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce8[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY + halfOfCellSize, + cellZ + halfOfCellSize, halfOfCellSize, + ce8[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce3[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY + halfOfCellSize, + cellZ - halfOfCellSize, halfOfCellSize, + ce3[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce2[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY - halfOfCellSize, + cellZ - halfOfCellSize, halfOfCellSize, + ce2[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce4[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY + halfOfCellSize, + cellZ - halfOfCellSize, halfOfCellSize, + ce4[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce5[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY - halfOfCellSize, + cellZ + halfOfCellSize, halfOfCellSize, + ce5[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce1[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY - halfOfCellSize, + cellZ - halfOfCellSize, halfOfCellSize, + ce1[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + } else { + // 3 + // 2 4 7 1 6 8 5 + if (ce3[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY + halfOfCellSize, + cellZ - halfOfCellSize, halfOfCellSize, + ce3[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce2[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY - halfOfCellSize, + cellZ - halfOfCellSize, halfOfCellSize, + ce2[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce4[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY + halfOfCellSize, + cellZ - halfOfCellSize, halfOfCellSize, + ce4[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce7[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY + halfOfCellSize, + cellZ + halfOfCellSize, halfOfCellSize, + ce7[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce6[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY - halfOfCellSize, + cellZ + halfOfCellSize, halfOfCellSize, + ce6[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce8[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY + halfOfCellSize, + cellZ + halfOfCellSize, halfOfCellSize, + ce8[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce1[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY - halfOfCellSize, + cellZ - halfOfCellSize, halfOfCellSize, + ce1[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce5[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY - halfOfCellSize, + cellZ + halfOfCellSize, halfOfCellSize, + ce5[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + } + } else if (ray.z > cellZ) { + // 6 + // 5 2 7 8 1 3 4 + if (ce6[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY - halfOfCellSize, cellZ + + halfOfCellSize, halfOfCellSize, ce6[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce7[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY + halfOfCellSize, cellZ + + halfOfCellSize, halfOfCellSize, ce7[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce2[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY - halfOfCellSize, cellZ + - halfOfCellSize, halfOfCellSize, ce2[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce5[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY - halfOfCellSize, cellZ + + halfOfCellSize, halfOfCellSize, ce5[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce8[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY + halfOfCellSize, cellZ + + halfOfCellSize, halfOfCellSize, ce8[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce3[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY + halfOfCellSize, cellZ + - halfOfCellSize, halfOfCellSize, ce3[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce1[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY - halfOfCellSize, cellZ + - halfOfCellSize, halfOfCellSize, ce1[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce4[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY + halfOfCellSize, cellZ + - halfOfCellSize, halfOfCellSize, ce4[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + } else { + // 2 + // 1 3 6 5 4 7 8 + if (ce2[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY - halfOfCellSize, cellZ + - halfOfCellSize, halfOfCellSize, ce2[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce3[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY + halfOfCellSize, cellZ + - halfOfCellSize, halfOfCellSize, ce3[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce1[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY - halfOfCellSize, cellZ + - halfOfCellSize, halfOfCellSize, ce1[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce6[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY - halfOfCellSize, cellZ + + halfOfCellSize, halfOfCellSize, ce6[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce7[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY + halfOfCellSize, cellZ + + halfOfCellSize, halfOfCellSize, ce7[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce5[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY - halfOfCellSize, cellZ + + halfOfCellSize, halfOfCellSize, ce5[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce4[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY + halfOfCellSize, cellZ + - halfOfCellSize, halfOfCellSize, ce4[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce8[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY + halfOfCellSize, cellZ + + halfOfCellSize, halfOfCellSize, ce8[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + } + } else if (ray.y > cellY) { + if (ray.z > cellZ) { + // 8 + // 5 7 4 1 6 3 2 + + if (ce8[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY + halfOfCellSize, cellZ + + halfOfCellSize, halfOfCellSize, ce8[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce7[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY + halfOfCellSize, cellZ + + halfOfCellSize, halfOfCellSize, ce7[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce5[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY - halfOfCellSize, cellZ + + halfOfCellSize, halfOfCellSize, ce5[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce4[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY + halfOfCellSize, cellZ + - halfOfCellSize, halfOfCellSize, ce4[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce3[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY + halfOfCellSize, cellZ + - halfOfCellSize, halfOfCellSize, ce3[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce1[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY - halfOfCellSize, cellZ + - halfOfCellSize, halfOfCellSize, ce1[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce6[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY - halfOfCellSize, cellZ + + halfOfCellSize, halfOfCellSize, ce6[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce2[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY - halfOfCellSize, cellZ + - halfOfCellSize, halfOfCellSize, ce2[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + } else { + // 4 + // 1 3 8 5 7 2 6 + + if (ce4[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY + halfOfCellSize, cellZ + - halfOfCellSize, halfOfCellSize, ce4[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce8[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY + halfOfCellSize, cellZ + + halfOfCellSize, halfOfCellSize, ce8[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce3[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY + halfOfCellSize, cellZ + - halfOfCellSize, halfOfCellSize, ce3[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce1[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY - halfOfCellSize, cellZ + - halfOfCellSize, halfOfCellSize, ce1[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce7[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY + halfOfCellSize, cellZ + + halfOfCellSize, halfOfCellSize, ce7[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce5[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + - halfOfCellSize, cellY - halfOfCellSize, cellZ + + halfOfCellSize, halfOfCellSize, ce5[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce2[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY - halfOfCellSize, cellZ + - halfOfCellSize, halfOfCellSize, ce2[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce6[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + + halfOfCellSize, cellY - halfOfCellSize, cellZ + + halfOfCellSize, halfOfCellSize, ce6[pointer], + ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + } + } else if (ray.z > cellZ) { + // 5 + // 1 6 8 4 2 7 3 + + if (ce5[pointer] != 0) { + rayIntersectionResult = traceCell(cellX - halfOfCellSize, + cellY - halfOfCellSize, cellZ + halfOfCellSize, + halfOfCellSize, ce5[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce1[pointer] != 0) { + rayIntersectionResult = traceCell(cellX - halfOfCellSize, + cellY - halfOfCellSize, cellZ - halfOfCellSize, + halfOfCellSize, ce1[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce6[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + halfOfCellSize, + cellY - halfOfCellSize, cellZ + halfOfCellSize, + halfOfCellSize, ce6[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce8[pointer] != 0) { + rayIntersectionResult = traceCell(cellX - halfOfCellSize, + cellY + halfOfCellSize, cellZ + halfOfCellSize, + halfOfCellSize, ce8[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce4[pointer] != 0) { + rayIntersectionResult = traceCell(cellX - halfOfCellSize, + cellY + halfOfCellSize, cellZ - halfOfCellSize, + halfOfCellSize, ce4[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce7[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + halfOfCellSize, + cellY + halfOfCellSize, cellZ + halfOfCellSize, + halfOfCellSize, ce7[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce2[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + halfOfCellSize, + cellY - halfOfCellSize, cellZ - halfOfCellSize, + halfOfCellSize, ce2[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce3[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + halfOfCellSize, + cellY + halfOfCellSize, cellZ - halfOfCellSize, + halfOfCellSize, ce3[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + } else { + // 1 + // 5 2 4 8 6 3 7 + + if (ce1[pointer] != 0) { + rayIntersectionResult = traceCell(cellX - halfOfCellSize, + cellY - halfOfCellSize, cellZ - halfOfCellSize, + halfOfCellSize, ce1[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce5[pointer] != 0) { + rayIntersectionResult = traceCell(cellX - halfOfCellSize, + cellY - halfOfCellSize, cellZ + halfOfCellSize, + halfOfCellSize, ce5[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce2[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + halfOfCellSize, + cellY - halfOfCellSize, cellZ - halfOfCellSize, + halfOfCellSize, ce2[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce4[pointer] != 0) { + rayIntersectionResult = traceCell(cellX - halfOfCellSize, + cellY + halfOfCellSize, cellZ - halfOfCellSize, + halfOfCellSize, ce4[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce6[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + halfOfCellSize, + cellY - halfOfCellSize, cellZ + halfOfCellSize, + halfOfCellSize, ce6[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce8[pointer] != 0) { + rayIntersectionResult = traceCell(cellX - halfOfCellSize, + cellY + halfOfCellSize, cellZ + halfOfCellSize, + halfOfCellSize, ce8[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + + if (ce3[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + halfOfCellSize, + cellY + halfOfCellSize, cellZ - halfOfCellSize, + halfOfCellSize, ce3[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + if (ce7[pointer] != 0) { + rayIntersectionResult = traceCell(cellX + halfOfCellSize, + cellY + halfOfCellSize, cellZ + halfOfCellSize, + halfOfCellSize, ce7[pointer], ray); + if (rayIntersectionResult >= 0) + return rayIntersectionResult; + } + } + } + return TRACE_NO_HIT; + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/package-info.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/package-info.java new file mode 100755 index 0000000..7494166 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/package-info.java @@ -0,0 +1,15 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.octree; + +/** + * Realtime voxel renderer (in software). + * Uses octree for data compression. + */ diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/Camera.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/Camera.java new file mode 100755 index 0000000..07a7909 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/Camera.java @@ -0,0 +1,107 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.octree.raytracer; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.geometry.Transform; +import eu.svjatoslav.sixth.e3d.gui.Avatar; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.TexturedRectangle; +import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.net.URL; + +public class Camera extends TexturedRectangle { + + public static final int CAMERA_SIZE = 100; + public static final int IMAGE_SIZE = 500; + private final CameraView cameraView; + private Point3D camCenter; + + public Camera(final Avatar avatar, final double zoom) { + super(new Transform(avatar.getLocation().clone())); + cameraView = new CameraView(avatar, zoom); + + computeCameraCoordinates(avatar); + + addWaitNotification(getTexture()); + } + + private void addWaitNotification(final Texture texture) { + // add hourglass icon + try { + final BufferedImage sprite = getSprite("eu/svjatoslav/sixth/e3d/examples/hourglass.png"); + texture.graphics.drawImage(sprite, IMAGE_SIZE / 2, + (IMAGE_SIZE / 2) - 30, null); + } catch (final Exception ignored) { + } + + // add "Please wait..." message + texture.graphics.setColor(java.awt.Color.WHITE); + texture.graphics.setFont(new Font("Monospaced", Font.PLAIN, 10)); + texture.graphics.drawString("Please wait...", (IMAGE_SIZE / 2) - 20, + (IMAGE_SIZE / 2) + 30); + } + + private void computeCameraCoordinates(final Avatar avatar) { + initialize(CAMERA_SIZE, CAMERA_SIZE, IMAGE_SIZE, IMAGE_SIZE, 3); + + camCenter = Point3D.ZERO.clone(); + + topLeft.setValues(camCenter.x, camCenter.y, camCenter.z + CAMERA_SIZE); + + topRight.clone(topLeft); + bottomLeft.clone(topLeft); + bottomRight.clone(topLeft); + + final float viewAngle = (float) .6; + + topLeft.rotate(camCenter, -viewAngle, -viewAngle); + topRight.rotate(camCenter, viewAngle, -viewAngle); + bottomLeft.rotate(camCenter, -viewAngle, viewAngle); + bottomRight.rotate(camCenter, viewAngle, viewAngle); + + topLeft.rotate(camCenter, -avatar.getAngleXZ(), -avatar.getAngleYZ()); + topRight.rotate(camCenter, -avatar.getAngleXZ(), -avatar.getAngleYZ()); + bottomLeft + .rotate(camCenter, -avatar.getAngleXZ(), -avatar.getAngleYZ()); + bottomRight.rotate(camCenter, -avatar.getAngleXZ(), + -avatar.getAngleYZ()); + + final Color cameraColor = new Color(255, 255, 0, 255); + final LineAppearance appearance = new LineAppearance(2, cameraColor); + + // addShape(appearance.getLine(camCenter, upLeft)); + // addShape(appearance.getLine(camCenter, upRight)); + // addShape(appearance.getLine(camCenter, downLeft)); + // addShape(appearance.getLine(camCenter, downRight)); + + addShape(appearance.getLine(topLeft, topRight)); + addShape(appearance.getLine(bottomLeft, bottomRight)); + addShape(appearance.getLine(topLeft, bottomLeft)); + addShape(appearance.getLine(topRight, bottomRight)); + + } + + public CameraView getCameraView() { + return cameraView; + } + + public BufferedImage getSprite(final String ref) throws IOException { + final URL url = this.getClass().getClassLoader().getResource(ref); + return ImageIO.read(url); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/CameraView.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/CameraView.java new file mode 100644 index 0000000..f5fc65e --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/CameraView.java @@ -0,0 +1,49 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.octree.raytracer; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.gui.Avatar; + +public class CameraView { + + Point3D camCenter; + Point3D upLeft; + Point3D upRight; + Point3D downLeft; + Point3D downRight; + + public CameraView(final Avatar avatar, final double zoom) { + computeCameraCoordinates(avatar, zoom); + } + + private void computeCameraCoordinates(final Avatar avatar, final double zoom) { + camCenter = new Point3D(avatar.getLocation()).scaleDown(zoom); + + upLeft = new Point3D(camCenter.x, camCenter.y, camCenter.z + + Camera.CAMERA_SIZE); + upRight = new Point3D(upLeft); + downLeft = new Point3D(upLeft); + downRight = new Point3D(upLeft); + + final float viewAngle = (float) .6; + + upLeft.rotate(camCenter, -viewAngle, -viewAngle); + upRight.rotate(camCenter, viewAngle, -viewAngle); + downLeft.rotate(camCenter, -viewAngle, viewAngle); + downRight.rotate(camCenter, viewAngle, viewAngle); + + upLeft.rotate(camCenter, -avatar.getAngleXZ(), -avatar.getAngleYZ()); + upRight.rotate(camCenter, -avatar.getAngleXZ(), -avatar.getAngleYZ()); + downLeft.rotate(camCenter, -avatar.getAngleXZ(), -avatar.getAngleYZ()); + downRight.rotate(camCenter, -avatar.getAngleXZ(), -avatar.getAngleYZ()); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/LightSource.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/LightSource.java new file mode 100755 index 0000000..fcf93a4 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/LightSource.java @@ -0,0 +1,33 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.octree.raytracer; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; + +public class LightSource { + + public int x, y, z; + + public Color color; + + public float brightness; + + public LightSource(final Point3D location, final Color color, + final float Brightness) { + x = (int) location.x; + y = (int) location.y; + z = (int) location.z; + + this.color = color; + brightness = Brightness; + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/Ray.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/Ray.java new file mode 100755 index 0000000..93c53f6 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/Ray.java @@ -0,0 +1,77 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.octree.raytracer; + +public class Ray { + + public double x, y, z; + + public double xp, yp, zp; + + public double hitX, hitY, hitZ; + + public int hitCellSize; + + public int hitCellX, hitCellY, hitCellZ; + + /* + * public int orientation; + * + * public static final int ORIENTATION_RIGHT = 0; + * + * public static final int ORIENTATION_LEFT = 1; + * + * public static final int ORIENTATION_DOWN = 2; + * + * public static final int ORIENTATION_UP = 3; + * + * public static final int ORIENTATION_FORWARD = 4; + * + * public static final int ORIENTATION_BACK = 5; + * + * public static final String orientations[] = { "RIGHT", "LEFT", "DOWN", + * "UP", "FORWARD", "BACK" }; + */ + + public Ray(final double X, final double Y, final double Z, final double Xp, + final double Yp, final double Zp) { + x = X; + y = Y; + z = Z; + xp = Xp; + yp = Yp; + zp = Zp; + // calculateOrientation(); + } + + /* + * public void calculateOrientation() { float axp = Math.abs(xp); float ayp + * = Math.abs(yp); float azp = Math.abs(zp); + * + * if (axp > ayp) { if (axp > azp) { if (xp > 0) { orientation = + * ORIENTATION_RIGHT; } else { orientation = ORIENTATION_LEFT; } } else { if + * (zp > 0) { orientation = ORIENTATION_FORWARD; } else { orientation = + * ORIENTATION_BACK; } } } else { if (ayp > azp) { if (yp > 0) { orientation + * = ORIENTATION_DOWN; } else { orientation = ORIENTATION_UP; } } else { if + * (zp > 0) { orientation = ORIENTATION_FORWARD; } else { orientation = + * ORIENTATION_BACK; } } } + * + * + * } + */ + @Override + public String toString() { + return "Ray \n" + " x " + x + "\n" + " y " + y + "\n" + " z " + z + + "\n" + " xp " + xp + "\n" + " yp " + yp + "\n" + " zp " + zp + + "\n"; /* + * + " orientation " + orientations[orientation]; + */ + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/RayHit.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/RayHit.java new file mode 100755 index 0000000..9f5348b --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/RayHit.java @@ -0,0 +1,25 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.octree.raytracer; + +public class RayHit { + + float x, y, z; + + int cellPointer; + + public RayHit(final float x, final float y, final float z, + final int cellPointer) { + this.x = x; + this.y = y; + this.z = z; + this.cellPointer = cellPointer; + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/RayTracer.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/RayTracer.java new file mode 100755 index 0000000..47b3bd3 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/RayTracer.java @@ -0,0 +1,306 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.octree.raytracer; + +import eu.svjatoslav.sixth.e3d.gui.View; +import eu.svjatoslav.sixth.e3d.renderer.octree.OctreeVolume; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture; + +import java.util.Vector; + +public class RayTracer implements Runnable { + + private static final int PROGRESS_UPDATE_FREQUENCY_MILLIS = 1000; + private final Camera camera; + private final Texture texture; + private final View view; + private OctreeVolume octreeVolume; + private Vector lights; + private int computedLights; + + public RayTracer(final Texture texture, final OctreeVolume octreeVolume, + final Vector lights, final Camera camera, + final View view) { + + this.texture = texture; + this.octreeVolume = octreeVolume; + this.lights = lights; + this.camera = camera; + this.view = view; + } + + @Override + public void run() { + computedLights = 0; + + // create camera + + // Camera cam = new Camera(camCenter, upLeft, upRight, downLeft, + // downRight); + + // add camera to the raytracing point + // Main.mainWorld.geometryCollection.addObject(cam); + // Main.mainWorld.compiledGeometry.compileGeometry(Main.mainWorld.geometryCollection); + + final int width = texture.primaryBitmap.width; + final int height = texture.primaryBitmap.height; + + final CameraView cameraView = camera.getCameraView(); + + // calculate vertical vectors + final double x1p = cameraView.downLeft.x - cameraView.upLeft.x; + final double y1p = cameraView.downLeft.y - cameraView.upLeft.y; + final double z1p = cameraView.downLeft.z - cameraView.upLeft.z; + + final double x2p = cameraView.downRight.x - cameraView.upRight.x; + final double y2p = cameraView.downRight.y - cameraView.upRight.y; + final double z2p = cameraView.downRight.z - cameraView.upRight.z; + + long nextBitmapUpdate = System.currentTimeMillis() + + PROGRESS_UPDATE_FREQUENCY_MILLIS; + + for (int y = 0; y < height; y++) { + final double cx1 = cameraView.upLeft.x + ((x1p * y) / height); + final double cy1 = cameraView.upLeft.y + ((y1p * y) / height); + final double cz1 = cameraView.upLeft.z + ((z1p * y) / height); + + final double cx2 = cameraView.upRight.x + ((x2p * y) / height); + final double cy2 = cameraView.upRight.y + ((y2p * y) / height); + final double cz2 = cameraView.upRight.z + ((z2p * y) / height); + + // calculate horisontal vector + final double x3p = cx2 - cx1; + final double y3p = cy2 - cy1; + final double z3p = cz2 - cz1; + + for (int x = 0; x < width; x++) { + final double cx3 = cx1 + ((x3p * x) / width); + final double cy3 = cy1 + ((y3p * x) / width); + final double cz3 = cz1 + ((z3p * x) / width); + + final Ray r = new Ray(cameraView.camCenter.x, + cameraView.camCenter.y, cameraView.camCenter.z, cx3 + - cameraView.camCenter.x, cy3 + - cameraView.camCenter.y, cz3 + - cameraView.camCenter.z); + final int c = traceRay(r); + + final Color color = new Color(c); + texture.primaryBitmap.drawPixel(x, y, color); + } + + if (System.currentTimeMillis() > nextBitmapUpdate) { + nextBitmapUpdate = System.currentTimeMillis() + + PROGRESS_UPDATE_FREQUENCY_MILLIS; + texture.resetResampledBitmapCache(); + view.repaintDuringNextViewUpdate(); + } + } + + texture.resetResampledBitmapCache(); + view.repaintDuringNextViewUpdate(); + // System.out.println("Raytracing done."); + // System.out.println("New lights computed:" + computedLights); + } + + public int traceLight(final LightSource l, final int cubeX, + final int cubeY, final int cubeZ) { + return 0; + } + + public int traceRay(final Ray r) { + + final int re = octreeVolume.traceCell(0, 0, 0, + octreeVolume.masterCellSize, 0, r); + + if (re != -1) { + // if lightening not computed, compute it + if (octreeVolume.ce3[re] == -1) + // if cell is larger than 1 + if (r.hitCellSize > 1) { + // break it up + octreeVolume.breakSolidCell(re); + return traceRay(r); + } else { + computedLights++; + float red = 30, green = 30, blue = 30; + + for (final LightSource l : lights) { + final int xDist = (l.x - r.hitCellX); + final int yDist = (l.y - r.hitCellY); + final int zDist = (l.z - r.hitCellZ); + + double newRed = 0, newGreen = 0, newBlue = 0; + double tempRed, tempGreen, tempBlue; + + double distance = Math.sqrt((xDist * xDist) + + (yDist * yDist) + (zDist * zDist)); + distance = (distance / 3) + 1; + + final Ray r1 = new Ray(r.hitCellX, r.hitCellY + - (float) 1.5, r.hitCellZ, + + (float) l.x - (float) r.hitCellX, l.y + - (r.hitCellY - (float) 1.5), (float) l.z + - (float) r.hitCellZ); + + final int rt1 = octreeVolume.traceCell(0, 0, 0, + octreeVolume.masterCellSize, 0, r1); + + if (rt1 == -1) { + newRed = (l.color.r * l.brightness) / distance; + newGreen = (l.color.g * l.brightness) / distance; + newBlue = (l.color.b * l.brightness) / distance; + } + + final Ray r2 = new Ray(r.hitCellX - (float) 1.5, + r.hitCellY, r.hitCellZ, + + l.x - (r.hitCellX - (float) 1.5), (float) l.y + - (float) r.hitCellY, (float) l.z + - (float) r.hitCellZ); + + final int rt2 = octreeVolume.traceCell(0, 0, 0, + octreeVolume.masterCellSize, 0, r2); + + if (rt2 == -1) { + tempRed = (l.color.r * l.brightness) / distance; + tempGreen = (l.color.g * l.brightness) / distance; + tempBlue = (l.color.b * l.brightness) / distance; + + if (tempRed > newRed) + newRed = tempRed; + if (tempGreen > newGreen) + newGreen = tempGreen; + if (tempBlue > newBlue) + newBlue = tempBlue; + } + + final Ray r3 = new Ray(r.hitCellX, r.hitCellY, + r.hitCellZ - (float) 1.5, + + (float) l.x - (float) r.hitCellX, (float) l.y + - (float) r.hitCellY, l.z + - (r.hitCellZ - (float) 1.5)); + + final int rt3 = octreeVolume.traceCell(0, 0, 0, + octreeVolume.masterCellSize, 0, r3); + + if (rt3 == -1) { + tempRed = (l.color.r * l.brightness) / distance; + tempGreen = (l.color.g * l.brightness) / distance; + tempBlue = (l.color.b * l.brightness) / distance; + if (tempRed > newRed) + newRed = tempRed; + if (tempGreen > newGreen) + newGreen = tempGreen; + if (tempBlue > newBlue) + newBlue = tempBlue; + } + + final Ray r4 = new Ray(r.hitCellX, r.hitCellY + + (float) 1.5, r.hitCellZ, + + (float) l.x - (float) r.hitCellX, l.y + - (r.hitCellY + (float) 1.5), (float) l.z + - (float) r.hitCellZ); + + final int rt4 = octreeVolume.traceCell(0, 0, 0, + octreeVolume.masterCellSize, 0, r4); + + if (rt4 == -1) { + tempRed = (l.color.r * l.brightness) / distance; + tempGreen = (l.color.g * l.brightness) / distance; + tempBlue = (l.color.b * l.brightness) / distance; + if (tempRed > newRed) + newRed = tempRed; + if (tempGreen > newGreen) + newGreen = tempGreen; + if (tempBlue > newBlue) + newBlue = tempBlue; + } + + final Ray r5 = new Ray(r.hitCellX + (float) 1.5, + r.hitCellY, r.hitCellZ, + + l.x - (r.hitCellX + (float) 1.5), (float) l.y + - (float) r.hitCellY, (float) l.z + - (float) r.hitCellZ); + + final int rt5 = octreeVolume.traceCell(0, 0, 0, + octreeVolume.masterCellSize, 0, r5); + + if (rt5 == -1) { + tempRed = (l.color.r * l.brightness) / distance; + tempGreen = (l.color.g * l.brightness) / distance; + tempBlue = (l.color.b * l.brightness) / distance; + if (tempRed > newRed) + newRed = tempRed; + if (tempGreen > newGreen) + newGreen = tempGreen; + if (tempBlue > newBlue) + newBlue = tempBlue; + } + + final Ray r6 = new Ray(r.hitCellX, r.hitCellY, + r.hitCellZ + (float) 1.5, + + (float) l.x - (float) r.hitCellX, (float) l.y + - (float) r.hitCellY, l.z + - (r.hitCellZ + (float) 1.5)); + + final int rt6 = octreeVolume.traceCell(0, 0, 0, + octreeVolume.masterCellSize, 0, r6); + + if (rt6 == -1) { + tempRed = (l.color.r * l.brightness) / distance; + tempGreen = (l.color.g * l.brightness) / distance; + tempBlue = (l.color.b * l.brightness) / distance; + if (tempRed > newRed) + newRed = tempRed; + if (tempGreen > newGreen) + newGreen = tempGreen; + if (tempBlue > newBlue) + newBlue = tempBlue; + } + red += newRed; + green += newGreen; + blue += newBlue; + + } + + final int cellColor = octreeVolume.ce2[re]; + + red = (red * ((cellColor & 0xFF0000) >> 16)) / 255; + green = (green * ((cellColor & 0xFF00) >> 8)) / 255; + blue = (blue * (cellColor & 0xFF)) / 255; + + if (red > 255) + red = 255; + if (green > 255) + green = 255; + if (blue > 255) + blue = 255; + + octreeVolume.ce3[re] = (((int) red) << 16) + + (((int) green) << 8) + ((int) blue); + + } + if (octreeVolume.ce3[re] == 0) + return octreeVolume.ce2[re]; + return octreeVolume.ce3[re]; + } + + // return (200 << 16) + (200 << 8) + 255; + return 0; + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/package-info.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/package-info.java new file mode 100755 index 0000000..a801ead --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/octree/raytracer/package-info.java @@ -0,0 +1,14 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.octree.raytracer; + +/** + * Raytracer through voxel data. + */ diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/package-info.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/package-info.java new file mode 100755 index 0000000..32d1160 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/package-info.java @@ -0,0 +1,14 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer; + +/** + * Various 3D renderers utilizing different rendering approaches. + */ diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/Color.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/Color.java new file mode 100644 index 0000000..125ef6e --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/Color.java @@ -0,0 +1,138 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster; + +public final class Color { + + public static final Color RED = new Color(255, 0, 0, 255); + public static final Color GREEN = new Color(0, 255, 0, 255); + public static final Color BLUE = new Color(0, 0, 255, 255); + public static final Color YELLOW = new Color(255, 255, 0, 255); + public static final Color WHITE = new Color(255, 255, 255, 255); + public static final Color BLACK = new Color(0, 0, 0, 255); + public static final Color PURPLE = new Color(255, 0, 255, 255); + public static final Color TRANSPARENT = new Color(0, 0, 0, 0); + + public final int r, g, b; + + /** + *
+     * 255 is opaque.
+     * 0 is transparent.
+     * 
+ */ + 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)); + } + + /** + *
+     *     Supported formats are:
+     *
+     *     RGB
+     *     RGBA
+     *     RRGGBB
+     *     RRGGBBAA
+     * 
+ */ + public Color(String colorHexCode) { + switch (colorHexCode.length()) { + case 3: + r = parseHexSegment(colorHexCode, 0, 1) * 16; + g = parseHexSegment(colorHexCode, 1, 1) * 16; + b = parseHexSegment(colorHexCode, 2, 1) * 16; + a = 255; + return; + + case 4: + r = parseHexSegment(colorHexCode, 0, 1) * 16; + g = parseHexSegment(colorHexCode, 1, 1) * 16; + b = parseHexSegment(colorHexCode, 2, 1) * 16; + a = parseHexSegment(colorHexCode, 3, 1) * 16; + return; + + case 6: + r = parseHexSegment(colorHexCode, 0, 2); + g = parseHexSegment(colorHexCode, 2, 2); + b = parseHexSegment(colorHexCode, 4, 2); + a = 255; + return; + + case 8: + r = parseHexSegment(colorHexCode, 0, 2); + g = parseHexSegment(colorHexCode, 2, 2); + b = parseHexSegment(colorHexCode, 4, 2); + a = parseHexSegment(colorHexCode, 6, 2); + return; + default: + throw new IllegalArgumentException("Unsupported color code: " + colorHexCode); + } + } + + public Color(final int rgb) { + r = (rgb & 0xFF0000) >> 16; + g = (rgb & 0xFF00) >> 8; + b = rgb & 0xFF; + a = 255; + } + + public Color(final int r, final int g, final int b) { + this(r, g, b, 255); + } + + public Color(final int r, final int g, final int b, final int a) { + this.r = ensureByteLimit(r); + this.g = ensureByteLimit(g); + this.b = ensureByteLimit(b); + this.a = ensureByteLimit(a); + } + + private int parseHexSegment(String hexString, int start, int length) { + return Integer.parseInt(hexString.substring(start, start + length), 16); + } + + /** + * Ensure that color values are within allowed limits of 0 to 255. + */ + private int ensureByteLimit(final int value) { + if (value < 0) + return 0; + + if (value > 255) + return 255; + + return value; + } + + public boolean isTransparent() { + return a == 0; + } + + public java.awt.Color toAwtColor() { + return new java.awt.Color(r, g, b, a); + } + + public int toInt() { + return toAwtColor().getRGB(); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/RenderAggregator.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/RenderAggregator.java new file mode 100644 index 0000000..74340c9 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/RenderAggregator.java @@ -0,0 +1,54 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster; + +import eu.svjatoslav.sixth.e3d.gui.RenderingContext; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractCoordinateShape; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; + +public class RenderAggregator { + + ArrayList 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, Serializable { + + @Override + public int compare(final AbstractCoordinateShape o1, final AbstractCoordinateShape o2) { + if (o1.getZ() < o2.getZ()) + return 1; + else if (o1.getZ() > o2.getZ()) + return -1; + + return Integer.compare(o1.shapeId, o2.shapeId); + } + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/ShapeCollection.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/ShapeCollection.java new file mode 100755 index 0000000..8864f74 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/ShapeCollection.java @@ -0,0 +1,68 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.geometry.Transform; +import eu.svjatoslav.sixth.e3d.geometry.TransformPipe; +import eu.svjatoslav.sixth.e3d.gui.Avatar; +import eu.svjatoslav.sixth.e3d.gui.RenderingContext; +import eu.svjatoslav.sixth.e3d.gui.ViewContext; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractShape; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class ShapeCollection { + + private final RenderAggregator aggregator = new RenderAggregator(); + private final TransformPipe transformPipe = new TransformPipe(); + private final List shapes = new ArrayList<>(); + + public synchronized void addShape(final AbstractShape shape) { + shapes.add(shape); + } + + public Collection getShapes() { + return shapes; + } + + public void clear() { + shapes.clear(); + } + + public synchronized void paint(final ViewContext viewContext, + final RenderingContext renderingContext) { + + renderingContext.frameNumber++; + + aggregator.reset(); + transformPipe.clear(); + + // translate scene according to avatar current location + final Avatar avatar = viewContext.getAvatar(); + + // rotate scene according to avatar looking direction + transformPipe.addTransform(new Transform(new Point3D(), avatar + .getAngleXZ(), avatar.getAngleYZ())); + + // translate scene according to avatar location in the world + final Point3D translation = new Point3D(-avatar.getLocation().x, + -avatar.getLocation().y, -avatar.getLocation().z); + + transformPipe.addTransform(new Transform(translation, 0, 0)); + + for (final AbstractShape shape : shapes) + shape.transform(transformPipe, aggregator, renderingContext); + + aggregator.paint(renderingContext); + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/package-info.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/package-info.java new file mode 100755 index 0000000..980cd12 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/package-info.java @@ -0,0 +1,19 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster; + +/** + *
+ * Realtime renderer (in software) using traditional approaches:
+ *      Wireframe
+ *      Textured polygons
+ *      Z-Buffer
+ * 
+ */ diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/AbstractCoordinateShape.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/AbstractCoordinateShape.java new file mode 100644 index 0000000..1a5bf0a --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/AbstractCoordinateShape.java @@ -0,0 +1,73 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes; + +import eu.svjatoslav.sixth.e3d.geometry.GeometryCoordinate; +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.geometry.TransformPipe; +import eu.svjatoslav.sixth.e3d.gui.RenderingContext; +import eu.svjatoslav.sixth.e3d.renderer.raster.RenderAggregator; + +import java.util.concurrent.atomic.AtomicInteger; + +public abstract class AbstractCoordinateShape extends AbstractShape { + + public static final AtomicInteger lastShapeId = new AtomicInteger(); + public final int shapeId; + public double onScreenZ; + public GeometryCoordinate[] coordinates; + + public AbstractCoordinateShape(final int pointsCount) { + coordinates = new GeometryCoordinate[pointsCount]; + for (int i = 0; i < pointsCount; i++) + coordinates[i] = new GeometryCoordinate(); + + shapeId = lastShapeId.getAndIncrement(); + } + + public AbstractCoordinateShape(final Point3D... locationPoints) { + coordinates = new GeometryCoordinate[locationPoints.length]; + + for (int i = 0; i < locationPoints.length; i++) + coordinates[i] = new GeometryCoordinate(locationPoints[i]); + + shapeId = lastShapeId.getAndIncrement(); + } + + public double getZ() { + return onScreenZ; + } + + public abstract void paint(RenderingContext renderBuffer); + + @Override + public void transform(final TransformPipe transforms, + final RenderAggregator aggregator, + final RenderingContext renderingContext) { + + double accumulatedZ = 0; + boolean paint = true; + + for (final GeometryCoordinate geometryPoint : coordinates) { + geometryPoint.transform(transforms, renderingContext); + + accumulatedZ += geometryPoint.transformedCoordinate.z; + + if (!geometryPoint.transformedCoordinate.withinDrawingLimits()) + paint = false; + } + + if (paint) { + onScreenZ = accumulatedZ / coordinates.length; + aggregator.queueShapeForRendering(this); + } + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/AbstractShape.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/AbstractShape.java new file mode 100644 index 0000000..d2db2bf --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/AbstractShape.java @@ -0,0 +1,30 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes; + +import eu.svjatoslav.sixth.e3d.geometry.TransformPipe; +import eu.svjatoslav.sixth.e3d.gui.RenderingContext; +import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController; +import eu.svjatoslav.sixth.e3d.renderer.raster.RenderAggregator; + +public abstract class AbstractShape { + + public MouseInteractionController mouseInteractionController; + + public void setMouseInteractionController( + final MouseInteractionController mouseInteractionController) { + this.mouseInteractionController = mouseInteractionController; + } + + public abstract void transform(final TransformPipe transforms, + final RenderAggregator aggregator, + final RenderingContext renderingContext); + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/ForwardOrientedTexture.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/ForwardOrientedTexture.java new file mode 100644 index 0000000..49af48c --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/ForwardOrientedTexture.java @@ -0,0 +1,108 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic; + +import eu.svjatoslav.sixth.e3d.geometry.Point2D; +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.gui.RenderingContext; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractCoordinateShape; +import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture; +import eu.svjatoslav.sixth.e3d.renderer.raster.texture.TextureBitmap; + +public class ForwardOrientedTexture extends AbstractCoordinateShape { + + public static final double SIZE_MULTIPLIER = 0.005; + public Texture texture; + private double scale; + + public ForwardOrientedTexture(final Point3D point, final double scale, + final Texture texture) { + super(point); + this.texture = texture; + setScale(scale); + } + + @Override + public void paint(final RenderingContext targetRenderingArea) { + + final double z = coordinates[0].transformedCoordinate.z; + + final double visibleHorizontalDistanceFromCenter = (targetRenderingArea.width + * scale * texture.primaryBitmap.width) + / z; + + final double visibleVerticalDistanceFromCenter = (targetRenderingArea.width + * scale * texture.primaryBitmap.height) + / z; + + // compute visible pixel density, and get appropriate bitmap + final double zoom = (visibleHorizontalDistanceFromCenter * 2) + / texture.primaryBitmap.width; + + final TextureBitmap textureBitmap = texture.getZoomedBitmap(zoom); + + final Point2D onScreenLocation = coordinates[0].onScreenCoordinate; + + // compute Y + final int initialYStart = (int) (onScreenLocation.y - visibleVerticalDistanceFromCenter); + final int initialYEnd = (int) (onScreenLocation.y + visibleVerticalDistanceFromCenter); + final int maxYDistance = initialYEnd - initialYStart; + + int yStart = initialYStart; + int yEnd = initialYEnd; + + if (yStart < 0) + yStart = 0; + + if (yEnd > targetRenderingArea.height) + yEnd = targetRenderingArea.height; + + // compute X + final int initialXStart = (int) (onScreenLocation.x - visibleHorizontalDistanceFromCenter); + final int initialXEnd = (int) (onScreenLocation.x + visibleHorizontalDistanceFromCenter); + final int maxXDistance = initialXEnd - initialXStart; + + int xStart = initialXStart; + int xEnd = initialXEnd; + + if (xStart < 0) + xStart = 0; + + if (xEnd > targetRenderingArea.width) + xEnd = targetRenderingArea.width; + + final byte[] targetRenderingAreaBytes = targetRenderingArea.bytes; + + final int textureWidth = textureBitmap.width; + + for (int y = yStart; y < yEnd; y++) { + + final int relativeTextureOffset = ((textureBitmap.height * (y - initialYStart)) / maxYDistance) + * textureWidth; + + int targetRenderingAreaOffset = ((y * targetRenderingArea.width) + xStart) * 4; + + for (int x = xStart; x < xEnd; x++) { + + final int textureOffset = (relativeTextureOffset + ((textureWidth * (x - initialXStart)) / maxXDistance)) * 4; + + textureBitmap.drawPixel(textureOffset, + targetRenderingAreaBytes, targetRenderingAreaOffset); + + targetRenderingAreaOffset += 4; + } + } + } + + public void setScale(final double scale) { + this.scale = scale * SIZE_MULTIPLIER; + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/GlowingPoint.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/GlowingPoint.java new file mode 100644 index 0000000..29cad63 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/GlowingPoint.java @@ -0,0 +1,56 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture; + +public class GlowingPoint extends ForwardOrientedTexture { + + private static final int TEXTURE_SIZE = 50; + private final Color color; + + public GlowingPoint(final Point3D point, final double pointSize, + final Color color) { + super(point, pointSize, getTexture(color)); + this.color = color; + + getTexture(color); + + } + + public static Texture getTexture(final Color color) { + final Texture texture = new Texture(TEXTURE_SIZE, TEXTURE_SIZE, 1); + for (int x = 0; x < TEXTURE_SIZE; x++) + for (int y = 0; y < TEXTURE_SIZE; y++) { + int address = texture.primaryBitmap.getAddress(x, y); + + final int distance = (int) Math + .sqrt((((TEXTURE_SIZE / 2) - x) * ((TEXTURE_SIZE / 2) - x)) + + (((TEXTURE_SIZE / 2) - y) * ((TEXTURE_SIZE / 2) - y))); + + int alpha = 255 - ((270 * distance) / (TEXTURE_SIZE / 2)); + if (alpha < 0) + alpha = 0; + + texture.primaryBitmap.bytes[address] = (byte) alpha; + address++; + texture.primaryBitmap.bytes[address] = (byte) color.b; + address++; + texture.primaryBitmap.bytes[address] = (byte) color.g; + address++; + texture.primaryBitmap.bytes[address] = (byte) color.r; + } + + return texture; + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/Line.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/Line.java new file mode 100644 index 0000000..490e825 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/Line.java @@ -0,0 +1,319 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line; + +import eu.svjatoslav.sixth.e3d.geometry.Point2D; +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.gui.RenderingContext; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractCoordinateShape; + +public class Line extends AbstractCoordinateShape { + + private static final double MINIMUM_WIDTH_THRESHOLD = 1; + + private static final double LINE_WIDTH_MULTIPLIER = 0.2d; + + public final double width; + final LineInterpolator[] li = new LineInterpolator[4]; + public Color color; + + public Line(final Line parentLine) { + this(parentLine.coordinates[0].coordinate.clone(), + parentLine.coordinates[1].coordinate.clone(), new Color( + parentLine.color), parentLine.width); + } + + public Line(final Point3D point1, final Point3D point2, final Color color, + final double width) { + + super(point1, point2); + + this.color = color; + this.width = width; + + for (int i = 0; i < li.length; i++) + li[i] = new LineInterpolator(); + + } + + public void drawHorizontalLine(final LineInterpolator line1, + final LineInterpolator line2, final int y, + final RenderingContext renderBuffer) { + + int x1 = line1.getX(y); + int x2 = line2.getX(y); + + double d1 = line1.getD(); + double d2 = line2.getD(); + + if (x1 > x2) { + final int tmp = x1; + x1 = x2; + x2 = tmp; + + final double tmp2 = d1; + d1 = d2; + d2 = tmp2; + } + + final int unclippedWidth = x2 - x1; + final double dinc = (d2 - d1) / unclippedWidth; + + if (x1 < 0) { + d1 += (dinc * (-x1)); + x1 = 0; + } + + if (x2 >= renderBuffer.width) + x2 = renderBuffer.width - 1; + + final int drawnWidth = x2 - x1; + + int offset = ((y * renderBuffer.width) + x1) * 4; + final byte[] offSreenBufferBytes = renderBuffer.bytes; + + final int lineAlpha = color.a; + + final int colorB = color.b; + final int colorG = color.g; + final int colorR = color.r; + + for (int i = 0; i < drawnWidth; i++) { + + final double alphaMultiplier = 1d - Math.abs(d1); + + final int realLineAlpha = (int) (lineAlpha * alphaMultiplier); + final int backgroundAlpha = 255 - realLineAlpha; + + offSreenBufferBytes[offset] = (byte) 255; + offset++; + offSreenBufferBytes[offset] = (byte) ((((offSreenBufferBytes[offset] & 0xff) * backgroundAlpha) + (colorB * realLineAlpha)) / 256); + offset++; + offSreenBufferBytes[offset] = (byte) ((((offSreenBufferBytes[offset] & 0xff) * backgroundAlpha) + (colorG * realLineAlpha)) / 256); + offset++; + offSreenBufferBytes[offset] = (byte) ((((offSreenBufferBytes[offset] & 0xff) * backgroundAlpha) + (colorR * realLineAlpha)) / 256); + offset++; + + d1 += dinc; + } + + } + + public void drawSinglePixelHorizontalLine(final RenderingContext buffer, + final int alpha) { + + final Point2D onScreenPoint1 = coordinates[0].onScreenCoordinate; + final Point2D onScreenPoint2 = coordinates[1].onScreenCoordinate; + + int xStart = (int) onScreenPoint1.x; + int xEnd = (int) onScreenPoint2.x; + + int lineHeight; + int yBase; + + if (xStart > xEnd) { + final int tmp = xStart; + xStart = xEnd; + xEnd = tmp; + lineHeight = (int) (onScreenPoint1.y - onScreenPoint2.y); + yBase = (int) onScreenPoint2.y; + } else { + yBase = (int) onScreenPoint1.y; + lineHeight = (int) (onScreenPoint2.y - onScreenPoint1.y); + } + + final int lineWidth = xEnd - xStart; + if (lineWidth == 0) + return; + + final byte[] offSreenBufferBytes = buffer.bytes; + final int backgroundAlpha = 255 - alpha; + + final int blueWithAlpha = color.b * alpha; + final int greenWithAplha = color.g * alpha; + final int redWithAlpha = color.r * alpha; + + for (int relativeX = 0; relativeX <= lineWidth; relativeX++) { + final int x = xStart + relativeX; + + if ((x >= 0) && (x < buffer.width)) { + + final int y = yBase + ((relativeX * lineHeight) / lineWidth); + if ((y >= 0) && (y < buffer.height)) { + int ramOffset = ((y * buffer.width) + x) * 4; + + offSreenBufferBytes[ramOffset] = (byte) 255; + ramOffset++; + offSreenBufferBytes[ramOffset] = (byte) ((((offSreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + blueWithAlpha) / 256); + ramOffset++; + offSreenBufferBytes[ramOffset] = (byte) ((((offSreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + greenWithAplha) / 256); + ramOffset++; + offSreenBufferBytes[ramOffset] = (byte) ((((offSreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + redWithAlpha) / 256); + } + } + } + + } + + public void drawSinglePixelVerticalLine(final RenderingContext buffer, + final int alpha) { + + final Point2D onScreenPoint1 = coordinates[0].onScreenCoordinate; + final Point2D onScreenPoint2 = coordinates[1].onScreenCoordinate; + + int yStart = (int) onScreenPoint1.y; + int yEnd = (int) onScreenPoint2.y; + + int lineWidth; + int xBase; + + if (yStart > yEnd) { + final int tmp = yStart; + yStart = yEnd; + yEnd = tmp; + lineWidth = (int) (onScreenPoint1.x - onScreenPoint2.x); + xBase = (int) onScreenPoint2.x; + } else { + xBase = (int) onScreenPoint1.x; + lineWidth = (int) (onScreenPoint2.x - onScreenPoint1.x); + } + + final int lineHeight = yEnd - yStart; + if (lineHeight == 0) + return; + + final byte[] offSreenBufferBytes = buffer.bytes; + final int backgroundAlpha = 255 - alpha; + + final int blueWithAlpha = color.b * alpha; + final int greenWithAplha = color.g * alpha; + final int redWithAlpha = color.r * alpha; + + for (int relativeY = 0; relativeY <= lineHeight; relativeY++) { + final int y = yStart + relativeY; + + if ((y >= 0) && (y < buffer.height)) { + + final int x = xBase + ((relativeY * lineWidth) / lineHeight); + if ((x >= 0) && (x < buffer.width)) { + int ramOffset = ((y * buffer.width) + x) * 4; + + offSreenBufferBytes[ramOffset] = (byte) 255; + ramOffset++; + offSreenBufferBytes[ramOffset] = (byte) ((((offSreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + blueWithAlpha) / 256); + ramOffset++; + offSreenBufferBytes[ramOffset] = (byte) ((((offSreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + greenWithAplha) / 256); + ramOffset++; + offSreenBufferBytes[ramOffset] = (byte) ((((offSreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + redWithAlpha) / 256); + } + } + } + } + + public int getLineInterpolator(final int startPointer, final int y) { + + for (int i = startPointer; i < li.length; i++) + if (li[i].containsY(y)) + return i; + return -1; + } + + @Override + public void paint(final RenderingContext buffer) { + + final Point2D onScreenPoint1 = coordinates[0].onScreenCoordinate; + final Point2D onScreenPoint2 = coordinates[1].onScreenCoordinate; + + final double xp = onScreenPoint2.x - onScreenPoint1.x; + final double yp = onScreenPoint2.y - onScreenPoint1.y; + + final double point1radius = (buffer.width * LINE_WIDTH_MULTIPLIER * width) + / coordinates[0].transformedCoordinate.z; + final double point2radius = (buffer.width * LINE_WIDTH_MULTIPLIER * width) + / coordinates[1].transformedCoordinate.z; + + if ((point1radius < MINIMUM_WIDTH_THRESHOLD) + || (point2radius < MINIMUM_WIDTH_THRESHOLD)) { + + double averageRadius = (point1radius + point2radius) / 2; + + if (averageRadius > 1) + averageRadius = 1; + + final int alpha = (int) (color.a * averageRadius); + if (alpha < 2) + return; + + if (Math.abs(xp) > Math.abs(yp)) + drawSinglePixelHorizontalLine(buffer, alpha); + else + drawSinglePixelVerticalLine(buffer, alpha); + return; + } + + final double lineLength = Math.sqrt((xp * xp) + (yp * yp)); + + final double yinc1 = (point1radius * xp) / lineLength; + final double yinc2 = (point2radius * xp) / lineLength; + + final double xdec1 = (point1radius * yp) / lineLength; + final double xdec2 = (point2radius * yp) / lineLength; + + final double p1x1 = onScreenPoint1.x - xdec1; + final double p1y1 = onScreenPoint1.y + yinc1; + + final double p1x2 = onScreenPoint1.x + xdec1; + final double p1y2 = onScreenPoint1.y - yinc1; + + final double p2x1 = onScreenPoint2.x - xdec2; + final double p2y1 = onScreenPoint2.y + yinc2; + + final double p2x2 = onScreenPoint2.x + xdec2; + final double p2y2 = onScreenPoint2.y - yinc2; + + li[0].setPoints(p1x1, p1y1, 1d, p2x1, p2y1, 1d); + li[1].setPoints(p1x2, p1y2, -1d, p2x2, p2y2, -1d); + + li[2].setPoints(p1x1, p1y1, 1d, p1x2, p1y2, -1d); + li[3].setPoints(p2x1, p2y1, 1d, p2x2, p2y2, -1d); + + double ymin = p1y1; + if (p1y2 < ymin) + ymin = p1y2; + if (p2y1 < ymin) + ymin = p2y1; + if (p2y2 < ymin) + ymin = p2y2; + if (ymin < 0) + ymin = 0; + + double ymax = p1y1; + if (p1y2 > ymax) + ymax = p1y2; + if (p2y1 > ymax) + ymax = p2y1; + if (p2y2 > ymax) + ymax = p2y2; + if (ymax >= buffer.height) + ymax = buffer.height - 1; + + for (int y = (int) ymin; y <= ymax; y++) { + final int li1 = getLineInterpolator(0, y); + if (li1 != -1) { + final int li2 = getLineInterpolator(li1 + 1, y); + if (li2 != -1) + drawHorizontalLine(li[li1], li[li2], y, buffer); + } + } + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/LineAppearance.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/LineAppearance.java new file mode 100644 index 0000000..d2d5b6a --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/LineAppearance.java @@ -0,0 +1,47 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; + +public class LineAppearance { + + private final double lineWidth; + + private Color color = new Color(100, 100, 255, 255); + + public LineAppearance() { + lineWidth = 1; + } + + public LineAppearance(final double lineWidth) { + this.lineWidth = lineWidth; + } + + public LineAppearance(final double lineWidth, final Color color) { + this.lineWidth = lineWidth; + this.color = color; + } + + public Line getLine(final Point3D point1, final Point3D point2) { + return new Line(point1, point2, color, lineWidth); + } + + public Line getLine(final Point3D point1, final Point3D point2, + final Color color) { + return new Line(point1, point2, color, lineWidth); + } + + public double getLineWidth() { + return lineWidth; + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/LineInterpolator.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/LineInterpolator.java new file mode 100644 index 0000000..65d67e4 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/LineInterpolator.java @@ -0,0 +1,65 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line; + +public class LineInterpolator { + + private double x1, y1, d1, x2, y2, d2; + + private double d; + private int height; + private int width; + private double dinc; + + public boolean containsY(final int y) { + + if (y1 < y2) { + if (y >= y1) + if (y <= y2) + return true; + } else if (y >= y2) + if (y <= y1) + return true; + + return false; + } + + public double getD() { + return d; + } + + public int getX(final int y) { + if (height == 0) + return (int) (x2 + x1) / 2; + + final int distanceFromY1 = y - (int) y1; + + d = d1 + ((dinc * distanceFromY1) / height); + + return (int) x1 + ((width * distanceFromY1) / height); + } + + public void setPoints(final double x1, final double y1, final double d1, + final double x2, final double y2, final double d2) { + + this.x1 = x1; + this.y1 = y1; + this.d1 = d1; + + this.x2 = x2; + this.y2 = y2; + this.d2 = d2; + + height = (int) y2 - (int) y1; + width = (int) x2 - (int) x1; + + dinc = d2 - d1; + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/solidpolygon/LineInterpolator.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/solidpolygon/LineInterpolator.java new file mode 100644 index 0000000..ffd417d --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/solidpolygon/LineInterpolator.java @@ -0,0 +1,81 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon; + +import eu.svjatoslav.sixth.e3d.geometry.Point2D; + +public class LineInterpolator implements Comparable { + + Point2D p1; + Point2D p2; + private int height; + private int width; + private int absoluteHeight; + + @Override + public boolean equals(final Object o) { + if (o == null) return false; + + return o instanceof LineInterpolator && compareTo((LineInterpolator) o) == 0; + } + + @Override + public int compareTo(final LineInterpolator o) { + if (absoluteHeight < o.absoluteHeight) + return 1; + if (absoluteHeight > o.absoluteHeight) + return -1; + + if (width < o.width) + return 1; + if (width > o.width) + return -1; + + return 0; + } + + @Override + public int hashCode() { + int result = width; + result = 31 * result + absoluteHeight; + return result; + } + + public boolean containsY(final int y) { + + if (p1.y <= p2.y) { + if (y >= p1.y) + if (y <= p2.y) + return true; + } else if (y >= p2.y) + if (y <= p1.y) + return true; + + return false; + } + + public int getX(final int y) { + + if (height == 0) + return (int) (p2.x + p1.x) / 2; + + return (int) (p1.x + ((width * (y - p1.y)) / height)); + } + + public void setPoints(final Point2D p1, final Point2D p2) { + this.p1 = p1; + this.p2 = p2; + height = (int) (p2.y - p1.y); + width = (int) (p2.x - p1.x); + + absoluteHeight = Math.abs(height); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/solidpolygon/SolidPolygon.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/solidpolygon/SolidPolygon.java new file mode 100644 index 0000000..d235606 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/solidpolygon/SolidPolygon.java @@ -0,0 +1,182 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon; + +import eu.svjatoslav.sixth.e3d.geometry.Point2D; +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.geometry.Polygon; +import eu.svjatoslav.sixth.e3d.gui.RenderingContext; +import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractCoordinateShape; + +public class SolidPolygon extends AbstractCoordinateShape { + + final static LineInterpolator polygonBoundary1 = new LineInterpolator(); + final static LineInterpolator polygonBoundary2 = new LineInterpolator(); + final static LineInterpolator polygonBoundary3 = new LineInterpolator(); + private Color color; + + public SolidPolygon(final Point3D point1, final Point3D point2, + final Point3D point3, final Color color) { + super(point1, point2, point3); + this.color = color; + } + + public static void drawHorizontalLine(final LineInterpolator line1, + final LineInterpolator line2, final int y, + final RenderingContext renderBuffer, final Color color) { + + int x1 = line1.getX(y); + int x2 = line2.getX(y); + + if (x1 > x2) { + final int tmp = x1; + x1 = x2; + x2 = tmp; + } + + if (x1 < 0) + x1 = 0; + + if (x2 >= renderBuffer.width) + x2 = renderBuffer.width - 1; + + final int width = x2 - x1; + + int offset = ((y * renderBuffer.width) + x1) * 4; + final byte[] offSreenBufferBytes = renderBuffer.bytes; + + final int polygonAlpha = color.a; + final int b = color.b; + final int g = color.g; + final int r = color.r; + + if (polygonAlpha == 255) + for (int i = 0; i < width; i++) { + offSreenBufferBytes[offset] = (byte) 255; + offset++; + offSreenBufferBytes[offset] = (byte) b; + offset++; + offSreenBufferBytes[offset] = (byte) g; + offset++; + offSreenBufferBytes[offset] = (byte) r; + offset++; + } + else { + final int backgroundAlpha = 255 - polygonAlpha; + + final int blueWithAlpha = b * polygonAlpha; + final int greenWithAlpha = g * polygonAlpha; + final int redWithAlpha = r * polygonAlpha; + + for (int i = 0; i < width; i++) { + offSreenBufferBytes[offset] = (byte) 255; + offset++; + offSreenBufferBytes[offset] = (byte) ((((offSreenBufferBytes[offset] & 0xff) * backgroundAlpha) + blueWithAlpha) / 256); + offset++; + offSreenBufferBytes[offset] = (byte) ((((offSreenBufferBytes[offset] & 0xff) * backgroundAlpha) + greenWithAlpha) / 256); + offset++; + offSreenBufferBytes[offset] = (byte) ((((offSreenBufferBytes[offset] & 0xff) * backgroundAlpha) + redWithAlpha) / 256); + offset++; + } + + } + + } + + public static void drawPolygon(final RenderingContext context, + final Point2D onScreenPoint1, final Point2D onScreenPoint2, + final Point2D onScreenPoint3, + final MouseInteractionController mouseInteractionController, + final Color color) { + + onScreenPoint1.roundToInteger(); + onScreenPoint2.roundToInteger(); + onScreenPoint3.roundToInteger(); + + if (mouseInteractionController != null) + if (context.mouseClick != null) + if (Polygon.pointWithinPolygon(context.mouseClick.coordinate, + onScreenPoint1, onScreenPoint2, onScreenPoint3)) + context.clickedItem = mouseInteractionController; + + if (color.isTransparent()) + return; + + // find top-most point + int yTop = (int) onScreenPoint1.y; + + if (onScreenPoint2.y < yTop) + yTop = (int) onScreenPoint2.y; + + if (onScreenPoint3.y < yTop) + yTop = (int) onScreenPoint3.y; + + if (yTop < 0) + yTop = 0; + + // find bottom-most point + int yBottom = (int) onScreenPoint1.y; + + if (onScreenPoint2.y > yBottom) + yBottom = (int) onScreenPoint2.y; + + if (onScreenPoint3.y > yBottom) + yBottom = (int) onScreenPoint3.y; + + if (yBottom >= context.height) + yBottom = context.height - 1; + + // paint + polygonBoundary1.setPoints(onScreenPoint1, onScreenPoint2); + polygonBoundary2.setPoints(onScreenPoint1, onScreenPoint3); + polygonBoundary3.setPoints(onScreenPoint2, onScreenPoint3); + + final LineInterpolator[] is = new LineInterpolator[3]; + is[0] = polygonBoundary1; + is[1] = polygonBoundary2; + is[2] = polygonBoundary3; + + java.util.Arrays.sort(is); + + for (int y = yTop; y < yBottom; y++) + if (is[0].containsY(y)) { + + if (is[1].containsY(y)) + drawHorizontalLine(is[0], is[1], y, context, color); + else if (is[2].containsY(y)) + drawHorizontalLine(is[0], is[2], y, context, color); + } else if (is[1].containsY(y)) + if (is[2].containsY(y)) + drawHorizontalLine(is[1], is[2], y, context, color); + } + + public Color getColor() { + return color; + } + + public void setColor(final Color color) { + this.color = color; + } + + @Override + public void paint(final RenderingContext renderBuffer) { + + final Point2D onScreenPoint1 = coordinates[0].onScreenCoordinate; + final Point2D onScreenPoint2 = coordinates[1].onScreenCoordinate; + final Point2D onScreenPoint3 = coordinates[2].onScreenCoordinate; + + drawPolygon(renderBuffer, onScreenPoint1, onScreenPoint2, + onScreenPoint3, mouseInteractionController, color); + + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/texturedpolygon/PolygonBorderInterpolator.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/texturedpolygon/PolygonBorderInterpolator.java new file mode 100644 index 0000000..a1dc9fc --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/texturedpolygon/PolygonBorderInterpolator.java @@ -0,0 +1,117 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.texturedpolygon; + +import eu.svjatoslav.sixth.e3d.geometry.Point2D; + +public class PolygonBorderInterpolator implements + Comparable { + + // on-screen coordinates + Point2D p1; + Point2D p2; + double distanceFromY1; + private int height; + private int width; + private int absoluteHeight; + private double twidth; + private double theight; + // texture coordinates + private Point2D tp1; + private Point2D tp2; + + + @Override + public boolean equals(final Object o) { + if (o == null) return false; + + return o instanceof PolygonBorderInterpolator && compareTo((PolygonBorderInterpolator) o) == 0; + } + + @Override + public int hashCode() { + int result = width; + result = 31 * result + absoluteHeight; + return result; + } + + @Override + public int compareTo(final PolygonBorderInterpolator o) { + if (absoluteHeight < o.absoluteHeight) + return 1; + if (absoluteHeight > o.absoluteHeight) + return -1; + + if (width < o.width) + return 1; + if (width > o.width) + return -1; + + return 0; + } + + public boolean containsY(final int y) { + + if (p1.y < p2.y) { + if (y >= p1.y) + if (y <= p2.y) + return true; + } else if (y >= p2.y) + if (y <= p1.y) + return true; + + return false; + } + + public double getTX() { + + if (height == 0) + return (tp2.x + tp1.x) / 2d; + + return tp1.x + ((twidth * distanceFromY1) / height); + } + + public double getTY() { + + if (height == 0) + return (tp2.y + tp1.y) / 2d; + + return tp1.y + ((theight * distanceFromY1) / height); + } + + public int getX() { + + if (height == 0) + return (int) ((p2.x + p1.x) / 2d); + + return (int) (p1.x + ((width * distanceFromY1) / height)); + } + + public void setCurrentY(final int y) { + distanceFromY1 = y - p1.y; + } + + public void setPoints(final Point2D p1, final Point2D p2, + final Point2D tp1, final Point2D tp2) { + + this.p1 = p1; + this.p2 = p2; + this.tp1 = tp1; + this.tp2 = tp2; + + height = (int) (p2.y - p1.y); + width = (int) (p2.x - p1.x); + absoluteHeight = Math.abs(height); + + twidth = tp2.x - tp1.x; + theight = tp2.y - tp1.y; + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/texturedpolygon/TexturedPolygon.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/texturedpolygon/TexturedPolygon.java new file mode 100644 index 0000000..ff900d8 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/texturedpolygon/TexturedPolygon.java @@ -0,0 +1,232 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.texturedpolygon; + +import eu.svjatoslav.sixth.e3d.geometry.Point2D; +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.geometry.Polygon; +import eu.svjatoslav.sixth.e3d.gui.RenderingContext; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractCoordinateShape; +import eu.svjatoslav.sixth.e3d.renderer.raster.slicer.PolygonCoordinate; +import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture; +import eu.svjatoslav.sixth.e3d.renderer.raster.texture.TextureBitmap; + +import java.awt.*; + +public class TexturedPolygon extends AbstractCoordinateShape { + + public final Texture texture; + final PolygonBorderInterpolator[] is = new PolygonBorderInterpolator[]{ + new PolygonBorderInterpolator(), new PolygonBorderInterpolator(), + new PolygonBorderInterpolator()}; + /** + * Polygon texture coordinates. + */ + public Point2D texturePoint1, texturePoint2, texturePoint3; + private boolean showBorders = false; + private double totalTextureDistance = -1; + + public TexturedPolygon(final Point3D p1, final Point3D p2, + final Point3D p3, final Point2D tp1, final Point2D tp2, + final Point2D tp3, final Texture texture) { + + super(p1, p2, p3); + + texturePoint1 = tp1; + texturePoint2 = tp2; + texturePoint3 = tp3; + + this.texture = texture; + } + + public TexturedPolygon(final PolygonCoordinate pc1, + final PolygonCoordinate pc2, final PolygonCoordinate pc3, + final Texture texture) { + + this(pc1.space, pc2.space, pc3.space, pc1.texture, pc2.texture, + pc3.texture, texture); + } + + private void computeTotalTextureDistance() { + // compute total texture distance + totalTextureDistance = texturePoint1.getDistanceTo(texturePoint2); + totalTextureDistance += texturePoint1.getDistanceTo(texturePoint3); + totalTextureDistance += texturePoint2.getDistanceTo(texturePoint3); + } + + private void drawHorizontalLine(final PolygonBorderInterpolator line1, + final PolygonBorderInterpolator line2, final int y, + final RenderingContext renderBuffer, + final TextureBitmap textureBitmap) { + + line1.setCurrentY(y); + line2.setCurrentY(y); + + int x1 = line1.getX(); + int x2 = line2.getX(); + + final double tx2, ty2; + final double tx1, ty1; + + if (x1 <= x2) { + + tx1 = line1.getTX() * textureBitmap.multiplicationFactor; + ty1 = line1.getTY() * textureBitmap.multiplicationFactor; + + tx2 = line2.getTX() * textureBitmap.multiplicationFactor; + ty2 = line2.getTY() * textureBitmap.multiplicationFactor; + + } else { + final int tmp = x1; + x1 = x2; + x2 = tmp; + + tx1 = line2.getTX() * textureBitmap.multiplicationFactor; + ty1 = line2.getTY() * textureBitmap.multiplicationFactor; + + tx2 = line1.getTX() * textureBitmap.multiplicationFactor; + ty2 = line1.getTY() * textureBitmap.multiplicationFactor; + } + + final double realWidth = x2 - x1; + final double realX1 = x1; + + if (x1 < 0) + x1 = 0; + + if (x2 >= renderBuffer.width) + x2 = renderBuffer.width - 1; + + int renderBufferOffset = ((y * renderBuffer.width) + x1) * 4; + final byte[] renderBufferBytes = renderBuffer.bytes; + + final double twidth = tx2 - tx1; + final double theight = ty2 - ty1; + + for (int x = x1; x < x2; x++) { + + final double distance = x - realX1; + + final double tx = tx1 + ((twidth * distance) / realWidth); + final double ty = ty1 + ((theight * distance) / realWidth); + + final int textureOffset = textureBitmap.getAddress((int) tx, + (int) ty); + + textureBitmap.drawPixel(textureOffset, renderBufferBytes, + renderBufferOffset); + + renderBufferOffset += 4; + } + + } + + @Override + public void paint(final RenderingContext renderBuffer) { + + final Point2D projectedPoint1 = coordinates[0].onScreenCoordinate; + final Point2D projectedPoint2 = coordinates[1].onScreenCoordinate; + final Point2D projectedPoint3 = coordinates[2].onScreenCoordinate; + + projectedPoint1.roundToInteger(); + projectedPoint2.roundToInteger(); + projectedPoint3.roundToInteger(); + + if (mouseInteractionController != null) + if (renderBuffer.mouseClick != null) + if (Polygon.pointWithinPolygon( + renderBuffer.mouseClick.coordinate, projectedPoint1, + projectedPoint2, projectedPoint3)) + renderBuffer.clickedItem = mouseInteractionController; + + // Show polygon boundaries (for debugging) + if (showBorders) + showBorders(renderBuffer); + + // find top-most point + int yTop = (int) projectedPoint1.y; + + if (projectedPoint2.y < yTop) + yTop = (int) projectedPoint2.y; + + if (projectedPoint3.y < yTop) + yTop = (int) projectedPoint3.y; + + if (yTop < 0) + yTop = 0; + + // find bottom-most point + int yBottom = (int) projectedPoint1.y; + + if (projectedPoint2.y > yBottom) + yBottom = (int) projectedPoint2.y; + + if (projectedPoint3.y > yBottom) + yBottom = (int) projectedPoint3.y; + + if (yBottom >= renderBuffer.height) + yBottom = renderBuffer.height - 1; + + // paint + double totalVisibleDistance = projectedPoint1 + .getDistanceTo(projectedPoint2); + totalVisibleDistance += projectedPoint1.getDistanceTo(projectedPoint3); + totalVisibleDistance += projectedPoint2.getDistanceTo(projectedPoint3); + + if (totalTextureDistance == -1) + computeTotalTextureDistance(); + final double scaleFactor = (totalVisibleDistance / totalTextureDistance) * 1.2d; + + final TextureBitmap zoomedBitmap = texture.getZoomedBitmap(scaleFactor); + + is[0].setPoints(projectedPoint1, projectedPoint2, texturePoint1, + texturePoint2); + is[1].setPoints(projectedPoint1, projectedPoint3, texturePoint1, + texturePoint3); + is[2].setPoints(projectedPoint2, projectedPoint3, texturePoint2, + texturePoint3); + + java.util.Arrays.sort(is); + + for (int y = yTop; y < yBottom; y++) + if (is[0].containsY(y)) { + + if (is[1].containsY(y)) + drawHorizontalLine(is[0], is[1], y, renderBuffer, + zoomedBitmap); + else if (is[2].containsY(y)) + drawHorizontalLine(is[0], is[2], y, renderBuffer, + zoomedBitmap); + } else if (is[1].containsY(y)) + if (is[2].containsY(y)) + drawHorizontalLine(is[1], is[2], y, renderBuffer, + zoomedBitmap); + + } + + private void showBorders(final RenderingContext renderBuffer) { + + final Point2D projectedPoint1 = coordinates[0].onScreenCoordinate; + final Point2D projectedPoint2 = coordinates[1].onScreenCoordinate; + final Point2D projectedPoint3 = coordinates[2].onScreenCoordinate; + + renderBuffer.graphics.setColor(Color.YELLOW); + renderBuffer.graphics.drawLine((int) projectedPoint1.x, + (int) projectedPoint1.y, (int) projectedPoint2.x, + (int) projectedPoint2.y); + renderBuffer.graphics.drawLine((int) projectedPoint3.x, + (int) projectedPoint3.y, (int) projectedPoint2.x, + (int) projectedPoint2.y); + renderBuffer.graphics.drawLine((int) projectedPoint1.x, + (int) projectedPoint1.y, (int) projectedPoint3.x, + (int) projectedPoint3.y); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/ForwardOrientedTextBlock.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/ForwardOrientedTextBlock.java new file mode 100644 index 0000000..49cd9e8 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/ForwardOrientedTextBlock.java @@ -0,0 +1,48 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.ForwardOrientedTexture; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas.TextCanvas; +import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture; + +public class ForwardOrientedTextBlock extends ForwardOrientedTexture { + + public ForwardOrientedTextBlock(final Point3D point, final double scale, + final int maxUpscaleFactor, final String text, + final eu.svjatoslav.sixth.e3d.renderer.raster.Color textColor) { + super(point, scale, getTexture(text, maxUpscaleFactor, textColor)); + + } + + public static Texture getTexture(final String text, + final int maxUpscaleFactor, + final eu.svjatoslav.sixth.e3d.renderer.raster.Color textColor) { + + final Texture texture = new Texture(text.length() + * TextCanvas.FONT_CHAR_WIDTH, TextCanvas.FONT_CHAR_HEIGHT, + maxUpscaleFactor); + + // texture.graphics.setColor(Color.BLUE); + // texture.graphics.fillRect(0, 0, texture.primaryBitmap.width, + // texture.primaryBitmap.width); + + texture.graphics.setFont(TextCanvas.FONT); + texture.graphics.setColor(textColor.toAwtColor()); + + for (int c = 0; c < text.length(); c++) + texture.graphics.drawChars(new char[]{text.charAt(c),}, 0, 1, + (c * TextCanvas.FONT_CHAR_WIDTH) - 0, + (0 * TextCanvas.FONT_CHAR_HEIGHT) + 11); + + return texture; + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/Galaxy.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/Galaxy.java new file mode 100755 index 0000000..231aeee --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/Galaxy.java @@ -0,0 +1,98 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.geometry.Transform; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.ForwardOrientedTexture; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.GlowingPoint; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape; +import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture; + +import java.util.Random; +import java.util.Vector; + +public class Galaxy extends AbstractCompositeShape { + + private Vector textures; + + public Galaxy(final int size, final int tailCount, final int starsCount, + Transform transform) { + + super(transform); + + Point3D location = new Point3D(); + + createReusableTextures(); + + final double angle1 = Math.random() * 10; + final double angle2 = Math.random() * 10; + + final double angleSin1 = Math.sin(angle1); + final double angleCos1 = Math.cos(angle1); + final double angleSin2 = Math.sin(angle2); + final double angleCos2 = Math.cos(angle2); + + Random random = new Random(); + + for (int i = 1; i < starsCount; i++) { + final double b = Math.random() * 10; + + final double s = (b * b) / 30; + + final double v1 = (Math.random() * (11.5 - b)) / 3; + final double v1p = v1 / 2; + + final double ane = ((Math.random() * (s / 2)) / tailCount) * 2; + final double sba = ((2 * Math.PI) / tailCount) + * random.nextInt(tailCount); + + final double x = (((Math.sin((b - sba) + ane) * s) + (Math.random() * v1)) - v1p) + * size; + final double z = (((Math.cos((b - sba) + ane) * s) + (Math.random() * v1)) - v1p) + * size; + final double y = ((Math.random() * v1) - v1p) * size; + + final double x1 = (x * angleCos1) + (z * angleSin1); + final double z1 = (z * angleCos1) - (x * angleSin1); + + final double y1 = (y * angleCos2) + (z1 * angleSin2); + final double z2 = (z1 * angleCos2) - (y * angleSin2); + + final Point3D point3d = new Point3D(x1 + location.x, y1 + + location.y, z2 + location.z); + + addStar(point3d); + } + } + + public void addStar(final Point3D point3d) { + + final Texture texture = textures.get((int) (Math.random() * textures + .size())); + + addShape(new ForwardOrientedTexture(point3d, 10, texture)); + } + + public void createReusableTextures() { + textures = new Vector<>(); + + for (int i = 0; i < 30; i++) { + final GlowingPoint glowingPoint = new GlowingPoint(null, 1, + new Color(Math.random() + 0.5, Math.random() + 0.5, + Math.random() + 0.5, 255)); + + textures.add(glowingPoint.texture); + + } + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/Graph.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/Graph.java new file mode 100644 index 0000000..38dbf27 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/Graph.java @@ -0,0 +1,132 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite; + +import eu.svjatoslav.sixth.e3d.geometry.Point2D; +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.geometry.Transform; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.Line; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas.TextCanvas; + +import java.io.IOException; +import java.util.List; + +public class Graph extends AbstractCompositeShape { + + private final double width; + private final double yMin; + private final double yMax; + private final double horizontalStep; + private final double verticalStep; + private final Color gridColor; + private final double lineWidth; + private final Color plotColor; + + public Graph(final double scale, final List 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 data) { + Point3D previousPoint = null; + for (final Point2D point : data) { + + final Point3D p3d = new Point3D(point.x, point.y, 0).scaleUp(scale); + + if (previousPoint != null) { + + final Line line = new Line(previousPoint, p3d, plotColor, + 0.4 * scale); + + addShape(line); + } + + previousPoint = p3d; + } + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/LightSourceMarker.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/LightSourceMarker.java new file mode 100755 index 0000000..ef1f4f3 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/LightSourceMarker.java @@ -0,0 +1,40 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape; + +public class LightSourceMarker extends AbstractCompositeShape { + + public LightSourceMarker(final Point3D location, final Color color) { + super(location); + final LineAppearance appearance = new LineAppearance(10); + + final int markerSize = 10; + + addShape(appearance.getLine(new Point3D(0, -markerSize, 0), + new Point3D(markerSize, markerSize, -markerSize), color)); + addShape(appearance.getLine(new Point3D(0, -markerSize, 0), + new Point3D(-markerSize, markerSize, -markerSize), color)); + addShape(appearance.getLine(new Point3D(0, -markerSize, 0), + new Point3D(0, markerSize, markerSize), color)); + + addShape(appearance.getLine(new Point3D(-markerSize, markerSize, + -markerSize), new Point3D(markerSize, markerSize, -markerSize), + color)); + addShape(appearance.getLine(new Point3D(-markerSize, markerSize, + -markerSize), new Point3D(0, markerSize, markerSize), color)); + addShape(appearance.getLine(new Point3D(0, markerSize, markerSize), + new Point3D(markerSize, markerSize, -markerSize), color)); + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/TexturedRectangle.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/TexturedRectangle.java new file mode 100644 index 0000000..8612f0b --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/TexturedRectangle.java @@ -0,0 +1,93 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite; + +import eu.svjatoslav.sixth.e3d.geometry.Point2D; +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.geometry.Transform; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.texturedpolygon.TexturedPolygon; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape; +import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture; + +public class TexturedRectangle extends AbstractCompositeShape { + + public Point3D topLeft; + public Point3D topRight; + public Point3D bottomRight; + public Point3D bottomLeft; + public Point2D textureTopLeft; + public Point2D textureTopRight; + public Point2D textureBottomRight; + public Point2D textureBottomLeft; + private Texture texture; + + public TexturedRectangle(final Transform transform) { + super(transform); + } + + public TexturedRectangle(final Transform transform, final int width, + final int height, final int maxTextureUpscale) { + this(transform, width, height, width, height, maxTextureUpscale); + } + + public TexturedRectangle(final Transform transform, final int width, + final int height, final int textureWidth, final int textureHeight, + final int maxTextureUpscale) { + + super(transform); + + initialize(width, height, textureWidth, textureHeight, + maxTextureUpscale); + } + + public Texture getTexture() { + return texture; + } + + public void initialize(final double width, final double height, + final int textureWidth, final int textureHeight, + final int maxTextureUpscale) { + + topLeft = new Point3D(-width / 2, -height / 2, 0); + topRight = new Point3D(width / 2, -height / 2, 0); + bottomRight = new Point3D(width / 2, height / 2, 0); + bottomLeft = new Point3D(-width / 2, height / 2, 0); + + texture = new Texture(textureWidth, textureHeight, maxTextureUpscale); + + textureTopRight = new Point2D(textureWidth, 0); + textureTopLeft = new Point2D(0, 0); + textureBottomRight = new Point2D(textureWidth, textureHeight); + textureBottomLeft = new Point2D(0, textureHeight); + + final TexturedPolygon texturedPolygon1 = new TexturedPolygon(topLeft, + topRight, bottomRight, textureTopLeft, textureTopRight, + textureBottomRight, texture); + + texturedPolygon1 + .setMouseInteractionController(mouseInteractionController); + + final TexturedPolygon texturedPolygon2 = new TexturedPolygon(topLeft, + bottomLeft, bottomRight, textureTopLeft, textureBottomLeft, + textureBottomRight, texture); + + texturedPolygon2 + .setMouseInteractionController(mouseInteractionController); + + addShape(texturedPolygon1); + addShape(texturedPolygon2); + } + + public void initialize(final int width, final int height, + final int maxTextureUpscale) { + initialize(width, height, width, height, maxTextureUpscale); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/base/AbstractCompositeShape.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/base/AbstractCompositeShape.java new file mode 100644 index 0000000..dd8b784 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/base/AbstractCompositeShape.java @@ -0,0 +1,216 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.geometry.Transform; +import eu.svjatoslav.sixth.e3d.geometry.TransformPipe; +import eu.svjatoslav.sixth.e3d.gui.RenderingContext; +import eu.svjatoslav.sixth.e3d.gui.UserRelativityTracker; +import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.RenderAggregator; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractShape; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.Line; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.texturedpolygon.TexturedPolygon; +import eu.svjatoslav.sixth.e3d.renderer.raster.slicer.Slicer; + +import java.util.ArrayList; +import java.util.List; + +/** + * In order to get perspective correct textures, large textured polygons are + * sliced into smaller ones. + */ + +public class AbstractCompositeShape extends AbstractShape { + private final List originalSubShapes = new ArrayList<>(); + private final UserRelativityTracker relativityTracker; + double currentSliceFactor = 5; + private List 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 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 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 result = new ArrayList<>(); + + final Slicer slicer = new Slicer(currentSliceFactor); + originalSubShapes.stream().filter(subShape -> subShape.isVisible()).forEach(subShape -> { + if (subShape.getShape() instanceof TexturedPolygon) + slicer.slice((TexturedPolygon) subShape.getShape()); + else + result.add(subShape.getShape()); + }); + + result.addAll(slicer.getResult()); + + renderedSubShapes = result; + } + + @Override + public void transform(final TransformPipe transformPipe, + final RenderAggregator aggregator, final RenderingContext context) { + + // add current composite shape transform to the end of the transform + // pipeline + transformPipe.addTransform(transform); + + relativityTracker.analyze(transformPipe, context); + + beforeTransformHook(transformPipe, context); + + // hack, to get somewhat perspective correct textures + resliceIfNeeded(); + + // transform rendered subshapes + for (final AbstractShape shape : renderedSubShapes) + shape.transform(transformPipe, aggregator, context); + + transformPipe.dropTransform(); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/base/SubShape.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/base/SubShape.java new file mode 100644 index 0000000..4b1ded6 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/base/SubShape.java @@ -0,0 +1,50 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base; + +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractShape; + +public class SubShape { + + private final AbstractShape shape; + private boolean visible = true; + private String groupIdentifier; + + public SubShape(AbstractShape shape) { + this.shape = shape; + } + + public boolean isUngrouped() { + return groupIdentifier == null; + } + + public boolean matchesGroup(final String groupIdentifier) { + if (this.groupIdentifier == null) + return groupIdentifier == null; + + return this.groupIdentifier.equals(groupIdentifier); + } + + public void setGroup(final String groupIdentifier) { + this.groupIdentifier = groupIdentifier; + } + + public AbstractShape getShape() { + return shape; + } + + public boolean isVisible() { + return visible; + } + + public void setVisible(boolean visible) { + this.visible = visible; + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/solid/SolidPolygonCube.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/solid/SolidPolygonCube.java new file mode 100755 index 0000000..a768008 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/solid/SolidPolygonCube.java @@ -0,0 +1,23 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; + +public class SolidPolygonCube extends SolidPolygonRectangularBox { + + public SolidPolygonCube(final Point3D center, final double size, + final Color color) { + super(new Point3D(center.x - size, center.y - size, center.z - size), + new Point3D(center.x + size, center.y + size, center.z + size), + color); + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/solid/SolidPolygonRectangularBox.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/solid/SolidPolygonRectangularBox.java new file mode 100755 index 0000000..1f149f4 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/solid/SolidPolygonRectangularBox.java @@ -0,0 +1,66 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2015, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape; + +public class SolidPolygonRectangularBox extends AbstractCompositeShape { + + /** + * Draws solid rectangular box between using center and size. + * + * @param center center of the box + * @param size size of the box + * @param color box color + */ + public SolidPolygonRectangularBox(final Point3D center, final double size, final Color color) { + this(// + new Point3D(center.x - (size / 2), center.y - (size / 2), center.z - (size / 2)), // + new Point3D(center.x + (size / 2), center.y + (size / 2), center.z + (size / 2)), // + color); + } + + /** + * Draws solid rectangular box between 2 given points in 3D space. + */ + public SolidPolygonRectangularBox(final Point3D p1, final Point3D p7, final Color color) { + super(); + + final Point3D p2 = new Point3D(p7.x, p1.y, p1.z); + final Point3D p3 = new Point3D(p7.x, p1.y, p7.z); + final Point3D p4 = new Point3D(p1.x, p1.y, p7.z); + + final Point3D p5 = new Point3D(p1.x, p7.y, p1.z); + final Point3D p6 = new Point3D(p7.x, p7.y, p1.z); + final Point3D p8 = new Point3D(p1.x, p7.y, p7.z); + + addShape(new SolidPolygon(p1, p2, p3, color)); + addShape(new SolidPolygon(p1, p4, p3, color)); + + addShape(new SolidPolygon(p5, p6, p7, color)); + addShape(new SolidPolygon(p5, p8, p7, color)); + + addShape(new SolidPolygon(p4, p3, p8, color)); + addShape(new SolidPolygon(p3, p7, p8, color)); + + addShape(new SolidPolygon(p1, p5, p4, color)); + addShape(new SolidPolygon(p4, p5, p8, color)); + + addShape(new SolidPolygon(p1, p2, p5, color)); + addShape(new SolidPolygon(p2, p6, p5, color)); + + addShape(new SolidPolygon(p2, p6, p3, color)); + addShape(new SolidPolygon(p6, p7, p3, color)); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/textcanvas/CanvasCharacter.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/textcanvas/CanvasCharacter.java new file mode 100644 index 0000000..863b362 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/textcanvas/CanvasCharacter.java @@ -0,0 +1,144 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas; + +import eu.svjatoslav.sixth.e3d.geometry.Point2D; +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.gui.RenderingContext; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractCoordinateShape; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon; + +import java.awt.*; + +public class CanvasCharacter extends AbstractCoordinateShape { + + public static final double SIZE_MULTIPLIER = 0.005; + private static final int MAX_FONT_SIZE = 500; + private static final Font[] fonts = new Font[MAX_FONT_SIZE]; + private String value; + + private eu.svjatoslav.sixth.e3d.renderer.raster.Color foregroundColor; + private Color foregroundAwtColor; + + private eu.svjatoslav.sixth.e3d.renderer.raster.Color backgroundColor; + + public CanvasCharacter(final Point3D point, final char character, + final eu.svjatoslav.sixth.e3d.renderer.raster.Color foregroundColor, + final eu.svjatoslav.sixth.e3d.renderer.raster.Color backgroundColor) { + + super(5); + coordinates[0].coordinate = point; + + value = String.valueOf(character); + + this.foregroundColor = foregroundColor; + foregroundAwtColor = foregroundColor.toAwtColor(); + + this.backgroundColor = backgroundColor; + + // set corner coordinates (for drawing background) + { + final double widthHalf = TextCanvas.FONT_CHAR_WIDTH / 2d; + final double heightHalf = TextCanvas.FONT_CHAR_HEIGHT / 2d; + + // upper left + coordinates[1].coordinate = point.clone().translateX(-widthHalf) + .translateY(-heightHalf); + + // upper right + coordinates[2].coordinate = point.clone().translateX(widthHalf) + .translateY(-heightHalf); + + // lower right + coordinates[3].coordinate = point.clone().translateX(widthHalf) + .translateY(heightHalf); + + // lower left + coordinates[4].coordinate = point.clone().translateX(-widthHalf) + .translateY(heightHalf); + + } + } + + public static Font getFont(final int size) { + if (fonts[size] != null) + return fonts[size]; + + final Font font = new Font("Courier", Font.BOLD, size); + fonts[size] = font; + return font; + } + + public eu.svjatoslav.sixth.e3d.renderer.raster.Color getBackgroundColor() { + return backgroundColor; + } + + public void setBackgroundColor( + final eu.svjatoslav.sixth.e3d.renderer.raster.Color backgroundColor) { + this.backgroundColor = backgroundColor; + } + + public eu.svjatoslav.sixth.e3d.renderer.raster.Color getForegroundColor() { + return foregroundColor; + } + + public void setForegroundColor( + final eu.svjatoslav.sixth.e3d.renderer.raster.Color foregroundColor) { + this.foregroundColor = foregroundColor; + foregroundAwtColor = foregroundColor.toAwtColor(); + } + + @Override + public void paint(final RenderingContext renderingContext) { + + SolidPolygon.drawPolygon(renderingContext, + coordinates[1].onScreenCoordinate, + coordinates[2].onScreenCoordinate, + coordinates[3].onScreenCoordinate, mouseInteractionController, + backgroundColor); + + SolidPolygon.drawPolygon(renderingContext, + coordinates[1].onScreenCoordinate, + coordinates[3].onScreenCoordinate, + coordinates[4].onScreenCoordinate, mouseInteractionController, + backgroundColor); + + final int size = (int) ((renderingContext.width * 4.5) / onScreenZ); + + // do not render too large characters + if (size >= MAX_FONT_SIZE) + return; + + final Point2D onScreenLocation = coordinates[0].onScreenCoordinate; + + // screen borders check + if (onScreenLocation.x < 0) + return; + if (onScreenLocation.y < 0) + return; + + if (onScreenLocation.x > renderingContext.width) + return; + if (onScreenLocation.y > renderingContext.height) + return; + + renderingContext.graphics.setFont(getFont(size)); + renderingContext.graphics.setColor(foregroundAwtColor); + renderingContext.graphics.drawString(value, (int) onScreenLocation.x + - (int) (size / 3.2), (int) onScreenLocation.y + + (int) (size / 2.5)); + + } + + public void setValue(final char value) { + this.value = String.valueOf(value); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/textcanvas/RenderMode.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/textcanvas/RenderMode.java new file mode 100644 index 0000000..ecb07e8 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/textcanvas/RenderMode.java @@ -0,0 +1,14 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas; + +public enum RenderMode { + TEXTURE, CHARACTERS +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/textcanvas/TextCanvas.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/textcanvas/TextCanvas.java new file mode 100644 index 0000000..5c594f2 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/textcanvas/TextCanvas.java @@ -0,0 +1,284 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.geometry.Transform; +import eu.svjatoslav.sixth.e3d.geometry.TransformPipe; +import eu.svjatoslav.sixth.e3d.gui.RenderingContext; +import eu.svjatoslav.sixth.e3d.gui.TextPointer; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.TexturedRectangle; + +import java.awt.*; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; + +public class TextCanvas extends TexturedRectangle { + + public static final int FONT_CHAR_WIDTH = 8; + public static final int FONT_CHAR_HEIGHT = 16; + public static final Font FONT = CanvasCharacter.getFont(15); + private static final String GROUP_TEXTURE = "texture"; + private static final String GROUP_CHARACTERS = "characters"; + private final TextPointer size; + private final TextPointer cursorLocation = new TextPointer(); + CanvasCharacter lines[][]; + private RenderMode renderMode = null; + private Color backgroundColor = Color.BLACK; + + private Color foregroundColor = Color.WHITE; + + public TextCanvas(final Transform location, final String text, + final Color foregroundColor, final Color backgroundColor) + throws IOException { + this(location, getTextDimensions(text), foregroundColor, + backgroundColor); + setText(text); + } + + public TextCanvas(final Transform location, final TextPointer dimensions, + final Color foregroundColor, final Color backgroundColor) { + super(location); + getRelativityTracker().enableOrientationTracking(); + + size = dimensions; + final int columns = dimensions.column; + final int rows = dimensions.row; + + this.backgroundColor = backgroundColor; + this.foregroundColor = foregroundColor; + + // initialize underlying textured rectangle + initialize(columns * FONT_CHAR_WIDTH, rows * FONT_CHAR_HEIGHT, 0); + + getTexture().primaryBitmap.fillColor(backgroundColor); + getTexture().resetResampledBitmapCache(); + + setGroupForUngrouped(GROUP_TEXTURE); + + lines = new CanvasCharacter[rows][]; + for (int row = 0; row < rows; row++) { + lines[row] = new CanvasCharacter[columns]; + + for (int column = 0; column < columns; column++) { + final Point3D characterCoordinate = getCharLocation(row, column); + + final CanvasCharacter character = new CanvasCharacter( + characterCoordinate, ' ', foregroundColor, + backgroundColor); + addShape(character); + lines[row][column] = character; + } + + } + + setGroupForUngrouped(GROUP_CHARACTERS); + + setRenderMode(RenderMode.TEXTURE); + } + + public static TextPointer getTextDimensions(final String text) + throws IOException { + + final BufferedReader reader = new BufferedReader(new StringReader(text)); + + int rows = 0; + int columns = 0; + + while (true) { + final String line = reader.readLine(); + + if (line == null) + return new TextPointer(rows, columns); + + rows++; + columns = Math.max(columns, line.length()); + } + } + + @Override + public void beforeTransformHook(final TransformPipe transformPipe, + final RenderingContext context) { + + final double textRelativeSize = context.width + / getRelativityTracker().getDistanceToUser(); + + if (textRelativeSize < 2d) { + if (renderMode == RenderMode.CHARACTERS) + setRenderMode(RenderMode.TEXTURE); + return; + } + + final double piHalf = Math.PI / 2; + + final double deviation = Math.abs(getRelativityTracker().getAngleXZ() + + piHalf) + + Math.abs(getRelativityTracker().getAngleYZ() + piHalf); + + final double maxDeviation = 0.5; + + if (deviation > maxDeviation) { + if (renderMode == RenderMode.CHARACTERS) + setRenderMode(RenderMode.TEXTURE); + } else if (renderMode == RenderMode.TEXTURE) + setRenderMode(RenderMode.CHARACTERS); + } + + public void cls() { + for (final CanvasCharacter[] line : lines) + for (final CanvasCharacter character : line) { + character.setValue(' '); + character.setBackgroundColor(backgroundColor); + character.setForegroundColor(foregroundColor); + } + + // set background color + getTexture().primaryBitmap.fillColor(backgroundColor); + getTexture().resetResampledBitmapCache(); + } + + private void drawCharToTexture(final int row, final int column, + final char character, final Color background, final Color foreground) { + final Graphics2D graphics = getTexture().graphics; + + getTexture().primaryBitmap.drawRectangle(column * FONT_CHAR_WIDTH, row + * FONT_CHAR_HEIGHT, (column * FONT_CHAR_WIDTH) + + FONT_CHAR_WIDTH, (row * FONT_CHAR_HEIGHT) + FONT_CHAR_HEIGHT, + backgroundColor); + + graphics.setFont(FONT); + graphics.setColor(foreground.toAwtColor()); + graphics.drawChars(new char[]{character,}, 0, 1, + (column * FONT_CHAR_WIDTH) - 0, (row * FONT_CHAR_HEIGHT) + 13); + + getTexture().resetResampledBitmapCache(); + } + + public Point3D getCharLocation(final int row, final int column) { + final Point3D coordinate = topLeft.clone(); + + coordinate.translateY((row * FONT_CHAR_HEIGHT) + + ((FONT_CHAR_HEIGHT / 2) - 3)); + + coordinate.translateX((column * FONT_CHAR_WIDTH) + + (FONT_CHAR_WIDTH / 2)); + + return coordinate; + } + + public TextPointer getSize() { + return size; + } + + public void locate(final int row, final int column) { + cursorLocation.row = row; + cursorLocation.column = column; + } + + public void print(final String text) { + for (final char c : text.toCharArray()) + putChar(c); + } + + public void putChar(final char character) { + putChar(cursorLocation, character); + + cursorLocation.column++; + if (cursorLocation.column >= size.column) { + cursorLocation.column = 0; + cursorLocation.row++; + } + } + + public void putChar(final int row, final int column, final char character) { + if ((row >= lines.length) || (row < 0)) + return; + + final CanvasCharacter[] line = lines[row]; + + if ((column >= line.length) || (column < 0)) + return; + + final CanvasCharacter canvasCharacter = line[column]; + canvasCharacter.setValue(character); + canvasCharacter.setBackgroundColor(backgroundColor); + canvasCharacter.setForegroundColor(foregroundColor); + drawCharToTexture(row, column, character, backgroundColor, + foregroundColor); + } + + public void putChar(final TextPointer location, final char character) { + putChar(location.row, location.column, character); + } + + public void setBackgroundColor( + final eu.svjatoslav.sixth.e3d.renderer.raster.Color backgroundColor) { + this.backgroundColor = backgroundColor; + } + + public void setForegroundColor( + final eu.svjatoslav.sixth.e3d.renderer.raster.Color foregroundColor) { + this.foregroundColor = foregroundColor; + } + + private void setRenderMode(final RenderMode mode) { + if (mode == renderMode) + return; + + switch (mode) { + case CHARACTERS: + hideGroup(GROUP_TEXTURE); + showGroup(GROUP_CHARACTERS); + break; + case TEXTURE: + hideGroup(GROUP_CHARACTERS); + showGroup(GROUP_TEXTURE); + break; + } + + renderMode = mode; + } + + public void setText(final String text) throws IOException { + final BufferedReader reader = new BufferedReader(new StringReader(text)); + + int row = 0; + + while (true) { + final String line = reader.readLine(); + + if (line == null) + return; + + int column = 0; + for (final char c : line.toCharArray()) { + putChar(row, column, c); + column++; + } + row++; + } + } + + public void setTextColor(final Color color) { + for (final CanvasCharacter[] line : lines) + for (final CanvasCharacter character : line) + character.setForegroundColor(color); + } + + // @Override + // public void transform(final TransformPipe transformPipe, + // final RenderAggregator aggregator, final RenderingContext buffer) { + // + // super.transform(transformPipe, aggregator, buffer); + // + // } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/Grid2D.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/Grid2D.java new file mode 100644 index 0000000..3e685d1 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/Grid2D.java @@ -0,0 +1,50 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.geometry.Rectangle; +import eu.svjatoslav.sixth.e3d.geometry.Transform; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape; + +public class Grid2D extends AbstractCompositeShape { + + public Grid2D(final Transform transform, final Rectangle rectangle, + final int divisionsX, final int divisionsY, + final LineAppearance appearance) { + + super(transform); + + final double stepY = rectangle.getHeight() / divisionsY; + final double stepX = rectangle.getWidth() / divisionsX; + + for (int yslice = 0; yslice <= divisionsY; yslice++) { + final double y = (yslice * stepY) + rectangle.getLowerY(); + + for (int xslice = 0; xslice <= divisionsX; xslice++) { + final double x = (xslice * stepX) + rectangle.getLowerX(); + + final Point3D p1 = new Point3D(x, y, 0); + final Point3D p2 = new Point3D(x + stepX, y, 0); + final Point3D p3 = new Point3D(x, y + stepY, 0); + + if (xslice < divisionsX) + addShape(appearance.getLine(p1, p2)); + + if (yslice < divisionsY) + addShape(appearance.getLine(p1, p3)); + } + + } + + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/Grid3D.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/Grid3D.java new file mode 100755 index 0000000..c84141b --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/Grid3D.java @@ -0,0 +1,67 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape; + +public class Grid3D extends AbstractCompositeShape { + + public Grid3D(final Point3D p1t, final Point3D p2t, final double step, + final LineAppearance appearance) { + + super(); + + final Point3D p1 = new Point3D(p1t); + final Point3D p2 = new Point3D(p2t); + + if (p1.x > p2.x) { + final double tmp = p1.x; + p1.x = p2.x; + p2.x = tmp; + } + + if (p1.y > p2.y) { + final double tmp = p1.y; + p1.y = p2.y; + p2.y = tmp; + } + + if (p1.z > p2.z) { + final double tmp = p1.z; + p1.z = p2.z; + p2.z = tmp; + } + + for (double x = p1.x; x <= p2.x; x += step) + for (double y = p1.y; y <= p2.y; y += step) + for (double z = p1.z; z <= p2.z; z += step) { + + final Point3D p3 = new Point3D(x, y, z); + + if ((x + step) <= p2.x) { + final Point3D point3d2 = new Point3D(x + step, y, z); + addShape(appearance.getLine(p3, point3d2)); + } + + if ((y + step) <= p2.y) { + final Point3D point3d3 = new Point3D(x, y + step, z); + addShape(appearance.getLine(p3, point3d3)); + } + + if ((z + step) <= p2.z) { + final Point3D point3d4 = new Point3D(x, y, z + step); + addShape(appearance.getLine(p3, point3d4)); + } + + } + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeBox.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeBox.java new file mode 100755 index 0000000..fa8f8c8 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeBox.java @@ -0,0 +1,57 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe; + +import eu.svjatoslav.sixth.e3d.geometry.Box; +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape; + +public class WireframeBox extends AbstractCompositeShape { + + public WireframeBox(final Box box, + final LineAppearance appearance) { + + this(box.p1, box.p2, appearance); + } + + public WireframeBox(final Point3D p1, final Point3D p2, + final LineAppearance appearance) { + super(); + + addShape(appearance.getLine(new Point3D(p1.x, p1.y, p1.z), new Point3D( + p2.x, p1.y, p1.z))); + addShape(appearance.getLine(new Point3D(p1.x, p2.y, p1.z), new Point3D( + p2.x, p2.y, p1.z))); + addShape(appearance.getLine(new Point3D(p1.x, p1.y, p1.z), new Point3D( + p1.x, p2.y, p1.z))); + addShape(appearance.getLine(new Point3D(p2.x, p1.y, p1.z), new Point3D( + p2.x, p2.y, p1.z))); + + addShape(appearance.getLine(new Point3D(p1.x, p1.y, p2.z), new Point3D( + p2.x, p1.y, p2.z))); + addShape(appearance.getLine(new Point3D(p1.x, p2.y, p2.z), new Point3D( + p2.x, p2.y, p2.z))); + addShape(appearance.getLine(new Point3D(p1.x, p1.y, p2.z), new Point3D( + p1.x, p2.y, p2.z))); + addShape(appearance.getLine(new Point3D(p2.x, p1.y, p2.z), new Point3D( + p2.x, p2.y, p2.z))); + + addShape(appearance.getLine(new Point3D(p1.x, p1.y, p1.z), new Point3D( + p1.x, p1.y, p2.z))); + addShape(appearance.getLine(new Point3D(p1.x, p2.y, p1.z), new Point3D( + p1.x, p2.y, p2.z))); + addShape(appearance.getLine(new Point3D(p2.x, p1.y, p1.z), new Point3D( + p2.x, p1.y, p2.z))); + addShape(appearance.getLine(new Point3D(p2.x, p2.y, p1.z), new Point3D( + p2.x, p2.y, p2.z))); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeCube.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeCube.java new file mode 100755 index 0000000..3881110 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeCube.java @@ -0,0 +1,23 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance; + +public class WireframeCube extends WireframeBox { + + public WireframeCube(final Point3D center, final double size, + final LineAppearance appearance) { + super(new Point3D(center.x - size, center.y - size, center.z - size), + new Point3D(center.x + size, center.y + size, center.z + size), + appearance); + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeDrawing.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeDrawing.java new file mode 100755 index 0000000..0ed7a05 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeDrawing.java @@ -0,0 +1,36 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.Line; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape; + +public class WireframeDrawing extends AbstractCompositeShape { + + final private LineAppearance lineAppearance; + Point3D currentPoint; + + public WireframeDrawing(final LineAppearance lineAppearance) { + super(); + this.lineAppearance = lineAppearance; + } + + public void addPoint(final Point3D point3d) { + if (currentPoint != null) { + final Line line = lineAppearance.getLine(currentPoint, point3d); + addShape(line); + } + + currentPoint = new Point3D(point3d); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeSphere.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeSphere.java new file mode 100755 index 0000000..bc3e827 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeSphere.java @@ -0,0 +1,62 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape; + +import java.util.ArrayList; + +public class WireframeSphere extends AbstractCompositeShape { + + ArrayList previousRing = new ArrayList<>(); + + public WireframeSphere(final Point3D location, final float radius, + final LineAppearance lineFactory) { + super(location); + + final double step = Math.PI / 10; + + final Point3D center = new Point3D(); + + int ringIndex = 0; + + for (double j = 0d; j <= (Math.PI * 2); j += step) { + + Point3D oldPoint = null; + int pointIndex = 0; + + for (double i = 0; i <= (Math.PI * 2); i += step) { + final Point3D newPoint = new Point3D(0, 0, radius); + newPoint.rotate(center, i, j); + + if (oldPoint != null) + addShape(lineFactory.getLine(newPoint, oldPoint)); + + if (ringIndex > 0) { + final Point3D previousRingPoint = previousRing + .get(pointIndex); + addShape(lineFactory.getLine(newPoint, previousRingPoint)); + + previousRing.set(pointIndex, newPoint); + } else + previousRing.add(newPoint); + + oldPoint = newPoint; + pointIndex++; + } + + ringIndex++; + } + + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/slicer/BorderLine.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/slicer/BorderLine.java new file mode 100644 index 0000000..70693f7 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/slicer/BorderLine.java @@ -0,0 +1,44 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.slicer; + +import eu.svjatoslav.sixth.e3d.geometry.Point2D; +import eu.svjatoslav.sixth.e3d.geometry.Point3D; + +import java.util.Comparator; + +public class BorderLine implements Comparator { + + public final int count; + final PolygonCoordinate c1; + final PolygonCoordinate c2; + + public BorderLine(final PolygonCoordinate c1, final PolygonCoordinate c2, + final int count) { + this.c1 = c1; + this.c2 = c2; + this.count = count; + } + + @Override + public int compare(final BorderLine o1, final BorderLine o2) { + return Double.compare(o1.getLength(), o2.getLength()); + } + + public double getLength() { + return c1.space.getDistanceTo(c2.space); + } + + public PolygonCoordinate getMiddlePoint() { + return new PolygonCoordinate(new Point3D().computeMiddlePoint(c1.space, + c2.space), new Point2D().computeMiddlePoint(c1.texture, + c2.texture)); + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/slicer/PolygonCoordinate.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/slicer/PolygonCoordinate.java new file mode 100644 index 0000000..e338145 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/slicer/PolygonCoordinate.java @@ -0,0 +1,25 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.slicer; + +import eu.svjatoslav.sixth.e3d.geometry.Point2D; +import eu.svjatoslav.sixth.e3d.geometry.Point3D; + +public class PolygonCoordinate { + + public Point3D space; + public Point2D texture; + + public PolygonCoordinate(final Point3D space, final Point2D texture) { + this.space = space; + this.texture = texture; + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/slicer/Slicer.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/slicer/Slicer.java new file mode 100644 index 0000000..edfba1c --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/slicer/Slicer.java @@ -0,0 +1,88 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.slicer; + +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.texturedpolygon.TexturedPolygon; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class Slicer { + + private final double maxDistance; + private final List 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 getResult() { + return result; + } + + public void slice(final TexturedPolygon originalPolygon) { + + final PolygonCoordinate pc1 = new PolygonCoordinate( + originalPolygon.coordinates[0].coordinate, + originalPolygon.texturePoint1); + + final PolygonCoordinate pc2 = new PolygonCoordinate( + originalPolygon.coordinates[1].coordinate, + originalPolygon.texturePoint2); + + final PolygonCoordinate pc3 = new PolygonCoordinate( + originalPolygon.coordinates[2].coordinate, + originalPolygon.texturePoint3); + + considerSlicing(pc1, pc2, pc3, originalPolygon); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/Texture.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/Texture.java new file mode 100644 index 0000000..d5041f3 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/Texture.java @@ -0,0 +1,235 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.texture; + +import eu.svjatoslav.sixth.e3d.gui.RenderingContext; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferByte; +import java.awt.image.WritableRaster; + +public class Texture { + + public final TextureBitmap primaryBitmap; + public final java.awt.Graphics2D graphics; + TextureBitmap[] upSampled; + TextureBitmap[] downSampled = new TextureBitmap[8]; + + public Texture(final int width, final int height, final int maxUpscale) { + upSampled = new TextureBitmap[maxUpscale]; + + final BufferedImage bufferedImage = new BufferedImage(width, height, + RenderingContext.bufferedImageType); + + final WritableRaster raster = bufferedImage.getRaster(); + final DataBufferByte dbi = (DataBufferByte) raster.getDataBuffer(); + graphics = (Graphics2D) bufferedImage.getGraphics(); + + graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + + graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + + primaryBitmap = new TextureBitmap(width, height, dbi.getData(), 1); + } + + public int detectDownscaleFactorForZoom(final double zoom) { + double size = 1; + for (int i = 0; i < downSampled.length; i++) { + size = size / 2; + if (size < zoom) + return i; + } + + return downSampled.length - 1; + } + + public int detectUpscaleFactorForZoom(final double zoom) { + double size = 2; + for (int i = 0; i < upSampled.length; i++) { + size = size * 2; + if (size > zoom) + return i; + } + + return upSampled.length - 1; + } + + public TextureBitmap downscaleBitmap(final TextureBitmap originalBitmap) { + int newWidth = originalBitmap.width / 2; + int newHeight = originalBitmap.height / 2; + + // Enforce minimum width and height + if (newWidth < 1) + newWidth = 1; + if (newHeight < 1) + newHeight = 1; + + final TextureBitmap downScaled = new TextureBitmap(newWidth, newHeight, + originalBitmap.multiplicationFactor / 2d); + + final ColorAccumulator accumulator = new ColorAccumulator(); + + for (int y = 0; y < newHeight; y++) + for (int x = 0; x < newWidth; x++) { + accumulator.reset(); + accumulator.accumulate(originalBitmap, x * 2, y * 2); + accumulator.accumulate(originalBitmap, (x * 2) + 1, y * 2); + accumulator.accumulate(originalBitmap, x * 2, (y * 2) + 1); + accumulator + .accumulate(originalBitmap, (x * 2) + 1, (y * 2) + 1); + accumulator.storeResult(downScaled, x, y); + } + + return downScaled; + } + + public TextureBitmap getDownscaledBitmap(final int scaleFactor) { + if (downSampled[scaleFactor] == null) { + + TextureBitmap largerBitmap; + if (scaleFactor == 0) + largerBitmap = primaryBitmap; + else + largerBitmap = getDownscaledBitmap(scaleFactor - 1); + + downSampled[scaleFactor] = downscaleBitmap(largerBitmap); + } + + return downSampled[scaleFactor]; + } + + public TextureBitmap getUpscaledBitmap(final int scaleFactor) { + if (upSampled[scaleFactor] == null) { + + TextureBitmap smallerBitmap; + if (scaleFactor == 0) + smallerBitmap = primaryBitmap; + else + smallerBitmap = getUpscaledBitmap(scaleFactor - 1); + + upSampled[scaleFactor] = upscaleBitmap(smallerBitmap); + } + + return upSampled[scaleFactor]; + } + + public TextureBitmap getZoomedBitmap(final double zoomLevel) { + + if (zoomLevel < 1) { + final int downscaleFactor = detectDownscaleFactorForZoom(zoomLevel); + return getDownscaledBitmap(downscaleFactor); + } else if (zoomLevel > 2) { + final int upscaleFactor = detectUpscaleFactorForZoom(zoomLevel); + + if (upscaleFactor < 0) + return primaryBitmap; + + return getUpscaledBitmap(upscaleFactor); + } + + // System.out.println(zoomLevel); + return primaryBitmap; + } + + public void resetResampledBitmapCache() { + for (int i = 0; i < upSampled.length; i++) + upSampled[i] = null; + + for (int i = 0; i < downSampled.length; i++) + downSampled[i] = null; + } + + public TextureBitmap upscaleBitmap(final TextureBitmap originalBitmap) { + final int newWidth = originalBitmap.width * 2; + final int newHeight = originalBitmap.height * 2; + + final TextureBitmap upScaled = new TextureBitmap(newWidth, newHeight, + originalBitmap.multiplicationFactor * 2d); + + final ColorAccumulator accumulator = new ColorAccumulator(); + + for (int y = 0; y < originalBitmap.height; y++) + for (int x = 0; x < originalBitmap.width; x++) { + accumulator.reset(); + accumulator.accumulate(originalBitmap, x, y); + accumulator.storeResult(upScaled, x * 2, y * 2); + + accumulator.reset(); + accumulator.accumulate(originalBitmap, x, y); + accumulator.accumulate(originalBitmap, x + 1, y); + accumulator.storeResult(upScaled, (x * 2) + 1, y * 2); + + accumulator.reset(); + accumulator.accumulate(originalBitmap, x, y); + accumulator.accumulate(originalBitmap, x, y + 1); + accumulator.storeResult(upScaled, x * 2, (y * 2) + 1); + + accumulator.reset(); + accumulator.accumulate(originalBitmap, x, y); + accumulator.accumulate(originalBitmap, x + 1, y); + accumulator.accumulate(originalBitmap, x, y + 1); + accumulator.accumulate(originalBitmap, x + 1, y + 1); + accumulator.storeResult(upScaled, (x * 2) + 1, (y * 2) + 1); + } + + return upScaled; + } + + public class ColorAccumulator { + public int r, g, b, a; + public int pixelCount = 0; + + public void accumulate(final TextureBitmap bitmap, final int x, + final int y) { + int address = bitmap.getAddress(x, y); + + a += bitmap.bytes[address] & 0xff; + address++; + + b += bitmap.bytes[address] & 0xff; + address++; + + g += bitmap.bytes[address] & 0xff; + address++; + + r += bitmap.bytes[address] & 0xff; + + pixelCount++; + } + + public void reset() { + a = 0; + r = 0; + g = 0; + b = 0; + pixelCount = 0; + } + + public void storeResult(final TextureBitmap bitmap, final int x, + final int y) { + int address = bitmap.getAddress(x, y); + + bitmap.bytes[address] = (byte) (a / pixelCount); + address++; + + bitmap.bytes[address] = (byte) (b / pixelCount); + address++; + + bitmap.bytes[address] = (byte) (g / pixelCount); + address++; + + bitmap.bytes[address] = (byte) (r / pixelCount); + } + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/TextureBitmap.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/TextureBitmap.java new file mode 100644 index 0000000..2fbd824 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/TextureBitmap.java @@ -0,0 +1,152 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.renderer.raster.texture; + +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; + +public class TextureBitmap { + + /** + * Byte order: ABGR + */ + public final byte[] bytes; + + public final int width; + + public final int height; + + public double multiplicationFactor; + + public TextureBitmap(final int width, final int height, final byte[] bytes, + final double multiplicationFactor) { + + this.width = width; + this.height = height; + this.bytes = bytes; + this.multiplicationFactor = multiplicationFactor; + } + + public TextureBitmap(final int width, final int height, + final double multiplicationFactor) { + + this(width, height, new byte[width * height * 4], multiplicationFactor); + } + + public void drawPixel(int textureOffset, + final byte[] targetRenderingAreaBytes, int targetRenderingAreaOffset) { + + final int textureAlpha = bytes[textureOffset] & 0xff; + + if (textureAlpha == 0) + return; + + if (textureAlpha == 255) { + // skip reading of background for fully opaque pixels + targetRenderingAreaBytes[targetRenderingAreaOffset] = (byte) 255; + + targetRenderingAreaOffset++; + textureOffset++; + targetRenderingAreaBytes[targetRenderingAreaOffset] = bytes[textureOffset]; + + targetRenderingAreaOffset++; + textureOffset++; + targetRenderingAreaBytes[targetRenderingAreaOffset] = bytes[textureOffset]; + + targetRenderingAreaOffset++; + textureOffset++; + targetRenderingAreaBytes[targetRenderingAreaOffset] = bytes[textureOffset]; + return; + } + + final int backgroundAlpha = 255 - textureAlpha; + textureOffset++; + + targetRenderingAreaBytes[targetRenderingAreaOffset] = (byte) 255; + targetRenderingAreaOffset++; + + targetRenderingAreaBytes[targetRenderingAreaOffset] = (byte) ((((targetRenderingAreaBytes[targetRenderingAreaOffset] & 0xff) * backgroundAlpha) + ((bytes[textureOffset] & 0xff) * textureAlpha)) / 256); + textureOffset++; + targetRenderingAreaOffset++; + + targetRenderingAreaBytes[targetRenderingAreaOffset] = (byte) ((((targetRenderingAreaBytes[targetRenderingAreaOffset] & 0xff) * backgroundAlpha) + ((bytes[textureOffset] & 0xff) * textureAlpha)) / 256); + textureOffset++; + targetRenderingAreaOffset++; + + targetRenderingAreaBytes[targetRenderingAreaOffset] = (byte) ((((targetRenderingAreaBytes[targetRenderingAreaOffset] & 0xff) * backgroundAlpha) + ((bytes[textureOffset] & 0xff) * textureAlpha)) / 256); + } + + public void drawPixel(final int x, final int y, final Color color) { + int address = getAddress(x, y); + + bytes[address] = (byte) color.a; + + address++; + bytes[address] = (byte) color.b; + + address++; + bytes[address] = (byte) color.g; + + address++; + bytes[address] = (byte) color.r; + } + + public void drawRectangle(int x1, final int y1, int x2, final int y2, + final Color color) { + + if (x1 > x2) { + final int tmp = x1; + x1 = x2; + x2 = tmp; + } + + if (y1 > y2) { + final int tmp = x1; + x1 = x2; + x2 = tmp; + } + + for (int y = y1; y < y2; y++) + for (int x = x1; x < x2; x++) + drawPixel(x, y, color); + } + + public void fillColor(final Color color) { + int address = 0; + while (address < bytes.length) { + bytes[address] = (byte) color.a; + address++; + + bytes[address] = (byte) color.b; + address++; + + bytes[address] = (byte) color.g; + address++; + + bytes[address] = (byte) color.r; + address++; + } + } + + public int getAddress(int x, int y) { + if (x < 0) + x = 0; + + if (x >= width) + x = width - 1; + + if (y < 0) + y = 0; + + if (y >= height) + y = height - 1; + + return ((y * width) + x) * 4; + } +} diff --git a/src/main/resources/eu/svjatoslav/sixth/e3d/examples/hourglass.png b/src/main/resources/eu/svjatoslav/sixth/e3d/examples/hourglass.png new file mode 100644 index 0000000..47a1638 Binary files /dev/null and b/src/main/resources/eu/svjatoslav/sixth/e3d/examples/hourglass.png differ diff --git a/src/main/resources/rebel.xml b/src/main/resources/rebel.xml new file mode 100644 index 0000000..5f75318 --- /dev/null +++ b/src/main/resources/rebel.xml @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/src/test/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/TextLineTest.java b/src/test/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/TextLineTest.java new file mode 100644 index 0000000..51e1de4 --- /dev/null +++ b/src/test/java/eu/svjatoslav/sixth/e3d/gui/textEditorComponent/TextLineTest.java @@ -0,0 +1,118 @@ +/* + * Sixth - System for data storage, computation, exploration and interaction. + * Copyright ©2012-2016, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + */ + +package eu.svjatoslav.sixth.e3d.gui.textEditorComponent; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class TextLineTest { + + @Test + public void testAddIdent() { + TextLine textLine = new TextLine("test"); + textLine.addIdent(4); + assertEquals(" test", textLine.toString()); + + textLine = new TextLine(); + textLine.addIdent(4); + assertEquals("", textLine.toString()); + } + + @Test + public void testCutFromBeginning() { + TextLine textLine = new TextLine("test"); + textLine.cutFromBeginning(2); + assertEquals("st", textLine.toString()); + + textLine = new TextLine("test"); + textLine.cutFromBeginning(4); + assertEquals("", textLine.toString()); + + textLine = new TextLine("test"); + textLine.cutFromBeginning(5); + assertEquals("", textLine.toString()); + + textLine = new TextLine("test"); + textLine.cutFromBeginning(100); + assertEquals("", textLine.toString()); + } + + @Test + public void testCutSubString() { + TextLine textLine = new TextLine("test"); + assertEquals("es", textLine.cutSubString(1, 3)); + assertEquals("tt", textLine.toString()); + + textLine = new TextLine("test"); + assertEquals("st ", textLine.cutSubString(2, 5)); + assertEquals("te", textLine.toString()); + } + + @Test + public void testGetCharForLocation() { + final TextLine textLine = new TextLine("test"); + assertEquals('s', textLine.getCharForLocation(2)); + assertEquals('t', textLine.getCharForLocation(3)); + assertEquals(' ', textLine.getCharForLocation(4)); + } + + @Test + public void testGetIdent() { + final TextLine textLine = new TextLine(" test"); + assertEquals(3, textLine.getIdent()); + } + + @Test + public void testGetLength() { + final TextLine textLine = new TextLine("test"); + assertEquals(4, textLine.getLength()); + } + + @Test + public void testInsertCharacter() { + TextLine textLine = new TextLine("test"); + textLine.insertCharacter(1, 'o'); + assertEquals("toest", textLine.toString()); + + textLine = new TextLine("test"); + textLine.insertCharacter(5, 'o'); + assertEquals("test o", textLine.toString()); + + } + + @Test + public void testIsEmpty() { + TextLine textLine = new TextLine(""); + assertEquals(true, textLine.isEmpty()); + + textLine = new TextLine(" "); + assertEquals(true, textLine.isEmpty()); + + textLine = new TextLine("l"); + assertEquals(false, textLine.isEmpty()); + } + + @Test + public void testRemoveCharacter() { + TextLine textLine = new TextLine("test"); + textLine.removeCharacter(0); + assertEquals("est", textLine.toString()); + + textLine = new TextLine("test"); + textLine.removeCharacter(3); + assertEquals("tes", textLine.toString()); + + textLine = new TextLine("test"); + textLine.removeCharacter(4); + assertEquals("test", textLine.toString()); + } + +} diff --git a/tools/open with IntelliJ IDEA b/tools/open with IntelliJ IDEA new file mode 100755 index 0000000..1f82875 --- /dev/null +++ b/tools/open with IntelliJ IDEA @@ -0,0 +1,6 @@ +#!/bin/bash + +cd "${0%/*}" + +cd .. +idea . diff --git a/tools/open with git-cola b/tools/open with git-cola new file mode 100755 index 0000000..8655908 --- /dev/null +++ b/tools/open with git-cola @@ -0,0 +1,7 @@ +#!/bin/bash + +cd "${0%/*}" + +cd .. + +cola diff --git a/tools/update web site b/tools/update web site new file mode 100755 index 0000000..8f63084 --- /dev/null +++ b/tools/update web site @@ -0,0 +1,27 @@ +#!/bin/bash +cd "${0%/*}"; if [ "$1" != "T" ]; then xterm -e "'$0' T"; exit; fi; + +# +# TODO: needs updating +# +# ( +# cd .. + +# mvn clean + +# mkdir -p target/website/codegraphs + +# mvn test package -e exec:java -Dexec.mainClass="eu.svjatoslav.sixth.DataGraph" -Dexec.classpathScope="test" + +# ( +# cd target/website/codegraphs/ +# meviz index -t Sixth +# ) + +# cp target/sixth.jar target/website/ + +# rsync -avz --delete target/website/ n0@svjatoslav.eu:/var/www/svjatoslav.eu/projects/sixth +# ) + +echo "Script finished. Press ENTER to close this terminal" +read