Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
  • Help Room /
avatar image
1
Question by alrakis · Feb 09, 2017 at 03:28 PM · mouseclicksurfaceareacalculatemeasure

Area calculation using mouse clicks in game window

Hi!

I saw it's possible to measure distances in game window using mouse clicks, but I can't figure out how can I measure the area, and I'm referring to a 3d scene. Ok, to measure a distance I need vector3.distance, but what should I do if I want to measure an area defined by mouse clicks, three or more points?

I specify I used a plane that interacts with the mouse clicks to constrained the clicks only on that plane.

Maybe you could help me, Thank you.

Here is an example with what I hope, I still hope to achieve... https://s26.postimg.org/3psmrh8tl/example2_2.png

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

2 Replies

· Add your reply
  • Sort: 
avatar image
0

Answer by oStaiko · Feb 19, 2017 at 10:34 PM

This is a tough question, completely dependent on what "3 or more" means to you. If you have an object of 50 click, forget about it. 3? 4? That's doable. the fewer the clicks, the easier this gets. And you must also realize that a 2d shape, using Vector2s, has areas, while a 3D shape, using Vector3s like you described, has a volume. I wont be going over volume of a random assortments of points because that just gets even more complex, so I'd highly recommend you just avoid that path in your project.

For 3 clicks on a 2D, this is going to be real easy, as all you have is a triangle no matter how the clicks are arranged. The area of a triangle is easily stated as A = h*w/2, Easy! However it's not quite that easy, as we don't know for sure that there will be a convenient flat line to call our base. I've derived a longer formula that works for any orientation of a triangle, which replaces h with adjacent side*sin(Θ), where Θ is the angle between our base and the adjacent side. This angle can be computed with the inverse cos of (base/adjacent side). You want all of this to be computed given arbitrary Points in the form of Vector2s. We don't know these, so they are simply going to be x1, y1, x2, so on. We can assume that our 'base' is between p1 and p2, so that the length of our base can be defined as b^2 = (x2-x1)^2 + (y2-y1)^2 (Pythagoreans theorem). The height value will be the same using either side, so we will just use p1 and p2. h is then calculated as a sin(cos^-1(b/a)), where a is the length of our adjacent side defined as a^2 = (x3-x1)^2 + (y3-y1)^2. So, in total used our variables b and a, the final equation of A is given as b a * sin(cos^-1(b/a)). Phew, and that was the easy one!

I'm going to walk you through 4 points, purely to show you how ridiculous this is going to get if you want any more than 4. But first, a quick explanation of why triangles are so easy: they are always convex. Once you introduce a 4th point, it becomes possible to create concave shapes, which in turns creates multiple possibilities of how to orientate the shape. For example, if you have 4 points in a square, you could connect them with 4 lines by drawing an hourglass. Making a shape convex out of those points would be easy, but what about... lets say 3 points in a triangle, and then 1 in the center? There are no convex ways to connect these 4 points, and now 3 possible orientations of points. This however can be negated, by either automatically canceling any point inside of the triangle of the first 3 points, or forcing lines to be drawn as points are placed, such that p1 -p2- p3 - p4 - p1, are linked in the order placed. One of these MUST be done. otherwise you're getting nowhere. I don't know your game, but the first pattern is much easier, so we're going with that one. Now the given problem is easier, but it's also a two stepper.

We're going to assume 3 points have already been placed and we've used the above formula to calculate its area, and are about to place a new point somewhere on our plane. Calculating the new area is as easy as drawing a new triangle to the two closest points, and adding the area together, as any convex quadrilateral can be divided in to two triangles. We already have that equation, but the hard part is now to determine which two points we're closest to... and to cancel out any points inside our triangle. First, lets tackle our "collision" detection, to make sure all new points are valid. This is a tough one because there's not just two possible options, there's 3. You can either place the point inside the triangle and be cancelled, place it outside within a golden region and create your ideal convex quadrilateral, or place it in a bad spot creating a concave quadrilateral. In the third case, our new point has to take over an older point, so that the existing point is removed, and we're back to being a now slightly larger triangle. So, we will test every new point to see which region it is in. This is mostly a series of if statements based on the following image I made in paint for you: ¯\_(ツ)_/¯

