Way to get around integer requirements of g2.drawLine() method - java

I am currently trying to draw a line and keep the line proportionally the same no matter how the user resizes the JFrame. However, the problem I run into is when I try to draw a line when the user makes the JFrame smaller than the default value, as I end up multiplying the coordinates by fractions under 1, and since g2.drawLine() requires integers, it takes them as 0's and nothing is drawn. I'm wondering if there's a work-around to this little glitch or if you guys have any suggestions of how I should change my logic.

I think what you're seeing is just because of integer division. See Why is the result of 1/3 == 0?. When you have (width / 624), the result of this is always 0 if width is less than 624.
You could:
use (width / 624.0), which performs the division in floating-point (as double), or
you could rearrange your parentheses to be e.g. (int) ((x * width) / 624)) instead of (int) (x * (width / 624)).
However, to answer the question directly, you can draw a line with floating-point coordinates by using java.awt.geom.Line2D:
Line2D line2D = new Line2D.Double(x1, y1, x2, y2);
graphics2D.draw(line2D);
(Also see https://docs.oracle.com/javase/tutorial/2d/geometry/primitives.html.)

multiplying the coordinates by fractions under 1, and since
g2.drawLine() requires integers, it takes them as 0's
That's obviously false! If a coordinates is say, 327, then multiplying it by say 0.7 gives 228.9. That's not an integer but is has integer part, so you can safely convert it to integer:
double factor = ...;
int newCoord, oldCoord = ...;
newCoord = (int)(oldCoord * factor)
will give you the rounded result.
Or something else is wrong...

Related

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.

Rotate a triangle continuously

I have a float[] newCoords variable that has a size of 9. The first 3 entries represent one vertex, the next 3 represent the second vertex and the last 3 represent the last vertex.
I have some code that is supposed to rotate a triangle anywhere in space when I feed it the coordinates. It looks like this:
float s = (float) Math.sin(0.5);
float c = (float) Math.cos(0.5);
float[] centroid = getCentroid(newCoords);
newCoords[0] -= centroid[0];
newCoords[1] -= centroid[1];
newCoords[3] -= centroid[0];
newCoords[4] -= centroid[1];
newCoords[6] -= centroid[0];
newCoords[7] -= centroid[1];
newCoords[0] = (newCoords[0] * c) - (newCoords[1] * s);
newCoords[1] = (newCoords[0] * s) + (newCoords[1] * c);
newCoords[3] = (newCoords[3] * c) - (newCoords[4] * s);
newCoords[4] = (newCoords[3] * s) + (newCoords[4] * c);
newCoords[6] = (newCoords[6] * c) - (newCoords[7] * s);
newCoords[7] = (newCoords[6] * s) + (newCoords[7] * c);
newCoords[0] += centroid[0];
newCoords[1] += centroid[1];
newCoords[3] += centroid[0];
newCoords[4] += centroid[1];
newCoords[6] += centroid[0];
newCoords[7] += centroid[1];
The problem is, its not rotating it properly, the triangles are spinning and getting smaller and smaller until they disappear for some reason, can anyone see why this is happening?
EDIT: whoops, almost forgot, here is my getCentroid() method.
private float[] getCentroid(float[] p1) {
float[] newCoords = new float[] {(p1[0] + p1[3] + p1[6]) / 3.0f,
(p1[1] + p1[4] + p1[7]) / 3.0f, 0};
return newCoords;
}
I see two problems with your code. Both are fixed with a little change.
You try to apply a rotation operation, taking X and Y coordinates as input and having the new X and Y as output. For every vertex you rotate, you have two lines of code: the first computes the X, the second the Y coordinate. But when computing the Y coordinate, you use the already rotated X coordinate! That's wrong.
There is also a numerical problem. You reuse the old values again and again, resulting in a chain of rotation computations a value makes though, so the numerical errors sum up. Never rely on such computations to work as expected. Instead, you should work with the original values and increase the angle in each frame. This makes sure that each value only participated in a single rotation computation.
For fixing both problems, keep the original coordinates somewhere in your code, I call them coords, and rewrite the code such that you take that array as input (keep newCoords as the output). Increase the rotation angle in each frame to achieve a rotation animation.
This fixes both problems because you get rid of that chain and also you have different arrays for input and output in your rotation function.
Pseudo-code:
// initial:
angle = 0.0;
coords = (initial coordinates)
// per frame:
angle += 0.5;
newCoords = rotate(coords, angle);
draw(newCoords);
Also, please note that 0.5 is a large angle if you want to rotate by that angle frame by frame. The math functions expect angle in radians (not degrees), so you might want to use a lower value depending on what you want to visualize in particular.
You might wonder why I reuse the old angle in each frame, as according to the above mentioned problem 2., it should introduce numerical problems, since it's also a chain of computations. That's not a problem with the rotation angle, as a simple summation doesn't show such bad numerical errors you experience with applying rotations. Yet it has some problems, but they only show up at very long running times when the angle reaches some billions. The reason why such a summation in general is not that bad is because you're changing the variable in the same direction in each frame as well as a slightly off rotation angle isn't noticed very much by the user.

Java Floating Point Math Mistake?

I am trying to calculate an aspect ratio for an image:
Log.d("img.width:", String.valueOf(img.getIntrinsicWidth()));
Log.d("img.height:", String.valueOf(img.getIntrinsicHeight()));
float aspect = ((float)img.getIntrinsicWidth()) / ((float)img.getIntrinsicWidth());
Log.d("aspect:", String.valueOf(aspect));
however this produces unexpected results:
img.width: 297
img.height: 167
aspect: 1.0
This seems like it has a simple answer, yet I can't figure it out.
You're dividing width by width. Try substituting one of them with the height.
You have a typo.
float aspect = ((float)img.getIntrinsicWidth()) /
((float)img.getIntrinsicWidth());
You are dividing the width by the width, which will always produce 1, divide by the height instead:
float aspect = ((float)img.getIntrinsicWidth()) /
((float)img.getIntrinsicHeight());
Its a typo, one of them has to be height. You are dividing width by width
float aspect = ((float)img.getIntrinsicWidth()) / ((float)img.getIntrinsicWidth());
have you try float aspect = (new float(img.getIntrinsicWidth())) / (new float(img.getIntrinsicWidth()));
You have a typo, you're dividing width by width. Should probably be width by height.
Also, to make it a bit easier on the eyes, you don't need to cast both sides for a division to use floats; the left side will be converted to float automatically if the right side is one.
float aspect = img.getIntrinsicWidth() / (float)img.getIntrinsicHeight();

Converting a coordinate from one space to another

I seem to be having a bit of a math fail here... I need to convert an (x,y) point from one coordinate space to another - not in the sense of polar to cartesian or anything of the sort... just from one bound to another. i.e., for a particular (x,y) that falls in the rectangle with lower left (-100, -100) and upper right (100,100), I need to find out where that point would be in a rectangle with lower left (0,0) and upper right (500, 500).
I feel like this is just simple math but I'm having a heck of a time getting it right...
It is for a little computer graphics program written in java. Essentially there is a clip window that changes, and that clip window needs to fill the whole view window. Initial values for clip and view are given by the above rectangles, in that order. However, the clip could change to, for example, a rectangle with lower left (-80, -65) and upper right (75, 65). I would then need to convert a point that falls within that rectangle to a point that falls within the view window (lower left (0,0), upper right (500, 500))
Here is what I have for it right now:
public int normalizeX(float x) {
float clipWidth = clipRight - clipLeft;
int viewWidth = viewRight - viewLeft;
x += 100; //Get x into range [0, 200] instead of [-100, 100]
//First convert x to value within clip width, then "scale" to viewport width
return (int)(((clipWidth*x)/200) * (viewWidth/clipWidth));
}
public int normalizeY(float y) {
float clipHeight = clipTop - clipBottom;
int viewHeight = viewTop - viewBottom;
y += 100; //Get y into range [0, 200] instead of [-100, 100]
//First convert y to value within clip height, then "scale" to viewport height
return (int)(((clipHeight*y)/200) * (viewHeight/clipHeight));
}
Thanks for any help!
Assuming your old bounds are xLoOld and xHiOld (-80 and 75, respectively, in your example) and your new bounds are xLoNew and xHiNew (0 and 500, respectively, in your example), then you can normalize your xOld to your new coordinate system like this:
xNew = (xOld-xLoOld) / (xHiOld-xLoOld) * (xHiNew-xLoNew) + xLoNew
Same thing for y.
Something like this may help you.
public static float scale(
float x,
float old_min, float old_max,
float new_min, float new_max)
{
float old_range = old_max - old_min;
float new_range = new_max - new_min;
return new_min + (x - old_min) * new_range / old_range;
}
You will need to scale your coordinates in both dimensions. I've left out other computations such as casting to int, etc.
I also recommend creating suitable types such as Region or Rectangle to simplify your code by, at least, reducing the number of arguments to this function.
Simple as hell: you want a transformation which maps -100 to 0 and 100 to 500. Or put blindly, which maps the range [-100, 100] to the range [0, 500] :
[-100, 100] ----> [0, 500]
First step is to convert the range [-100, 100] to [0, 200]:
x ----> x + 100
Next step is to convert the range [0, 200] to [0, 500]
x ----> x * 500 / 200 = 2.5 * x
In fine, your transformation reads
x ----> 2.5 * (x + 100)
and likewise for y:
y ----> 2.5 * (y + 100).
Hope this makes it clear, and that you will be able to reuse the logic in more complex cases.
The slick and general way to do this is with an affine transform.
You'll have a 2 x 2 matrix (call it A) that characterizes the "stretchiness" of the original field to the second field, and a 2 x 1 matrix (call it b) characterizing the offset.
Then, with x as your (2 x 1) input and y as your (2 x 1) output, it's just y = Ax + b.
The technique also lets you do a lot more (like rotations) but those probably aren't important for your application.
http://en.wikipedia.org/wiki/Affine_transformation

Rounding when converting from double to float in java

I am trying to convert a Minimum Bounding Rectangle from double values to float values. After the conversion, I need the (float) rectangle to be equal to or contain the (double) rectangle (the float rectangle needs to be bigger than or equal to the double rectangle). To do that I want to be able to specify which way to round the double to convert it to float. So, when casting the "Top" of the rectangle, I would round up, but when casting the "Bottom" of the rectangle, I would round down.
Is there a class that allows me to do this?
Thanks.
Math.ceil( double ); - round up
Math.round( double ); - round down
You can compare the float to the double and if it needs to be slightly larger or smaller you can call floatToIntBits and intToFloatBits with an increment or decrement as required.

Categories