using System.Reflection;
using UnityEngine;
// Line drawing routine originally courtesy of Linusmartensson:
// http://forum.unity3d.com/threads/71979-Drawing-lines-in-the-editor
//
// Rewritten to improve performance by Yossarian King / August 2013.
//
// This version produces virtually identical results to the original (tested by drawing
// one over the other and observing errors of one pixel or less), but for large numbers
// of lines this version is more than four times faster than the original, and comes
// within about 70% of the raw performance of Graphics.DrawTexture.
//
// Peak performance on my laptop is around 200,000 lines per second. The laptop is
// Windows 7 64-bit, Intel Core2 Duo CPU 2.53GHz, 4G RAM, NVIDIA GeForce GT 220M.
// Line width and anti-aliasing had negligible impact on performance.
//
// For a graph of benchmark results in a standalone Windows build, see this image:
// https://app.box.com/s/hyuhi565dtolqdm97e00
//
// For a Google spreadsheet with full benchmark results, see:
// https://docs.google.com/spreadsheet/ccc?key=0AvJlJlbRO26VdHhzeHNRMVF2UHZHMXFCTVFZN011V1E&usp=sharing
public static class Drawing
{
private static Texture2D aaLineTex = null;
private static Texture2D lineTex = null;
private static Material blitMaterial = null;
private static Material blendMaterial = null;
private static Rect lineRect = new Rect(0, 0, 1, 1);
// Draw a line in screen space, suitable for use from OnGUI calls from either
// MonoBehaviour or EditorWindow. Note that this should only be called during repaint
// events, when (Event.current.type == EventType.Repaint).
//
// Works by computing a matrix that transforms a unit square -- Rect(0,0,1,1) -- into
// a scaled, rotated, and offset rectangle that corresponds to the line and its width.
// A DrawTexture call used to draw a line texture into the transformed rectangle.
//
// More specifically:
// scale x by line length, y by line width
// rotate around z by the angle of the line
// offset by the position of the upper left corner of the target rectangle
//
// By working out the matrices and applying some trigonometry, the matrix calculation comes
// out pretty simple. See https://app.box.com/s/xi08ow8o8ujymazg100j for a picture of my
// notebook with the calculations.
public static void DrawLine(Vector2 pointA, Vector2 pointB, Color color, float width, bool antiAlias)
{
// Normally the static initializer does this, but to handle texture reinitialization
// after editor play mode stops we need this check in the Editor.
#if UNITY_EDITOR
if (!lineTex)
{
Initialize();
}
#endif
// Note that theta = atan2(dy, dx) is the angle we want to rotate by, but instead
// of calculating the angle we just use the sine (dy/len) and cosine (dx/len).
float dx = pointB.x - pointA.x;
float dy = pointB.y - pointA.y;
float len = Mathf.Sqrt(dx * dx + dy * dy);
// Early out on tiny lines to avoid divide by zero.
// Plus what's the point of drawing a line 1/1000th of a pixel long??
if (len < 0.001f)
{
return;
}
// Pick texture and material (and tweak width) based on anti-alias setting.
Texture2D tex;
Material mat;
if (antiAlias)
{
// Multiplying by three is fine for anti-aliasing width-1 lines, but make a wide "fringe"
// for thicker lines, which may or may not be desirable.
width = width * 3.0f;
tex = aaLineTex;
mat = blendMaterial;
}
else
{
tex = lineTex;
mat = blitMaterial;
}
float wdx = width * dy / len;
float wdy = width * dx / len;
Matrix4x4 matrix = Matrix4x4.identity;
matrix.m00 = dx;
matrix.m01 = -wdx;
matrix.m03 = pointA.x + 0.5f * wdx;
matrix.m10 = dy;
matrix.m11 = wdy;
matrix.m13 = pointA.y - 0.5f * wdy;
// Use GL matrix and Graphics.DrawTexture rather than GUI.matrix and GUI.DrawTexture,
// for better performance. (Setting GUI.matrix is slow, and GUI.DrawTexture is just a
// wrapper on Graphics.DrawTexture.)
GL.PushMatrix();
GL.MultMatrix(matrix);
//Graphics.DrawTexture(lineRect, tex, lineRect, 0, 0, 0, 0, color, mat);
//Replaced by:
GUI.color = color;//this and...
GUI.DrawTexture( lineRect, tex );//this
GL.PopMatrix();
}
//
public static void DrawCircle(float x, float y, float z, float r, float accuracy)
{
GL.PushMatrix();
//绘制2D图像
GL.LoadOrtho();
float stride = r * accuracy;
float size = 1 / accuracy;
float x1 = x, x2 = x, y1 = 0, y2 = 0;
float x3 = x, x4 = x, y3 = 0, y4 = 0;
double squareDe;
squareDe = r * r - Mathf.Pow(x - x1, 2);
squareDe = squareDe > 0 ? squareDe : 0;
y1 = (float)(y + Mathf.Sqrt((float)squareDe));
squareDe = r * r - Mathf.Pow(x - x1, 2);
squareDe = squareDe > 0 ? squareDe : 0;
y2 = (float)(y - Mathf.Sqrt((float)squareDe));
for (int i = 0; i < size; i++)
{
x3 = x1 + stride;
x4 = x2 - stride;
squareDe = r * r - Mathf.Pow(x - x3, 2);
squareDe = squareDe > 0 ? squareDe : 0;
y3 = (float)(y + Mathf.Sqrt((float)squareDe));
squareDe = r * r - Mathf.Pow(x - x4, 2);
squareDe = squareDe > 0 ? squareDe : 0;
y4 = (float)(y - Mathf.Sqrt((float)squareDe));
//绘制线段
GL.Begin(GL.LINES);
GL.Color(Color.blue);
GL.Vertex(new Vector3(x1 / Screen.width, y1 / Screen.height, z));
GL.Vertex(new Vector3(x3 / Screen.width, y3 / Screen.height, z));
GL.End();
GL.Begin(GL.LINES);
GL.Color(Color.blue);
GL.Vertex(new Vector3(x2 / Screen.width, y1 / Screen.height, z));
GL.Vertex(new Vector3(x4 / Screen.width, y3 / Screen.height, z));
GL.End();
GL.Begin(GL.LINES);
GL.Color(Color.blue);
GL.Vertex(new Vector3(x1 / Screen.width, y2 / Screen.height, z));
GL.Vertex(new Vector3(x3 / Screen.width, y4 / Screen.height, z));
GL.End();
GL.Begin(GL.LINES);
GL.Color(Color.blue);
GL.Vertex(new Vector3(x2 / Screen.width, y2 / Screen.height, z));
GL.Vertex(new Vector3(x4 / Screen.width, y4 / Screen.height, z));
GL.End();
x1 = x3;
x2 = x4;
y1 = y3;
y2 = y4;
}
GL.PopMatrix();
}
public static void DrawCircle(Vector2 center, int radius, Color color, float width, int segmentsPerQuarter) {
DrawCircle(center, radius, color, width, false, segmentsPerQuarter);
}
public static void DrawCircle(Vector2 center, int radius, Color color, float width, bool antiAlias, int segmentsPerQuarter) {
float rh = (float)radius / 2;
Vector2 p1 = new Vector2(center.x, center.y - radius);
Vector2 p1_tan_a = new Vector2(center.x - rh, center.y - radius);
Vector2 p1_tan_b = new Vector2(center.x + rh, center.y - radius);
Vector2 p2 = new Vector2(center.x + radius, center.y);
Vector2 p2_tan_a = new Vector2(center.x + radius, center.y - rh);
Vector2 p2_tan_b = new Vector2(center.x + radius, center.y + rh);
Vector2 p3 = new Vector2(center.x, center.y + radius);
Vector2 p3_tan_a = new Vector2(center.x - rh, center.y + radius);
Vector2 p3_tan_b = new Vector2(center.x + rh, center.y + radius);
Vector2 p4 = new Vector2(center.x - radius, center.y);
Vector2 p4_tan_a = new Vector2(center.x - radius, center.y - rh);
Vector2 p4_tan_b = new Vector2(center.x - radius, center.y + rh);
DrawBezierLine(p1, p1_tan_b, p2, p2_tan_a, color, width, antiAlias, segmentsPerQuarter);
DrawBezierLine(p2, p2_tan_b, p3, p3_tan_b, color, width, antiAlias, segmentsPerQuarter);
DrawBezierLine(p3, p3_tan_a, p4, p4_tan_b, color, width, antiAlias, segmentsPerQuarter);
DrawBezierLine(p4, p4_tan_a, p1, p1_tan_a, color, width, antiAlias, segmentsPerQuarter);
}
// Other than method name, DrawBezierLine is unchanged from Linusmartensson's original implementation.
public static void DrawBezierLine(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 + 1; ++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;
}
// This static initializer works for runtime, but apparently isn't called when
// Editor play mode stops, so DrawLine will re-initialize if needed.
static Drawing()
{
Initialize();
}
private static void Initialize()
{
if (lineTex == null)
{
lineTex = new Texture2D(1, 1, TextureFormat.ARGB32, false);
lineTex.SetPixel(0, 1, Color.white);
lineTex.Apply();
}
if (aaLineTex == null)
{
// TODO: better anti-aliasing of wide lines with a larger texture? or use Graphics.DrawTexture with border settings
aaLineTex = new Texture2D(1, 3, TextureFormat.ARGB32, false);
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();
}
// GUI.blitMaterial and GUI.blendMaterial are used internally by GUI.DrawTexture,
// depending on the alphaBlend parameter. Use reflection to "borrow" these references.
blitMaterial = (Material)typeof(GUI).GetMethod("get_blitMaterial", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, null);
blendMaterial = (Material)typeof(GUI).GetMethod("get_blendMaterial", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, null);
}
}