You have very stupid CPU, and it can't do too much. Or, you aiming for speed. You want to find out velocity vector of length V that aims from starting point, into destination point. All calculations in integers, thus only approximation is possible. We working in 2D. Length of velocity we want is fixed. It's constant in program.
Input: two points source = (x1, x2) destination = (y1, y2)
Output: vector (vx, vy) which length is close to V, and aiming from source to destination.
Straight ahead rejected: all ideas with roots or inverse roots, because it anyway require multiplication. Also rejected following:
- Split whole range of possible dx, dy into WxH blocks, where W and H are powers of two.
- Precalculate all vectors aiming to corresponding blocks using modern machine.
- When velocity is need, just select appropriate block using division by W and H, and read velocity from table. In my task: it would take space. Not much but I want to avoid that. Also, precision is not good.
Maybe you've seen cannons in old games shoots in eight directions: four alighed to axis, and four diagonals. Lets think a bit about it. How is it done?
It should be done making virtual eight regions around cannon, and selecting velocity of bullet according to region. First question: what does the region looks like?
Intuitively it's easy to figure out that region of diagonal is all points that is closer to diagonal than axis. Thus borders of the regions is defined with equation where distance between diagonal and axis is same:
(x - y)/sqrt(2) = y
There is trouble for calculation it straight forward. Division by square root is pain.
If you rearrange you'll find out that slope angle is pi/8, because this line by difinition is bisection of pi/4 angle. Thus, slope itself is tg(pi/8) = 0.41421... Notice it's kinda close to 0.5 = 1/2. So, if aiming vector has angle with tg(angle) >= 1/2 then it's closer to diagonaly for sure. And if tg(angle) < 1/2 then it's either closer to axis, or in range [tg(pi/8), 1/2] we have error. But it's fine.
How to implement this approximated version? Just use definition of tg(). It is length of opposite side devided by length of adjacent side. y/x in our case. Thus, tg >= 0.5 if y/x = tg >= 0.5. Or y >= 0.5 x. So, for one quarter of plane we have:
if (y < x/2)
closer_to_x_axis;
else if (x < y/2)
closer_to_y_axis;
else
closer_to_diagonal;
And all other quarters of plane is processed in same way but with preparation: invert x, and y if they are negative, and remember what was inverted. Then postprocess: invert velocity in same way after detection what line is closer. In result, we have eight possible directions of velocity.
Extend this idea by adding new directions.
- First, notice that lines with slope 1/4 and 3/4 are splitting regions rougly equal.
- Then, notice that 1/2 is 2/4.
- Step a bit further: notice that slopes i/8 where i in range [0, 8] produce regions which roughly equal too. In short: find i/8 slope closest to direction vector, and use i as index from premade table. This means:
i/8 ~ tg(angle) = y/x
x*i/8 ~ y
- Split plane using i/8 slopes, and prebuild direction vectors for that regions using modern machine / programming language.
- Remember signs which should be produced by subtraction in analytical math x2-x1, and y2-y1 before actual subtraction using comparison of corresponded values. This is need only in case if result can be stored only by unsigned integer (as in my case). Otherwise remember signs of result of subtraction at step 2.
- Subtract: dx = x2 - x1; dy = y2 - y1
- Invert those which analytical result should be negative. Now dx, dy is positive. Or at least treat it so.
- If dx <= dy then swap. It's to make (dx, dy) placed in triangle of quarter of plane. Remember did you swap it.
- Set dx to be dx/8 from now on. From now on we calculate angle, which is in range [0, 7] inclusive.
- If dx is equal to zero, then source point and destination point is too close, and all we can do is set angle to zero.
- Else find first slope i/8 which is higher than slope of (dx*8, dy). Notice that we previously devided dx by 8. This means that dy/(dx*8) < i/8 or dy < dx*8*i/8 = dx*i. Then angle = i - 1
- Now ensure that angle is in range [0, 7] inclusive, if outside bring it back in range.
- Retrive vx, vy from table generated at step 0 using angle.
- If we did swap dx, dy at step 4, then swap vx, vy.
- Invert vx, vy according to saved signs from step 1.
- Return vx, vy as result of algorithm.
- 8*8 = 64 directions. Optionaly you can change 8 into 16 or higher power of two.
- Only add, sub and division by powers of two.
- Only integers.
- Almost no memory overhead.
- Pan & Drag + Zoom. Everyone likes zoom.
- Velocity vectors. Blue - using floating point arithmetic. Orange - using proposed algorithm.
- Velocity selection.
- Snap to grid of integer coordinates.