I am having some problems with some vector math.

Imagine a square and coming from each corner of the square is an invisible vector, which starts at the square's centre and ends at the edge of the screen.

   \          /
    \   a    /
     |      |
  d  |      | b
    /        \
   /    c     \
  /            \

There are 4 areas: a, b, c and d

I would like to say that if you touch within one of the areas then something happens.

Is there a simple way to do this?

  • 1
    \$\begingroup\$ What is the shape of the object that "touches" the areas? \$\endgroup\$ – sam hocevar Apr 17 '12 at 13:13
  • \$\begingroup\$ @SamHocevar Finger touch. This is for android. So will be just an x,y vector \$\endgroup\$ – maffo Apr 17 '12 at 13:28

Translate and scale the input coordinates to have the origin in the center of the screen and have vectors towards the corners of the screen at 90° angles. Then, to exclude the inner square, use:

abs(x) > s
abs(y) > s

Where s is the size of the square. Then, to identify the area, use:

a: y > x and y > -x
b: x > y and x > -y
c: y < x and y < -x
d: x < y and x < -y
  • \$\begingroup\$ This only works if the square is (a) centered and (b) of exactly the same aspect ratio as the screen as a whole; otherwise it won't be perfect. \$\endgroup\$ – Steven Stadnicki May 28 '12 at 5:00
  • \$\begingroup\$ If it's not centered or the same aspect ratio, just apply a translation and scale in addition to the rotation. Easy matrix op. \$\endgroup\$ – 3Dave May 28 '12 at 6:56
  • \$\begingroup\$ @DavidLively That can't work mathematically - for instance, if it's not the same aspect ratio (or not centered, for that matter) then the two lines from opposite corners of the squares may not be collinear (that is, they might not be 'the same line' in the middle), but this solution assumes they are and no linear transformation will change that. \$\endgroup\$ – Steven Stadnicki May 28 '12 at 15:02

I would just use two point-in-triangle tests for each of the areas.



I would suggest that the easiest way is to look at the angle between one of these vectors and where the user touches. You can use the dot product method to work this out; see here.

The dot product will give you the angle between the chosen vector and the user's touch point (you may have to translate this touch point into the squares local coordinate space). Then if you use the top left vector for the comparison, you will find that if the angle is less than 90 then the user touches quadrant a, if it's more than 90 but less than 180, then it's quadrant b and so on.

Edit: Sorry dot product doesn't give you the angle, it gives you the cosine of the angle. So you have to do inverse cosine on the result. You can also use atan2 to get the angle.

  • \$\begingroup\$ arc-cosines/arc-tangents are severe overkill for this problem, and the angles to compare against aren't trivial to calculate either - the two diagonals don't have to be at right angles to each other. \$\endgroup\$ – Steven Stadnicki May 28 '12 at 5:01
  • \$\begingroup\$ @Steve It does say square in the question, so I think they do have to be at right angles. \$\endgroup\$ – OriginalDaemon May 28 '12 at 10:21
  • \$\begingroup\$ The center region might be square, but the outer screen doesn't have to be (and almost certainly won't for any display), so the diagonals out to the edges aren't guaranteed to be. Imagine a 1x1 square in the middle of a 3x5 box; then the diagonals out have slopes +/- 1/2, and they won't be at right angles to each other. \$\endgroup\$ – Steven Stadnicki May 28 '12 at 15:05

For concreteness, let's say that your screen spans from sx_0 to sx_1 and sy_0 to sy_1 (so the four corners of the screen are (sx_0, sy_0), (sx_1, sy_0), (sx_1, sy_1) and (sx_0, sy_1) ) and likewise that the inner box runs from ix_0 to ix_1 and iy_0 to iy_1; finally, say the user has touched at (tx,ty).

The simplest way to order the tests is to first test the touch point against the inner square; knowing that it's not inside simplifies the other ones. This test is straightforward: if

(tx >= ix_0) && (ty >= iy_0) && (tx < ix_1) && (ty < iy_1)

then your point is in the inner rectangle. How you want to handle the edge cases with respect to points in the center box is, of course, up to you - be careful with them, though!

Once you know your point isn't in the inner box, the most straightforward way of testing each of the other regions is by doing two 'half-space' comparisons against the diagonal lines that bound them. To explain in slightly more detail: the equation of a line, generically, is 'A.P=b', where P=(x,y) is an arbitrary point on the line, A is a vector perpendicular to the line, and b is some constant; what's more, if a vector in the direction of the line is (vx, vy), then the vector (vy, -vx) is perpendicular to the line. What's that mean here? Well, consider the dividing line in the top-left corner: this line goes from (sx_0, sy_0) to (ix_0, iy_0), so a vector in the direction of the line is (ix_0-sx_0, iy_0-sy_0) and a vector perpendicular to the line is (iy_0-sy_0, sx_0-ix_0) (be careful - notice how the second coordinate got flipped!) What's more, we can find the value of b by plugging in one of the two points we know is on the line: for instance, using (sx_0, sy_0) we find that the dot product is sx_0*(iy_0-sy_0)+sy_0*(sx_0-ix_0), or (cancelling out the sx_0*sy_0 terms) sx_0*iy_0-sy_0*ix_0. In other words, the equation of the line is (iy_0-sy_0)*x+(sx_0-ix_0)*y = sx_0*iy_0-sy_0*ix_0. What's more, which side of the line a point is on is determined by whether the dot product is less than or greater than sx_0*iy_0-sy_0*ix_0.

Finally, to use this: consider for instance the top region. Then this is the section that lies to a specific side of the line from (sx_0, sy_0) to (ix_0, iy_0) and also on a specific side of the line from (sx_1, sy_0) to (ix_1, iy_0). In other words, to test your point (tx, ty), you'll know that it's in your region a if:

tx*(iy_0-sy_0)+ty*(sx_0-ix_0) > sx_0*iy_0-sy_0*ix_0


tx*(iy_0-sy_0)+ty*(sx_1-ix_1) <= sx_1*iy_0-sy_0*ix_1

Likewise, you can do similar tests for the other three regions using the appropriate lines from inner to outer corner. These tests may look complicated, but they're pretty straightforward and they have a lot of advantages: they'll work no matter where your inner box is (as long as the box is entirely contained inside your screen), whatever the dimensions of your screen and inner box are, and they don't involve anything more complicated than a few multiplies - they can even be done in purely integer math if need be.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.