Create hexagonal field with flat tiles with JavaFX - java

I want to create an hexagonal field with flat tiles in JavaFX. The following stackoverflow question allows to create a field with pointy tiles: Create hexagonal field with JavaFX
This code example works perfectly with pointy tiles:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;
import javafx.stage.Stage;
public class UISolution extends Application {
private final static int WINDOW_WIDTH = 800;
private final static int WINDOW_HEIGHT = 600;
private final static double r= 20; // the inner radius from hexagon center to outer corner
private final static double n= Math.sqrt(r * r * 0.75); // the inner radius from hexagon center to middle of the axis
private final static double TILE_HEIGHT = 2 * r;
private final static double TILE_WIDTH = 2 * n;
public static void main(String[] args) {
launch(args);
}
public void start(Stage primaryStage) {
AnchorPane tileMap = new AnchorPane();
Scene content = new Scene(tileMap, WINDOW_WIDTH, WINDOW_HEIGHT);
primaryStage.setScene(content);
int rowCount = 4; // how many rows of tiles should be created
int tilesPerRow = 6; // the amount of tiles that are contained in each row
int xStartOffset = 40; // offsets the entire field to the right
int yStartOffset = 40; // offsets the entire fiels downwards
for (int x = 0; x < tilesPerRow; x++) {
for (int y = 0; y < rowCount; y++) {
double xCoord = x * TILE_WIDTH + (y % 2) * n + xStartOffset;
double yCoord = y * TILE_HEIGHT * 0.75 + yStartOffset;
Polygon tile = new Tile(xCoord, yCoord);
tileMap.getChildren().add(tile);
}
}
primaryStage.show();
}
private class Tile extends Polygon {
Tile(double x, double y) {
// creates the polygon using the corner coordinates
getPoints().addAll(
x, y,
x, y + r,
x + n, y + r * 1.5,
x + TILE_WIDTH, y + r,
x + TILE_WIDTH, y,
x + n, y - r * 0.5
);
// set up the visuals and a click listener for the tile
setFill(Color.ANTIQUEWHITE);
setStrokeWidth(1);
setStroke(Color.BLACK);
setOnMouseClicked(e -> System.out.println("Clicked: " + this));
}
}
}
I think that I only have to modify the part here:
getPoints().addAll(
x, y,
x, y + r,
x + n, y + r * 1.5,
x + TILE_WIDTH, y + r,
x + TILE_WIDTH, y,
x + n, y - r * 0.5
);
but I'm struggling to have a correct shape and position for my tiles. And if I'm doing:
getPoints().addAll(x, y,
x + n * 0.5, y + r,
x + n * 1.5, y + r,
x + TILE_WIDTH, y,
x + n * 1.5, y - r,
x + n * 0.5, y - r
);
the tiles have a correct flat shape but are not positioned correctly relative to each other. I think that this time I should modify the following code:
double xCoord = x * TILE_WIDTH + (y % 2) * n + xStartOffset;
double yCoord = y * TILE_HEIGHT * 0.75 + yStartOffset;
An example of the pointy tiles result with this code:

I found a solution for flat tiles. Here it is:
package org.hexagon.check;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;
public class HexagonFlat extends Application {
private final static double TILE_WIDTH = 20;
private final static double TILE_HEIGHT = TILE_WIDTH;
private final static int WINDOW_WIDTH = 800;
private final static int WINDOW_HEIGHT = 600;
double v = Math.sqrt(3) / 2.0;
double v2 = Math.sqrt(3);
public static void main(String[] args) {
launch(args);
}
public void start(Stage primaryStage) {
AnchorPane tileMap = new AnchorPane();
Scene content = new Scene(tileMap, WINDOW_WIDTH, WINDOW_HEIGHT);
primaryStage.setScene(content);
int rowCount = 4; // how many rows of tiles should be created
int tilesPerRow = 6; // the amount of tiles that are contained in each row
int xStartOffset = 40; // offsets the entire field to the right
int yStartOffset = 40; // offsets the entire fiels downwards
for (int y = 0; y < rowCount; y++) {
double yCoordInit = yStartOffset + y * TILE_WIDTH * v2;
double yCoord = yCoordInit;
for (int x = 0; x < tilesPerRow; x++) {
double xCoord = 1.5 * x * TILE_WIDTH + xStartOffset;
Polygon tile = new Tile(xCoord, yCoord);
tileMap.getChildren().add(tile);
yCoord = yCoord == yCoordInit ? yCoord + TILE_HEIGHT * v : yCoordInit;
}
}
primaryStage.show();
}
private class Tile extends Polygon {
Tile(double x, double y) {
// creates the polygon using the corner coordinates
getPoints().addAll(
x, y,
x + TILE_WIDTH, y,
x + TILE_WIDTH * 1.5, y + TILE_HEIGHT * v,
x + TILE_WIDTH, y + TILE_HEIGHT * v2,
x, y + TILE_WIDTH * v2,
x - (TILE_WIDTH / 2.0), y + TILE_HEIGHT * v
);
// set up the visuals and a click listener for the tile
setFill(Color.ANTIQUEWHITE);
setStrokeWidth(1);
setStroke(Color.BLACK);
setOnMouseClicked(e -> System.out.println("Clicked: " + this));
}
}
}
The result is:

