context: #region Copyright & License Information
  /*
   * Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
   * This file is part of OpenRA, which is free software. It is made
   * available to you under the terms of the GNU General Public License
   * as published by the Free Software Foundation, either version 3 of
   * the License, or (at your option) any later version. For more
   * information, see COPYING.
   */
  #endregion
  
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using OpenRA.Primitives;
  
  namespace OpenRA.Graphics
  {
  	public class RgbaColorRenderer
  	{
  		static readonly float3 Offset = new float3(0.5f, 0.5f, 0f);
  
  		readonly SpriteRenderer parent;
  		readonly Vertex[] vertices = new Vertex[6];
  
  		public RgbaColorRenderer(SpriteRenderer parent)
  		{
  			this.parent = parent;
  		}
  
  		public void DrawLine(float3 start, float3 end, float width, Color startColor, Color endColor)
  		{
  			var delta = (end - start) / (end - start).XY.Length;
  			var corner = width / 2 * new float3(-delta.Y, delta.X, delta.Z);
  
  			startColor = Util.PremultiplyAlpha(startColor);
  			var sr = startColor.R / 255.0f;
  			var sg = startColor.G / 255.0f;
  			var sb = startColor.B / 255.0f;
  			var sa = startColor.A / 255.0f;
  
  			endColor = Util.PremultiplyAlpha(endColor);
  			var er = endColor.R / 255.0f;
  			var eg = endColor.G / 255.0f;
  			var eb = endColor.B / 255.0f;
  			var ea = endColor.A / 255.0f;
  
  			vertices[0] = new Vertex(start - corner + Offset, sr, sg, sb, sa, 0, 0);
  			vertices[1] = new Vertex(start + corner + Offset, sr, sg, sb, sa, 0, 0);
  			vertices[2] = new Vertex(end + corner + Offset, er, eg, eb, ea, 0, 0);
  			vertices[3] = new Vertex(end + corner + Offset, er, eg, eb, ea, 0, 0);
  			vertices[4] = new Vertex(end - corner + Offset, er, eg, eb, ea, 0, 0);
  			vertices[5] = new Vertex(start - corner + Offset, sr, sg, sb, sa, 0, 0);
  
  			parent.DrawRGBAVertices(vertices);
  		}
  
  		public void DrawLine(float3 start, float3 end, float width, Color color)
  		{
  			var delta = (end - start) / (end - start).XY.Length;
  			var corner = width / 2 * new float2(-delta.Y, delta.X);
  
  			color = Util.PremultiplyAlpha(color);
  			var r = color.R / 255.0f;
  			var g = color.G / 255.0f;
  			var b = color.B / 255.0f;
  			var a = color.A / 255.0f;
  
  			vertices[0] = new Vertex(start - corner + Offset, r, g, b, a, 0, 0);
  			vertices[1] = new Vertex(start + corner + Offset, r, g, b, a, 0, 0);
  			vertices[2] = new Vertex(end + corner + Offset, r, g, b, a, 0, 0);
  			vertices[3] = new Vertex(end + corner + Offset, r, g, b, a, 0, 0);
  			vertices[4] = new Vertex(end - corner + Offset, r, g, b, a, 0, 0);
  			vertices[5] = new Vertex(start - corner + Offset, r, g, b, a, 0, 0);
  			parent.DrawRGBAVertices(vertices);
  		}
  
  		/// <summary>
  		/// Calculate the 2D intersection of two lines.
  		/// Will behave badly if the lines are parallel.
  		/// Z position is the average of a and b (ignores actual intersection point if it exists)
  		/// </summary>
  		float3 IntersectionOf(float3 a, float3 da, float3 b, float3 db)
  		{
  			var crossA = a.X * (a.Y + da.Y) - a.Y * (a.X + da.X);
  			var crossB = b.X * (b.Y + db.Y) - b.Y * (b.X + db.X);
  			var x = da.X * crossB - db.X * crossA;
  			var y = da.Y * crossB - db.Y * crossA;
  			var d = da.X * db.Y - da.Y * db.X;
  			return new float3(x / d, y / d, 0.5f * (a.Z + b.Z));
  		}
  
  		void DrawDisconnectedLine(IEnumerable<float3> points, float width, Color color)
  		{
  			using (var e = points.GetEnumerator())
  			{
  				if (!e.MoveNext())
  					return;
  
  				var lastPoint = e.Current;
  				while (e.MoveNext())
  				{
  					var point = e.Current;
  					DrawLine(lastPoint, point, width, color);
  					lastPoint = point;
  				}
  			}
  		}
  
  		void DrawConnectedLine(float3[] points, float width, Color color, bool closed)
  		{
  			// Not a line
  			if (points.Length < 2)
  				return;
  
  			// Single segment
  			if (points.Length == 2)
  			{
  				DrawLine(points[0], points[1], width, color);
  				return;
  			}
  
  			color = Util.PremultiplyAlpha(color);
  			var r = color.R / 255.0f;
  			var g = color.G / 255.0f;
  			var b = color.B / 255.0f;
  			var a = color.A / 255.0f;
  
  			var start = points[0];
  			var end = points[1];
  			var dir = (end - start) / (end - start).XY.Length;
  			var corner = width / 2 * new float3(-dir.Y, dir.X, dir.Z);
  
  			// Corners for start of line segment
  			var ca = start - corner;
  			var cb = start + corner;
  
  			// Segment is part of closed loop
  			if (closed)
  			{
  				var prev = points[points.Length - 1];
  				var prevDir = (start - prev) / (start - prev).XY.Length;
  				var prevCorner = width / 2 * new float3(-prevDir.Y, prevDir.X, prevDir.Z);
  				ca = IntersectionOf(start - prevCorner, prevDir, start - corner, dir);
  				cb = IntersectionOf(start + prevCorner, prevDir, start + corner, dir);
  			}
  
  			var limit = closed ? points.Length : points.Length - 1;
  			for (var i = 0; i < limit; i++)
  			{
  				var next = points[(i + 2) % points.Length];
  				var nextDir = (next - end) / (next - end).XY.Length;
  				var nextCorner = width / 2 * new float3(-nextDir.Y, nextDir.X, nextDir.Z);
  
  				// Vertices for the corners joining start-end to end-next
  				var cc = closed || i < limit ? IntersectionOf(end + corner, dir, end + nextCorner, nextDir) : end + corner;
  				var cd = closed || i < limit ? IntersectionOf(end - corner, dir, end - nextCorner, nextDir) : end - corner;
  
  				// Fill segment
  				vertices[0] = new Vertex(ca + Offset, r, g, b, a, 0, 0);
  				vertices[1] = new Vertex(cb + Offset, r, g, b, a, 0, 0);
  				vertices[2] = new Vertex(cc + Offset, r, g, b, a, 0, 0);
  				vertices[3] = new Vertex(cc + Offset, r, g, b, a, 0, 0);
  				vertices[4] = new Vertex(cd + Offset, r, g, b, a, 0, 0);
  				vertices[5] = new Vertex(ca + Offset, r, g, b, a, 0, 0);
  				parent.DrawRGBAVertices(vertices);
  
  				// Advance line segment
  				end = next;
  				dir = nextDir;
  				corner = nextCorner;
  
  				ca = cd;
  				cb = cc;
  			}
  		}
  
  		public void DrawLine(IEnumerable<float3> points, float width, Color color, bool connectSegments = false)
  		{
  			if (!connectSegments)
  				DrawDisconnectedLine(points, width, color);
  			else
  				DrawConnectedLine(points as float3[] ?? points.ToArray(), width, color, false);
  		}
  
  		public void DrawPolygon(float3[] vertices, float width, Color color)
  		{
  			DrawConnectedLine(vertices, width, color, true);
  		}
  
  		public void DrawPolygon(float2[] vertices, float width, Color color)
  		{
  			DrawConnectedLine(vertices.Select(v => new float3(v, 0)).ToArray(), width, color, true);
  		}
  
  		public void DrawRect(float3 tl, float3 br, float width, Color color)
  		{
  			var tr = new float3(br.X, tl.Y, tl.Z);
  			var bl = new float3(tl.X, br.Y, br.Z);
  			DrawPolygon(new[] { tl, tr, br, bl }, width, color);
  		}
  
  		public void FillTriangle(float3 a, float3 b, float3 c, Color color)
  		{
  			color = Util.PremultiplyAlpha(color);
  			var cr = color.R / 255.0f;
  			var cg = color.G / 255.0f;
  			var cb = color.B / 255.0f;
  			var ca = color.A / 255.0f;
  
  			vertices[0] = new Vertex(a + Offset, cr, cg, cb, ca, 0, 0);
  
gt: 			vertices[1] = new Vertex(b + Offset, cr, cg, cb, ca, 0, 0);