Flipping graphics object without using G2D? - java

Say I have a shape that I have drawn through paintComponent(), fillRect, or drawOval,
How would I go about flipping it, so that the shape would not be going from the top left, but from the bottom upwards?

All I had to do was use the height of my panel, and subtract it from the height of the object.
for ex:
int height = (1000 - n);
where n is the height that you wish the shape to be.

Related

custom Zoom in/out on JPanel is not linear

To avoid wasting your time I will make it short:
My project: a graphical presentation of the Mandelbrot set.
My progress: finished except a bug in the zoom in/zoom out feature
The bug: The zoom in scale is smaller (slower) than the zoom out scale
The shorter version: how can I simulate a zoom in/out by a factor on a jpanel.
In the end a small section of the jPanel should be enlarged to the whole jPanel.
Every pixel in the jPanel represents an integer number, but you don't have to worry about a lack of integers after the zoom in.
The longer version (only if you need more information):
the zoom-function in the Mandelbrot set:
public void recalculate(int x, int y, int width, int height, double scale){
downright=new Complex(upleft.getReal()+stepwidth*(x+width/scale),upleft.getImg()-stepwidth*(y+height/scale));
upleft=new Complex(upleft.getReal()+stepwidth*(x-width/scale), upleft.getImg()-stepwidth*(y-height/scale));
stepwidth=(downright.getReal()-upleft.getReal())/width;
calc(lastrep);
}
A short terminology:
downright is the bottom-right complex number, the last pixel to be painted on the jpanel
upleft is, therefore, the top-left complex-number, the first pixel to be painted on the jpanel
stepwidth is the distance between each complex number represented by one pixel
calc is finally the function that calculates every color for each pixel according to the Mandelbrot set rules
the width and height parameters are the pixel width/height of the jpanel, x and y are the coordination where to zoom in and scale the factor for the zoom.
When I call this function mandelbrot.recalculate(x,y, getWidth(), getHeight(), 10); than this should zoom in on the point (x,y) so that the new represented image is 1/5 of the actual image.
1/5 should be because the start of the image (upleft) is 1/10 of the amount of pixels of the full width and height shifted to the up and left of the point (x,y) and the end of the image (downright) is 1/10 of the amount of pixels of the full width and height shifted down and right.
So if I call the function after the zoom in like this: mandelbrot.recalculate(x,y, getWidth(), getHeight(), 0.1);
it should reverse the whole process, but it doesn't
this is the intended zoom:
The Solution was rather simple.
The Problem was the attempt to calculate the zoom in as well as the shift to a new midpoint in one go.
Since the shift to the new midpoint is dependent on the zoom in, you can't put that into one equation, or at least only with a lot of effort.
I changed my code like shown below, so that I first calculate the zoom in and then shift the anchor to the wanted position. Sometimes the longer way is the better way.
//The new midpoint needs the old stepwith to calculate correctly
Complex newmidpoint = new Complex(upleft.getReal()+x*stepwidth, upleft.getImg()-y*stepwidth);
//zooming in
double distwidth = downright.getReal()-upleft.getReal();
double distheight = upleft.getImg()-downright.getImg();
double newreal = upleft.getReal()+(1/scale)*distwidth;
double newimg = upleft.getImg()-(1/scale)*distheight;
downright=new Complex(newreal, newimg);
stepwidth=(downright.getReal()-upleft.getReal())/width;
//the old midpoint is actually the already zoomed in midpoint, it needs the new stepwidth
Complex oldmidpoint = new Complex(upleft.getReal()+width/2.0*stepwidth,upleft.getImg()-height/2.0*stepwidth);
Complex diffmidpoint = newmidpoint.subtract(oldmidpoint);
upleft=new Complex(upleft.getReal()+diffmidpoint.getReal(),upleft.getImg()+diffmidpoint.getImg());
downright=new Complex(downright.getReal()+diffmidpoint.getReal(), downright.getImg()+diffmidpoint.getImg());
calc(lastrep);

Set background color in java Graphics object

