How do I modify my geometric equations to compensate for Cartesian/Computer difference?

closed account (Ezyq4iN6)
I know this may seem more like a math question, but I'm asking it here because I know that graphics and math are done a lot with C++, so I figure there must be an easy way to modify equations in C++ so that they respect the computer coordinate system rather than the Cartesian one.

I get that the y is flipped, but that simple difference gives me a lot of trouble when the math gets more complex than simple calculations.

I would love it if there is an overall solution, but here is an example I have been working, and I would at least like to get it to work. I'm calculating the intersection between two lines, which are formed from two points which both have angles. Here is my code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// The m character denotes slope, and b the y-intercept.
// Using the y = mx + b formula for a line.

// Define car center and goal center
double car_x = 100.0;
double car_y = 100.0;
int car_angle = 0;
double goal_x = 600.0;
double goal_y = 250.0;
int goal_angle = 300;

// Angle constant
const double deg2rad = 3.14159265358979323846 / 180.0;

double car_m;
// Make sure slope isn't undefined.
if (car_angle == 90 || car_angle == 270)
  car_m = 9999;
else
  car_m = tan(car_angle * deg2rad);

double goal_m;
// Make sure slope isn't undefined.
if (goal_angle == 90 || goal_angle == 270)
  goal_m = 9999;
else
  goal_m = tan(goal_angle * deg2rad);

// Y-intercepts
double car_b = car_y / (car_x * car_m);
double goal_b = goal_y / (goal_x * goal_m);

// Find the points where the lines intersect.
double x_intersect = (goal_b + car_b) / (car_m + goal_m);
double y_intersect = car_m * x_intercept + car_b;


When I use graphics to plot this out I see lines going from both the goal and the car almost parallel to the north west. What I should be seeing is the 0 angle car having a line going east, intersecting with a north west line coming out of the goal.
computer coordinate system rather than the Cartesian one.

From the start, sorry but I don't know what you mean. There is no such thing as "computer coordinate system". We as humans define what type of coordinate system we want to push our computations through.

The Y is flipped
Okay, so you're talking about screen coordinates, where in many cases the top-left coordinate is (0, 0). This is not always in the case. In OpenGL, for example, (0, 0) is in the middle of the viewport.

But still, there's a bigger point here: The coordinate system doesn't matter. As long as you keep everything consistent. The actual logic should be separate from whatever other logic is used to map coordinates to actual pixels on the screen.

If you want to write unit tests for the math, then do that. Just worrying about the numbers. Once the numbers are correct, then you can worry about how it's actually shown on the screen for whatever graphing program you're using.

Your intersection logic is a bit off, regardless of coordinate system. Your car_b becomes inf. You need to account for your car_m being zero so that you aren't dividing by 0. (Edit: Well actually you should just re-do your logic for finding b).

Last edited on
1
2
car_m = tan(car_angle * deg2rad);         // 0.00
double car_b = car_y / (car_x * car_m);   // undefined 
Given a line with a certain slope, m, and a coordinate on the line, (x1, y1), you need to plug x and y the slope-intercept-form equation to find b.
y = m x + b
y1 = m x1 + b
b = y1 - m x1

PS: There's better way of determining intersections that can handle lines like x = 1, but the math is a bit more complicated.
Last edited on
closed account (Ezyq4iN6)
Yes, I mean screen coordinates, and the one with the origin in the top-left is the only variation I have ever seen, so that's why I assumed that.

I can understand that the coordinates system doesn't matter in the calculations, but it does matter when translating it to the screen, and that's where I'm having trouble. I know it may be a simple fix, but for some reason I have a lot of trouble trying to visualize how to compensate for the transition.

I updated the divided by zero part and modified that code segment with the y-intercepts.

1
2
3
// Y-intercepts
    double car_b = car_y - (car_x * car_m);
    double goal_b = goal_y - (goal_x * goal_m);


It's still giving me a similar problem, so I'm not sure exactly what is wrong. I even checked my formulas online, so I'll keep trying other options.
Last edited on
Forget about translating to screen coords for now. That's totally simple.

Your formulas are wrong. Here's something that seems to get the right answer (using 360 instead of 0). It still needs extra logic to handle possible division by 0 and other possibilities.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/*
250                             *
                                 *
                                  *
100    * * * * * * * * * * * * * * * *
                                    *
  0   100                      600
*/
#include <iostream>
#include <cmath>
using namespace std;

int main()
{
    const double deg2rad = acos(0) / 90;

    // Define car center and goal center
    double car_x  = 100, car_y  = 100, car_angle  = 360;
    double goal_x = 600, goal_y = 250, goal_angle = 300;

    // Slopes
    double car_m = tan(car_angle * deg2rad);
    double goal_m = tan(goal_angle * deg2rad);
    cout << car_m << '\t' << goal_m << '\n';

    // Y-intercepts
    double car_b  = car_y  - (car_x  * car_m );
    double goal_b = goal_y - (goal_x * goal_m);
    cout << car_b << '\t' << goal_b << '\n';

    // Find the points where the lines intersect.
    double x_intersect = (goal_b - car_b) / (car_m - goal_m);
    double y_intersect = car_m * x_intersect + car_b;
    cout << x_intersect << '\t' << y_intersect << '\n';
}

Last edited on
There's a phrase in software engineering called "Unit testing". Basically, it means writing your code in such a way that you can independently test small portions of your logic for correctness.

For example, given the function
1
2
3
4
double f(double x)
{
    return x * x + 3;
}

You can then test this function with particular inputs.
Given a particular input to this function, what do you expect the output to this function to be?
For example, if the input is f(4), then I expect the output to be 4*4+3, or 19.
This can be testing independently of any logic

1
2
3
4
5
6
bool test_f()
{
    double input = 3;
    double output = f(3);
    return output == 19;
}


For another example, if you wrote a function,
1
2
3
4
5
Point intersection(double m1, double x1, double y1, double m2, double x2, double y2)
{
    // calculate intersection
    return Point(x_intersection, y_intersection);
}

you could also test this, and compare it to what the expected result of the calculation should be.


I say this with caution, because I don't want to sound hubristic, and I could be mistaken, but I think your calculations for determining b are wrong.

I don't see the need to divide at all there. I already wrote above how I would find b. Can you explain why you're dividing there, to help me understand what you're doing?

Edit: dutch showed what I was trying to get it :)
But the concept of testing your code is an important skill.
Last edited on
Let me simplify my above post: Multiple times you said that you "weren't exactly sure what's wrong".

Regardless of the specific math involved in this issue, this is the point I'm trying to bring home: To properly test and debug code, you need a metric of determining what is right and wrong. (To be fair, you did say roughly what you were graphically expecting, but this didn't translate directly to the code you presented.)

Given an input of A, you expect B, but you're actually getting C.
It would help to quantify these numbers, every step of the way, for every significant calculation in the code in question.
- First, make sure m is producing expected values
- Then, make sure b is producing expected values
- Finally, make sure the intersection (x, y) is making producing expected values.

This helps split your code up so that you can pinpoint where something is going wrong, and in what way it is going wrong.
Last edited on
closed account (Ezyq4iN6)
@dutch

Thanks for the example. I'm sorry I didn't make my correction soon enough for the y-intercept formula, but I missed that because I copied it directly from a web site (which apparently wasn't a good idea). I'm replaced part of my code with what you have, and now I get slightly different results. The car still points NW, but with a smaller-looking slope. The goal line is very similar and basically parallel looking.


@Ganado

Thanks for also pointing out the math error for the _b variables. I have never heard of unit testing before, but then again I'm still pretty new to programming. I usually output my values to the screen so that I can see what I'm getting (I didn't include that code in my example). I'll take your advice and try to remember to test my code at each section before I proceed to the next. I have been trying to separate out parts of my code to new .h/.cpp files, classes, and structs when I can, because I was told that can help readability.

I'll make some more modifications and post an update if I have any progress. Thanks to both of you for the help thus far.
You haven't said anything about how you are drawing the lines (code?), so we can't help you with that.
closed account (Ezyq4iN6)
Here is my whole code that looks like it is working math-wise, but the angle is increasing and it rotates cw instead of ccw. Is this the point at which I need to translate the math to the screen coordinates?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include <iostream>
#include <math.h>

using namespace std;

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include "SDL_Helper.h"

int limitAngle(int angle_arg);

int main(int argc, char* argv[])
{
  // Point Structure
  struct Point_w_Dir
  {
    double x;
    double y;
    int a;
  };

  // Window Constants
  const int SCREEN_W = 800;
  const int SCREEN_H = 600;
  const string WINDOW_NAME = "Car and Goal";

  // Math Constants
  const double deg2rad = acos(0) / 90;

  // Program Variables.
  Point_w_Dir car = {100, 100, 0};
  Point_w_Dir goal = {600, 250, 300};

  // SDL window and renderer
  SDL_Renderer *renderer;
  SDL_Window* window;

  // SDL input and end program variables
  SDL_Event e;
  bool quit = false;

  // Initialize SDL and TTF.
  init_SDL(renderer, window, SCREEN_W, SCREEN_H, WINDOW_NAME);

  // Create font.
  TTF_Font *font = TTF_OpenFont("arial_black.ttf", 18);

  // Main loop.
  while (!quit)
  {
    // Check for input.
    while (SDL_PollEvent(&e))
    {
      switch(e.type)
      {
        case SDL_QUIT:
          quit = true;
          break;
        default:
          break;
      }
    }

    // Increase angle and keep it between 0 and 359
    car.a++;
    car.a = limitAngle(car.a);

    // Calculate car angle in degrees
    double car_radians = car.a * deg2rad;

    // Build text strings for output.
    string car_angle = "Car angle: " + to_string(car.a) +
                       "\xB0";

    // Calculate slope for the car, but prevent undefined.
    double car_m;
    if (car.a == 90 || car.a == 270)
      car_m = 9999;
    else
      car_m = tan(car_radians);

    // Calculate slope for the goal, but prevent undefined.
    double goal_m;
    if (goal.a == 90 || goal.a == 270)
      goal_m = 9999;
    else
      goal_m = tan(goal.a * deg2rad);

    double car_b = car.y - (car.x * car_m);
    double goal_b = goal.y - (goal.x * goal_m);

    // Calculate the intercept points
    double x_intercept = (goal_b - car_b) / (car_m - goal_m);
    double y_intercept = car_m * x_intercept + car_b;

    // Clear the background.
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
    SDL_RenderClear(renderer);

    // Draw goal
    SDL_SetRenderDrawColor(renderer, 120, 120, 255, 255);
    SDL_RenderDrawPoint(renderer, goal.x, goal.y);

    // Draw car
    SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
    SDL_RenderDrawPoint(renderer, car.x, car.y);

    // Draw lines from car and goal to intersection point.
    SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
    SDL_RenderDrawLine(renderer, goal.x, goal.y, x_intercept, y_intercept);
    SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
    SDL_RenderDrawLine(renderer, car.x, car.y, x_intercept, y_intercept);

    // Draw text.
    draw_text(renderer, font, car_angle, 20, SCREEN_H - 38);

    // Draw everything to screen.
    SDL_RenderPresent(renderer);

  }

  // Destroy Font
  TTF_CloseFont(font);
  font = NULL;

  // Close down SDL and TTF.
  stop_SDL(renderer, window);

  return 0;
}

// Limit angle from 0 to 359 degrees.
int limitAngle(int angle_arg)
{
  if (angle_arg >= 360.0)
    return (angle_arg - 360.0);
  else if (angle_arg < 0.0)
    return (angle_arg + 360.0);
  else
    return angle_arg;
}
SDL_Helper.h ?

Flipping y: MAX_Y - y;
Flipping angles: 360 - (angle % 360)
closed account (Ezyq4iN6)
I made the following changes to my code based on your suggestions:

1
2
3
4
5
6
7
8
9

// Before
double y_intercept = car_m * x_intercept + car_b;
double car_radians = car.a * deg2rad;

//After
double y_intercept = SCREEN_H - (car_m * x_intercept + car_b);
double car_radians = (360 - (car.a % 360)) * deg2rad;


I'm not sure about the angle part. I tried (angle % 360), but isn't that the same as angle?
I tried (angle % 360), but isn't that the same as angle?

Only if angle is between 0 and 359, which is the case for you.

Can you post SDL_Helper.h? (Or a link to it?)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// Compile: g++ -std=c++14 -Wall -W -pedantic cargoal.cpp -o cargoal -lSDL2 -lSDL2_ttf
// Ubuntu install SDL2: sudo apt-get install libsdl2-dev libsdl2-ttf-dev
#include <iostream>
#include <cmath>
using namespace std;

#include <SDL2/SDL.h>

struct Point_w_Dir {
    double x;
    double y;
    int a;
};

int main() {
    const double deg2rad = acos(0) / 90;
    const int SCREEN_W = 800;
    const int SCREEN_H = 600;
    Point_w_Dir car = {100, 100, 0};
    Point_w_Dir goal = {600, 250, 300};

    if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_EVENTS) != 0) {
        SDL_Log("Failed to initialize SDL: %s", SDL_GetError());
        return 1;
    }
    SDL_Window *window = SDL_CreateWindow("Car and Goal", SDL_WINDOWPOS_UNDEFINED,
             SDL_WINDOWPOS_UNDEFINED, SCREEN_W, SCREEN_H, SDL_WINDOW_OPENGL);
    SDL_Renderer *renderer =  SDL_CreateRenderer(window, -1,
             SDL_RENDERER_ACCELERATED);
    SDL_SetRenderDrawColor( renderer, 255, 0, 0, 0 );

    for (bool quit = false; !quit; ) {

        for (SDL_Event e; SDL_PollEvent(&e); )
            quit = e.type == SDL_QUIT;

        car.a = (car.a + 1) % 360;

        // Calculate slope for the car, but prevent undefined.
        double car_m;
        if (car.a == 90 || car.a == 270)
            car_m = 9999;
        else
            car_m = tan((360 - car.a) * deg2rad);

        // Calculate slope for the goal, but prevent undefined.
        double goal_m;
        if (goal.a == 90 || goal.a == 270)
            goal_m = 9999;
        else
            goal_m = tan((360 - goal.a) * deg2rad);

        double car_b = (SCREEN_H - car.y) - (car.x * car_m);
        double goal_b = (SCREEN_H - goal.y) - (goal.x * goal_m);

        // Calculate the intercept points
        double x_intercept = (goal_b - car_b) / (car_m - goal_m);
        double y_intercept = car_m * x_intercept + car_b;

        // Clear the background.
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderClear(renderer);

        // Draw goal
        SDL_SetRenderDrawColor(renderer, 120, 120, 255, 255);
        SDL_RenderDrawPoint(renderer, goal.x, (SCREEN_H - goal.y));

        // Draw car
        SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
        SDL_RenderDrawPoint(renderer, car.x, (SCREEN_H - car.y));

        // Draw lines from car and goal to intersection point.
        SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
        SDL_RenderDrawLine(renderer, goal.x, (SCREEN_H - goal.y),
                           x_intercept, y_intercept);
        SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
        SDL_RenderDrawLine(renderer, car.x, (SCREEN_H - car.y),
                           x_intercept, y_intercept);

        SDL_RenderPresent(renderer);
        SDL_Delay(50);
    }

    SDL_DestroyWindow(window);
    SDL_Quit();
}

