refactor(demos): adopt new sixth-3d API and add tutorial docs
authorSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Mon, 30 Mar 2026 16:45:36 +0000 (19:45 +0300)
committerSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Mon, 30 Mar 2026 16:45:36 +0000 (19:45 +0300)
Update all demos to use the modernized sixth-3d engine API with fluent
factory methods and in-place CSG operations. Add comprehensive tutorial
documentation in the Essentials section with screenshots and code examples.

- Use static imports: Point3D.point(), Color.hex()
- Replace CSG class usage with in-place union/subtract/intersect methods
- Update to renamed classes: SolidTriangle, TexturedTriangle
- Use SolidPolygon.quad() for quad surfaces instead of two triangles
- Add Minimal Example tutorial with step-by-step walkthrough
- Add screenshots for CSG, Coordinate System, Shape Gallery, Winding Order
- Rename AxisArrowsDemo to CoordinateSystemDemo with class references

25 files changed:
doc/Screenshots/Essentials/CSG demo.png [new file with mode: 0644]
doc/Screenshots/Essentials/Coordinate system.png [new file with mode: 0644]
doc/Screenshots/Essentials/Minimal example.png [new file with mode: 0644]
doc/Screenshots/Essentials/Shape gallery.png [new file with mode: 0644]
doc/Screenshots/Essentials/Winding order.png [new file with mode: 0644]
doc/index.org
src/main/java/eu/svjatoslav/sixth/e3d/examples/ArrowDemo.java
src/main/java/eu/svjatoslav/sixth/e3d/examples/OctreeDemo.java
src/main/java/eu/svjatoslav/sixth/e3d/examples/RainingNumbersDemo.java
src/main/java/eu/svjatoslav/sixth/e3d/examples/SineHeightmap.java
src/main/java/eu/svjatoslav/sixth/e3d/examples/TextEditorDemo.java
src/main/java/eu/svjatoslav/sixth/e3d/examples/TextEditorDemo2.java
src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/TexturedCube.java
src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/AxisArrowsDemo.java [deleted file]
src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/CSGDemo.java
src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/CoordinateSystemDemo.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/MinimalExample.java
src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/ShapeGalleryDemo.java
src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/WindingOrderDemo.java
src/main/java/eu/svjatoslav/sixth/e3d/examples/graph_demo/MathGraphsDemo.java
src/main/java/eu/svjatoslav/sixth/e3d/examples/graph_demo/SurfaceGraph3D.java
src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/ApplicationListPanel.java
src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Cell.java
src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Main.java
src/main/java/eu/svjatoslav/sixth/e3d/examples/terrain_demo/TerrainDemo.java

diff --git a/doc/Screenshots/Essentials/CSG demo.png b/doc/Screenshots/Essentials/CSG demo.png
new file mode 100644 (file)
index 0000000..5602255
Binary files /dev/null and b/doc/Screenshots/Essentials/CSG demo.png differ
diff --git a/doc/Screenshots/Essentials/Coordinate system.png b/doc/Screenshots/Essentials/Coordinate system.png
new file mode 100644 (file)
index 0000000..7896688
Binary files /dev/null and b/doc/Screenshots/Essentials/Coordinate system.png differ
diff --git a/doc/Screenshots/Essentials/Minimal example.png b/doc/Screenshots/Essentials/Minimal example.png
new file mode 100644 (file)
index 0000000..6bee1cb
Binary files /dev/null and b/doc/Screenshots/Essentials/Minimal example.png differ
diff --git a/doc/Screenshots/Essentials/Shape gallery.png b/doc/Screenshots/Essentials/Shape gallery.png
new file mode 100644 (file)
index 0000000..76a4fe5
Binary files /dev/null and b/doc/Screenshots/Essentials/Shape gallery.png differ
diff --git a/doc/Screenshots/Essentials/Winding order.png b/doc/Screenshots/Essentials/Winding order.png
new file mode 100644 (file)
index 0000000..f939231
Binary files /dev/null and b/doc/Screenshots/Essentials/Winding order.png differ
index cb10d9b..734d9cb 100644 (file)
@@ -53,34 +53,291 @@ It requires Java 21 or newer to run.
 To start the demo application, use command:
 : java -jar sixth-3d-demos.jar
 
