Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Drag line with mouse on a fixed angle

This thread is locked; no one can reply to it. rss feed Print
Drag line with mouse on a fixed angle
chelneru
Member #15,378
November 2013
avatar

Hi I know this is not Allegro related but I hope that maybe someone will explain me some things about analitical geometry.

I have a line that has a point fixed on certain coordinates and the other one follows the mouse. The next thing that I want to do is when I press the left mouse button , the line is fixed on a certain angle pointing to my mouse and I can move it on the axis formed by the mouse and the fixed point. The mouse event is not a problem , the only thins is I don't know how to write it. Can you give me some hints or ideas how to do this ? :D

LennyLen
Member #5,313
December 2004
avatar

I just want to paraphrase what you are saying so that we're on the same page. So basically what you want is when you have the mouse button pressed, and you move the muse towards the fixed point, the line will get shorter and if you move the mouse away from the fixed point the line will get longer. But so long as the mouse button is held down, the angle the line is on will remain the same.

Is that correct?

If so. The first thing I would do is calculate the x/y ratio. eg:

float ratio = (mouse_x - initial_x) / (mouse_y - initial_y);

and store either the x or y component at this time:

float x_length = mouse_x - initial_x;

Then I would calculate the length (actually, I'd calculate its square to save calculation on times) of the line as it stands right now:

float initial_line_length = (mouse_x - initial_x) * (mouse_x - initial_x) + (mouse_y - initial_y) * (mouse_y - initial_y);

Then do your loop that checks if the mouse is held down, and while it is, keep rechecking the new squared distance from the current position to the initial position:

<code>float current_line_length = (mouse_x - initial_x) * (mouse_x - initial_x) + (mouse_y - initial_y) * (mouse_y - initial_y);

The new x position for the end of the line is now:

float new_x = initial_x + x_length * initial_line_length / current_line_length;

The new y position is now:

float new_y = initial_y + x_length * initial_line_length / current_line_length * ratio;

If that causes the line to shrink and grow too quickly, just multiply the current_line_length value to some number between 0 and 1 until the speed is good for you.

chelneru
Member #15,378
November 2013
avatar

The initial_x and initial_y are the fixed point's coordinates ?

What I want is when I press the mouse button the line won't get shorter it will be moved .Anyway I think your code gave me some help because I think the difference between getting shorter and be moved will be that for the move I also have to modify the fixed point coordinates.
I will try your advice . Thanks in advance. :D

LennyLen
Member #5,313
December 2004
avatar

chelneru said:

The initial_x and initial_y are the fixed point's coordinates ?

Yes. I probably should have named them fixed_x and fixed_y.

Quote:

Anyway I think your code gave me some help because I think the difference between getting shorter and be moved will be that for the move I also have to modify the fixed point coordinates.

That's even easier. First calculate the difference in position from the fixed point to the end of the line when you detect the mouse button is pressed, and store the mouse location:

float x_diff = mouse_x - fixed_x;
float y_diff = mouse_y - fixed_y;

float mouse_clicked_x = mouse_x;
float mouse_clicked_y = mouse_y;

Now while the mouse is held down, just loop through the following, which determines how far the mouse has moved since you pressed the button and moves the line by that amount:

float x1 = mouse_x - mouse_clicked_x + fixed_x;
float y1 = mouse_y - mouse_clicked_y + fixed_y;
float x2 = x1 + x_diff;
float y2 = y1 + y_diff;

The new line is drawn form x1, y1 to x2, y2.

chelneru
Member #15,378
November 2013
avatar

So , here's what I did ::):

#SelectExpand
1//Some code before.... 2 3else if (events.type== ALLEGRO_EVENT_TIMER) 4{ 5 6 al_get_mouse_state(&MouseState); 7 if (MouseState.buttons & 1) 8 { 9 10float mouse_clicked_x = mx; //When I press the mouse button 11 //I store the coordinates 12float mouse_clicked_y = my; 13 14 15 16} 17 else { 18 19 20//Now in the Timer event 21 22float x1 = mx - mouse_clicked_x; //If I add "+fixed_x" the line 23 //will be at a 'fixed_x' distance from the mouse 24float y1 = my - mouse_clicked_y; 25 26// I put at the third and forth parameter +200 because my line will always have 27 28//the same distance so I will not need the x_diff and y_diff 29al_draw_line(x1,y1,x1+200,y1+200,al_map_rgb(200,100,155),2); 30 }

But this will make a line (that has an angle of 90+ degrees I don't know why..???) that follows the mouse. I am sorry if I misunderstood what you were trying to explain but I want the line to move on a certain angle even if the mouse is not on the same angle.

I want to make a pool game . This will help me with the cue. When I press the mouse button i want to drag the cue to set the force that will apply on the ball. So first I set the angle when I just move my mouse over the screen and then when I press the mouse button I only move the cue (line) on that angle that the line had immediately before i press the button.

I was thinking to save the mouse coordinates and when I click , I see if the x is decreasing and if yes I will decrease the x coordinate of the line and with the line equation I find the y according to that x coordinate of the line . The problem is i can't seem to make it to work... :-/

I'm sorry if I'm confusing and hard to understand ,I hope you have the patience to explain me my problem :D

ph03nix
Member #15,028
April 2013
avatar

A pool cue is pretty simple.

Here's the pseudocode to get the cue's position:

float newAngle = atan2(mouseY - ballY, mouseX - ballX) - cueAngle;
float D = sqrt((mouseX-ballX)*(mouseX-ballX) + (mouseY-ballY)*(mouseY-ballY)) * cos(newAngle);
if(D < 0) D = 0;

cueX = ballX + cos(cueAngle);
cueY = ballY + sin(cueAngle);

mouseX and mouseY are the mouse's current position, do not stop updating mouseX and mouseY while the mouse is pressed.

ballX and ballY are the target ball's position

cueAngle is the angle of the cue. Stop updating this when you click. Store it like this:

cueAngle = atan2(mouseY - ballY, mouseX - ballX);

EDIT: I'm guessing you want the cue to start from the ball's position. To do this just store this in a float called ballDist once when you click:

ballDist = sqrt((mouseX-ballX)*(mouseX-ballX) + (mouseY-ballY)*(mouseY-ballY));

Then subtract ballDist from D in the code above.

chelneru
Member #15,378
November 2013
avatar

I have this :

#SelectExpand
1while(!done) 2 { 3 ALLEGRO_EVENT events; 4 al_wait_for_event(event_queue,&events); 5 6 7if(events.type==ALLEGRO_EVENT_MOUSE_AXES) 8{ 9 10 11 mx=events.mouse.x; 12 my=events.mouse.y; 13 14 15} 16 17 else if (events.type== ALLEGRO_EVENT_TIMER) 18{ 19 20 al_get_mouse_state(&MouseState); 21 if (MouseState.buttons & 1) 22 { 23 24 mx=events.mouse.x; 25 my=events.mouse.y; 26 27 ballDist = sqrt((mx-ballX)*(mx-ballX) + (my-ballY)*(my-ballY)); 28 float newAngle = atan2(my - ballY, mx - ballX) - cueAngle; 29 D = sqrt((mx-ballX)*(mx-ballX) + (my-ballY)*(my-ballY)) * cos(newAngle)-ballDist; 30 if(D < 0) D = 0; 31 32cueX = ballX + cos(cueAngle); 33cueY = ballY + sin(cueAngle); 34 35 36} 37 38 39 cueAngle = atan2(my - ballY, mx - ballX); 40 41 draw = true; 42 43} 44if (draw) 45{ 46 draw = false; 47 48 al_draw_line(ballX,ballY,cueX,cueY,al_map_rgb(200,100,155),2); 49 50 al_flip_display(); 51 al_clear_to_color(al_map_rgb(0,0,0)); 52 }

When I run it i Get a line from the top left corner ??? to the ball coordinates.When I press the mouse button I only see a point :-/ that it has a static move or something. Can you tell me how to apply your solution corectly ? ::) Also Where I use the D variable ? :-/

ph03nix
Member #15,028
April 2013
avatar

Whoops, I wrote the code in wrong (it was pseudocode after all ;D

cueX = ballX + cos(cueAngle);
cueY = ballY + sin(cueAngle);

should be:

cueX = ballX + cos(cueAngle) * D;
cueY = ballY + sin(cueAngle) * D;

EDIT: Also, this line should only be done once when you click, you seem to do it continuously when the mouse is down:

ballDist = sqrt((mx-ballX)*(mx-ballX) + (my-ballY)*(my-ballY));

chelneru
Member #15,378
November 2013
avatar

So , I made the modification but now when I press the mouse button it only appears on ...(let's say the ball coordinates are the centre of the screen) 0 angle I mean it's a line from the ball's coordinates and horizontal to right and is blinking.Here is an image of what I got : click to see image

Also ballDist can be optional ? because I have to write an another event so it's calculated only once when the button is pressed.

EDIT: The cue's coordinates are wierd...here's another image with the coordinates that i 'am getting (the point is not very accurate but still )

cue's coordinates

ph03nix
Member #15,028
April 2013
avatar

Without "ballDist" the cue will jump to the mouse's distance relative to the ball instead of relative to the mouse's original position. You don't need another event, you can do something like this:

#SelectExpand
1//defined outside loop 2bool wasMousePressed = false; 3 4if (events.type== ALLEGRO_EVENT_TIMER) { 5 al_get_mouse_state(&MouseState); 6 7 //if mouse is currently pressed but was not pressed on the previous iteration 8 if((MouseState.buttons & 1) && !wasMousePressed){ 9 ballDist = sqrt((mx-ballX)*(mx-ballX) + (my-ballY)*(my-ballY)); 10 } 11 12 ... 13 14 //after all the cue logic 15 wasMousePressed = MouseState.buttons & 1; 16}

Try and see if that fixes it.

chelneru
Member #15,378
November 2013
avatar

Here is my code :

#SelectExpand
1 while(!done) 2 { 3 ALLEGRO_EVENT events; 4 al_wait_for_event(event_queue,&events); 5 6if( events.type == ALLEGRO_EVENT_KEY_UP) 7{ 8 switch(events.keyboard.keycode) 9 { 10 case ALLEGRO_KEY_ESCAPE: 11 done = true; 12 13 } 14} 15else if(events.type==ALLEGRO_EVENT_MOUSE_AXES) 16{ 17 18 19 mx=events.mouse.x; 20 my=events.mouse.y; 21 22 23} 24 25 else if (events.type== ALLEGRO_EVENT_TIMER) 26{ 27 28 al_get_mouse_state(&MouseState); 29 if((MouseState.buttons & 1) && !wasMousePressed) 30 { 31 32 ballDist = sqrt((mx-ballX)*(mx-ballX) + (my-ballY)*(my-ballY)); 33 } 34 35 mx=events.mouse.x; 36 my=events.mouse.y; 37 float newAngle = atan2(my - ballY, mx - ballX) - cueAngle; 38 D = sqrt((mx-ballX)*(mx-ballX) + (my-ballY)*(my-ballY)) * cos(newAngle)-ballDist; 39 if(D < 0) D = 0; 40 41cueX = ballX + cos(cueAngle)*D; 42cueY = ballY + sin(cueAngle)*D; 43 cueAngle = atan2(my - ballY, mx - ballX); 44 45 46 wasMousePressed = MouseState.buttons & 1; 47} 48 draw = true; 49 50if (draw) 51{ 52 draw = false; 53 54 al_draw_line(ballX,ballY,cueX,cueY,al_map_rgb(200,100,155),2); 55 56 al_flip_display(); 57 al_clear_to_color(al_map_rgb(0,0,0)); 58 } 59}

What/Where is the problem ??

Also it's ok with that wasMousePressed to be the only expression in that event ? I mean I thought "all the cue logic " should be in the braces of the if function where is the ballDist Atribution.

ph03nix
Member #15,028
April 2013
avatar

Change this block of code:

   mx=events.mouse.x;  
   my=events.mouse.y;
   float newAngle = atan2(my - ballY, mx - ballX) - cueAngle;
   D = sqrt((mx-ballX)*(mx-ballX) + (my-ballY)*(my-ballY)) * cos(newAngle)-ballDist;
   if(D < 0) D = 0;
 
 cueX = ballX + cos(cueAngle)*D;
 cueY = ballY + sin(cueAngle)*D;
  cueAngle = atan2(my - ballY, mx - ballX);

to this:

if(MouseState.buttons & 1){
   mx=events.mouse.x;  
   my=events.mouse.y;
   float newAngle = atan2(my - ballY, mx - ballX) - cueAngle;
   D = sqrt((mx-ballX)*(mx-ballX) + (my-ballY)*(my-ballY)) * cos(newAngle)-ballDist;
   if(D < 0) D = 0;
 
 cueX = ballX + cos(cueAngle)*D;
 cueY = ballY + sin(cueAngle)*D;
}else{
  cueAngle = atan2(my - ballY, mx - ballX);
}

hopefully this will make it perfect ;D

chelneru
Member #15,378
November 2013
avatar

When I click the line appears on good coordinates but the cue's coordinates are faar long, it keeps blinking and if I want to to move it , it's not any difference it's just blinking .I guess we advanced a little :D

I think the problem is with the distance . In the console the coordinates for the cue are bad when i keep the mouse pressed and move the mouse , there are some negative/positive numbers with some 'e' .If you can help me with that too , i would be very grateful :D

EDIT : The D variable when I keep the mouse button pressed is a changing values , even if the mouse is not moving (sometimes is 0 and some infinite values)

ph03nix
Member #15,028
April 2013
avatar

Hard to say now, post the full code. I put the logic into a little flash program to test it and it works perfectly. See attached (If you don't have flash player open the file with your internet browser).

chelneru
Member #15,378
November 2013
avatar

Here is the full code :

#SelectExpand
1#include <stdio.h> 2#include <allegro5/allegro.h> 3#include <allegro5/allegro_native_dialog.h> 4#include <allegro5/allegro_primitives.h> 5#include <allegro5/allegro_image.h> 6#include <iostream> 7#include <stdlib.h> 8#include <math.h> 9using namespace std; 10#define Screenwidth 800 11#define Screenheight 600 12 13#define PI 3.14 14bool wasMousePressed = false; 15 16float ballX=Screenwidth/2 ,ballY = Screenheight/2 ; 17int i,j,k=0; 18float bitmapw,bitmaph; 19float mx,my; 20float x,y,cueAngle,newAngle,cueX,cueY,D,ballDist; 21 22float angle; 23int v[1000][6]; 24 25int main(int argc, char **argv) 26{ 27 ALLEGRO_DISPLAY *display ; 28 const float FPS = 60.0; 29 if(!al_init()) { 30 al_show_native_message_box(NULL,NULL,NULL,"Could not initialize Allegro 5",NULL,NULL); 31 return -1; 32 } 33 34 al_set_new_display_flags(ALLEGRO_WINDOWED ); 35 display = al_create_display(Screenwidth, Screenheight); 36 al_set_window_position(display,200,100); 37 al_set_window_title(display,"Test"); 38 if(!display) { 39 al_show_native_message_box(display,"Sample Title","Display Settings","Display window was not created",NULL,ALLEGRO_MESSAGEBOX_QUESTION); 40 return -1; 41 } 42 43 al_init_primitives_addon(); 44 al_install_keyboard(); 45 al_install_mouse(); 46 al_init_image_addon(); 47 48 49 ALLEGRO_TIMER *timer= al_create_timer(1.0/FPS); 50 ALLEGRO_EVENT_QUEUE *event_queue = al_create_event_queue(); 51 al_register_event_source(event_queue,al_get_timer_event_source(timer)); 52 al_register_event_source(event_queue,al_get_display_event_source(display)); 53 al_register_event_source(event_queue,al_get_mouse_event_source()); 54 al_register_event_source(event_queue,al_get_keyboard_event_source()); 55 56 bool done = false, draw = true; 57 ALLEGRO_MOUSE_STATE MouseState; 58 59al_start_timer(timer); 60 while(!done) 61 { 62 ALLEGRO_EVENT events; 63 al_wait_for_event(event_queue,&events); 64 65if( events.type == ALLEGRO_EVENT_KEY_UP) 66{ 67 switch(events.keyboard.keycode) 68 { 69 case ALLEGRO_KEY_ESCAPE: 70 done = true; 71 72 } 73} 74else if(events.type==ALLEGRO_EVENT_MOUSE_AXES) 75{ 76 77 78 mx=events.mouse.x; 79 my=events.mouse.y; 80 81 82} 83 84 else if (events.type== ALLEGRO_EVENT_TIMER) 85{ 86 87 al_get_mouse_state(&MouseState); 88 if((MouseState.buttons & 1) && !wasMousePressed) 89 { 90 91 ballDist = sqrt((mx-ballX)*(mx-ballX) + (my-ballY)*(my-ballY)); 92 } 93 94 if(MouseState.buttons & 1) 95 96 { 97 mx=events.mouse.x; 98 my=events.mouse.y; 99 float newAngle = atan2(my - ballY, mx - ballX) - cueAngle; 100 D = sqrt((mx-ballX)*(mx-ballX) + (my-ballY)*(my-ballY)) * cos(newAngle)-ballDist; 101 if(D < 0) D = 0; 102 103 104 cueX = ballX + cos(cueAngle)*D; 105 cueY = ballY + sin(cueAngle)*D; 106} 107 else{ 108 cueAngle = atan2(my - ballY, mx - ballX); 109} 110 111 112 wasMousePressed = MouseState.buttons & 1; 113} 114 115 116 draw = true; 117 118 119if (draw) 120{ 121 draw = false; 122 123 al_draw_line(ballX,ballY,cueX,cueY,al_map_rgb(200,100,155),2); 124 125 al_flip_display(); 126 al_clear_to_color(al_map_rgb(0,0,0)); 127 } 128} 129 al_destroy_display(display); 130 al_destroy_timer(timer); 131 al_destroy_bitmap(player); 132 al_destroy_event_queue(event_queue); 133 134 return 0; 135}

It might have some unused variables from my other tries )

EDIT : that flash is EXACTLY what I want to do !

ph03nix
Member #15,028
April 2013
avatar

The logic seems correct. The only problem I see is that you define newAngle at the top of your code and within the loop, maybe that causes it? Also, you should initialize cueAngle to 0 or something just to be safe.

chelneru
Member #15,378
November 2013
avatar

If I remove from the top I get an error that it wasn't declared.

In the console the cueAngle seems to be okay

The newAngle is not really ok . If I click repeatedly in the same place it takes different values like: 0 , infinite values and sometimes normal numbers.

EDIT: Can you post the flash's source ? Maybe I can find the problem by spending some time analyzing that perfect flash application :o

ph03nix
Member #15,028
April 2013
avatar

Did you remove the declaration of cueAngle before the loop? I meant the declaration of newAngle before the loop instead. Try that first. Here's the full flash code if you want, but it's mostly identical to yours.

#SelectExpand
1import flash.events.MouseEvent; 2 3var cueAngle:Number = 0; 4var mousex:Number = 0; 5var mousey:Number = 0; 6var ballDist:Number = 0; 7var ballX:Number = 275; 8var ballY:Number = 200; 9var isDown:Boolean = false; 10var wasDown:Boolean = false; 11 12stage.addEventListener(MouseEvent.MOUSE_DOWN, mouse_isDown); 13stage.addEventListener(MouseEvent.MOUSE_UP, mouse_up); 14addEventListener(Event.ENTER_FRAME, gameLoop); 15 16function mouse_isDown(e:MouseEvent) { 17 isDown = true; 18} 19function mouse_up(e:MouseEvent) { 20 isDown = false; 21} 22function gameLoop(event:Event) { 23 mousex = stage.mouseX; 24 mousey = stage.mouseY; 25 if(!isDown){ 26 cue_mc.x = ballX; 27 cue_mc.y = ballY; 28 //flash uses degrees instead of radians for movie clip rotation 29 cue_mc.rotation = Math.atan2(mousey - ballY, mousex - ballX)*180/Math.PI - 90; 30 } 31 if(isDown && !wasDown){ 32 cueAngle = Math.atan2(mousey - ballY, mousex - ballX); 33 ballDist = Math.sqrt((mousex-ballX)*(mousex-ballX) + (mousey-ballY)*(mousey-ballY)); 34 } 35 if(isDown){ 36 var newAngle:Number = Math.atan2(mousey - ballY, mousex - ballX) - cueAngle; 37 var D:Number = Math.sqrt((mousex-ballX)*(mousex-ballX) + (mousey-ballY)*(mousey-ballY)) * Math.cos(newAngle) - ballDist; 38 D = Math.max(D, 0); 39 40 cue_mc.x = ballX + Math.cos(cueAngle) * D; 41 cue_mc.y = ballY + Math.sin(cueAngle) * D; 42 } 43 wasDown = isDown; 44}

chelneru
Member #15,378
November 2013
avatar

So based on your source I made some changes in my program :

#SelectExpand
1 while(!done) 2 { 3 ALLEGRO_EVENT events; 4 al_wait_for_event(event_queue,&events); 5 6if( events.type == ALLEGRO_EVENT_KEY_UP) 7{ 8 switch(events.keyboard.keycode) 9 { 10 case ALLEGRO_KEY_ESCAPE: 11 done = true; 12 13 } 14} 15else if(events.type==ALLEGRO_EVENT_MOUSE_AXES) 16{ 17 18 19 mx=events.mouse.x; 20 my=events.mouse.y; 21 cueAngle = atan2(my - ballY, mx - ballX); 22} 23 24 else if (events.type== ALLEGRO_EVENT_TIMER) 25{ 26 27 al_get_mouse_state(&MouseState); 28 if((MouseState.buttons & 1) && !wasMousePressed) 29 { 30 31 ballDist = sqrt((mx-ballX)*(mx-ballX) + (my-ballY)*(my-ballY)); 32 cueAngle = atan2(my - ballY, mx - ballX); 33 } 34 35 if(MouseState.buttons & 1) 36 37 { 38 float newAngle = atan2(my - ballY, mx - ballX) - cueAngle; 39 cout<<newAngle<<endl; 40 D = sqrt((mx-ballX)*(mx-ballX) + (my-ballY)*(my-ballY)) * cos(newAngle)-ballDist; 41 if(D < 0) D = 0; 42 43 cueX = ballX + cos(cueAngle)*D; 44 cueY = ballY + sin(cueAngle)*D; 45 } 46 47 wasMousePressed = MouseState.buttons & 1; 48} 49 50 draw = true; 51 52 53if (draw) 54{ 55 draw = false; 56 57 al_draw_line(ballX,ballY,cueX,cueY,al_map_rgb(200,100,155),2); 58 59 60 61 al_flip_display(); 62 al_clear_to_color(al_map_rgb(0,0,0)); 63 } 64}

In the console the newAngle is always 0. And if I click the cue's coordinates are ok but if i go further than the ball's coordinates the distance between my mouse and the cue's coorinates gets higher.The other point it's just fixed in the center and the line gets shorter or longer and isn't on the same angle. Hope I didn't go backwards but do you see any more differences in my c source code and your flash source code ?

EDIT: Now I declare my newAngle in the loop, and the cueAngle at the top and in the loop.

ph03nix
Member #15,028
April 2013
avatar

Maybe take

cueAngle = atan2(my - ballY, mx - ballX);

out of the events.type==ALLEGRO_EVENT_MOUSE_AXES if statement?

chelneru
Member #15,378
November 2013
avatar

Yes, that fixed the angle but the ball's coordinates don't change . I mean the cue doesn't not have a constant lenght and the fixed point just stays there ,fixed, and the line gets shorter or longer.

I think this is only a problem at the screening..how I use the al_draw_line() function ?

ph03nix
Member #15,028
April 2013
avatar

you should draw a line from cueX,cueY to a point further down the cue, for example

al_draw_line(cueX,cueY, cueX + cos(cueAngle) * CUELENGTH, cueY + sin(cueAngle) * CUELENGTH,al_map_rgb(200,100,155),2);

set CUELENGTH to some value, for example 100

chelneru
Member #15,378
November 2013
avatar

YES !:D It's working ! :D Thanks very much I really appreciate your help and sorry if I took much of your time :D

Go to: