1
\$\begingroup\$

This is pretty straightforward, but is there a better way to do this?

function isWithinBounds(obj, pos) {
    var oXmin, oXmax, oYmin, oYmax;
    if (obj.startPos.x < obj.endPos.x) {
        oXmin = obj.startPos.x;
        oXmax = obj.endPos.x;
    }
    else {
        oXmin = obj.endPos.x;
        oXmax = obj.startPos.x
    }

    if (obj.startPos.y < obj.endPos.y) {
        oYmin = obj.startPos.y;
        oYmax = obj.endPos.y;
    }
    else {
        oYmin = obj.endPos.y;
        oYmax = obj.startPos.y;
    }

    return (pos.x > oXmin && pos.x < oXmax && pos.y > oYmin && pos.y < oYmax);

}
\$\endgroup\$
3
\$\begingroup\$

There is. You can use Math.min and Math.max to determine which is the smaller end and the larger end. This way, you avoid the if statements.

function isWithinBounds(obj, pos) {
    var oXmin = Math.min(obj.startPos.x, obj.endPos.x);
    var oXmax = Math.max(obj.startPos.x, obj.endPos.x);
    var oYmin = Math.min(obj.startPos.y, obj.endPos.y);
    var oYmax = Math.max(obj.startPos.y, obj.endPos.y);
    return ( oXmin < pos.x && pos.x < oXmax && oYmin < pos.y && pos.y < oYmax);
}
\$\endgroup\$
1
\$\begingroup\$

• Function signature :

since you are checking if a point is within a rectangle, your function signature is too vague, it would be better called isPointInRect. obviously if it is meant to be a method shared by circles, polygons, ... the name is fine.
But as your question is written i'd change the signature and add a comment to precise the strict inclusion, and have arguments ordered like in english :

// Returns if the point is strictly within the rect.
function isPointInRect(pt, rect) {

• DRY / cache your property access :

You should notice at the first glance that you are using again and again the same properties, and writing the same things : it hinders both readability and performances.

You should at least cache startPos, endPos, ptX, ptY :

var startPos = rect.startPos, endPos, rect.endPos;
var ptX = pt.x, ptY = pt.y;

And you might also want to cache startPos.x/y and endPos.x/y, but see the next point.

• Do not repeat computations :

It is most likely that there will be far more point test than rect change : but as you wrote the code, for each point testing you'll have to re-order the rect points.
I suggest that your Rect class already keeps your points or coordinates in the right order. So either have two topLeft, downRight points or left,right, top, down properties (or, the classics : x, y, width, height).

• exit the sooner you can :

Assuming that most point test will fail, you'd better write you last conditional the other way around to exit sooner:

return ! ( ptX <= rect.left || ... || ... || ... ) ;

So your function might now read :

// Returns if the point is strictly within the rect.
function isPointInRect(pt, rect) {
    var ptX = pt.x, ptY = pt.y;
    return ! ( ptX <= rect.left || ptX >= rect.right 
                ||  ptY <= rect.top || ptY >= rect.down ) ;
}

(personaly i prefer the x/y/width/height version of the Rect, especially since Rect might extends Point :

// Returns if the point is strictly within the rect.
function isPointInRect(pt, rect) {
    var dx = pt.x-rect.x, dy = pt.y-rect.y;
    return ! ( dx <= 0 || dx >= rect.width 
                ||  dy <= 0 || dy >= rect.height ) ;
}

)

\$\endgroup\$
0
\$\begingroup\$

I'd go with someone like this, which reduces the heart of the logic to a single declarative line (the one with the return statement):

function isWithinBounds(obj, pos) {
  var xs = [obj.startPos.x, obj.endPos.x].sort(),
      ys = [obj.startPos.y, obj.endPos.y].sort();
  return contains(xs, pos.x) && contains(ys, pos.y);
  function contains(arr, x) { return x > arr[0] && x < arr[1] }
}

Or if you want to avoid all repetition:

function isWithinBounds(obj, pos) {
  var xs = boundsOf('x'), ys = boundsOf('y');
  return contains(xs, pos.x) && contains(ys, pos.y);
  function contains(arr, x) { return x > arr[0] && x < arr[1] }
  function boundsOf(prop) { return [obj.startPos[prop], obj.endPos[prop]].sort() }
}

Though personally I prefer the first variation.

\$\endgroup\$
0
\$\begingroup\$

Have not checked to see if this function is faster:

function check(a, b, p) {
    var f = b - a, g = b - p;
    return (Math.abs(f) >= Math.abs(g)) && (Math.sign(f) === Math.sign(g));
}

To use the function do the following:

function isWithinBounds(obj, pos) {
    return check(obj.startPos.x, obj.endPos.x, pos.x) &&
           check(obj.startPos.y, obj.endPos.y, pos.y);
}

How Does It Work?

Since your rectangle is always upright, we can focus on just looking at one dimension at a time. The hard part is the fact that you only have two points on the rectangle and we do not know what is the top/right or bottom/left which is why you first figure out which one is greater. So, this attempt was to see if I could still figure out if was in between without actually checking which was top or bottom.

First we take a to be the start and b to be the end. Then we define f to be the difference between the two f = b - a. Also, let the value that we want to know if is between a and b be p. Then we can define g to be the difference between b and p, g = b - p.

So, we can represent all of the different cases a, b, and p can be placed.

|----a----p----b----|
|----b----p----a----|
|----b----a----p----|
|----p----a----b----|
|----a----b----p----|
|----p----b----a----|

Now let us also place the f and g values next to the different combinations.

                        f   g
|----a----p----b----|  +#  +# (|g| < |f|) true
|----b----p----a----|  -#  -# (|g| < |f|) true
|----b----a----p----|  -#  -# (|g| > |f|) false
|----p----a----b----|  +#  +# (|g| > |f|) false
|----a----b----p----|  +#  -# (|g| ? |f|) false
|----p----b----a----|  -#  +# (|g| ? |f|) false

As you can see, when |g| is smaller than |f| and the signs of g and f are the same then p is between a and b.

\$\endgroup\$

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.