I have two objects in a 2D space. I expect object1 to begin orbiting object2. I derived my methods from the equation
f = G * (m1 * m2 / r*r)
and
dx1 += x1 - x2 * f
etc. However, I am struggling because the object is only moving in the pos pos direction. Here is the class for each object:
Mass.java
import java.awt.Point;
public class Mass {
public static float G = 0.1f;
public Point center;
public float mass, radius, dx = 0, dy = 0;
public boolean locked = false;
public Mass(Point center, float[] vect, float mass) {
this.center = center;
this.dx = vect[0];
this.dy = vect[1];
this.mass = mass;
this.radius = mass;
}
public void moveVector(float[] vector) {
if(!this.locked) {
this.dx += vector[0];
this.dy += vector[1];
}
}
public void lock() {
this.locked = true;
}
public static float distance(Mass obj1, Mass obj2) {
float dX = obj1.center.x - obj2.center.x;
float dY = obj1.center.y - obj2.center.y;
double ans = Math.sqrt(Math.pow(dX, 2) + Math.pow(dY, 2));
return (float) ans;
}
public static float force(Mass obj1, Mass obj2) {
double ans = ((obj1.mass * obj2.mass) / Math.pow(distance(obj1, obj2), 2)) * G;
return (float) ans;
}
public static float[] vector(Mass obj1, Mass obj2) {
// total change between the two objects
float force = force(obj1, obj2);
float totalX = Math.abs(obj1.center.x - obj2.center.x);
float totalY = Math.abs(obj1.center.y - obj2.center.y);
float x = totalX * force;
float y = totalY * force;
float[] vector = {x, y};
return vector;
}
}
This is the main class.
Sim.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Sim extends JPanel {
private static final long serialVersionUID = -2669101810074157675L;
public static final int PREF_W = 800, PREF_H = 600;
private Mass object1, object2;
private Sim() {
this.setFocusable(true);
this.setBackground(Color.WHITE);
float[] vect1 = {0, -1}, vect2 = {0, 0};
object1 = new Mass(new Point(PREF_W / 2 - 100, PREF_H / 2 - 100), vect1, 10);
object2 = new Mass(new Point(PREF_W / 2 + 100, PREF_H / 2 + 100), vect2, 30);
gameTimer.start();
}
private Timer gameTimer = new Timer(1000/30, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
object1.moveVector(Mass.vector(object1, object2));
object1.center.x += object1.dx;
object1.center.y += object1.dy;
System.out.println("[" + object1.dx + "," + object1.dy + "]");
}
});
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.fillOval(
(int) object1.center.x - (int) object1.radius,
(int) object1.center.y - (int) object1.radius,
(int) object1.radius,
(int) object1.radius
);
g2.fillOval(
(int) object2.center.x - (int) object2.radius,
(int) object2.center.y - (int) object2.radius,
(int) object2.radius,
(int) object2.radius
);
g2.drawLine(object1.center.x, object1.center.y, object2.center.x, object2.center.y);
repaint();
}
/* METHODS FOR CREATING JFRAME AND JPANEL */
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Gravity Simulation");
JPanel gamePanel = new Sim();
frame.getContentPane().add(gamePanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
I have it printing out the DX and DY of object1 (the unlocked object) at all times. It seems to get flung super fast, as expected, but it never slows down. Instead, the dx is just increasing slower and slower. I'm not very mathy, but it seems to be logistic. I wonder why this is happening.
So far I have tried rewriting my formula and using a different equation. I have also attempted using different datatypes, and making some things negative. Nothing works, though.
TLDR, the problem:
Objects are not changing DX / DY as expected.
Thank you in advance! Sorry if this was posted somewhere else, I could not find any duplicates.
OK, let's try to derive formulas.
You already have difference vector dX, dY, and make also normalized vector
udX = dX / distance
udY = dY / distance
You also have force magnitude. To get force vector for object 1, just multiply normalized difference components by this magnitude (note minus sign because force direction is TO object2 (while dx, dy is vector FROM object 2))
fx1 = - udX * force
fy1 = - udY * force
(and force vector for object2 if needed)
fx2 = - fx1
fy2 = - fy1
First object velocity vector is (vx1, vy1). At every step you have to modify it with acceleration, where deltaT is time period between cadrs.
vx1 = vx1 + fx1 / mass1 * deltaT
vy1 = vy1 + fy1 / mass1 * deltaT
Now you can modify position with velocity
x1 = x1 + vx * deltaT
y1 = y1 + vy * deltaT
Related
As you can see on the image, I have a p1 and p2 objects with (x,y) coordinates which I know the values, and I know radius of all these circle objects.
However, I want to calculate new position x,y which would be p3 center point. Basically, as you can see it's p2 position + radius.
I am doing this for java game which is based on libgdx. I would appreciate any math or java language directions/examples.
See code comments for explanation.
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import javax.swing.*;
class CenteredCircle extends Ellipse2D.Double {
CenteredCircle(Point2D.Double p, double radius) {
super(p.x - radius, p.y - radius, 2 * radius, 2 * radius);
}
}
public class CircleDemo extends JFrame {
public CircleDemo() {
int width = 640; int height = 480;
setSize(new Dimension(width, height));
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
JPanel p = new JPanel() {
#Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
// center p1
Point2D.Double p1 = new Point2D.Double(getSize().width/2, getSize().height/2);
double radius = 130.0;
// big circle
Shape circle2 = new CenteredCircle(p1, radius);
g2d.draw(circle2);
// 12 small circles
for (int angle = 0; angle < 360; angle += 30) {
// this is the magic part
// a polar co-ordinate has a length and an angle
// by changing the angle we rotate
// the transformed co-ordinate is the center of the small circle
Point2D.Double newCenter = polarToCartesian(radius, angle);
// draw line just for visualization
Line2D line = new Line2D.Double(p1.x, p1.y, p1.x + newCenter.x, p1.y+ newCenter.y);
g2d.draw(line);
// draw the small circle
Shape circle = new CenteredCircle(
new Point2D.Double(p1.x + newCenter.x, p1.y + newCenter.y),
radius/4);
g2d.draw(circle);
}
}
};
setTitle("Circle Demo");
getContentPane().add(p);
}
public static void main(String arg[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new CircleDemo();
}
});
}
static Point2D.Double polarToCartesian(double r, double theta) {
theta = (theta * Math.PI) / 180.0; // multiply first, then divide to keep error small
return new Point2D.Double(r * Math.cos(theta), r * Math.sin(theta));
}
// not needed, just for completeness
public static Point2D.Double cartesianToPolar(double x, double y) {
return new Point2D.Double(Math.sqrt(x * x + y * y), (Math.atan2(y, x) * 180) / Math.PI);
}
}
Now using libgdx for the graphics. Thus no need for polar co-ordinates, on the outside.
I am not doing frame rate relative animation. Therefore, this is no perfect match to your code.
Using the following calculation (if (theta >= 360) { theta = 0.0f; }) at the end of the render method will let the animation restart with its original value.
package org.demo;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.ScreenUtils;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
public class CircleDemo extends ApplicationAdapter {
ShapeRenderer shapeRenderer;
float theta = 0.0f;
#Override
public void create () {
shapeRenderer = new ShapeRenderer();
}
#Override
public void render () {
ScreenUtils.clear(0, 0.4f, 0.4f, 1);
Vector2 p1 = new Vector2( Gdx.graphics.getWidth() / 2.0f , Gdx.graphics.getHeight() / 2.0f);
Vector2 smallCircleCenter = new Vector2(150.0f, 0.0f);
smallCircleCenter.add(p1); // translate center by p1
shapeRenderer.begin(ShapeRenderer.ShapeType.Line);
// static lines and circles
for (int angle = 0; angle < 360; angle += 30) {
Vector2 lineEnd = new Vector2(smallCircleCenter);
lineEnd.rotateAroundDeg(p1, angle);
shapeRenderer.line(p1, lineEnd);
shapeRenderer.circle(lineEnd.x, lineEnd.y, 20);
}
// animated line and circle in red
shapeRenderer.setColor(0.75f, 0, 0, 1);
Vector2 movingCircleCenter = new Vector2(smallCircleCenter);
movingCircleCenter.rotateAroundDeg(p1, theta);
shapeRenderer.line(p1, movingCircleCenter);
shapeRenderer.circle(movingCircleCenter.x, movingCircleCenter.y, 20);
shapeRenderer.setColor(1, 1, 1, 1);
shapeRenderer.end();
theta++;
// for the screenshot stop at 90 degrees
if (theta >= 90) {
theta = 90.0f;
}
}
#Override
public void dispose () {
shapeRenderer.dispose();
}
}
So I wrote a test in my project, based on your approach:
package com.bigbang.test.impl;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import com.bigbang.Game;
import com.bigbang.graphics.g2d.shapes.impl.Ellipse;
import com.bigbang.graphics.g2d.shapes.impl.Line;
import com.bigbang.graphics.gl.Color;
import com.bigbang.math.BBMath;
public class PolarToCartesianTest extends AbstractTest {
private Array<GraphicalObject> graphicalObjectArray;
private GraphicalObject dynamicGraphicalObject;
private float radius, smallCircleRadius;
private float centerX, centerY;
public PolarToCartesianTest(Game game) {
super(game);
}
#Override
public void create() {
radius = 200f;
centerX = game.getScreenController().getScreenWidth() / 2;
centerY = game.getScreenController().getScreenHeight() / 2;
smallCircleRadius = radius / 4;
graphicalObjectArray = new Array<>();
for (int angle = 0; angle < 360; angle += 30) {
GraphicalObject graphicalObject = new GraphicalObject();
graphicalObject.angle = angle;
graphicalObjectArray.add(graphicalObject);
}
dynamicGraphicalObject = new GraphicalObject();
game.getCameraController().getCamera().position.x = game.getScreenController().getScreenWidth() / 2;
game.getCameraController().getCamera().position.y = game.getScreenController().getScreenHeight() / 2;
}
#Override
public void update(float deltaTime) {
for (GraphicalObject graphicalObject : graphicalObjectArray) {
Vector2 polarToCartesianPosition = BBMath.polarToCartesian(radius, graphicalObject.angle);
graphicalObject.line.x1 = centerX + 0;
graphicalObject.line.y1 = centerY + 0;
graphicalObject.line.x2 = centerX + polarToCartesianPosition.x;
graphicalObject.line.y2 = centerY + polarToCartesianPosition.y;
graphicalObject.line.color = Color.WHITE_COLOR;
graphicalObject.ellipse.x = centerX + polarToCartesianPosition.x;
graphicalObject.ellipse.y = centerY + polarToCartesianPosition.y;
graphicalObject.ellipse.width = 2 * smallCircleRadius;
graphicalObject.ellipse.height = 2 * smallCircleRadius;
graphicalObject.ellipse.color = Color.WHITE_COLOR;
}
float shift = 0;
float theta = (shift * smallCircleRadius) * (centerY / centerX);
Vector2 pos = BBMath.polarToCartesian(radius, theta);
dynamicGraphicalObject.line.color = new Color(Color.RED);
dynamicGraphicalObject.line.x1 = centerX + 0;
dynamicGraphicalObject.line.y1 = centerY + 0;
dynamicGraphicalObject.line.x2 = centerX + pos.x;
dynamicGraphicalObject.line.y2 = centerY + pos.y;
dynamicGraphicalObject.ellipse.x = centerX + pos.x;
dynamicGraphicalObject.ellipse.y = centerY + pos.y;
dynamicGraphicalObject.ellipse.width = 2 * smallCircleRadius;
dynamicGraphicalObject.ellipse.height = 2 * smallCircleRadius;
dynamicGraphicalObject.ellipse.color = new Color(Color.RED);
}
#Override
public void draw() {
game.getShapeRenderer().begin(ShapeRenderer.ShapeType.Line);
for (GraphicalObject graphicalObject : graphicalObjectArray) {
graphicalObject.line.draw();
graphicalObject.ellipse.draw();
}
dynamicGraphicalObject.line.draw();
dynamicGraphicalObject.ellipse.draw();
game.getShapeRenderer().end();
}
class GraphicalObject {
Ellipse ellipse;
Line line;
float angle;
public GraphicalObject() {
this.ellipse = new Ellipse(game);
this.line = new Line(game);
}
}
}
Which is same math like in your example, with some modifications:
However, you can notice I have this dynamicGraphicalObject (red circle), which I want to shift position around circle by using theta value calculated as (shift * smallCircleRadius) * (centerY / centerX);. This works perfect for shift=0 value. It's properly positioned/overlapping white. But if I would change shift variable to 1, 2, 3, or 11, you can see that it's not precisely aligned with white circles. Is this floating point issue or am I missing something in calculation of theta ?
shift values used: 2,6 and 11 in order by images
--
SOLUTION:
float fixPrecision = 1.1f;
float theta = (shift * fixPrecision) + ((shift * smallCircleRadius) * (centerY / centerX));
I am attempting to simulate a sphere, and shade it realistically given an origin vector for the light, and the sphere being centered around the origin. Moreover, the light's vector is the normal vector on a larger invisible sphere at a chosen point. The sphere looks off.
https://imgur.com/a/IDIwQQF
The problem, is that it is very difficult to bug fix this kind of program. Especially considering that I know how I want it to look in my head, but when looking at the numbers in my program there is very little meaning attached to them.
Since I don't know where the issue is, I'm forced to paste all of it here.
public class SphereDrawing extends JPanel {
private static final long serialVersionUID = 1L;
private static final int ADJ = 320;
private static final double LIGHT_SPHERE_RADIUS = 5;
private static final double LIGHT_X = 3;
private static final double LIGHT_Y = 4;
private static final double LIGHT_Z = 0;
private static final double DRAWN_SPHERE_RADIUS = 1;
private static final int POINT_COUNT = 1000000;
private static Coord[] points;
private static final double SCALE = 200;
public SphereDrawing() {
setPreferredSize(new Dimension(640, 640));
setBackground(Color.white);
points = new Coord[POINT_COUNT];
initializePoints();
for (int i = 0; i < points.length; i++) {
points[i].scale();
}
new Timer(17, (ActionEvent e) -> {
repaint();
}).start();
}
public void initializePoints() { //finding the points on the surface of the sphere (hopefully somewhat equidistant)
double random = Math.random() * (double)POINT_COUNT;
double offset = 2/(double)POINT_COUNT;
double increment = Math.PI * (3 - Math.sqrt(5));
for (int i = 0; i < POINT_COUNT; i++) {
double y = ((i * offset) - 1) + (offset / 2);
double r = Math.sqrt(1 - Math.pow(y, 2));
double phi = ((i + random) % (double)POINT_COUNT) * increment;
double x = Math.cos(phi) * r;
double z = Math.sin(phi) * r;
points[i] = new Coord(x, y, z);
}
}
public void drawSphere(Graphics2D g) {
g.translate(ADJ, ADJ); //shifting from origin for drawing purposes
Arrays.sort(points); //sorting points by their z coordinates
double iHat = -2 * LIGHT_X;
double jHat = -2 * LIGHT_Y; //Light vector
double kHat = -2 * LIGHT_Z;
double angL1 = 0;
if (Math.abs(iHat) != 0.0)
angL1 = Math.atan(jHat / iHat); //converting light vector to spherical coordinates
else
angL1 = Math.PI/2;
double angL2 = Math.atan(Math.sqrt(Math.pow(iHat, 2) + Math.pow(jHat, 2))/ kHat);
double maxArcLength = LIGHT_SPHERE_RADIUS * Math.PI; // maximum arc length
for (int i = 0; i < points.length; i++) {
if(points[i].checkValid()) {
double siHat = -2 * points[i].x;
double sjHat = -2 * points[i].y; //finding normal vector for the given point on the sphere
double skHat = -2 * points[i].z;
double angSF1 = -1 * Math.abs(Math.atan(sjHat / siHat)); // converting vector to spherical coordinates
double angSF2 = Math.atan(Math.sqrt(Math.pow(siHat, 2) + Math.pow(sjHat, 2))/ skHat);
double actArcLength = LIGHT_SPHERE_RADIUS * Math.acos(Math.cos(angL1) * Math.cos(angSF1) + Math.sin(angL1) * Math.sin(angSF1) * Math.cos(angL2 - angSF2)); //calculating arc length at this point
double comp = actArcLength / maxArcLength; // comparing the maximum arc length to the calculated arc length for this vector
int col = (int)(comp * 255);
col = Math.abs(col);
g.setColor(new Color(col, col, col));
double ovalDim = (4 * Math.PI * Math.pow(DRAWN_SPHERE_RADIUS, 2))/POINT_COUNT; //using surface area to determine how large size of each point should be drawn
if (ovalDim < 1) // if it too small, make less small
ovalDim = 2;
g.fillOval((int)points[i].x, (int)points[i].y, (int)ovalDim, (int)ovalDim); //draw this oval
}
}
}
#Override
public void paintComponent(Graphics gg) {
super.paintComponent(gg);
Graphics2D g = (Graphics2D) gg;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
drawSphere(g);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setTitle("Sphere");
f.setResizable(false);
f.add(new SphereDrawing(), BorderLayout.CENTER);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
#SuppressWarnings("rawtypes")
private class Coord implements Comparable {
public double x;
public double y;
public double z;
public Coord(double x2, double y2, double z2) {
x = x2;
y = y2;
z = z2;
}
public void scale() {
x *= SCALE;
y *= SCALE; //drawing purposes
z *= SCALE;
}
public String toString() {
return x + " " + y + " " + z;
}
public int compareTo(Object c) {
double diff = this.z - ((Coord)c).z;
if (diff < 0)
return -1;
else if (diff > 0) //for sorting the array of points
return 1;
else
return 0;
}
public boolean checkValid() {
return (z > 0); //checks if need to draw this point
}
}
}
I was hoping to at least draw a realistic looking sphere, even if not completely accurate, and I couldn't tell you what exactly is off with mine
I'm making an application about space physics, so I do lots with orbits. Naturally, I encounter the Ellipse2D.Double to draw my orbits on the screen.
Whenever my JPanel refreshes, I draw the orbit of a body using an Ellipse2D, as well as the body itself with a different method.
Essentially, I discovered that when numbers get very large (whether it be the size of the orbits get large or the visualization is zoomed in very far), the position of the body and the Ellipse2D do not line up.
I calculate the position of the body using a conversion from polar coordinates to rectangular coordinates, and I leave the math for the Ellipse2D up to the geom package.
Take a look at this code sample. It's the most self-contained version of my problem that I can make, since scale of the circle has to be very large:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.math.BigDecimal;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class EllipseDemo extends JPanel {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.add(new EllipseDemo());
frame.setVisible(true);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
// These values allow for a very zoomed in view of a piece of the circle
BigDecimal[] circleCenter = { new BigDecimal(-262842.5), new BigDecimal(-93212.8) };
BigDecimal circleRadius = new BigDecimal(279081.3);
// Draw the circle at the given center, with the given width and height
// x = centerx - radius, y = centery - radius, w = h = radius * 2
g2d.draw(new Ellipse2D.Double(circleCenter[0].subtract(circleRadius).doubleValue(),
circleCenter[1].subtract(circleRadius).doubleValue(), circleRadius.multiply(new BigDecimal(2)).doubleValue(),
circleRadius.multiply(new BigDecimal(2)).doubleValue()));
// Get a rectangular conversion of a point on the circle at this angle
BigDecimal angle = new BigDecimal(0.34117696217);
BigDecimal[] rectangular = convertPolarToRectangular(new BigDecimal[] {
circleRadius, angle });
// Draw a line from the center of the circle to the point
g2d.draw(new Line2D.Double(circleCenter[0].doubleValue(), circleCenter[1].doubleValue(),
circleCenter[0].add(rectangular[0]).doubleValue(), circleCenter[1]
.add(rectangular[1]).doubleValue()));
}
public BigDecimal[] convertPolarToRectangular(BigDecimal[] polar) {
BigDecimal radius = polar[0];
BigDecimal angle = polar[1];
BigDecimal x = radius.multiply(new BigDecimal(Math.cos(angle.doubleValue())));
BigDecimal y = radius.multiply(new BigDecimal(Math.sin(angle.doubleValue())));
return new BigDecimal[] { x, y };
}
}
The code above essentially draws a circle on the screen very far away with a large radius. I've picked the dimension so that a piece of the circle is visible in the small window.
Then it draws a line from the center of the circle to a point on the circle that's visible in the window: I picked an angle that was visible on the window and used geometry to convert that angle and the radius of the circle into rectangular coordinates.
This is what the program displays:
Notice that the line doesn't actually end up touching the ellipse. Now, I decided I had to find out whether it was the point I calculated or the ellipse that were incorrect. I did the math on my calculator, and found that the line was correct, and the ellipse incorrect:
Considering that the calculator is probably not wrong, I am led to believe the Ellipse2D is not drawing correctly. However, I tried many other angles, and this is the pattern I found:
And that leads me to believe the calculations are somehow wrong.
So that's my problem. Should I be using something other than Ellipse2D? Maybe Ellipse2D is not accurate enough? I used BigDecimals in my code sample because I thought it would give me more precision - is that the wrong approach? My ultimate goal is to be able to calculate the rectangular position of a point on an ellipse at a specific angle.
Thanks in advance.
You see this error because Ellipse2D is approximated by four cubic curves. To make sure just take a look at its path iterator defining shape border: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/awt/geom/EllipseIterator.java#187
To improve quality we should approximate ellipse by higher number of cubic curves. Here is an extention of standard java implementation with changeable number of segments:
class BetterEllipse extends Ellipse2D.Double {
private int segments;
public BetterEllipse(int segments, double x, double y, double w, double h) {
super(x, y, w, h);
this.segments = segments;
}
public int getSegments() {
return segments;
}
#Override
public PathIterator getPathIterator(final AffineTransform affine) {
return new PathIterator() {
private int index = 0;
#Override
public void next() {
index++;
}
#Override
public int getWindingRule() {
return WIND_NON_ZERO;
}
#Override
public boolean isDone() {
return index > getSegments() + 1;
}
#Override
public int currentSegment(double[] coords) {
int count = getSegments();
if (index > count)
return SEG_CLOSE;
BetterEllipse ellipse = BetterEllipse.this;
double x = ellipse.getCenterX() + Math.sin(2 * Math.PI * index / count) * ellipse.getWidth() / 2;
double y = ellipse.getCenterY() + Math.cos(2 * Math.PI * index / count) * ellipse.getHeight() / 2;
if (index == 0) {
coords[0] = x;
coords[1] = y;
if (affine != null)
affine.transform(coords, 0, coords, 0, 1);
return SEG_MOVETO;
}
double x0 = ellipse.getCenterX() + Math.sin(2 * Math.PI * (index - 2) / count) * ellipse.getWidth() / 2;
double y0 = ellipse.getCenterY() + Math.cos(2 * Math.PI * (index - 2) / count) * ellipse.getHeight() / 2;
double x1 = ellipse.getCenterX() + Math.sin(2 * Math.PI * (index - 1) / count) * ellipse.getWidth() / 2;
double y1 = ellipse.getCenterY() + Math.cos(2 * Math.PI * (index - 1) / count) * ellipse.getHeight() / 2;
double x2 = x;
double y2 = y;
double x3 = ellipse.getCenterX() + Math.sin(2 * Math.PI * (index + 1) / count) * ellipse.getWidth() / 2;
double y3 = ellipse.getCenterY() + Math.cos(2 * Math.PI * (index + 1) / count) * ellipse.getHeight() / 2;
double x1ctrl = x1 + (x2 - x0) / 6;
double y1ctrl = y1 + (y2 - y0) / 6;
double x2ctrl = x2 + (x1 - x3) / 6;
double y2ctrl = y2 + (y1 - y3) / 6;
coords[0] = x1ctrl;
coords[1] = y1ctrl;
coords[2] = x2ctrl;
coords[3] = y2ctrl;
coords[4] = x2;
coords[5] = y2;
if (affine != null)
affine.transform(coords, 0, coords, 0, 3);
return SEG_CUBICTO;
}
#Override
public int currentSegment(float[] coords) {
double[] temp = new double[6];
int ret = currentSegment(temp);
for (int i = 0; i < coords.length; i++)
coords[i] = (float)temp[i];
return ret;
}
};
}
}
And here is how you can use it in your code instead of standard one (I use 100 segments here):
g2d.draw(new BetterEllipse(100, circleCenter[0].subtract(circleRadius).doubleValue(),
circleCenter[1].subtract(circleRadius).doubleValue(), circleRadius.multiply(new BigDecimal(2)).doubleValue(),
circleRadius.multiply(new BigDecimal(2)).doubleValue()));
I'm building an application which has a slideshow in its homepage, currently I use Thread.sleep(10) and add/sub the x position of panel I want to slide.
For example: slideIn(30, panel_1, 10) < this will cause panel_1 to slide in with interval of 30ms and subtracts its x by 10 overtime until the panel is in center/occupy the slideshow_panel. But the con of this method is that the sliding animation won't smooth, I want the sliding animation/transition as smooth as Bootstrap Carousel. Is there a way to calculate the speed and increment/decrement value for slide transition speed?
Actually, I've something that's almost perfect for this. I assume you can create a Path2D for your animation path, right? And it also seems like you want a constant speed. There are a couple of references to my project (http://sourceforge.net/p/tus/code/HEAD/tree/) for calculating distance and showing the JPanel for instance, but it shouldn't be hard to remove them and replace with standard java. Try it out
public abstract class PathAnimation {
private Path2D mPath;
private double totalLength;
/**
* Be careful to call path.closePath before invoking this constructor
* #param path
*/
public PathAnimation(Path2D path) {
mPath = path;
totalLength = 0;
PathIterator iterator = mPath.getPathIterator(null);
//Point2D currentLocation;// = path.getCurrentPoint();
double[] location = new double[6];
iterator.currentSegment(location);
while (!iterator.isDone()) {
double[] loc = new double[6];
iterator.next();
iterator.currentSegment(loc);
if (loc[0] == 0 && loc[1] == 0) continue;
double distance = MathUtils.distance(location[0], location[1], loc[0], loc[1]);
totalLength += distance;
location = loc;
}
}
#Override
public Point2D getLocationAtTime(int time) {
return getLocationAtTime(time / (double) getTotalAnimationTime());
}
public Point2D getLocationAtTime(double pctTime) {
double len = totalLength * pctTime;
PathIterator iterator = mPath.getPathIterator(null);
double[] location = new double[6];
iterator.currentSegment(location);
while (!iterator.isDone()) {
double[] loc = new double[6];
iterator.next();
iterator.currentSegment(loc);
double distance= MathUtils.distance(location[0], location[1], loc[0], loc[1]);
if (distance > len) {
double pctThere = len / distance;
double xSpot = location[0] * (1 - pctThere) + loc[0] * pctThere;
double ySpot = location[1] * (1 - pctThere) + loc[1] * pctThere;
return new Point2D.Double(xSpot, ySpot);
}
len -= distance;
location = loc;
}
throw new ArrayIndexOutOfBoundsException("Path is too short or time is too long!");
}
/**
* Number of milliseconds that this animation spans
* #return
*/
public abstract int getTotalAnimationTime();
public static void main(String args[]) {
Rectangle rect = new Rectangle(10,10,20,20);
final Path2D.Double myPath = new Path2D.Double((Shape)rect);
myPath.closePath();
final PathAnimation myAnimation = new PathAnimation(myPath) {
Area star = new Area(PaintUtils.createStandardStar(15, 15, 5, .5, 0));
#Override
public Dimension getSizeAtTime(int time) {
return new Dimension(15,15);
}
#Override
public void paintAtTime(Graphics2D g, int time) {
Area toPaint = star;
if ((time / 150) % 2 == 1) {
Dimension size = getSizeAtTime(0);
toPaint = new Area(toPaint);
PaintUtils.rotateArea(toPaint, Math.PI / 6);
}
g.setColor(Color.YELLOW);
g.fill(toPaint);
g.setColor(Color.RED);
g.draw(toPaint);
}
#Override
public int getTotalAnimationTime() {
return 10000;
}
};
System.out.println(myAnimation.getLocationAtTime(0));
System.out.println(myAnimation.getLocationAtTime(2500));
System.out.println(myAnimation.getLocationAtTime(4000));
System.out.println(myAnimation.getLocationAtTime(5000));
System.out.println(myAnimation.getLocationAtTime(7000));
System.out.println(myAnimation.getLocationAtTime(7500));
System.out.println(myAnimation.getLocationAtTime(9000));
System.out.println(myAnimation.getLocationAtTime(10000));
final JPanel jp = new JPanel() {
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
int time = ((int) System.currentTimeMillis()) % myAnimation.getTotalAnimationTime();
int time2 = (time + myAnimation.getTotalAnimationTime() / 2) % myAnimation.getTotalAnimationTime();
Point2D pt = myAnimation.getLocationAtTime(time);
Point2D pt2 = myAnimation.getLocationAtTime(time2);
Dimension size = myAnimation.getSizeAtTime(time);
g2.translate(pt.getX() - size.width / 2, pt.getY() - size.height / 2);
myAnimation.paintAtTime(g2, time);
g2.translate(- (pt.getX() - size.width / 2), - (pt.getY() - size.height / 2));
g2.translate(pt2.getX() - size.width / 2, pt2.getY() - size.height / 2);
myAnimation.paintAtTime(g2, time2);
g2.translate(- (pt2.getX() - size.width / 2), - (pt2.getY() - size.height / 2));
g2.setColor(Color.BLACK);
g2.draw(myPath);
}
};
WindowUtilities.visualize(jp);
AbstractAction action = new AbstractAction() {
public void actionPerformed(ActionEvent ae) {
jp.repaint();
}
};
javax.swing.Timer t = new javax.swing.Timer(30, action);
t.start();
}
}
I have been trying to figure this one out for some time now, I am making a program that uses a triangle as an arrow and been trying to figure out how to make an arrow with two points, meaning that that the first point would be at the midpoint of the base of the triangle, while the second point would be at the tip facing the direction away from the first point.
This crude paint drawing should help figure out what I am talking about
http://i.stack.imgur.com/f3ktz.png (Would put direct images but don't have enough rep)
Now, I went through and tried figuring out how to calculate those other two endpoints of the triangle so I could make the polygon, but I am not doing it correctly because I am getting a triangle that isn't isosceles and the endpoints don't create a line perpendicular to the original line.
What I am currently getting (With some drawing over it to show the points)
http://i.stack.imgur.com/dljsn.png
My current code
public class Triangle extends Shape{
private boolean assigned = false;
private int[] x;
private int[] y;
public Triangle(Point startPoint, Point endPoint){
this.startPoint = startPoint;
this.endPoint = endPoint;
}
#Override
public void draw(Graphics g) {
g.setColor(Color.white);
if(!assigned) {
x = new int[3];
y = new int[3];
double distance = startPoint.distance(endPoint);
double halfDistance = distance/2;
double angle = getAngle(startPoint,endPoint)- Math.PI/2.0;
x[0] = (int)endPoint.getX();
y[0] = (int)endPoint.getY();
x[1] = (int)((Math.sin(angle)*halfDistance) + startPoint.getX());
y[1] = (int)((Math.cos(angle)*halfDistance) + startPoint.getY());
x[2] = (int)(startPoint.getX() - (Math.sin(angle)*halfDistance));
y[2] = (int)(startPoint.getY() - (Math.cos(angle)*halfDistance));
assigned = true;
if(endPoint.distance(x[1],y[1]) == (Math.sqrt(5)*halfDistance))
System.out.println("DEBUG: Confirm Correct 1");
if(endPoint.distance(x[1],y[1]) == endPoint.distance(x[2],y[2]))
System.out.println("DEBUG: Confirm Correct 2");
}
g.fillPolygon(x,y,3);
g.setColor(Color.blue);
}
private double getAngle(Point pointOne, Point pointTwo){
double angle = Math.atan2(pointTwo.getY()- pointOne.getY(),pointTwo.getX()-pointOne.getX());
while(angle < 0){
angle += (2.0*Math.PI);
}
return angle;
}
}
I have working at this for hours and can't seem to figure it out, someone please help.
So, I ended up replacing double angle = getAngle(startPoint,endPoint)- Math.PI/2.0; with something more like double angle = -Math.atan2(endPoint.y - startPoint.y, endPoint.x - startPoint.x);
I wrote this little test program, which allows you to move to points around a circle and which generates the resulting triangle...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Point;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
TestPane tp = new TestPane();
JPanel control = new JPanel(new BorderLayout());
control.add(tp);
final JSlider startAngel = new JSlider(0, 359);
final JSlider endAngel = new JSlider(0, 359);
JPanel sliders = new JPanel(new GridLayout(1, 2));
sliders.add(startAngel);
sliders.add(endAngel);
startAngel.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
tp.setStartAngle(startAngel.getValue());
}
});
endAngel.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
tp.setEndAngle(endAngel.getValue());
}
});
startAngel.setValue(0);
endAngel.setValue(180);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(control);
frame.add(sliders, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Point startPoint, endPoint;
private float startAngle = 0;
private float endAngle = 180;
public TestPane() {
}
#Override
public void invalidate() {
super.invalidate();
recalculate();
}
protected void recalculate() {
int dim = Math.min(getWidth(), getHeight());
dim -= 50;
float radius = dim / 2f;
startPoint = getPointOnCircle(startAngle, radius);
endPoint = getPointOnCircle(endAngle, radius);
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected Point getPointOnCircle(float degress, float radius) {
int x = Math.round(getWidth() / 2);
int y = Math.round(getHeight() / 2);
double rads = Math.toRadians(degress - 90); // 0 becomes the top
// Calculate the outter point of the line
int xPosy = Math.round((float) (x + Math.cos(rads) * radius));
int yPosy = Math.round((float) (y + Math.sin(rads) * radius));
return new Point(xPosy, yPosy);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int[] x = new int[3];
int[] y = new int[3];
double distance = startPoint.distance(endPoint);
double halfDistance = distance / 2;
double angle = -Math.atan2(endPoint.y - startPoint.y, endPoint.x - startPoint.x);
System.out.println(angle);
x[0] = (int) endPoint.getX();
y[0] = (int) endPoint.getY();
x[1] = (int) ((Math.sin(angle) * halfDistance) + startPoint.getX());
y[1] = (int) ((Math.cos(angle) * halfDistance) + startPoint.getY());
x[2] = (int) (startPoint.getX() - (Math.sin(angle) * halfDistance));
y[2] = (int) (startPoint.getY() - (Math.cos(angle) * halfDistance));
g2d.setColor(Color.RED);
g2d.fillPolygon(x, y, 3);
g2d.setColor(Color.BLUE);
g2d.fillOval(startPoint.x - 5, startPoint.y - 5, 10, 10);
g2d.setColor(Color.GREEN);
g2d.fillOval(endPoint.x - 5, endPoint.y - 5, 10, 10);
g2d.dispose();
}
public void setStartAngle(float value) {
startAngle = value;
recalculate();
}
public void setEndAngle(float value) {
endAngle = value;
recalculate();
}
}
}
If that still gives you some weird results, apart from sharing some test data, I might consider using something like Math.atan2(Math.abs(endPoint.y - startPoint.y), Math.abs(endPoint.x - startPoint.x)) or simular
You don't need to calculate angles at all.
double startX = 40;
double startY = 120;
double endX = 110;
double endY = 15;
double deltaX = ( startY - endY ) / 2;
double deltaY = ( endX - startX ) / 2;
double[] polygonX = new double[3];
double[] polygonY = new double[3];
polygonX[0] = endX;
polygonY[0] = endY;
polygonX[1] = startX - deltaX;
polygonY[1] = startY - deltaY;
polygonX[2] = startX + deltaX;
polygonY[2] = startY + deltaY;
The drawing is VERY bad :D, but the point is that:
cos(ang) = 'distance' / ( startY - endY )
and
cod(ang) = ('distance'/2) / deltaX
so
deltaX = ( startY - endY ) / 2
The same aplies to deltaY = ( endX - startX ) / 2
So the other 2 point of the triangle, will be the startPoint minus and plus those deltas.