Good day,
Know that in Java Graphics object, we can user the setColor() method to set the object color. But this is only apply to the object border. Is it anyway to set color for the whole object? I means the background of the Graphics object.
void draw(Graphics g)
{
g.setColor(color);
g.drawRect(left, right, width, height);
}
Kindly advise.
use fillRect() method .
g.fillRect(left, right, width, height);
from javadoc
drawRect()
Draws the outline of the specified rectangle. The left and right edges of the rectangle are at x and x + width. The top and bottom edges are at y and y + height. The rectangle is drawn using the graphics context's current color.
fillRect()
Fills the specified rectangle. The left and right edges of the rectangle are at x and x + width - 1. The top and bottom edges are at y and y + height - 1. The resulting rectangle covers an area width pixels wide by height pixels tall. The rectangle is filled using the graphics context's current
color.
" this is only apply to the object border " because drawRect draw the outlines only.
" Is it anyway to set color for the whole object? " well you misunderstand . and setColor() set the color to what you draw if you draw a outline then you can see outline only and it's not because of setColor() set colors to border .

Render Fog-Of-War on 2d tilemap

I am currently creating a small 2d-game with lwjgl.
I tried to figure out a way of implementing a Fog-Of-War.
I used a black backgound with alpha set to 0.5.
Then I added a Square, to set alpha to 1 for each tile, which is lit, ending up having a black Background with differend Alpha values.
Then I rendered my Background using the blendfunction:
glBlendFunc(GL_ZERO, GL_SRC_ALPHA)
This works well, but now I have a problem with adding a second layer with transparent parts and apply the Fog-Of-War on them, too.
I've read something about FrameBufferObjects, but I don't know how to use them and if they are the right choice.
Later on I want to lit tiles with an texture/Image to give it a smoother look. So these textures may overlap. This is the reason why I chose to first render the Fog-Of-War.
Do you have an idea how to fix this problem?
Thanks to samgak.
Now I try to render a dark square on each dark tile exept the lit tiles.
I divided each tile in an 8x8 grid for more details. This is my method:
public static void drawFog() {
int width = map.getTileWidth()>>3; //Divide by 8
int height = map.getTileHeight()>>3;
int mapWidth = map.getWidth() << 3;
int mapHeight = map.getHeight() << 3;
//background_x/y is the position of the background in pixel
int mapStartX = (int) Math.floor(background_x / width);
int mapStartY = (int) Math.floor(background_y / height);
//Multiply each color component with 0.5 to get a darker look
glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
glColor4f(0.0f, 0.0f, 0.0f, 0.5f);
glBegin(GL_QUADS);
//RENDERED_TILES_X/Y is the amount of tiles to fill the screen
for(int x = mapStartX; x < (RENDERED_TILES_X<<3) + mapStartX
&& x < mapWidth; x++){
for(int y = mapStartY; y < (RENDERED_TILES_Y<<3) + mapStartY
&& y < mapHeight; y++){
//visible is an boolean-array for each subtile
if(!visible[x][y]){
float tx = (x * width) - background_x;
float ty = (y * height) - background_y;
glVertex2f(tx, ty);
glVertex2f(tx+width, ty);
glVertex2f(tx+width, ty+height);
glVertex2f(tx, ty+height);
}
}
}
glEnd();
}
I set the visible array to false except for an small square.
It will render fine, but if I move the background the whole screen except the visible square turns black.
One approach is to render the Fog-of-War layer last, using an untextured black square rendered over the top of all the other layers after they have been rendered.
Use this blend function:
glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA)
and set the Fog-of-War alpha per-vertex so that when it is 1.0 the black overlay is transparent, and when it is 0.0, it is entirely black. (If you want the alpha to have the opposite meaning, just swap the arguments).
To make it more smooth you can set the alpha per vertex at each of the corners of the square to vary smoothly across it. You could also use a texture with varying alpha values instead of a plain black square, or subdivide the square into 4 or 16 squares to allow finer control.

What is the source of these pixel gaps in between identical vertices in OpenGL's Ortho? How can I eliminate them?

