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);
}
Related
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.
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.
Im trying to draw an arc that stretches around part of the circle. Im confused on how to use the DrawArc method. I read few articles online and to simply put, im confused on how the parameters exactly work. My circle is centered at 100, 100 with radius of 50. How would i use the drawArc method to draw an arc that overlaps the circle ? Any thoughts would be highly appreciated
Reading articles is good, but you should always read a method's documentation.
The drawArc method does not take a center and radius as arguments. Instead, it takes a rectangle. The x and y arguments are the upper-left corner of that rectangle; the width and height are the horizontal and vertical diameters of your arc's ellipse.
You can just do the math yourself:
int centerX = 100;
int centerY = 100;
int radius = 50;
graphics.drawArc(centerX - radius,
centerY - radius,
radius * 2,
radius * 2,
startAngle,
span);
Note that the final argument is the number of degrees the arc spans, not the absolute end angle.
I'm meant to draw a pentagon with lines going from the vertices to the centre. These 'arms' are being drawn correctly but when I try to connect the vertices it is being drawn incorrectly. To connect the lines I placed another draw function in the loop as below, which should take the end point coordinates of the first line drawn as the starting point, and the end point coordinates of the next 'arm' that is drawn in the iteration, as its end point. Am I missing something here? Am I wrong the use 'i+angle' in the second draw?
for (int i = 0; i < arms; i += angle) {
double endPointX = armLength * Math.cos(i*angle-Math.PI/2);
double endPointY = armLength * Math.sin(i*angle-Math.PI/2);
double endPointX2 = armLength * Math.cos((i+angle)*angle-Math.PI/2);
double endPointY2 = armLength * Math.sin((i+angle)*angle-Math.PI/2);
g2d.drawLine(centreX, centreY,centreX+ (int) endPointX,centreY+ (int) endPointY);
g2d.drawLine(centreX+ (int) endPointX,centreY+ (int) endPointY, (int) endPointX2,(int) endPointY2);
}
I have a solution for this here in PolygonFactory
Abstractly, the way to generate a regular polygon with n points is to put these points on the unit circle. So:
Calculate your angle step, which is 2 * pi / #vertices
Calculate your radius
Starting at angle 0 (or an offset if you want) use Math.sin(angle) and Math.cos(angle) to calculate the x and y coordinates of your vertices
Store the vertex points somewhere / somehow. If you look at the Polygon class or the class I wrote, you can get some ideas on how to do this in a way that is friendly to converting to a java.awt.Polygon.
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.