using System.Collections.Generic; using System.Linq; using System.Diagnostics; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System.Collections; using System; public class Line { private List points; private float thickness; public enum JoinType { None, Miter, Bevel, Round } public enum LineCaps { None, Round } private Color LineColor { get; set; } private LineCaps LineEndings { get; set; } private JoinType JoinTypes { get; set; } //Graphical representation private VertexBuffer vbo; private IndexBuffer ibo; private GraphicsDevice gDevice; private class LinkedMeshList : IEnumerable { public int Count { get; set; } public LinkedMeshListNode FirstNode { get; private set; } public LinkedMeshListNode LastNode { get; private set; } public LinkedMeshList() { this.Count = 0; } public void Remove(LinkedMeshListNode node) { throw new NotImplementedException(); } public void AddBefore(LinkedMeshListNode node, LinkedMeshListNode beforeNode) { var oldBefore = node.Previous; oldBefore.Next = beforeNode; beforeNode.Previous = oldBefore; beforeNode.Next = node; node.Previous = beforeNode; Count++; } public void AddAfter(LinkedMeshListNode node, LinkedMeshListNode afterNode) { var oldNext = node.Next; node.Next = afterNode; afterNode.Previous = node; afterNode.Next = oldNext; oldNext.Previous = afterNode; Count++; } public void Append(LinkedMeshListNode node) { if(this.Count == 0) { this.FirstNode = node; this.LastNode = node; Count++; return; } this.LastNode.Next = node; node.Previous = this.LastNode; this.LastNode = node; this.Count++; } public List GetVerticeList() { List verticesList = new List(); foreach(var node in this) { verticesList.AddRange(node.GenerateVerticeList()); } return verticesList; } public List GetIndiceList() { List indeicesList = new List(); foreach(var node in this) { indeicesList.AddRange(node.GenerateIndiceList()); } return indeicesList; } public IEnumerator GetEnumerator() { LinkedMeshListNode curNode = this.FirstNode; while(curNode != null) { yield return curNode; curNode = curNode.Next; } } IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } } private abstract class LinkedMeshListNode { public LinkedMeshListNode Next { get; set; } private LinkedMeshListNode previous; public LinkedMeshListNode Previous { get { return this.previous; } set { this.IndexOffset = value.IndexOffset + value.GetVertexCount(); this.previous = value; } } public int IndexOffset { get; private set; } public abstract int GetVertexCount(); public abstract List GenerateVerticeList(); public abstract List GenerateIndiceList(); public LinkedMeshListNode() { this.IndexOffset = 0; } } private class Segment : LinkedMeshListNode { public Vector2 Start { get; private set; } public Vector2 End { get; private set; } public Vector2 Normal { get; private set; } public const ushort INDEX_BL = 0; public const ushort INDEX_BR = 1; public const ushort INDEX_TL = 2; public const ushort INDEX_TR = 3; private Color color { get; set; } public Segment(Vector2 start, Vector2 end, Color color) : base() { this.Start = start; this.End = end; Vector2 dir = end - start; Vector2 perpendincularVector = new Vector2(dir.Y, -dir.X); perpendincularVector.Normalize(); this.Normal = perpendincularVector; this.color = color; } public override List GenerateVerticeList() { List vertices = new List(); var offsetPoint1 = new Vector3(this.Start.X, this.Start.Y ,0.0f); vertices.Add(new VertexPositionColorTexture(offsetPoint1, this.color, this.Normal)); vertices.Add(new VertexPositionColorTexture(offsetPoint1, this.color, this.Normal * -1)); var offsetPoint1Next = new Vector3(this.End.X, this.End.Y ,0.0f); vertices.Add(new VertexPositionColorTexture(offsetPoint1Next, this.color, this.Normal)); vertices.Add(new VertexPositionColorTexture(offsetPoint1Next, this.color, this.Normal * -1)); return vertices; } public override List GenerateIndiceList() { List indices = new List(); indices.Add((ushort)(this.IndexOffset + INDEX_BL)); indices.Add((ushort)(this.IndexOffset + INDEX_BR)); indices.Add((ushort)(this.IndexOffset + INDEX_TL)); indices.Add((ushort)(this.IndexOffset + INDEX_TL)); indices.Add((ushort)(this.IndexOffset + INDEX_BR)); indices.Add((ushort)(this.IndexOffset + INDEX_TR)); return indices; } public override int GetVertexCount() { return 4; } } private class BevelLineJoin : LinkedMeshListNode { private Color color { get; set; } public BevelLineJoin(Color color) : base() { this.color = color; } public override List GenerateIndiceList() { Debug.Assert(this.Previous != null); Debug.Assert(this.Previous is Segment); Debug.Assert(this.Next != null); Debug.Assert(this.Next is Segment); var next = this.Next as Segment; var previous = this.Previous as Segment; ushort lastOffset = (ushort) previous.IndexOffset; ushort nextOffset = (ushort) next.IndexOffset; List indices = new List(); indices.Add((ushort)(lastOffset+Segment.INDEX_TL)); indices.Add((ushort)(lastOffset+Segment.INDEX_TR)); indices.Add((ushort)(nextOffset+Segment.INDEX_BL)); indices.Add((ushort)(lastOffset+Segment.INDEX_TL)); indices.Add((ushort)(lastOffset+Segment.INDEX_TR)); indices.Add((ushort)(nextOffset+Segment.INDEX_BR)); return indices; } public override List GenerateVerticeList() { return new List(); } public override int GetVertexCount() { return 0; } } public Line(List points) : this(points, 1.0f) {} public Line(List points, float thickness) : this(points, thickness, Color.Black) {} public Line(List points, float thickness, Color color) : this(points,thickness, color, JoinType.None) {} public Line(List points, float thickness, Color color, JoinType joinType) : this(points, thickness, color, joinType, LineCaps.None) {} public Line(List points, float thickness, Color color, JoinType joinType, LineCaps caps) { this.points = points; this.thickness = thickness; this.LineColor = color; this.JoinTypes = joinType; this.LineEndings = caps; } public void InitOnGraphicalDevice(GraphicsDevice device) { this.gDevice = device; Debug.Assert(this.points.Count >= 2); LinkedMeshList meshList = new LinkedMeshList(); for(int i = 0; i < this.points.Count - 1 ; i++) { var p = this.points[i]; var pNext = this.points[i+1]; meshList.Append(new Segment(p, pNext, this.LineColor)); } var curNode = meshList.FirstNode; while(curNode != null && curNode.Next != null) { meshList.AddAfter(curNode, new BevelLineJoin(this.LineColor)); curNode = curNode.Next.Next; } List vertices = meshList.GetVerticeList(); List indices = meshList.GetIndiceList(); this.vbo = new VertexBuffer(device, typeof(VertexPositionColorTexture), vertices.Count, BufferUsage.WriteOnly); this.vbo.SetData(vertices.ToArray()); this.ibo = new IndexBuffer(device, typeof(ushort), indices.Count,BufferUsage.WriteOnly); this.ibo.SetData(indices.ToArray()); } public void Draw(Effect lineEffect) { this.gDevice.SetVertexBuffer(this.vbo); this.gDevice.Indices = this.ibo; RasterizerState rasterizerState = new RasterizerState(); // rasterizerState.CullMode = CullMode.None; //rasterizerState.FillMode = FillMode.WireFrame; this.gDevice.RasterizerState = rasterizerState; lineEffect.Parameters["thickness"].SetValue(this.thickness); foreach (EffectPass pass in lineEffect.CurrentTechnique.Passes) { pass.Apply(); this.gDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList,0,0,this.ibo.IndexCount / 3); //this.gDevice.DrawPrimitives(PrimitiveType.LineList, 0, this.vbo.VertexCount); } } }