How to create an arc with Slick2D? - java

I cant seem to figure out how to create an arc with Slick2D. Basically I want to be able to save it as an object so I can do collicion detection with it, but Slick2D only has a g.drawArc() function.
I thought about using Arc2D, which is perfect for what I need, but I can't figure out how to get slick to draw the Arc2D.
Heres is a mockup of what I want to have:
This is pretty easy to do with Arc2D and then using jFrame to draw it, but I dont know how to create something like this with slick.
Edit: The solution I see that I can do, is just use Arc2D for all the collision detection, and then just use myArc.x, myArc.y, myArc.width, etc. when I need to draw the arc with g.drawArc(), but Arc2D cant use the .interesects() function with slick2d shapes, which are used everywhere else.
Ive created a method that creates an approximate arc based on a curve
public Curve createArc(float centerX, float centerY, float radius, float theta){
// creates a 180 degree arc around centerX and Y, theta (in degrees) is used for rotation
float x1,x2,x3,x4,y1,y2,y3,y4;
x1 = (float) (radius*(1-Math.cos(Math.toRadians(theta)))) + startX - radius;
y1 = (float) (radius*(1-Math.sin(Math.toRadians(theta)))) + startY - radius;
x2 = (float) (Math.sqrt(25*(radius*radius)/9)*(Math.cos(Math.toRadians(theta-36.87-90)))) + startX;
y2 = (float) (Math.sqrt(25*(radius*radius)/9)*(Math.sin(Math.toRadians(theta-36.87-90)))) + startY;
x3 = (float) (Math.sqrt(25*(radius*radius)/9)*(Math.cos(Math.toRadians(theta-53.13)))) + startX;
y3 = (float) (Math.sqrt(25*(radius*radius)/9)*(Math.sin(Math.toRadians(theta-53.13)))) + startY;
x4 = (float) (radius*(1-Math.cos(Math.toRadians(theta+180)))) - radius + startX;
y4 = (float) (radius*(1-Math.sin(Math.toRadians(theta+180)))) - radius + startY;
Vector2f vectorTest1 = new Vector2f(x1,y1);
Vector2f vectorTest2 = new Vector2f(x2,y2);
Vector2f vectorTest3 = new Vector2f(x3,y3);
Vector2f vectorTest4 = new Vector2f(x4,y4);
Curve curve = new Curve(vectorTest1,vectorTest2,vectorTest3,vectorTest4);
return curve;
}

Hey you can create a variety of different shapes with Slick2D. You can see them here:
http://slick.ninjacave.com/javadoc/org/newdawn/slick/geom/package-summary.html
They all inherit from shape and share the same methods (e.g. intersects());
There is no specific arc shape but you might be able to use a curve or maybe a polygon (or something else). So for example you simply could do:
org.newdawn.slick.geom.Curve myCurve = new org.newdawn.slick.geom.Curve(10f, 5f, 9f, 4f);
to create a weird looking curve. Just read the documentation and play a little bit around.
In your render method you could either call g.drawArc(myCurve) or you call the drawing method on myCurve directly (myCurve.draw());
I'm sure you will be able to do this if you read the documentation and play a little bit around with the coordinates.
Furthermore you could create your own Shapes in Slick2D if you create a new class and let it inherit from Shape.

Related

Java Arc2D Collision detection (With Rotation)

