I'm using swt gc for drawing images. One of the option of my program is to rotate the image. I'm also drawing a rectangle as a border. To rotate I'm using following code:
Transform oldTransform = new Transform(gc.getDevice());
gc.getTransform(oldTransform);
Transform transform = new Transform(GCController.getCanvas().getDisplay());
transform.translate(this.x+width/2, this.y+height/2);
transform.rotate(rotation);
transform.scale(this.scaleX, this.scaleY);
transform.getElements(elements);
transform.translate(-this.x-width/2, -this.y-height/2);
gc.setTransform(transform);
gc.drawImage(image, this.x, this.y);
gc.setTransform(oldTransform);
transform.dispose();
After rotation I would like to calculate positions of corners of my rectangle.
I was trying something like this:
int tx = (this.x+width/2);
int ty = (this.y+height/2);
double rot = rotation * Math.PI/180;
double newX = tx*Math.cos(rot) - ty*Math.sin(rot);
double newY = tx*Math.sin(rot) + ty*Math.cos(rot);
But it does not working as I expected.
I've also tried using transformation matrix which I'm geting into elements array after each transformation:
int tx = (this.x+width/2);
int ty = (this.y+height/2);
double newX = this.x * elements[0] + this.y * elements[2];
double newY = this.x * elements[1] + this.y * elements[3];
But it gives same results as using equations for rotation. Any ideas ?
I've solved it:
int tx = (-width/2);
int ty = (-height/2);
double rot = rotation * Math.PI/180;
double newX = (tx)*Math.cos(rot) - (ty)*Math.sin(rot) + this.x + width/2;
double newY = (tx)*Math.sin(rot) + (ty)*Math.cos(rot) + this.y + height/2;
I had back to 0,0. Make rotation and after rotation translate it back.
You just need to multiply the transformation matrix by the position vector for each of the points of your rectangle.
The Transform class presumably has a method for doing this (would make sense), but I can't find readable "swt gc" API documentation, so I can't tell you what it is.
Related
I am making a game engine in Java. I am trying to add box collision to my game and this box needs to have the ability to rotate with the player. I have searched and found a formula which is supposed to calculate the new point of a rotated point, however, when I rotate my points they seem to follow a weird out of proportion figure 8 path instead of a circle around the center of my box.
for (Point p : points) {
//Loops through every point on the box (Square)
//top, left, bottom, right
float pointX = p.getX();
float pointY = p.getY();
//rotation as radians
float cos = (float) Math.cos(rotation);
float sin = (float) Math.sin(rotation);
pointX = centerX +(pointX-centerX) * cos + (pointY-centerY) * sin;
pointY = centerY -(pointY-centerY) * cos + (pointX-centerX) * sin;
p.setPos(pointX, pointY);
}
Here is what happens to the box as I rotate my player:
https://gyazo.com/ff801ce8458269c2385e24b2dc5404f5
Any help would be greatly appreciated, I have been tackling this for almost a week now with the same results.
The problem is that you calculate pointY with the new value of pointX.
Thanks to #Imus answer for the proper calculation.
Try:
float pointX = p.getX();
float pointY = p.getY();
//rotation as radians
float cos = (float) Math.cos(rotation);
float sin = (float) Math.sin(rotation);
float newPointX = centerX +(pointX-centerX) * cos + (pointY-centerY) * sin;
float newPointY = centerY +(pointY-centerY) * cos - (pointX-centerX) * sin;
p.setPos(newPointX, newPointY);
Remember that in java the Y axis is pointing downwards.
Not tested myself but you could try the following lines:
pointX = centerX +(pointX-centerX) * cos + (pointY-centerY) * sin;
pointY = centerY +(pointY-centerY) * cos - (pointX-centerX) * sin;
In the math club at my school we are doing a problem involving points that are evenly spaced around a circle. I decided to use computers in assisting us with this problem. Here I have two methods to help create the points.
public Point2D pointRotater(Point2D point, double angle){
double oldX = point.getX(), oldY = point.getY();
double centerX = 750, centerY = 750;
double newX = centerX + (oldX - centerX)*Math.cos(angle) - (oldY - centerY)*Math.sin(angle);
double newY = centerY + (oldX - centerX)*Math.sin(angle) + (oldY - centerY)*Math.cos(angle);
Point2D rPoint = new Point2D.Double(newX, newY);
return rPoint;
}
public void pointGen(int vertices){
lm.clearPoints();
Point2D startP = new Point2D.Double(750, 100);
lm.addPoint(startP);
double angle = 360 / vertices;
for(int i = 1; i < vertices; i++){
lm.addPoint(pointRotater(startP, angle * i));
}
}
lm is simply a class I made to manage the points, and to draw the points.
This does rotate the points, and does rotate them in a circular fashion, however the spacing between the points is clearly not accurate.
Where am I going wrong? I've looked over my code for hours and can't seem to find the problem. Thanks.
I wrote a gui where a user draws something in a (640x480) window. It makes that drawing into a set of points stored in a Vector array. Now, how do I translate those set of points to the origin (0,0 top left corner of the window) or put it at a specified pos? The width and height of the window I want it in is also 640x480.
After that is solved, how do you scale that new set of points to a size I want?
UPDATE 1
I solved the scale issue, but not the positioning issue. The drawing is not going where I tell it to be. Code below of what I have so far.
float scaleX = (float)width/boundingPoints.width;
float scaleY = (float)height/boundingPoints.height;
for(int i = 0; i < cg_points.size()-1; i++){
Point p1 = cg_points.get(i);
Point p2 = cg_points.get(i+1);
g.drawLine((int)(p1.x*scaleX) + pos.x, (int)(p1.y*scaleY) + pos.y, (int)(p2.x*scaleX) + pos.x, (int)(p2.y*scaleY) + pos.y);
}
I want the drawing to start at where pos [x, y] is. What is currently the problem is this. It does follow what pos.x and pos.y does, but it is way off and not starting at pos[x,y].
Here is a screen shot of the issue
As you can see from the picture, the box is where the star is supposed to be. The scaling is right as you can see, just not the pos. That is because the points in the drawing may NOT start at (0,0).
Any suggestions?
Thanks!
To translate a drawing, simply
foreach point in array
point.x += translate.x
point.y += translate.y
If you're going to center a drawing, pick a center (such as averaging all your points), negate that value, then translate all your points by that value.
To scale a drawing:
foreach point in array
point.x *= scale
point.y *= scale
So I solved it...YAY!!! Here is the code below in case you run into the same issue as I had.
float scaleX = (float)width/boundingPoints.width;
float scaleY = (float)height/boundingPoints.height;
int bx = boundingPoints.x;
int by = boundingPoints.y;
for(int i = 0; i < cg_points.size()-1; i++){
Point p1 = cg_points.get(i);
Point p2 = cg_points.get(i+1);
int x1 = (int) ((p1.x-bx)*scaleX);
x1 += pos.x;
int y1 = (int) ((p1.y-by)*scaleY);
y1 += pos.y;
int x2 = (int) ((p2.x-bx)*scaleX);
x2 += pos.x;
int y2 = (int) ((p2.y-by)*scaleY);
y2 += pos.y;
g.drawLine(x1, y1, x2, y2);
}
I got x and y (My position) and also destination.x and destination.y (where I want to get). This is not for homework, just for training.
So what I did already is
float x3 = x - destination.x;
float y3 = y - destination.y;
float angle = (float) Math.atan2(y3, x3);
float distance = (float) Math.hypot(x3, y3);
I got angle and distance but don't know how to make it move directly.
Please help!
Thanks!
Maybe using this will help
float vx = destination.x - x;
float vy = destination.y - y;
for (float t = 0.0; t < 1.0; t+= step) {
float next_point_x = x + vx*t;
float next_point_y = y + vy*t;
System.out.println(next_point_x + ", " + next_point_y);
}
Now you have the coordinates of the points on the line. Choose step to small enough according to your need.
To calculate the velocity from a given angle use this:
velx=(float)Math.cos((angle)*0.0174532925f)*speed;
vely=(float)Math.sin((angle)*0.0174532925f)*speed;
*speed=your speed :) (play with the number to see what is the right)
I recommend calculating the x and y components of your movement independently.
using trigonometric operations slows your program down significantly.
a simple solution for your problem would be:
float dx = targetX - positionX;
float dy = targetY - positionY;
positionX = positionX + dx;
positionY = positionY + dy;
in this code example, you calculate the x and y distance from your position to your target
and you move there in one step.
you can apply a time factor (<1) and do the calculation multiple times, to make it look like your object is moving.
Note that + and - are much faster than cos(), sin() etc.
I'm working with a Java 3D application called "Walrus" that is used to display directed graphs. The code already has a feature to highlight a node and draw label adjacent in graph given its screen coordinates.
Upon rotating the screen, the node is no more highlighted.
What I have is the node coordinates in 3D. I need to draw label to it.
Code for highlight using 3D coordinates
Point3d p = new Point3d();
m_graph.getNodeCoordinates(node, p);
PointArray array = new PointArray(1, PointArray.COORDINATES);
array.setCoordinate(0, p);
m_parameters.putModelTransform(gc);
gc.setAppearance(m_parameters.getPickAppearance());
How can I draw Label with 3D coordinates( Raster graphics throws error Renderer: Error creating immediate mode Canvas3D graphics context )
How can I convert 3D coordinates to 2D screen and use existing code to draw label at 2D screen point
Thanks,
Dakshina
I have an algorithm/method for converting [x,y,z] into [x,y] with the depth parameter:
The x value is : (int) (x - (z / depth * x))
The y value is : (int) (y - (z / depth * y))
Essentially, the depth is the focal point. The vanishing point will be at [0,0,depth].
Here's what i used to convert my 3D coordinates into perspective 2D, x2 and y2 being the 2dimensional coordinates, xyz being the 3D coordinates.
use these formulas:
x2 = cos(30)*x - cos(30)*y
y2 = sin(30)*x + sin(30)*y + z
I picked the angle 30 as it is easy for perspective purposes, also used in Isometric grids for drawing 3D on 2D papers. As the z axe will be the vertical one, x and y are the ones at 60 degrees from it right and left. Isometric Grid Picture.
I'm still working on rotation, but without altering the axes, just coordinate rotation in 3D.
Enjoy.
I found the solution.
This is the function to display Text3D at image 2D coordinates
public void drawLabel(GraphicsContext3D gc, double x, double y, int zOffset, String s) {
boolean frontBufferRenderingState = gc.getFrontBufferRendering();
gc.setBufferOverride(true);
gc.setFrontBufferRendering(true);
Point3d eye = getEye();
double labelZ = zOffset * LABEL_Z_OFFSET_SCALE
+ LABEL_Z_SCALE * eye.z + LABEL_Z_OFFSET;
double xOffset = LABEL_X_OFFSET * m_pixelToMeterScale;
double yOffset = LABEL_Y_OFFSET * m_pixelToMeterScale;
Point3d p = new Point3d(x + xOffset, y + yOffset, 0.0);
{
// Project given (x, y) coordinates to the plane z=labelZ.
// Convert from image-plate to eye coordinates.
p.x -= eye.x;
p.y -= eye.y;
double inversePerspectiveScale = 1.0 - labelZ / eye.z;
p.x *= inversePerspectiveScale;
p.y *= inversePerspectiveScale;
// Convert from eye to image-plate coordinates.
p.x += eye.x;
p.y += eye.y;
}
Transform3D scale = new Transform3D();
scale.set(LABEL_SCALE);
Vector3d t = new Vector3d(p.x, p.y, labelZ);
Transform3D translation = new Transform3D();
translation.set(t);
translation.mul(scale);
Transform3D transform = new Transform3D(m_imageToVworld);
transform.mul(translation);
gc.setModelTransform(transform);
//-----------------
int fontSize=(int)(10*m_magnification);
if(fontSize>20)
fontSize=20;
//---------------
// XXX: Courier may not be available on all systems.
Text2D text = new Text2D(s, new Color3f(1.0f, 1.0f, 1.0f),
"Courier", fontSize, Font.BOLD);
gc.draw(text);
gc.flush(true);
// NOTE: Resetting the model transform here is very important.
// For some reason, not doing this causes the immediate
// following frame to render incorrectly (but subsequent
// frames will render correctly). In some ways, this
// makes sense, because most rendering code assumes that
// GraphicsContext3D has been set to some reasonable
// transform.
gc.setModelTransform(m_objectTransform);
gc.setFrontBufferRendering(frontBufferRenderingState);
}
This is the function to take 3D coordinates and convert them to image 2D coordinates and render using above function
private boolean displayOnScreenLabel(int node, String label) {
boolean success = false;
try {
Transform3D transform = m_parameters.getObjectToEyeTransform();
Point3d nodeC = new Point3d();
m_graph.getNodeCoordinates(node, nodeC);
transform.transform(nodeC);
Point3d eye = m_parameters.getEye();
double perspectiveScale = 1.0 / (1.0 - nodeC.z / eye.z);
double centerX = eye.x + nodeC.x * perspectiveScale;
double centerY = eye.y + nodeC.y * perspectiveScale;
GraphicsContext3D gc = m_canvas.getGraphicsContext3D();
m_parameters.drawLabel(gc, centerX, centerY, m_labelZOffsetCounter++, label);
success = true;
} catch (final java.lang.OutOfMemoryError error) {
JOptionPane.showMessageDialog(m_frame, "The 3D Graphics is unable to find enough memory on your system. Kill the application!", "Out Of Memory!", JOptionPane.ERROR_MESSAGE);
} catch (Exception e) {
success = false;
}
return success;
}