Added at 7:00 on December 12th We have corrected the mistakes pointed out in the comments. (We did not consider the case where the minimum orientation of the surveillance camera is less than 0 ° and the maximum is 360 ° or more) I am very sorry that I posted it with insufficient verification.
We have created a surveillance camera that is not bound by common sense and allows people to pass through in front of them.
import java.util.Scanner;
class CameraTest {
public static void main(String[] args) {
//input
Scanner sc = new Scanner(System.in);
//player
double px = sc.nextDouble(); //Player x coordinate
double py = sc.nextDouble(); //Player y coordinate
Player player = new Player(px, py);
//Surveillance camera
int N = sc.nextInt(); //Number of surveillance cameras
for (int n = 0; n < N; n ++) {
//Input / processing for each surveillance camera
double x = sc.nextDouble(); //X coordinate of surveillance camera
double y = sc.nextDouble(); //Y coordinate of surveillance camera
double d = sc.nextDouble(); //Distance visible to surveillance cameras
double e = sc.nextDouble(); //Direction of surveillance camera(direction)Angle representing
double f = sc.nextDouble(); //Viewing angle of surveillance camera
Camera camera = new Camera(x, y, d, e, f);
if (camera.findPlayer(player)) {
System.out.println("I was able to see");
} else {
System.out.println("I couldn't see");
}
}
sc.close();
}
}
class Player {
private double x; //Player x coordinate
private double y; //Player y coordinate
public Player(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() {
return this.x;
}
public double getY() {
return this.y;
}
}
class Camera {
private double x; //X coordinate of surveillance camera
private double y; //Y coordinate of surveillance camera
private double distance; //Distance visible to surveillance cameras
private double dirAngle; //Direction of surveillance camera(direction)Angle representing
private double viewAngle; //Viewing angle of surveillance camera
public Camera(double x, double y, double d, double e, double f) {
this.x = x;
this.y = y;
this.distance = d;
this.dirAngle = e;
this.viewAngle = f;
}
public boolean findPlayer(Player player) {
double px = player.getX();
double py = player.getY();
//Judgment by distance
double d = Math.sqrt(Math.pow(this.x - px, 2) + Math.pow(this.y - py, 2));
if (d > this.distance) {
return false;
}
//Judgment by angle
double a = Math.toDegrees(Math.atan2(py - this.y, px - this.x));
double aMin = this.dirAngle - this.viewAngle/2;
double aMax = this.dirAngle + this.viewAngle/2;
if (a < aMin || aMax < a) {
return false;
}
return true;
}
}
The biggest evil __ in programming this time was that I used the library in an ambiguous manner without careful examination. Don't get me wrong, it's not a bad thing to use a library (rather, it's much more efficient to use it than to create your own weird methods). __ It's bad to use it without looking it up __. Let's see specifically which part was evil. To conclude, it is the part that uses the atan2 method of the library in the judgment by the angle in the findPlayer method of the Camera class. Let's pay attention to the return value of this method.
From the Oracle website * The appearance has been slightly changed to make it easier to read. https://docs.oracle.com/javase/jp/1.4/api/java/lang/Math.html#atan2(double,%20double)
public static double atan2 (double y, double x) (click to open) summary>
Convert Cartesian coordinates (x, y) to polar coordinates (r, theta). This method calculates the arctangent of y / x in the range __-pi to pi __ font> to find the phase theta. As a special case-If either argument is NaN, the result is NaN. -If the first argument is positive zero and the second argument is positive, or the first argument is positive finite and the second argument is positive infinity, the result is positive zero. -If the first argument is negative zero and the second argument is positive, or the first argument is negative finite and the second argument is positive infinity, the result is negative zero. -If the first argument is positive zero and the second argument is negative, or the first argument is positive finite and the second argument is negative infinity, the result is a double value that is closest to pi. Become. -If the first argument is negative zero and the second argument is negative, or the first argument is negative finite and the second argument is negative infinity, the result is a double value that is closest to -pi. Will be. -If the first argument is positive and the second argument is positive or negative zero, or the first argument is positive infinity and the second argument is finite, the result is double, which is the closest approximation to pi / 2. It will be a value. -If the first argument is negative and the second argument is positive or negative zero, or the first argument is negative infinity and the second argument is finite, the result is closest to -pi / 2. It has a double value. -If both arguments are positive infinity, the result is a double value that is the closest approximation to pi / 4. -If the first argument is positive infinity and the second argument is negative infinity, the result is a double value that is closest to 3 * pi / 4. -If the first argument is negative infinity and the second argument is positive infinity, the result is a double value that is closest to -pi / 4. -If both arguments are negative infinity, the result is a double value that is the closest approximation to -3 * pi / 4.
The result must be within 2 ulps of the correctly rounded result. Results are limited to semi-monotonous.
__ Parameters: __ y --Vertical coordinates x --Abscissa Return value: Theta component of a point (r, theta) on polar coordinates that corresponds to a point (x, y) on Cartesian coordinates (Descartes)
The point is that if you just use the inverse function of tan (atan method of the library), you can only know the __direction __ [^ 1] of the vector with the $ x $ and $ y $ components of the argument. You also need to look at the sign of each component to find out the __direction __ [^ 1] of the vector. The atan2 method implements such a wonderful process. [^ 1]: In the world of mathematics and physics, there is a clear distinction between "direction" and "direction". As a rough example, if there is a road connecting two points, the road itself is the "direction", and the distinction between the lane you are in and the opposite lane is the "direction" (north-south "direction", or It's called "direction" in the north and "direction" in the south).
The most important part is the return value shown in bold red, and the range from -pi to pi is the angle expressed by the radian method [^ 2], which is converted to the pi method [^ 2]. When converted (library toDegrees method), it becomes -180 ° to 180 °. If you look a little more closely, when the $ x $ component is -1 and the $ y $ component is 0, the value returned from the atan2 method is converted to the frequency method by the toDegrees method, and it becomes 180.0, so it seems to be facing left. Seems to be 180 ° instead of -180 ° for the vector of. In other words, it will return a value greater than __-180 ° and less than 180 ° __. [^ 2]: Roughly speaking, the arc degree method is the one in which the central angle of the goose shape with a radius of 1 and the arc length of 1 is determined to be 1 radian, and the frequency method is the one we usually use, 1 The circumference is set to 360 °. Since the circumference of radius 1 is 2 $ \ pi $, the 2 $ \ pi $ radians represent an angle as large as 360 °.
I finally got to the core. I first decided that the viewing angle of the surveillance camera would be __0 ° or more and less than 360 ° __. However, the orientation of the player obtained by the program was greater than __-180 ° and less than 180 ° __. In fact, let's use the example that failed last time to check while also displaying the angle that represents the player's orientation.
10 16 8 270 120 a = -90.0 aMin = 210.0, aMax = 330.0 I couldn't see
I see. The surveillance camera covers the range of 210 ° or more and 330 ° or less, but the player is calculated as an angle outside the range, so it was passed through. Now let's consider what to do when the angle representing the player's orientation is outside the viewing angle of the surveillance camera, that is, greater than __-180 ° and less than 0 ° __. In the first place, negative angles are rarely used in everyday life, so some people may be overwhelmed, but once the principle is understood, it is not so difficult. Simply put, it's just a positive angle when it's rotated counterclockwise, and a negative angle when it's rotated clockwise. For example, rotating 90 ° clockwise is ultimately the same as rotating 270 ° counterclockwise. The position rotated by -90 ° is the same as the position rotated by 270 °. Therefore, if it is larger than __-180 ° and less than 0 °, the correct answer is to add 360 ° and correct it to the range of 0 ° or more and less than 360 °.
Problem # 2
Now that we have verified the angle that represents the orientation of the player, the next step is the orientation of the surveillance camera. For example, consider the following example.
- If the orientation of the surveillance camera is 10 ° and the viewing angle is 60 °, the range of orientation of the surveillance camera will be -20 ° to 40 °, so the range of orientation of the surveillance camera should be 0 ° to 40 °. It is necessary to judge by dividing into two, 340 ° to 360 °.
- If the orientation of the surveillance camera is 350 ° and the viewing angle is 60 °, the range of orientation of the surveillance camera will be 320 ° to __380 ° __, so the range of orientation of the surveillance camera will be 320 ° to 360 °. It is necessary to judge by dividing into two, 0 ° to 20 °.
…… I have a headache just thinking about it, but I can't avoid it. You need to add some processing so that you can determine if the player is visible even if it is less than 0 ° or more than 360 °. Now, in the findPlayer method, rewrite the part that makes the judgment by angle as follows.
public boolean findPlayer(Player player) { //Omission //Judgment by angle double a = Math.toDegrees(Math.atan2(py - this.y, px - this.x)); if (a < 0) { a += 360.0; } System.out.println("a = " + a); double aMin = this.dirAngle - this.viewAngle/2; if (aMin < 0.0) { //When the minimum camera orientation is negative, aMin+Determine if it is visible in the range of 360 to 360 aMin += 360; if (aMin <= a && a < 360.0) { return true; } //After that, aMin+You don't have to think about the range of 360-360 aMin = 0.0; } double aMax = this.dirAngle + this.viewAngle/2; if (aMax >= 360.0) { //0 to aMax when the maximum camera orientation is 360 or more-Determine if it is visible in the 360 range aMax -= 360.0; if (0.0 <= a && a <= aMax) { return true; } //After that, 0 to aMax-You don't have to think about the 360 range aMax = 360.0; } System.out.println("aMin = " + aMin + ", aMax = " + aMax); if (aMin <= a && a <= aMax) { return true; } return false; }
As explained in Problem 1, after finding the orientation of the player (variable a), if the value is negative, add 360. This ensures that the player's orientation is always greater than or equal to 0 ° and less than 360 °. Next, after finding the minimum orientation of the camera (variable aMin), if the value is less than 0 °, first determine whether the player can be seen within the range of aMin + 360 ° or more and less than 360 °, and if it can be seen, true. Returns (skips the rest of the process). If it is not visible, it is necessary to judge the remaining range, so the process continues. Since it is no longer necessary to think about the range of aMin + 360 ° or more and less than 360 °, aMin is set to 0 °. As a result, it will be judged later even in the range of 0 ° or more and aMax or less. The same applies to the maximum camera orientation (variable aMax).
3. 3. Complete
It is a program that reflects the verification so far. It was long ……… ~~ Actually, it was a more detour ~~
Reborn player visibility program (click to open) summary>
import java.util.Scanner; class CameraTest { public static void main(String[] args) { //input Scanner sc = new Scanner(System.in); //player double px = sc.nextDouble(); //Player x coordinate double py = sc.nextDouble(); //Player y coordinate Player player = new Player(px, py); //Surveillance camera int N = sc.nextInt(); //Number of surveillance cameras for (int n = 0; n < N; n ++) { //Input / processing for each surveillance camera double x = sc.nextDouble(); //X coordinate of surveillance camera double y = sc.nextDouble(); //Y coordinate of surveillance camera double d = sc.nextDouble(); //Distance visible to surveillance cameras double e = sc.nextDouble(); //Direction of surveillance camera(direction)Angle representing double f = sc.nextDouble(); //Viewing angle of surveillance camera Camera camera = new Camera(x, y, d, e, f); if (camera.findPlayer(player)) { System.out.println("I was able to see"); } else { System.out.println("I couldn't see"); } } sc.close(); } } class Player { private double x; //Player x coordinate private double y; //Player y coordinate public Player(double x, double y) { this.x = x; this.y = y; } public double getX() { return this.x; } public double getY() { return this.y; } } class Camera { private double x; //X coordinate of surveillance camera private double y; //Y coordinate of surveillance camera private double distance; //Distance visible to surveillance cameras private double dirAngle; //Direction of surveillance camera(direction)Angle representing private double viewAngle; //Viewing angle of surveillance camera public Camera(double x, double y, double d, double e, double f) { this.x = x; this.y = y; this.distance = d; this.dirAngle = e; this.viewAngle = f; } public boolean findPlayer(Player player) { double px = player.getX(); double py = player.getY(); //Judgment by distance double d = Math.sqrt(Math.pow(this.x - px, 2) + Math.pow(this.y - py, 2)); if (d > this.distance) { return false; } //Judgment by angle double a = Math.toDegrees(Math.atan2(py - this.y, px - this.x)); if (a < 0) { a += 360.0; } System.out.println("a = " + a); double aMin = this.dirAngle - this.viewAngle/2; if (aMin < 0.0) { //When the minimum camera orientation is negative, aMin+Determine if it is visible in the range of 360 to 360 aMin += 360; System.out.println("aMin = " + aMin + ", aMax = 360.0"); if (aMin <= a && a < 360.0) { return true; } //After that, aMin+You don't have to think about the range of 360-360 aMin = 0.0; } double aMax = this.dirAngle + this.viewAngle/2; if (aMax >= 360.0) { //0 to aMax when the maximum camera orientation is 360 or more-Determine if it is visible in the 360 range aMax -= 360.0; System.out.println("aMin = 0.0, aMax = " + aMax); if (0.0 <= a && a <= aMax) { return true; } //After that, 0 to aMax-You don't have to think about the 360 range aMax = 360.0; } System.out.println("aMin = " + aMin + ", aMax = " + aMax); if (aMin <= a && a <= aMax) { return true; } return false; } }
In the findPlayer method, when the value of the variable a that stores the angle indicating the direction of the player is negative, we just added the process of adding 360. And the revenge result.
10 16 8 270 120 a = 270.0 aMin = 210.0, aMax = 330.0 I was able to see
It's perfect!
4. This lesson
It's hard to mention, but this time I'll write about the points that I especially pulled.
Be sure to check the specifications when using something made by others!
This is the biggest reflection point. I learned about the atan2 method a long time ago, and this time I used it for the first time in a while, but the return value was completely passed through. Rather, I was convinced that it would return at 0 ° or more and less than 360 °. Also, because I was convinced, it took me a long time to notice the cause. To confess further, it is correct to assign the arguments of the atan2 method in the order of $ y $ component and $ x $ component, but at the beginning I put it in the opposite direction. It's a lot. It would be scary to think that this was a development site. The compiler passed, but it didn't do the right thing, I believe I was right ... It's a catastrophe. Of course, I'm responsible for it, so I can't make any excuses. So, when you use something made by someone else, be sure to check the __ specification __. As for the atan2 method this time, we need to find out what is in the argument, what the return value is, or what range it is. At that time, try to eliminate all your assumptions.
Be sure to clarify the specifications when you want others to use what you have made!
This is a lesson for the future. When developing with a team of multiple people, it is necessary to properly organize what kind of input the program in charge is supposed to be, what kind of processing is performed, and what kind of result is output. There is. There are various rules depending on the site, such as leaving it as a comment in the program you wrote or saving it as a separate document, but be sure to leave it in any form (yourself). Even the ones made by will be forgotten over time!). Otherwise, it will cause a great deal of trouble to your teammates.
5. At the end
The other day, after posting the first part of the article, I learned a lot from the comments that I didn't know about methods and processing methods. I will continue to post articles on Qiita, and I will do my best so that I can give advice in the future. Thank you for reading this far.
Recommended Posts