Despite passing equal (exactly equal) coordinates for 'adjacent' edges, I'm ending up with some strange lines between adjacent elements when scaling my grid of rendered tiles.
My tile grid rendering algorithm accepts scaled tiles, so that I can adjust the grid's visual size to match a chosen window size of the same aspect ratio, among other reasons. It seems to work correctly when scaled to exact integers, and a few non-integer values, but I get some inconsistent results for the others.
Some Screenshots:
The blue lines are the clear color showing through. The chosen texture has no transparent gaps in the tilesheet, as unused tiles are magenta and actual transparency is handled by the alpha layer. The neighboring tiles in the sheet have full opacity. Scaling is achieved by setting the scale to a normalized value obtained through a gamepad trigger between 1f and 2f, so I don't know what actual scale was applied when the shot was taken, with the exception of the max/min.
Attribute updates and entity drawing are synchronized between threads, so none of the values could have been applied mid-draw. This isn't transferred well through screenshots, but the lines don't flicker when the scale is sustained at that point, so it logically shouldn't be an issue with drawing between scale assignment (and thread locks prevent this).
Scaled to 1x:
Scaled to A, 1x < Ax < Bx :
Scaled to B, Ax < Bx < Cx :
Scaled to C, Bx < Cx < 2x :
Scaled to 2x:
Projection setup function
For setting up orthographic projection (changes only on screen size changes):
.......
float nw, nh;
nh = Display.getHeight();
nw = Display.getWidth();
GL11.glOrtho(0, nw, nh, 0, 1, -1);
orthocenter.setX(nw/2); //this is a Vector2, floats for X and Y, direct assignment.
orthocenter.setY(nh/2);
.......
For the purposes of the screenshot, nw is 512, nh is 384 (implicitly casted from int). These never change throughout the example above.
General GL drawing code
After cutting irrelevant attributes that didn't fix the problem when cut:
#Override
public void draw(float xOffset, float yOffset, float width, float height,
int glTex, float texX, float texY, float texWidth, float texHeight) {
GL11.glLoadIdentity();
GL11.glTranslatef(0.375f, 0.375f, 0f); //This is supposed to fix subpixel issues, but makes no difference here
GL11.glTranslatef(xOffset, yOffset, 0f);
if(glTex != lastTexture){
GL11.glBindTexture(GL11.GL_TEXTURE_2D, glTex);
lastTexture = glTex;
}
GL11.glBegin(GL11.GL_QUADS);
GL11.glTexCoord2f(texX,texY + texHeight);
GL11.glVertex2f(-height/2, -width/2);
GL11.glTexCoord2f(texX + texWidth,texY + texHeight);
GL11.glVertex2f(-height/2, width/2);
GL11.glTexCoord2f(texX + texWidth,texY);
GL11.glVertex2f(height/2, width/2);
GL11.glTexCoord2f(texX,texY);
GL11.glVertex2f(height/2, -width/2);
GL11.glEnd();
}
Grid drawing code (dropping the same parameters dropped from 'draw'):
//Externally there is tilesize, which contains tile pixel size, in this case 32x32
public void draw(Engine engine, Vector2 offset, Vector2 scale){
int xp, yp; //x and y position of individual tiles
for(int c = 0; c<width; c++){ //c as in column
xp = (int) (c*tilesize.a*scale.getX()); //set distance from chunk x to column x
for(int r = 0; r<height; r++){ //r as in row
if(tiles[r*width+c] <0) continue; //skip empty tiles ('air')
yp = (int) (r*tilesize.b*scale.getY()); //set distance from chunk y to column y
tileset.getFrame(tiles[r*width+c]).draw( //pull 'tile' frame from set, render.
engine, //drawing context
new Vector2(offset.getX() + xp, offset.getY() + yp), //location of tile
scale //scale of tiles
);
}
}
}
Between the tiles and the platform specific code, vectors' components are retrieved and passed along to the general drawing code as pasted earlier.
My analysis
Mathematically, each position is an exact multiple of the scale*tilesize in either the x or y direction, or both, which is then added to the offset of the grid's location. It is then passed as an offset to the drawing code, which translates that offset with glTranslatef, then draws a tile centered at that location through halving the dimensions then drawing each plus-minus pair.
This should mean that when tile 1 is drawn at, say, origin, it has an offset of 0. Opengl then is instructed to draw a quad, with the left edge at -halfwidth, right edge at +halfwidth, top edge at -halfheight, and bottom edge at +halfheight. It then is told to draw the neighbor, tile 2, with an offset of one width, so it translates from 0 to that width, then draws left edge at -halfwidth, which should coordinate-wise be exactly the same as tile1's right edge. By itself, this should work, and it does. When considering a constant scale, it breaks somehow.
When a scale is applied, it is a constant multiple across all width/height values, and mathematically shouldn't make anything change. However, it does make a difference, for what I think could be one of two reasons:
OpenGL is having issues with subpixel filling, ie filling left of a vertex doesn't fill the vertex's containing pixel space, and filling right of that same vertex also doesn't fill the vertex's containing pixel space.
I'm running into float accuracy problems, where somehow X+width/2 does not equal X+width - width/2 where width = tilewidth*scale, tilewidth is an integer, and X is a float.
I'm not really sure about how to tell which one is the problem, or how to remedy it other than to simply avoid non-integer scale values, which I'd like to be able to support. The only clue I think might apply to finding the solution is how the pattern of line gaps isn't really consistant (see how it skips tiles in some cases, only has vertical or horizontal but not both, etc). However, I don't know what this implies.
This looks like it's probably a floating point precision issue. The critical statement in your question is this:
Mathematically, each position is an exact multiple [..]
While that's mathematically true, you're dealing with limited floating point precision. Sequences of operations that should mathematically produce the same result can (and often do) produce slightly different results due to rounding errors during expression evaluation.
Specifically in your case, it looks like you're relying on identities of this form:
i * width + width/2 == (i + 1) * width - width/2
This is mathematically correct, but you can't expect to get exactly the same numbers when evaluating the values with limited floating point precision. Depending on how the small errors end up getting rounded to pixels, it can result in visual artifacts.
The only good way to avoid this is that you actually use the same values for coordinates that must be the same, instead of using calculations that mathematically produce the same results.
In the case of coordinates on a grid, you could calculate the coordinates for each grid line (tile boundary) once, and then use those values for all draw operations. Say if you have n tiles in the x-direction, you calculate all the x-values as:
x[i] = i * width;
and then when drawing tile i, use x[i] and x[i + 1] as the left and right x-coordinates.

