I need to draw a spider using the Graphics package. The problem though is that its size, number of legs and eyes are specified by the user.
I've got a few questions:
how can I randomly select a point on a circle so I can draw a line (for legs) from there while keeping in mind that drawLine(), for instance, takes only integer arguments?
how can I randomly select a point inside the circle used as a center of an eye so that the circle (eye) fits within the ranges of an outer circle (body)?
Selecting a point on a circle just requires getting a random angle. Java uses radians for it's trigonometric functions so a random double between 0 and 1 is multiplied by 2π.
Random r = new Random();
double angle = r.nextDouble() * Math.PI * 2;
Drawing legs is simple trigonometry which requires finding the x and y of each line. For this sine and cosine functions are used. The line can then be drawn off the center point of the circle (centerX and centerY), ending at a specified length in pixels (legLength).
The process can be repeated to draw multiple legs with a specified offset (legOffset) and repeated and offset again (by π) to draw legs on the other side.
for (int i = 0; i < 4; i++) {
int lineX = (int) radius * Math.cos(angle);
int lineY = (int) radius * Math.sin(angle));
g.drawLine(circleX + lineX , circleY + lineY , circleX + lineX * legLength, circleY + lineY * legLength);
angle += legOffset;
}
Drawing the eyes is essentially the same process as the legs. Each eye can drawn at a specified angle and distance from the center of the circle.
int eyeX = (int) distance * Math.cos(angle);
int eyeY = (int) distance * Math.sin(angle));
g.fillOval(eyeX - eyeRadius, eyeY - eyeRadius, eyeRadius* 2, eyeRadius* 2);
The easiest way to get random integers is to create an instance of Random and with random.nextInt(bound) you get an integer between 0 (inclusive) and bound (exclusive), [0, bound).
Instead of selecting the upper left corner of the spider, I would randomly select the center of the spider and then draw everything in relation to it.
Now let's define the radius r = size / 2.
Selecting a random point with insuring that the spider is fully visible:
x = r + random.nextInt(width - 2 * r);
y = r + random.nextInt(height - 2 * r);
Drawing the body with a diameter of r and not 2r to ensure the legs are visible: g.fillOval(x - r / 2, y - r / 2, r, r);
Drawing the legs and eyes: There are numerous strategies, you could draw lines from the center with length r for the legs and very small circles at distance r/4 from the center for the eyes. After selecting an initial random angle, you can use the golden angle to calculate the position of the next leg / eye, this ensures they are never drawn at the same positon (https://en.wikipedia.org/wiki/Golden_angle).
Note: draw the legs first, then the body and the eyes last.
Related
I am attempting to create an application that draws Fibonacci Arcs similar to these.
However, I'd like full circles instead of arcs, and I'd like to draw more than the three Fibonacci lines shown in the picture. I've created an application using JFreeChart to attempt to accomplish this. However, here is the result when trying to draw the same arcs (but as circles) shown in the previous picture.
Initially, it just looks wrong, but when I zoom out, it is indeed a circle, but it's way too big.
To calculate the arcs, you draw a line, then take a Fibonacci ratio - let's use .381 for example - the percentage of that line. If you look at the first picture, you'll see the innermost arc intersects the line at .381% the distance of the line from the centre of the circle. First I calculate this point. Then I construct a line from the .381% point to the centre. Then I take the distance of this line, which should be the radius. Then I use this radius to draw the circle.
Here's the code to calculate the radius. Where stop and start are the stop and start points of the line drawn.
multiplier = ratio38Value + i;
diffx = (stop.getX() - start.getX()) * multiplier;
diffy = (stop.getY() - start.getY()) * multiplier;
xValue = start.getX() + diffx;
yValue = start.getY() + diffy;
point = new Point(xValue, yValue);
lineSegment = new Line(point, stop);
radius = lineSegment.getDistance();
circle = new Circle(stop.getX(), stop.getY(), radius);
circles.add(circle);
Here is the code to calculate the distance of a line
public double getDistance(){
double x = Math.pow(endPoint.getX() - startPoint.getX(), 2);
double y = Math.pow(endPoint.getY() - startPoint.getY(), 2);
return Math.sqrt(x + y);
}
I get back a list of circle objects (this is an object I created that holds the radius and centre point) one for each circle that needs to be drawn and then draw them.
List<Circle> circles = fibonacciCalculations.getFibonacciArcs(startPoint, endPoint);
if(circles != null)
{
for (Circle circle : circles){
double xCenter = circle.getX();
double yCenter = circle.getY();
double radius = circle.getRadius();
plot.addAnnotation(new XYShapeAnnotation(new Ellipse2D.Double(xCenter - radius, yCenter - radius, radius + radius, radius + radius)));
}
}
I think the issue has something to do with how the x-axis of time and the y axis of price doesn't exactly correlate. What I mean is, if the radius is 20, you'll be going 20 units away from the centre at each point. So say you're stock price is only 5 dollars, at your lowest point you will then be at -15. If that is the case, I have no idea how to fix it. But it also could be some error in my logic. Any ideas would be appreciated.
EDIT: While the bars look like they may be weekly bars in the first picture, they are indeed daily bars. Also, I have already converted the coordinates from data space to x y coordinates. I use this code below to do that.
#Override
public void chartMouseMoved(ChartMouseEvent event) {
Rectangle2D dataArea = cp.getScreenDataArea();
JFreeChart chart = event.getChart();
XYPlot plot = (XYPlot) chart.getPlot();
ValueAxis xAxis = plot.getDomainAxis();
ValueAxis yAxis = plot.getRangeAxis();
double x = xAxis.java2DToValue(event.getTrigger().getX(), dataArea,
RectangleEdge.BOTTOM);
double y = yAxis.java2DToValue(event.getTrigger().getY(), dataArea,
RectangleEdge.LEFT);
I'm not sure of the proper terminology, so lets call the actual (x,y) coordinates that represent where you are on your monitor "screen space" and let's call the (x,y) coordinates of the chart "chart space".
My issue was I was converting the points from screen space to chart space and then calculating my points. Instead, I should have calculated all my points in screen space, and then converted each calculated point to chart space.
Where i is the amount of groups of arcs I want to draw. (i = 0, then I am drawing circles for the 38, 50, 62 ratios, i = 1 then I'm drawing circles for the -1.68, -1.50...1.50, 1.68 ratios) I use this code to get my points that are a given ratio between the center and the starting point.
multiplier = ratio62Value + i;
diffx = (stop.getX() - start.getX()) * multiplier;
diffy = (stop.getY() - start.getY()) * multiplier;
xValue = start.getX() + diffx;
yValue = start.getY() + diffy;
point = new Point(xValue, yValue);
line = new Line(point, stop);
line.calculateCirclePoints();
Here is the method to calculate the points on the circle. Where, endPoint is the center point, and the radius is the distance from the start point to the end point.
public void calculateCirclePoints(){
double radius = getDistance();
double radians;
double x;
double y;
Point currentPoint;
for (int degrees = 0; degrees <= 360; degrees += 1){
radians = Math.toRadians(degrees);
x = endPoint.getX() + (radius * Math.cos(radians));
y = endPoint.getY() + (radius * Math.sin(radians));
currentPoint = new Point(x, y);
points.add(currentPoint);
}
}
Lastly, I convert all of these points to chart space, and draw them on the chart.
public static Point converPointTo2D(Point point, Rectangle2D dataArea, XYPlot plot){
double x;
double y;
CustomNumberAxis xAxis = (CustomNumberAxis) plot.getDomainAxis();
CustomNumberAxis yAxis = (CustomNumberAxis) plot.getRangeAxis();
x = xAxis.java2DToValue(point.getX(), dataArea,
RectangleEdge.BOTTOM);
y = yAxis.java2DToValue(point.getY(), dataArea,
RectangleEdge.RIGHT);
return new Point(x, y);
}
One point to note, the radius of the circles is dependent on how much of a specific chart you're showing. A circle drawn on a 1 year chart from point a to point b will be smaller than a circle drawn on a 5 year chart from those same points.
i'm creating an android app(top-down shooter) on libgdx and have a problem with bullet positioning(No, with math for real)
So the problem is calculating a position of bullet
I want to spawn bullet here:
example
(where the red line is)
I calculating a position as:
bullet.setPosition(world.getPlayer().getX() + world.getPlayer().getWidth() * (float) Math.cos(Math.toRadians(world.getPlayer().getRotation())),
world.getPlayer().getY() + world.getPlayer().getHeight() * (float) Math.sin(Math.toRadians(world.getPlayer().getRotation())));
And it works well while rotation = 0, but as soon as i start to rotate my player it goes wrong :(
I am assuming that your player object is a libGDX Sprite. Therefore, the getX and getY are the coordinates of the bottom-left corner of the sprite and the getRotation is the rotation of the sprite about the origin (assumed to be at the centre of the player).
By doing some basic trigonometry you can convert the angle and a displacement to an (x,y) coordinate as you have attempted. The bullet needs to be rotated about the centre of the player. This can be done with the following code:
Sprite player = world.getPlayer(); //Just to tidy up the code
//Half width, half height and rotation
float hw = player.getWidth() / 2.0f;
float hh = player.getHeight() / 2.0f;
float rot = Math.toRadians(player.getRotation());
bullet.setPosition(player.getX() + hw + hw * (float) Math.cos(rot),
player.getY() + hh + hh * (float) Math.sin(rot));
Problem
I'd like to calculate the origin of a bullet which comes out of turret barrels which aren't at the center of the turret.
Current Situation
I have a turret with a center barrel which can shoot bullets from it. The turret is at the vector location and as a varying angle, i. e. it keeps rotating. In order to make the center turret shoot bullets from the end of the barrel I calculate the end of it like this:
double speed = 1;
double angle = turret.angle;
// start at the end of the barrel
double x = turret.location.x + Math.cos( angle) * turret.centerCannonLength;
double y = turret.location.y + Math.sin( angle) * turret.centerCannonLength;
// calculate angle and velocity of bullets
double dx = Math.cos(angle) * speed;
double dy = Math.sin(angle) * speed;
from that I can set the initial location and the velocity of the bullets with this:
Bullet bullet = new Bullet();
bullet.setLocation( x, y);
bullet.setVelocity( dx, dy);
bullet.setAngle( angle);
Task
Now I'd like to have 2 additional barrels from which the turret can fire. The barrels aren't in the center, they are offset left and right.
Question
How do I calculate the origin of the bullets from the left and right barrel?
Here's a demo screenshot:
The blue circle describes the rotation of the turret. The yellow parts are the turret and the center barrel. The red parts are the additional left and right barrels of which I'd like to calculate the bullet origin position.
Thank you very much for the help!
First, define the location of the origin in the turret's local coordinate space. The center barrel would be:
localX = turret.centerCannonLength
localY = 0
Then, find the current rotation matrix of the turret:
M = / cos(angle) -sin(angle) \
\ sin(angle) cos(angle) /
And multiply this matrix with the local position:
globalX = turret.location.x + cos(angle) * localX - sin(angle) * localY
globalY = turret.location.y + sin(angle) * localX + cos(angle) * localY
I have a world that is rendered in 2D and I'm looking at it from the top. Tjat looks like this (the floor tiles have no texture and only random green color yet):
Before rendering my entities, I transform the model-view matrix like this (while position is the position and zoom the zoom of the camera, ROTATION is 45):
glScalef(this.zoom, this.zoom, 1);
glTranslatef(this.position.x, this.position.y, 0);
glRotatef(ROTATION, 0, 0, 1);
Now I want to calculate the world coordinates for the current position of my camera. What I'm trying is to create a new matrix with glPushMatrix, then transform it the same way that the camera is transformed, and then get the matrix and multiply the given camera coordinate with it:
private Vector2f toWorldCoordinates(Vector2f position) {
glPushMatrix();
// do the same as when rendering
glScalef(this.zoom, this.zoom, 1);
glTranslatef(this.position.x, this.position.y, 0);
glRotatef(ROTATION, 0, 0, 1);
// get the model-view matrix
ByteBuffer m = ByteBuffer.allocateDirect(64);
m.order(ByteOrder.nativeOrder());
glGetFloatv(GL_MODELVIEW_MATRIX, m);
// calculate transformed position
float x = (position.x * m.getFloat(0)) + (position.y * m.getFloat(4)) + m.getFloat(12);
float y = (position.x * m.getFloat(1)) + (position.y * m.getFloat(5)) + m.getFloat(13);
System.out.println(x + "/" + y);
glPopMatrix();
return new Vector2f(x, y);
}
The problem now is: this works for the x coordinate, but the y coordinate is wrong and always 0. Have I misused the matrix somehow? Is there a "smoother" way of getting the world coordinates from the eye coordinates?
The problem is with the way you're calling getFloat(). When you call it with an index on a ByteBuffer, the index is the number of bytes into the buffer at which to start reading the float, not the number of floats. You need to multiply each of your indices by 4:
float x = (position.x * m.getFloat(0)) + (position.y * m.getFloat(16)) + m.getFloat(48);
float y = (position.x * m.getFloat(4)) + (position.y * m.getFloat(20)) + m.getFloat(52);
However given that x is working for you already, I suspect you might also need to transpose your matrix co-ordinates, and so the correct code is:
float x = (position.x * m.getFloat(0)) + (position.y * m.getFloat(4)) + m.getFloat(12);
float y = (position.x * m.getFloat(16)) + (position.y * m.getFloat(20)) + m.getFloat(28);
(By a co-incidence, transposing the first row of the matrix into the first column gives indices that are 4 times as great, so the 2 bugs cancel each other out in the case of x but not y).
If you're looking for a smoother way of doing it, look into using gluUnProject, although you may have to apply some additional transforms (it maps from window to object co-ordinates).
I'm practicing for an exam, and I'm doing one of the practice problems. I have a method that takes two arguments: one for the radius of a circle, and one for the number of dots to place within that circle. The method is below:
private void drawDots(int radius, int numDots){
double ycord;
double xcord;
for(int q = 0; q < numDots; q++){
ycord = -radius + random()*(radius+radius+1);
xcord = pow((pow(radius,2)-pow(ycord,2)),0.5);
turt.moveTo(xcord,ycord);
turt.penDown();
turt.forward(0);
turt.penUp();
}
}
turt is an object I'm using to draw with, and penDown()/penUp() is placing and removing the object from the canvas respectively.
I'm trying to define the x-coordinate and y-coordinate of the turt object to stay within a radius. Say the radius is 100, and the number of dots is 200, how do I keep the object within that radius?
The question states that:
"To constain the dots to a circle of radius r, a random y-coord in the interval -r, r is chosen. To x-coord is then randomly chosen in the interval -b, b, where b = sqrt(r^2 - y^2)."
I'm just not sure how to make sense of this math. The code above was my best attempt, but the output is strange.
Here is my failed output:
The distance from the center (0,0) to a dot must be less than the radius of the circle, r. The distance can be expressed as sqrt(x² + y²). Therefore, if you choose your y coordinate randomly between [-r, r], you just have to make sure that your x coordinate respects the previous equation, hence your math.
Demonstration
sqrt(x² + y²) < r
x² + y² < r²
x² < r² - y²
x < sqrt(r² - y²)
#
Your algorithm should be as follows. Once you chose the y coordinate, you can randomly choose x as long as it respects the distance constraint.
private void drawDots(int radius, int numDots){
double y;
double x;
double xMax;
for (int q = 0; q < numDots; q++){
// y is chosen randomly
y = -radius + random() * (radius + radius + 1);
// x must respect x² + y² < r²
xMax = pow((pow(radius,2)-pow(ycord,2)), 0.5);
x = random() * 2 * xMax - xMax;
turt.moveTo(x, y);
turt.penDown();
turt.forward(0);
turt.penUp();
}
}
Take a look at the documentation for random, you will see by default it produces a number between 0 and 1.
Basically this means that the expression you are looking for is:
ycord=-radius+random()*(radius*2);
This gives you a point on the y axis between -radius and radius (consider if the random() returns 0 you get -radius, it it returns 1 you get -radius+(2*radius())=radius.
You calculation for the x co-ordinate is correct but it gives you the x coordinate point on the circle (lets call it b). I suspect you want to use a new random to select an x co-ordinate between b and -b.
At present you are drawing points on the circle, not inside it. That is because you are not following the guideline correctly.
b = pow((pow(radius,2)-pow(ycord,2)),0.5); // this should be b
xcord = -b + random()*(b+b);