How to build a Triangle-based Grid in Java using Swing.
Context:
I've been searching this for quite a while;
I've seen many questions throughout my search of the internet.
How to Draw Triangles in Java
How do I draw a Triangle in Java
However, as you can see; a majority of them only focus on one Triangle. I am attempting to make a Grid.
The Problem:
Below is a screenshot of my current predicament. I have successfully made row's of triangles however, I have clearly done something wrong. The spacing between row's are offset.
First off, I Apologies for how dark this image is. As you can see, each row contains four Triangles. You can just about see the "Rows"between the Yellow and Green Triangles.
(I apologies for the link, This is my first question, I do not have the ability to post embedded imagery)
The Code Snippet
I apologies in advanced this code looks poorly written.
This is the code I've come up with thus far, for making a Triangle Grid.
for (int x = 0; x < board.width; x++) {
for (int y = 0; y < board.height; y++) {
int numberOfSides = 3;
double sideLength = cellSize;
if(y%2==0) {
if(x%2==0) {
Polygon triA = new Polygon();
for (int i = 0; i < numberOfSides; i++) {
triA.addPoint(
(int) ((cellSize) + (cellSize * x)
+ sideLength * Math.sin(i * 2 * Math.PI / numberOfSides)),
(int) ((cellSize) + (cellSize * y)
+ sideLength * Math.cos(i * 2 * Math.PI / numberOfSides))
);
}
g.setColor(Color.GREEN);
g.drawPolygon(triA);
} else {
Polygon triA = new Polygon();
for (int i = 0; i < numberOfSides; i++) {
triA.addPoint(
(int) ((cellSize) + (cellSize * x)
- sideLength * Math.sin(i * 2 * Math.PI / numberOfSides)),
(int) ((cellSize+sideLength/2) + (cellSize * y)
- sideLength * Math.cos(i * 2 * Math.PI / numberOfSides))
);
}
g.setColor(Color.RED);
g.drawPolygon(triA);
} else {
if(x%2==0) {
Polygon triA = new Polygon();
for (int i = 0; i < numberOfSides; i++) {
triA.addPoint(
(int) ((cellSize) + (cellSize * x)
- sideLength * Math.sin(i * 2 * Math.PI / numberOfSides)),
(int) ((cellSize+cellSize) + (cellSize * y)
- sideLength * Math.cos(i * 2 * Math.PI / numberOfSides))
);
}
g.setColor(Color.YELLOW);
g.drawPolygon(triA);
} else {
Polygon triA = new Polygon();
for (int i = 0; i < numberOfSides; i++) {
triA.addPoint(
(int) ((cellSize) + (cellSize * x)
+ sideLength * Math.sin(i * 2 * Math.PI / numberOfSides)),
(int) ((cellSize+cellSize/2) + (cellSize * y)
+ sideLength * Math.cos(i * 2 * Math.PI / numberOfSides))
);
}
g.setColor(Color.BLUE);
g.drawPolygon(triA);
}
}
The Obvious Issues
So, the main issue thus far is the Size and Spacing of Triangles. My code is obviously not the best in the world and I'm sure there are some much simpler solutions. This image below is what I am trying to replicate:
Here is my end Goal.
I do not wish to make a small grid like this image, but using the same rules for the rows, I would like to make a Triangle Grid which fills an entire board. In this scenario, we can assume this is either a JPanel or JFrame.
The Purpose of this goal?
Once again, I wish to apologies for the vagueness of this question.
I am trying to build a Triangle Grid for some research I am doing into Cellular Automata. The code snippet I have provided was once fashioned from a Hexagon Grid Generation Method I built. I changed the numberOfSides variable to three and adjusted the sideLength variable to try and remove spacing. This is where my problem has occured.
For those interested, here is a link to a blog by a Computer Scientist into Triangular Cellular Automata. I wish to replicate their results one day.
Triangular Game of Life
End Note
I would greatly appreciate some guidance on how to build a Triangle Grid. I'm sure there are some much more accurate methods such as using DrawLine. I would also appreciate some feedback on my question. This is my first question on Stackoverflow, after using the website for quite some time.
I will try my best to monitor this post throughout the day if anyone has any questions for me.
A Polygon can be reused in painting so you only need to create 2 Polygons, one with the triangle pointed up and the other with the triangle pointed down.
Then when you do your painting you use the Graphic.translate(...) method to position the Polygon. Or another option is to create a custom class containing 3 properties:
Polygon
Point where the Polygon is painted
Color of the polygon
Then you add each cell to an ArrayList. In the painting code you then just iterate through the ArrayList to paint each cell. The advantage of this approach is that you are not doing all the calculations every time the panel needs to be repainted.
The spacing between row's are offset.
Once you have the Shape you want to paint, you can use the getBounds() method of the Shape. Then:
for horizontal position you would use the width / 2
for vertical position you would use the height.
In this scenario, we can assume this is either a JPanel or JFrame.
It would never be a JFrame. Custom painting is done by overriding paintComponent() of a JPanel (or JComponent). Then you add the panel to the frame.
I'm sure there are some much simpler solutions.
Not necessarily simpler, but you may want to check out Playing With Shapes for a reusable class that allows you to create some interesting shapes. It also allows you to rotate a given Shape.
Related
I want to code a program in java that utilises loops and JFrame to create various polygons around one another.
ie. Triangle, then Square, then Pentagon etc...
Refrence image of example
super.paintComponent(g);
int y = 0;
int z = 0;
g2D.setPaint(Color.CYAN);
Polygon p = new Polygon();
for (int x = 4; x < 10; x++) {
y = y + 50;
z = z + 100;
System.out.println("y = "+y);
System.out.println("z = " + z);
for (int i = 0; i < 10; i++) {
p.addPoint((int) (z + y * Math.cos(i * 2 * Math.PI / x)),
(int) (z + y * Math.sin(i * 2 * Math.PI / x)));
}
g2D.drawPolygon(p);
}
}
Output of Code
Any help would be much appreciated.
Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Learning Swing with the NetBeans IDE section. Pay close attention to the Performing Custom Painting section.
This is a picture of what the OP is supposed to draw.
The only regular polygon in the picture is the square. At least, I think it's a square.
The triangle is an isosceles triangle.
I'm not sure how you would define the pentagon. The five sides are not equal in length. The side two segments are shorter than the bottom segment. The top two segments appear to be longer than the bottom segment.
You can't use the center point and angle of rotation math to create the polygons. The triangle is not an equilateral triangle.
You have to create this by calculating line segments. I'm assuming that the three polygons share the same base. Calculate one line segment at a time and test your code. You're going to run hundreds of tests before you're done.
I'd start by calculating the square first. A square is made up of four equal length line segments. To start, I'd take 1/2 of the width of the drawing area to be the length of the line segment. The first two X values are at the 1/4 and 3/4 points. The Y value can be 20 pixels off the bottom.
Now that you've drawn one line segment, calculate the line segments for the square. Draw the square.
Find the midpoint of the top square line segment. calculate the triangle line segments. Draw the triangle.
For the pentagon, the side line segments are shorter than the line segments of the square. Each angle of a regular pentagon is 108 degrees from the previous line segment. This polygon has slightly different angles at each point.
You'll have to experiment with the pentagon line segment lengths until the top two segments just intersect the square line segments. You could probably do the math, but I suspect it would be easier to just keep guessing until you get an acceptable drawing.
Edited to add:
I created the following GUI.
Calculating the pentagon line segments took the most time.
The side line segments are 85% of the line length of the bottom line segment. I used a 105 degree angle to calculate the end points of the side line segments.
I used a slope formula to calculate the y point of the top line segments. I'd already calculated the start point and the intersect point to draw the pentagon side line segments and the square, respectively. This calculation took most of my debugging time.
Edited to add:
I don't want to just give you the complete code. Part of learning is struggling with the code and learning how to solve problems as they come up. As I said, you should be writing a little bit of code at a time and testing each little bit of code to see what it does.
Here are two methods I wrote to calculate the four additional pentagon line segments. The Point class is java.awt.Point.
private Point calculateSideSegment(Point start, int radius, int angle) {
double theta = Math.toRadians(angle);
int x = (int) Math.round(Math.cos(theta) * radius) + start.x;
int y = (int) Math.round(Math.sin(theta) * radius) + start.y;
return new Point(x, y);
}
private int calculateTopSegment(Point start, Point intersect, int x) {
int yDiff = start.y - intersect.y;
int xDiff1 = start.x - intersect.x;
int xDiff2 = start.x - x;
double slope = (double) yDiff / xDiff1;
double y = Math.round(slope * xDiff2);
return start.y - (int) y;
}
I want to make a Java program, in which moving sky is generated out of Simplex Noise, but I have performance issues (framerate is too low). I'm using https://github.com/KdotJPG/OpenSimplex2/blob/master/java/OpenSimplex2F.java noise class.
My function which generates the sky, generates it entirely again each frame:
private void generate() {
float y = offset;
for (int i = 0; i < frame.getHeight(); i++) {
float x = offset;
for (int j = 0; j < frame.getWidth(); j++) {
double a = noise.noise2(x, y) + 0.25f * noise.noise2(2 * x, 2 * y) + 0.125f * noise.noise2(4 * x, 4 * y);
a = a / (1 + 0.25 + 0.125);
a = (a + 1) / 2;
a *= 100;
Color color = Color.getHSBColor(220f / 360f, a / 100f, 1f);
background.setRGB(j, i, color.getRGB());
x += noiseResolution;
}
y += noiseResolution;
}
}
Where background is BufferedImage I'm drawing on and offset says how much noise is moved.
I have tried to save an array of pixels of background each frame and translate it by the number of pixels it should be moved, and then I generated only pixels that are new. Unfortunately, because it was rendered too fast then, the number of pixels it should be moved was e.g. 0.2, so I couldn't translate array indices by a fraction.
So I guess the only way is to somehow generate it in another way, but I totally have no idea how.
Thanks!
Not sure about Java, but in C++ with DirectX, OpenGL or any low level interface like that, this should be easily done on the GPU in either HLSL (DirectX), or GLSL (OpenGL). I implemented 5D Simplex noise and even zoomed in so it fills my large screen, and on my 9 year old computer with my old ho-hum graphics card it still does a couple hundred frames per second. Here's what it looks like.
https://www.youtube.com/watch?v=oRO1IGcWIwg
If you can run a fragment shader from Java, I would think that would be the way to go. I think there is some OpenGL interface in Java so you might want to look int that.
This is my first attempt at creating a 2D game, so my code probably isn't as efficient as it could be. Anyway, I tried creating a method to create circles out of my tiles. The point of this method is to create circular dirt patches across my screen. Here is a bit of my code:
private void generateDirt(int x, int y) {
int dirt = 3;
int radius = random.nextInt(7) + 3;
for (int i = radius; i > 1; i--) {
for (int angle = 0; angle < 360; angle++) {
double theta = Math.toRadians(angle);
// Broken Line to solve jutting blocks
// if (theta % Math.PI == 0) theta = 0;
tiles[(int) (x + radius * (Math.sin(theta) * Math.cos(theta)))
+ (int) (y + radius
* (Math.sin(theta) * Math.sin(theta))) * width] = dirt;
}
radius--;
}
}
If I comment out the part where I decrease the radius, and draw just a single circle outline (comment out the outermost for loop(int i = radius...) then the circle is drawn perfectly, except for these two strange tiles jutting out in the side. Sometimes the jutting block is on the right side (I thought it was when it was equal to pi / 2) and on the bottom side as well. But the main problem is that when I attempt to fill the circle by decreasing the radius, the circle...well... becomes a square. It loses its round shape and develops very rigid corners.
I worked on this pretty late, I'm not even sure if my math is correct. TBH, I just kinda threw in the trig functions at random and finally got something that looked like a circle. If you can help me identify what is wrong, or tell me a better way to do this, please let me know! Thanks for the help!
*Also, the radius is actually the diameter (I counted), I need to change the name...
Well I found the answer to my own question. It turns out I don't need to convert my angles to radians. In fact, that just messes up the coordinates. Just using the "angle" instead of "theta" variable fixes the problem.
I need to draw random shapes on a grid such as lines squares etc. This part I'm able to do. My problem is the start and end point of the lines I'm drawing falls anywhere in a grid cell. I would like them to be only at intersection points. One cell in the grid is a 10x10 pixel grid. Do i have to write an algorithm to assign the pixel to its nearest intersection point on the grid or is there a easier way. I'm using a buffered image to draw the grid. Please Help. this is what i have so far
for (int i = 0; i < 61; i++) {
g2d.drawLine((imgDim.width + 2) / 40 * i, 0,
(imgDim.width + 2) / 40 * i, imgDim.height - 1);
g2d.drawLine(0, (imgDim.height + 2) / 60 * i,
imgDim.width - 1, (imgDim.height + 2) / 60 * i);
}
Thank you
How are you coming up with the random points? Making an adjustment there might be the easiest way. That is, just drop a 0 in the process you are using to come up with the points in the first place. Then when you are ready to draw it, add a 0 back.
Seriously? In order to make a random point (pixelX, pixelY) snap to the closest point of a grid.
int gridSize = 10;
int x = (pixelX + gridSize / 2) / gridSize * gridSize;
int y = (pixelY + gridSize / 2) / gridSize * gridSize;
I'm trying to create a simple game library mainly to teach myself. I read some posts here and in the web as such. But I think my problem is a bit different because I'm using my "own logic".
The basics:
All my objects on screen are called "entity" and they are capable to implement an interface called "EntityActionListener" wich allows to interact with the mouse, the keyboard, to move on the screen etc.
How to move the entity the best way?
Idea:
First of all I want to implement the movement, then the bouncing and then the collision for the entities.
For the movement and bouncing, and this is where I have problems, I want the following variables and functions:
protected float posx = 0, posy = 0;
protected float v = 0, vx = 0, vy = 0;
protected int direction = 0;
I use the setVelocity(float arg1) function to set the velocity (v) to arg1 and update the velocity on the axis (vx, vy):
/**
* Set the velocity on both axis according to the direction given
*
* #param arg1 the direction in degrees
* #param arg2 the velocity
*/
private void setVelocityOnAxis(int arg1, float arg2)
{
// Check for speed set to 0
if (arg2 == 0) {
vx = 0;
vy = 0;
return;
}
// Set velocity on axis
vx = (float) (Math.cos(arg1 * Math.PI / 180) * arg2);
vy = (float) (Math.sin(arg1 * Math.PI / 180) * arg2);
}
So the velocity (v) may updates within a triggered event for example.
=> This step seems to work fine.
But I'm having some troubles with the direction which should be handled as follows:
/**
* Set the entity direction
*
* #param arg1 the direction in degrees
*/
protected final void setDir(int arg1)
{
// Update direction
direction = arg1;
// Update velocity on axis
setVelocityOnAxis(direction, v);
}
/**
* Get the enity direction based on the axis
*
* #param arg1 the x velocity
* #param arg2 the y velocity
*/
protected final int getPointDir(float arg1, float arg2)
{
// Set the direction based on the axis
return (int) (360 - Math.abs(Math.toDegrees(Math.atan2(arg1, arg2)) % 360));
}
I wanted to have a bouncing on the border of the frame so I checked for the 4 directions by comparing the x and y coordinates and set either the vx or the vy according to the side to its additive inverse (like 1 to -1). But this does fail.
If I would just update the vx or vy on each side it would bounce like expected but the direction is not updated for this reason.
Here is the code I used:
// ...
// Hit bounds on x axis
direction = -vx; // works.
setDir(getPointDir(-vx, vy)); // does not work.
I'm not that good in geometry and trigonometry. The problem is that I can't say why a collision with a horizontal speed of 1 in direction 360 results in 45 degrees or other strange things I got from the debug prints ...
I really hope someone out there can help me. I just can't fix it.
EDIT:
My question is: Why is this not working. Some code in either setDir() or getPointDir() must be wrong.
EDIT 2
So, I finally get it work. The problem was that the entity had a direction of 45 and moved downwards instead of upwards so I additive inverse for the v speed - this causes this stupid error because minus and minus are positive and while I was bouncing it changed always both velocities, the vx and the vy. I just had to change something in the calculation for the degrees instead. Thank you for your help.
I'm taking a guess that getPointDir() and setVelocity() should calculate a direction in degrees from an x,y and an x,y from a direction in degrees (respectively). In that case, here's the proper line of getPointDir():
return (int) (360 + Math.toDegrees(Math.atan2(arg2, arg1))) % 360;
A simple test:
public static void main(String[] args) {
Test t = new Test();
for (int i = 0; i < 360; i++) {
t.setVelocityOnAxis(i, 10);
System.out.println("Angle: " + i + " vx: " + t.vx + " vy: " + t.vy);
System.out.println("getPointDir: " + t.getPointDir(t.vx, t.vy));
}
}
Edit as for the error, atan2() is always y,x -- easier to spot with variables names other than arg1, arg2. The other error was in the Math.abs logic.
Check this answer. It has the bounce on an axis correctly. You have to think the incoming angle is just as big as the outgoing angle, just opposite. The picture in the post describes this. Good luck.
Maybe this is what you're looking for... (still not sure I get your code).
In getPointDir():
double deg = Math.toDegrees(Math.atan2(arg1, arg2));
if (deg < 0) deg += 360;
return (int) deg;
And then use setDir(getPointDir(-vx, vy)) like in your original post.