I'm trying to create a basic model editor but either I'm not understanding atan2 or it's not the right solution for me to be using, because it is returning unpredictable and frustrating results. I've been using a bit of code borrowed from another thread on this site to get the angle between two points, but the problem is, it's just not doing what I want and isn't rotating to the right place. Here's the class where I design my models, render them and drag them.
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
public class ModelDesigner extends JPanel implements MouseListener, MouseMotionListener{
Skeleton CurrentSkeleton = new Skeleton();
ArrayList<Rectangle> ClickableJointPoints = new ArrayList<Rectangle>();
Joint SelectionJoint = null;
boolean Dragging = false;
Point ClickPoint = new Point(0, 0);
public ModelDesigner(){
setBackground(Color.white);
addMouseListener(this);
addMouseMotionListener(this);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
RenderSkeleton(g2, CurrentSkeleton);
}
void RenderSkeleton(Graphics2D g2, Skeleton e){
for (Joint j : e.ChildJoints){
RenderJoint(g2, j, getWidth() / 2, 10, 0);
}
}
void RenderJoint(Graphics2D g2, Joint j, int ParentX, int ParentY, double r){
g2.setColor(Color.black);
Point p = getEndPointOfRotatedLine(j.Rotation + r, j.X, j.Y, j.Length);
g2.drawLine(ParentX + j.X, ParentY + j.Y, ParentX + p.x, ParentY + p.y);
g2.setColor(Color.red);
g2.fillOval(ParentX + p.x - 3, ParentY + p.y - 3, 6, 6);
if (j.HasChildren){
for (Joint j2 : j.ChildJoints){
RenderJoint(g2, j2, ParentX + p.x, ParentY + p.y, j.Rotation);
}
}
}
public static Point getEndPointOfRotatedLine(double angle, int x, int y, int length){
Point p = new Point();
p.x = (int) (x + length * Math.cos(angle));
p.y = (int) (y + length * Math.sin(angle));
return p;
}
@Override
public void mouseDragged(MouseEvent e) {
Rectangle Click = new Rectangle(e.getX(), e.getY(), 1, 1);
if (Dragging){
SelectionJoint.Rotation = getAngle(ClickPoint, new Point(Click.x, Click.y));
repaint();
}
}
public double getAngle(Point orig, Point target) {
double angle = (double) (Math.atan2(target.y - orig.y, target.x - orig.x));
if(angle < 0){
angle += 360;
}
return angle;
}
@Override
public void mouseMoved(MouseEvent arg0) {
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
Rectangle Click = new Rectangle(e.getX(), e.getY(), 1, 1);
/*
* Point p = getEndPointOfRotatedLine(j.Rotation, j.X, j.Y, j.Length);
* ClickableJointPoints.add(new Rectangle(p.x - 3, p.y - 3, 6, 6));
*/
for (Joint j : CurrentSkeleton.ChildJoints){
Point p = getEndPointOfRotatedLine(j.Rotation, j.X, j.Y , j.Length);
//this.getGraphics().drawRect(p.x - 3 + (getWidth() / 2), p.y - 3 + 10, 6, 6);
if (new Rectangle(p.x - 3 + getWidth() / 2, p.y - 3 + 10, 6, 6).intersects(Click)){
System.out.println("intersection");
SelectionJoint = j;
Dragging = true;
ClickPoint = p;
return;
}else{
if (j.HasChildren){
for (Joint j2 : j.ChildJoints){
if (getIntersectionOfRectAndJoints(Click, j2, j.X + p.x, j.Y + p.y, j.Rotation)){
return;
}
}
}
}
}
}
public boolean getIntersectionOfRectAndJoints(Rectangle r, Joint j, int parentX, int parentY, double rotation){
Point p = getEndPointOfRotatedLine(j.Rotation + rotation, j.X, j.Y, j.Length);
this.getGraphics().drawRect(parentX + p.x - 3 + (getWidth() / 2), parentY + p.y - 3 + 10, 6, 6);
if (new Rectangle(parentX + p.x - 3 + getWidth() / 2, parentY + p.y - 3 + 10, 6, 6).intersects(r)){
System.out.println("intersection");
SelectionJoint = j;
Dragging = true;
ClickPoint = p;
return true;
}else{
if (j.HasChildren){
for (Joint j2 : j.ChildJoints){
if (getIntersectionOfRectAndJoints(r, j2, parentX + j.X + p.x, parentY + j.Y + p.y, j.Rotation)){
return true;
}
}
}
}
return false;
}
@Override
public void mouseReleased(MouseEvent e) {
Dragging = false;
}
}
Here's the code for the skeleton class (it comes with a constructor that makes a basic model):
import java.util.ArrayList;
public class Skeleton {
ArrayList<Joint> ChildJoints = new ArrayList<Joint>();
/*
* The constructor just adds a fancy default skeleton, you should be able to add
* and remove limbs, as well as change lengths eventually.
*/
public Skeleton(){
//add the primary parent joint
Joint HeadJoint = new Joint();
HeadJoint.Length = 10;
HeadJoint.Rotation = Math.toRadians(90);
Joint Spine = new Joint();
Spine.Length = 100;
Spine.Rotation = Math.toRadians(0);
Joint leftArm = new Joint();
leftArm.Length = 50;
leftArm.Rotation = Math.toRadians(45);
Joint rightArm = new Joint();
rightArm.Length = 50;
rightArm.Rotation = Math.toRadians(315);
Joint leftLeg = new Joint();
leftLeg.Length = 60;
leftLeg.Rotation = Math.toRadians(112);
Joint rightLeg = new Joint();
rightLeg.Length = 60;
rightLeg.Rotation = Math.toRadians(68);
Joint bottomLeftLeg = new Joint();
bottomLeftLeg.Length = 35;
bottomLeftLeg.Rotation = Math.toRadians(-10);
Joint bottomRightLeg = new Joint();
bottomRightLeg.Length = 35;
bottomRightLeg.Rotation = Math.toRadians(10);
ChildJoints.add(HeadJoint);
HeadJoint.AddChild(Spine);
HeadJoint.AddChild(leftArm);
HeadJoint.AddChild(rightArm);
Spine.AddChild(leftLeg);
Spine.AddChild(rightLeg);
leftLeg.AddChild(bottomLeftLeg);
rightLeg.AddChild(bottomRightLeg);
}
public void MoveSkeleton(int newX, int newY){
for (Joint j : ChildJoints){
j.MoveJoint(newX, newY);
}
}
}
And here's the code of the Joint class:
import java.util.ArrayList;
public class Joint {
ArrayList<Joint> ChildJoints = new ArrayList<Joint>();
int Length = 0;
double Rotation = 0.0;
boolean HasChildren = false;
int X;
int Y;
String JointName = "unknown joint";
public void MoveJoint(int newX, int newY){
X = newX;
Y = newY;
for (Joint j : ChildJoints){
if (j.HasChildren){
j.MoveJoint(newX, newY);
}else{
j.X = newX;
j.Y = newY;
}
}
}
public void AddChild(Joint j){
ChildJoints.add(j);
HasChildren = true;
}
public void RemoveChild(int v){
if (ChildJoints.size() == 0){
HasChildren = false;
}
}
}
The code is still a bit messy because not everything is implemented but it's getting a bit irritating, because if I can't do this I can't really move on and the program doesn't do what it's supposed to. Basically, it's causing the angles to go to the wrong place. So if anybody understands my problem, please give me a hand. Thank you.
I have solved the problem, to a degree. I was forgetting to add the correct co-ordinates to the ClickPoint when I initialised it, such as the x-and-y locations of the parent joints as well as the actual render co-ordinates. Thank you to all for your help, it was a silly mistake on my part.