GML Q: Position in an Arc

zendraw

Member
how do i calculate a position in an arc
for instance point A is at 0 and point B is at 256. now that straight line bends down 32 pixels and creates an arc, and i want to move an instance accordingly to that arc. so far i managed to move it on an angle towards the bottom of the arc, but its again a straight line from point A to the center going down and from the center to point B going up

this is the arc
s_rope.png
and this is the calculations i use
Code:
    var rpw=floor(rp.sprite_width*.5);
    var rph=rp.sprite_height;
    var xw=abs(x-(rp.bbox_left+rpw));
    y=rp.bbox_top+(((rpw-xw)/rpw)*rph);
 

jaydee

Member
The easiest way I can think of is to just treat it as a circle segment. So you'd have a couple of formulas you could use, but this is how I'd do it:

Code:
// Note slow code below, calculate this once and then reuse the value.
radius = sqrt( pow(width, 2) + pow(height, 2) ) / ( 2 * cos( atan2(height, width) ) );

// To get the y position from an x value:
// x^2 + y^2 = r^2
y = sqrt( pow(radius, 2) - pow(x - width / 2, 2) );
Note I did this without testing it, the y axis may need inverting. But the maths should be right. You'll need to adapt it to your needs, right now it will always originate at (0, 0).
 

dannyjenn

Member
This involves a lot of trigonometry...