Anything in our green region will create a new convex quadrilateral, which is exactly what we want here. Anything in the red zone can just be ignored. Anything in the yellow zone however, would have to remove the point closest to it, and take that points role. If we know which region the point is in, the next step isn't so bad, but finding the region gets a little tricky. We have 3 lines, these three lines can be represented by the connections between any two points, since triangles couldn't possibly overlap. The only way I know how to do this is by checking to see whether each point is above or below a line. First we must check if any lines are vertical, because those can't have any equation and must be caught as exceptions. those are easier however, as a vertical line would only need to check if the x value is greater or lower.

How to find the equation of a line: we could use the standard linear equation of y = mx + b, but instead we will be using linear interpolation, as it's faster in this scenario. We can use linear interpolation to get a y value for an x value on some line that passes through any two points, and then compare this to this y value to Py. This equation would look something like... f(Px) = ((y2-y1)*(Px-x1)/(x2-x1)) + y1, where P1 and P2 were any two points on any of our three lines. Running this three times, would give us 3 y values, which we would check if it was lower or higher than our Py. In the end this would give us 3 bools, and using these bools would tell us which region our point is in, and from that we can continue to proceed to the next step.

To find out which section the new point is in is by far the hardest step here! I'll skip a lot of the thought process behind it and skip to how we can do it. First, we can check if our new point is within the triangle, or outside of it. We can do this by several popular methods, but for this example we will be using the Barycentric Coordinate System, which is worth looking in to if you're a diehard fan of geometry. Now that we know if it's inside or outside of the triangle, lets assume our new point is now outside the triangle. This gives us 6 total regions to decide from, for which we will be using basic if logic to check for, because it's late and I'm tired. First we'll see if point 2 is above or below our line 0-1. From there, we can check if our new point is above or below the other two lines, each possible result giving different answers, and so on, as described in the code example below.

Now for what to do based on which section our new point landed in! If we are in the red zone, we simply disregard the new point. That is by far the easiest. If we are in yellow, you can check the distance of each point, and remove the point with the shortest distance, and add our new point to the list/array of points. If it's in the green point, you'll have to check (based on on our bools) exactly which region it's in, and use the two points connecting to it to calculate the area of that triangle (using the above method) and add it to your previous triangle. This would give you your new Quadrilateral and it's area.

