Smooth Zoom with mouse in Mandelbrot set (C) - c

I've been working on a C Mandelbrot set program for the past few days and I managed to make it work fine, however, my end goal is to be able to smoothly zoom in the set with my mouse and that's something I haven't yet been able to do yet so I might need a bit of help !
Here's part of my code (well, the full mandelbrot function) :
//removed to free space
Here's a picture of the output :
(Sorry, it's not very pretty, colors were not my priority but I'll be sure to work on them as soon as I figure out the zoom !)
Mandelbrot
What I want to be able to do :
left click -> center of the image becomes mouse_x an mouse_y. Then, it starts zooming in as long as left click is held
right click -> [...] it starts zooming out as long as right click is held
move mouse -> if currently zooming in/out, the center of the image moves to the mouse's coordinates with it. Else nothing happens.
(already have a function that gets mouse's position and button being pressed)
Thanks a lot for your help !

The visible area is a rectangle defined by (Re.min, Im.min) and (Re.max, Im.max). When you click on a particular point, you can map the mouse position to a point (mouseRe, mouseIm) by using the same mapping as you use when rendering:
double mouseRe = (double)mouse_x / (WIN_L / (e->Re.max - e->Re.min)) + e->Re.min;
double mouseIm = (double)mouse_y / (WIN_H / (e->Im.max - e->Im.min)) + e->Im.min;
To zoom in, imagine drawing a line from the (mouseRe, mouseIm) zooming centerpoint to each of the corners of the visible area, forming a lopsided X. Based on the zoom amount, find 4 new points a certain fraction of the distance along these lines, these points will give you your new rectangle. For example, if you are zooming in by a factor of 3, find a point 1/3rd of the way from the centerpoint to the corners. This will produce a new rectangle with sides 1/3rd the size of the original and an area 1/9th the size.
To do this you can define a simple interpolation function:
double interpolate(double start, double end, double interpolation)
{
return start + ((end - start) * interpolation);
}
Then use the function to find your new points:
void applyZoom(t_fractal* e, double mouseRe, double mouseIm, double zoomFactor)
{
double interpolation = 1.0 / zoomFactor;
e->Re.min = interpolate(mouseRe, e->Re.min, interpolation);
e->Im.min = interpolate(mouseIm, e->Im.min, interpolation);
e->Re.max = interpolate(mouseRe, e->Re.max, interpolation);
e->Im.max = interpolate(mouseIm, e->Im.max, interpolation);
}
Based on my description, you might think you need to find 8 values (4 points for the 4 legs of the X with 2 dimension each) but in practise there are only 4 unique values because each of the sides is axis aligned.
For a smooth zoom, call it with a zoom factor of a little over 1.0 e.g. 1.01. To zoom out, pass the inverse e.g. 1.0 / 1.01.
Alternatively, if you want the center of the view to jump to a certain position when you click the mouse, calculate mouseRe and mouseIm as above and then offset the corners of the view rectangle by the difference between these values and the center of the view rectangle. You could store these values at the time the mouse button was first pressed down, and use them to zoom in as long as it is held.

Related

Stuck in figuring out how to adjust scrollbar position when zooming

I am implementing a zoom feature of a 20x20 grid that also has a 640x480 grid on top, where each made up square is called a resolution. The entire space is for instance 3x3 resolutions (640 * 3) in width and (480 * 3) in height and there is a scrollbar that can scroll over this area.
The first zoom level is 100% and so:
I set the grid size to *2, the resolutions also twice as big and the entire space * 2.
However, I'll also have to re-adjust the scrollbar position so that it remains on the same visual location after the zoom and I am really not that good in math. How do I do that?
A diagram:
*where the visible area should be after zoom
Edit: I create this simulator to quickly and easily do real-time simulations:
https://youtu.be/RfiaUXWNjQE
this one works with the following:
hNew = hCurrent * 2 + resolution.width / 2;
it obviously works with the first zoom level.
later update:
hNew = hCurrent * (1 + zoomLevels) + resolution.width / 2;
appears to be working for every zoom level.
The aforementioned approach works, but it can only map from a no-zoomed world to any zoom level rather than from every zoom level to every zoom level.
A better more conventional way is to convert current (before zoom) values of the horizontal/vertical scrollbar to %
then after zoom calculations and resizing get the value of that % of the new world size.
so for horizontal adjustment it would look like this:
h% = hValue * 100.0 / hValueMax;
// ...zoom...
hValueNew = h% * hValueNewMax / 100.0;

Pseudo 3D walls (top-down raycasting, sort of)

See, I'm not posting code because I need logic, math, algorithms. Well:
I'm trying to achieve a 3d-looking visual for a top-down tile map using layers and parallax scrolling. The thing is: At the moment I simply set different "speeds" for each layer. But that would only work with some very specific camera positions, also, it makes so that the blocks have virtually an infinite height (as they will "increase in height" until they are out of the camera's FOV).
Is there a better (should be) to achieve the effect? Oh, and I'm using C with Allegro 5.
I thought about limiting each layer's offset, but I have no idea how.
My current method:
That's my current code for the layer "speed" (it repeats for up, down, left and right, changing coordinates):
if (key[ALLEGRO_KEY_UP])
camera_y[0] -= 1;
camera_y[1] -= 2;
camera_y[2] -= 3;
Then I run a loop to draw the map with the tiles relative to the current layer's offset.
By the way, that's the desired effect (example with 3 layers):
For parallax scrolling, layers that scroll faster must be correspondingly larger:
You can use unscaled tiles stacked on top of each other, offset by a fixed fraction of the distance from the center of the tile to the center of the viewport,
but the tops will not be continuous (unless the bottoms overlap). If all layer tiles are hand-drawn or rendered images, this is not an issue.
If the walls are box-shaped, and you have images of the top and each of the four sides, you can draw them in almost 3D,
where at most two sides of each box wall is drawn, skewed.
In all cases:
If the center of the viewport is at world coordinates (xc, yc), point (x, y, z) maps to coordinates (x', y') relative to the center of the viewport:
x' = (x - xc) × (z + z0) / z0
y' = (y - yc) × (z + z0) / z0
where z0 is a constant that determines the "size" of the parallax or depth effect.
I think you're on the right lines, but the "infinite height" issue can be solved by simply giving the camera an "altitude" property, and adjust the "speed" of each layer by calculating ...
layer.speed = (layer.altitude / camera.altitude) * ZOOM_FACTOR; //gives a float value.
Can't really suggest anything more until you show us some of your math code.

Any way to tell what part of a WPF Ellipse shape a user has clicked on?

This may seem like a silly question, but is there some way to determine what part (say, a quadrant) of an Ellipse that a user clicked on?
Or is the only option to just figure it out based on mouse point coordinates relative to the shape itself?
I'm working on a control that allows the user to click on any part of the ellipse and drag it to resize if that helps.
Figuring out the mouse coordinates in relationship to the shape should be fairly easy. Check the X coordinate and see if it is greater than or equal to Shape.Width / 2. Then do the same with Y and Shape.Height. It should give you the correct quadrant the mouse was clicked in.
You can see in Mathworld that the general equation for an ellipse is:
.
The points within the ellipse are the ones for which the equation yields <= 1.
If the ellipse is circumscribed to a rectangle R, then:
(x0,y0) = center(R)
a = width(R)/2
b = height(R)/2
(x,y) is the clicked point.

simple plot algorithm with autoscale

I need to implement a simple plotting component in C#(WPF to be more precise). What i have is a collection of data samples containing time (X axis) and a value (both double types).
I have a drawing canvas of a fixed size (Width x Height) and a DrawLine method/function that can draw on it. The problem I am facing now is how do I draw the plot so that it is autoscaled? In other words how do I map the samples I have to actual pixels on my Width x Height canvas?
One hacky method that may work is to use a Viewbox control. This control will scale the rendering of its content to fit the size available. However, this might lead to your lines and labels looking too thick or thin.
The more sensible method that you're probably after, though, is how to work out at what scale to draw your graph at in the first place. To do that, work out the range of values on a given axis (for example, your Y-axis value might range from 0 to 100). Work out the available drawing space on that axis (for example, your canvas might have 400 pixels of height). Your Y-axis "scale factor" when drawing the graph would be <available space> / <data range> - or, in this case, 4.
Your canvas' coordinates start from zero in the top-left so, to calculate the Y-position for a given data point, you would calculate like this:
double availableSpace = 400.0; // the size of your canvas
double dataRange = 100.0; // the range of your values
double scaleFactor = availableSpace / dataRange;
double currentValue = 42.0; // the value we're trying to plot
double plottableY = availableSpace - (currentValue * scaleFactor); // the position on screen to draw at
The value of plottableY is the y-coordinate that you would use to draw this point on the canvas.
(Obviously this code would need to be spread out across your drawing method so you're not recalculating all of the values for each point, but it demonstrates the math).

Pathfinding Algorithm for Robot

I have a robot that uses an optical mouse as a position track. Basically, as the robot moves it is able to track change in X and Y directions using the mouse. The mouse also tracks which direction you are moving - ie negative X or positive X. These values are summed into separate X and Y registers.
Now, the robot rotates in place and moves forward only. So the movement of the robot is ideally in straight lines (although the mouse tracking can pickup deviations if you veer off) at particular angles. A particular set of movements of the robot would be like:
A: Rotate 45 degrees, move 3 inches
B: Rotate 90 degrees, move 10 inches
C: Rotate -110 degrees, move 5 inches
D: Rotate 10 degrees, move 1 inch
But each time the mouse X and mouse Y registers give the real distances you moved in each direction.
Now, if I want to repeat the movement set going from A to D only, how can I do this using the information I have already gathered. I know I can basically sum all the angles and distances I feed into it already, but this would prove to be inaccurate if there were large errors in each movement orders. How can I use the raw information from my mouse? A friend provided an idea that I could continuously sine and cosine the mouse values and calculate the final vector but I'm not really sure how this would work.
The problem is that the mouse only gives relative readings so rotating or moving backwards, you are potentially erasing information. So yeah, what I am wondering is how you can implement the algorithm so it can continually track changes to give you a shortest path if you moved in zigzags to get there originally.
I think the basic algorithm you need to do is this:
currentX = currentY = 0;
heading = 0; // radians
while (true)
{
deltas = SampleMouseDeltas();
heading += deltas.Heading;
currentX += Math.Cos(heading) * deltas.Distance;
currentY += Math.Sin(heading) * deltas.Distance;
}
You are right in your idea that this won't be precise. It is called "dead reckoning" for a reason.
Where you can get your deltas.Heading based on the "X" coordinate (the formula will be (deltax in inches) / (mouse sensor distance in inches from center of rotation). Also, the deltas.Distance would come from the "Y" sensor, after you convert it from pixels to inches.
Then to perform the steps, you could do something like:
robot.RotateLeft();
heading = 0;
while (heading < 45 degrees)
heading += SampleMouseDeltas.Heading;
robot.StopRotateLeft();
... etc ...
Not an answer to your question, but perhaps a cautionary tale...
I did exactly this kind of robot as a school project a year back. It was an utter failure, though I learnt quite a bit while doing it.
As for using the mouse for tracking how far you have driven: It did not work well for us at all, or any of the other groups. Probably because the camera in the mouse was out of focus due to the fact that we needed to have the mouse a few mm above the floor. The following year no group doing the same project used this methid. They instead put markings on the weels and used a simple ir-sensor to calculate how many revolutions the wheels made.
I know I'm somewhat necroing this thread, but if you wanted more accurate angle tracking, two optical mice would be ideal. Basically if you cancelled out the motion in the same direction, you would be left with the motion that the mice made relative to eachother. From there, it would just be some simple math to accurately determine how far the 'bot has turned.

Resources