Checking User is inside Polygone on Googlemap or not.. - Flutter [duplicate] - flutter

I'm working on flutter project using google-maps-flutter plugin, and I want to check if the user location is inside the polygon that I created on the map. There is an easy way using JavaScript api (containsLocation() method) but for flutter I only found a third party plugin,google_map_polyutil, which is only for android and I get a security worming when I run my app. Is there another way to do so??

I found this answer and just modified some minor things to work with dart, I ran a test on a hardcoded polygon. The list _area is my polygon and _polygons is required for my mapcontroller.
final Set<Polygon> _polygons = {};
List<LatLng> _area = [
LatLng(-17.770992200, -63.207739700),
LatLng(-17.776386600, -63.213576200),
LatLng(-17.778348200, -63.213576200),
LatLng(-17.786848100, -63.214262900),
LatLng(-17.798289700, -63.211001300),
LatLng(-17.810547700, -63.200701600),
LatLng(-17.815450600, -63.185252100),
LatLng(-17.816267800, -63.170660900),
LatLng(-17.800741300, -63.153838100),
LatLng(-17.785867400, -63.150919800),
LatLng(-17.770501800, -63.152636400),
LatLng(-17.759712400, -63.160361200),
LatLng(-17.755952300, -63.169802600),
LatLng(-17.752519100, -63.186625400),
LatLng(-17.758404500, -63.195551800),
LatLng(-17.770992200, -63.206538100),
LatLng(-17.770996000, -63.207762500)];
The function ended like this:
bool _checkIfValidMarker(LatLng tap, List<LatLng> vertices) {
int intersectCount = 0;
for (int j = 0; j < vertices.length - 1; j++) {
if (rayCastIntersect(tap, vertices[j], vertices[j + 1])) {
intersectCount++;
}
}
return ((intersectCount % 2) == 1); // odd = inside, even = outside;
}
bool rayCastIntersect(LatLng tap, LatLng vertA, LatLng vertB) {
double aY = vertA.latitude;
double bY = vertB.latitude;
double aX = vertA.longitude;
double bX = vertB.longitude;
double pY = tap.latitude;
double pX = tap.longitude;
if ((aY > pY && bY > pY) || (aY < pY && bY < pY) || (aX < pX && bX < pX)) {
return false; // a and b can't both be above or below pt.y, and a or
// b must be east of pt.x
}
double m = (aY - bY) / (aX - bX); // Rise over run
double bee = (-aX) * m + aY; // y = mx + b
double x = (pY - bee) / m; // algebra is neat!
return x > pX;
}
Notice the polygons property and the onTap method. I was trying to check if the marker created in my map was inside my polygon:
GoogleMap(
initialCameraPosition: CameraPosition(
target: target, //LatLng(0, 0),
zoom: 16,
),
zoomGesturesEnabled: true,
markers: markers,
polygons: _polygons,
onMapCreated: (controller) =>
_mapController = controller,
onTap: (latLng) {
_getAddress(latLng);
},
)
Then i just used the following call in my _getAddress method:
_checkIfValidMarker(latLng, _area);
I hope it helps you to create what you need.

The easiest way to use it - https://pub.dev/packages/maps_toolkit
with isLocationOnPath method.

L. Chi's answer really help.
But due to I have pretty close points, rayCastIntersect might have wrong boolean return if aX is equal to bX
Therefore, I just add aX == bX condition check before calculate m then it works.
bool rayCastIntersect(LatLng tap, LatLng vertA, LatLng vertB) {
double aY = vertA.latitude;
double bY = vertB.latitude;
double aX = vertA.longitude;
double bX = vertB.longitude;
double pY = tap.latitude;
double pX = tap.longitude;
if ((aY > pY && bY > pY) || (aY < pY && bY < pY) || (aX < pX && bX < pX)) {
return false; // a and b can't both be above or below pt.y, and a or
// b must be east of pt.x
}
if (aX == bX) {
return true;
}
double m = (aY - bY) / (aX - bX); // Rise over run
double bee = (-aX) * m + aY; // y = mx + b
double x = (pY - bee) / m; // algebra is neat!
return x > pX;
}

The easiest way to use it - https://pub.dev/packages/maps_toolkit
with PolygonUtil.containsLocation - computes whether the given point lies inside the specified polygon.

Related

How to define self-intersection of polygon in google maps (flutter)