-* Navigating in space
+* Essentials
+
+*Resources to help you understand the Sixth 3D library:*
+- Read online [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/][JavaDoc]].
+- See [[https://www3.svjatoslav.eu/projects/sixth-3d/graphs/][Sixth 3D class diagrams]]. (Diagrams were generated by using
+  [[https://www3.svjatoslav.eu/projects/javainspect/][JavaInspect]] utility)
+
+** Minimal example
 :PROPERTIES:
-:CUSTOM_ID: navigating-in-space
-:ID:       0aca7419-48df-48ac-b18b-a8bf7b9db0ac
+:CUSTOM_ID: minimal-example
+:ID:       1a2b3c4d-5e6f-7890-abcd-ef1234567890
 :END:
 
-| key                 | result                               |
-|---------------------+--------------------------------------|
-| Keyboard arrow keys | Move: left, right, forward, backward |
-| Mouse scroll wheel  | Move: up <-> down                    |
-| Mouse drag          | Turn head around in 3D space         |
+This is the "Hello World" of *Sixth 3D* - the minimal boilerplate
+needed to render any 3D geometry.
+
+: eu.svjatoslav.sixth.e3d.examples.essentials.MinimalExample
+
+*Brief tutorial:*
+
+Here we guide you through creating your first 3D scene with Sixth 3D
+engine.
+
+Prerequisites:
+- Java 21 or later installed
+- Maven 3.x
+- Basic Java knowledge
 
-* Example scenes
+*** Add Dependency to Your Project
 :PROPERTIES:
-:CUSTOM_ID: example-scenes
-:ID:       5f88b493-6ab3-4659-8280-803f75dbd5e0
+:CUSTOM_ID: add-dependency-to-your-project
+:ID:       3fffc32e-ae66-40b7-ad7d-fab6093c778b
+:END:
+
+Add *Sixth 3D* to your pom.xml:
+
+#+BEGIN_SRC xml
+<dependencies>
+    <dependency>
+        <groupId>eu.svjatoslav</groupId>
+        <artifactId>sixth-3d</artifactId>
+        <version>1.4-SNAPSHOT</version>
+    </dependency>
+</dependencies>
+
+<repositories>
+    <repository>
+        <id>svjatoslav.eu</id>
+        <name>Svjatoslav repository</name>
+        <url>https://www3.svjatoslav.eu/maven/</url>
+    </repository>
+</repositories>
+#+END_SRC
+
+
+*** Create Your First 3D Scene
+:PROPERTIES:
+:CUSTOM_ID: create-your-first-3d-scene
+:ID:       564fa596-9b2b-418a-9df9-baa46f0d0a66
 :END:
 
+
+#+BEGIN_SRC java
+  import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+  import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
+  import eu.svjatoslav.sixth.e3d.math.Transform;
+  import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+  import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+  import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonRectangularBox;
+
+  public class MyFirstScene {
+      public static void main(String[] args) {
+          // Create the application window
+          ViewFrame viewFrame = new ViewFrame("My First Scene");
+
+          // Get the collection where you add 3D shapes
+          ShapeCollection shapes = viewFrame.getViewPanel().getRootShapeCollection();
+
+          // Create a red box centered at origin
+          final Transform boxTransform = new Transform();
+          SolidPolygonRectangularBox box = new SolidPolygonRectangularBox(
+                  point(-50, -50, -50),
+                  point(50, 50, 50),
+                  Color.RED
+          );
+          box.setTransform(boxTransform);
+          shapes.addShape(box);
+
+          // Position your camera
+          viewFrame.getViewPanel().getCamera().getTransform()
+                  .setTranslation(point(0, -100, -300));
+
+          // Trigger initial render
+          viewFrame.getViewPanel().repaintDuringNextViewUpdate();
+      }
+  }
+#+END_SRC
+
+Compile and run *MyFirstScene* class. A new window should open that will
+display 3D scene with red box.
+
+
+You should see this:
+#+attr_html: :class responsive-img
+#+attr_latex: :width 1000px
+[[file:Screenshots/Essentials/Minimal example.png]]
+
+
+*Navigating the scene:*
+
+| Input              | Action                      |
+|--------------------+-----------------------------|
+| Arrow Up           | Move forward                |
+| Arrow Down         | Move backward               |
+| Arrow Left         | Move left (strafe)          |
+| Arrow Right        | Move right (strafe)         |
+| Mouse drag         | Look around (rotate camera) |
+| Mouse scroll wheel | Move up / down              |
+
+Movement uses physics-based acceleration for smooth, natural
+motion. The faster you're moving, the more acceleration builds up,
+creating an intuitive flying experience.
+
 Press *F12* in any demo to open the [[https://www3.svjatoslav.eu/projects/sixth-3d/#outline-container-developer-tools][Developer Tools panel]]. This
 debugging interface provides real-time insight into the rendering
 pipeline with diagnostic toggles.
 
+** Coordinate system
+:PROPERTIES:
+:CUSTOM_ID: coordinate-system
+:ID:       2b3c4d5e-6f7a-8901-bcde-f12345678901
+:END:
+
+: eu.svjatoslav.sixth.e3d.examples.essentials.CoordinateSystemDemo
+
+#+attr_html: :class responsive-img
+#+attr_latex: :width 1000px
+[[file:Screenshots/Essentials/Coordinate system.png]]
+
+Visual reference for the Sixth 3D coordinate system using three colored
+arrows originating from (0,0,0):
+
+| Axis | Color | Direction                          |
+|------+-------+------------------------------------|
+| X    | Red   | Points RIGHT (positive X)          |
+| Y    | Green | Points DOWN (positive Y)           |
+| Z    | Blue  | Points AWAY from viewer (positive Z) |
+
+A wireframe grid on the Y=0 plane provides spatial context and scale. Text
+labels (X, Y, Z) appear at each arrow tip.
+
+This demo is essential for understanding Sixth 3D's coordinate system where:
+- Positive Y goes down (screen coordinates convention)
+- Positive Z goes into the screen (away from camera)
+- Positive X goes to the right
+
+See the [[https://www3.svjatoslav.eu/projects/sixth-3d/#coordinate-system][Coordinate System]] section in the Sixth 3D documentation
+for a detailed explanation of the axis conventions and coordinate math.
+
+** Winding order
+:PROPERTIES:
+:CUSTOM_ID: winding-order
+:ID:       3c4d5e6f-7a8b-9012-cdef-123456789012
+:END:
+
+: eu.svjatoslav.sixth.e3d.examples.essentials.WindingOrderDemo
+
+#+attr_html: :class responsive-img
+#+attr_latex: :width 1000px
+[[file:Screenshots/Essentials/Winding order.png]]
+
+This demo demonstrates polygon winding order and backface culling - fundamental
+concepts for efficient 3D rendering.
+
+The green triangle uses counter-clockwise (CCW) vertex ordering:
+1. Upper-center vertex
+2. Lower-left vertex
+3. Lower-right vertex
+
+With backface culling enabled:
+- *CCW winding* (negative signed area) = front-facing = visible
+- *CW winding* (positive signed area) = back-facing = culled (not rendered)
+
+This optimization prevents rendering polygons facing away from the camera,
+improving performance by roughly 50% for closed meshes. Navigate around the
+scene and observe that the triangle becomes invisible when viewed from behind,
+demonstrating the culling effect.
+
+See the [[https://www3.svjatoslav.eu/projects/sixth-3d/#winding-order-backface-culling][Winding Order & Backface Culling]] section
+in the Sixth 3D documentation for a detailed explanation with diagrams.
+
+** Shape gallery
+:PROPERTIES:
+:CUSTOM_ID: shape-gallery
+:ID:       4d5e6f7a-8b9c-0123-def0-234567890123
+:END:
+
+: eu.svjatoslav.sixth.e3d.examples.essentials.ShapeGalleryDemo
+
+#+attr_html: :class responsive-img
+#+attr_latex: :width 1000px
+[[file:Screenshots/Essentials/Shape gallery.png]]
+
+Comprehensive showcase of all primitive 3D shapes available in Sixth 3D,
+organized in a 7×2 grid:
+
+| Column | Shape Type | Description                    |
+|--------+------------+--------------------------------|
+| 1      | Arrow      | 3D arrow with conical tip      |
+| 2      | Cone       | Circular base cone             |
+| 3      | Cube       | Regular hexahedron             |
+| 4      | Cylinder   | Tube with circular caps        |
+| 5      | Pyramid    | Square base pyramid            |
+| 6      | Box        | Rectangular parallelepiped     |
+| 7      | Sphere     | Geodesic sphere approximation  |
+
+*Top row:* Wireframe versions - edges only, no surfaces
+*Bottom row:* Solid polygon versions - filled surfaces with lighting
+
+Additional features demonstrated:
+- Dynamic lighting from 10 orbiting colored light sources (visible as small
+  glowing dots)
+- Per-shape color assignment for visual distinction
+- Grid floor showing spatial layout and alignment
+- Text labels identifying each shape type
+- Shading enabled on solid shapes to show lighting effects
+
+See the [[https://www3.svjatoslav.eu/projects/sixth-3d/#mesh][Mesh]] section in the Sixth 3D documentation
+for an explanation of 3D mesh fundamentals and how shapes are constructed.
+
+** CSG demo
+:PROPERTIES:
+:CUSTOM_ID: csg-demo
+:ID:       5e6f7a8b-9c0d-1234-ef01-345678901234
+:END:
+
+: eu.svjatoslav.sixth.e3d.examples.essentials.CSGDemo
+
+#+attr_html: :class responsive-img
+#+attr_latex: :width 1000px
+[[file:Screenshots/Essentials/CSG demo.png]]
+
+Constructive Solid Geometry (CSG) boolean operations on 3D meshes. Three
+operations displayed left to right:
+
+1. *Subtract (Cube - Sphere)* - A red cube with a spherical cavity carved out
+   where a blue sphere intersects it. Shows the cube's interior surfaces.
+
+2. *Union (Cube + Sphere)* - A merged shape combining both volumes. Red
+   surfaces from the cube, blue surfaces from the sphere, seamlessly joined.
+
+3. *Intersect (Cube ∩ Sphere)* - Only the volume shared by both shapes
+   remains. Shows which parts of space were occupied by both the cube and
+   sphere simultaneously.
+
+Color scheme:
+- *Red* = geometry from the first operand (cube)
+- *Blue* = geometry from the second operand (sphere)
+
+Each operation includes a text description panel below the shape. The demo
+uses:
+- SolidPolygonCube (red)
+- SolidPolygonSphere (blue)
+- CSGSolid.subtract(), union(), intersect() methods
+- toMesh() conversion for rendering
+- Backface culling and shading enabled
+
+CSG is powerful for procedural modeling, allowing complex shapes to be built
+from simple primitives through boolean combinations.
+
+* Advanced examples
+:PROPERTIES:
+:CUSTOM_ID: example-scenes
+:ID:       5f88b493-6ab3-4659-8280-803f75dbd5e0
+:END:
+
+
 ** Conway's Game of Life
 :PROPERTIES:
 :CUSTOM_ID: conways-game-of-life
 :ID:       08914390-742b-4c78-88bf-602ab9640082
 :END:
 
+: eu.svjatoslav.sixth.e3d.examples.life_demo.Main
+
 The Game of Life, also known simply as Life, is a cellular automaton
 devised by the British mathematician John Horton Conway in 1970.
 
@@ -94,9 +351,9 @@ devised by the British mathematician John Horton Conway in 1970.
 
 [[file:Screenshots/Life.png]]
 
-Current application projects 2D game grid/matrix onto three
-dimensional space. Extra dimension (height) is used to visualize
-history (previous iterations) using glowing dots suspended in space.
+This demo projects the 2D game grid onto three-dimensional space. The extra
+dimension (height) visualizes history (previous iterations) using glowing dots
+suspended in space.
 
 Usage:
 | key                            | result                               |
@@ -112,26 +369,26 @@ Usage:
 :ID:       39250157-db8e-4861-a21b-8568912bd160
 :END:
 
+: eu.svjatoslav.sixth.e3d.examples.TextEditorDemo
+
 [[file:Screenshots/Text editors.png]]
 
-Initial test for creating user interfaces in 3D and:
+Initial test for creating user interfaces in 3D, demonstrating:
 + window focus handling
-+ picking objects using mouse
-+ redirecting keyboard input to focused window
-
++ picking objects using the mouse
++ redirecting keyboard input to the focused window
 
 Window focus acts like a stack.
 
-When window is clicked with the mouse, previously focused window (if
-any) is pushed to the focus stack and new window receives focus. Red
+When a window is clicked with the mouse, the previously focused window (if
+any) is pushed to the focus stack and the new window receives focus. A red
 frame appears around the window to indicate this.
 
-When ESC key is pressed, window focus is returned to previous window
-(if any).
+When the ESC key is pressed, focus is returned to the previous window (if any).
 
-When any window is focused, all keyboard input is redirected to that
-window, including cursor keys. To be able to navigate around the world
-again, window must be unfocused first using ESC key.
+When any window is focused, all keyboard input is redirected to that window,
+including cursor keys. To navigate around the world again, the window must be
+unfocused first by pressing the ESC key.
 
 
 + TODO:
@@ -151,11 +408,13 @@ again, window must be unfocused first using ESC key.
 :ID:       a7b8c9d0-e1f2-3456-0123-567890123456
 :END:
 
+: eu.svjatoslav.sixth.e3d.examples.TextEditorDemo2
+
 *Quite a lot of text editors can be rendered:*
 
 [[file:Screenshots/Text editors 2.png]]
 
-See also [[https://hackers-1995.vercel.app/][similar looking web based demo]] ! :)
+See also this [[https://hackers-1995.vercel.app/][similar-looking web-based demo]]!
 
 ** Math graphs demo
 :PROPERTIES:
@@ -163,6 +422,8 @@ See also [[https://hackers-1995.vercel.app/][similar looking web based demo]] !
 :ID:       b1c2d3e4-f5a6-7890-bcde-f12345678901
 :END:
 
+: eu.svjatoslav.sixth.e3d.examples.graph_demo.MathGraphsDemo
+
 [[file:Screenshots/Mathematical formulas.png]]
 
 + TODO: instead of projecting 2D visualizations onto 3D space,
@@ -174,6 +435,8 @@ See also [[https://hackers-1995.vercel.app/][similar looking web based demo]] !
 :ID:       b2c3d4e5-f6a7-8901-bcde-f12345678901
 :END:
 
+: eu.svjatoslav.sixth.e3d.examples.SineHeightmap
+
 [[file:Screenshots/Sine heightmap and sphere.png]]
 
 Simple test scene. Easy to implement and looks nice.
@@ -184,6 +447,8 @@ Simple test scene. Easy to implement and looks nice.
 :ID:       c3d4e5f6-a7b8-9012-cdef-123456789012
 :END:
 
+: eu.svjatoslav.sixth.e3d.examples.OctreeDemo
+
 [[file:Screenshots/Raytracing fractal in voxel polygon hybrid scene.png]]
 
 Test scene that is generated simultaneously using:
@@ -192,31 +457,33 @@ Test scene that is generated simultaneously using:
 + voxels
   + for on-demand raytracing
 
-Instead of storing voxels in dumb [X * Y * Z] array, dynamically
-partitioned [[https://en.wikipedia.org/wiki/Octree][octree]] is used to compress data. Press "r" key anywhere in
-the scene to raytrace current view through compressed voxel
+Instead of storing voxels in a naive [X × Y × Z] array, a dynamically
+partitioned [[https://en.wikipedia.org/wiki/Octree][octree]] compresses the data. Press the "r" key anywhere in
+the scene to raytrace the current view through the compressed voxel
 data structure.
 
-** Graphics Benchmark
+* Graphics Benchmark
 :PROPERTIES:
 :CUSTOM_ID: graphics-benchmark
 :ID:       e5f6a7b8-c9d0-1234-ef01-345678901234
 :END:
 
+: eu.svjatoslav.sixth.e3d.examples.benchmark.GraphicsBenchmark
+
 An automated graphics benchmark that measures the engine's rendering
 performance across different rendering modes.
 
 [[file:Screenshots/Benchmark.png]]
 
-The benchmark will cycle through different scenes that utilize different
-rendering primitives (textured polygons, billboards, solid polygons,
-etc.) to measure their relative performance.
+The benchmark cycles through scenes that utilize different rendering
+primitives (textured polygons, billboards, solid polygons, etc.) to measure
+their relative performance.
 
 The camera follows a deterministic orbital path around the scene,
 ensuring reproducible results across runs.
 
-At the end, the benchmark will output a report that is easy to preserve for
-later comparisons.
+Upon completion, the benchmark outputs a report suitable for preservation
+and later comparisons.
 
 Example benchmark report:
 #+begin_example
@@ -252,8 +519,8 @@ Star Grid                    318.97
 :ID:       d4e5f6a7-b8c9-0123-def0-234567890123
 :END:
 
-*This program is free software: released under Creative Commons Zero
-(CC0) license*
+*This program is free software, released under the Creative Commons Zero
+(CC0) license.*
 
 *Program author:*
 - Svjatoslav Agejenko
@@ -268,5 +535,5 @@ Star Grid                    318.97
   : git clone https://www2.svjatoslav.eu/git/sixth-3d-demos.git
 
 *Understanding the Sixth 3D demos source code:*
-- Read online [[https://www3.svjatoslav.eu/projects/sixth-3d-demos/apidocs/][JavaDoc]].
-- Study underlying [[https://www3.svjatoslav.eu/projects/sixth-3d/][Sixth 3D]] engine.
+- Read the online [[https://www3.svjatoslav.eu/projects/sixth-3d-demos/apidocs/][JavaDoc]].
+- Study the underlying [[https://www3.svjatoslav.eu/projects/sixth-3d/][Sixth 3D]] engine.
index a04c237..3b45854 100644 (file)
@@ -14,6 +14,9 @@ import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolyg
 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonCone;
 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid3D;
 
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex;
+
 /**
  * Demo showcasing the SolidPolygonArrow shape with various colors, sizes,
  * orientations, and transparency levels.
@@ -34,13 +37,13 @@ public class ArrowDemo {
         final ShapeCollection shapes = viewPanel.getRootShapeCollection();
 
         // Position camera to view the scene
-        viewPanel.getCamera().getTransform().setTranslation(new Point3D(0, -200, -600));
+        viewPanel.getCamera().getTransform().setTranslation(point(0, -200, -600));
 
         // Add a 3D grid for spatial reference
-        final LineAppearance gridAppearance = new LineAppearance(1, new Color(100, 100, 100, 80));
+        final LineAppearance gridAppearance = new LineAppearance(1, hex("64646450"));
         final Grid3D grid = new Grid3D(
-                new Point3D(-300, -200, -300),
-                new Point3D(300, 200, 300),
+                point(-300, -200, -300),
+                point(300, 200, 300),
                 100,
                 gridAppearance
         );
@@ -50,49 +53,49 @@ public class ArrowDemo {
 
         // Red arrow pointing up (negative Y direction)
         final SolidPolygonArrow redArrow = new SolidPolygonArrow(
-                new Point3D(0, 150, 0),
-                new Point3D(0, -150, 0),
+                point(0, 150, 0),
+                point(0, -150, 0),
                 8, Color.RED
         );
         shapes.addShape(redArrow);
 
         // Green arrow pointing along positive X
         final SolidPolygonArrow greenArrow = new SolidPolygonArrow(
-                new Point3D(-200, 0, 0),
-                new Point3D(0, 0, 0),
+                point(-200, 0, 0),
+                point(0, 0, 0),
                 6, Color.GREEN
         );
         shapes.addShape(greenArrow);
 
         // Blue arrow pointing along positive Z
         final SolidPolygonArrow blueArrow = new SolidPolygonArrow(
-                new Point3D(0, 0, -200),
-                new Point3D(0, 0, 0),
+                point(0, 0, -200),
+                point(0, 0, 0),
                 6, Color.BLUE
         );
         shapes.addShape(blueArrow);
 
         // Yellow arrow pointing diagonally
         final SolidPolygonArrow yellowArrow = new SolidPolygonArrow(
-                new Point3D(100, 100, 100),
-                new Point3D(300, -100, 300),
+                point(100, 100, 100),
+                point(300, -100, 300),
                 10, Color.YELLOW
         );
         shapes.addShape(yellowArrow);
 
         // Semi-transparent cyan arrow (50% opacity)
         final SolidPolygonArrow transparentCyanArrow = new SolidPolygonArrow(
-                new Point3D(-150, 50, -100),
-                new Point3D(-50, -100, 100),
-                8, new Color(0, 255, 255, 128)
+                point(-150, 50, -100),
+                point(-50, -100, 100),
+                8, hex("00FFFF80")
         );
         shapes.addShape(transparentCyanArrow);
 
         // Semi-transparent magenta arrow (25% opacity)
         final SolidPolygonArrow transparentMagentaArrow = new SolidPolygonArrow(
-                new Point3D(50, 200, 50),
-                new Point3D(50, 0, 50),
-                12, new Color(255, 0, 255, 64)
+                point(50, 200, 50),
+                point(50, 0, 50),
+                12, hex("FF00FF40")
         );
         shapes.addShape(transparentMagentaArrow);
 
@@ -105,21 +108,21 @@ public class ArrowDemo {
             final double endX = (radius + 80) * Math.cos(angle);
             final double endZ = (radius + 80) * Math.sin(angle);
 
-final SolidPolygonArrow circleArrow = new SolidPolygonArrow(
-                        new Point3D(startX, -80, startZ),
-                        new Point3D(endX, -80, endZ),
-                        4, Color.WHITE
-                );
+            final SolidPolygonArrow circleArrow = new SolidPolygonArrow(
+                    point(startX, -80, startZ),
+                    point(endX, -80, endZ),
+                    4, Color.WHITE
+            );
             shapes.addShape(circleArrow);
         }
 
         // A standalone cone to demonstrate SolidPolygonCone
         final SolidPolygonCone standaloneCone = new SolidPolygonCone(
-                new Point3D(-300, 0, 0),
+                point(-300, 0, 0),
                 40,
                 80,
                 16,
-                new Color(255, 128, 0)
+                hex("FF8000FF")
         );
         shapes.addShape(standaloneCone);
 
index aedc08c..d4e1c9c 100755 (executable)
@@ -28,6 +28,9 @@ import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid3D
 import java.awt.event.KeyEvent;
 import java.util.Vector;
 
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex;
+
 /**
  * Demo showing volumetric octree rendering with raytracing capability.
  * Creates a 3D scene with various geometric shapes stored in an octree data structure.
@@ -39,12 +42,12 @@ public class OctreeDemo extends WorldNavigationUserInputTracker {
      * Scale factor for rendering octree voxels in the scene.
      */
     private static final double magnification = 5;
-    private final LineAppearance gridAppearance = new LineAppearance(40, new Color(255,
-            0, 0, 60));
+    private final LineAppearance gridAppearance = new LineAppearance(40, hex("FF00003C"));
     private final Vector<eu.svjatoslav.sixth.e3d.renderer.octree.raytracer.LightSource> lights = new Vector<>();
     private OctreeVolume octreeVolume;
     private ShapeCollection shapeCollection;
     private ViewPanel viewPanel;
+
     /**
      * Creates a new OctreeDemo instance.
      */
@@ -70,13 +73,13 @@ public class OctreeDemo extends WorldNavigationUserInputTracker {
     private void addLightToBothSystems(final Point3D location, final Color color,
                                        final float brightness) {
         shapeCollection.addShape(new LightSourceMarker(new Point3D(location)
-                .scaleUp(magnification), color));
+                .multiply(magnification), color));
 
         lights.add(new eu.svjatoslav.sixth.e3d.renderer.octree.raytracer.LightSource(
                 location, color, brightness));
 
         viewPanel.getLightingManager().addLight(new LightSource(
-                new Point3D(location).scaleUp(magnification), color, brightness * 50));
+                new Point3D(location).multiply(magnification), color, brightness * 50));
     }
 
     /**
@@ -143,32 +146,32 @@ public class OctreeDemo extends WorldNavigationUserInputTracker {
         shapeCollection = viewPanel.getRootShapeCollection();
 
         shapeCollection.addShape(new Grid3D(
-                new Point3D(-10000, -10000, -10000), new Point3D(10000, 10000,
+                point(-10000, -10000, -10000), point(10000, 10000,
                 10000), 4000, gridAppearance));
 
-        addLightToBothSystems(new Point3D(20, -450, 240), new Color(255, 255, 255), 100);
-        addLightToBothSystems(new Point3D(-150, -116, 141), new Color(255, 0, 0), 10);
+        addLightToBothSystems(point(20, -450, 240), new Color(255, 255, 255), 100);
+        addLightToBothSystems(point(-150, -116, 141), new Color(255, 0, 0), 10);
 
         dotSpiral();
 
         // arbitrary rectangles
         putRect(new IntegerPoint(-10, -10, -10),
                 new IntegerPoint(10, 10, -20),
-                new Color(200, 255, 200, 100));
+                hex("C8FFC864"));
 
         putRect(new IntegerPoint(-3, 0, -30),
                 new IntegerPoint(12, 3, 300),
-                new Color(255, 200, 200, 100));
+                hex("FFC8C864"));
 
         putRect(new IntegerPoint(-20, 20, -20),
                 new IntegerPoint(20, 80, 20),
-                new Color(255, 200, 255, 100));
+                hex("FFC8FF64"));
 
         tiledFloor();
 
         fractal(-50, 20, 100, 32, 1);
 
-        final TextCanvas message = new TextCanvas(new Transform(new Point3D(
+        final TextCanvas message = new TextCanvas(new Transform(point(
                 -10, 20, -180)), "Press \"r\" to raytrace current view",
                 Color.WHITE, Color.PURPLE);
         shapeCollection.addShape(message);
@@ -204,8 +207,8 @@ public class OctreeDemo extends WorldNavigationUserInputTracker {
      */
     private void putPixel(final int x, final int y, final int z,
                           final Color color) {
-        shapeCollection.addShape(new GlowingPoint(new Point3D(x, y, z)
-                .scaleUp(magnification), 3 * magnification, color));
+        shapeCollection.addShape(new GlowingPoint(point(x, y, z)
+                .multiply(magnification), 3 * magnification, color));
         octreeVolume.putCell(x, y, z, color);
 
     }
@@ -219,8 +222,8 @@ public class OctreeDemo extends WorldNavigationUserInputTracker {
      */
     private void putRect(IntegerPoint p1, IntegerPoint p2, final Color color) {
         final SolidPolygonRectangularBox box = new SolidPolygonRectangularBox(
-                new Point3D(p1).scaleUp(magnification),
-                new Point3D(p2).scaleUp(magnification), color);
+                new Point3D(p1).multiply(magnification),
+                new Point3D(p2).multiply(magnification), color);
         box.setShadingEnabled(true);
         shapeCollection.addShape(box);
         octreeVolume.fillRectangle(p1, p2, color);
@@ -247,7 +250,7 @@ public class OctreeDemo extends WorldNavigationUserInputTracker {
     private void tiledFloor() {
         final int step = 40;
         final int size = step - 15;
-        Color color = new Color(255, 255, 255, 100);
+        Color color = hex("FFFFFF64");
         for (int x = -200; x < 200; x += step)
             for (int z = -200; z < 200; z += step)
                 putRect(
index c2c82cd..bc6f6b8 100644 (file)
@@ -12,6 +12,8 @@ import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
 import eu.svjatoslav.sixth.e3d.gui.FrameListener;
 import eu.svjatoslav.sixth.e3d.math.Transform;
 import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
 import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractShape;
 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas.TextCanvas;
@@ -87,7 +89,7 @@ public class RainingNumbersDemo implements FrameListener {
         Random random = new Random();
 
         for (int i = 0; i < NUMBERS_COUNT; i++) {
-            final Point3D location = new Point3D((Math.random() * AREA)
+            final Point3D location = point((Math.random() * AREA)
                     - AREA_HALF, (Math.random() * AREA) - AREA_HALF,
                     (Math.random() * AREA) - AREA_HALF);
 
index 25642dd..64433da 100755 (executable)
@@ -1,5 +1,5 @@
 /*
- * Sixth 3D engine demos. Author: Svjatoslav Agejenko. 
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
  * This project is released under Creative Commons Zero (CC0) license.
  *
  */
@@ -14,49 +14,56 @@ import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon;
 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.WireframeSphere;
 
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.origin;
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex;
+
 /**
  * Demo showing a sine heightmap surface with a central wireframe sphere.
  * Two wobbly surfaces are positioned above and below the sphere.
  */
 public class SineHeightmap {
 
+    /**
+     * Frequency of the wave pattern in the wobbly surfaces.
+     */
+    private static final double WAVE_FREQUENCY = 50d;
+    /**
+     * Amplitude of the wave pattern in the wobbly surfaces.
+     */
+    private static final double WAVE_AMPLITUDE = 50d;
+    /**
+     * Color for the square plates in the wobbly surfaces.
+     */
+    private static final Color SQUARE_PLATE_COLOR = hex("88F7");
     /**
      * Creates a new GraphDemo instance.
      */
     public SineHeightmap() {
     }
 
-    /** Frequency of the wave pattern in the wobbly surfaces. */
-    private static final double WAVE_FREQUENCY = 50d;
-    /** Amplitude of the wave pattern in the wobbly surfaces. */
-    private static final double WAVE_AMPLITUDE = 50d;
-    /** Color for the square plates in the wobbly surfaces. */
-    private static final Color SQUARE_PLATE_COLOR = new Color("88F7");
-    /** Scale factor for the graph rendering. */
-    private static final double GRAPH_SCALE = 50d;
-
     /**
      * Creates a single square plate at the specified position.
+     *
      * @param shapeCollection the collection to add the plate to
-     * @param y the Y coordinate (elevation)
-     * @param x the X coordinate
-     * @param z the Z coordinate
+     * @param y               the Y coordinate (elevation)
+     * @param x               the X coordinate
+     * @param z               the Z coordinate
      */
     private static void makeSquarePlate(final ShapeCollection shapeCollection,
                                         final double y, final double x, final double z) {
-        final Point3D p1 = new Point3D(x, y, z);
-        final Point3D p2 = new Point3D(x + 20, y, z);
-        final Point3D p3 = new Point3D(x, y, z + 20);
-        final Point3D p4 = new Point3D(x + 20, y, z + 20);
-        final SolidPolygon polygon1 = new SolidPolygon(p1, p2, p3, SQUARE_PLATE_COLOR);
-        final SolidPolygon polygon2 = new SolidPolygon(p4, p2, p3, SQUARE_PLATE_COLOR);
-        shapeCollection.addShape(polygon1);
-        shapeCollection.addShape(polygon2);
+        final Point3D p1 = point(x, y, z);
+        final Point3D p2 = point(x + 20, y, z);
+        final Point3D p3 = point(x, y, z + 20);
+        final Point3D p4 = point(x + 20, y, z + 20);
+        final SolidPolygon quad = SolidPolygon.quad(p1, p2, p4, p3, SQUARE_PLATE_COLOR);
+        shapeCollection.addShape(quad);
     }
 
     /**
      * Creates a wobbly surface composed of square plates arranged in a wave pattern.
-     * @param shapeCollection the collection to add plates to
+     *
+     * @param shapeCollection  the collection to add plates to
      * @param surfaceElevation the base Y elevation of the surface
      */
     private static void addWobblySurface(final ShapeCollection shapeCollection,
@@ -75,6 +82,7 @@ public class SineHeightmap {
 
     /**
      * Entry point for the graph demo.
+     *
      * @param args command line arguments (ignored)
      */
     public static void main(final String[] args) {
@@ -83,25 +91,26 @@ public class SineHeightmap {
         final ShapeCollection geometryCollection = viewFrame.getViewPanel()
                 .getRootShapeCollection();
 
-        viewFrame.getViewPanel().getCamera().getTransform().setTranslation(new Point3D(0, 0, -500));
+        viewFrame.getViewPanel().getCamera().getTransform().setTranslation(point(0, 0, -500));
 
         addSphere(geometryCollection);
         addWobblySurface(geometryCollection, 200);
         addWobblySurface(geometryCollection, -200);
-        
+
         viewFrame.getViewPanel().repaintDuringNextViewUpdate();
     }
 
     /**
      * Adds a wireframe sphere at the center of the scene.
+     *
      * @param geometryCollection the collection to add the sphere to
      */
     private static void addSphere(ShapeCollection geometryCollection) {
-        geometryCollection.addShape(new WireframeSphere(new Point3D(0, 0, 0),
+        geometryCollection.addShape(new WireframeSphere(origin(),
                 100,
                 new LineAppearance(
                         4,
-                        new Color(255,0, 0, 30))
+                        hex("FF00001E"))
         ));
     }
 
index 676c3fc..79fa5ad 100644 (file)
@@ -1,24 +1,25 @@
 /*
- * Sixth 3D engine demos. Author: Svjatoslav Agejenko. 
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
  * This project is released under Creative Commons Zero (CC0) license.
  *
-*/
+ */
 
 package eu.svjatoslav.sixth.e3d.examples;
 
 import eu.svjatoslav.sixth.e3d.geometry.Point2D;
-import eu.svjatoslav.sixth.e3d.geometry.Point3D;
 import eu.svjatoslav.sixth.e3d.geometry.Rectangle;
 import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
 import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
 import eu.svjatoslav.sixth.e3d.gui.textEditorComponent.LookAndFeel;
 import eu.svjatoslav.sixth.e3d.gui.textEditorComponent.TextEditComponent;
 import eu.svjatoslav.sixth.e3d.math.Transform;
-import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
 import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid2D;
 
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex;
+
 /**
  * Demo showing a grid of 3D text editor components.
  * Creates a 5x5 grid of text editors floating in 3D space with a decorative grid
@@ -34,6 +35,7 @@ public class TextEditorDemo {
 
     /**
      * Entry point for the text editor demo.
+     *
      * @param args command line arguments (ignored)
      */
     public static void main(final String[] args) {
@@ -49,27 +51,28 @@ public class TextEditorDemo {
         addGrid(shapeCollection);
 
         addTextEditors(viewPanel, shapeCollection);
-        
+
         viewFrame.getViewPanel().repaintDuringNextViewUpdate();
     }
 
     /**
      * Adds a decorative grid below the text editors.
+     *
      * @param shapeCollection the collection to add the grid to
      */
     private static void addGrid(ShapeCollection shapeCollection) {
         final Transform transform = Transform.fromAngles(0, 100, 0, 0, Math.PI / 2, 0);
 
         final Rectangle rectangle = new Rectangle(2000);
-        final LineAppearance appearance = new LineAppearance(10, new Color(
-                "00b3ad"));
+        final LineAppearance appearance = new LineAppearance(10, hex("00b3ad"));
 
         shapeCollection.addShape(new Grid2D(transform, rectangle, 10, 10, appearance));
     }
 
     /**
      * Creates a grid of text editor components arranged in 3D space.
-     * @param viewPanel the view panel for the text editors
+     *
+     * @param viewPanel       the view panel for the text editors
      * @param shapeCollection the collection to add editors to
      */
     private static void addTextEditors(ViewPanel viewPanel, ShapeCollection shapeCollection) {
@@ -78,7 +81,7 @@ public class TextEditorDemo {
             for (double x = -500 * m; x <= (500 * m); x += 250 * m) {
 
                 final TextEditComponent textEditor = new TextEditComponent(
-                        new Transform(new Point3D(x, 0, z)), viewPanel,
+                        new Transform(point(x, 0, z)), viewPanel,
                         new Point2D(200, 120), new LookAndFeel());
 
                 shapeCollection.addShape(textEditor);
index 28b0246..ed38515 100644 (file)
@@ -1,20 +1,18 @@
 /*
- * Sixth 3D engine demos. Author: Svjatoslav Agejenko. 
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
  * This project is released under Creative Commons Zero (CC0) license.
  *
-*/
+ */
 
 package eu.svjatoslav.sixth.e3d.examples;
 
 import eu.svjatoslav.sixth.e3d.geometry.Point2D;
-import eu.svjatoslav.sixth.e3d.geometry.Point3D;
 import eu.svjatoslav.sixth.e3d.geometry.Rectangle;
 import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
 import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
 import eu.svjatoslav.sixth.e3d.gui.textEditorComponent.LookAndFeel;
 import eu.svjatoslav.sixth.e3d.gui.textEditorComponent.TextEditComponent;
 import eu.svjatoslav.sixth.e3d.math.Transform;
-import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
 import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid2D;
@@ -26,7 +24,9 @@ import java.io.InputStreamReader;
 import java.net.URISyntaxException;
 import java.util.stream.Collectors;
 
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
 import static eu.svjatoslav.sixth.e3d.math.Transform.fromAngles;
+import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex;
 import static java.lang.Math.PI;
 
 /**
@@ -45,6 +45,7 @@ public class TextEditorDemo2 {
 
     /**
      * Entry point for the text editor city demo.
+     *
      * @param args command line arguments (ignored)
      */
     public static void main(final String[] args) {
@@ -57,10 +58,43 @@ public class TextEditorDemo2 {
         }
     }
 
+    /**
+     * Adds a grid to the scene for visual reference.
+     *
+     * @param shapeCollection the collection to add the grid to
+     */
+    private static void addGrid(ShapeCollection shapeCollection) {
+        final Transform transform = fromAngles(0, 100, 0, 0, PI / 2, 0);
+
+        final Rectangle rectangle = new Rectangle(10000);
+        final LineAppearance appearance = new LineAppearance(10, hex("00b3ad"));
+
+        shapeCollection.addShape(new Grid2D(transform, rectangle, 50, 50, appearance));
+    }
+
+    /**
+     * Reads a resource file as a string.
+     *
+     * @param fileName the name of the resource file
+     * @return the file contents as a string, or null if not found
+     * @throws IOException if an I/O error occurs
+     */
+    static String getResourceFileAsString(String fileName) throws IOException {
+        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
+        try (InputStream is = classLoader.getResourceAsStream(fileName)) {
+            if (is == null) return null;
+            try (InputStreamReader isr = new InputStreamReader(is);
+                 BufferedReader reader = new BufferedReader(isr)) {
+                return reader.lines().collect(Collectors.joining(System.lineSeparator()));
+            }
+        }
+    }
+
     /**
      * Builds and displays the 3D city scene with text editors.
+     *
      * @throws URISyntaxException if resource URI is malformed
-     * @throws IOException if demo text file cannot be read
+     * @throws IOException        if demo text file cannot be read
      */
     public void build() throws URISyntaxException, IOException {
         final ViewFrame viewFrame = new ViewFrame("Text Editors City");
@@ -74,66 +108,53 @@ public class TextEditorDemo2 {
         addGrid(shapeCollection);
 
         addCity(viewPanel, shapeCollection);
-        
+
         viewFrame.getViewPanel().repaintDuringNextViewUpdate();
     }
 
     /**
      * Creates a grid of buildings in the city.
-     * @param viewPanel the view panel for the text editors
+     *
+     * @param viewPanel       the view panel for the text editors
      * @param shapeCollection the collection to add buildings to
      * @throws URISyntaxException if resource URI is malformed
-     * @throws IOException if demo text file cannot be read
+     * @throws IOException        if demo text file cannot be read
      */
     private void addCity(ViewPanel viewPanel, ShapeCollection shapeCollection) throws URISyntaxException, IOException {
         int citySize = 4000;
-        for (int z = -citySize; z< citySize; z+= 1000 ){
-            for (int x = -citySize; x< citySize; x+= 1000 ){
+        for (int z = -citySize; z < citySize; z += 1000) {
+            for (int x = -citySize; x < citySize; x += 1000) {
                 addBuilding(viewPanel, shapeCollection, x, z);
             }
         }
     }
 
-
-    /**
-     * Adds a grid to the scene for visual reference.
-     * @param shapeCollection the collection to add the grid to
-     */
-    private static void addGrid(ShapeCollection shapeCollection) {
-        final Transform transform = fromAngles(0, 100, 0, 0, PI / 2, 0);
-
-        final Rectangle rectangle = new Rectangle(10000);
-        final LineAppearance appearance = new LineAppearance(10, new Color(
-                "00b3ad"));
-
-        shapeCollection.addShape(new Grid2D(transform, rectangle, 50, 50, appearance));
-    }
-
-
     /**
      * Adds a single building with four text editor panels facing different directions.
-     * @param viewPanel the view panel for the text editors
+     *
+     * @param viewPanel       the view panel for the text editors
      * @param shapeCollection the collection to add the building to
-     * @param x the X coordinate of the building center
-     * @param z the Z coordinate of the building center
+     * @param x               the X coordinate of the building center
+     * @param z               the Z coordinate of the building center
      * @throws URISyntaxException if resource URI is malformed
-     * @throws IOException if demo text file cannot be read
+     * @throws IOException        if demo text file cannot be read
      */
     private void addBuilding(ViewPanel viewPanel, ShapeCollection shapeCollection, double x, double z) throws URISyntaxException, IOException {
-        addTextEditor(viewPanel, shapeCollection, new Transform(new Point3D(x, -390, z-200)));
+        addTextEditor(viewPanel, shapeCollection, new Transform(point(x, -390, z - 200)));
 
-        addTextEditor(viewPanel, shapeCollection, fromAngles(x, -390, z+200, PI, 0, 0));
+        addTextEditor(viewPanel, shapeCollection, fromAngles(x, -390, z + 200, PI, 0, 0));
 
-        addTextEditor(viewPanel, shapeCollection, fromAngles(x-200, -390, z, PI/2, 0, 0));
+        addTextEditor(viewPanel, shapeCollection, fromAngles(x - 200, -390, z, PI / 2, 0, 0));
 
-        addTextEditor(viewPanel, shapeCollection, fromAngles(x+200, -390, z, PI/2*3f, 0, 0));
+        addTextEditor(viewPanel, shapeCollection, fromAngles(x + 200, -390, z, PI / 2 * 3f, 0, 0));
     }
 
     /**
      * Adds a single text editor component at the specified transform.
-     * @param viewPanel the view panel for the text editor
+     *
+     * @param viewPanel       the view panel for the text editor
      * @param shapeCollection the collection to add the editor to
-     * @param transform the position and orientation of the editor
+     * @param transform       the position and orientation of the editor
      * @throws IOException if demo text file cannot be read
      */
     private void addTextEditor(ViewPanel viewPanel, ShapeCollection shapeCollection, Transform transform) throws IOException {
@@ -144,42 +165,26 @@ public class TextEditorDemo2 {
                 viewPanel,
                 new Point2D(400, 1000),
                 lookAndFeel
-                );
+        );
 
         String text = getResourceFileAsString("demo.txt");
 
         textEditor.setText(text);
-        textEditor.goToLine((int)(Math.random()*200f));
+        textEditor.goToLine((int) (Math.random() * 200f));
         shapeCollection.addShape(textEditor);
     }
 
     /**
      * Creates the look and feel for text editors with a dark theme.
+     *
      * @return a LookAndFeel configured with dark blue background and light text
      */
     private LookAndFeel getLookAndFeel() {
         LookAndFeel lookAndFeel = new LookAndFeel();
-        lookAndFeel.background = new Color(20, 20, 50, 150);
+        lookAndFeel.background = hex("141E3296");
         lookAndFeel.tabStopBackground = lookAndFeel.background;
-        lookAndFeel.foreground = new Color(150, 150, 255,250);
+        lookAndFeel.foreground = hex("9696FFFA");
         return lookAndFeel;
     }
 
-    /**
-     * Reads a resource file as a string.
-     * @param fileName the name of the resource file
-     * @return the file contents as a string, or null if not found
-     * @throws IOException if an I/O error occurs
-     */
-    static String getResourceFileAsString(String fileName) throws IOException {
-        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
-        try (InputStream is = classLoader.getResourceAsStream(fileName)) {
-            if (is == null) return null;
-            try (InputStreamReader isr = new InputStreamReader(is);
-                 BufferedReader reader = new BufferedReader(isr)) {
-                return reader.lines().collect(Collectors.joining(System.lineSeparator()));
-            }
-        }
-    }
-
 }
index ecb883e..f4ddb33 100644 (file)
@@ -8,7 +8,7 @@ package eu.svjatoslav.sixth.e3d.examples.benchmark;
 import eu.svjatoslav.sixth.e3d.geometry.Point2D;
 import eu.svjatoslav.sixth.e3d.geometry.Point3D;
 import eu.svjatoslav.sixth.e3d.math.Vertex;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.texturedpolygon.TexturedPolygon;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.texturedpolygon.TexturedTriangle;
 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
 import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture;
 
@@ -54,7 +54,7 @@ public class TexturedCube extends AbstractCompositeShape {
     private void addTexturedFace(Point3D p1, Point3D p2, Point3D p3, Point3D p4,
                                  Point2D t1, Point2D t2, Point2D t3, Point2D t4,
                                  Texture texture) {
-        TexturedPolygon tri1 = new TexturedPolygon(
+        TexturedTriangle tri1 = new TexturedTriangle(
                 new Vertex(p1, t1),
                 new Vertex(p2, t2),
                 new Vertex(p3, t3),
@@ -62,7 +62,7 @@ public class TexturedCube extends AbstractCompositeShape {
         );
         tri1.setBackfaceCulling(true);
 
-        TexturedPolygon tri2 = new TexturedPolygon(
+        TexturedTriangle tri2 = new TexturedTriangle(
                 new Vertex(p1, t1),
                 new Vertex(p3, t3),
                 new Vertex(p4, t4),
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/AxisArrowsDemo.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/AxisArrowsDemo.java
deleted file mode 100644 (file)
index 8bcd0a0..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
- * This project is released under Creative Commons Zero (CC0) license.
- */
-package eu.svjatoslav.sixth.e3d.examples.essentials;
-
-import eu.svjatoslav.sixth.e3d.geometry.Point3D;
-import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
-import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
-import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
-import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.ForwardOrientedTextBlock;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonArrow;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid3D;
-
-/**
- * Demo displaying coordinate system axes using 3D arrows.
- *
- * <p>This demo illustrates the Sixth 3D coordinate system with three colored
- * arrows originating from the origin (0,0,0), each pointing along a positive
- * axis direction:</p>
- *
- * <ul>
- *   <li><b>Red arrow</b> — +X axis (points RIGHT)</li>
- *   <li><b>Green arrow</b> — +Y axis (points DOWN visually)</li>
- *   <li><b>Blue arrow</b> — +Z axis (points AWAY from viewer)</li>
- * </ul>
- *
- * <p>A reference grid at Y=0 provides spatial context. Text labels identify
- * each axis.</p>
- *
- * @see SolidPolygonArrow
- * @see ForwardOrientedTextBlock
- */
-public class AxisArrowsDemo {
-
-    /**
-     * Length of each axis arrow.
-     */
-    private static final double ARROW_LENGTH = 200;
-
-    /**
-     * Radius of the arrow body (cylindrical shaft).
-     */
-    private static final double BODY_RADIUS = 6;
-
-    /**
-     * Distance from arrow tip to text label position.
-     */
-    private static final double LABEL_OFFSET = 20;
-
-    /**
-     * Scale factor for text labels.
-     */
-    private static final double LABEL_SCALE = 20.0;
-
-    /**
-     * Entry point for the axis arrows demo.
-     *
-     * @param args command line arguments (ignored)
-     */
-    public static void main(final String[] args) {
-        final ViewFrame viewFrame = new ViewFrame("Axis Arrows Demo");
-        final ViewPanel viewPanel = viewFrame.getViewPanel();
-        final ShapeCollection shapes = viewPanel.getRootShapeCollection();
-
-        // Position camera to view all three axes
-        viewPanel.getCamera().getTransform().set(130.66, -65.49, -248.18, -0.06, -0.36, -0.00);
-
-        // Create reference grid at Y=0 plane
-        createReferenceGrid(shapes);
-
-        // Create axis arrows
-        createAxisArrows(shapes);
-
-        viewPanel.repaintDuringNextViewUpdate();
-    }
-
-    /**
-     * Creates a reference grid on the Y=0 plane.
-     *
-     * @param shapes the shape collection to add the grid to
-     */
-    private static void createReferenceGrid(final ShapeCollection shapes) {
-        final LineAppearance gridAppearance = new LineAppearance(
-                1, new Color(80, 80, 100));
-        final Grid3D grid = new Grid3D(
-                new Point3D(-300, 0, -300),
-                new Point3D(300, 0, 300),
-                50,
-                gridAppearance
-        );
-        shapes.addShape(grid);
-    }
-
-    /**
-     * Creates the three axis arrows and their labels.
-     *
-     * @param shapes the shape collection to add the arrows to
-     */
-    private static void createAxisArrows(final ShapeCollection shapes) {
-        // X-axis arrow (RED) - points in positive X direction (RIGHT)
-        final SolidPolygonArrow xArrow = new SolidPolygonArrow(
-                new Point3D(0, 0, 0),
-                new Point3D(ARROW_LENGTH, 0, 0),
-                BODY_RADIUS, new Color(255, 0, 0, 180)
-        );
-        shapes.addShape(xArrow);
-        createLabel(shapes, "X", new Point3D(ARROW_LENGTH + LABEL_OFFSET, 0, 0), Color.RED);
-
-        // Y-axis arrow (GREEN) - points in positive Y direction (DOWN)
-        final SolidPolygonArrow yArrow = new SolidPolygonArrow(
-                new Point3D(0, 0, 0),
-                new Point3D(0, ARROW_LENGTH, 0),
-                BODY_RADIUS, new Color(0, 255, 0, 180)
-        );
-        shapes.addShape(yArrow);
-        createLabel(shapes, "Y", new Point3D(0, ARROW_LENGTH + LABEL_OFFSET, 0), Color.GREEN);
-
-        // Z-axis arrow (BLUE) - points in positive Z direction (AWAY)
-        final SolidPolygonArrow zArrow = new SolidPolygonArrow(
-                new Point3D(0, 0, 0),
-                new Point3D(0, 0, ARROW_LENGTH),
-                BODY_RADIUS, new Color(0, 0, 255, 180)
-        );
-        shapes.addShape(zArrow);
-        createLabel(shapes, "Z", new Point3D(0, 0, ARROW_LENGTH + LABEL_OFFSET), Color.BLUE);
-    }
-
-    /**
-     * Creates a text label at the specified position.
-     *
-     * @param shapes   the shape collection to add the label to
-     * @param text     the label text
-     * @param position the 3D position of the label
-     * @param color    the text color
-     */
-    private static void createLabel(final ShapeCollection shapes,
-                                    final String text,
-                                    final Point3D position,
-                                    final Color color) {
-        final ForwardOrientedTextBlock label = new ForwardOrientedTextBlock(
-                position, LABEL_SCALE, 2, text, color
-        );
-        shapes.addShape(label);
-    }
-}
\ No newline at end of file
index b8d2cda..6169fc0 100644 (file)
@@ -5,17 +5,21 @@
  */
 package eu.svjatoslav.sixth.e3d.examples.essentials;
 
-import eu.svjatoslav.sixth.e3d.csg.CSG;
 import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.gui.TextPointer;
 import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
 import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
+import eu.svjatoslav.sixth.e3d.math.Transform;
 import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
 import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
 import eu.svjatoslav.sixth.e3d.renderer.raster.lighting.LightSource;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.ForwardOrientedTextBlock;
 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonCube;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonMesh;
 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonSphere;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas.TextCanvas;
+
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.origin;
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex;
 
 /**
  * Demo showcasing Constructive Solid Geometry (CSG) boolean operations.
@@ -27,40 +31,35 @@ import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolyg
  *   <li><b>Intersect:</b> The volume shared by a cube and sphere</li>
  * </ul>
  *
- * <p>Shapes are rendered with slight transparency (85% opacity) to allow
- * seeing internal structure. The demo demonstrates how existing composite
- * shapes like {@link SolidPolygonCube} and {@link SolidPolygonSphere} can
- * be used with CSG operations.</p>
+ * <p>All operations use a consistent color scheme: red for the first argument
+ * (cube) and blue for the second argument (sphere). This makes it easy to see
+ * which parts of the result came from which input shape.</p>
  *
  * <p><b>Run this demo:</b></p>
  * <pre>{@code
- * java -cp target/sixth-3d-demos.jar eu.svjatoslav.sixth.e3d.examples.CSGDemo
+ * java -cp target/sixth-3d-demos.jar eu.svjatoslav.sixth.e3d.examples.essentials.CSGDemo
  * }</pre>
  *
- * @see CSG the CSG solid class
- * @see SolidPolygonMesh the renderable mesh
+ * @see eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape#union
+ * @see eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape#subtract
+ * @see eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape#intersect
  */
 public class CSGDemo {
 
     /**
-     * Distance between shapes on the X axis.
-     */
-    private static final double SPACING = 500;
-
-    /**
-     * Size of the cube (half-edge length).
+     * Color for the first argument in boolean operations (cube).
      */
-    private static final double CUBE_SIZE = 80;
+    private static final Color COLOR_FIRST = hex("FF6464D9");
 
     /**
-     * Radius of the sphere (slightly larger for interesting intersection).
+     * Color for the second argument in boolean operations (sphere).
      */
-    private static final double SPHERE_RADIUS = 96;
+    private static final Color COLOR_SECOND = hex("6496FFD9");
 
     /**
-     * Number of segments for the sphere (smoothness).
+     * Distance between shapes on the X axis.
      */
-    private static final int SPHERE_SEGMENTS = 12;
+    private static final double SPACING = 400;
 
     /**
      * Entry point for the CSG demo.
@@ -68,26 +67,23 @@ public class CSGDemo {
      * @param args command line arguments (ignored)
      */
     public static void main(final String[] args) {
-        final ViewFrame viewFrame = new ViewFrame("CSG Demo - Boolean Operations");
+        final ViewFrame viewFrame = new ViewFrame("CSG demo - Boolean operations");
         final ViewPanel viewPanel = viewFrame.getViewPanel();
         final ShapeCollection shapes = viewPanel.getRootShapeCollection();
 
         // Position camera to view all three shapes
-        viewPanel.getCamera().getTransform().set(0, -150, -600, 0, 0, 0);
+        viewPanel.getCamera().getTransform().set(-244.24, 254.40, -458.83, -0.26, 0.24, -0.00);
 
         // Set up lighting
-        viewPanel.getLightingManager().setAmbientLight(new Color(60, 60, 70));
+        viewPanel.getLightingManager().setAmbientLight(hex("3C3C46"));
 
         // Create lights
         createLights(viewPanel, shapes);
 
         // Create the three CSG demonstrations
-        createSubtractDemo(shapes, -SPACING, 0, 0);
-        createUnionDemo(shapes, 0, 0, 0);
-        createIntersectDemo(shapes, SPACING, 0, 0);
-
-        // Add labels
-        createLabels(shapes);
+        createSubtractDemo(shapes, point(-SPACING, 0, 0));
+        createUnionDemo(shapes, origin());
+        createIntersectDemo(shapes, point(SPACING, 0, 0));
 
         viewPanel.repaintDuringNextViewUpdate();
     }
@@ -96,118 +92,100 @@ public class CSGDemo {
      * Creates the subtract operation demo: cube - sphere.
      * Results in a cube with a spherical cavity.
      *
-     * @param shapes the shape collection
-     * @param x      the X position
-     * @param y      the Y position
-     * @param z      the Z position
+     * @param shapes   the shape collection
+     * @param location the position for the result
      */
-    private static void createSubtractDemo(final ShapeCollection shapes,
-                                           final double x, final double y, final double z) {
-        final SolidPolygonCube cube = new SolidPolygonCube(
-                new Point3D(0, 0, 0), CUBE_SIZE, Color.WHITE);
-        final SolidPolygonSphere sphere = new SolidPolygonSphere(
-                new Point3D(0, 0, 0), SPHERE_RADIUS, SPHERE_SEGMENTS, Color.WHITE);
-
-        final CSG cubeCSG = CSG.fromCompositeShape(cube);
-        final CSG sphereCSG = CSG.fromCompositeShape(sphere);
+    private static void createSubtractDemo(final ShapeCollection shapes, final Point3D location) {
 
-        final CSG result = cubeCSG.subtract(sphereCSG);
+        final SolidPolygonCube cube = new SolidPolygonCube(origin(), 80, COLOR_FIRST);
+        cube.subtract(new SolidPolygonSphere(origin(), 96, 12, COLOR_SECOND));
 
-        final SolidPolygonMesh mesh = result.toMesh(new Color(255, 100, 100, 217), new Point3D(x, y, z));
-        mesh.setShadingEnabled(true);
-        mesh.setBackfaceCulling(true);
+        shapes.addShape(cube
+                .setTransform(new Transform(location))
+                .setShadingEnabled(true)
+                .setBackfaceCulling(true));
 
-        shapes.addShape(mesh);
+        final String description =
+                "Subtract: Cube - Sphere\n" +
+                        "\n" +
+                        "Red = Cube (kept)\n" +
+                        "Blue = Sphere (carved out)";
 
-        System.out.println("Subtract (cube - sphere): " + mesh.getTriangleCount() + " triangles");
+        shapes.addShape(createDescriptionPanel(location, description));
     }
 
     /**
      * Creates the union operation demo: cube + sphere.
      * Results in a combined shape.
      *
-     * @param shapes the shape collection
-     * @param x      the X position
-     * @param y      the Y position
-     * @param z      the Z position
+     * @param shapes   the shape collection
+     * @param location the position for the result
      */
-    private static void createUnionDemo(final ShapeCollection shapes,
-                                        final double x, final double y, final double z) {
-        final SolidPolygonCube cube = new SolidPolygonCube(
-                new Point3D(0, 0, 0), CUBE_SIZE, Color.WHITE);
-        final SolidPolygonSphere sphere = new SolidPolygonSphere(
-                new Point3D(0, 0, 0), SPHERE_RADIUS, SPHERE_SEGMENTS, Color.WHITE);
+    private static void createUnionDemo(final ShapeCollection shapes, final Point3D location) {
 
-        final CSG cubeCSG = CSG.fromCompositeShape(cube);
-        final CSG sphereCSG = CSG.fromCompositeShape(sphere);
+        final SolidPolygonCube cube = new SolidPolygonCube(origin(), 80, COLOR_FIRST);
+        cube.union(new SolidPolygonSphere(origin(), 96, 12, COLOR_SECOND));
 
-        final CSG result = cubeCSG.union(sphereCSG);
+        shapes.addShape(cube
+                .setTransform(new Transform(location))
+                .setShadingEnabled(true)
+                .setBackfaceCulling(true));
 
-        final SolidPolygonMesh mesh = result.toMesh(new Color(100, 255, 100, 217), new Point3D(x, y, z));
-        mesh.setShadingEnabled(true);
-        mesh.setBackfaceCulling(true);
+        final String description =
+                "Union: Cube + Sphere\n" +
+                        "\n" +
+                        "Red = Cube\n" +
+                        "Blue = Sphere";
 
-        shapes.addShape(mesh);
-
-        System.out.println("Union (cube + sphere): " + mesh.getTriangleCount() + " triangles");
+        shapes.addShape(createDescriptionPanel(location, description));
     }
 
     /**
      * Creates the intersect operation demo: cube ∩ sphere.
      * Results in the volume shared by both shapes.
      *
-     * @param shapes the shape collection
-     * @param x      the X position
-     * @param y      the Y position
-     * @param z      the Z position
+     * @param shapes   the shape collection
+     * @param location the position for the result
      */
-    private static void createIntersectDemo(final ShapeCollection shapes,
-                                            final double x, final double y, final double z) {
-        final SolidPolygonCube cube = new SolidPolygonCube(
-                new Point3D(0, 0, 0), CUBE_SIZE, Color.WHITE);
-        final SolidPolygonSphere sphere = new SolidPolygonSphere(
-                new Point3D(0, 0, 0), SPHERE_RADIUS, SPHERE_SEGMENTS, Color.WHITE);
-
-        final CSG cubeCSG = CSG.fromCompositeShape(cube);
-        final CSG sphereCSG = CSG.fromCompositeShape(sphere);
+    private static void createIntersectDemo(final ShapeCollection shapes, final Point3D location) {
 
-        final CSG result = cubeCSG.intersect(sphereCSG);
+        final SolidPolygonCube cube = new SolidPolygonCube(origin(), 80, COLOR_FIRST);
+        cube.intersect(new SolidPolygonSphere(origin(), 96, 12, COLOR_SECOND));
 
-        final SolidPolygonMesh mesh = result.toMesh(new Color(100, 150, 255, 217), new Point3D(x, y, z));
-        mesh.setShadingEnabled(true);
-        mesh.setBackfaceCulling(true);
+        shapes.addShape(cube
+                .setTransform(new Transform(location))
+                .setShadingEnabled(true)
+                .setBackfaceCulling(true));
 
-        shapes.addShape(mesh);
+        final String description =
+                "Intersect: Cube ∩ Sphere\n" +
+                        "\n" +
+                        "Red = from Cube\n" +
+                        "Blue = from Sphere";
 
-        System.out.println("Intersect (cube ∩ sphere): " + mesh.getTriangleCount() + " triangles");
+        shapes.addShape(createDescriptionPanel(location, description));
     }
 
     /**
-     * Creates labels for each operation.
+     * Creates a description panel positioned below a shape.
      *
-     * @param shapes the shape collection
+     * @param location the position of the associated shape
+     * @param text     the description text to display
+     * @return a TextCanvas positioned below the shape
      */
-    private static void createLabels(final ShapeCollection shapes) {
-        final double labelY = -150;
-        final double labelZ = 0;
-
-        // Subtract label
-        shapes.addShape(new ForwardOrientedTextBlock(
-                new Point3D(-SPACING, labelY, labelZ),
-                8.0, 2, "Subtract", Color.RED
-        ));
-
-        // Union label
-        shapes.addShape(new ForwardOrientedTextBlock(
-                new Point3D(0, labelY, labelZ),
-                8.0, 2, "Union", Color.GREEN
-        ));
-
-        // Intersect label
-        shapes.addShape(new ForwardOrientedTextBlock(
-                new Point3D(SPACING, labelY, labelZ),
-                8.0, 2, "Intersect", Color.BLUE
-        ));
+    private static TextCanvas createDescriptionPanel(final Point3D location, final String text) {
+        final Transform transform = Transform.fromAngles(
+                point(location.x, location.y + 220, location.z),
+                0, 0);
+
+        final TextCanvas panel = new TextCanvas(
+                transform,
+                new TextPointer(5, 35),
+                Color.WHITE,
+                new Color(0, 0, 40, 180));
+
+        panel.setText(text);
+        return panel;
     }
 
     /**
@@ -219,16 +197,16 @@ public class CSGDemo {
     private static void createLights(final ViewPanel viewPanel, final ShapeCollection shapes) {
         // Main light from above-front
         final LightSource mainLight = new LightSource(
-                new Point3D(0, -300, -400),
-                new Color(255, 255, 255),
+                point(0, -300, -400),
+                hex("FFFFFF"),
                 1.5
         );
         viewPanel.getLightingManager().addLight(mainLight);
 
         // Fill light from the side
         final LightSource fillLight = new LightSource(
-                new Point3D(500, 100, -200),
-                new Color(150, 150, 200),
+                point(500, 100, -200),
+                hex("9696C8"),
                 0.8
         );
         viewPanel.getLightingManager().addLight(fillLight);
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/CoordinateSystemDemo.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/CoordinateSystemDemo.java
new file mode 100644 (file)
index 0000000..dafd300
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+package eu.svjatoslav.sixth.e3d.examples.essentials;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
+import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.ForwardOrientedTextBlock;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonArrow;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid3D;
+
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.origin;
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex;
+
+/**
+ * Demo displaying coordinate system axes using 3D arrows.
+ *
+ * <p>This demo illustrates the Sixth 3D coordinate system with three colored
+ * arrows originating from the origin (0,0,0), each pointing along a positive
+ * axis direction:</p>
+ *
+ * <ul>
+ *   <li><b>Red arrow</b> — +X axis (points RIGHT)</li>
+ *   <li><b>Green arrow</b> — +Y axis (points DOWN visually)</li>
+ *   <li><b>Blue arrow</b> — +Z axis (points AWAY from viewer)</li>
+ * </ul>
+ *
+ * <p>A reference grid at Y=0 provides spatial context. Text labels identify
+ * each axis.</p>
+ *
+ * @see SolidPolygonArrow
+ * @see ForwardOrientedTextBlock
+ */
+public class CoordinateSystemDemo {
+
+    /**
+     * Length of each axis arrow.
+     */
+    private static final double ARROW_LENGTH = 200;
+
+    /**
+     * Radius of the arrow body (cylindrical shaft).
+     */
+    private static final double BODY_RADIUS = 6;
+
+    /**
+     * Distance from arrow tip to text label position.
+     */
+    private static final double LABEL_OFFSET = 20;
+
+    /**
+     * Scale factor for text labels.
+     */
+    private static final double LABEL_SCALE = 20.0;
+
+    /**
+     * Entry point for the coordinate system demo.
+     *
+     * @param args command line arguments (ignored)
+     */
+    public static void main(final String[] args) {
+        final ViewFrame viewFrame = new ViewFrame("Coordinate System Demo");
+        final ViewPanel viewPanel = viewFrame.getViewPanel();
+        final ShapeCollection shapes = viewPanel.getRootShapeCollection();
+
+        // Position camera to view all three axes
+        viewPanel.getCamera().getTransform().set(130.66, -65.49, -248.18, -0.06, -0.36, -0.00);
+
+        // Create reference grid at Y=0 plane
+        createReferenceGrid(shapes);
+
+        // Create axis arrows
+        createAxisArrows(shapes);
+
+        viewPanel.repaintDuringNextViewUpdate();
+    }
+
+    /**
+     * Creates a reference grid on the Y=0 plane.
+     *
+     * @param shapes the shape collection to add the grid to
+     */
+    private static void createReferenceGrid(final ShapeCollection shapes) {
+        final LineAppearance gridAppearance = new LineAppearance(
+                1, hex("505064FF"));
+        final Grid3D grid = new Grid3D(
+                point(-300, 0, -300),
+                point(300, 0, 300),
+                50,
+                gridAppearance
+        );
+        shapes.addShape(grid);
+    }
+
+    /**
+     * Creates the three axis arrows and their labels.
+     *
+     * @param shapes the shape collection to add the arrows to
+     */
+    private static void createAxisArrows(final ShapeCollection shapes) {
+        // X-axis arrow (RED) - points in positive X direction (RIGHT)
+        final SolidPolygonArrow xArrow = new SolidPolygonArrow(
+                origin(),
+                point(ARROW_LENGTH, 0, 0),
+                BODY_RADIUS, hex("FF0000B4")
+        );
+        shapes.addShape(xArrow);
+        createLabel(shapes, "X", point(ARROW_LENGTH + LABEL_OFFSET, 0, 0), Color.RED);
+
+        // Y-axis arrow (GREEN) - points in positive Y direction (DOWN)
+        final SolidPolygonArrow yArrow = new SolidPolygonArrow(
+                origin(),
+                point(0, ARROW_LENGTH, 0),
+                BODY_RADIUS, hex("00FF00B4")
+        );
+        shapes.addShape(yArrow);
+        createLabel(shapes, "Y", point(0, ARROW_LENGTH + LABEL_OFFSET, 0), Color.GREEN);
+
+        // Z-axis arrow (BLUE) - points in positive Z direction (AWAY)
+        final SolidPolygonArrow zArrow = new SolidPolygonArrow(
+                origin(),
+                point(0, 0, ARROW_LENGTH),
+                BODY_RADIUS, hex("0000FFB4")
+        );
+        shapes.addShape(zArrow);
+        createLabel(shapes, "Z", point(0, 0, ARROW_LENGTH + LABEL_OFFSET), Color.BLUE);
+    }
+
+    /**
+     * Creates a text label at the specified position.
+     *
+     * @param shapes   the shape collection to add the label to
+     * @param text     the label text
+     * @param position the 3D position of the label
+     * @param color    the text color
+     */
+    private static void createLabel(final ShapeCollection shapes,
+                                    final String text,
+                                    final Point3D position,
+                                    final Color color) {
+        final ForwardOrientedTextBlock label = new ForwardOrientedTextBlock(
+                position, LABEL_SCALE, 2, text, color
+        );
+        shapes.addShape(label);
+    }
+}
\ No newline at end of file
index b526eb3..aa4347c 100644 (file)
@@ -5,13 +5,14 @@
  */
 package eu.svjatoslav.sixth.e3d.examples.essentials;
 
-import eu.svjatoslav.sixth.e3d.geometry.Point3D;
 import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
 import eu.svjatoslav.sixth.e3d.math.Transform;
 import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
 import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonRectangularBox;
 
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+
 /**
  * Minimal example demonstrating how to create a basic 3D scene.
  * <p>
@@ -29,12 +30,12 @@ public class MinimalExample {
         ViewFrame viewFrame = new ViewFrame("Minimal example");
         ShapeCollection shapes = viewFrame.getViewPanel().getRootShapeCollection();
 
-        viewFrame.getViewPanel().getCamera().getTransform().setTranslation(new Point3D(0, -100, -300));
+        viewFrame.getViewPanel().getCamera().getTransform().setTranslation(point(0, -100, -300));
 
-        Transform boxTransform = Transform.fromAngles(0, 0, 0, 0, 0, 0);
+        final Transform boxTransform = new Transform();
         SolidPolygonRectangularBox box = new SolidPolygonRectangularBox(
-                new Point3D(-50, -50, -50),
-                new Point3D(50, 50, 50),
+                point(-50, -50, -50),
+                point(50, 50, 50),
                 Color.RED
         );
         box.setTransform(boxTransform);
index 28e6337..9efa14b 100644 (file)
@@ -17,7 +17,9 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Random;
 
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
 import static eu.svjatoslav.sixth.e3d.math.Transform.fromAngles;
+import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex;
 
 /**
  * Gallery demo showcasing all available 3D shapes in the Sixth 3D engine.
@@ -69,13 +71,13 @@ public class ShapeGalleryDemo {
      * Color palette - one color per column (shape type).
      */
     private static final Color[] SHAPE_COLORS = {
-            new Color(255, 100, 100),  // Arrow - Red
-            new Color(255, 180, 100),  // Cone - Orange
-            new Color(255, 255, 100),  // Cube - Yellow
-            new Color(100, 255, 100),  // Cylinder - Green
-            new Color(100, 255, 255),  // Pyramid - Cyan
-            new Color(100, 150, 255),  // RectangularBox - Blue
-            new Color(200, 150, 255),  // Sphere - Purple
+            hex("FF6464FF"),  // Arrow - Red
+            hex("FFB464FF"),  // Cone - Orange
+            hex("FFFF64FF"),  // Cube - Yellow
+            hex("64FF64FF"),  // Cylinder - Green
+            hex("64FFFFFF"),  // Pyramid - Cyan
+            hex("6496FFFF"),  // RectangularBox - Blue
+            hex("C896FFFF"),  // Sphere - Purple
     };
 
     /**
@@ -105,7 +107,7 @@ public class ShapeGalleryDemo {
         viewPanel.getCamera().getTransform().set(-615.31, -299.50, -378.64, -0.62, -0.66, 0.00);
 
         // Set up lighting
-        viewPanel.getLightingManager().setAmbientLight(new Color(40, 40, 50));
+        viewPanel.getLightingManager().setAmbientLight(hex("282832FF"));
 
         // Create floor grid
         createFloorGrid(shapes);
@@ -125,14 +127,14 @@ public class ShapeGalleryDemo {
      * @param shapes the shape collection to add the grid to
      */
     private static void createFloorGrid(final ShapeCollection shapes) {
-        final LineAppearance gridAppearance = new LineAppearance(1, new Color(80, 80, 100));
+        final LineAppearance gridAppearance = new LineAppearance(1, hex("505064FF"));
 
         // Calculate grid bounds: 7 columns, 2 rows, centered around origin
         final double halfWidth = (COLUMN_COUNT * CELL_SIZE) / 2.0;
         final double gridDepth = 2 * CELL_SIZE;
 
-        final Point3D cornerA = new Point3D(-halfWidth, 0, -CELL_SIZE / 2);
-        final Point3D cornerB = new Point3D(halfWidth, 0, gridDepth - CELL_SIZE / 2);
+        final Point3D cornerA = point(-halfWidth, 0, -CELL_SIZE / 2);
+        final Point3D cornerB = point(halfWidth, 0, gridDepth - CELL_SIZE / 2);
 
         final Grid3D grid = new Grid3D(cornerA, cornerB, CELL_SIZE, gridAppearance);
         shapes.addShape(grid);
@@ -150,12 +152,12 @@ public class ShapeGalleryDemo {
             final String name = SHAPE_NAMES[col];
 
             // Row 0: Solid polygon shapes
-            final Point3D solidPos = new Point3D(x, 0, 0);
+            final Point3D solidPos = point(x, 0, 0);
             createSolidShape(shapes, col, solidPos, color);
             createLabel(shapes, solidPos, name, color);
 
             // Row 1: Wireframe shapes
-            final Point3D wireframePos = new Point3D(x, 0, CELL_SIZE);
+            final Point3D wireframePos = point(x, 0, CELL_SIZE);
             createWireframeShape(shapes, col, wireframePos, color);
             createLabel(shapes, wireframePos, "Wireframe " + name, color);
         }
@@ -184,8 +186,8 @@ public class ShapeGalleryDemo {
         switch (col) {
             case 0: // Arrow
                 final SolidPolygonArrow arrow = new SolidPolygonArrow(
-                        new Point3D(pos.x, SHAPE_RADIUS, pos.z),
-                        new Point3D(pos.x, -SHAPE_RADIUS, pos.z),
+                        point(pos.x, SHAPE_RADIUS, pos.z),
+                        point(pos.x, -SHAPE_RADIUS, pos.z),
                         8, color);
                 arrow.setShadingEnabled(true);
                 shapes.addShape(arrow);
@@ -193,8 +195,8 @@ public class ShapeGalleryDemo {
 
             case 1: // Cone (pointing upward)
                 final SolidPolygonCone cone = new SolidPolygonCone(
-                        new Point3D(pos.x, -SHAPE_RADIUS, pos.z),  // apex above
-                        new Point3D(pos.x, SHAPE_RADIUS / 2, pos.z), // base below
+                        point(pos.x, -SHAPE_RADIUS, pos.z),  // apex above
+                        point(pos.x, SHAPE_RADIUS / 2, pos.z), // base below
                         SHAPE_RADIUS * 0.8, SEGMENTS, color);
                 cone.setShadingEnabled(true);
                 shapes.addShape(cone);
@@ -208,8 +210,8 @@ public class ShapeGalleryDemo {
 
             case 3: // Cylinder
                 final SolidPolygonCylinder cylinder = new SolidPolygonCylinder(
-                        new Point3D(pos.x, SHAPE_RADIUS, pos.z),
-                        new Point3D(pos.x, -SHAPE_RADIUS, pos.z),
+                        point(pos.x, SHAPE_RADIUS, pos.z),
+                        point(pos.x, -SHAPE_RADIUS, pos.z),
                         SHAPE_RADIUS * 0.7, SEGMENTS, color);
                 cylinder.setShadingEnabled(true);
                 shapes.addShape(cylinder);
@@ -217,8 +219,8 @@ public class ShapeGalleryDemo {
 
             case 4: // Pyramid
                 final SolidPolygonPyramid pyramid = new SolidPolygonPyramid(
-                        new Point3D(pos.x, -SHAPE_RADIUS, pos.z),  // apex above
-                        new Point3D(pos.x, SHAPE_RADIUS / 2, pos.z), // base below
+                        point(pos.x, -SHAPE_RADIUS, pos.z),  // apex above
+                        point(pos.x, SHAPE_RADIUS / 2, pos.z), // base below
                         SHAPE_RADIUS * 0.8, color);
                 pyramid.setShadingEnabled(true);
                 shapes.addShape(pyramid);
@@ -226,8 +228,8 @@ public class ShapeGalleryDemo {
 
             case 5: // RectangularBox
                 final SolidPolygonRectangularBox box = new SolidPolygonRectangularBox(
-                        new Point3D(pos.x - SHAPE_RADIUS * 0.35, pos.y - SHAPE_RADIUS, pos.z - SHAPE_RADIUS * 0.35),
-                        new Point3D(pos.x + SHAPE_RADIUS * 0.35, pos.y + SHAPE_RADIUS, pos.z + SHAPE_RADIUS * 0.35),
+                        point(pos.x - SHAPE_RADIUS * 0.35, pos.y - SHAPE_RADIUS, pos.z - SHAPE_RADIUS * 0.35),
+                        point(pos.x + SHAPE_RADIUS * 0.35, pos.y + SHAPE_RADIUS, pos.z + SHAPE_RADIUS * 0.35),
                         color);
                 box.setShadingEnabled(true);
                 shapes.addShape(box);
@@ -259,16 +261,16 @@ public class ShapeGalleryDemo {
         switch (col) {
             case 0: // Arrow
                 final WireframeArrow arrow = new WireframeArrow(
-                        new Point3D(pos.x, SHAPE_RADIUS, pos.z),
-                        new Point3D(pos.x, -SHAPE_RADIUS, pos.z),
+                        point(pos.x, SHAPE_RADIUS, pos.z),
+                        point(pos.x, -SHAPE_RADIUS, pos.z),
                         8, appearance);
                 shapes.addShape(arrow);
                 break;
 
             case 1: // Cone
                 final WireframeCone cone = new WireframeCone(
-                        new Point3D(pos.x, -SHAPE_RADIUS, pos.z),  // apex above
-                        new Point3D(pos.x, SHAPE_RADIUS / 2, pos.z), // base below
+                        point(pos.x, -SHAPE_RADIUS, pos.z),  // apex above
+                        point(pos.x, SHAPE_RADIUS / 2, pos.z), // base below
                         SHAPE_RADIUS * 0.8, SEGMENTS, appearance);
                 shapes.addShape(cone);
                 break;
@@ -280,24 +282,24 @@ public class ShapeGalleryDemo {
 
             case 3: // Cylinder
                 final WireframeCylinder cylinder = new WireframeCylinder(
-                        new Point3D(pos.x, SHAPE_RADIUS, pos.z),
-                        new Point3D(pos.x, -SHAPE_RADIUS, pos.z),
+                        point(pos.x, SHAPE_RADIUS, pos.z),
+                        point(pos.x, -SHAPE_RADIUS, pos.z),
                         SHAPE_RADIUS * 0.7, SEGMENTS, appearance);
                 shapes.addShape(cylinder);
                 break;
 
             case 4: // Pyramid
                 final WireframePyramid pyramid = new WireframePyramid(
-                        new Point3D(pos.x, -SHAPE_RADIUS, pos.z),  // apex above
-                        new Point3D(pos.x, SHAPE_RADIUS / 2, pos.z), // base below
+                        point(pos.x, -SHAPE_RADIUS, pos.z),  // apex above
+                        point(pos.x, SHAPE_RADIUS / 2, pos.z), // base below
                         SHAPE_RADIUS * 0.8, appearance);
                 shapes.addShape(pyramid);
                 break;
 
             case 5: // Box (WireframeBox)
                 final WireframeBox box = new WireframeBox(
-                        new Point3D(pos.x - SHAPE_RADIUS * 0.35, pos.y - SHAPE_RADIUS, pos.z - SHAPE_RADIUS * 0.35),
-                        new Point3D(pos.x + SHAPE_RADIUS * 0.35, pos.y + SHAPE_RADIUS, pos.z + SHAPE_RADIUS * 0.35),
+                        point(pos.x - SHAPE_RADIUS * 0.35, pos.y - SHAPE_RADIUS, pos.z - SHAPE_RADIUS * 0.35),
+                        point(pos.x + SHAPE_RADIUS * 0.35, pos.y + SHAPE_RADIUS, pos.z + SHAPE_RADIUS * 0.35),
                         appearance);
                 shapes.addShape(box);
                 break;
@@ -322,7 +324,7 @@ public class ShapeGalleryDemo {
      */
     private static void createLabel(final ShapeCollection shapes, final Point3D pos,
                                     final String text, final Color color) {
-        final Point3D labelPos = new Point3D(pos.x, pos.y + LABEL_Y_OFFSET, pos.z);
+        final Point3D labelPos = point(pos.x, pos.y + LABEL_Y_OFFSET, pos.z);
         final ForwardOrientedTextBlock label = new ForwardOrientedTextBlock(
                 labelPos, 8.0, 2, text, color);
         shapes.addShape(label);
@@ -353,7 +355,7 @@ public class ShapeGalleryDemo {
             final int axis = random.nextInt(3);
             final double ellipseFactor = 0.7 + random.nextDouble() * 0.3;
 
-            final LightSource light = new LightSource(new Point3D(0, 0, CELL_SIZE / 2), color, intensity);
+            final LightSource light = new LightSource(point(0, 0, CELL_SIZE / 2), color, intensity);
             final LightSourceMarker marker = new LightSourceMarker(light.getPosition(), color);
 
             viewPanel.getLightingManager().addLight(light);
@@ -428,7 +430,7 @@ public class ShapeGalleryDemo {
                         break;
                 }
 
-                final Point3D newPosition = new Point3D(x, y + centerY, z);
+                final Point3D newPosition = point(x, y + centerY, z);
                 orbitingLight.light.setPosition(newPosition);
                 orbitingLight.marker.setTransform(fromAngles(
                         newPosition.x, newPosition.y, newPosition.z, 0, 0, 0));
index 250fc62..fceba7b 100644 (file)
@@ -12,6 +12,8 @@ import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
 import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon;
 
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+
 /**
  * Demo to test winding order and backface culling documentation.
  * <p>
@@ -36,17 +38,17 @@ public class WindingOrderDemo {
      * @param args command line arguments (ignored)
      */
     public static void main(String[] args) {
-        ViewFrame viewFrame = new ViewFrame("Winding Order Demo");
+        ViewFrame viewFrame = new ViewFrame("Winding order demo");
         ViewPanel viewPanel = viewFrame.getViewPanel();
         ShapeCollection shapes = viewPanel.getRootShapeCollection();
 
-        viewPanel.getCamera().getTransform().setTranslation(new Point3D(0, 0, -500));
+        viewPanel.getCamera().getTransform().setTranslation(point(0, 0, -500));
 
         double size = 150;
 
-        Point3D upperCenter = new Point3D(0, -size, 0);
-        Point3D lowerLeft = new Point3D(-size, +size, 0);
-        Point3D lowerRight = new Point3D(+size, +size, 0);
+        Point3D upperCenter = point(0, -size, 0);
+        Point3D lowerLeft = point(-size, +size, 0);
+        Point3D lowerRight = point(+size, +size, 0);
 
         SolidPolygon triangle = new SolidPolygon(upperCenter, lowerLeft, lowerRight, Color.GREEN);
         triangle.setBackfaceCulling(true);
index 1f28002..508fb7c 100644 (file)
@@ -10,6 +10,9 @@ import eu.svjatoslav.sixth.e3d.geometry.Point2D;
 import eu.svjatoslav.sixth.e3d.geometry.Point3D;
 import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
 import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.origin;
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
 import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.Graph;
 
@@ -163,22 +166,22 @@ public class MathGraphsDemo {
      */
     private static void addMathFormulas(ShapeCollection geometryCollection) {
         int z = 1000;
-        Point3D location = new Point3D(-600, -300, z);
+        Point3D location = point(-600, -300, z);
         geometryCollection.addShape(getSineGraph(location));
 
-        location = new Point3D(600, -300, z);
+        location = point(600, -300, z);
         geometryCollection.addShape(getFormula1Graph(location));
 
-        location = new Point3D(-600, 0, z);
+        location = point(-600, 0, z);
         geometryCollection.addShape(getCosineGraph(location));
 
-        location = new Point3D(600, 0, z);
+        location = point(600, 0, z);
         geometryCollection.addShape(getFormula2Graph(location));
 
-        location = new Point3D(-600, 300, z);
+        location = point(-600, 300, z);
         geometryCollection.addShape(getTangentGraph(location));
 
-        location = new Point3D(600, 300, z);
+        location = point(600, 300, z);
         geometryCollection.addShape(getFormula3Graph(location));
     }
 
@@ -196,7 +199,7 @@ public class MathGraphsDemo {
                 new Color(150, 150, 150, 180),
                 Color.WHITE,
                 scale,
-                new Point3D(0, 0, 0)
+                origin()
         );
 
         surface.addYIntersectionCurve(4, function, new Color(255, 255, 0, 220), 1.2);
index 661a854..c4507d3 100644 (file)
@@ -122,13 +122,9 @@ public class SurfaceGraph3D extends AbstractCompositeShape {
     }
 
     private void addQuad(final Point3D p1, final Point3D p2, final Point3D p3, final Point3D p4) {
-        final SolidPolygon poly1 = new SolidPolygon(p1, p2, p3, surfaceColor);
-        poly1.setBackfaceCulling(false);
-        addShape(poly1);
-
-        final SolidPolygon poly2 = new SolidPolygon(p2, p4, p3, surfaceColor);
-        poly2.setBackfaceCulling(false);
-        addShape(poly2);
+        final SolidPolygon quad = SolidPolygon.quad(p1, p2, p4, p3, surfaceColor);
+        quad.setBackfaceCulling(false);
+        addShape(quad);
     }
 
     private void addGridLines(final Point3D p1, final Point3D p2, final Point3D p3, final Point3D p4) {
index 86cda8a..550e60d 100644 (file)
@@ -11,7 +11,7 @@ import eu.svjatoslav.sixth.e3d.examples.RainingNumbersDemo;
 import eu.svjatoslav.sixth.e3d.examples.SineHeightmap;
 import eu.svjatoslav.sixth.e3d.examples.TextEditorDemo;
 import eu.svjatoslav.sixth.e3d.examples.TextEditorDemo2;
-import eu.svjatoslav.sixth.e3d.examples.essentials.AxisArrowsDemo;
+import eu.svjatoslav.sixth.e3d.examples.essentials.CoordinateSystemDemo;
 import eu.svjatoslav.sixth.e3d.examples.essentials.CSGDemo;
 import eu.svjatoslav.sixth.e3d.examples.essentials.MinimalExample;
 import eu.svjatoslav.sixth.e3d.examples.essentials.ShapeGalleryDemo;
@@ -44,13 +44,13 @@ class ApplicationListPanel extends JPanel {
             new DemoEntry("Winding order",
                     "Triangle demo for winding order & backface culling",
                     new ShowWindingOrder()),
-            new DemoEntry("Axis arrows",
+            new DemoEntry("Coordinate system",
                     "Coordinate system axes: X (red), Y (green), Z (blue)",
-                    new ShowAxisArrows()),
+                    new ShowCoordinateSystem()),
             new DemoEntry("Shape gallery",
                     "All 3D shapes with orbiting colored light sources",
                     new ShowShadedShapes()),
-            new DemoEntry("CSG Demo",
+            new DemoEntry("CSG demo",
                     "Boolean operations: union, subtract, intersect on 3D shapes",
                     new ShowCSG()),
     };
@@ -174,7 +174,7 @@ class ApplicationListPanel extends JPanel {
 
     private static class ShowMinimalExample extends AbstractAction {
         ShowMinimalExample() {
-            putValue(NAME, "Minimal Example");
+            putValue(NAME, "Minimal example");
         }
 
         @Override
@@ -185,7 +185,7 @@ class ApplicationListPanel extends JPanel {
 
     private static class ShowWindingOrder extends AbstractAction {
         ShowWindingOrder() {
-            putValue(NAME, "Winding Order");
+            putValue(NAME, "Winding order");
         }
 
         @Override
@@ -293,14 +293,14 @@ class ApplicationListPanel extends JPanel {
         }
     }
 
-    private static class ShowAxisArrows extends AbstractAction {
-        ShowAxisArrows() {
-            putValue(NAME, "Axis arrows");
+    private static class ShowCoordinateSystem extends AbstractAction {
+        ShowCoordinateSystem() {
+            putValue(NAME, "Coordinate system");
         }
 
         @Override
         public void actionPerformed(final ActionEvent e) {
-            AxisArrowsDemo.main(null);
+            CoordinateSystemDemo.main(null);
         }
     }
 
@@ -328,7 +328,7 @@ class ApplicationListPanel extends JPanel {
 
     private static class ShowCSG extends AbstractAction {
         ShowCSG() {
-            putValue(NAME, "CSG Demo");
+            putValue(NAME, "CSG demo");
         }
 
         @Override
index 80622ff..c9be293 100755 (executable)
@@ -15,14 +15,16 @@ import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCom
 class Cell extends AbstractCompositeShape implements
         MouseInteractionController {
 
-    /** Visual size of each cell in world units. */
+    /**
+     * Visual size of each cell in world units.
+     */
     static final int SIZE = 20;
     /**
      * Color of the active cell (R, G, B, A)
      */
     private static final Color ACTIVE_COLOR = new Color("A8FF");
     /**
-     * Color of the active cell (R, G, B, A) while mouse is over it.
+     * Color of the active cell (R, G, B, A) while the mouse is over it.
      */
     private static final Color ACTIVE_COLOR_MOUSE_OVER = new Color("F9FF");
     /**
@@ -49,6 +51,7 @@ class Cell extends AbstractCompositeShape implements
 
     /**
      * Constructs a cell at the specified center position.
+     *
      * @param center the 3D position of the cell center
      */
     public Cell(final Point3D center) {
@@ -60,7 +63,9 @@ class Cell extends AbstractCompositeShape implements
         setMouseInteractionController(this);
     }
 
-    /** Creates the visual representation of the cell as two triangles. */
+    /**
+     * Creates the visual representation of the cell as two triangles.
+     */
     private void createCellShape() {
         final double halfSize = SIZE / 2f;
 
@@ -80,6 +85,7 @@ class Cell extends AbstractCompositeShape implements
 
     /**
      * Computes the cell color based on active state and mouse hover.
+     *
      * @return the appropriate color for the current state
      */
     private Color computeCellColor() {
@@ -96,6 +102,7 @@ class Cell extends AbstractCompositeShape implements
 
     /**
      * Returns whether this cell is currently active (alive).
+     *
      * @return true if the cell is active
      */
     public boolean isActive() {
@@ -104,6 +111,7 @@ class Cell extends AbstractCompositeShape implements
 
     /**
      * Sets the active state of this cell and updates its color.
+     *
      * @param active true to make the cell active, false for inactive
      */
     public void setActive(final boolean active) {
@@ -113,6 +121,7 @@ class Cell extends AbstractCompositeShape implements
 
     /**
      * Handles mouse click by toggling the cell state.
+     *
      * @param button the mouse button that was clicked
      * @return true to indicate the event was consumed
      */
@@ -124,6 +133,7 @@ class Cell extends AbstractCompositeShape implements
 
     /**
      * Handles mouse entering the cell area by highlighting it.
+     *
      * @return true to indicate the event was consumed
      */
     @Override
@@ -134,6 +144,7 @@ class Cell extends AbstractCompositeShape implements
 
     /**
      * Handles mouse exiting the cell area by removing highlight.
+     *
      * @return true to indicate the event was consumed
      */
     @Override
@@ -144,6 +155,7 @@ class Cell extends AbstractCompositeShape implements
 
     /**
      * Sets the mouse-over state and updates the cell color.
+     *
      * @param isMouseOver true if mouse is over the cell
      */
     private void setMouseOver(final boolean isMouseOver) {
@@ -151,7 +163,9 @@ class Cell extends AbstractCompositeShape implements
         updateColor();
     }
 
-    /** Updates the cell's visual color to match its current state. */
+    /**
+     * Updates the cell's visual color to match its current state.
+     */
     private void updateColor() {
         setColor(computeCellColor());
     }
index 0e77299..d60dad7 100644 (file)
@@ -8,6 +8,10 @@ import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
 import eu.svjatoslav.sixth.e3d.gui.humaninput.WorldNavigationUserInputTracker;
 import eu.svjatoslav.sixth.e3d.math.Transform;
 import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.origin;
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex;
 import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas.TextCanvas;
@@ -34,7 +38,7 @@ public class Main extends WorldNavigationUserInputTracker {
      * The game of life matrix, centered at the origin.
      */
     private static final Matrix MATRIX = new Matrix(
-            new Point3D() // position matrix in the center of the scene
+            origin() // position matrix in the center of the scene
     );
 
     /**
@@ -116,7 +120,7 @@ public class Main extends WorldNavigationUserInputTracker {
                 5, 5,
 
                 new LineAppearance(3,
-                        new Color("FF000050")
+                        hex("FF000050")
                 )
         );
     }
@@ -147,7 +151,7 @@ public class Main extends WorldNavigationUserInputTracker {
                         "Hover  - Highlight cell";
 
         final Transform location = Transform.fromAngles(
-                new Point3D(500, -80, 0),
+                point(500, -80, 0),
                 -Math.PI / 2,
                 0
         );
@@ -158,7 +162,7 @@ public class Main extends WorldNavigationUserInputTracker {
                 location,
                 dimensions,
                 Color.WHITE,
-                new Color(0, 0, 80, 200)
+                hex("000050C8")
         );
 
         panel.setText(helpText);
index 1ad9d43..1bf03ec 100644 (file)
@@ -9,6 +9,10 @@ import eu.svjatoslav.sixth.e3d.geometry.Point3D;
 import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
 import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
 import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.origin;
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex;
 import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
 import eu.svjatoslav.sixth.e3d.renderer.raster.lighting.LightSource;
 import eu.svjatoslav.sixth.e3d.renderer.raster.lighting.LightingManager;
@@ -41,7 +45,7 @@ public class TerrainDemo {
 
         // Get terrain height at center and place tree trunk on it
         final double terrainHeight = terrain.getHeightAt(0, 0);
-        shapes.addShape(new FractalTree(new Point3D(0, terrainHeight, 0), 30, 6));
+        shapes.addShape(new FractalTree(point(0, terrainHeight, 0), 30, 6));
 
         viewPanel.repaintDuringNextViewUpdate();
     }
@@ -54,21 +58,21 @@ public class TerrainDemo {
      */
     private static void setLights(ViewFrame viewFrame, ShapeCollection shapes) {
         LightingManager lightingManager = viewFrame.getViewPanel().getLightingManager();
-        lightingManager.setAmbientLight(new Color(250, 150, 100));
+        lightingManager.setAmbientLight(hex("FA9664FF"));
 
         final LightSource warmLight = new LightSource(
-                new Point3D(-400, -500, 0),
-                new Color(255, 180, 100),
+                point(-400, -500, 0),
+                hex("FFB464FF"),
                 190.0
         );
         final LightSource coolLight = new LightSource(
-                new Point3D(400, -500, 0),
-                new Color(100, 200, 255),
+                point(400, -500, 0),
+                hex("64C8FFFF"),
                 220.0
         );
         final LightSource neutralLight = new LightSource(
-                new Point3D(0, -600, 300),
-                new Color(255, 255, 255),
+                point(0, -600, 300),
+                hex("FFFFFFFF"),
                 250.0
         );
 
@@ -76,8 +80,8 @@ public class TerrainDemo {
         lightingManager.addLight(coolLight);
         lightingManager.addLight(neutralLight);
 
-        shapes.addShape(new LightSourceMarker(warmLight.getPosition(), new Color(255, 180, 100)));
-        shapes.addShape(new LightSourceMarker(coolLight.getPosition(), new Color(100, 200, 255)));
-        shapes.addShape(new LightSourceMarker(neutralLight.getPosition(), new Color(255, 255, 255)));
+        shapes.addShape(new LightSourceMarker(warmLight.getPosition(), hex("FFB464FF")));
+        shapes.addShape(new LightSourceMarker(coolLight.getPosition(), hex("64C8FFFF")));
+        shapes.addShape(new LightSourceMarker(neutralLight.getPosition(), hex("FFFFFFFF")));
     }
 }
\ No newline at end of file