I have been writing a image editing application for fun and all is well but i have ran into a problem with the zoom feature. The image editor plane is 512 x 512 pixels large but the image i want to edit is only 16 x 16. I want to know how to project my mouse coordinates to the smaller image to edit it pixel by pixel.
i have devised this algorithm to to such.
/**
*
* #param pointx The x position of the point thats being bound
* #param pointy The y position of the point thats being bound
* #param oldsizeX The old grid size x of which the point is currently in. ( eg ==> 512*512)
* #param oldsizeY The old grid size y of which the point is currently in. ( eg 512* ==> 512)
* #param newsizeX The new grid size x for the new grid size of the point. ( eg ==> 16*16)
* #param newsizeY The new grid size y for the new grid size of the point. ( eg 16* ==> 16)
* #param normalOffsetX The offset x, if any, the grid has in the normal plane ( eg ==> 32*32 # (512*512))
* #param normalOffsetY The offset y, if any, the grid has in the normal plane ( eg 32* ==> 32 # (512*512)
* #return A Vector2 containing the bound points in the new plane.
*/
public static Vector2 bindPoint(int pointx, int pointy, int oldsizeX, int oldsizeY, int newsizeX, int newsizeY,int normalOffsetX,int normalOffsetY) {
Vector2 vec = new Vector2();
int tileSizeX = oldsizeX / newsizeX;
int tileSizeY = oldsizeY / newsizeY;
int offsetX = normalOffsetX, offsetY = normalOffsetY;
vec.x = (int) (pointx / 2) / (oldsizeX / tileSizeX) - (offsetX / tileSizeX);
vec.y = (int) (pointy / 2) / (oldsizeY / tileSizeY) - (offsetY / tileSizeY);
if(pointx >= normalOffsetX && pointx <= normalOffsetX + oldsizeX && pointy >= normalOffsetY && pointy <= normalOffsetY + oldsizeY) {
return vec;
}else {
return new Vector2(-1,-1);
}
}
This works as long as the smaller resolution is 16x16 and i have found that if i change the 2 after the pointX and pointY division to 0.5 and an image of 32x32 works. What i want to know is if there is a better way to do so, so that i can use any size image at any zoom level?
You should not use integers to represent the position. Use double instead when you do the calculations. In the end, when you have calculated everything and need a pixel value, round the double to an integer. Otherwise you will have lose precision all over the place (which explains the problems you see).
You get different results depending on how you use your brackets. For example, from a math point of view the below Systems out's should give you the same result, but they don't:
int i = 700;
int j = 70;
int k = 30;
System.out.println((i / 2) / (j / k)); --> 175
System.out.println(i / 2 / j * k); --> 150
I figured it out on my own lol, sorry, its late and i spaced and forgot how to proportion.
Here is the answer for anyone else who needs it!
/**
*
* #param pointx The x position of the point thats being bound
* #param pointy The y position of the point thats being bound
* #param oldsizeX The old grid size x of which the point is currently in. ( eg ==> 512*512)
* #param oldsizeY The old grid size y of which the point is currently in. ( eg 512* ==> 512)
* #param newsizeX The new grid size x for the new grid size of the point. ( eg ==> 16*16)
* #param newsizeY The new grid size y for the new grid size of the point. ( eg 16* ==> 16)
* #param normalOffsetX The offset x, if any, the grid has in the normal plane ( eg ==> 32*32 # (512*512))
* #param normalOffsetY The offset y, if any, the grid has in the normal plane ( eg 32* ==> 32 # (512*512)
* #return A Vector2 containing the bound points in the new plane.
*/
public static Vector2 bindPoint(int pointx, int pointy, int oldsizeX, int oldsizeY, int newsizeX, int newsizeY,int normalOffsetX,int normalOffsetY) {
Vector2 vec = new Vector2();
int tileSizeX = oldsizeX / newsizeX;
int tileSizeY = oldsizeY / newsizeY;
int offsetX = normalOffsetX, offsetY = normalOffsetY;
vec.x = (int) Math.floor(pointx * ((float) newsizeX) / (float) oldsizeX) - (offsetX / tileSizeX);
vec.y = (int) Math.floor(pointy * ((float) newsizeY) / (float) oldsizeY) - (offsetY / tileSizeY);
return vec;
}
Related
I get the x- and y-orientation from an RFID-Tag and want to animate the movement in a JavaFX application. It's my first java project so I'm sorry if there are stupid mistakes.
I rotate an box-node in the way of this and this thread.
Two pictures of how the green node rotates in front of the RFID-reader image in the background
xAxis=red, yAxis=green, zAxis=blue
.
I call the rotateNode method like that:
// calculate necessary variables:
delta_x = -x_angle + x_angle_old;
delta_y = -y_angle + y_angle_old;
delta_x_radians = Math.toRadians(delta_x);
delta_y_radians = Math.toRadians(delta_y);
pitch_rad = delta_y_radians;
yaw_rad = 0d; // not used at the moment
roll_rad = delta_x_radians;
if (!((roll_rad == 0d) && (pitch_rad == 0d) && (yaw_rad == 0d))) {
rotateNode(model3D, pitch_rad, yaw_rad, roll_rad);
}
My box-node has the position (0,0,-200) at the beginning and the center of the object should stay in that position the hole time. Just the orientation in two directions should change. My rotateNode method looks like this:
public static void rotateNode(Group n, double pitch_rad, double yaw_rad, double roll_rad) {// , TranslateTransition
// tt_z) {
double A11 = Math.cos(roll_rad) * Math.cos(yaw_rad);
double A12 = Math.cos(pitch_rad) * Math.sin(roll_rad)
+ Math.cos(roll_rad) * Math.sin(pitch_rad) * Math.sin(yaw_rad);
double A13 = Math.sin(roll_rad) * Math.sin(pitch_rad)
- Math.cos(roll_rad) * Math.cos(pitch_rad) * Math.sin(yaw_rad);
double A21 = -Math.cos(yaw_rad) * Math.sin(roll_rad);
double A22 = Math.cos(roll_rad) * Math.cos(pitch_rad)
- Math.sin(roll_rad) * Math.sin(pitch_rad) * Math.sin(yaw_rad);
double A23 = Math.cos(roll_rad) * Math.sin(pitch_rad)
+ Math.cos(pitch_rad) * Math.sin(roll_rad) * Math.sin(yaw_rad);
double A31 = Math.sin(yaw_rad);
double A32 = -Math.cos(yaw_rad) * Math.sin(pitch_rad);
double A33 = Math.cos(pitch_rad) * Math.cos(yaw_rad);
double d = Math.acos((A11 + A22 + A33 - 1d) / 2d);
if (d != 0d) {
double den = 2d * Math.sin(d);
if (den != 0d) {
Point3D p = new Point3D((A32 - A23) / den, (A13 - A31) / den, (A21 - A12) / den);
x_pos_node = (n.localToScene(n.getBoundsInLocal()).getMaxX()
+ n.localToScene(n.getBoundsInLocal()).getMinX()) / 2d;
y_pos_node = (n.localToScene(n.getBoundsInLocal()).getMaxY()
+ n.localToScene(n.getBoundsInLocal()).getMinY()) / 2d;
z_pos_node = (n.localToScene(n.getBoundsInLocal()).getMaxZ()
+ n.localToScene(n.getBoundsInLocal()).getMinZ()) / 2d;
r.setPivotX(x_pos_node);
r.setPivotY(y_pos_node);
r.setPivotZ(z_pos_node);
r.setAxis(p);
r.setAngle(Math.toDegrees(d));
n.getTransforms().add(r);
Transform all = n.getLocalToSceneTransform();
n.getTransforms().clear();
n.getTransforms().add(all);
}
}
}
Printing the following variables shows that the node moves in y although I don't want that to happen. Also I see, that slowly with time the pivot point of the rotation isn't in the center of the node anymore and when I turn the RFID-Tag it doesn't spin around the middle of the node it spins in a circle which gets bigger and bigger..
from:
x_pos_node: 0,00
y_pos_node: 0,39
z_pos_node: -200,00
MaxX: 199,00
MinX: -199,00
MaxY: 2,78
MinY: -2,00
MaxZ: -176,12
MinZ: -223,88
Depth: 47,76
Height: 4,78
Width: 398,00
to:
x_pos_node: 0,00
y_pos_node: 15,52
z_pos_node: -200,00
MaxX: 198,51
MinX: -198,51
MaxY: 38,35
MinY: -7,31
MaxZ: -130,85
MinZ: -269,15
Depth: 138,30
Height: 45,67
Width: 397,02
Picture from the side that shows how the green node moves under blue z-Axis / zero line:
.
Where is my mistake? Why does the object slowly moves instead of just rotating?
It is possible to fix the wrong position when I add an Translation:
n.getTransforms().add(new Translate(0, -y_pos_node, 0));
But that's just an hotfix and you can see how the object moves down and up again.. I think there is an error in the calculations or the positioning of the pivot point. It also turns a bit around the green y-Axis although "yaw_rad" is set to 0;
Right now im Creating an 3D game for android/OpenGL the sence is to fly through such 3D Rings made with index Buffer Objects as shown in the pictures.
My Problem: Actually my twisted Loop is generating the Coordinates like it should but if i translate it got squezzed,
because this is causing in this case the Y Coordinate not to match to the z Coordinate.
I tried around a few days finding a way to compensate this issue but succesless,
anyone of you could say me a way creating a translateble 3D Ring in OpenGL/android with or even without my basics that would Help me very well.
for (int y = 0; y < 32; y++) {
for (int x = 0; x < 32; x++) {
final float xPosR = 0.5f * (float) Math.cos((x) * move) ; //move = 2*Pi/31 : In this part the x coordinates are getting builded (after rotating they are actually the z coordinates)
final float yPosR = translationY + (float) Math.sin((y) * move) //translation = 1[enter image description here][1]/0 otherwise you could do it as translation with the modelmatrix
+ 0.5f * (float) Math.cos((x) * move) * (float) Math.cos((y) * move) ; //In this part the Y Coordinates are getting builded
final float zPosR = - (1f * (float) Math.cos((y) * move)) +
0.5f * (float) Math.cos((x) * move) * (float) Math.sin((y) * move); //In this part the z Coordinates are getting builded(after rotating they are actually the z coordinates)
//in the onDrawFrame Method it gets rotate 90 degree around the y-axis and translated -5 in the z-axis
heightMapVertexDataR[offsetR++] = xPosR;
heightMapVertexDataR[offsetR++] = yPosR;
heightMapVertexDataR[offsetR++] = zPosR;
Translation = 1
Translation = 0
public class SimpleHarmonic {
public static void main(String[] args) {
StdDraw.setXscale(0,900);
StdDraw.setYscale(0,700);
while (true) {
StdDraw.setPenColor(StdDraw.BLACK);
StdDraw.line(0,350,900,350); // x-axis
StdDraw.line(450,0,450,900); // y-axis
StdDraw.setPenColor(StdDraw.RED);
for (double x = -450; x <= 450; x += 0.5) {
double y = 50 * Math.sin(x * (Math.PI / 180));
int Y = (int) y;
int X = (int) x;
StdDraw.line(450 + X, 350 - Y, 450 + X, 350 - Y);
}
StdDraw.clear();
}
}
}
In this code I am attempting to simulate simple harmonic motion. However, I have only been able to draw a static graph, but I need it to move continously.
I believe I need to use a loop to contionusly redraw the points, but I am not sure how to do that.
How can I make my current sine graph move contionusly?
Edit: Voted to close as non-programming? what?
I took a look at the StdDraw class you are using and it looks like what you want is the
StdDRaw.show(int) method, this method comment states:
/**
* Display on screen, pause for t milliseconds, and turn on
* <em>animation mode</em>: subsequent calls to
* drawing methods such as {#code line()}, {#code circle()}, and {#code square()}
* will not be displayed on screen until the next call to {#code show()}.
* This is useful for producing animations (clear the screen, draw a bunch of shapes,
* display on screen for a fixed amount of time, and repeat). It also speeds up
* drawing a huge number of shapes (call {#code show(0)} to defer drawing
* on screen, draw the shapes, and call {#code show(0)} to display them all
* on screen at once).
* #param t number of milliseconds
*/
In this library any time you call a draw method such as line or circle it conditionally repaints the frame. By passing the int param to the draw method it will turn all painting methods into "animation mode" and defer repainting the frame until you call draw() (no params).
To make it animate you must make each iteration of your while loop 1 animation frame, each frame will need to differ from the previous one. You can do this by using a variable outside your loop to offset each frame by a small ammount. Ill call this offset
With this information you can alter your loop to look like:
double offset = 0;
while (true) {
offset+=1; // move the frame slightly
StdDraw.show(10); // defer repainting for 10 milisecoinds
StdDraw.clear(); // clear before painting
StdDraw.setPenColor(StdDraw.BLACK);
StdDraw.line(0,350,900,350); // x-axis
StdDraw.line(450,0,450,900); // y-axis
StdDraw.setPenColor(StdDraw.RED);
for (double x = -450; x <= 450; x += 0.5) {
// apply the offset inside of calculation of Y only such that it
// slowly "moves" the sin wave
double y = 50 * Math.sin((offset+x) * (Math.PI / 180));
int Y = (int) y;
int X = (int) x;
StdDraw.line(450 + X, 350 - Y, 450 + X, 350 - Y);
}
StdDraw.show(); // end animation frame. force a repaint
}
A few improvements in your code
1 Inside your loop where you draw each "dot" you are increnting by .5. Because that X value is literally 1 pixel you arent gaining anything by going to .5 instead of 1. 1 is quite literally the smallest you can visually see in this enviroment. I recommend making it at least be x+=1
for (double x = -450; x <= 450; x += 1)
2 You are using the .line method but drawing to the same point. You could significantly speed up your program by only calculating every 3rd pixels Y value and connecting the dots. For instance
double prevX = -450;
double prevY = 50 * Math.sin((prevX+offset) * (Math.PI / 180)); // seed the previous Y to start
for (double x = 0; x <= 450; x += 3) {
double y = 50 * Math.sin((x+offset) * (Math.PI / 180));
StdDraw.line(450 + (int)prevX, 350 - (int)prevY, 450 + (int)x, 350 - (int)y);
prevX = x;
prevY = y;
}
3 This isnt your code but in the StdDraw.init method you can set some rendering hints to allow for cleaner lines. This should make it look alot nicer
offscreen.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_PURE);
Combining all those things heres what I wrote
public static void main(String[] args) {
StdDraw.setXscale(0,900);
StdDraw.setYscale(0,700);
double offset = 0;
while (true) {
StdDraw.show(10);
StdDraw.clear();
offset-=1;
StdDraw.setPenColor(StdDraw.BLACK);
StdDraw.line(0,350,900,350); // x-axis
StdDraw.line(450,0,450,900); // y-axis
StdDraw.setPenColor(StdDraw.RED);
double prevX = 0;
double prevY = 50 * Math.sin((prevX+offset) * (Math.PI / 180)); // seed the previous Y to start
StdDraw.filledCircle(450 + prevX, 350 - prevY, 5);
for (double x = 0; x <= 450; x += 3) {
double y = 50 * Math.sin((x+offset) * (Math.PI / 180));
StdDraw.line(450 + (int)prevX, 350 - (int)prevY, 450 + (int)x, 350 - (int)y);
prevX = x;
prevY = y;
}
StdDraw.show();
}
}
I dont have an animation recorder so heres a picture
Let's say I am given two colors.
public final static Color FAR = new Color(237, 237, 30);
public final static Color CLOSE = new Color(58, 237, 221);
How would I transition from one color to the next without dipping into dark colors?
I have come up with ideas such as
double ratio = diff / range; // goes from 1 to 0
int red = (int)Math.abs((ratio * FAR.getRed()) - ((1 - ratio) * CLOSE.getRed()));
int green = (int)Math.abs((ratio * FAR.getGreen()) - ((1 - ratio) * CLOSE.getGreen()));
int blue = (int)Math.abs((ratio * FAR.getBlue()) - ((1 - ratio) * CLOSE.getBlue()));
OR
double ratio = diff / range; // goes from 1 to 0
int red = (int) ((1 - (diff / range)) * FAR.getRed() + CLOSE.getRed() - FAR.getRed());
int green = (int) ((1 - (diff / range)) * FAR.getGreen() + CLOSE.getGreen() - FAR.getGreen());
int blue = (int) ((1 - (diff / range)) * FAR.getBlue() + CLOSE.getBlue() - FAR.getBlue());
But unfortunately none of them smoothly transition from one color to the next.
Would anyone know how to do so while keeping the color bright and not dipping into darker colors, or how to ensure that gradient transition is smooth rather than slow at first then fast and then slow again?
I really ca not come up with any formula.
You're using the wrong sign in the calcuations. Should be plus, not minus, to apply the ratio properly.
int red = (int)Math.abs((ratio * FAR.getRed()) + ((1 - ratio) * CLOSE.getRed()));
int green = (int)Math.abs((ratio * FAR.getGreen()) + ((1 - ratio) * CLOSE.getGreen()));
int blue = (int)Math.abs((ratio * FAR.getBlue()) + ((1 - ratio) * CLOSE.getBlue()));
The reason you are getting dark colours with your existing implementation is that with (-), they would often fall close to zero (less than 50? or negative but greater than -50?) and in the negative case, well, you are taking the absolute value so it becomes a small positive number, i.e. a dark colour.
(ratio * FAR.getGreen()) + ((1 - ratio) * CLOSE.getGreen())
if ratio goest from 0 to 1, then this is weighted average, lets say ratio = 1/2, then it would be aritmetical average, if ratio = 1/3, then it is weighted average where FAR has weight 1 and CLOSE has weight 2
This works nicely for me:
// Steps between fading from one colour to another.
private static final int FadeSteps = 25;
private void fade(Label panel, Color colour) throws InterruptedException {
final Color oldColour = panel.getBackground();
final int dRed = colour.getRed() - oldColour.getRed();
final int dGreen = colour.getGreen() - oldColour.getGreen();
final int dBlue = colour.getBlue() - oldColour.getBlue();
// No point if no difference.
if (dRed != 0 || dGreen != 0 || dBlue != 0) {
// Do it in n steps.
for (int i = 0; i <= FadeSteps; i++) {
final Color c = new Color(
oldColour.getRed() + ((dRed * i) / FadeSteps),
oldColour.getGreen() + ((dGreen * i) / FadeSteps),
oldColour.getBlue() + ((dBlue * i) / FadeSteps));
panel.setBackground(c);
Thread.sleep(10);
}
}
}
Not the neatest bit of code but it works.
I have a few geometric shapes which are in different co-ordinates that I want to be brought to the center of the panel. I have written a function called "Set Scale," which is bringing it in to center, but the shape is reversed.
My Code:
private void SetScale1(Graphics2D gr, int gr_width , int gr_height, double left_x , double right_x , double top_y , double bottom_y ){
Rectangle2D drawing_rect = new Rectangle2D.Double(left_x, top_y, right_x - left_x, bottom_y - top_y);
double drawing_cx=( left_x+ right_x) / 2;
double drawing_cy =(top_y + bottom_y) / 2;
AffineTransform at = AffineTransform.getTranslateInstance(-1 * drawing_cx, -1 * drawing_cy);
//gr.translate(-1 * drawing_cx, -1 * drawing_cy);
//gr.TranslateTransform(0, 0)
double scale_x=gr_width / drawing_rect.getWidth();
double scale_y=gr_height / Math.abs(drawing_rect.getHeight());
scale_x = Math.min(scale_x, scale_y);
scale_y = scale_x;
scale_x = Math.abs(scale_x);
// at = AffineTransform.getScaleInstance(scale_x, -1 * scale_y);
//gr.transform(tt);
gr.transform(at);
//' Translate to center over the drawing area.
double graphics_cx =gr_width / 2;
double graphics_cy = gr_height / 2;
gr.translate(graphics_cx, graphics_cy);
}
From the description given, I would recommend you take a look at your transformation matrix:
AffineTransform at = AffineTransform.getTranslateInstance(-1 * drawing_cx, -1 * drawing_cy);
Are you certain that your tx and ty parameters are correct? Here is the matrix returned by getTranslationInstance:
[ 1 0 tx ]
[ 0 1 ty ]
[ 0 0 1 ]
The -1 values you have set in the parameters for getTranslationInstance seem suspect to me... unless I've missed something, I don't see any reason why they should be negative. Negative values should mirror the result, so that may very well be why you're seeing the reversal. Try changing those (they will change the tx and ty fields in the matrix above, respectively) and let us know if it helps.
i Tried this... it Works:-)
Thanks for the help:-)
Rectangle2D drawing_rect = new Rectangle2D.Double(left_x, top_y, right_x - left_x, bottom_y - top_y);
drawing_cx=(left_x+ right_x) / 2;
drawing_cy =(top_y + bottom_y) / 2;
at= AffineTransform.getTranslateInstance( -1* drawing_cx, -1*drawing_cy);
//gr.translate(-1 * drawing_cx, -1 * drawing_cy);
//gr.TranslateTransform(0, 0)
double scale_x=gr_width / drawing_rect.getWidth();
double scale_y=gr_height / Math.abs(drawing_rect.getHeight());
scale_x = Math.min(scale_x, scale_y);
scale_y = scale_x;
scale_x = Math.abs(scale_x);
// at = AffineTransform.getScaleInstance(scale_x, -1 * scale_y);
gr.scale(Math.round(scale_x), Math.round( scale_y));
//gr.scale(1.2, 1.2);
//gr.transform(tt);
gr.transform(at);
//' Translate to center over the drawing area.
graphics_cx =gr_width / 2;
graphics_cy = gr_height / 2;
gr.translate(graphics_cx, graphics_cy);