How to draw a square with the mouse

What I'm trying to do is basically the thing you can do in the desktop when you click and drag te mouse making a square. The problem is I don't know how to make it draw "backwards" or how to clean the previous parameters when you start a new square. here is the entire code:
public void paint (Graphics j){
super.paint(j);
j.drawRect(x,y,z,w);
}
private void formMousePressed(java.awt.event.MouseEvent evt) {
x=evt.getX();
y=evt.getY();
repaint();
}
private void formMouseDragged(java.awt.event.MouseEvent evt) {
z=evt.getX();
w=evt.getY();
repaint();
}
The signature for drawRect is: drawRect(int x, int y, int width, int height). You need to calculate the top left corner of the square, and the width and height.
The top-left corner is (min(x, z), min(y, w)).
The width is abs(x-z) and the height is abs(y-w)
Putting this together we get
Try
j.drawRect(Math.min(x, z), Math.min(y, w), Math.abs(x - z), Math.abs(y - w));
Why does this work? Well you're given 2 points. It's a well known fact that 2 points can determine a square(opposite corners). The first problem is that you have to translate the points you're given, into an input that java likes. In this case, you first need the upper left hand corner. You don't know which point you have is that corner, or actually it could be that neither of them are.
So what do we know about the upper left corner? We know that it's x value is the smallest x value that exists in the square. We also know that at least one of the 2 points given rest on that same edge. Using this information we can determine that the x coordinate of the top left corner is the smallest x value of our 2 points. Or min(x, z). We use the same procedure to find the y coordinate.
Now width and height are easy. The width is the right edge - the left edge. We don't know which point is the right side, and which is the left side, but it doesn't matter. If we take the absolute value of the difference will always give you the positive difference between the points. In this case abs(x-z). The process is the same for the height.
As for resetting the square try adding a formMouseReleased method and setting x, y, z, w to 0.
I think you might create a method that resets the parameters
something like: void modifyMouse() in your Mouse class
//your parameters=0
I might try to give you a better help if you clarify your question, for now try that.

Categories