/**
* Finds the intersection point between
* * the rectangle
* with parallel sides to the x and y axes
* * the half-line pointing towards (x,y)
* originating from the middle of the rectangle
*
* Note: the function works given min[XY] <= max[XY],
* even though minY may not be the "top" of the rectangle
* because the coordinate system is flipped.
* Note: if the input is inside the rectangle,
* the line segment wouldn't have an intersection with the rectangle,
* but the projected half-line does.
* Warning: passing in the middle of the rectangle will return the midpoint itself
* there are infinitely many half-lines projected in all directions,
* so let's just shortcut to midpoint (GIGO).
*
* @param x:Number x coordinate of point to build the half-line from
* @param y:Number y coordinate of point to build the half-line from
* @param minX:Number the "left" side of the rectangle
* @param minY:Number the "top" side of the rectangle
* @param maxX:Number the "right" side of the rectangle
* @param maxY:Number the "bottom" side of the rectangle
* @param validate:boolean (optional) whether to treat point inside the rect as error
* @return an object with x and y members for the intersection
* @throws if validate == true and (x,y) is inside the rectangle
* @author TWiStErRob
* @see <a href="http://stackoverflow.com/a/31254199/253468">source</a>
* @see <a href="http://stackoverflow.com/a/18292964/253468">based on</a>
*/
function pointOnRect(x, y, minX, minY, maxX, maxY, validate) {
\t //assert minX <= maxX;
\t //assert minY <= maxY;
\t if (validate && (minX < x && x < maxX) && (minY < y && y < maxY))
\t \t throw "Point " + [x,y] + "cannot be inside "
\t \t + "the rectangle: " + [minX, minY] + " - " + [maxX, maxY] + ".";
\t var midX = (minX + maxX)/2;
\t var midY = (minY + maxY)/2;
\t // if (midX - x == 0) -> m == ±Inf -> minYx/maxYx == x (because value/±Inf = ±0)
\t var m = (midY - y)/(midX - x);
\t if (x <= midX) { // check "left" side
\t \t var minXy = m * (minX - x) + y;
\t \t if (minY <= minXy && minXy <= maxY)
\t \t \t return {x: minX, y: minXy};
\t }
\t if (x >= midX) { // check "right" side
\t \t var maxXy = m * (maxX - x) + y;
\t \t if (minY <= maxXy && maxXy <= maxY)
\t \t \t return {x: maxX, y: maxXy};
\t }
\t if (y <= midY) { // check "top" side
\t \t var minYx = (minY - y)/m + x;
\t \t if (minX <= minYx && minYx <= maxX)
\t \t \t return {x: minYx, y: minY};
\t }
\t if (y >= midY) { // check "bottom" side
\t \t var maxYx = (maxY - y)/m + x;
\t \t if (minX <= maxYx && maxYx <= maxX)
\t \t \t return {x: maxYx, y: maxY};
\t }
\t // edge case when finding midpoint intersection: m = 0/0 = NaN
\t if (x === midX && y === midY) return {x: x, y: y};
\t // Should never happen :) If it does, please tell me!
\t throw "Cannot find intersection for " + [x,y]
\t + " inside rectangle " + [minX, minY] + " - " + [maxX, maxY] + ".";
}
(function tests() {
\t var left = 100, right = 200, top = 50, bottom = 150; // a square, really
\t var hMiddle = (left + right)/2, vMiddle = (top + bottom)/2;
\t function intersectTestRect(x, y) { return pointOnRect(x,y, left,top, right,bottom, true); }
\t function intersectTestRectNoValidation(x, y) { return pointOnRect(x,y, left,top, right,bottom, false); }
\t function checkTestRect(x, y) { return function() { return pointOnRect(x,y, left,top, right,bottom, true); }; }
\t QUnit.test("intersects left side", function(assert) {
\t \t var leftOfRect = 0, closerLeftOfRect = 25;
\t \t assert.deepEqual(intersectTestRect(leftOfRect, 25), {x:left, y:75}, "point above top");
\t \t assert.deepEqual(intersectTestRect(closerLeftOfRect, top), {x:left, y:80}, "point in line with top");
\t \t assert.deepEqual(intersectTestRect(leftOfRect, 70), {x:left, y:90}, "point above middle");
\t \t assert.deepEqual(intersectTestRect(leftOfRect, vMiddle), {x:left, y:100}, "point exact middle");
\t \t assert.deepEqual(intersectTestRect(leftOfRect, 130), {x:left, y:110}, "point below middle");
\t \t assert.deepEqual(intersectTestRect(closerLeftOfRect, bottom), {x:left, y:120}, "point in line with bottom");
\t \t assert.deepEqual(intersectTestRect(leftOfRect, 175), {x:left, y:125}, "point below bottom");
\t });
\t QUnit.test("intersects right side", function(assert) {
\t \t var rightOfRect = 300, closerRightOfRect = 250;
\t \t assert.deepEqual(intersectTestRect(rightOfRect, 25), {x:right, y:75}, "point above top");
\t \t assert.deepEqual(intersectTestRect(closerRightOfRect, top), {x:right, y:75}, "point in line with top");
\t \t assert.deepEqual(intersectTestRect(rightOfRect, 70), {x:right, y:90}, "point above middle");
\t \t assert.deepEqual(intersectTestRect(rightOfRect, vMiddle), {x:right, y:100}, "point exact middle");
\t \t assert.deepEqual(intersectTestRect(rightOfRect, 130), {x:right, y:110}, "point below middle");
\t \t assert.deepEqual(intersectTestRect(closerRightOfRect, bottom), {x:right, y:125}, "point in line with bottom");
\t \t assert.deepEqual(intersectTestRect(rightOfRect, 175), {x:right, y:125}, "point below bottom");
\t });
\t QUnit.test("intersects top side", function(assert) {
\t \t var aboveRect = 0;
\t \t assert.deepEqual(intersectTestRect(80, aboveRect), {x:115, y:top}, "point left of left");
\t \t assert.deepEqual(intersectTestRect(left, aboveRect), {x:125, y:top}, "point in line with left");
\t \t assert.deepEqual(intersectTestRect(120, aboveRect), {x:135, y:top}, "point left of middle");
\t \t assert.deepEqual(intersectTestRect(hMiddle, aboveRect), {x:150, y:top}, "point exact middle");
\t \t assert.deepEqual(intersectTestRect(180, aboveRect), {x:165, y:top}, "point right of middle");
\t \t assert.deepEqual(intersectTestRect(right, aboveRect), {x:175, y:top}, "point in line with right");
\t \t assert.deepEqual(intersectTestRect(220, aboveRect), {x:185, y:top}, "point right of right");
\t });
\t QUnit.test("intersects bottom side", function(assert) {
\t \t var belowRect = 200;
\t \t assert.deepEqual(intersectTestRect(80, belowRect), {x:115, y:bottom}, "point left of left");
\t \t assert.deepEqual(intersectTestRect(left, belowRect), {x:125, y:bottom}, "point in line with left");
\t \t assert.deepEqual(intersectTestRect(120, belowRect), {x:135, y:bottom}, "point left of middle");
\t \t assert.deepEqual(intersectTestRect(hMiddle, belowRect), {x:150, y:bottom}, "point exact middle");
\t \t assert.deepEqual(intersectTestRect(180, belowRect), {x:165, y:bottom}, "point right of middle");
\t \t assert.deepEqual(intersectTestRect(right, belowRect), {x:175, y:bottom}, "point in line with right");
\t \t assert.deepEqual(intersectTestRect(220, belowRect), {x:185, y:bottom}, "point right of right");
\t });
\t QUnit.test("intersects a corner", function(assert) {
\t \t assert.deepEqual(intersectTestRect(left-50, top-50), {x:left, y:top}, "intersection line aligned with top-left corner");
\t \t assert.deepEqual(intersectTestRect(right+50, top-50), {x:right, y:top}, "intersection line aligned with top-right corner");
\t \t assert.deepEqual(intersectTestRect(left-50, bottom+50), {x:left, y:bottom}, "intersection line aligned with bottom-left corner");
\t \t assert.deepEqual(intersectTestRect(right+50, bottom+50), {x:right, y:bottom}, "intersection line aligned with bottom-right corner");
\t });
\t QUnit.test("on the corners", function(assert) {
\t \t assert.deepEqual(intersectTestRect(left, top), {x:left, y:top}, "top-left corner");
\t \t assert.deepEqual(intersectTestRect(right, top), {x:right, y:top}, "top-right corner");
\t \t assert.deepEqual(intersectTestRect(right, bottom), {x:right, y:bottom}, "bottom-right corner");
\t \t assert.deepEqual(intersectTestRect(left, bottom), {x:left, y:bottom}, "bottom-left corner");
\t });
\t QUnit.test("on the edges", function(assert) {
\t \t assert.deepEqual(intersectTestRect(hMiddle, top), {x:hMiddle, y:top}, "top edge");
\t \t assert.deepEqual(intersectTestRect(right, vMiddle), {x:right, y:vMiddle}, "right edge");
\t \t assert.deepEqual(intersectTestRect(hMiddle, bottom), {x:hMiddle, y:bottom}, "bottom edge");
\t \t assert.deepEqual(intersectTestRect(left, vMiddle), {x:left, y:vMiddle}, "left edge");
\t });
\t QUnit.test("validates inputs", function(assert) {
\t \t assert.throws(checkTestRect(hMiddle, vMiddle), /cannot be inside/, "center");
\t \t assert.throws(checkTestRect(hMiddle-10, vMiddle-10), /cannot be inside/, "top left of center");
\t \t assert.throws(checkTestRect(hMiddle-10, vMiddle), /cannot be inside/, "left of center");
\t \t assert.throws(checkTestRect(hMiddle-10, vMiddle+10), /cannot be inside/, "bottom left of center");
\t \t assert.throws(checkTestRect(hMiddle, vMiddle-10), /cannot be inside/, "above center");
\t \t assert.throws(checkTestRect(hMiddle, vMiddle), /cannot be inside/, "center");
\t \t assert.throws(checkTestRect(hMiddle, vMiddle+10), /cannot be inside/, "below center");
\t \t assert.throws(checkTestRect(hMiddle+10, vMiddle-10), /cannot be inside/, "top right of center");
\t \t assert.throws(checkTestRect(hMiddle+10, vMiddle), /cannot be inside/, "right of center");
\t \t assert.throws(checkTestRect(hMiddle+10, vMiddle+10), /cannot be inside/, "bottom right of center");
\t \t assert.throws(checkTestRect(left+10, vMiddle-10), /cannot be inside/, "right of left edge");
\t \t assert.throws(checkTestRect(left+10, vMiddle), /cannot be inside/, "right of left edge");
\t \t assert.throws(checkTestRect(left+10, vMiddle+10), /cannot be inside/, "right of left edge");
\t \t assert.throws(checkTestRect(right-10, vMiddle-10), /cannot be inside/, "left of right edge");
\t \t assert.throws(checkTestRect(right-10, vMiddle), /cannot be inside/, "left of right edge");
\t \t assert.throws(checkTestRect(right-10, vMiddle+10), /cannot be inside/, "left of right edge");
\t \t assert.throws(checkTestRect(hMiddle-10, top+10), /cannot be inside/, "below top edge");
\t \t assert.throws(checkTestRect(hMiddle, top+10), /cannot be inside/, "below top edge");
\t \t assert.throws(checkTestRect(hMiddle+10, top+10), /cannot be inside/, "below top edge");
\t \t assert.throws(checkTestRect(hMiddle-10, bottom-10), /cannot be inside/, "above bottom edge");
\t \t assert.throws(checkTestRect(hMiddle, bottom-10), /cannot be inside/, "above bottom edge");
\t \t assert.throws(checkTestRect(hMiddle+10, bottom-10), /cannot be inside/, "above bottom edge");
\t });
\t QUnit.test("doesn't validate inputs", function(assert) {
\t \t assert.deepEqual(intersectTestRectNoValidation(hMiddle-10, vMiddle-10), {x:left, y:top}, "top left of center");
\t \t assert.deepEqual(intersectTestRectNoValidation(hMiddle-10, vMiddle), {x:left, y:vMiddle}, "left of center");
\t \t assert.deepEqual(intersectTestRectNoValidation(hMiddle-10, vMiddle+10), {x:left, y:bottom}, "bottom left of center");
\t \t assert.deepEqual(intersectTestRectNoValidation(hMiddle, vMiddle-10), {x:hMiddle, y:top}, "above center");
\t \t assert.deepEqual(intersectTestRectNoValidation(hMiddle, vMiddle), {x:hMiddle, y:vMiddle}, "center");
\t \t assert.deepEqual(intersectTestRectNoValidation(hMiddle, vMiddle+10), {x:hMiddle, y:bottom}, "below center");
\t \t assert.deepEqual(intersectTestRectNoValidation(hMiddle+10, vMiddle-10), {x:right, y:top}, "top right of center");
\t \t assert.deepEqual(intersectTestRectNoValidation(hMiddle+10, vMiddle), {x:right, y:vMiddle}, "right of center");
\t \t assert.deepEqual(intersectTestRectNoValidation(hMiddle+10, vMiddle+10), {x:right, y:bottom}, "bottom right of center");
\t });
})();
<link href="https://code.jquery.com/qunit/qunit-2.3.2.css" rel="stylesheet"/>
<script src="https://code.jquery.com/qunit/qunit-2.3.2.js"></script>
<div id="qunit"></div>
¿Podemos suponer que el rectángulo está alineado con los ejes y no está inclinado? – Grandpa
Para aquellos que votan para cerrar: tradicionalmente hemos permitido que este tipo de preguntas de matemáticas sean lo suficientemente cercanas a los problemas de programación y lo suficientemente comunes tanto en la programación de la vida real como en la educación de programación. Lo que buscaría en estas preguntas es la posibilidad real de que sea un duplicado. – dmckee