Geometer»Blog

Quick note - bugs from mistaking points for vectors.

A couple of the more time-consuming/confusing bugs to track down have been from mistakenly passing an absolute position (point) instead of a relative position (vector) to functions that use these for drawing or intersection or similar.

These are a little difficult to track down because by default I'm drawing pretty close to the origin, so the relative positions are often very similar to the absolute ones. As a result, the bugs would appear inconsistently. I was attempting to fix these a little while after writing them, and so didn't have all the intentions behind each passed parameter cached in my brain. When stepping through the debugger, the points being passed didn't raise any red flags and so I didn't pay much attention to them. I thought the source of the problem was elsewhere.

Once I had fixed the first bug after an embarrassingly long time, the second time a similar bug came up went much quicker. Awareness is your best defence!

'Best'? Ok, probably not, but it's definitely helpful. There are also a few other potential solutions:

  1. Differentiating at runtime
  2. This involves adding more data to your vector/point structure that indicates which it is at any point during execution. You can look up the details if you're interested, but suffice it to say that I didn't feel like this bug warranted the memory cost. I believe the normal way to do this for 2D would amount to structs 50% larger.

  3. Language-level types
  4. Probably viable - if a little cumbersome - in C++, where types are differentiated basically if they have different names. (Un)fortunately, I'm working in C, not C++ where types are differentiated by structural similarity. i.e. because both types would look something like the following, they would both be allowed as parameters anyway.
    1
    2
    3
    4
    typedef struct {
        f32 X;
        f32 Y;
    } point/vector;
    
    EDIT: This is wrong. The similarity rule only applies to primitive types, not structs. See comments below.

  5. Be smarter and better at debugging
  6. Would probably be effective, unfortunately I can only really work with what I have.

  7. Hungarian notation (screams, babies crying, dramatic hellish music plays...)
  8. I happened to read Joel Spolsky's article 'Making Wrong Code Look Wrong' at around the time I had this issue. This probably won't be popular, but this is the route I have taken. Absolute positions/points are now prefixed with 'po', and I have currently left other vectors unprefixed.


So... just a quick reminder to try and reconsider what assumptions you're making when debugging is unproductive. Stay safe out there!
Dumitru Frunza,
I'm working in C, not C++ where types are differentiated by structural similarity.

I seem to recall that in C, struct types are differentiated by name:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include "dummy.h"

struct point
{
  float X;
  float Y;
};

struct vector
{
  float X;
  float Y;
};

void foo(struct point* pt);

struct vector v;
foo(&v); // --> does not compile
Andrew Reece,
Just tried it out, you're right! It looks like it's just for primitive types - I must have just assumed it was the same for structs...

I might reconsider doing this, but I'm not sure if I want to account for all transformations between points and vectors throughout all my functions, and it's sometimes useful to be able to use either... hmm, I'll have a think.
A very quick and dirty fix would be to move everything away from 0,0 in absolutes position.

That way the absolute positions are around say 1000, 1000 and the relative position are around 0,0 as normal.