Let's divide line AB in half and call the midpoint M, such that M is at 128.
And let's call the 'bottom of the arc' point C, such that point C is exactly 32 points beneath M.
Let's also imagine that the arc is part of a larger circle centered at point O, which is located some unknown distance OM from M, i.e. some unknown distance OM+32 from C.
So lines OA, OB, and OC are all the same distance, which we will call R (because it is the circle's radius).
We need to figure out two things: R (the radius), and theta (the angle at O from A to B).
To find R, we first imagine drawing a right triangle between A, M, and C, such that the angle at M from A to C is 90 degrees.
We already know that line AM is 128, and that line MC is 32. And with respect to the angle at C from A to M, line AM is the opposite while line MC is the adjacent. So we can calculate that angle by using sohcahtoa... tangent is opposite divided by adjacent, so the tangent of that angle is AM over MC, and so the angle is the arctangent of AM over MC.
Now imagine we draw an isosceles non-right triangle between points A, C, and O. And we just calculated that the angle of C from A to O is the arctangent of AM over MC... so we also know that the angle of A from C to O is the same thing (since the triangle is isosceles), and the angle at O from A to C is 180 minus twice the arctangent of (AM over MC). This is the value we want, because if we just double that, we have our theta.
Now we just need to know R... which we can get with some more sohcahtoa. We draw a right triangle between A, M, and O, such that the angle at M from A to O is 90 degrees. And we already have the angle at O from A to M (it's 180 minus twice the arctangent of (AM over MC)). So with regard to the angle at O, AO is the hypotenuse, OM is the adjacent, and AM is the opposite. Sine is the opposite over the hypotenuse, and the opposite is 128, so the sine of (180 minus twice the arctangent of (AM over MC)) is equal to 128 over the hypotenuse, i.e. hypotenuse is equal to 128 over the sine of (180 minus twice the arctangent of (AM over MC)). Thus,
theta is roughly 56.144974 degrees, and
R is perhaps 272.

With these values, you can do the movement. Let's see...
Code:
// create event:
theta = 0; // some value between 0 (point A) and 56.144974 (point B)
spd = 1; // how quickly you want the instance to move

#macro CURVE_WIDTH 256
#macro CURVE_HEIGHT 32
#macro CIRCLE_THETA 2*(180-(2*(darctan((CURVE_WIDTH/2)/CURVE_HEIGHT)))) // should be roughly 56.144974 here
#macro CIRCLE_R (CURVE_WIDTH/2)/(dsin(CIRCLE_THETA/2)) // should be around 272
#macro OFFSET 270-(CIRCLE_THETA/2) // controls the curve's position/rotation
Code:
// step event:
theta = max(0,min((theta+(spd*((keyboard_check(vk_left)-keyboard_check(vk_right))),CIRCLE_THETA)); // user input
var o_x = (point_a.x+(CURVE_WIDTH/2)); // the circle's origin's x coordinate
var o_y = ((point_a.y+CURVE_AMOUNT)-CIRCLE_R); // the circle's origin's y coordinate
x = o_x + lengthdir_x(CIRCLE_R,(theta+OFFSET));
y = o_y + lengthdir_y(CIRCLE_R,(theta+OFFSET));
Note: I haven't tested any of this.

edit - I've edited the code to make it more adjustable / less hard-coded
 
Last edited:

zendraw

Member
@dannyjenn you gave me an idea, like to find the center of the circle and then just move with lengthdir, basically perhaps waht you suggested, but you calculate so much stuff i cant keep up with it. perhaps if you can posta also some drawing?
also i am thing about points that are on diffrent height, so the arc becomes more like a part of a spiral then a circle.

thanks.
 

jaydee

Member
@dannyjenn you gave me an idea, like to find the center of the circle and then just move with lengthdir, basically perhaps waht you suggested, but you calculate so much stuff i cant keep up with it. perhaps if you can posta also some drawing?
also i am thing about points that are on diffrent height, so the arc becomes more like a part of a spiral then a circle.

thanks.
What you've described is exactly how both myself and dannyjenn have approached this.

To implement this in an example:

Code:
// Create event:
width = 256;
height = 32;
x = 32;
y = 32;
pos_x = 0; // Position along arc

radius = sqrt( pow(width, 2) + pow(height, 2) ) / ( 2 * cos( atan2(height, width) ) );

// Step event
if(keyboard_check(vk_left)) pos_x -= 1;
if(keyboard_check(vk_right)) pos_x += 1;

// Draw event:
// (Only drawing a position along the arc, not the arc itself)
var dx = x + pos_x;
var dy = y + sqrt( pow(radius, 2) - pow(pos_x - width / 2, 2) );
draw_circle(dx, dr, 2, false);
 

zendraw

Member
ill try to adopt these later and write again. btw do you guys think using this (last jaydee comment) will make possible creating an arc as i described in my last comment? not a perfect circle, but more like a spiral. i was also thinking a circle squished horizontally but in actuality, if the right point goes up, the center point goes more to the left.
 

jaydee

Member
So I actually tested my code, and found my errors. This code works:

Code:
// Create
width = 256;
height = 32;
x = 32;
y = 32;
pos_x = 0; // Position along arc
radius = sqrt( sqr(width / 2) + sqr(height) ) / (2 * cos( arctan2( width, 2 * height) ) ); // In my original math, I did not divide width by 2

// Draw
// Drawing bounds of arc
draw_set_color(c_green);
draw_circle(x, y, 2, false);
draw_circle(x + width, y, 2, false);
draw_circle(x + width / 2, y + height, 2, false);

// Draw position on arc
draw_set_color(c_red);
var dx = x + pos_x;
var dy = y + sqrt( power(radius, 2) - power(pos_x - width / 2, 2) ) - radius + height; // I wasn't offsetting the centre of the circle correctly originally.
draw_circle(dx, dy, 2, false);
You could easily move the origin of the circle, but I'm not entirely clear on how you want your spiral implemented.
 

zendraw

Member
so, i dont really understand the functions you use, im bad at math, but this is what i found logical to use and it gives me an error that i cant apply sqrt to a negative number
Code:
var width=sprite_width;
var height=sprite_height-16;
var radius=sqrt(sqr(width/2)+sqr(height))/(2*cos(arctan2(width,2*height)));
    y=sqrt(power(radius, 2)-power(x-width/2, 2))-radius+height;
 
Top