I am trying to draw a 3D tile grid that can be rotated and elevated.
The grid though, when rotated sometimes, does not show the graphics as it should, and mutilates them. The regular grid should look nothing like this.
The top half should not be present.
Can anyone help explain or give examples as to what may be causing this issue?
Just found something else: When you see the spike appear, on one side of your screen, on the opposite, that tile is missing. :
I found that if you set azimuth to 133, elevation to 324, zmod to -0.9, leave xmod at 0 and change the grid to 7 × 7 instead of 10 × 10, then only one single point 'misbehaves'. For this one misbehaving point, we have:
i = 48, j = 3,
x0 = 7.0, y0 = 0.0, z0 = 6.1,
x1 = -0.31273127, y1 = 5.4544506, z1 = -7.5074077,
near = 6.0 and nearToObj = 1.5,
just before the following two lines:
x1 = x1 * near / (z1 + near + nearToObj);
y1 = y1 * near / (z1 + near + nearToObj);
The critical thing here is that z1 + near + nearToObj has crossed below zero. As it crossed below zero, it caused the sign of the values of x1 and y1 calculated by the above two lines to change. This sign change is what causes the grid to appear wrong.
I'm no expert in 3D graphics, but I believe this outcome suggests that you can't plot the point in question because it has gone behind the camera. I'm afraid I'm not sure what the best way to solve this problem would be - that would require more knowledge of 3D graphics than I have.
As for my other answer, it was totally wrong, so I've deleted it. If I was going to claim that the Java 2D graphics API was any doing wrapping using 16-bit integers, I could at least have tried to verify that
g.drawLine(30, 30, 60, 60);
and
g.drawLine(30, 30, 60 + 65536, 60 + 65536);
produced the same output. They do not.
What's (new BasicStroke(6 / 5)) supposed to do?
6 / 5 is a fancy way to write 1.
$ cat Int.java
class Int {
public static void main(String args[]) {
System.out.println("6 / 5 == " + (6 / 5));
return;
}
}
$ make Int.class
javac Int.java
$ java Int
6 / 5 == 1
$
Related
I have built a simple program to draw a city skyline within a frame with some random elements. The number of buildings will be a random number between [5 - 9], the width and height of each building are also random (within a given range) as is the gap between each building. The problem I am having comes when I try to select a different gap range depending on the amount of buildings. Here is the the section of code in question (I have imported java.util.*;):
Random gen = new Random();
int buildNum = gen.nextInt(5) + 5; //generate random number [5 - 9]
for (int i = 0; i < buildNum; i++)
{
//generate random width
buildWidth = gen.nextInt(26) + 35; //[35 - 60]
//generate random height
buildHeight = gen.nextInt(201) + 200; //[200 - 400]
//instantiate building
building = new Building (gap, 500 - buildHeight, buildWidth, buildHeight, Color.gray);
buildingsArray.add(building);
//draw building
buildingsArray.get(i).draw (page);
//generate random gap between building
if (buildNum < 7)
{
gap += buildWidth + gen.nextInt(16) + 40;
}
if (buildNum == 7)
{
gap += buildWidth + gen.nextInt(9) + 15;
}
if (buildNum > 7)
{
gap += buildWidth + gen.nextInt(9) + 8;
}
}
The buildNum variable is initialized with a random number, then that number is used to iterate through. The issue is that when I check the buildNum variable in the three ifs at the bottom, the output does not seem to correspond with what I would expect. I will get 5 buildings with very small gaps (as though it passed the buildNum > 7 test), or I will get 7 buildings with the first six normal, but a very small gap before the seventh building. I'm sure it's something very simple that I am overlooking, but I have been starting at this for hours and tried many different things (including testing buildNum at fixed values 5, 6, 7, 8, or 9, and putting an else clause instead of a third if.) Ultimately, I would like the program to pick a number of buildings between 5 and 9 and then scale the gaps accordingly. I tried factoring in both buildNum and buildWidth, but I couldn't get it to work and settled for this. If anyone has a better way or can shed some light on my problem, I would greatly appreciate it!
Update: I have tested with a constant number of buildings to see what the range of output looks like. The problem is that I would be expecting 5 or 6 buildings to have wide gaps due to the if (buildNum < 7) condition and be progressively smaller due to the other 2 conditions. Instead, I end up with 5 buildings having really really small gaps (6 buildings is fine) and seven and 8 buildings going off the page (even though I've done the math and the gap that is added within the if conditionals would not allow this). There are some helpful comments here already, to answer your questions: Yes, the gap is an absolute position from the left. A small gap is initialized so that the first building does not touch the left side of the frame. Then, buildings are instantiated one at a time with a gap size chosen from those if statements. The position of the next building's left side is determined from a compounding gap variable that includes all previous gaps and all previous building widths. I am especially curious about Lee's comment that the if (buildNum ==7) and (buildNum>7) clauses might never happen. Could you explain that a little further? Wouldn't 8 or 9 satisfy the >7 condition and 7 satisfy the ==7 condition? if not, what can I do to change it? also, here is my constructor:
/**
* Constructor: sets background color and frame size
*/
public SkylinePanel()
{
setPreferredSize (new Dimension(600, 500));
setBackground (Color.black);
}
There is not much to it, the real work (including the above shown for loop) is within paintComponent and I've shown everything that is related to the building gaps already. Here is the draw method (it's in my building class):
/**
* Draws a building or window with the specified parameters
*/
public void draw (Graphics page)
{
page.setColor (color);
page.fillRect (x, y, width, height);
winNum = gen.nextInt(8) + 2;
//generate a random number [2 - 9] of windows to draw on a given building
for (int i = 0; i < winNum; i++)
{
page.setColor (Color.yellow);
page.fillRect (gen.nextInt(width - WIN_X) + x, gen.nextInt(height - WIN_Y) + y, WIN_X, WIN_Y);
}
}
Ignore the code about windows, that works fine. I added that after I decided to move on from the gap problem for the time being.
Oh, and just in case anyone needs to see it, here is the constructor of the Building class:
public Building (int upperX, int upperY, int buildWidth, int buildHeight, Color shade)
{
x = upperX;
y = upperY;
width = buildWidth;
height = buildHeight;
color = shade;
}
Any other thoughts or questions would be greatly appreciated!
I'm creating a racing game, in which I run into some problems while creating the AI. What I need to do is get the AI from a X,Y position to a "checkpoint" with another X,Y position. The AI driving track will be created by placing different invisible checkpoints around the map that is will go to.
I can get the direction to the checkpoint by calculating the difference in X,Y from me to it and then use tan(Deg) = Y/X, witch gives me the direction I have to go from my current position.
distanceToMoveX = checkpoint1X - this.getX();
distanceToMoveY = checkpoint1Y - this.getY();
newDirection = ((double)distanceToMoveY / distanceToMoveX) * 100;
So now I have the direction in which I have to go from my current position, and I also have a direction in which I'm currently moving. What I want to do now is get my AI to realize its closer to turn right if my current direction is 350 and I want to go to 10. As it is right now I can only get it to turn right or left all the time.
Well I guess somewhere you compute the difference between the actual angle and the next one, something like turn = newDirection - actualDirection.
Doing 10 - 350 = -340 tells you AI it needs to turn -340°.
However, (10 - 350 + 360) % 360 = 20 tells your AI it needs to turn +20°. Adding the 360 is to make sure your angle is positive, so the modulo will work as expected (because -340 % 360 = -340).
What you really want, is never turn more than 180°. Thus, add another 180 before reducing between 0 and 360, and then remove them, that will shift your result in [-180,180] while staying module 360° (thus the same angle) :
turn = (newDirection - actualDirection + 540) % 360 - 180
Then you breakup this turn however you want if you don't want it to do in one go, for example 5 degrees at a time like you seem to be already doing.
This is what you need for choosing the direction (this is an example with hardcoded values):
private int correctedDirectionCalculation(int calculation){
return calculation >= 0 ? calculation : calculation + 360;
}
public void chooseDirection() {
int targetDirection = 5;
int currentDirection = 360;
int goingRightDistance = correctedDirectionCalculation(targetDirection - currentDirection);
int goingLeftDistance = correctedDirectionCalculation(currentDirection - targetDirection);
System.out.println(goingLeftDistance < goingRightDistance ? "Go Left!" : "Go Right!");
}
As for the inverted values on the Y axis when rendering, I guess you just need to invert the Y value to fix it (multiply by -1).
You can use degrees outside the range of (0;360), so you can f.e. go from 360 to 365 and it works as expected. If you want later to, say, compare two angles, just say angle % 360
From Google Earth I got a Box with coordinates for a picture, like following:
<LatLonBox>
<north>53.10685</north>
<south>53.10637222222223</south>
<east>8.853144444444444</east>
<west>8.851858333333333</west>
<rotation>-26.3448</rotation>
</LatLonBox>
Now I want to test weather a point intersect with this LatLonBox.
My base idea to check, whether a point intersect with the LatLonBox was, to rotate the point back by the given angle, and then to test whether the point intersect with a regular (not rotated) rectangle.
I tried to calculate the rotation manually:
public static MyGeoPoint rotatePoint(MyGeoPoint point, MyGeoPoint origion, double degree)
{
double x = origion.getLatitude() + (Math.cos(Math.toRadians(degree)) * (point.getLatitude() - origion.getLatitude()) - Math.sin(Math.toRadians(degree)) * (point.getLongitude() - origion.getLongitude()));
double y = origion.getLongitude() + (Math.sin(Math.toRadians(degree)) * (point.getLatitude() - origion.getLatitude()) + Math.cos(Math.toRadians(degree)) * (point.getLongitude() - origion.getLongitude()));
return new MyGeoPoint(x, y);
}
public boolean intersect(MyGeoPoint geoPoint)
{
geoPoint = MyGeoPoint.rotatePoint(geoPoint, this.getCenter(), - this.getRotation());
return (geoPoint.getLatitude() < getTopLeftLatitude()
&& geoPoint.getLatitude() > getBottomRightLatitude()
&& geoPoint.getLongitude() > getTopLeftLongitude()
&& geoPoint.getLongitude() < getBottomRightLongitude());
}
And it seems that the results are wrong.
LatLonBox box = new LatLonBox(53.10685, 8.851858333333333, 53.10637222222223, 8.853144444444444, -26.3448);
MyGeoPoint point1 = new MyGeoPoint(53.106872, 8.852311);
MyGeoPoint point2 = new MyGeoPoint(53.10670378322918, 8.852967186822669);
MyGeoPoint point3 = new MyGeoPoint(53.10652664993972, 8.851994565566875);
MyGeoPoint point4 = new MyGeoPoint(53.10631650700605, 8.85270995172055);
System.out.println(box.intersect(point1));
System.out.println(box.intersect(point2));
System.out.println(box.intersect(point3));
System.out.println(box.intersect(point4));
The result is true, false, false, true. But it should be 4x true.
Probably I´, making some kind of error in reasoning.
Maybe because the latitude values are getting bigger upwards. But I don´t knwo how to change the formular.
I need some help ...
EDIT:
I think my basic idea and formular is right. Also I found similar solutions eg. link and couldn´t find any difference.
So I think the only possible error source is, that the axis are not proportional. So the problem is how to take account of this.
I hope someone has got an idea.
The problem was indeed that the axis were not proportional.
The following method takes care of it.
public static MyGeoPoint rotatePoint(MyGeoPoint point, MyGeoPoint origion, double degree)
{
double x = origion.longitude + (Math.cos(Math.toRadians(degree)) * (point.longitude - origion.longitude) - Math.sin(Math.toRadians(degree)) * (point.latitude - origion.latitude) / Math.abs(Math.cos(Math.toRadians(origion.latitude)));
double y = origion.latitude + (Math.sin(Math.toRadians(degree)) * (point.longitude - origion.longitude) * Math.abs(Math.cos(Math.toRadians(origion.latitude))) + Math.cos(Math.toRadians(degree)) * (point.latitude - origion.latitude));
return new MyGeoPoint(x, y);
}
if I understand correctly you want to check if these four points are in rotated rectangle.
I would recommend checking not by corner points because your rectangle is rotated but:
if you have rotated rectangle ABCD then calculate lines |AB|, |BC|,|CD| and |DA|. If you have two points then use y=ax+b (you will calculate a,b by by giving [x,y] of both coordinates that gives you two easy equatations).
Finally function intersect will check
if point <= line |CD|
AND point >= line |AB|
AND point <= line |BC|
AND point >= |DA|
then it is inside rect.
This can be done when your point P[x,y] you put in ax+y+b (a>0 or -ax-y-b). If it is zero it is lying on the line, if it is < than it is under line or "on the left side". Hope I helped..
BTW why are you using -degree value, which you multiply by -1 , is it necessary?
The problem appears to be that the data structure LatLonBox doesn't make any sense as a description for the boundary of a picture. A box in lat-lon coordinates is not a geometric rectangle. (Think about a box near or including the north pole.) You need to re-think your application to deal in a lat/lon coordinate for the center of the picture and then deal with the rotation as an angle with respect to lines of latitude (parallel to the equator). (Even then, a picture with center on the north or south pole will be a degenerate case that must be handled separately.) So a box should properly be something like:
<geobox>
<center_lat>41</center_lat>
<center_lon>-74</center_lon>
<rotation_degrees_ccw>-23</rotation_degrees_ccw>
<width>1000</width> <!-- in pixels or meters, but not in degrees! -->
<height>600</height> <!-- same as above -->
</geobox>
Having said all that, suppose you have a true geometric box centered at (x0,y0), width w, height h, rotated angle T about its center. Then you can test a point P(x,y) for membership in the box with the following. You need the transformation that takes the box to the origin and aligns it with the axes. This is Translate(-x0,-y0) then Rotate(-T). This transformation as a matrix is
[cos(-T) -sin(-T) 0][1 0 -x0] [ cos(T) sin(T) -x0*cos(T)-y0*sin(T)]
[sin(-T) cos(-T) 0][0 1 -y0] = [-sin(T) cos(T) x0*sin(T)-y0*cos(T)]
[0 0 1][0 0 1] [ 0 0 1 ]
You want to apply this transformation to the point to be tested and then see if it lies in the desired box:
// Transform the point to be tested.
ct = cos(T);
st = sin(T);
xp = ct * x + st * y - x0 * ct - y0 * st;
yp = -st * x + ct * y + x0 * st - y0 * ct;
// Test for membership in the box.
boolean inside = xp >= -w/2 && xp <= w/2 && yp >= -h/2 && yp <= h/2;
It's late and I haven't checked this arithmetic, but it's close. Say if it doesn't work.
Alright, so I'm trying to get the distance of a point in a 2d triangle without calculating perpendicular vectors.
float qd = Vector2f.dot(new Vector2f(pos.x, pos.z),
new Vector2f(normal.pos.x, normal.pos.z)) -
Vector2f.dot(new Vector2f(q.center.x, q.center.z),
new Vector2f(normal.pos.x, normal.pos.z));
That's the code I'm using. (Note: it's converting 3f vectors to 2d ones, but you don't have to worry about that). I need the result of the calculation to be between 0 and 1 I.E. 0.5 or something.
If I'm still not explaining right maybe this will help?
My question is: How do I get the distance of a point in a 2d triangle without calculating perpendicular vector's distance? I.E. if the triangle is facing up (y = -1) without any tilt
I would need the distance in the triangle without any X.
Edit1: About what you're saying, Banthar, This is what I got out of it, and it doesn't work, but it seems like it's close to working.
float d = (float) Math.sqrt( 0 /*cause the two x's should be the same */ + Math.pow(pos.z - q.max.z, 2));
float h = (float) Math.sqrt( 0 /*cause the two x's should be the same */ + Math.pow(q.min.z - q.max.z, 2));
float myDist = d/h;
Let's say your triangle is ABC and the point is P.
The number you are looking for is the distance from P to AB divided by the distance from C to AB.
This is the same as the ratio of the corresponding areas. So you can compute the two areas:
Area(ABP) / Area(ABC)
The best way to compute the triangle area depends on what information you have about your triangle.
If you have the vertices only, then you can use:
Area(ABP) / Area(ABC) = ( Ax*By - Ax*Py + Ay*Px - Ay*Bx + Bx*Py - By*Px ) /
( Ax*By - Ax*Cy + Ay*Cx - Ay*Bx + Bx*Cy - By*Cx )
I have a spaceship, that spaceship moves though space 360 Degrees.
The spaceship needs thrust animation in 2d. The trust animation needs to be at the bottom middle line of the spaceship. I have the following variables.
_Rotation
_Spaceship.width
_Spaceship.height
_Spaceship.Position(x,y)
I've uploaded an image of my problem too in-case people don't understand my bad explanation:
http://imgur.com/Lgchc
Both animation render like so:
this._itemAnimation.render(this.getPosition(), canvas, this._degrees);
this._thrustAnimation.render(this.thrustPosition(), canvas, this._degrees);
I have tried so far and failed:
_thurstPosition.set(((int)_object_x + Math.cos(_degrees) * _itemAnimation.getWidth() / 2) ,
((int)_object_y + Math.sin(_degrees) * _itemAnimation.getWidth() / 2));
I'm fail, somebody help me.
--- UPDATE ---
I've updated the code so it's better understood:
int SpaceshipCenterX = getPosition().x + (_SpaceshipAnimation.getWidth() / 2) - (_thrustAnimation.getWidth() / 2);
int SpaceshipCenterY = getPosition().y + ((_SpaceshipAnimation.getHeight() / 2) - (_thrustAnimation.getHeight() / 2));
double OffSetCos = Math.cos(_spaceshipDegrees);
double OffSetSin = Math.sin(_spaceshipDegrees);
_thurstPosition.set
(
(int)(SpaceshipCenterX + (SpaceshipAnimation.getWidth() / 2) * OffSetCos)
,
(int)(SpaceshipCenterY + (SpaceshipAnimation.getHeight() / 2) * OffSetSin)
);
I still can't get it too work. It's going around the spaceship but very fast and flashing everywhere.
--- UPDATE 2 ---
This is almost working but it's going too far out:
int xOffset = -1 * (_itemAnimation.getWidth() / 2);
double DegreeToRadien = Math.toRadians(_degrees);
_thurstPosition.set
(
(int)(((xOffset) * Math.cos(DegreeToRadien)) + getPosition().x),
(int)(((xOffset) * Math.sin(DegreeToRadien)) + getPosition().y)
);
Assuming that you are using this coordinate/angle system:
90 (pi/2) - Up
^
|
Left - 180 (pi) <----|----> 0 - Right
|
v
Down - 270 (3pi/2)
And that your spaceship is going to the right at 0 degrees
>[ } 0
Then for any direction you need to translate the thrust relatively from the centre of the spaceship, let's say we translate in the x direction by
offset = -1 * width/2;
Then rotate it by the angle of the spaceship and finally translate it by the position of the spaceship.
To compute this transformation, write out the 3 transformations as matrices in reverse order and multiply them out, transforming a point starting at (0,0)
[1 0 spaceshipX] [cos(dir) -sin(dir) 0] [1 0 -width/2] [0]
[0 1 spaceshipY] [sin(dir) cos(dir) 0] [0 1 0 ] [0]
[0 0 1 ] [ 0 0 1] [0 0 1 ] [1]
So that would give you the position of the thrust as
thrustX = (-width/2)cos(dir) + spaceshipX
thrustY = (-width/2)sin(dir) + spaceshipY
So I suppose you just missed the fact you need to subtract width/2, not add it.
I've edited this with a correct and more readable syntax. Using underscores everywhere really hurts readability. I am assuming you have a spaceship class, and the spaceship has a width, height, x position, y position and rotation. You have a thruster class which also has a width, height, x position, y position and rotation. (They could inherit from a Sprite abstract class). To set the position of a thruster object we call thruster.setPosition(x,y);
int xOffset = -1 * (spaceship.width / 2);
thruster.setPosition(
(int)(((xOffset) * Math.cos(spaceship.rotation)) + spaceship.x),
(int)(((xOffset) * Math.sin(spaceship.rotation)) + spaceship.y)
);
Hopefully this makes it obvious to you which values you need to be setting where. I can't decipher your code without seeing more of it, where these variables are declared and what they actually mean.
Update
Just to conclude, as I think you may have discovered. Math.cos and Math.sin require angles to be in Radians, not degrees. The solution I have given here is correct, and I have shown how you compute the position of any relatively positioned object by performing the matrix calculation. You just have to remember that spaceship.rotation must be in radians or you must translate it to radians from degrees before passing it to Math.cos() or Math.sin().
int xOffset = -1 * (spaceship.width / 2);
double radians = Math.toRadians(spaceship.rotation);
thruster.setPosition(
(int)(((xOffset) * Math.cos(radians)) + spaceship.x),
(int)(((xOffset) * Math.sin(radians)) + spaceship.y)
);
Your code looks close to me. You need to be careful about what _degrees means. If _degrees is the direction in which the rocket points (counterclockwise from the +x axis), then you'll need to put some minus signs in there because the thrust is at the back of the rocket, not the front. Also, I would think the thrust is on the height end of the rocket, so use getLength instead of getWidth. You may need to add some extra for the height of the thrust animation as well (depending on where its anchor is). So something like
_thurstPosition.set(((int)_object_x - Math.cos(_degrees) * (_itemAnimation.getHeight() / 2 + _thrustAnimation.getHeight() / 2)) ,
((int)_object_y - Math.sin(_degrees) * (_itemAnimation.getHeight() / 2 + _thrustAnimation.getHeight() / 2)));
You'll also need to set thrust orientation if it isn't a circle.
(I'm assuming _degrees == _Rotation)
double DegreeToRadien = Math.toRadians(_degrees);
int ObjectXCenter = (int) (_object_x + ((_itemAnimation.getWidth() / 2)) - _thrustAnimation.getWidth() / 2);
int ObjectYCenter = (int) (_object_y + ((_itemAnimation.getHeight() / 2)) - _thrustAnimation.getHeight() / 2);
int xOffset = -1 * (_itemAnimation.getWidth() / 2);
_thurstPosition.set
(
(int)(((xOffset) * Math.cos(DegreeToRadien)) + ObjectXCenter),
(int)(((xOffset) * Math.sin(DegreeToRadien)) + ObjectYCenter)
);