Here's all of that, in c#

 List<Vector2> points = new List<Vector2> ();
 float area;

 bool AddPoint (Vector2 newPoint)
 {
     if (points.Count > 0)
     {
         foreach (Vector2 p in points)
             if (p == newPoint)
                 return false;
     }
     if (points.Count>1)
     {
         //Check for in-line points, delete midpoint
         float f = points[0].y+(points[1].y-points[0].y)*(newPoint.x-points[0].x)/(points[1].x-points[0].x);
         if (newPoint.y==f)
             return false;
     }
     switch (points.Count+1)
     {
         case 1:
             points.Add (newPoint);
             DrawPoint (newPoint);
             return true;
         case 2:
             //rearange points for simplicity
             if (points [0].x>newPoint.x)
             {
                 Vector2 temp = points [0];
                 points [0] = newPoint;
                 points.Add (temp);
             }
             else if (points [0].x==newPoint.x)
                 points.Add (new Vector2(newPoint.x+1, newPoint.y));  //Now p0 is to the left of p1
             DrawPoint (newPoint);
             DrawLine (points[0], points[1]);
             return true;
         case 3:
             //Check for in-line points, delete midpoint
             if (newPoint.y==points [0].y+(points [1].y-points [0].y)*(newPoint.x-points [0].x)/(points [1].x-points [0].x))
             {
                 if (newPoint.x<points [0].x)
                 {
                     DeleteLine (points [0], points [1]);
                     points [0] = newPoint;
                     DrawLine (points [0], points [1]);
                 }
                 else if (newPoint.x>points [1].x)
                 {
                     DeleteLine (points [0], points [1]);
                     points [1] = newPoint;
                     DrawLine (points [0], points [1]);
                 }
                 //else, point between lines, can be ignored
                 return false;
             }

             points.Add (newPoint);
             DrawPoint (newPoint);
             DrawLine (points [0], newPoint);
             DrawLine (points [1], newPoint);
             area = ComputeArea (points[0], points[1], newPoint);
             return true;
         case 4:
             //we will be using these A LOT
             float x0 = points [0].x;
             float y0 = points [0].y;
             float x1 = points [1].x;
             float y1 = points [1].y;
             float x2 = points [2].x;
             float y2 = points [2].y;
             float Px = newPoint.x;
             float Py = newPoint.y;

             //Test if point is inside triangle
             float denom = ((y1-y2)*(x0-x2)+(x2-x1)*(y0-y2));
             float a = ((y1-y2)*(Px-x2)+(x2-x1)*(Py-y2))/denom;
             float b = ((y2-y0)*(Px-x2)+(x0-x2)*(Py-y2))/denom;
             float c = 1-(a+b);
             if (0<=a && a<=1 && 0<=b && b<=1 && 0<=c && c<=1)
                 return false; //Ignore this point, inside triangle

             bool abv01 = false; //line 0-1
             bool abv12 = false; //line 1-2
             bool abv20 = false; //line 2-0

             while (x0==x1 || x0==x2 || x0==Px || x1==x2 || x1==Px || x2==Px)
             {
                 //Vertical line exists
                 if (x0==x1 || x0==x2 || x0==Px)
                     x0 += x0>500? -1 : 1; //Replace 500 with middle of screen value
                 else if (x1==x2 || x1==Px)
                     x1 += x1>500? -1 : 1;
                 else if (x2==Px)
                     x2 += x2>500? -1 : 1;
             }

             float f1 = y0+(y1-y0)*(Px-x0)/(x1-x0);
             if (Py>f1)
                 abv01 = true;
             float f2 = y1+(y2-y1)*(Px-x1)/(x2-x1);
             if (newPoint.y>f2)
                 abv12 = true;
             float f3 = y2+(y0-y2)*(Px-y2)/(x0-x2);
             if (newPoint.y>f3)
                 abv20 = true;
             float f4 = y0+(y1-y0)*(x2-x0)/(x1-x0);

             Vector2 p1 = new Vector2(), p2 = new Vector2();

             //All values callibrated to coordinates based in sample image, top left p0, top right p1, bottom p2.
             //Values adjusted based on actual position of p3 relative to p0 and p1


             if (y2>f4) //line 0-1 is on bottom, or "upside down"
             {
                 abv01 = !abv01;
                 abv12 = !abv12;
                 abv20 = !abv20;
             }
             if (x2<x0) //If x2 is not between x0 and x1
                 abv20 = !abv20;
             else if (x2>x1)
                 abv12 = !abv12;
             if ((abv01 && abv12 && abv20) || (!abv01 && abv12 && !abv20) || (!abv01 && !abv12 && abv20))
             {
                 points.Add (newPoint);
                 DrawPoint (newPoint);
                 if (abv12)  DrawLine (newPoint, points [0]);
                 if (abv20)  DrawLine (newPoint, points [1]);
                 if (!abv01) DrawLine (newPoint, points [2]);

                 if (!abv12) p1=points[1];p2=points[2];
                 if (!abv20) p1=points[2];p2=points[0];
                 if (abv01)  p1=points[0];p2=points[1];

                 DeleteLine (p1, p2);
                 area += ComputeArea (newPoint, p1, p2);
             }
             else
             {
                 //DoStuffForYellow
                 if (abv12)
                 {
                     DeleteLine (points[0], points[1]);
                     DeleteLine (points[0], points[2]);
                     points [0] = newPoint;
                     DrawLine (points[0], points[1]);
                     DrawLine (points[0], points[2]);
                 }
                 else if (abv20)
                 {
                     DeleteLine (points[1], points[0]);
                     DeleteLine (points[1], points[2]);
                     points [1] = newPoint;
                     DrawLine (points[1], points[0]);
                     DrawLine (points[1], points[2]);
                 }
                 else
                 {
                     DeleteLine (points[2], points[0]);
                     DeleteLine (points[2], points[1]);
                     points [2] = newPoint;
                     DrawLine (points[2], points[0]);
                     DrawLine (points[2], points[1]);
                 }
             }
             return true;
         default:
             Debug.Log ("Too many points!");
             //Do nothing?
             return false;
     }
 }

 float ComputeArea (Vector2 p1, Vector2 p2, Vector2 p3)
 {
     float a = Mathf.Sqrt(Mathf.Pow(p3.x-p2.x, 2) + Mathf.Pow(p3.y-p2.y, 2));
     float b = Mathf.Sqrt(Mathf.Pow(p3.x-p1.x, 2) + Mathf.Pow(p3.y-p1.y, 2));
     return a * b * Mathf.Sin(Mathf.Acos(b/a))/2;
 }

 void DrawPoint(Vector2 p)
 {
     //DrawPoint
 }

 void DrawLine (Vector2 p1, Vector2 p2)
 {
     //DrawLine
 }

 void DeleteLine (Vector2 p1, Vector2 p2)
 {
     //DeleteLine
 }

This was fun.

tl;dr: math


ツ.png (5.6 kB)
Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
0

Answer by alrakis · Feb 18, 2017 at 12:28 PM

Thank you very much for your answer @oStaiko, but maybe your solution still has some problems or I don't know how to use it.

I create a C# script in Unity and after I used "using System;" from 128 errors now I get only 9, I tried to replace float to double, it seems VB has a problem with that but this doesn't solve the problems.

error list

And I don't know, that "marker" (the image) I can use it anywhere in the scene? And I'm speaking of a 3d scene, because there I need to measure area using the mouse click. That's why I said vector3 because I used "RaycastHit.point" to interact with the plane because I'm looking at that plane with a Camera. And I didn't specify from the beginning, also I need to see the border of that area "in game".

And to be more precise with what I'm searching for, please see the image below. Example

Thakns, R.

ps. Indeed the area of a triangle is easy to calculate if we have the h and the base but why is divided by 3? I know something with 2, a = h*w/2


example.png (161.8 kB)
error-list.zip (335.1 kB)
Comment
Add comment · Show 8 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image oStaiko · Feb 18, 2017 at 11:31 PM 0
Share

I just resubmitted it as an edit to the code to fix the errors, but now it's gotta go through the moderation queue again... Anyways that's just a fix for the errors, not the converting 3D to 2D part... Why don't you go ahead and make a separate question for that, "how to covert Vector3s on a 2D plane to relative Vector2s" and I'll help you with that part there? I'll need a bit of time before I can get on it for you, but someone else might be able to assist in the meantime. After you figure that part out, use the Vector2 points you get from there, and plug em in here! Should be that easy, hopefully...

avatar image alrakis oStaiko · Feb 19, 2017 at 10:43 AM 0
Share

I believe I don't need that, at the beginning indeed I though I need to convert those values from 2D to 3D, but I found an easier way, I always use only the position of the middle screen to send the mouse clicks. This way I only need to control the camera position and I send the clicks wherever I want in the 3D scene.

avatar image alrakis oStaiko · Feb 21, 2017 at 04:05 PM 0
Share

And thank you again William, indeed this code now has no errors, but I have a problem to implement all this. When I clicked on screen when run the app, nothing happens, what should I do?

ps. maybe for you is ridiculous this question but you are talking with a guy that not even finish the fundamentals course of c#, but he has a lot of deter$$anonymous$$ation :)

avatar image oStaiko alrakis · Feb 21, 2017 at 07:35 PM 0
Share

Did you replace the DrawLine () and DrawPoint() functions with your own code that does those things?

A simple way to call the function on click, would be like this:

 void Update ()
 {
     if (Input.Get$$anonymous$$ouseButtonDown(0))
         AddPoint();
 }

Put that in the same class/file you have the AddPoint method. Now whenever your click, it'll run the code.

Show more comments
Show more comments
avatar image alrakis oStaiko · Feb 22, 2017 at 09:33 PM 0
Share

No William, the things I want to draw are only on x and y plane and does not move, and again yes, I use only one plane where do I want to draw.

Only there I measure the distance with my code and only there I want to use more than 2 points to get an area defined by those points, and of course I want to see the perimeter of that area.

Ideal, I would like to measure an area of almost any shape but is good even a shape defined by at least 4 points. (created with mouse click)

When the coordinates of the first point are equal with the last point (approximately equal in fact because it's hard to obtain the same coordinates with a mouse click), the shape is closed and I can have the area value.

Here an example https://s26.postimg.org/3psmrh8tl/example2_2.png

Thank you again for your support, Razvan

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

5 People are following this question.

avatar image avatar image avatar image avatar image avatar image

Related Questions

Click an object and show text 0 Answers

Dividing object surface into sectors or specific areas 0 Answers

La touche "Fire1" ne marche pas 2 Answers

how can i move my gameobject on mouse click by script?? 2 Answers

Action on click instead of hold button. 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges