I am writing a program in c #, help with math. We have the coordinates of the vector, which originates from a point (0; 0) and the coordinates of a point on the plane. We rotate the vector 35 degrees to the left and 35 degrees to the right relative to the initial position. It turns out 2 vectors, the angle between which is 70 degrees (sector of a circle). It is necessary to determine whether a point lying on a plane falls into this sector. DO NOT confuse the segment and sector!
- 2What are the difficulties? Variant in the forehead: by distance to a point, we determine whether it falls into a circle or not. If it falls, then the coordinates of the point find the angle between the x-axis and the straight line passing through the point and the beginning of the circle. Next, compare this angle with the angles obtained from the rotation of your vector. - BOPOH
- The task to translate into polar coordinates. Move the coordinates of the point from the Cartesian coordinates to the polar (Google to help you), after which the answer to the question of the problem becomes trivial. - AnT
4 answers
The task is actually interesting because it is not so easy to determine whether a ray lies between two other rays. If you compare the value of the polar angle, you will have to correctly handle the case when the "cut" of the set of angle values lies between your vectors. Example: if yours are in the gap (-180, 180], then beam 180 lies between rays 179 and -179.
If the rays are given by angles, we will need the operation of normalizing the angle: bringing the angle to the desired interval.
double mod(double n, double d) { if (d <= 0) throw new ArgumentException("denominator is negative"); double remainder = n % d; return (remainder >= 0) ? remainder : (remainder + d); } // return value is the same as =angle=, but in interval [basis, basis + 2*pi) double normalize(double angle, double basis) { var diff = mod(angle - basis, 2 * Math.PI); // diff is in [0, 2pi) return basis + diff; } Now, let these rays be given by the angles α₁ and α₂, and the desired ray - by angle β. What does it mean that the ray lies between these rays? After all, two beams set two angles. There are two possible answers:
β lies inside one of the angles that is less than π.
Get the code:
bool isWithinSmallerAngle(double a1, double a2, double b) { var spread = normalize(a2 - a1, 0); if (spread <= Math.PI) { var diffTo1 = normalize(b - a1, 0); return diffTo1 > 0 && diffTo1 < spread; } else { spread = 2 * Math.PI - spread; var diffTo2 = normalize(b - a2, 0); return diffTo2 > 0 && diffTo2 < spread; } }β lies inside the angle considered in the direction of increase from α₁ to α₂.
Get the code:
bool isWithinDirectedAngle(double a1, double a2, double b) { var spread = normalize(a2 - a1, 0); var diffTo1 = normalize(b - a1, 0); return diffTo1 > 0 && diffTo1 < spread; }
The operation of converting to polar coordinates is rather heavy (it is necessary to use the atan2 function), so I would like to avoid it if possible. If the rays are given to us as codirectional vectors, then everything can be calculated as follows. We need the operation “to find out whether the vector data are in one half-plane with respect to the third vector” (I assume that there is an obvious vector structure):
double dotProduct(vector v1, vector v2) { return v1.x * v2.x + v1.y * v2.y; } double crossProduct(vector v1, vector v2) { return v1.x * v2.y - v1.y * v2.x; } bool isInSameHalfPlane(vector v1, vector v2, vector axis) { var p1 = crossProduct(axis, v1); var p2 = crossProduct(axis, v2); return Math.Sign(p1) * Math.Sign(p2) == 1; } We again have two possible determinations of whether the ray lies between the other two.
The ray lies in the first quadrant of the oblique coordinate system defined by the vectors v ₁ and v ₂.
// для параллельных векторов результат неопределён bool isWithinPositiveQuadrant(vector v1, vector v2, vector w) { return isInSameHalfPlane(v2, w, v1) && isInSameHalfPlane(v1, w, v2); }The ray lies in the corner swept up by a positive rotation from the vector v ₁ to the vector v ₂.
bool isDirectedAngle(vector v1, vector v2, vector w) { var crossSign = Math.Sign(crossproduct(v1, v2)) if (crossSign > 0) { // угол от v1 к v2 меньше развёрнутого return isInSameHalfPlane(v2, w, v1) && isInSameHalfPlane(v1, w, v2); } if (crossSign < 0) { // угол от v1 к v2 больше развёрнутого return !isInSameHalfPlane(v2, w, v1) && !isInSameHalfPlane(v1, w, v2); } // здесь вектора параллельны. проверим, сонаправлены ли они var areCodirected = dotProduct(v1, v2) > 0; if (areCodirected) { // между сонаправленными векторами ничего не может лежать return false; } else { // угол между противопололжно направленными векторами - вся полуплоскость return crossProduct(v1, w) > 0; } }
The part of the task that consists in checking the distances is trivial.
- Difficult something ... Well, you can do without corners, sort of? - Qwertiy ♦
- @Qwertiy: I do not know, but how? - VladD
- Although the second method is without corners. - Qwertiy ♦
- Somehow to mix scalar and vector products. If I remember, I will answer :) - Qwertiy ♦
- @Qwertiy: Well, about the vector art I have the whole second part of the answer.
dotProductused only once and randomly :) - VladD
We need to check two things:
- the specified point lies in a circle
- the angle between the radius vector of the specified point and the vector defining the sector does not exceed 35 ° (modulo).
The first check is trivial. I'll tell you about the second.
If the angle does not exceed 35 ° - then its cosine should not be less than cos 35° , the reverse is also true. Here, cosine symmetry greatly helps us ( cos -x = cos x ).
The cosine of the angle between the vectors can be found as their scalar product divided by the product of vector lengths.
As a result, we get the following test (here (x0, y0) is the vector that defines the sector, and (x, y) the coordinates of the point):
bool IsInSector(double x0, double y0, double x, double y) { var rq0 = x0*x0 + y0*y0; var rq = x*x + y*y; return rq0 >= rq && (x0*x + y0*y)/Math.Sqrt(rq0*rq) >= Math.Cos(35.0/180.0*Math.PI); } - Yep Perhaps it would be more effective not to take the root, but to check the sign separately, and compare the squares. - VladD
- Yes. would be more effective. But did not complicate the code. - Pavel Mayorov
The formula for the tangent of the half argument is: tg (fi / 2) = sin (fi) / (1 + cos (fi)) = y / (x + r) = s, and in the context of this problem, r = sqrt (x ^ 2 + y ^ 2) (1), x, y is the length of the vector (or the radius vector of a point) and its projection on the axis. In this case, the angle fi is determined up to a period of 360 degrees.
The "noticing" vector (u, v) has the length w = sqrt (u ^ 2 + v ^ 2) (2) and the initial angle alpha, for which tg (alpha / 2) = u / (v + w) = t.
The parameters of the radius vector of the point (x, y) are defined above. In this case, in accordance with the formula for the difference tangent, for the tangent of half difference of angles, we have
tg ((fi-alpha) / 2) = tg (fi / 2 - alpha / 2) = (st) / (1 + st),
| s - t | <= | 1 + st | tg 17.5 deg, or
| y (v + w) - u (x + r) | <= | (x + r) (u + w) + yu | tg 17.5 deg (3).
The algorithm for checking whether a point (x, y) hits a 35-degree sector of a vector (u, v) has the form:
- We calculate the length r of the radius-vector of a point (x, y) by the formula (1).
- We calculate the length w of the vector (u, v) by the formula (2).
- Compare the values of r and w. If r> w, the point is not in the sector.
- Check condition (3). If it is done - a point in the sector, otherwise - no
Check the entry into the specified range of angles can be as follows:
double AngleDiff(double angle1, double angle2) { return ((((angle1 - angle2) % 360) + 540) % 360) - 180; } bool IsInside(double rayAngle, double alpha, double pointAngle) { return (AngleDiff(pointAngle, rayAngle - alpha) > 0 && AngleDiff(pointAngle, rayAngle + alpha) < 0); } Used as var res = IsInside(vectorAngle, 35.0, pointAngle)
The distance between the points can be obtained as follows:
double GetDistance(double x1, double y1, double x2, double y2) { return Math.Pow(Math.Pow(x1 - x2, 2) + Math.Pow(y1 - y2, 2), 0.5); } That is, check whether the point is in the range of angles and the distance from the center of the circle to the point is within the radius.
- How does your answer differ from VladD's? - Pavel Mayorov
- Do not you see the differences? Seriously? - Dmi7ry
- You have less information - and you also use degrees instead of radians. Neither one nor the other difference, I would not call positive. - Pavel Mayorov
- First, where in the problem statement did you see the radians? There are several times referred to as degrees. It makes no sense to use radians here. - Dmi7ry
- Secondly, what good is that information sheet? There is a convenient short script that performs its task - I brought it up, and also added an example of use. Do I also need to write a sheet of text explaining how this formula is derived? I do not see the point. If the author of the question becomes interested, he will ask, and then I can give a detailed description. - Dmi7ry