I am making an application in which the user can draw a polygon on a map by points.
I need to somehow make sure that the polygon does not have self-intersections.
I know it is possible to manually check each line.
There are various methods for this.
But I noticed that Google Maps
automatically fills polygons that do not have self-intersections.
Is it possible to get this value from the plugin somehow?
i am using google_maps_flutter
without self-intersection
with self-intersection
Perhaps this will be useful to someone.
I did not find a built-in function in the plugins listed in the question.
So I wrote my own function:
true - there are self-intersections
bool isNotSimplePolygon(List<LatLng> polygon){
if(polygon.length <= 3)
return false;
for(int i = 0; i < polygon.length - 2; i++){
double x1 = polygon[i].latitude;
double y1 = polygon[i].longitude;
double x2 = polygon[i + 1].latitude;
double y2 = polygon[i + 1].longitude;
double maxx1 = max(x1, x2), maxy1 = max(y1, y2);
double minx1 = min(x1, x2), miny1 = min(y1, y2);
for (int j = i + 2; j < polygon.length; j++) {
double x21 = polygon[j].latitude;
double y21 = polygon[j].longitude;
double x22 = polygon[(j + 1) == polygon.length ? 0 : (j + 1)].latitude;
double y22 = polygon[(j + 1) == polygon.length ? 0 : (j + 1)].longitude;
double maxx2 = max(x21, x22), maxy2 = max(y21, y22);
double minx2 = min(x21, x22), miny2 = min(y21, y22);
if ((x1 == x21 && y1 == y21) || (x2 == x22 && y2 == y22) || (x1 == x22 && y1 == y22) || (x2 == x21 && y2 == y21))
continue;
if (minx1 > maxx2 || maxx1 < minx2 || miny1 > maxy2 || maxy1 < miny2)
continue; // The moment when the lines have one common vertex...
double dx1 = x2-x1, dy1 = y2-y1; // The length of the projections of the first line on the x and y axes
double dx2 = x22-x21, dy2 = y22-y21; // The length of the projections of the second line on the x and y axes
double dxx = x1-x21, dyy = y1-y21;
double div = dy2 * dx1 - dx2 * dy1;
double mul1 = dx1 * dyy - dy1 * dxx;
double mul2 = dx2 * dyy - dy2 * dxx;
if (div == 0)
continue; // Lines are parallel...
if (div > 0) {
if (mul1 < 0 || mul1 > div)
continue; // The first segment intersects beyond its boundaries...
if (mul2 < 0 || mul2 > div)
continue; // // The second segment intersects beyond its borders...
}
else{
if (-mul1 < 0 || -mul1 > -div)
continue; // The first segment intersects beyond its boundaries...
if (-mul2 < 0 || -mul2 > -div)
continue; // The second segment intersects beyond its borders...
}
return true;
}
}
return false;
}

Best Way to Add 3 Numbers (or 4, or N) in Java - Kahan Sums?

