Search Unity

Bug How to draw a triangle in the center of a line using GL?

Discussion in 'Editor & General Support' started by Long2904, Feb 23, 2021.

  1. Long2904

    Long2904

    Joined:
    May 13, 2019
    Posts:
    81
    I am making a node-based custom editor window (something like the animator window). I now can draw nodes and edges between them. The thing I want to do now is to draw a triangle arrowhead in the middle of the edge (like the one you see in the animator window). From this post, I started to try GL to draw the triangle. Here's my code:
    Code (CSharp):
    1. private void OnGUI()
    2.      {
    3.          // Do other stuff...
    4.          Vector2 startPos = ...; // The start position (Center of the from node. This is from the node's rect which mean it used the rect's coordinate system)
    5.          Vector2 endPos = ...; // The end position (Center of the to node. Also from a rect)
    6.  
    7.          Vector2 center = MathUtils.Average(startPos, endPos); // The center of the current edge
    8.          Vector2 dir = (endPos - startPos).normalized; // The direction of the current egde
    9.  
    10.          // Making an equilateral triangle
    11.          Vector3 a = center + dir                               * 10; // From the center move 10 more unit along the line's direction
    12.          Vector3 b = center + Rotate(dir,  120 * Mathf.Deg2Rad) * 10; // Rotate the dir  120 degree and move along that direction 10 more unit
    13.          Vector3 c = center + Rotate(dir, -120 * Mathf.Deg2Rad) * 10; // Rotate the dir -120 degree and move along that direction 10 more unit
    14.  
    15.          // The coordinate of the GUI is (0, 0) on the top left conner, this will convert it to the normal GL coordinate ((0, 0) on the bottom left connor)
    16.          // Also the position property is a inheritance property from the EditorWindow (in case you don't know)
    17.          a.y = position.height - a.y;
    18.          b.y = position.height - b.y;
    19.          c.y = position.height - c.y;
    20.  
    21.          // InverseLerp will return the value from [(0, 0), (1, 1)] because that what GL.Vertex() use
    22.          a = InverseLerp(Vector2.zero, position.size, a);
    23.          b = InverseLerp(Vector2.zero, position.size, b);
    24.          c = InverseLerp(Vector2.zero, position.size, c);
    25.  
    26.          DrawGLTriangle(a, b, c); // Draw the triangle
    27.          Handles.DrawAAPolyLine(10, startPos, endPos); // Draw the egde between the two current nodes
    28.  
    29.          // Do other stuff...
    30.      }
    31.  
    32.      public static Vector2 InverseLerp(Vector2 start, Vector2 end, Vector2 value)
    33.      {
    34.          return (value - start) / (end - start);
    35.      }
    36.  
    37.      public static Vector2 Rotate(Vector2 v, float angle)
    38.      {
    39.          return new Vector2(
    40.          v.x * Mathf.Cos(angle) - v.y * Mathf.Sin(angle),
    41.          v.x * Mathf.Sin(angle) + v.y * Mathf.Cos(angle)
    42.      );
    43.      }
    44.  
    45.      // This function is directly taken from Unity https://docs.unity3d.com/ScriptReference/GL.TRIANGLES.html
    46.      public static void DrawGLTriangle(Vector3 a, Vector3 b, Vector3 c)
    47.      {
    48.          GL.PushMatrix();
    49.          GL.LoadOrtho();
    50.          GL.Begin(GL.TRIANGLES);
    51.          GL.Vertex(a);
    52.          GL.Vertex(b);
    53.          GL.Vertex(c);
    54.          GL.End();
    55.          GL.PopMatrix();
    56.      }
    Here's the result, the triangle is not on the line. Its x position seems correct but the y position is a little higher than it should be. It seems to rotate correctly.
     
  2. Long2904

    Long2904

    Joined:
    May 13, 2019
    Posts:
    81
    Anyone?
     
  3. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,692
    I haven't taken a deep look at your code, but try drawing a debug point at center to make sure your center is correct. If it's correct, then your code may be computing the triangle points wrong. If it's incorrect, then maybe the center is being offset by the height of the window heading ("State Machine").
     
  4. Long2904

    Long2904

    Joined:
    May 13, 2019
    Posts:
    81
    @TonyLi The center is correct. I tested it before and just tested it again, still exactly on the center of the line.
    I also think that the problem comes from the triangle points. Even if I set one of them to the center, it didn't draw correctly (the Y is still above the center but the X works well). The computing of the triangle consists of two parts: Create the points and convert them from the GUI coordinate to the GL coordinate. The first part works fine, I think it's the second part that fails. Here's the converting part:
     
  5. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,692
    Did you check if it's offset by the height of the title bar?
     
  6. Long2904

    Long2904

    Joined:
    May 13, 2019
    Posts:
    81
    How high is the title bar? In the above code, I just flip it by removing y from position.height (a.y = position.height - a.y).
     
  7. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,692
    I believe, depending on your Unity version, the title bar height is determined by the GUI skin's window style border, and position.height includes the height of the title bar.
     
  8. Long2904

    Long2904

    Joined:
    May 13, 2019
    Posts:
    81
    The GL class actually uses the whole screen to draw, including the editor window tab height (You can access it with Screen.safeArea). I was clamping the value between 0 and position.height (doesn't include the tab height) so that's why the Y coordinate didn't align correctly (account for the tab height). Just change the code to this:
    Code (CSharp):
    1.        
    2. // GL use the whole screen and clamp it to [0,1] but position doesn't include the window tab height so need to use Screen.height and Screen.safeArea
    3.         a.y = Screen.height - a.y;
    4.         b.y = Screen.height - b.y;
    5.         c.y = Screen.height - c.y;
    6.  
    7.         // Remove the height of the window tab.
    8.         a.y -= 21;
    9.         b.y -= 21;
    10.         c.y -= 21;
    11.  
    12.         // Return the value in [(0, 0), (1, 1)] because that what GL.Vertex use
    13.         a = MathUtils.InverseLerp(Vector2.zero, Screen.safeArea.size, a);
    14.         c = MathUtils.InverseLerp(Vector2.zero, Screen.safeArea.size, c);
    15.         b = MathUtils.InverseLerp(Vector2.zero, Screen.safeArea.size, b);
    Also, if you want to know how high is the window tab just use something like:
    Code (CSharp):
    1. Debug.Log(Screen.height - position.height);