I am making a heightmap using simplex noise. I am running into problems adjusting it to give a slight tendency to form an island. I am just working on getting the values to be correct before actually rendering the biomes and other features. I have run into the problem that my code, which should work to create this tendency to make an island in the middle only seems to work in one direction.
If there a particular reason for this? My class that deals with the smoothing out of the terrain does the same thing in the x and y directions, but only one works.
public class MapGenerator{
public double[][] toRender;
int maxHeight = 300;
public MapGenerator() {
int xResolution = 200;
int yResolution = 200;
double[][] result = new double[xResolution][yResolution];
for (int x = 0; x < xResolution; x++){
for (int y = 0; y < yResolution; y++){
result[x][y] = transformPoint(x, y, xResolution, yResolution, SimplexNoise.noise(x, y));
}
}
toRender = result;
}
private double transformPoint(int x, int y, int xSize, int ySize, double point){
System.out.println();
System.out.println(point);
point += 20 * Math.sin(x * Math.PI / xSize);
point += 20 * Math.sin(y * Math.PI / ySize);
System.out.println(point);
return point;
}
}
Images of the white noise:
With X and Y:
With only X (Y commented out):
[With only Y (X commented out):](Similar to X and Y, can't post link because of reputation.)
[Without X and Y:](Similar to only X, can't post link because of reputation.)
I'm not 100% where your error is. It could be in your noise routine. To debug this, I removed the call to noise and substituted a constant value of 0.5. Then I got the rest of it working so I saw a white halo in the center of the image. Then I added the noise call back. (Note I'm using my own SimplexNoise here.)
So the problem is either in your bounds (Math.min( 1.0, point )) or in your graphics display (which you don't show) or in your SimplexNoise (which you also don't show).
import SimpleUtils.noise.SimplexNoise;
import java.awt.Image;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
/**
*
* #author Brenden Towey
*/
public class MapGenerator
{
public static void main( String[] args )
{
SwingUtilities.invokeLater( new Runnable()
{
public void run()
{
JFrame frame = new JFrame();
frame.add( new JLabel( new ImageIcon( new MapGenerator().toImage() )));
frame.pack();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
} );
}
public double[][] toRender;
int maxHeight = 300;
int xResolution = 200;
int yResolution = 200;
public MapGenerator()
{
double[][] result = new double[ xResolution ][ yResolution ];
SimplexNoise noise = new SimplexNoise();
for( int x = 0; x < xResolution; x++ )
for( int y = 0; y < yResolution; y++ )
result[x][y] = transformPoint( x, y, noise.noise(x, y) );
toRender = result;
}
private double transformPoint( int x, int y, double point )
{
point += 2 * Math.sin( x * Math.PI / xResolution )/2.0;
point += 2 * Math.sin( y * Math.PI / yResolution )/2.0;
return Math.min( 1.0, point);
}
public Image toImage()
{
BufferedImage image = new BufferedImage( xResolution,
yResolution, BufferedImage.TYPE_INT_RGB );
for( int x = 0; x < xResolution; x++ )
for( int y = 0; y < yResolution; y++ )
image.setRGB( x, y, greyScale( toRender[x][y] ) );
return image;
}
private int greyScale( double toRender )
{
int scale = (int) ( 255 * toRender );
return scale + (scale << 8) + (scale << 16);
}
}
Related
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:
I don't know whether or not this question was answered. At least I didn't find an answer.
So here is a thing: I'm making some space-themed 2D game on android, and I'm testing it on emulator with screen size = 2560x1600. In this game there is a field where space ship is flying. And of course it (a field) must have a beautiful background with high resolution. My background image's resolution is 4500x4500. I want to make my image move in opposite direction relative to camera movement, so thats why I can't use small static image. At the time only a part of this image is visible:
When I tried to draw it I got fps = 1-2 (of course it is low because of the image size):
canvas.drawBitmap(getBigImage(), -x, -y, null);
/* getBigImage() method does nothing but returning
a Bitmap object (no calculation or decoding is performing in there) */
I tried to cut out the needed image from the big one but fps was still low:
Bitmap b = Bitmap.createBitmap(getBigImage(), x, y, sw, sh);
canvas.drawBitmap(b, 0, 0, null);
How can I draw this big bitmap with high fps?
Try
drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)
which takes a rectangle of pixels from the source image to display in a rectangle on the Canvas. I found this to be faster back when I did a scrolling game.
I was thinking a lot and came up with an idea to divide input bitmap to small chunks and save them to an array. So now to draw that bitmap all I have to do is to draw visible chunks.
Picture:
Big black rectangle means input bitmap, green rectangle means viewport, red rectangle means visible chunks that are drawn
I've wrote an object that does that all (I didn't check it for bugs yet :/). I've tested it and it draws 3000x3000 bitmap with ~45 fps. I'm considering this way as very effective. The object itself may need to be developed more but I think this functionality is enough for my needs. Hope it'll help someone :)
P.S. https://stackoverflow.com/a/25953122/6121671 - used this for inspiration :)
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
public final class DividedBitmap {
private final Bitmap[][] mArray; // array where chunks is stored
private final int mWidth; // original (full) width of source image
private final int mHeight; // original (full) height of source image
private final int mChunkWidth; // default width of a chunk
private final int mChunkHeight; // default height of a chunk
/* Init */
public DividedBitmap(Bitmap src) {
this(new Options(src, 100, 100));
}
public DividedBitmap(Options options) {
mArray = divideBitmap(options);
mWidth = options.source.getWidth();
mHeight = options.source.getHeight();
mChunkWidth = options.chunkWidth;
mChunkHeight = options.chunkHeight;
}
/* Getters */
public int getWidth() {
return mWidth;
}
public int getHeight() {
return mHeight;
}
public Bitmap getChunk(int x, int y) {
if (mArray.length < x && x > 0 && mArray[x].length < y && y > 0) {
return mArray[x][y];
}
return null;
}
/* Methods */
/**
* x, y are viewport coords on the image itself;
* w, h are viewport's width and height.
*/
public void draw(Canvas canvas, int x, int y, int w, int h, Paint paint) {
if (x >= getWidth() || y >= getHeight() || x + w <= 0 || y + h <= 0)
return;
int i1 = x / mChunkWidth; // i1 and j1 are indices of visible chunk that is
int j1 = y / mChunkHeight; // on the top-left corner of the screen
int i2 = (x + w) / mChunkWidth; // i2 and j2 are indices of visible chunk that is
int j2 = (y + h) / mChunkHeight; // on the right-bottom corner of the screen
i2 = i2 >= mArray.length ? mArray.length - 1 : i2;
j2 = j2 >= mArray[i2].length ? mArray[i2].length - 1 : j2;
int offsetX = x - i1 * mChunkWidth;
int offsetY = y - j1 * mChunkHeight;
for (int i = i1; i <= i2; i++) {
for (int j = j1; j <= j2; j++) {
canvas.drawBitmap(
mArray[i][j],
(i - i1) * mChunkWidth - offsetX,
(j - j1) * mChunkHeight - offsetY,
paint
);
}
}
}
/* Static */
public static Bitmap[][] divideBitmap(Bitmap bitmap) {
return divideBitmap(new Options(bitmap, 100, 100));
}
public static Bitmap[][] divideBitmap(Options options) {
Bitmap[][] arr = new Bitmap[options.xCount][options.yCount];
for (int x = 0; x < options.xCount; ++x) {
for (int y = 0; y < options.yCount; ++y) {
int w = Math.min(options.chunkWidth, options.source.getWidth() - (x * options.chunkWidth));
int h = Math.min(options.chunkHeight, options.source.getHeight() - (y * options.chunkHeight));
arr[x][y] = Bitmap.createBitmap(options.source, x * options.chunkWidth, y * options.chunkHeight, w, h);
}
}
return arr;
}
public static final class Options {
final int chunkWidth;
final int chunkHeight;
final int xCount;
final int yCount;
final Bitmap source;
public Options(Bitmap src, int chunkW, int chunkH) {
chunkWidth = chunkW;
chunkHeight = chunkH;
xCount = ((src.getWidth() - 1) / chunkW) + 1;
yCount = ((src.getHeight() - 1) / chunkH) + 1;
source = src;
}
public Options(int xc, int yc, Bitmap src) {
xCount = xc;
yCount = yc;
chunkWidth = src.getWidth() / xCount;
chunkHeight = src.getHeight() / yCount;
source = src;
}
}
}
I have pie chart to display the rate of sales through out the year,now i want to draw a string at the center of each arc respectively,to specify the month in my pie chart
this is how my code looks like
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
public class TopSectionPanel extends JPanel {
double[] sales = {4000, 3000, 2000, 6000 , 10000 , 2500,
3400 , 8700 , 6734 , 1200 , 4500 , 6700};
double[] angle = new double[sales.length];
Color[] color = {Color.RED, Color.BLACK, Color.BLUE, Color.DARK_GRAY, Color.GREEN,
Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.WHITE,
Color.YELLOW, Color.GRAY};
double sum = 0;
/**
* Create the panel.
*/
public TopSectionPanel() {
setBackground(Color.CYAN);
setPreferredSize(new Dimension(400 , 400));
for(int l = 0 ; l < sales.length ; l++)
sum += sales[l];
for(int i = 0 ; i < angle.length ; i++)
angle[i] = sales[i] / sum * 360;
}
#Override
protected void paintComponent(Graphics g) {
// TODO Auto-generated method stub
super.paintComponent(g);
int radius;
// calculations to fit the circle in exact center
if(getHeight() < getWidth())
radius = (int) ((getHeight() / 2.) * 0.8) ;
else
radius = (int) ((getWidth() / 2.) * 0.8) ;
int diameter = radius * 2;
int x = (int)(getWidth() / 2) - radius;
int y = (int)(getHeight() / 2) - radius;
double a = 0;
for( int i = 0 ; i < angle.length ; i++ ) {
g.setColor(color[i]);
g.fillArc(x, y, diameter, diameter, (int)a, (int)angle[i]);
a = a + angle[i];
}
}
}
Not knowing minor details of the requirements such as distance from the arc or font or size I can only produce a raw sketch, using some elementary geometry. Add this after the loop drawing the pie chart.
int mx = x + radius;
int my = y + radius;
double b = 0;
int rad = (int)(radius*1.20);
for( int i = 0 ; i < angle.length ; i++ ) {
b += angle[i]/2;
double brad = b*Math.PI/180.0;
int ix = (int)(rad*Math.cos(brad));
int iy = (int)(rad*Math.sin(brad));
g.drawString( Integer.toString(i+1), mx+ix, my-iy );
b += angle[i]/2;
}
Note that the computed coordinates are the point on the baseline where the text starts. If you really want to center the text around this point you'll have to compute the bounding box for the text and modify the coordinates by these small amounts in x- and y-direction.
Really having trouble trying to draw a four leaf rose: This is the exercise:
Draw a picture of the “fourleaved rose” whose equation in polar coordinates is
r =cos(2θ) . Let θ go from 0 to 2*pi in 100 steps. Each time, compute r and then compute the (x, y) coordinates from the polar coordinates by using the formula
x = r ⋅ cos( θ) , y = r ⋅ sin(θ )
My Code:
public Rose(double awidth, double aheight)
{
width = awidth;
height = aheight;
theta = 0;
}
public void drawRose(Graphics2D g2)
{
Ellipse2D.Double test ;
double r = 0;
for(int i = 0; i <= 100; i++)
{
r = Math.cos(Math.toRadians(2*theta) );
x = r *( Math.cos( Math.toRadians(theta) ) * width ) + 300;
y = r * ( Math.sin( Math.toRadians(theta) ) * height ) + 300 ;
test = new Ellipse2D.Double(x, y, width, height);
theta += 3.6;
g2.draw(test);
}
}
}
Any help will be greatly appreciately.
Your biggest mistake is here:
test = new Ellipse2D.Double(x, y, width, height);
You're creating 100 Ellipses with the points that are on the rose, but that with heights and widths of the desired rose. You really don't want 100 ellipses, but rather you want to connect lines between the x and y points you've created, that is connect the current x, y with the previous ones created (as long as there is a previous x and y).
One way is via these suggestions, but there are other ways to do this:
Use a Path2D object, the concrete implementation would be a Path2D.Double, to hold your data points. Create this before creating data points.
Use a for loop that goes from 0 to 100, and do this in the class's constructor
set your double theta in the loop
set your double r variable in the loop
Calculate your x and y double points
Scale your x and y points by multiplying them with a scale factor so that the drawing has some size. I used 150.0
Translate your x and y values by adding a translation constant. I used 200 and it worked nicely in a 400 x 400 JPanel. Else the center of the rose will be at 0, 0 and only a fourth of it will be visible.
In the first iteration of the for loop call the Path2D's moveTo(...) method to add a starting point
In all other iterations call the lineTo(...) method. This will draw a line between neighboring points.
After the for loop, close the path by calling closePath() on it.
Draw the path in your JPanel's paintComponent method by casting your Graphics parameter into a Graphics2D object (actually you don't need this since your draw method gets a Grahpics2D object), and calling draw(path) with the Graphics2D object, passing in your Path2D object.
For example, this is created:
with this code:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
#SuppressWarnings("serial")
public class RosePanel extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private static final int MAX = 100;
private static final double SCALE = 150.0;
private static final double DELTA_X = 200;
private static final double DELTA_Y = DELTA_X;
private static final Color ROSE_COLOR = Color.red;
private static final Stroke ROSE_STROKE = new BasicStroke(8f);
private Path2D path = new Path2D.Double();
public RosePanel() {
for (int i = 0; i < MAX; i++) {
double theta = i * 2 * Math.PI / MAX;
double r = Math.cos(2 * theta);
double dX = SCALE * r * Math.cos(theta) + DELTA_X;
double dY = SCALE * r * Math.sin(theta) + DELTA_Y;
if (i == 0) {
path.moveTo(dX, dY);
} else {
path.lineTo(dX, dY);
}
}
path.closePath();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(ROSE_COLOR);
g2.setStroke(ROSE_STROKE);
g2.draw(path);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
RosePanel mainPanel = new RosePanel();
JFrame frame = new JFrame("RosePanel");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Note that the key difference between my code and yours, other than the translations and the scaling is that I'm connecting the line between points created.
I'm having trouble finding what's wrong with my program. When I execute it, it appears to get stuck into an infinite loop (or something similar) and I can't figure out what's wrong with my program. Here's what I have so far:
public class Spiral extends JComponent{
int WIDTH = 0;
int HEIGHT = 0;
public Spiral(int WIDTH, int HEIGHT) {
this.WIDTH = WIDTH;
this.HEIGHT = HEIGHT;
}
public void paintSpiral(Graphics g){
double a = 3;
double b = 0;
double t = 0;
double theta = Math.toRadians(t);
double r = theta * a + b;
double pi = Math.PI/180;
double end = 720 * pi;
int middle_x = WIDTH / 2;
int middle_y = HEIGHT / 2;
for (theta = 0; theta < end; theta += pi) {
double x = Math.cos(theta) * r + middle_x;
double y = Math.sin(theta) * r + middle_y;
int xx = (int) Math.round(x);
int yy = (int) Math.round(y);
g.drawLine(xx, yy, xx + 10, yy + 20);
}
}
public void paintComponent(Graphics g) {
paintSpiral(g);
}
public static void main(String[] args) {
int WINDOW_WIDTH = 1024;
int WINDOW_HEIGHT = 1024;
JFrame frame = new JFrame();
frame.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
// Set the title of the window
frame.setTitle("Archimedean Spiral");
// Make a new Spiral, add it to the window, and make it visible
Spiral d = new Spiral(1024, 1024);
frame.add(d);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
I have to use Graphics which is why I have the Math.round changing the x values to integers so I can actually draw the lines. That is what I suspect is the problem, but I can't seem to fix it. Any suggestions?
The loop variable t is of type int, so t += pi is effectively a no-op, resulting in an infinite loop.
t should be of type double. Furthermore, it should be local to paintSpiral and not a member of the class. I don't understand why you're using t (which is zero) to initialize r.
Also, your degrees and radians seem all confused.