I found a completely different answer to this question, the whole original question makes no sense anymore. However, the answer way be useful, so I modify it a bit...
I want to sum up three double numbers, say a, b, and c, in the most numerically stable way possible.
I think using a Kahan Sum would be the way to go.
However, a strange thought occured to me: Would it make sense to:
First sum up a, b, and c and remember the (absolute value of the) compensation.
Then sum up a, c, b
If the (absolute value of the) compensation of the second sum is smaller, use this sum instead.
Proceed similar with b, a, c and other permutations of the numbers.
Return the sum with the smallest associated absolute compensation.
Would I get a more "stable" Addition of three numbers this way? Or does the order of numbers in the sum have no (use-able) impact on the compensation left at the end of the Summation? With (use-able) I mean to ask whether the compensation value itself is stable enough to contain Information that I can use?
(I am using the Java programming language, although I think this does not matter here.)
Many thanks,
Thomas.
I think I found a much more reliable way to solve the "Add 3" (or "Add 4" or "Add N" numbers problem.
First of all, I implemented my idea from the original post. It resulted into quite some big code which seemed, initially, to work. However, it failed in the following case: add Double.MAX_VALUE, 1, and -Double.MAX_VALUE. The result was 0.
#njuffa's comments inspired me dig somewhat deeper and at http://code.activestate.com/recipes/393090-binary-floating-point-summation-accurate-to-full-p/, I found that in Python, this problem has been solved quite nicely. To see the full code, I downloaded the Python source (Python 3.5.1rc1 - 2015-11-23) from https://www.python.org/getit/source/, where we can find the following method (under PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2):
static PyObject*
math_fsum(PyObject *self, PyObject *seq)
{
PyObject *item, *iter, *sum = NULL;
Py_ssize_t i, j, n = 0, m = NUM_PARTIALS;
double x, y, t, ps[NUM_PARTIALS], *p = ps;
double xsave, special_sum = 0.0, inf_sum = 0.0;
volatile double hi, yr, lo;
iter = PyObject_GetIter(seq);
if (iter == NULL)
return NULL;
PyFPE_START_PROTECT("fsum", Py_DECREF(iter); return NULL)
for(;;) { /* for x in iterable */
assert(0 <= n && n <= m);
assert((m == NUM_PARTIALS && p == ps) ||
(m > NUM_PARTIALS && p != NULL));
item = PyIter_Next(iter);
if (item == NULL) {
if (PyErr_Occurred())
goto _fsum_error;
break;
}
x = PyFloat_AsDouble(item);
Py_DECREF(item);
if (PyErr_Occurred())
goto _fsum_error;
xsave = x;
for (i = j = 0; j < n; j++) { /* for y in partials */
y = p[j];
if (fabs(x) < fabs(y)) {
t = x; x = y; y = t;
}
hi = x + y;
yr = hi - x;
lo = y - yr;
if (lo != 0.0)
p[i++] = lo;
x = hi;
}
n = i; /* ps[i:] = [x] */
if (x != 0.0) {
if (! Py_IS_FINITE(x)) {
/* a nonfinite x could arise either as
a result of intermediate overflow, or
as a result of a nan or inf in the
summands */
if (Py_IS_FINITE(xsave)) {
PyErr_SetString(PyExc_OverflowError,
"intermediate overflow in fsum");
goto _fsum_error;
}
if (Py_IS_INFINITY(xsave))
inf_sum += xsave;
special_sum += xsave;
/* reset partials */
n = 0;
}
else if (n >= m && _fsum_realloc(&p, n, ps, &m))
goto _fsum_error;
else
p[n++] = x;
}
}
if (special_sum != 0.0) {
if (Py_IS_NAN(inf_sum))
PyErr_SetString(PyExc_ValueError,
"-inf + inf in fsum");
else
sum = PyFloat_FromDouble(special_sum);
goto _fsum_error;
}
hi = 0.0;
if (n > 0) {
hi = p[--n];
/* sum_exact(ps, hi) from the top, stop when the sum becomes
inexact. */
while (n > 0) {
x = hi;
y = p[--n];
assert(fabs(y) < fabs(x));
hi = x + y;
yr = hi - x;
lo = y - yr;
if (lo != 0.0)
break;
}
/* Make half-even rounding work across multiple partials.
Needed so that sum([1e-16, 1, 1e16]) will round-up the last
digit to two instead of down to zero (the 1e-16 makes the 1
slightly closer to two). With a potential 1 ULP rounding
error fixed-up, math.fsum() can guarantee commutativity. */
if (n > 0 && ((lo < 0.0 && p[n-1] < 0.0) ||
(lo > 0.0 && p[n-1] > 0.0))) {
y = lo * 2.0;
x = hi + y;
yr = x - hi;
if (y == yr)
hi = x;
}
}
sum = PyFloat_FromDouble(hi);
_fsum_error:
PyFPE_END_PROTECT(hi)
Py_DECREF(iter);
if (p != ps)
PyMem_Free(p);
return sum;
}
This summation method is different from Kahan's method, it uses a variable number of compensation variables. When adding the ith number, at most i additional compensation variables (stored in the array p) get used. This means if I want to add 3 numbers, I may need 3 additional variables. For 4 numbers, I may need 4 additional variables. Since the number of used variables may increase from n to n+1 only after the nth summand is loaded, I can translate the above code to Java as follows:
/**
* Compute the exact sum of the values in the given array
* {#code summands} while destroying the contents of said array.
*
* #param summands
* the summand array – will be summed up and destroyed
* #return the accurate sum of the elements of {#code summands}
*/
private static final double __destructiveSum(final double[] summands) {
int i, j, n;
double x, y, t, xsave, hi, yr, lo;
boolean ninf, pinf;
n = 0;
lo = 0d;
ninf = pinf = false;
for (double summand : summands) {
xsave = summand;
for (i = j = 0; j < n; j++) {
y = summands[j];
if (Math.abs(summand) < Math.abs(y)) {
t = summand;
summand = y;
y = t;
}
hi = summand + y;
yr = hi - summand;
lo = y - yr;
if (lo != 0.0) {
summands[i++] = lo;
}
summand = hi;
}
n = i; /* ps[i:] = [summand] */
if (summand != 0d) {
if ((summand > Double.NEGATIVE_INFINITY)
&& (summand < Double.POSITIVE_INFINITY)) {
summands[n++] = summand;// all finite, good, continue
} else {
if (xsave <= Double.NEGATIVE_INFINITY) {
if (pinf) {
return Double.NaN;
}
ninf = true;
} else {
if (xsave >= Double.POSITIVE_INFINITY) {
if (ninf) {
return Double.NaN;
}
pinf = true;
} else {
return Double.NaN;
}
}
n = 0;
}
}
}
if (pinf) {
return Double.POSITIVE_INFINITY;
}
if (ninf) {
return Double.NEGATIVE_INFINITY;
}
hi = 0d;
if (n > 0) {
hi = summands[--n];
/*
* sum_exact(ps, hi) from the top, stop when the sum becomes inexact.
*/
while (n > 0) {
x = hi;
y = summands[--n];
hi = x + y;
yr = hi - x;
lo = y - yr;
if (lo != 0d) {
break;
}
}
/*
* Make half-even rounding work across multiple partials. Needed so
* that sum([1e-16, 1, 1e16]) will round-up the last digit to two
* instead of down to zero (the 1e-16 makes the 1 slightly closer to
* two). With a potential 1 ULP rounding error fixed-up, math.fsum()
* can guarantee commutativity.
*/
if ((n > 0) && (((lo < 0d) && (summands[n - 1] < 0d)) || //
((lo > 0d) && (summands[n - 1] > 0d)))) {
y = lo * 2d;
x = hi + y;
yr = x - hi;
if (y == yr) {
hi = x;
}
}
}
return hi;
}
This function will take the array summands and add up the elements while simultaneously using it to store the compensation variables. Since we load the summand at index i before the array element at said index may become used for compensation, this will work.
Since the array will be small if the number of variables to add is small and won't escape the scope of our method, I think there is a decent chance that it will be allocated directly on the stack by the JIT, which may make the code quite fast.
I admit that I did not fully understand why the authors of the original code handled infinities, overflows, and NaNs the way they did. Here my code deviates from the original. (I hope I did not mess it up.)
Either way, I can now sum up 3, 4, or n double numbers by doing:
public static final double add3(final double x0, final double x1,
final double x2) {
return __destructiveSum(new double[] { x0, x1, x2 });
}
public static final double add4(final double x0, final double x1,
final double x2, final double x3) {
return __destructiveSum(new double[] { x0, x1, x2, x3 });
}
If I want to sum up 3 or 4 long numbers and obtain the precise result as double, I will have to deal with the fact that doubles can only represent longs in -9007199254740992..9007199254740992L. But this can easily be done by splitting each long into two parts:
public static final long add3(final long x0, final long x1,
final long x2) {
double lx;
return __destructiveSum(new long[] {new double[] { //
lx = x0, //
(x0 - ((long) lx)), //
lx = x1, //
(x1 - ((long) lx)), //
lx = x2, //
(x2 - ((long) lx)), //
});
}
public static final long add4(final long x0, final long x1,
final long x2, final long x3) {
double lx;
return __destructiveSum(new long[] {new double[] { //
lx = x0, //
(x0 - ((long) lx)), //
lx = x1, //
(x1 - ((long) lx)), //
lx = x2, //
(x2 - ((long) lx)), //
lx = x3, //
(x3 - ((long) lx)), //
});
}
I think this should be about right. At least I can now add Double.MAX_VALUE, 1, and -Double.MAX_VALUE and get 1 as result.

Determine if a point reside inside a leaflet polygon

Suppose I Draw a polygan using leaflet like in the follow demo:
http://leaflet.github.io/Leaflet.draw/
My question is how I can determine if a given point reside inside the polygon or not.
Use the Ray Casting algorithm for checking if a point (marker) lies inside of a polygon:
function isMarkerInsidePolygon(marker, poly) {
var polyPoints = poly.getLatLngs();
var x = marker.getLatLng().lat, y = marker.getLatLng().lng;
var inside = false;
for (var i = 0, j = polyPoints.length - 1; i < polyPoints.length; j = i++) {
var xi = polyPoints[i].lat, yi = polyPoints[i].lng;
var xj = polyPoints[j].lat, yj = polyPoints[j].lng;
var intersect = ((yi > y) != (yj > y))
&& (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
if (intersect) inside = !inside;
}
return inside;
};
See jsfiddle for example.
Original source for the code: https://github.com/substack/point-in-polygon/blob/master/index.js
See also 2014's similar answer, https://stackoverflow.com/a/41138512/287948
Here is the modified(with #Sumit hints) version of #gusper answer that worked for me:(i had donuts)
function isMarkerInsidePolygon(marker, poly) {
var inside = false;
var x = marker.getLatLng().lat, y = marker.getLatLng().lng;
for (var ii=0;ii<poly.getLatLngs().length;ii++){
var polyPoints = poly.getLatLngs()[ii];
for (var i = 0, j = polyPoints.length - 1; i < polyPoints.length; j = i++) {
var xi = polyPoints[i].lat, yi = polyPoints[i].lng;
var xj = polyPoints[j].lat, yj = polyPoints[j].lng;
var intersect = ((yi > y) != (yj > y))
&& (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
if (intersect) inside = !inside;
}
}
return inside;
};
I found that none of the above answers worked for counting markers within non-contiguous polygons. Here's an example polygon where the above functions returned 0 markers inside:
For anyone who needs to do that, the Leaflet.PointInPolygon package worked for me: https://github.com/hayeswise/Leaflet.PointInPolygon
It's a bit slow, but it seems to be accurate.

imregionalmax matlab function's equivalent in opencv

I have an image of connected components(circles filled).If i want to segment them i can use watershed algorithm.I prefer writing my own function for watershed instead of using the inbuilt function in OPENCV.I have successfu How do i find the regionalmax of objects using opencv?
I wrote a function myself. My results were quite similar to MATLAB, although not exact. This function is implemented for CV_32F but it can easily be modified for other types.
I mark all the points that are not part of a minimum region by checking all the neighbors. The remaining regions are either minima, maxima or areas of inflection.
I use connected components to label each region.
I check each region for any point belonging to a maxima, if yes then I push that label into a vector.
Finally I sort the bad labels, erase all duplicates and then mark all the points in the output as not minima.
All that remains are the regions of minima.
Here is the code:
// output is a binary image
// 1: not a min region
// 0: part of a min region
// 2: not sure if min or not
// 3: uninitialized
void imregionalmin(cv::Mat& img, cv::Mat& out_img)
{
// pad the border of img with 1 and copy to img_pad
cv::Mat img_pad;
cv::copyMakeBorder(img, img_pad, 1, 1, 1, 1, IPL_BORDER_CONSTANT, 1);
// initialize binary output to 2, unknown if min
out_img = cv::Mat::ones(img.rows, img.cols, CV_8U)+2;
// initialize pointers to matrices
float* in = (float *)(img_pad.data);
uchar* out = (uchar *)(out_img.data);
// size of matrix
int in_size = img_pad.cols*img_pad.rows;
int out_size = img.cols*img.rows;
int x, y;
for (int i = 0; i < out_size; i++) {
// find x, y indexes
y = i % img.cols;
x = i / img.cols;
neighborCheck(in, out, i, x, y, img_pad.cols); // all regions are either min or max
}
cv::Mat label;
cv::connectedComponents(out_img, label);
int* lab = (int *)(label.data);
in = (float *)(img.data);
in_size = img.cols*img.rows;
std::vector<int> bad_labels;
for (int i = 0; i < out_size; i++) {
// find x, y indexes
y = i % img.cols;
x = i / img.cols;
if (lab[i] != 0) {
if (neighborCleanup(in, out, i, x, y, img.rows, img.cols) == 1) {
bad_labels.push_back(lab[i]);
}
}
}
std::sort(bad_labels.begin(), bad_labels.end());
bad_labels.erase(std::unique(bad_labels.begin(), bad_labels.end()), bad_labels.end());
for (int i = 0; i < out_size; ++i) {
if (lab[i] != 0) {
if (std::find(bad_labels.begin(), bad_labels.end(), lab[i]) != bad_labels.end()) {
out[i] = 0;
}
}
}
}
int inline neighborCleanup(float* in, uchar* out, int i, int x, int y, int x_lim, int y_lim)
{
int index;
for (int xx = x - 1; xx < x + 2; ++xx) {
for (int yy = y - 1; yy < y + 2; ++yy) {
if (((xx == x) && (yy==y)) || xx < 0 || yy < 0 || xx >= x_lim || yy >= y_lim)
continue;
index = xx*y_lim + yy;
if ((in[i] == in[index]) && (out[index] == 0))
return 1;
}
}
return 0;
}
void inline neighborCheck(float* in, uchar* out, int i, int x, int y, int x_lim)
{
int indexes[8], cur_index;
indexes[0] = x*x_lim + y;
indexes[1] = x*x_lim + y+1;
indexes[2] = x*x_lim + y+2;
indexes[3] = (x+1)*x_lim + y+2;
indexes[4] = (x + 2)*x_lim + y+2;
indexes[5] = (x + 2)*x_lim + y + 1;
indexes[6] = (x + 2)*x_lim + y;
indexes[7] = (x + 1)*x_lim + y;
cur_index = (x + 1)*x_lim + y+1;
for (int t = 0; t < 8; t++) {
if (in[indexes[t]] < in[cur_index]) {
out[i] = 0;
break;
}
}
if (out[i] == 3)
out[i] = 1;
}
The following listing is a function similar to Matlab's "imregionalmax". It looks for at most nLocMax local maxima above threshold, where the found local maxima are at least minDistBtwLocMax pixels apart. It returns the actual number of local maxima found. Notice that it uses OpenCV's minMaxLoc to find global maxima. It is "opencv-self-contained" except for the (easy to implement) function vdist, which computes the (euclidian) distance between points (r,c) and (row,col).
input is one-channel CV_32F matrix, and locations is nLocMax (rows) by 2 (columns) CV_32S matrix.
int imregionalmax(Mat input, int nLocMax, float threshold, float minDistBtwLocMax, Mat locations)
{
Mat scratch = input.clone();
int nFoundLocMax = 0;
for (int i = 0; i < nLocMax; i++) {
Point location;
double maxVal;
minMaxLoc(scratch, NULL, &maxVal, NULL, &location);
if (maxVal > threshold) {
nFoundLocMax += 1;
int row = location.y;
int col = location.x;
locations.at<int>(i,0) = row;
locations.at<int>(i,1) = col;
int r0 = (row-minDistBtwLocMax > -1 ? row-minDistBtwLocMax : 0);
int r1 = (row+minDistBtwLocMax < scratch.rows ? row+minDistBtwLocMax : scratch.rows-1);
int c0 = (col-minDistBtwLocMax > -1 ? col-minDistBtwLocMax : 0);
int c1 = (col+minDistBtwLocMax < scratch.cols ? col+minDistBtwLocMax : scratch.cols-1);
for (int r = r0; r <= r1; r++) {
for (int c = c0; c <= c1; c++) {
if (vdist(Point2DMake(r, c),Point2DMake(row, col)) <= minDistBtwLocMax) {
scratch.at<float>(r,c) = 0.0;
}
}
}
} else {
break;
}
}
return nFoundLocMax;
}
I do not know if it is what you want, but in my answer to this post, I gave some code to find local maxima (peaks) in a grayscale image (resulting from distance transform).
The approach relies on subtracting the original image from the dilated image and finding the zero pixels).
I hope it helps,
Good luck
I had the same problem some time ago, and the solution was to reimplement the imregionalmax algorithm in OpenCV/Cpp. It is not that complicated, because you can find the C++ source code of the function in the Matlab distribution. (somewhere in toolbox). All you have to do is to read carefully and understand the algorithm described there. Then rewrite it or remove the matlab-specific checks and you'll have it.

Finding Line segment-Rectangle intersection point

Simply put, I have:
A viewport rectangle where (0,0) is the bottom-left corner, (1,1) is the top-right and (0.5,0.5) is the centre of the screen.
A point (a,b) which is outside of the rectangle.
This is in viewport coordinates so +X is right, +Y is up on the screen.
And I need a function which takes these parameters and returns the point on the edge of the rectangle where the line (between the centre of the rectangle (0.5,0.5) and the point (a,b)) intersects it.
I know how to do this on paper with given coordinates but I just can't figure it out when it comes down to code. Also, I realise questions like this have been addressed in different threads - but I can't find a simple input to output function anywhere.
I'm doing this in the Unity3D engine so preferably in Javascript but any language or pseudocode would be a great help as I can probably hand convert it.
EDIT
To clarify, I'm looking for something like:
function IntersectFromViewportCenter(x : float, y : float) {
...
return Point(x1, y1);
}
Where (x,y) is the point outside of the circle and (x1,y1) is the intersection point.
Thanks
Shift all system to be centered at point (0,0). Calc intersection of ray from origin to (shifted) point (x',y') with box (-1,-1)-(1,1). Scale and shift back. I did not consider trivial case with point inside the box ( is it needed?)
x = x - 0.5
y = y - 0.5
if Abs(x) >= Abs(y) then //vertical box edge
y1 = y/x //care with case both y and x = 0
x1 = Sign(x) //+-1
else // horizontal edge
x1 = x/y
y1 = Sign(y)
x1 = 0.5*x1 + 0.5
y1 = 0.5*y1 + 0.5
Since some general Line/Rect approaches have been provided, here is an approach that is optimized to avoid Raycasts in trivial cases (completely inside or completely outside the Rect): https://gist.github.com/JohannesMP/50dad3175bf2925df508b642091e41c4
It also efficiently provides both the entry and exit point (necessary in my use case):
Here is a basic overview of this approach:
Divide the area around the rect into Sectors as follows (Where S4 is the rect itself):
S0| S1 |S2
--+----+-- ^
S3| S4 |S5 |
--+----+-- y
S6| S7 |S8 x-->
Given the sectors where the line segment begins and ends, we know what raycasts need to be performed (Ex: S0-S2 never needs to raycast, while S4-S1 only needs to raycast the top edge, etc)
Pre-compute this data and store it in a small 9x9 static array. We are basically using a bit of static memory to avoid conditional checks.
At runtime find the Sector that the start and end points of the line occupy, and use the result to Raycast only the necessary edges of the Rect.
Additionally you can simplify the raycast into one dimension if you handle vertical and horizontal lines separately.
In my personal use case (lots of line segments, where the vast majority are either completely inside or completely outside the Rect) this approach is faster than the general case, since Raycasts are only performed when necessary.
MBo has the right idea. Here's a way to implement in in Unity. I don't think UnityScript is worth using – in particular, it doesn't support extension methods – so you really should switch languages. (Also, Unity is actually not named Unity3D.)
This script can go anywhere in the Project:
using UnityEngine;
public static class UnityEngineExtensions {
public static Vector2 Abs(this Vector2 vector) {
for (int i = 0; i < 2; ++i) vector[i] = Mathf.Abs(vector[i]);
return vector;
}
public static Vector2 DividedBy(this Vector2 vector, Vector2 divisor) {
for (int i = 0; i < 2; ++i) vector[i] /= divisor[i];
return vector;
}
public static Vector2 Max(this Rect rect) {
return new Vector2(rect.xMax, rect.yMax);
}
public static Vector2 IntersectionWithRayFromCenter(this Rect rect, Vector2 pointOnRay) {
Vector2 pointOnRay_local = pointOnRay - rect.center;
Vector2 edgeToRayRatios = (rect.Max() - rect.center).DividedBy(pointOnRay_local.Abs());
return (edgeToRayRatios.x < edgeToRayRatios.y) ?
new Vector2(pointOnRay_local.x > 0 ? rect.xMax : rect.xMin,
pointOnRay_local.y * edgeToRayRatios.x + rect.center.y) :
new Vector2(pointOnRay_local.x * edgeToRayRatios.y + rect.center.x,
pointOnRay_local.y > 0 ? rect.yMax : rect.yMin);
}
}
Attach this other script to a Game Object, and set its variables in the Inspector.
#pragma warning disable 0649
using System;
using UnityEngine;
public class VisualizeRectIntersectionWithRayFromCenter : MonoBehaviour {
[SerializeField] Rect rect;
[SerializeField] Vector2 point;
[Serializable] class Colors {
public Color rect, point, intersection;
} [SerializeField] Colors colors;
void OnDrawGizmos() {
Gizmos.color = colors.rect;
Vector2[] corners = {new Vector2(rect.xMin, rect.yMin), new Vector2(rect.xMin, rect.yMax),
rect.Max(), new Vector2(rect.xMax, rect.yMin)};
int i = 0;
while (i < 3) Gizmos.DrawLine(corners[i], corners[++i]);
Gizmos.DrawLine(corners[3], corners[0]);
Gizmos.color = colors.point;
Gizmos.DrawLine(rect.center, point);
Gizmos.color = colors.intersection;
Gizmos.DrawLine(rect.center, rect.IntersectionWithRayFromCenter(pointOnRay: point));
}
}
bool LineRectIntersection(Vector2 lineStartPoint, Vector2 lineEndPoint, Rect rectangle, ref double resultX, ref double resultY)
{
Vector2 minXLinePoint = (lineStartPoint.x <= lineEndPoint.x) ? (lineStartPoint) : (lineEndPoint);
Vector2 maxXLinePoint = (lineStartPoint.x <= lineEndPoint.x) ? (lineEndPoint) : (lineStartPoint);
Vector2 minYLinePoint = (lineStartPoint.y <= lineEndPoint.y) ? (lineStartPoint) : (lineEndPoint);
Vector2 maxYLinePoint = (lineStartPoint.y <= lineEndPoint.y) ? (lineEndPoint) : (lineStartPoint);
double rectMaxX = rectangle.xMax;
double rectMinX = rectangle.xMin;
double rectMaxY = rectangle.yMax;
double rectMinY = rectangle.yMin;
if (minXLinePoint.x <= rectMaxX && rectMaxX <= maxXLinePoint.x)
{
double m = (maxXLinePoint.y - minXLinePoint.y) / (maxXLinePoint.x - minXLinePoint.x);
double intersectionY = ((rectMaxX - ((double)minXLinePoint.x)) * m) + ((double)minXLinePoint.y);
if(minYLinePoint.y <= intersectionY && intersectionY <= maxYLinePoint.y)
{
resultX = rectMaxX;
resultY = intersectionY;
return true;
}
}
if (minXLinePoint.x <= rectMinX && rectMinX <= maxXLinePoint.x)
{
double m = (maxXLinePoint.y - minXLinePoint.y) / (maxXLinePoint.x - minXLinePoint.x);
double intersectionY = ((rectMinX - ((double)minXLinePoint.x)) * m) + ((double)minXLinePoint.y);
if (minYLinePoint.y <= intersectionY && intersectionY <= maxYLinePoint.y)
{
resultX = rectMinX;
resultY = intersectionY;
return true;
}
}
if (minYLinePoint.y <= rectMaxY && rectMaxY <= maxYLinePoint.y)
{
double rm = (maxYLinePoint.x - minYLinePoint.x) / (maxYLinePoint.y - minYLinePoint.y);
double intersectionX = ((rectMaxY - ((double)minYLinePoint.y)) * rm) + ((double)minYLinePoint.x);
if (minXLinePoint.x <= intersectionX && intersectionX <= maxXLinePoint.x)
{
resultX = intersectionX;
resultY = rectMaxY;
return true;
}
}
if (minYLinePoint.y <= rectMinY && rectMinY <= maxYLinePoint.y)
{
double rm = (maxYLinePoint.x - minYLinePoint.x) / (maxYLinePoint.y - minYLinePoint.y);
double intersectionX = ((rectMinY - ((double)minYLinePoint.y)) * rm) + ((double)minYLinePoint.x);
if (minXLinePoint.x <= intersectionX && intersectionX <= maxXLinePoint.x)
{
resultX = intersectionX;
resultY = rectMinY;
return true;
}
}
return false;
}
#chakmeshma, your solution is almost correct, but you must also check whether the intersection point is within the rect to avoid border cases:
private static bool LineRectIntersection(Vector2 lineStartPoint, Vector2 lineEndPoint, Rect rectangle, ref Vector2 result)
{
Vector2 minXLinePoint = lineStartPoint.x <= lineEndPoint.x ? lineStartPoint : lineEndPoint;
Vector2 maxXLinePoint = lineStartPoint.x <= lineEndPoint.x ? lineEndPoint : lineStartPoint;
Vector2 minYLinePoint = lineStartPoint.y <= lineEndPoint.y ? lineStartPoint : lineEndPoint;
Vector2 maxYLinePoint = lineStartPoint.y <= lineEndPoint.y ? lineEndPoint : lineStartPoint;
double rectMaxX = rectangle.xMax;
double rectMinX = rectangle.xMin;
double rectMaxY = rectangle.yMax;
double rectMinY = rectangle.yMin;
if (minXLinePoint.x <= rectMaxX && rectMaxX <= maxXLinePoint.x)
{
double m = (maxXLinePoint.y - minXLinePoint.y) / (maxXLinePoint.x - minXLinePoint.x);
double intersectionY = ((rectMaxX - minXLinePoint.x) * m) + minXLinePoint.y;
if (minYLinePoint.y <= intersectionY && intersectionY <= maxYLinePoint.y
&& rectMinY <= intersectionY && intersectionY <= rectMaxY)
{
result = new Vector2((float)rectMaxX, (float)intersectionY);
return true;
}
}
if (minXLinePoint.x <= rectMinX && rectMinX <= maxXLinePoint.x)
{
double m = (maxXLinePoint.y - minXLinePoint.y) / (maxXLinePoint.x - minXLinePoint.x);
double intersectionY = ((rectMinX - minXLinePoint.x) * m) + minXLinePoint.y;
if (minYLinePoint.y <= intersectionY && intersectionY <= maxYLinePoint.y
&& rectMinY <= intersectionY && intersectionY <= rectMaxY)
{
result = new Vector2((float)rectMinX, (float)intersectionY);
return true;
}
}
if (minYLinePoint.y <= rectMaxY && rectMaxY <= maxYLinePoint.y)
{
double rm = (maxYLinePoint.x - minYLinePoint.x) / (maxYLinePoint.y - minYLinePoint.y);
double intersectionX = ((rectMaxY - minYLinePoint.y) * rm) + minYLinePoint.x;
if (minXLinePoint.x <= intersectionX && intersectionX <= maxXLinePoint.x
&& rectMinX <= intersectionX && intersectionX <= rectMaxX)
{
result = new Vector2((float)intersectionX, (float)rectMaxY);
return true;
}
}
if (minYLinePoint.y <= rectMinY && rectMinY <= maxYLinePoint.y)
{
double rm = (maxYLinePoint.x - minYLinePoint.x) / (maxYLinePoint.y - minYLinePoint.y);
double intersectionX = ((rectMinY - minYLinePoint.y) * rm) + minYLinePoint.x;
if (minXLinePoint.x <= intersectionX && intersectionX <= maxXLinePoint.x
&& rectMinX <= intersectionX && intersectionX <= rectMaxX)
{
result = new Vector2((float)intersectionX, (float)rectMinY);
return true;
}
}
return false;
}
}