Related

Why are the colors of my Mandelbrot set crooked looking and not nice and symmetric?

I am wondering why is the background of my Mandelbrot set not as nice as the background on the other picture. Also, why does the Mandelbrot set still show up if I set the number 4 in the if statement of iterationChecker to any other number bigger than 4?
Mandelbrot set, and the background looks crooked:
I want the background to look like this:
package com.example.demo2;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.paint.Color;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.Group;
import javafx.scene.canvas.Canvas;
import javafx.scene.image.WritableImage;
public class myMandelbrot extends Application {
static double width = 800;
static double height = 600;
double fullHeight= Screen.getPrimary().getBounds().getHeight();
double fullWidth= Screen.getPrimary().getBounds().getWidth();
static int maximumIterations = 50;
static final Canvas canvas = new Canvas(800, 600);
static double zoom = 250.0;
static double xPos = 0; //add 0 on both of the coordinates for the accurate plane
static double yPos = 0;//30;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group(canvas), 800, 600); //ovde prosiri za buttons
scene.setOnKeyPressed(event -> {
switch (event.getCode()) {
case W, UP -> {
yPos -= (height / zoom) * 100;
MandelbrotSet();
}
case A, LEFT -> {
xPos -= (width / zoom) * 100;
MandelbrotSet();
}
case S, DOWN -> {
yPos += (height / zoom) * 100;
MandelbrotSet();
}
case D, RIGHT -> {
xPos += (width / zoom) * 100;
MandelbrotSet();
}
case EQUALS -> {
zoom/=0.7;
MandelbrotSet();
}
case MINUS -> {
zoom*=0.7;
MandelbrotSet();
}
case U -> {
stage.setHeight(stage.getHeight()+50);
stage.setWidth(stage.getWidth()+50);
canvas.setHeight(canvas.getHeight()+50);
canvas.setWidth(canvas.getWidth()+50);
if(canvas.getHeight()>=fullHeight){
canvas.setHeight(fullHeight);
}
if(canvas.getWidth()>=fullWidth){
canvas.setWidth(fullWidth);
}
MandelbrotSet();
}
case O -> {
canvas.setWidth(800);
canvas.setHeight(600);
stage.setWidth(800);
stage.setHeight(600);
MandelbrotSet();
}
case SPACE -> {
canvas.setHeight(fullHeight);
canvas.setWidth(fullWidth);
stage.setMaximized(true);
MandelbrotSet();
}
case R -> {
zoom = 250.0;
xPos = -470;
yPos = 0;
MandelbrotSet();
}
case ESCAPE -> Platform.exit();
}
});
scene.setOnMouseClicked(event -> {
switch(event.getButton()){
case PRIMARY -> {
zoom/=0.7;
MandelbrotSet();
}
case SECONDARY -> {
zoom*=0.7;
MandelbrotSet();
}
}
});
stage.setScene(scene);
MandelbrotSet();
stage.setTitle("Mandelbrot Set");
stage.show();
}
/* ========================================================================================== */
public int iterationChecker(double cr, double ci) {
int iterationsOfZ = 0;
double zr = 0.0;
double zi = 0.0;
while (iterationsOfZ < maximumIterations && (zr * zr) + (zi + zi) < 4){
double oldZr = zr;
zr = (zr * zr) - (zi * zi) + cr;
zi = 2 * (oldZr * zi) + ci;
iterationsOfZ++;
}
return iterationsOfZ;
}
/* ========================================================================================== */
public void MandelbrotSet() {
WritableImage image = new WritableImage((int)canvas.getWidth(), (int)canvas.getHeight());
double centerY = canvas.getWidth() / 2.0;
double centerX = canvas.getHeight() / 2.0;
for (int x = 0; x < canvas.getWidth(); x++) {
for (int y = 0; y < canvas.getHeight(); y++) {
double cr = xPos / width + (x - centerY) / zoom;
double ci = yPos / height + (y - centerX) / zoom;
int iterations = iterationChecker(cr, ci);
int hue = 300 * iterations / maximumIterations; //the main formula
if (iterations == maximumIterations) {
image.getPixelWriter().setColor(x, y, Color.rgb(35, 0, 35));
}
else{
image.getPixelWriter().setColor(x, y, Color.hsb(hue, 0.9, 1));
}
}
canvas.getGraphicsContext2D().drawImage(image, 0, 0); //x and y coordinates of the image.
}
/* ========================================================================================== */
}
}
The iteration count is slightly off because of a typo in the condition for counting iterations to escape:
public int iterationChecker(double cr, double ci) {
int iterationsOfZ = 0;
double zr = 0.0;
double zi = 0.0;
//while (iterationsOfZ < maximumIterations && (zr * zr) + (zi + zi) < 4){
while (iterationsOfZ < maximumIterations && (zr * zr) + (zi * zi) < 4){
double oldZr = zr;
zr = (zr * zr) - (zi * zi) + cr;
zi = 2 * (oldZr * zi) + ci;
iterationsOfZ++;
}
return iterationsOfZ;
}
Also, why does the Mandelbrot set still show up if I set the number 4 in the if statement of iterationChecker to any other number bigger than 4?
A point c is considered to be in the Mandelbrot set if the sequence defined by z(n+1) = z(n)^2 + c (with z(0)=0) is bounded for all n.
It's easy enough to prove that if |c|>2 then c is not in the Mandelbrot set, and for |c|<=2, that if |z(n)| > 2 for any n, then the sequence z(n) is unbounded, and hence if |z(n)| > 2 (i.e. |z|^2=zr^2+zi^2 > 4) then c is not in the Mandelbrot set.
So the strategy is to iterate z -> z^2 + c, and if |z|^2>4 at any point conclude z is not in the Mandelbrot set. Of course, if you choose any k>4, then if |z|^2 > k, then it's also true that |z|^2 > 4, and z is not in the Mandelbrot set.
Finding z with |z|^2 > 4 is called "escaping", and the number of iterations until escape determines the color. If you reach some maximum number of iterations (you chose 50) without escaping, then you assume c is "close to" or inside the Mandelbrot set and color it black.
So changing the escape level to another number bigger than 4 might change the colors, but it will not change it by much: the absolute value of |z(n)| grows as n^2 from that point. So you would have to increase the escape threshold by a lot to make a difference of more than 1 or 2 in the time to escape.

