Drawing lines in the editor

Hello everyone!

So, I had three things: some left-over time, a problematic line drawing script, and a desire to get it working.
Give and take a few hours:

It’s based on the original Drawing.DrawLine script found on the wiki, but I modified it a bit to allow it to work in editors, rather than the scene. Apparently, some issues exist that prevent it from working as expected there. Both scaling and rotating around pivots apparently have problems on non-zero pivot points.
Whilst looking around in the forums, I noticed some people having issues with that, so I thought I’d post it up here to see if it works for everyone.

The changes aren’t all straight-forward, but it should be fairly simple to compare to the original (C# version).
In addition, I took the liberty of fixing a few bugs and adding in some fake anti-aliasing (a 1x3 texture going transparent-white-transparent) as well as beziér curve drawing. (I also temporarily removed most of the alternative functions, since I wanted to minimize error sources)

I’ll be contributing it back to the wiki in a while, but first I’d be a very happy puppy if people could tell me whether or not it’s stable, what works and what doesn’t. :slight_smile:

Linus

Edit:
Another image, showing this in a more useful context, along with the corresponding unitypackage. :slight_smile:

You’ll also notice some limitations in the shadows, that’s because the lines aren’t capped. I probably won’t fix that at the moment.

460138–16100–$linedrawing.unitypackage (3.55 KB)
460138–16101–$linedrawing2.unitypackage (3.69 KB)

2 Likes

Thank god, a good method for drawing lines in editor windows!

I’ll let you know how my implementation goes.

Thank’s a lot for sharing Linus!

working great so far, having some problems with bezierline, but I’m pretty sure its just because I’m not using it right, the DrawLine however works amazing

Huge thanks!

This would have taken me too long to do myself. Massive time saver!

I got them working after a small hump: The tangents you give aren’t relative to the related start/end points. So, I was doing:

 Drawing.bezierLine( startPoint, Vector2.right * 50.0f, endPoint, - Vector2.right * 50.0f, Color.gray, 1.0f, true, 8);

But this should have been:

 public static void bezierLine(Vector2 start, Vector2 startTangent, Vector2 end, Vector2 endTangent, Color color, float width, bool antiAlias, int segments);

 Drawing.bezierLine(startPoint,[B] startPoint+[/B] Vector2.right * 50.0f, endPoint, [B]endPoint- [/B]Vector2.right * 50.0f, Color.gray, 1.0f, true, 8);

So just add the start/end “tangents” onto the start or end vectors for those fields. They’re not really tangents - they’re control points in a bezier of (some*) degree.

Linus: my only real feedback at the moment is just the minor point above… the name “tangent” is slightly misleading? Also maybe call the function “DrawBezier” to be more in line with convention? Really minor crits… thanks so much for this. Lovely work!

*2nd?

Awesome. I’ve been looking for a way to manually draw lines in the editor (Unity GUI is useless to build node based extensions without it). I noticed DrawLine in the .Net API, but I wasn’t sure if it would work in Unity (and I pretty much assumed that it didn’t).
I had just about resigned myself to trying to scale a 2d image of a curve using GUITexture, or using plugins to draw it (not too sure how to go about that), so this is an awesome solution!

Thanks for sharing this.

Just noticed another issue.

When using scrollbars (vertical or horizontal), and you’re not at the very top and very left of the area the lines disappear altogether. I’m not sure how you’d fix this but I’m starting to look into it.

i.e.

scrollPos = GUI.BeginScrollView(
                new Rect(0, 0, position.width, position.height),
                scrollPos,
                new Rect(0, 0, 10000, 10000), true, true
            );

is used, and possibly the view matrix then starts drawing the lines waaay outside the actual window if the scrollPos isn’t (0,0)?

Haven’t yet looked into a way to fix it but if I find anything, I’ll be back.

From what I can tell, GUI.matrix isn’t affected at all by BeginScrollView/EndScrollView scrolling, so it’s not that.

Thanks! Helped me a lot! Saved me a bunch of time! Thanks!

Hi I’ll also worked on an NodeBasedEditor thx’s for this scripts.
I also have troubles with the lines, inside a ScrollView. If anyone of you is still working on this issue please post some of your tries. I’ll try to fix this bug, any idea would be nice

Whats wrong with;

?

Hi thx for that hint!! Works create also with Handles.DrawLine. But why isn’t the Drawing script it self isn’t using the Handles routines??

I had been looking around the scripting area’s and such for this very exact thing. Thanks just made me very happy

Handles.DrawBezier() is meant for drawing bezier lines in the scene in 3d. The code OP gave is for bezier lines in editor GUI.

Nice

I’m currently using a similar method for my generic graph editor aka “The Spaghetti Machine”. The problem is that when you have a large number of spagh… err, splines, this consumes a lot of CPU time.

The solution I found is the following : When a spline is currently changing (windows being dragged around), the spines are drawn directly on screen. Otherwise they are once drawn to a texture (with SetPixel) and then the texture is drawn to the screen at every refresh, without any new spline calculations. This way, we only need to recalculate the spline if vEndPoint - vStartPoint changes.

Thought this might help other people encountering similar problems.

[Edit:] I replaced the single texture per line by one or more textures, depending on the length of the spline. Each texture has a maximal size. This avoids having giant textures for long, roughly diagonal splines (with surface proportional to the square of the length).

I too have run into problems trying to use this script inside a ScrollView. Anyone know what’s up with that?

This class works very well in Windows editor.But incorrect in OSX editor.

here is the screen capture.

on windows:

on OSX:

They are run the same script, but result not same.

Does anyone has the same problem? How to solve this?

I do some modify to the code. It works well on Mac now.

here is the code:

public class Drawing
{

	static Texture2D _aaLineTex = null;

	static Texture2D _lineTex = null;

	static Texture2D adLineTex
	{
		get
		{
			if (!_aaLineTex)
			{
				_aaLineTex = new Texture2D(1, 3, TextureFormat.ARGB32, true);
				_aaLineTex.SetPixel(0, 0, new Color(1, 1, 1, 0));
				_aaLineTex.SetPixel(0, 1, Color.white);
				_aaLineTex.SetPixel(0, 2, new Color(1, 1, 1, 0));
				_aaLineTex.Apply();
			}
			return _aaLineTex;
		}
	}

	static Texture2D lineTex
	{
		get
		{
			if (!_lineTex)
			{
				_lineTex = new Texture2D(1, 1, TextureFormat.ARGB32, true);
				_lineTex.SetPixel(0, 1, Color.white);
				_lineTex.Apply();
			}
			return _lineTex;
		}
	}


	static void DrawLineMac(Vector2 pointA, Vector2 pointB, Color color, float width, bool antiAlias)
	{
		Color savedColor = GUI.color;
		Matrix4x4 savedMatrix = GUI.matrix;
		
		float oldWidth = width;
		
		if (antiAlias) width *= 3;
		float angle = Vector3.Angle(pointB - pointA, Vector2.right) * (pointA.y <= pointB.y?1:-1);
		float m = (pointB - pointA).magnitude;

		if (m > 0.01f)
		{
			Vector3 dz = new Vector3(pointA.x, pointA.y, 0);
			Vector3 offset = new Vector3((pointB.x - pointA.x) * 0.5f,
									   (pointB.y - pointA.y) * 0.5f,
									   0f);

			Vector3 tmp = Vector3.zero;

			if (antiAlias)
				tmp = new Vector3( -oldWidth * 1.5f * Mathf.Sin(angle * Mathf.Deg2Rad), oldWidth * 1.5f * Mathf.Cos(angle * Mathf.Deg2Rad) );
			else
				tmp = new Vector3( -oldWidth * 0.5f * Mathf.Sin(angle * Mathf.Deg2Rad), oldWidth * 0.5f * Mathf.Cos(angle * Mathf.Deg2Rad) );

			GUI.color = color;
			GUI.matrix = translationMatrix(dz) * GUI.matrix;
			GUIUtility.ScaleAroundPivot(new Vector2(m, width), new Vector2(-0.5f, 0));
			GUI.matrix = translationMatrix(-dz) * GUI.matrix;
			GUIUtility.RotateAroundPivot(angle, Vector2.zero);
			GUI.matrix = translationMatrix(dz  - tmp - offset) * GUI.matrix;

			GUI.DrawTexture(new Rect(0, 0, 1, 1), antiAlias ? adLineTex :  lineTex);
		}

		GUI.matrix = savedMatrix;

		GUI.color = savedColor;
	}


	static void DrawLineWindows(Vector2 pointA, Vector2 pointB, Color color, float width, bool antiAlias)
	{

		Color savedColor = GUI.color;

		Matrix4x4 savedMatrix = GUI.matrix;




		if (antiAlias) width *= 3;

		float angle = Vector3.Angle(pointB - pointA, Vector2.right) * (pointA.y <= pointB.y ? 1 : -1);

		float m = (pointB - pointA).magnitude;

		Vector3 dz = new Vector3(pointA.x, pointA.y, 0);

		GUI.color = color;

		GUI.matrix = translationMatrix(dz) * GUI.matrix;

		GUIUtility.ScaleAroundPivot(new Vector2(m, width), new Vector2(-0.5f, 0));

		GUI.matrix = translationMatrix(-dz) * GUI.matrix;

		GUIUtility.RotateAroundPivot(angle, new Vector2(0, 0));

		GUI.matrix = translationMatrix(dz + new Vector3(width / 2, -m / 2) * Mathf.Sin(angle * Mathf.Deg2Rad)) * GUI.matrix;


		GUI.DrawTexture(new Rect(0, 0, 1, 1), !antiAlias ? lineTex : adLineTex);

		GUI.matrix = savedMatrix;

		GUI.color = savedColor;

	}



	public static void DrawLine(Vector2 pointA, Vector2 pointB, Color color, float width, bool antiAlias)
	{
		if (Application.platform == RuntimePlatform.WindowsEditor)
		{
			DrawLineWindows(pointA, pointB, color, width, antiAlias);
		}
		else if (Application.platform == RuntimePlatform.OSXEditor)
		{
			DrawLineMac(pointA, pointB, color, width, antiAlias);
		}
	}



	static void bezierLine(Vector2 start, Vector2 startTangent, Vector2 end, Vector2 endTangent, Color color, float width, bool antiAlias, int segments)
	{

		Vector2 lastV = cubeBezier(start, startTangent, end, endTangent, 0);

		for (int i = 1; i < segments; ++i)
		{

			Vector2 v = cubeBezier(start, startTangent, end, endTangent, i / (float)segments);



			Drawing.DrawLine(

				lastV,

				v,

				color, width, antiAlias);

			lastV = v;

		}

	}



	private static Vector2 cubeBezier(Vector2 s, Vector2 st, Vector2 e, Vector2 et, float t)
	{

		float rt = 1 - t;

		return rt * rt * rt * s + 3 * rt * rt * t * st + 3 * rt * t * t * et + t * t * t * e;

	}



	private static Matrix4x4 translationMatrix(Vector3 v)
	{

		return Matrix4x4.TRS(v, Quaternion.identity, Vector3.one);

	}
}