Last edited on
closed account (Ezyq4iN6)
@dutch

Sure, here are the .h and .cpp files for SDL_Helper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// SDL_Helped.h
#ifndef SDL_HELPER_H
#define SDL_HELPER_H

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <string>

void init_SDL(SDL_Renderer *& renderer, SDL_Window *& window, int w, int h,
              std::string window_name);

void draw_text(SDL_Renderer *& renderer, TTF_Font *&font,
               std::string text_str, int x, int y);

void stop_SDL(SDL_Renderer *& renderer, SDL_Window *& window);

#endif 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// SDL_Helped.cpp
#include "SDL_Helper.h"

void init_SDL(SDL_Renderer *& renderer, SDL_Window *& window, int w, int h,
              std::string window_name)
{
  // Initialize SDL and TTF.
  SDL_Init(SDL_INIT_EVERYTHING);
  TTF_Init();

  // Create a window for your game.
  window = SDL_CreateWindow(window_name.c_str(), SDL_WINDOWPOS_UNDEFINED,
                            SDL_WINDOWPOS_UNDEFINED, w, h, 0);

  // Create a renderer to display your graphics.
  renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED |
                                SDL_RENDERER_PRESENTVSYNC);

}

void draw_text(SDL_Renderer *& renderer, TTF_Font *&font,
               std::string text_str, int x, int y)
{
  // Variables for text box width and height.
  int temp_w, temp_h;

  // Color for text.
  SDL_Color text_color = {255, 255, 0};

  // Text for output.
  const char * text = text_str.c_str();

  // Create a temporary surface and texture for the text.
  SDL_Surface *text_surface = TTF_RenderText_Blended(font, text, text_color);
  SDL_Texture *text_texture = SDL_CreateTextureFromSurface(renderer, text_surface);

  // Find the size of the text to get the width and height for the rectangle.
  TTF_SizeText(font, text, &temp_w, &temp_h);

  // Create the text rectangle.
  SDL_Rect text_rect = {x, y, temp_w, temp_h};

  // Draw the text to the renderer.
  SDL_RenderCopy(renderer, text_texture, NULL, &text_rect);

  // Destroy surface and texture.
  SDL_FreeSurface(text_surface);
  text_surface = NULL;
  SDL_DestroyTexture(text_texture);
  text_texture = NULL;
}

void stop_SDL(SDL_Renderer *& renderer, SDL_Window *& window)
{
  // Close down SDL and TTF.
  SDL_DestroyRenderer(renderer);
  renderer = NULL;
  SDL_DestroyWindow(window);
  window = NULL;

  // Quit TTF and SDL.
  TTF_Quit();
  SDL_Quit();
}
Topic archived. No new replies allowed.