Java, 3D prospective projection

I'm learning 3D rendering with java and I encountered a weird issue.
I'm able to rotate and display 3D objects to the screen. but when I tried to use prospective projection to show depth, the program freaks out. No errors or anything, but the depth seems to stretch incurability long. I narrowed the problem down to the perspective divide in the projection function.
Can anyone help?
public class Renderer extends JPanel{
public Renderer() {
}
double angle = 0;
double a;
double f;
double l;
double offSet;
public void update() {
angle += 0.03;
repaint();
}
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
super.paintComponent(g2d);
for(Triangle element: MidService.mesh)
{
Vertex v1 = rotateY(element.v1);
Vertex v2 = rotateY(element.v2);
Vertex v3 = rotateY(element.v3);
v1 = projection(v1);
v2 = projection(v2);
v3 = projection(v3);
int offSet = (int) (MidService.displayX/2);
g2d.drawLine(offSet + v1.x, offSet + v1.y, offSet + v2.x, offSet + v2.y);
g2d.drawLine(offSet + v2.x, offSet + v2.y, offSet + v3.x, offSet + v3.y);
g2d.drawLine(offSet + v3.x, offSet + v3.y, offSet + v1.x, offSet + v1.y);
}
}
public Vertex projection(Vertex v)
{
a = MidService.displayY / MidService.displayX;
f = 1 / (Math.tan(MidService.fieldOfView/2));
l = MidService.Zfar / (MidService.Zfar - MidService.Znear);
offSet = MidService.Zfar / (MidService.Zfar - MidService.Znear) * MidService.Znear;
double x = (v.x * a * f);
double y = (v.y * f);
double z = (v.z * l - offSet);
double w = v.z;
//the if function below caused the issue
if(w != 0.0) {
x /= w;
y /= w;
z /= w;
}
return new Vertex((int)x, (int)y, (int)z);
}
public Vertex rotateY(Vertex v)
{
double x = v.x * Math.cos(angle) + v.z * Math.sin(angle);
double y = v.y;
double z = v.x * (-Math.sin(angle)) + v.z * Math.cos(angle);
return new Vertex((int)x, (int)y, (int)z);
}
//-------------------------------------------------------
public class MidService {
public static double displayX = 1000;
public static double displayY = 1000;
public static double fieldOfView = 180;
public static double Zfar = 10;
public static double Znear = 1;
public static ArrayList<Triangle> mesh = new ArrayList<>();
}
//---------------------------------------------
public class Driver {
public static void main(String[] args) {
Display display = new Display();
MidService.mesh.add(new Triangle(new Vertex(-100, 100, 100),
new Vertex(-100, -100, 100),
new Vertex(100, -100, 100)));
MidService.mesh.add(new Triangle(new Vertex(-100, 100, 100),
new Vertex(100, -100, 100),
new Vertex(100, 100, 100)));
Here's a video of it:[https://youtu.be/bFJLU5c3JE0]
A follow up to the perspective division issue. It turns out to be the inverse of the difference between camera distance and z coordinate. So instead of division by z, its 1/(distance - z)
Below is modification for Gaba Miau's matrix multiply function
Vertex Mult(float[][] mat,Vertex v){
float ver[] = {v.x,v.y,v.z,v.w};
float ans[] = {0,0,0,0};
for (int i=0;i<mat.length;i++){
for(int j=0;j<mat[0].length;j++){
ans[i] += mat[i][j] * ver[j];
}
}
float temp = 100/(500 - v.z);
if(temp != 0)
{
ans[0] *= temp;
ans[1] *= temp;
ans[2] *= temp;
}
Vertex vans = new Vertex((int)ans[0],(int)ans[1],(int)ans[2]);
vans.w =(int) ans[3];
return vans;
}
P.S. it's ans[i] += mat[i][j] * ver[j]; in the forloop
Firstly you should be using a graphics API like OpenGL or DirectX, secondly you should be using a math library like GLM that contains all the projection matrix formulas and other stuff. Thirdly, you shouldn't be using int as a datatype to store vertex data.
I made a few changes to your code so it doesn't freak out anymore.
The fov should be in the range(0.001,179.999), never 180, it is recommanded using 90 deg. Also the math function tan takes in radians not deg.
package Render;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.sound.midi.MidiDevice;
import javax.swing.JPanel;
import Beans.Triangle;
import Beans.Vertex;
import Utilities.MidService;
public class Renderer extends JPanel{
public Renderer() {
}
double angle = 0;
double a;
double f;
double l;
double offSet;
public void update() {
//angle += 0.03;
repaint();
}
float projMat[][]= {
{1f / ((float)Math.tan(MidService.fieldOfView/2f)),0,0,0},
{0,1f / ((float)Math.tan(MidService.fieldOfView/2f)),0,0},
{0,0,MidService.Zfar / (MidService.Zfar - MidService.Znear),1},
{0,0,-MidService.Zfar* MidService.Znear / (MidService.Zfar - MidService.Znear),0},
};
Vertex Mult(float[][] mat,Vertex v){
float ver[] = {v.x,v.y,v.z,v.w};
float ans[] = {0,0,0,0};
for (int i=0;i<mat.length;i++){
for(int j=0;j<mat[0].length;j++){
ans[i] += mat[i][j] * ver[i];
}
}
Vertex vans = new Vertex((int)ans[0],(int)ans[1],(int)ans[2]);
vans.w =(int) ans[3];
return vans;
}
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
super.paintComponent(g2d);
for(Triangle element: MidService.mesh)
{
Vertex v1 = rotateZ(element.v1);
Vertex v2 = rotateZ(element.v2);
Vertex v3 = rotateZ(element.v3);
v1 = rotateY(v1);
v2 = rotateY(v2);
v3 = rotateY(v3);
v1 = Mult(projMat,v1);
v2 = Mult(projMat,v2);
v3 = Mult(projMat,v3);
int offSet = (int) (MidService.displayX/2);
g2d.drawLine(offSet + (int)v1.x, offSet + (int)v1.y, offSet + (int)v2.x, offSet + (int)v2.y);
g2d.drawLine(offSet + (int)v2.x, offSet + (int)v2.y, offSet + (int)v3.x, offSet + (int)v3.y);
g2d.drawLine(offSet + (int)v3.x, offSet + (int)v3.y, offSet + (int)v1.x, offSet + (int)v1.y);
}
}
public Vertex projection(Vertex v)
{
a = MidService.displayY / MidService.displayX;
f = 1 / (Math.tan(MidService.fieldOfView/2));
l = MidService.Zfar / (MidService.Zfar - MidService.Znear);
offSet = MidService.Zfar / (MidService.Zfar - MidService.Znear) * MidService.Znear;
double x = (v.x * a * f);
double y = (v.y * f);
double z = (v.z * l - offSet);
double w = v.z;
if(w != 0.0) {
x /= w;
y /= w;
z /= w;
}
return new Vertex((int)x, (int)y, (int)z);
}
public Vertex rotateY(Vertex v)
{
double x = v.x * Math.cos(angle) + v.z * Math.sin(angle);
double y = v.y;
double z = v.x * (-Math.sin(angle)) + v.z * Math.cos(angle);
return new Vertex((int)x, (int)y, (int)z);
}
public Vertex rotateZ(Vertex v)
{
double x = v.x * Math.cos(angle) + v.y * (-Math.sin(angle));
double y = v.x * Math.sin(angle) + v.y * Math.cos(angle);
double z = v.z;
return new Vertex((int)x, (int)y, (int)z);
}
}
Here is the MidService class
package Utilities;
import java.util.ArrayList;
import Beans.Triangle;
public class MidService {
public static float displayX = 1000;
public static float displayY = 1000f;
public static float fieldOfView = 3.14f/2f;
public static float Zfar = 100f;
public static float Znear = 0.1f;
public static ArrayList<Triangle> mesh = new ArrayList<>();
}
And Vertex
package Beans;
public class Vertex {
public float x;
public float y;
public float z;
public float w = 1;
public Vertex(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
}

I am trying to find a path through a randomly generated maze

This is a maze solver and I am trying to find a path through the maze. The Map is generated using Prim's algorithm and the position class which is added at the end.
I am trying to find an optimal path through the randomly generated maze using BFS, A*, or DFS but I seem to be doing something wrong.
//impoted by wildcard
import java.awt.*;
import java.io.*;
import java.util.*;
import javax.swing.*;
public class MazeGenerator extends Canvas {
private final Random rand = new Random(); //random variable
private static int WIDTH;
private static int HEIGHT;
private int TILE_WIDTH = 15;
private int TILE_HEIGHT = 15;
private static LinkedList <position> maze = new LinkedList<>(); // maze
private static Map<position, Color> colors = new HashMap<>();
// paint class to draw out the maze
public void paint(Graphics g) {
super.paint(g);
g.translate(5, 5);
g.setColor(Color.DARK_GRAY);
g.fillRect(0, 0, WIDTH * TILE_WIDTH, HEIGHT * TILE_HEIGHT);
g.setColor(Color.LIGHT_GRAY);
g.drawLine(0, 0, 0, HEIGHT * TILE_HEIGHT);
g.drawLine(0, 0, WIDTH * TILE_WIDTH, 0);
g.drawLine(WIDTH * TILE_WIDTH, 0, WIDTH * TILE_WIDTH, HEIGHT * TILE_HEIGHT);
g.drawLine(0, HEIGHT * TILE_HEIGHT, WIDTH * TILE_WIDTH, HEIGHT * TILE_HEIGHT);
LinkedList <position> mazeSteped = maze;
for(int y = 0; y < HEIGHT; y++)
{
for(int x = 0; x < WIDTH; x++)
{
int current = (y * WIDTH) + x;
int lower = ((y + 1) * WIDTH) + x;
if(!mazeSteped.contains(new position(current, lower)))
g.drawLine(x * TILE_WIDTH, (y + 1) * TILE_HEIGHT, (x + 1) * TILE_WIDTH, (y + 1) * TILE_HEIGHT);
if(!mazeSteped.contains(new position(current, current + 1)))
g.drawLine((x + 1) * TILE_WIDTH, y * TILE_HEIGHT, (x + 1) * TILE_WIDTH, (y + 1) * TILE_HEIGHT);
if(colors.containsKey(new position(x, y)))
{
g.setColor(colors.get(new position(x, y)));
g.fillRect(x * TILE_WIDTH, y * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT);
g.setColor(Color.LIGHT_GRAY);
}
}
}
}
// Prim's algorithm
public void generate(int Width, int Height)
{
LinkedList<Integer> visited = new LinkedList<>();
LinkedList<position> toVisit = new LinkedList<>();
visited.add(0);
toVisit.add(new position(0, 1));
toVisit.add(new position(0, Width));
while(toVisit.size() > 0)
{
int randomIndex = rand.nextInt(toVisit.size());
position nextPath = toVisit.remove(randomIndex);
if(visited.contains(nextPath.end))
continue;
if(nextPath.start > nextPath.end)
maze.add(new position(nextPath.end, nextPath.start));
else
maze.add(nextPath);
visited.add(nextPath.end);
//this is showing all four directions that the random variable can generate towards.
int above = nextPath.end - Width;
if(above > 0 && !visited.contains(above))
toVisit.add(new position(nextPath.end, above));
int left = nextPath.end - 1;
if(left % Width != Width - 1 && !visited.contains(left))
toVisit.add(new position(nextPath.end, left));
int right = nextPath.end + 1;
if(right % Width != 0 && !visited.contains(right))
toVisit.add(new position(nextPath.end, right));
int below = nextPath.end + Width;
if(below < Width * Height && !visited.contains(below))
toVisit.add(new position(nextPath.end, below));
}
}
public static void main(String[] args)
{
MazeGenerator mazeGen = new MazeGenerator();
int Height = 30;
HEIGHT = Height;
int Width = 30;
WIDTH = Width;
mazeGen.generate(Width, Height);
mazeGen.setSize(16*Width, 17*Height);
JFrame frame = new JFrame("Maze Generator");
frame.add(mazeGen);
frame.setSize(16*Width, 17*Height);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
//position or vector class that keeps track of position.
public class position{
public int start;
public int end;
public position(int start, int end){
this.start = start;
this.end = end;
}
public String toString(){
return "(" + start + ", " + end + ")";
}
public boolean equals(Object obj){
if(!(obj instanceof position))
return false;
position pos = (position) obj;
return pos.start == start && pos.end == end;
}
public int hashCode(){
return this.toString().hashCode();
}
public static void main(String [] args) {
position my = new position(1,1);
System.out.println(new position(1,1));
}
}
https://github.com/yuchinchenTW/MazeGenerate/tree/master
try this maze generator
using depth-first search and Recursive division method
The recursive division method can prevent stackoverflow on recursion
https://en.wikipedia.org/wiki/Maze_generation_algorithm

How to more realistically simulate light on a sphere?

I am attempting to simulate a sphere, and shade it realistically given an origin vector for the light, and the sphere being centered around the origin. Moreover, the light's vector is the normal vector on a larger invisible sphere at a chosen point. The sphere looks off.
https://imgur.com/a/IDIwQQF
The problem, is that it is very difficult to bug fix this kind of program. Especially considering that I know how I want it to look in my head, but when looking at the numbers in my program there is very little meaning attached to them.
Since I don't know where the issue is, I'm forced to paste all of it here.
public class SphereDrawing extends JPanel {
private static final long serialVersionUID = 1L;
private static final int ADJ = 320;
private static final double LIGHT_SPHERE_RADIUS = 5;
private static final double LIGHT_X = 3;
private static final double LIGHT_Y = 4;
private static final double LIGHT_Z = 0;
private static final double DRAWN_SPHERE_RADIUS = 1;
private static final int POINT_COUNT = 1000000;
private static Coord[] points;
private static final double SCALE = 200;
public SphereDrawing() {
setPreferredSize(new Dimension(640, 640));
setBackground(Color.white);
points = new Coord[POINT_COUNT];
initializePoints();
for (int i = 0; i < points.length; i++) {
points[i].scale();
}
new Timer(17, (ActionEvent e) -> {
repaint();
}).start();
}
public void initializePoints() { //finding the points on the surface of the sphere (hopefully somewhat equidistant)
double random = Math.random() * (double)POINT_COUNT;
double offset = 2/(double)POINT_COUNT;
double increment = Math.PI * (3 - Math.sqrt(5));
for (int i = 0; i < POINT_COUNT; i++) {
double y = ((i * offset) - 1) + (offset / 2);
double r = Math.sqrt(1 - Math.pow(y, 2));
double phi = ((i + random) % (double)POINT_COUNT) * increment;
double x = Math.cos(phi) * r;
double z = Math.sin(phi) * r;
points[i] = new Coord(x, y, z);
}
}
public void drawSphere(Graphics2D g) {
g.translate(ADJ, ADJ); //shifting from origin for drawing purposes
Arrays.sort(points); //sorting points by their z coordinates
double iHat = -2 * LIGHT_X;
double jHat = -2 * LIGHT_Y; //Light vector
double kHat = -2 * LIGHT_Z;
double angL1 = 0;
if (Math.abs(iHat) != 0.0)
angL1 = Math.atan(jHat / iHat); //converting light vector to spherical coordinates
else
angL1 = Math.PI/2;
double angL2 = Math.atan(Math.sqrt(Math.pow(iHat, 2) + Math.pow(jHat, 2))/ kHat);
double maxArcLength = LIGHT_SPHERE_RADIUS * Math.PI; // maximum arc length
for (int i = 0; i < points.length; i++) {
if(points[i].checkValid()) {
double siHat = -2 * points[i].x;
double sjHat = -2 * points[i].y; //finding normal vector for the given point on the sphere
double skHat = -2 * points[i].z;
double angSF1 = -1 * Math.abs(Math.atan(sjHat / siHat)); // converting vector to spherical coordinates
double angSF2 = Math.atan(Math.sqrt(Math.pow(siHat, 2) + Math.pow(sjHat, 2))/ skHat);
double actArcLength = LIGHT_SPHERE_RADIUS * Math.acos(Math.cos(angL1) * Math.cos(angSF1) + Math.sin(angL1) * Math.sin(angSF1) * Math.cos(angL2 - angSF2)); //calculating arc length at this point
double comp = actArcLength / maxArcLength; // comparing the maximum arc length to the calculated arc length for this vector
int col = (int)(comp * 255);
col = Math.abs(col);
g.setColor(new Color(col, col, col));
double ovalDim = (4 * Math.PI * Math.pow(DRAWN_SPHERE_RADIUS, 2))/POINT_COUNT; //using surface area to determine how large size of each point should be drawn
if (ovalDim < 1) // if it too small, make less small
ovalDim = 2;
g.fillOval((int)points[i].x, (int)points[i].y, (int)ovalDim, (int)ovalDim); //draw this oval
}
}
}
#Override
public void paintComponent(Graphics gg) {
super.paintComponent(gg);
Graphics2D g = (Graphics2D) gg;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
drawSphere(g);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setTitle("Sphere");
f.setResizable(false);
f.add(new SphereDrawing(), BorderLayout.CENTER);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
#SuppressWarnings("rawtypes")
private class Coord implements Comparable {
public double x;
public double y;
public double z;
public Coord(double x2, double y2, double z2) {
x = x2;
y = y2;
z = z2;
}
public void scale() {
x *= SCALE;
y *= SCALE; //drawing purposes
z *= SCALE;
}
public String toString() {
return x + " " + y + " " + z;
}
public int compareTo(Object c) {
double diff = this.z - ((Coord)c).z;
if (diff < 0)
return -1;
else if (diff > 0) //for sorting the array of points
return 1;
else
return 0;
}
public boolean checkValid() {
return (z > 0); //checks if need to draw this point
}
}
}
I was hoping to at least draw a realistic looking sphere, even if not completely accurate, and I couldn't tell you what exactly is off with mine

Java, Colours of a buffered image are completely different to the original image

I am trying to add some texture to my game. I am running into some problems getting the image to display properly.
This is what the texture should look like, just a boring black square:
And this is what I get. A little bit of black with blue lines.
This is the code I used to import the image. The BufferedImage is set to Type_INT_RGB:
package com.mime.minefront.graphics;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
public class Texture {
public static Render floor = loadBitmap("/textures/floorb.png");
public static Render loadBitmap(String fileName) {
try {
BufferedImage image = ImageIO.read(Texture.class.getResource(fileName));
int width = image.getWidth();
int height = image.getHeight();
Render result = new Render(width, height);
image.getRGB(0, 0, width, height, result.pixels, 0, width);
return result;
} catch (Exception e) {
System.out.println("CRASH!");
throw new RuntimeException(e);
}
}
}
Any help or advice would be great. I have tried to search for the answer but with no luck.
This is my Render class.
package com.mime.minefront.graphics;
public class Render {
public final int width;
public final int height;
public final int[] pixels;
public Render(int width, int height) {
this.width = width;
this.height = height;
pixels = new int[width * height];
}
public void draw(Render render, int xOffset, int yOffset) {
for (int y = 0; y < render.height; y++) {
int yPix = y + yOffset;
if (yPix < 0 || yPix >= height) {
continue;
}
for (int x = 0; x < render.width; x++) {
int xPix = x + xOffset;
if (xPix < 0 || xPix >= width) {
continue;
}
int aplha = render.pixels[x + y * render.width];
if (aplha > 0) {
pixels[xPix + yPix * width] = aplha;
}
}
}
}
}
and this is my Render3D class
package com.mime.minefront.graphics;
import com.mime.minefront.Game;
import com.mimi.minefront.input.Controller;
import com.mimi.minefront.input.InputHandler;
import java.awt.Robot;
import java.util.Random;
public class Render3D extends Render {
public double[] zBuffer;
private double renderDistance = 5000;
private double forward, right, up, cosine, sine;
public Render3D(int width, int height) {
super(width, height);
zBuffer = new double[width * height];
}
public void floor(Game game) {
double floorPosition = 8;
double cellingPosition = 8;
forward = game.controls.z;
right = game.controls.x;
up = game.controls.y;
double walking = Math.sin(game.time / 6.0) * 0.5;
if (Controller.crouchWalk) {
walking = Math.sin(game.time / 6.0) * 0.25;
}
if (Controller.runWalk) {
walking = Math.sin(game.time / 6.0) * 0.8;
}
double rotation = 0;//Math.sin(game.time / 20) * 0.5; //game.controls.rotation;
cosine = Math.cos(rotation);
sine = Math.sin(rotation);
for (int y = 0; y < height; y++) {
double celling = (y - height / 2.0) / height;
double z = (floorPosition + up) / celling;
if (Controller.walk) {
z = (floorPosition + up + walking) / celling;
}
if (celling < 0) {
z = (cellingPosition - up) / -celling;
if (Controller.walk) {
z = (cellingPosition - up - walking) / -celling;
}
}
for (int x = 0; x < width; x++) {
double depth = (x - width / 2.0) / height;
depth *= z;
double xx = depth * cosine + z * sine;
double yy = z * cosine - depth * sine;
int xPix = (int) (xx + right);
int yPix = (int) (yy + forward);
zBuffer[x + y * width] = z;
pixels[x + y * width] = //((xPix & 15) * 16 | ((yPix % 15) * 16) << 8);
Texture.floor.pixels[xPix & 7] + (yPix & 7) * 8;
if (z > 500) {
pixels[x + y * width] = 0;
}
}
}
}
public void renderWall(double xLeft, double xRight, double zDistance, double yHeight) {
double xcLeft = ((xLeft) - right) * 2;
double zcLeft = ((zDistance) - forward) * 2;
double rotLeftSideX = xcLeft * cosine - zcLeft * sine;
double yCornerTL = ((-yHeight) - up) * 2;
double yCornerBL = ((+0.5 - yHeight) - up) * 2;
double rotLeftSideZ = zcLeft * cosine + xcLeft * sine;
double xcRight = ((xRight) - right) * 2;
double zcRight = ((zDistance) - forward) * 2;
double rotRightSideX = xcRight * cosine - zcLeft * sine;
double yCornerTR = ((-yHeight) - up) * 2;
double yCornerBR = ((+0.5 - yHeight) - up) * 2;
double rotRightSideZ = zcRight * cosine + xcRight * sine;
double xPixelLeft = (rotLeftSideX / rotLeftSideZ * height + width / 2);
double xPixelRight = (rotRightSideX / rotRightSideZ * height + width / 2);
if (xPixelLeft >= xPixelRight) {
return;
}
int xPixelLeftInt = (int) (xPixelLeft);
int xPixelRightInt = (int) (xPixelRight);
if (xPixelLeftInt < 0) {
xPixelLeftInt = 0;
}
if (xPixelRightInt > width) {
xPixelRightInt = width;
}
double yPixelLeftTop = (yCornerTL / rotLeftSideZ * height + height / 2);
double yPixelLeftBottom = (yCornerBL / rotLeftSideZ * height + height / 2);
double yPixelRightTop = (yCornerTR / rotRightSideZ * height + height / 2);
double yPixelRightBottom = (yCornerBR / rotRightSideZ * height + height / 2);
double tex1 = 1 / rotLeftSideZ;
double tex2 = 1 / rotRightSideZ;
double tex3 = 0 / rotLeftSideZ;
double tex4 = 8 / rotRightSideZ - tex3;
for (int x = xPixelLeftInt; x < xPixelRightInt; x++) {
double pixelRotation = (x - xPixelLeft) / (xPixelRight - xPixelLeft);
double xTexture= (int) ((tex3+tex4*pixelRotation)/tex1+(tex2-tex1)*pixelRotation);
double yPixelTop = yPixelLeftTop + (yPixelRightTop - yPixelLeftTop) * pixelRotation;
double yPixelBottom = yPixelLeftBottom + (yPixelRightBottom - yPixelLeftBottom) * pixelRotation;
int yPixelTopInt = (int) (yPixelTop);
int yPixelBottomInt = (int) (yPixelBottom);
if (yPixelTopInt < 0) {
yPixelTopInt = 0;
}
if (yPixelBottomInt > height) {
yPixelBottomInt = height;
}
for (int y = yPixelTopInt; y < yPixelBottomInt; y++) {
pixels[x + y * width] = (int) xTexture*100;
zBuffer[x + y * width] = 0;
}
}
}
public void renderDistanceLimiter() {
for (int i = 0; i < width * height; i++) {
int colour = pixels[i];
int brightness = (int) (renderDistance / (zBuffer[i]));
if (brightness < 0) {
brightness = 0;
}
if (brightness > 255) {
brightness = 255;
}
int r = (colour >> 16) & 0xff;
int g = (colour >> 8) & 0xff;
int b = (colour) & 0xff;
r = r * brightness / 255;
g = g * brightness / 255;
b = b * brightness / 255;
pixels[i] = r << 16 | g << 8 | b;
}
}
}
From getRGB() :
Returns an array of integer pixels in the default RGB color model
(TYPE_INT_ARGB) and default sRGB color space, from a portion of the
image data. Color conversion takes place if the default model does not
match the image ColorModel
See if using TYPE_INT_ARGB instead of TYPE_INT_RGB works.

Categories