I have tried to create NPC character that can "see" the player by using cones of vision.
The NPC will rotate back and forth at all times.
My problem is that the arc has a generic and unchanging position, but when its drawn to the screen it looks correct.
[Screenshots of the collisions in action][1]
[GitHub link for java files][2]
I'm using Arc2D to draw the shape like this in my NPC class
// Update the shapes used in the npc
rect.setRect(x, y, w, h);
ellipse.setFrame(rect);
visionArc.setArcByCenter(cx, cy, visionDistance, visionAngle, visionAngle * 2, Arc2D.PIE);
/ CenterX, CenterY (of the npc),
/ the distance from the arc to the npc
/ a constant value around 45 degrees and a constant value around 90 degress (to make a pie shape)
I've tried multiplying the position and the angles by the sin and cosine of the NPC's current angle
something like these
visionArc.setArcByCenter(cx * (Math.cos(Math.toRadians(angle))), cy (Math.sin(Math.toRadians(angle)), visionDistance, visionAngle, visionAngle * 2, Arc2D.PIE);
or
visionArc.setArcByCenter(cx, cy, visionDistance, visionAngle - angle, (visionAngle + angle) * 2, Arc2D.PIE);
or
visionArc.setArcByCenter(cx, cy, visionDistance, visionAngle * (Math.cos(Math.toRadians(angle))), visionAngle * 2, Arc2D.PIE);
I've tried a lot but can't seem to find what works. Making the vision angles not constant makes an arc that expands and contracts, and multiplying the position by the sin or cosine of the angle will make the arc fly around the screen, which doesn't really work either.
This is the function that draws the given NPC
public void drawNPC(NPC npc, Graphics2D g2, AffineTransform old) {
// translate to the position of the npc and rotate
AffineTransform npcTransform = AffineTransform.getRotateInstance(Math.toRadians(npc.angle), npc.x, npc.y);
// Translate back a few units to keep the npc rotating about its own center
// point
npcTransform.translate(-npc.halfWidth, -npc.halfHeight);
g2.setTransform(npcTransform);
// g2.draw(npc.rect); //<-- show bounding box if you want
g2.setColor(npc.outlineColor);
g2.draw(npc.visionArc);
g2.setColor(Color.BLACK);
g2.draw(npc.ellipse);
g2.setTransform(old);
}
This is my collision detection algorithim - NPC is a superclass to ninja (Shorter range, higher peripheral)
public void checkNinjas(Level level) {
for (int i = 0; i < level.ninjas.size(); i++) {
Ninja ninja = level.ninjas.get(i);
playerRect = level.player.rect;
// Check collision
if (playerRect.getBounds2D().intersects(ninja.visionArc.getBounds2D())) {
// Create an area of the object for greater precision
Area area = new Area(playerRect);
area.intersect(new Area(ninja.visionArc));
// After checking if the area intersects a second time make the NPC "See" the player
if (!area.isEmpty()) {
ninja.seesPlayer = true;
}
else {
ninja.seesPlayer = false;
}
}
}
}
Can you help me correct the actual positions of the arcs for my collision detection? I have tried creating new shapes so I can have one to do math on and one to draw to the screen but I scrapped that and am starting again from here.
[1]: https://i.stack.imgur.com/rUvTM.png
[2]: https://github.com/ShadowDraco/ArcCollisionDetection
After a few days of coding and learning and testing new ideas I came back to this program and implemented the collision detection using my original idea (ray casting) and have created the equivalent with rays!
Screenshot of the new product
Github link to the project that taught me the solution
Here's the new math
public void setRays() {
for (int i = 0; i < rays.length; i++) {
double rayStartAngleX = Math.sin(Math.toRadians((startAngle - angle) + i));
double rayStartAngleY = Math.cos(Math.toRadians((startAngle - angle) + i));
rays[i].setLine(cx, cy, cx + visionDistance * rayStartAngleX, cy + visionDistance * rayStartAngleY);
}
}
Here is a link the the program I started after I asked this question and moved on to learn more, and an image to what the new product looks like
(The original github page has been updated with a new branch :) I'm learning git hub right now too
I do not believe that using Arc2D in the way I intended is possible, however there is .setArcByTangent method, it may be possible to use that but I wasn't going to get into that. Rays are cooler.

How to rotate a line based on a given number of degrees

I have a line drawn with a Graphics object. I want to rotate this line a certain amount of degrees based on how much the mouse is dragged. I can get the number of degrees i need to rotate it but how do i then rotate the line based on that?
Thank You!
You can create a Line2D object for your original line. Then you can use AffineTransform#getRotateInstance to obtain an AffineTransform that does the rotation about a certain angle, around a certain point. Using this AffineTransform, you can create a rotated Line2D object to paint. So your painting code could roughly look like this:
protected void paintComponent(Graphics gr) {
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
// Create the original line, starting at the origin,
// and extending along the x-axis
Line2D line = new Line2D.Double(0,0,100,0);
// Obtain an AffineTransform that describes a rotation
// about a certain angle (given in radians!), around
// the start point of the line. (Here, this is the
// origin, so this could be simplified. But in this
// form, it's more generic)
AffineTransform at =
AffineTransform.getRotateInstance(
Math.toRadians(angleInDegrees), line.getX1(), line.getY1());
// Draw the rotated line
g.draw(at.createTransformedShape(line));
}
Alright, you will need to compute the length of the line, assuming that the ends of the line are (x0,y0) and (x1,y1), and (x,y) are the mouse coordinates, what you want is the point (x2,y2) that's on the line between (x0,y0) and (x,y), the distance between (x0,y0) and (x2,y2) must be the same as the one between (x0,y0) and (x1,y1).
The distance between (x0,y0) and (x1,y1) is:
double dx = x1-x0;
double dy = y1-y0;
double length = Math.sqrt(dx*dx, dy*dy);
The distance between (x0,y0) and (x,y) is:
double dx1 = x-x0;
double dy1 = y-y0;
double mouseDist = Math.sqrt(dx1*dx1, dy1*dy1);
And (x2,y2) are:
int x2 = x0 + (int)(dx1*length/mouseDist);
int y2 = y0 + (int)(dy1*length/mouseDist);
I suppose you are talking about Java AWT Graphics class. Graphics can be thought of as a canvas. It's an array of pixel values and "drawing a line" is just a utility function that changes the values of some of these pixels - there is no "line object" to speak of, from it's point of view. Normally you should erase the whole thing and draw a new line with the angle you want. However for that, you may want to look at Graphics2D (http://docs.oracle.com/javase/7/docs/api/java/awt/Graphics2D.html) and in particular at setTransform and the AffineTransform class.
static Point rotateLineClockWise(Point center, Point edge, int angle) {
double xRot = (int) center.x + Math.cos(Math.toRadians(angle)) * (edge.x - center.x) - Math.sin(Math.toRadians(angle)) * (edge.y - center.y);
double yRot = (int) center.y + Math.sin(Math.toRadians(angle)) * (edge.x - center.x) + Math.cos(Math.toRadians(angle)) * (edge.y - center.y);
return new Point((int) xRot, (int) yRot);
}

Getting the x and y point of a transformed object

I am transforming the barrel of a turret with AffineTransform, and I want a bullet to shoot right out of the tip of the barrel. Is there a method in shape to get these coordinates or do I have to calculate it manually?
code for transform
AffineTransform rotate = AffineTransform.getRotateInstance(rotation, getX() + getWidth()/2, getY() + getHeight()/2);
barrel = rotate.createTransformedShape(new Rectangle(getX() + getWidth()/2, getY() - getHeight()/2, 2, getHeight()/2 + 1));
code for bulllet
int dx = getX() - o.getX();
int dy = o.getY() - getY();
bullets.add(new Bullet((int)barrel.getBounds2D().getX(), (int)barrel.getBounds2D().getY(), SPEED, new NVector(dx, dy)));
Use the same transform (rotate) to transform the coordinate of the tip of the barrel:
rotate.transform(tipOfTheBarrel, transformedTipOfTheBarrel);
When your barrel is a rotated rectangle, then you can't use it because your bullet should probably come out of the middle of the side of the rectangle and not one of the corners.
But if you create your own shape and you make sure the point where the bullets appear is the first in the shape, then you can use getPathIterator() to get the transformed point.

Move Camera Around Sphere

I'm trying to move my camera in a spherical motion around a model in my world. I've seen converting spherical coordinates(rho, theta, phi) to cartesian coordinates (x, y, z), but I'm not sure how to go about setting this up. Here is what I've tried to so far but it isn't continuously orbiting the model. It gets to a certain point and then the rotation seems to reverse itself.
Initialize theta and phi:
private float theta = 0.0f;
private float phi = 0.0f;
Update theta and phi each frame:
// This should move the camera toward the upper-right continuously, correct?
theta = (theta+1.0f)%360;
phi = (phi+1.0f)%360;
Convert theta and phi to cartesian coordinates for the camera:
camera.position.x = CAMERA_DISTANCE * (float)Math.sin(theta*MathHelper.PIOVER180) * (float)Math.cos(phi*MathHelper.PIOVER180);
camera.position.y = CAMERA_DISTANCE * (float)Math.sin(theta*MathHelper.PIOVER180) * (float)Math.sin(phi*MathHelper.PIOVER180);
camera.position.z = CAMERA_DISTANCE * (float)Math.cos(theta*MathHelper.PIOVER180);
Then update the camera look at point and view matrix:
camera.lookAt(0, 0, 0);
camera.update();
Note:
I am using Java on Android with the libGDX framework and I am trying to control the rotation using an 2D on-screen virtual joystick and I still need to find a way to map the joystick to theta and phi.
Any help is greatly appreciated!
I recently did something just like this. This website helped me a lot to visualize what I needed to do.
What you need to do is convert your local joystick coordinates (relative to it's center) to pitch and yaw values:
public float getPitch()
{
return (position.X - center.X) * MathHelper.PIOVER180;
}
public float getYaw()
{
return (position.Y - center.Y) * MathHelper.PIOVER180;
}
Then you can use a quaternion to represent it's rotation:
public void rotate(float pitch, float yaw, float roll)
{
newQuat.setEulerAngles(-pitch, -yaw, roll);
rotationQuat.mulLeft(newQuat);
}
Then you can apply the quaternion to the camera's view matrix using libGDX's built in rotate(quaternion) method:
camera.view.rotate(rotationQuat);
// Remember to set the camera to "look at" your model
camera.lookAt(0, 0, 0);

Whats wrong with my Matrix Rotation?

I'm trying to rotate a model by
(float) Math.atan2(-camX.getXf() * padX, -camDir.getZf() * padY)
Y
and
-MathUtils.HALF_PI
Z
But
model.setRotation(new Matrix3(1,0,0,
0,(float) Math.atan2(-camX.getXf() * padX, -camDir.getZf() * padY),0,
0,0,-MathUtils.HALF_PI));
It rotates on the y axis (Though it's sideways because it's a md2 model) but rotating the Z axis doesn't make it right side up. Any idea why?
Each variable is in it's respective area of the matrix.
EDIT: alright, now I'm using this code:
float x = 0;
float y = (float) Math.atan2(-camX.getXf() * padX, -camDir.getZf() * padY);
float z = (float) -MathUtils.HALF_PI;
float a = (float) Math.sin(x);
float A = (float) Math.cos(x);
float b = (float) Math.sin(y);
float B = (float) Math.cos(y);
float c = (float) Math.sin(z);
float C = (float) Math.cos(z);
Matrix3 m = new Matrix3(A*b, -(B*a),b,
(C*a)+(A*b*c), (A*C)-(a*b*c), -(B*c),
(a*c)-(A*C*b), (A*c)+(C*a*b), B*C);
But now none of the axis are rotating correctly.
This is how the matrix is set up:
xx, xy, xz,
yx, yy, yz,
zx, zy, zz
Rotation matrices don't work this way. Angles don't go into matrices! Instead I assume that Java handles a rotation matrix just like any other transformation matrix in cartesian coordinates. Since I think you don't want to input the rotation matrix by hand, you are probably better off starting with a new Matrix3 (I hope it is automatically initialized at the identity matrix), and then successively rotating it using rotateX(float x), rotateY(float y) and rotateZ(float z), where x, y, z are the angles you want to rotate about. (In case you are using com.threed.jpct.Matrix, at least.) Note that the result does depend on the succession of the three rotations.
Here is a typical tutorial on how to use rotation matrices http://www.euclideanspace.com/maths/algebra/matrix/orthogonal/rotation/index.htm. The order of applying rotations round the three axes is critical. Alternatively you can rotate about an arbitrary axis. Also you may want to explore quaternions.
This is what a rotation matrix looks like in 2D; it rotates a point in (x,y) space about the z-axis in the counterclockwise direction.
http://en.wikipedia.org/wiki/Rotation_matrix

Categories