im programing a small drawing program.
But i have a bug with painting. left, right, does work correct, but top, bottom creates curious images. why? ANyone a idea? Im programming in java.
The Bug: (left normal, right bug) And no it ISNT because mouseevent isnt getting all points. Im drawing all circles between to points i actually get. see the code.
The code for painting:
double m = 0;
int width = draggedX - mouseX;
int height = draggedY - mouseY;
if(draggedX - mouseX != 0){
m = (double) (draggedY - mouseY) / (double) (draggedX - mouseX);
}
if(width > 0){
for(int i = 0; i < width; i++) {
double x = mouseX + i;
double y = mouseY + (m * i);
g.fillOval((int) x, (int) y, 5, 5);
}
}
else if(width < 0){
for(int i = -width; i > 0; i--) {
double x = mouseX - i;
double y = mouseY - (m * i);
g.fillOval((int) x, (int) y, 5, 5);
}
}
else{
if(height > 0){
for(int i = 0; i < height; i++){
g.fillOval(mouseX, (int) i + mouseY, 5, 5);
}
}
else if(height < 0){
for(int i = -height; i > 0; i--){
g.fillOval(mouseX, (int) i - mouseY, 5, 5);
}
}
else{
g.fillOval(mouseX, mouseY, 5, 5);
}
}
Mouse events are tricky, they don't always follow a linear progression. Instead of using oval, I would simple paint lines between each distinct event point. If you need to make the lines thicker, the take a look at Stroke (or BasicStroke in particular)
For example Resize the panel without revalidation
I also have a very bad feeling your a painting directly to the graphics context by using getGraphics instead of using paintComponent
Suppose you draw a nearly vertical line with width = 10 and height = 200. With the given loop you will draw exactly 10 points, which is not enough to cover the entire line.
To fix this you need to check if height > width and in that case plot the points "vertically," iterating along the y-axis.
// when height > width
for(int i = 0; i < height; i++) {
double x = mouseX + i/m;
double y = mouseY + i;
g.fillOval((int) x, (int) y, 5, 5);
}
As you can see the algorithm to plot a single line becomes quite complex. That's why Bresenham's algorithm is such a big deal.
Related
I have a problem in regards to parallelism in javafx. More specifically, dividing the canvas of my program to run with multiple threads. When compiled, the program glitches out and doesn't work as supposed to. The code in the thread class is supposed to print out the Mandelbrot set, and the code in the application class is dividing the canvas into different parts that each should run in parallel. Here is the code:
public void MandelbrotSet(int n) {
int widthh = (int)canvas.getWidth();
int portion = widthh / n;
for (int i = 0; i < n; i++) {
int startX = i * portion;
int endX = startX + portion;
myMandelbrotParallelTEST2 thread = new myMandelbrotParallelTEST2(startX, endX, image, width, height, maximumIterations, canvas, zoom, xPos, yPos, hue, brightness, saturation, R, G, B);
thread.start();
//would add thread.join() here with try and catch
}
canvas.getGraphicsContext2D().drawImage(image, 0, 0);
}
this is how the code in the Thread class looks:
#Override
public void run(){
double centerY = canvas.getWidth() / 2.0;
double centerX = canvas.getHeight() / 2.0;
for (int x = start; x < end; x++) {
for (int y = 0; y < canvas.getHeight(); y++) {
double cr = xPos / width + (x - centerY) / zoom;
double ci = yPos / height + (y - centerX) / zoom; //getting position of the points on the canvas
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++;
}
int iterations = iterationsOfZ;
if (iterations == maximumIterations) { //inside the set
imagee.getPixelWriter().setColor(x, y, Color.rgb(R, G, B));
} else if (brightness == 0.9) { //white background
imagee.getPixelWriter().setColor(x, y, Color.hsb(hue, iterations / maximumIterations, brightness));
} else if (hue == 300) { //colorful background
imagee.getPixelWriter().setColor(x, y, Color.hsb(hue * iterations / maximumIterations, saturation, brightness));
} else if (hue == 0 && saturation == 0 && brightness == 1) {
imagee.getPixelWriter().setColor(x, y, Color.hsb(hue, saturation, brightness));
} else { //black background
imagee.getPixelWriter().setColor(x, y, Color.hsb(hue, saturation, iterations / brightness));
}
}
}
//canvas.getGraphicsContext2D().drawImage(imagee, 0, 0);
}
[this is how the program looks, and while panning around it glitches even more, but the execution time is really fast, which means that it is ran concurrently.]enter code here(https://i.stack.imgur.com/IZUfY.png)
I found that adding thread.join() fixes the problem, but then the program is ran on a single thread. Could anyone help me out resolve this issue?
What you are trying to do cannot be done with a Canvas because of the fx application thread restiction which has already been mentioned.
But it can be done via the WritableImage of JavaFX 13+ with support for Buffers. Writing into the buffer is not restricted in the above way and can thus be done in parallel with high performance.
I have compiled some examples (not any parallel ones) of how to use WritableImages with Buffers here: https://github.com/mipastgt/JFXToolsAndDemos
I made a little program to approximate Pi and I would like to represent it. I started but I thought it would look better if the points outside the circle were red and the ones inside were green. I can't do it and I don't understand why the problem is there. I don't know if it is in pure math or in dev.
for(int i = 0; i<1000; i++) {
double x = Math.random() * (500-250) + 250;
double y = Math.random() * 250;
double applyformula = (x - 250)*(x - 250) + (y - 250) * (y - 250);
if(applyformula == y || (y*y) - 500 * y < applyformula ) {
g.fillOval((int) x,(int) y, 5, 5);
g.setColor(Color.GREEN);
} else {
g.fillOval((int) x,(int) y, 5, 5);
g.setColor(Color.RED);
}
}
If someone could help me, that will be perfect.
It could be done in a simpler way.
for (int i = 0; i < 1000; i++) {
double x = Math.random();
double y = Math.random();
double applyformula = (x * x) + (y * y);
if (applyformula <= 1) {
g.setColor(Color.GREEN);
} else {
g.setColor(Color.RED);
}
// Set the actual coordinates in a 250 pixels wide square.
x *= 250;
y *= 250;
g.fillOval((int) x, (int) y, 5, 5);
}
I appear to have hit a wall in my most recent project involving wave/ripple generation over an image. I made one that works with basic colors on a grid that works perfectly; heck, I even added shades to the colors depending on the height of the wave.
However, my overall goal was to make this effect work over an image like you would see here. I was following an algorithm that people are calling the Hugo Elias method (though idk if he truly came up with the design). His tutorial can be found here!
When following that tutorial I found his pseudo code challenging to follow. I mean the concept for the most part makes sense until I hit the height map portion over an image. The problem being the x and y offsets throw an ArrayIndexOutOfBoundsException due to him adding the offset to the corresponding x or y. If the wave is too big (i.e. in my case 512) it throws an error; yet, if it is too small you can't see it.
Any ideas or fixes to my attempted implementation of his algorithm?
So I can't really make a compile-able version that is small and shows the issue, but I will give the three methods I'm using in the algorithm. Also keep in mind that the buffer1 and buffer2 are the height maps for the wave (current and previous) and imgArray is a bufferedImage represented by a int[img.getWidth() * img.getHeight()] full of ARGB values.
Anyways here you go:
public class WaveRippleAlgorithmOnImage extends JPanel implements Runnable, MouseListener, MouseMotionListener
{
private int[] buffer1;
private int[] buffer2;
private int[] imgArray;
private int[] movedImgArray;
private static double dampening = 0.96;
private BufferedImage img;
public WaveRippleAlgorithmOnImage(BufferedImage img)
{
this.img = img;
imgArray = new int[img.getHeight()*img.getWidth()];
movedImgArray = new int[img.getHeight()*img.getWidth()];
imgArray = img.getRGB(0, 0,
img.getWidth(), img.getHeight(),
null, 0, img.getWidth());
//OLD CODE
/*for(int y = 0; y < img.getHeight(); y++)
{
for(int x = 0; x < img.getWidth(); x++)
{
imgArray[y][x] = temp[0 + (y-0)*img.getWidth() + (x-0)];
}
}*/
buffer1 = new int[img.getHeight()*img.getWidth()];
buffer2 = new int[img.getHeight()*img.getWidth()];
buffer1[buffer1.length/2] = (img.getWidth() <= img.getHeight() ? img.getWidth() / 3 : img.getHeight() / 3);
//buffer1[25][25] = 10;
back = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);
this.addMouseListener(this);
this.addMouseMotionListener(this);
}
//<editor-fold defaultstate="collapsed" desc="Used Methods">
#Override
public void run()
{
while(true)
{
this.update();
this.repaint();
this.swap();
}
}
//Called from Thread to update movedImgArray prior to being drawn.
private void update()
{
//This is my attempt of trying to convert his code to java.
for (int i=img.getWidth(); i < imgArray.length - 1; i++)
{
if(i % img.getWidth() == 0 || i >= imgArray.length - img.getWidth())
continue;
buffer2[i] = (
((buffer1[i-1]+
buffer1[i+1]+
buffer1[i-img.getWidth()]+
buffer1[i+img.getWidth()]) >> 1)) - buffer2[i];
buffer2[i] -= (buffer2[i] >> 5);
}
//Still my version of his code, because of the int[] instead of int[][].
for (int y = 1; y < img.getHeight() - 2; y++)
{
for(int x = 1; x < img.getWidth() - 2; x++)
{
int xOffset = buffer1[((y)*img.getWidth()) + (x-1)] - buffer1[((y)*img.getWidth()) + (x+1)];
int yOffset = buffer1[((y-1)*img.getWidth()) + (x)] - buffer1[((y+1)*img.getWidth()) + (x)];
int shading = xOffset;
//Here is where the error occurs (after a click or wave started), because yOffset becomes -512; which in turn gets
//multiplied by y... Not good... -_-
movedImgArray[(y*img.getWidth()) + x] = imgArray[((y+yOffset)*img.getWidth()) + (x+xOffset)] + shading;
}
}
//This is my OLD code that kidna worked...
//I threw in here to show you how I was doing it before I switched to images.
/*
for(int y = 1; y < img.getHeight() - 1; y++)
{
for(int x = 1; x < img.getWidth() - 1; x++)
{
//buffer2[y][x] = ((buffer1[y][x-1] +
//buffer1[y][x+1] +
//buffer1[y+1][x] +
//buffer1[y-1][x]) / 4) - buffer2[y][x];
buffer2[y][x] = ((buffer1[y][x-1] +
buffer1[y][x+1] +
buffer1[y+1][x] +
buffer1[y-1][x] +
buffer1[y + 1][x-1] +
buffer1[y + 1][x+1] +
buffer1[y - 1][x - 1] +
buffer1[y - 1][x + 1]) / 4) - buffer2[y][x];
buffer2[y][x] = (int)(buffer2[y][x] * dampening);
}
}*/
}
//Swaps buffers
private void swap()
{
int[] temp;
temp = buffer2;
buffer2 = buffer1;
buffer1 = temp;
}
//This creates a wave upon clicking. It also is where that 512 is coming from.
//512 was about right in my OLD code shown above, but helps to cause the Exeception now.
#Override
public void mouseClicked(MouseEvent e)
{
if(e.getX() > 0 && e.getY() > 0 && e.getX() < img.getWidth() && e.getY() < img.getHeight())
buffer2[((e.getY())*img.getWidth()) + (e.getX())] = 512;
}
private BufferedImage back;
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
back.setRGB(0, 0, img.getWidth(), img.getHeight(), movedImgArray, 0, img.getWidth());
g.drawImage(back, 0, 0, null);
}
}
P.S. Here are two images of the old code working.
Looking at my original pseudocode, I assume the Array Out Of Bounds error is happening when you try to look up the texture based on the offset. The problem happens because the refraction in the water is allowing us to see outside of the texture.
for every pixel (x,y) in the buffer
Xoffset = buffer(x-1, y) - buffer(x+1, y)
Yoffset = buffer(x, y-1) - buffer(x, y+1)
Shading = Xoffset
t = texture(x+Xoffset, y+Yoffset) // Array out of bounds?
p = t + Shading
plot pixel at (x,y) with colour p
end loop
The way to fix this is simply to either clamp the texture coordinates, or let them wrap. Also, if you find that the amount of refraction is too much, you can reduce it by bit-shifting the Xoffset and Yoffset values a little bit.
int clamp(int x, int min, int max)
{
if (x < min) return min;
if (x > max) return max;
return x;
}
int wrap(int x, int min, int max)
{
while (x<min)
x += (1+max-min);
while (x>max)
x -= (1+max-min);
return x;
}
for every pixel (x,y) in the buffer
Xoffset = buffer(x-1, y) - buffer(x+1, y)
Yoffset = buffer(x, y-1) - buffer(x, y+1)
Shading = Xoffset
Xoffset >>= 1 // Halve the amount of refraction
Yoffset >>= 1 // if you want.
Xcoordinate = clamp(x+Xoffset, 0, Xmax) // Use clamp() or wrap() here
Ycoordinate = clamp(y+Yoffset, 0, Ymax) //
t = texture(Xcoordinate, Ycoordinate)
p = t + Shading
plot pixel at (x,y) with colour p
end loop
So I was wondering how to run through a multi-dimensional array without having to use for loops to test whether or not the objects are intersecting in a rectangle and then render the interesting?
Currently I'm using two for loops to go through it and within the nested loop I have to use the intersecting() method and since this needs to happen every frame my game is getting low FPS. I assume it's because I have 650x350 entities in my array. I'll show the code below and restate the question. So my official question is how do I test whether or not an entity is intersecting with my rectangle camera so that it doesn't lag the game?
for (int x = 0; x < entities.length; x++) // entities.length is 650
{
for (int y = 0; y < entities[0].length; y++) // entities[0].length is 350
{
if (camera.intersecting(entities[x][y]))
{
entities[x][y].render(g); // X and Y indices are multiplied by 32 to get the position
}
}
}
This depends quite a bit on the mechanics of your game. But in a general sense I will recommend looking into Quadtrees ( http://en.m.wikipedia.org/wiki/Quadtree). Then check which nodes your camera overlaps and draw their contents.
When I ran into this problem awhile ago, I made my render function use the camera's x, y, width, and height values to determine where to render by changing the starting positions of the for loops.
public void render(Camera camera, Graphics g) {
// --------------------------------------------------------- //
// Optimized rendering function to only draw what the camera //
// can see //
// --------------------------------------------------------- //
int minX = toTileX(camera.getX());
int minY = toTileY(camera.getY());
int maxX = toTileX(camera.getX()) + toTileX(camera.getViewWidth());
int maxY = toTileY(camera.getY()) + toTileY(camera.getViewHeight());
for (TiledLayer l : layers) {
for (int y = minY; y < maxY + 1; y++) {
if (y < height && y >= 0) {
for (int x = minX; x < maxX + 1; x++) {
if (x < width && x >= 0) {
Tile tile = l.getTile(x, y);
TileSheet sheet = null;
try {
sheet = tileSheets.get(tile.getTileSetId());
} catch (InvalidKeyException e) {
e.printStackTrace();
}
Image image = sheet.getImage(tile.getTileId());
g.drawImage(image,
x * tileWidth,
y * tileHeight,
(x * tileWidth) + tileWidth,
(y * tileHeight) + tileHeight,
0,
0,
tileWidth,
tileHeight);
}
}
}
}
}
}
Im writing a small painting programm in java, and im stucked on the pen:
Therory: When im dragging the mouse i have to fill the circles between P(draggedX|draggedY) and P2(mouseX|mouseY) with circles. So i have to create a line / a path (?..) and calculate all points that are on it.
What ive tried:
double m = 0;
int width = draggedX - mouseX;
int height = draggedY - mouseY;
if(draggedX - mouseX != 0){
m = (draggedY - mouseY) / (draggedX - mouseX);
}
if(width > 0){
for(int i = 0; i < width; i++) {
double x = mouseX + i;
double y = mouseY + (m * i);
g.fillOval((int) x, (int) y, 5, 5);
}
}
else if(width < 0){
for(int i = -width; i > 0; i--) {
double x = mouseX + i;
double y = mouseY + (m * i);
g.fillOval((int) x, (int) y, 5, 5);
}
}
else{
if(height > 0){
for(int i = 0; i < height; i++){
g.fillOval(mouseX, (int) i + mouseY, 5, 5);
}
}
else if(height < 0){
for(int i = -height; i > 0; i--){
g.fillOval(mouseX, (int) i + mouseY, 5, 5);
}
}
}
It didnt work correct. sometimes curious lines splashed up and circles werent painted, like this:
Any other ideas, how to solve it?
Thank you!
Java will not generate events for all intermediate points - you can test this by drawing a point at each place where you actually receive an event. If the mouse moves too quickly, you will miss points. This happens in all drawing programs.
Bresenham's line-drawing algorithm is the traditional way to find integer pixels between two pixels coordinates. But you are programming in Java, and you have something much better: you can trace arbitrary paths, defined through coordinates. Two flavors are available,
The old Graphics version (g is a Graphics, possibly from your paintComponent() method):
// uses current g.setColor(color)
g.drawPolyline(xPoints, yPoints, int nPoints); // xPoints and yPoints are integer arrays
And the new Shape-based version (g2d is a Graphics2D; your Graphics in Swing can be cast to Graphics2D without problems):
// uses current stroke
g2d.draw(p); // p is a Path2D, build with successive moveTo(point) and lineTo(point)
I recommend the second version, since the stroke offers a lot more flexibility (line width, dashes, ...) than just simple colors
The division between two integers discards the fractional part: for example 2/3 returns 0. You can use floating point types for computation to keep the fractional parts.
double m;
m = (double) (draggedY - mouseY) / (draggedX - mouseX);
In addition to what the other answer said, you also need to do your drawing differently if the absolute value of m is greater than 1 or not. If it's 1 or less, then you'll want to iterate along the x direction and calculate the y from the slope. Otherwise, you'll need to iterate along the y direction and calculate the m from the (inverse) slope. You have the right idea in the code, but it's not quite implemented correctly. It should be something more like this:
if (abs(m) <= 1)
{
for (int i = startX; i < endX; i++)
{
float y = startY + (float)i * m;
float x = i;
g.fillOval(x, y, 5, 5);
}
}
else
{
for (int i = startY; i < endY; i++)
{
float x = startX + (float)i / m;
float y = i;
g.fillOval(x, y, 5, 5);
}
}