I've been using a customized PannableCanvas that was presented here, and I've run into a bit of trouble. My software is almost finished, and the last thing I need to do is add support for spawning the Nodes on the center of the screen. So what I did originally was:
nodeTranslateX/Y = -pannableCanvas.getTranslateX/Y
And that worked until I realized that if you zoom in/out (where scale no longer = 1.0), it completely messes it up and the node starts getting placed in weird spots. So then I tried:
nodeTranslateX/Y = -pannableCanvas.getTranslateX/Y * pannableCanvas.getScale()
And that didn't work either! It gets thrown even further out of whack.
I tried numerous combinations of transformations such as dividing the scale, inverting the scale, using screenToLocal(), you name it. None of them have worked. The problem is that when you zoom out, the translateX/Y of the canvas is adjusted for the scale, but that gives you stuff like 1500 when the nodes should be spawn with sane coordinates like 300.
Is there a way to get the coordinates of the top-left corner of the screen relative to the canvas? I'm completely at a loss on how to fix this. Again, I just need to be able to spawn the node at the center of the screen. What should I do?
Thank you for your time!
I got it to work by doing this:
Window window = scene.getWindow();
double screenCenterX = (window.getX() + window.getWidth()/2);
double screenCenterY = (window.getY() + window.getHeight()/2);
Point2D point = pannablePane.screenToLocal(screenCenterX, screenCenterY);
double nodeX = point.getX() - MyNodeImplementation.WIDTH/2;
double nodeY = point.getY() - MyNodeImplementation.HEIGHT/2;
I had mistaken screenX/Y for being within the window (I.E. center = WIDTH/2, HEIGHT/2), but instead you actually have to take the actual window position into account too.
Related
I am trying to program a visualisation for the Mandelbrot set in java, and there are a couple of things that I am struggling with to program. I realize that questions around this topic have been asked a lot and there is a lot of documentation online but a lot of things seem very complicated and I am relatively new to programming.
The first issue
The first issue I have is to do with zooming in on the fractal. My goal is to make an "infinite" zoom on the fractal (of course not infinite, as far as a regular computer allows it regarding calculation time and precision). The approach I am currently going for is the following on a timer:
Draw the set using some number of iterations on the range (-2, 2) on the real axis and (2, 2) on the imaginary axis.
Change those ranges to zoom in.
Redraw that section of the set with the number of iterations.
It's the second step that I struggle with. This is my current code:
for (int Py = beginY; Py < endY; Py++) {
for (int Px = beginX; Px < endX; Px++) {
double x0 = map(Px, 0, height,-2, 2);
double y0 = map(Py, 0, width, -2, 2);
Px and Py are the coordinates of the pixels in the image. The image is 1000x1000. The map funtion takes a number, in this case Px or Py, with a range of (0, 1000) and devides it evenly over the range (-2, 2), so it returns the corresponding value in that range.
I think that in order to zoom in, I'll have to change the -2 and 2 values by some way in the timer, but whatever I try, it doesn't seem to work. The zoom always ends up slowing down after a while or it will end up zooming in on a part of the set that is in the set, so not the borders. I tried multiplying them by some scale factor every timer tick, but that doesn't really produce the result I was looking for.
Now I have two questions about this issue.
Is this the right approach to visualizing the set and zooming in(draw, change range, redraw)?
If it is, how do I zoom in properly on an area that is interesting and that will keep zooming in properly even after running for a minute?
The second issue
Of course when visualizing something, you need to get some actual visual thing. In this case I want to color the set in a way similar to what you see here: (https://upload.wikimedia.org/wikipedia/commons/f/fc/Mandel_zoom_08_satellite_antenna.jpg).
My guess is that you have use the amount of iterations a pixel went through to before breaking out of the loop to give it some color value. However, I only really know how to do this with a black and white color scheme. I tried making a color array that holds the same amount of different gray colors as the amount of max iterations, starting from black and ending in white. Here is my code:
Color[] colors = new Color[maxIterations + 2];
for (int i = 0; i < colors.length; i++) {
colors[i] = new Color((int)map(i, 0, maxIterations + 2, 0, 255),
(int)map(i, 0, maxIterations + 2, 0, 255),
(int)map(i, 0, maxIterations + 2, 0, 255));
}
I then just filled in the amount of iterations in the array and assigned that color to the pixel. I have two questions about this:
Will this also work as we zoom into the fractal in the previously described manner?
How can I add my own color scheme in this, like in the picture? I've read some things about "linear interpolation" but I don't really understand what it is and in what way it can help me.
It sounds like you've made a good start.
Re the first issue: I believe there are ways to automatically choose an "interesting" portion of the set to zoom in on, but I don't know what they are. And I'm quite sure it involves more than just applying some linear function to your current bounding rectangle, which is what it sounds like you're doing.
So you could try to find out what these methods are (might get mathematically complicated), but if you're new to programming, you'll probably find it easier to let the user choose where to zoom. This is also more fun in the beginning, since you can run your program repeatedly and explore a new part of the set each time.
A simple way to do this is to let the user draw a rectangle over the image, and use your map function to convert the pixel coordinates of the drawn rectangle to the new real and imaginary coordinates of your zoom area.
You could also combine both approaches: once you've found somewhere you find interesting by manually selecting the zoom area, you can set this as your "final destination", and have the code gradually and smoothly zoom into it, to create a nice movie.
It will always get gradually slower though, as you start using ever more precise coordinates, until you reach the limits of precision with double and it becomes a pixellated mess. From there, if you want to zoom further, you'll have to look into arbitrary-precision arithmetic with BigDecimal - and it will continue to get slower and slower.
Re the second issue: starting off by calculating a value of numIterations / maxIterations (i.e. between 0 and 1) for each pixel is the right idea (I think this is basically what you're doing).
From there, there are all sorts of ways to convert this value to a colour, it's time to get creative!
A simple one is to have an array of a few very different colours. E.g. if you had white (0.0), red (0.25), green (0.5), blue (0.75), black (1.0), then if your calculated number was exactly one of the ones listed, you'd use the corresponding colour. If it's somewhere between, you blend the colours, e.g. for 0.3 you'd take:
((0.5-0.3)*red + (0.3-0.25)*green) / (0.5 - 0.25)
= 0.8*red + 0.2*green
Taking a weighted average of two colours is something I'll leave as an exercise ;)
(hint: take separate averages of the r, g, and b values. Playing with the alpha values could maybe also work).
Another one, if you want to get more mathsy, is to take an equation for a spiral and use it to calculate a point on a plane in HSB colour space (you can keep the brightness at some fixed value, say 1). In fact, any curve in 2D or 3D which you know how to write as an equation of one real variable can be used this way to give you smoothly changing colours, if you interpret the coordinates as points in some colour space.
Hope that's enough to keep you going! Let me know if it's not clear.
I am trying to set my applications center in the middle of the screen, and I apparently figured out myself the common solution proposed on other threads here at StackOverFlow, although you can clearly see that the application should be further to the left.
The screen I am using now is a 1920x1080, but the same thing used to happen on the 1366x766 screen.
Rectangle2D p = Screen.getPrimary().getVisualBounds();
double w = p.getWidth() / 1.5;
double h = p.getHeight() / 1.5;
stage.setWidth(w);
stage.setHeight(h);
stage.setX(p.getWidth()/2 - stage.getWidth()/2);
stage.setY(p.getHeight()/2 - stage.getHeight()/2);
The stage proportions are (in my case) 1280x720
Is there any reason why this is happening? Thanks in advance.
EDIT: Ok, I figured out that it works pretty well (from someones comment) and that the issue is the windows application bar that I positioned on the left, whilst when it is on the original position, the application is central.
im trying do develop a Zelda like game. So far i am using bitmaps and everything runs smooth. At this point the camera of the hero is fixed, meaning, that he can be anywhere on the screen.
The problem with that is scaling. Supporting every device and keeping every in perfect sized rects doesnt seem to be that easy :D
To prevent that i need a moving camera. Than i can scale everything to be equally sized on every device. The hero would than be in the middle of the screen for the first step.
The working solution for that is
xCam += hero.moveX;
yCam += hero.moveY;
canvas.translate(xCam,yCam);
drawRoom();
canvas.restore();
drawHero();
I do it like this, because i dont wand to rearrange every tile in the game. I guess that could be too much processing on some devices. As i said, this works just fine. the hero is in the middle of the screen, and the whole room is moving.
But the problem is collision detection.
Here a quick example:
wall.rect.intersects(hero.rect);
Assuming the wall was originally on (0/0) and the hero is on (screenWitdh/2 / screenHeight/2) they should collide on some point.
The problem is, that the x and y of the wall.rect never change. They are (0/0) at any point of the canvas translation, so they can never collide.
I know, that I can work with canvas.getClipBounds() and then use the coordinates of the returned rect to change every tile, but as I mentioned above, I am trying to avoid that plus, the returned rect only works with int values, and not float.
Do you guys know any solution for that problem, or has anyone ever fixed something like this?
Looking forward to your answers!
You can separate your model logic and view logic. Suppose your development dimension for the window is WxH. In this case if your sprite in the model is 100x100 and placed at 0,0, it will cover area from 0,0 to 100, 100. Let's add next sprite (same 100x100 dimension) at 105,0 (basically slightly to the right of the first one), which covers area from 105,0 to 205,100. It is obvious that in the model they are not colliding. Now, as for view if your target device happens to be WxH you just draw the model as it is. If your device has a screen with w = 2*W, h = 2*H, so twice as big in each direction. You just multiply the x and y by w / W and h / H respectively. Therefore we get 2x for x and y, which on screen becomes 1st object - from 0,0 to 200, 200, 2nd object - from 210,0 to 410, 200. As can be seen they are still not colliding. To sum up, separate your game logic from your drawing (rendering) logic.
I think you should have variables holding the player's position on the "map". So you can use this to determine the collision with the non changing wall. It should look something like (depensing on the rest of your code):
canvas.translate(-hero.rect.centerX(), -.rect.centerY());
drawRoom();
canvas.restore();
drawHero();
Generally you should do the calculations in map coordinates, not on screen. For rendering just use the (negative) player position for translation.
I have a completely graphed out blueprint of X, Y coordinates of 8 different multi-pointed shapes on paper. I put these coordinates into an array such as..
Polygon shape1;
int[] shapeOneX = {1,2,3,4,5,6,7,8,9};
int[] shapeOneY = {1,2,3,4,5,6,7,8,9};
shape1 = new Polygon {shapeOneX, shapeOneY, shapeOneX.length};
These coordinates are fake, and not my actual ones but on paper, these coordinates would follow the rules completely on how you would expect vector graphing to look like. When I load this into a Java Applet, the shape does not follow these exact coordinates. They're sometimes close, but not exact, and I need precision for my project.
Does anyone know why, or if there is a different formula you need to use on the coordinates to have it look the same in a java applet? If need more info, let me know.
I understand that starting coordinates for java applet start at the top left 0,0 then expand from there. I guess my questioning is,I have the understanding that "vector" cords start at 0,0 as a Mid point. I don't know much about graphing. So... my shapes are being created from a vector style, but being "placed" into an applet which has a 0,0 top left origin. Which is fine, I have the tools to adjust them where I need to put them. I just can't get them to create the shape I actually graph on paper. Do I need to graph on paper from a 0,0 top left origin and only create positive X, Y variables?
Another Edit-- I've noticed that when it draws onto the applet, it draws it almost mirrored as well. In other words, (x) goes right, (-x) goes left. That's normal. But (y) goes DOWN, and (-y) goes UP.. That doesn't seem normal HMM.. Confused.
Final Edit(probably) -- Well I was right about the Y axis being mirrored. Why? I don't know. But it has allowed for me to redesign some coordinates. I am currently under the impression that line borders were so thick that connected each vertex, that they reformed the shapes into a blob of junk. Because of the overlapping borders. It was hard to see where each vertex actually truly was. I also had to increase the values of my (x,y) coordinates in order to compensate for the size difference. Which I have probably near 100 or so different (x,y) combinations that I will need to re-do because of this... I really wish there was an easier answer. I am open to any and all suggestions, meanwhile I will plug away at remapping this. Thanks everyone who has, or continues to contribute.
For Example.. This first was the orignal coordinates:
int[] wallX = { -2,-2,-1,-1, 2, 2 };
int[] wallY = { -1, 3, 3, 0, 0,-1 };
And then the new WORKING coordinates I found to work are:
int[] wallOneX = { -2,-2, 1, 1, 10, 10 };
int[] wallOneY = { 4,-8,-8, 1, 1, 4 };
So thats the difference of numbers needed to create the same shape from paper, into the java applet. I don't really see a pattern or anything to recreate it for all my other ones. So I don't know.
You need to scale your coordinates based on the height and width of your jpanel or canvas object on which you are painting the polygon. use getHeight() and getWidth() to get the dimensions. Also, the origin is in the upper right corner of the jpanel or canvass, so you either need to use addition/subtraction to shift the scaled coordinates, or you need to use the affine transform to get the polygon where you want it to go.
Sometimes it helps to start with working examples. You might try this approach or this approach. Here is a third approach already in an applet.
UPDATE: I think I figured it out. The scaleMultiplier also applied to the translate that I was trying to do. I had a suspicion this was the case but couldn't figure out exactly how it got affected.
Anyone familiar with a spinoff of Java called Processing? I'm trying to do something simple, scale a shape and place it in the center of the sketch. This is my code in a nutshell:
pushMatrix();
float scaleX, scaleY, scaleMultiplier, resetX, resetY, transX, transY;
scaleX = 500 / (float)clickState.bounds.getWidth();
scaleY = 500 / (float)clickState.bounds.getHeight();
scaleMultiplier = min(scaleX,scaleY);
resetX = -(float)clickState.bounds.getX();
resetY = -(float)clickState.bounds.getY();
transX = resetX + ((800 - ((float)clickState.bounds.getWidth() * scaleMultiplier))/2);
transY = resetY + ((550 - ((float)clickState.bounds.getHeight() * scaleMultiplier))/2);
scale(scaleMultiplier);
shape(clickState.pshape, transX, transY);
popMatrix();
What I'm trying to do is scale a state on a US Map. clickState is the state that the user clicked on. clickState.bounds is a Rectangle that surrounds the shape of the state. getX() and getY() return the x and y coords of the upper left hand corner of said box. I want to scale the state so that it's no bigger than 500x500 pixels. After that, I want to translate it so that it's in the middle of the sketch, which is 800x550.
Here's where I run into trouble: When I do
shape(clickState.pshape, resetX, resetY);
It draws the state in the upper left hand corner of the sketch. That's exactly what it should do. Then from there I want to add the number of pixels that it would take to center the shape, which is what transX and transY are for. However, when I use transX and transY, the shape gets drawn almost completely off the canvas, depending on where the state is located (even if its only being moved 50 pixels to the right from 0,0). It doesn't make any sense to me whatsoever. I've tried all sorts of combinations of translate(), and even skipping scale() altogether and using a width and height in shape(). It's like Processing is trying to frustrate me on purpose. Any help is appreciated.
I'm not familiar with Processing, but matrix operations are not commutative—the order matters. Typically, operations are applied in an apparent last-specified-first-applied order, as shown in this example.
Vince, I think I was trying to do something very similar last week. Sounds like you figured it out, but check out the answers on this board:
http://forum.processing.org/topic/how-do-i-move-svg-child-shapes#25080000000689051