4 using System.Collections.Generic;
5 using System.Collections.Immutable;
6 using Microsoft.Xna.Framework;
7 using Microsoft.Xna.Framework.Graphics;
11 internal class CircuitBoxWireRenderer
13 private const int VertsPerQuad = 4,
15 VertsPerLine = QuadsPerLine * VertsPerQuad,
16 TotalVertsPerWire = VertsPerLine * 2;
18 private readonly Texture2D texture;
20 private VertexPositionColorTexture[] verts =
new VertexPositionColorTexture[TotalVertsPerWire];
21 private readonly Vector2[][] colliders =
new Vector2[2][];
22 private SquareLine skeleton;
24 private Vector2 lastStart, lastEnd;
25 private Color lastColor;
26 private readonly Option<CircuitBoxWire> wire;
28 public CircuitBoxWireRenderer(Option<CircuitBoxWire> wire, Vector2 start, Vector2 end, Color color, Sprite? wireSprite)
31 texture = wireSprite?.Texture ?? GUI.WhiteTexture;
32 Recompute(start, end, color);
35 private void UpdateColor(Color color)
37 for (
int i = 0; i < TotalVertsPerWire; i++)
39 verts[i].Color = color;
45 public void Recompute(Vector2 start, Vector2 end, Color color)
47 if (MathUtils.NearlyEqual(lastStart, start) && MathUtils.NearlyEqual(lastEnd, end))
49 if (lastColor == color) {
return; }
59 skeleton = ToolBox.GetSquareLineBetweenPoints(start, end, CircuitBoxSizes.WireKnobLength);
60 var points = skeleton.Points;
62 Vector2 centerOfLine = (points[2] + points[3]) / 2f;
64 ImmutableArray<Vector2> points1 = GetLinePoints(points[1], points[2], centerOfLine),
65 points2 = GetLinePoints(centerOfLine, points[3], points[4]);
67 colliders[0] = ConstructQuads(ref verts, 0, points1, color);
68 colliders[1] = ConstructQuads(ref verts, VertsPerLine, points2, color);
70 static ImmutableArray<Vector2> GetLinePoints(Vector2 start, Vector2 control, Vector2 end)
72 var points = ImmutableArray.CreateBuilder<Vector2>(QuadsPerLine);
73 for (
int i = 0; i < QuadsPerLine; i++)
75 float t = (float)i / (QuadsPerLine - 1);
76 Vector2 pos = MathUtils.Bezier(start, control, end, t);
80 return points.ToImmutable();
83 static Vector2[] ConstructQuads(ref VertexPositionColorTexture[] verts,
int startOffset, IReadOnlyList<Vector2> points, Color color)
86 var collider =
new Vector2[VertsPerLine - VertsPerQuad];
88 int leftIndex = collider.Length - 1,
92 const float halfWidth = CircuitBoxSizes.WireWidth / 2f;
95 for (
int i = 0; i < points.Count - 1; i++)
97 bool isFirst = i == 0 && startOffset == 0,
98 isLast = i == points.Count - 2 && startOffset > 0;
100 Vector2 start = points[i],
103 Vector2 dir = Vector2.Normalize(end - start);
104 Vector2 length =
new Vector2(dir.Y, -dir.X) * halfWidth;
106 int vertIndex = startOffset + i * 4;
108 Vector2 topRight = end + length;
109 Vector2 topLeft = end - length;
115 int prevIndex = vertIndex - 4;
117 if ((prevIndex - startOffset) >= 0)
120 Vector3 prevTopRight = verts[
TopRight(prevIndex)].Position,
121 prevTopLeft = verts[
TopLeft(prevIndex)].Position;
123 bottomRight = ToVector2(prevTopRight);
124 bottomLeft = ToVector2(prevTopLeft);
128 bottomRight = start + length;
129 bottomLeft = start - length;
134 if (MathF.Abs(dir.Y) > MathF.Abs(dir.X))
136 float offset = dir.Y < 0 ? halfWidth : -halfWidth;
138 bottomRight.Y = start.Y - offset;
139 bottomLeft.Y = start.Y - offset;
144 bottomRight.X = start.X;
145 bottomLeft.X = start.X;
150 if (MathF.Abs(dir.Y) > MathF.Abs(dir.X))
152 float offset = dir.Y < 0 ? halfWidth : -halfWidth;
154 topRight.Y = end.Y + offset;
155 topLeft.Y = end.Y + offset;
165 collider[rightIndex++] = bottomLeft;
166 collider[rightIndex++] = topLeft;
168 collider[leftIndex--] = bottomRight;
169 collider[leftIndex--] = topRight;
172 Vector2 uvTopRight =
new Vector2(0, 1),
173 uvTopLeft =
new Vector2(0, 0),
174 uvBottomRight =
new Vector2(1, 1),
175 uvBottomLeft =
new Vector2(1, 0);
177 SetPos(ref verts,
TopRight(vertIndex), topRight, color, uvTopRight);
178 SetPos(ref verts,
TopLeft(vertIndex), topLeft, color, uvTopLeft);
179 SetPos(ref verts,
BottomRight(vertIndex), bottomRight, color, uvBottomRight);
180 SetPos(ref verts,
BottomLeft(vertIndex), bottomLeft, color, uvBottomLeft);
182 static void SetPos(ref VertexPositionColorTexture[] verts,
int index, Vector2 pos, Color color, Vector2 uv)
184 verts[index].Position = ToVector3(pos);
185 verts[index].Color = color;
186 verts[index].TextureCoordinate = uv;
187 static Vector3 ToVector3(Vector2 v) =>
new Vector3(v.X, v.Y, 0f);
190 static int TopRight(
int vertIndex) => vertIndex;
191 static int TopLeft(
int vertIndex) => vertIndex + 1;
192 static int BottomRight(
int vertIndex) => vertIndex + 2;
193 static int BottomLeft(
int vertIndex) => vertIndex + 3;
195 static Vector2 ToVector2(Vector3 v) =>
new Vector2(v.X, v.Y);
202 public bool Contains(Vector2 pos)
205 foreach (Vector2[] collider
in colliders)
207 if (ToolBox.PointIntersectsWithPolygon(pos, collider, checkBoundingBox:
false)) {
return true; }
213 public void Draw(SpriteBatch spriteBatch, Color selectionColor)
215 if (GameMain.DebugDraw)
217 for (
int i = 0; i < skeleton.Points.Length; i++)
219 Vector2 point = skeleton.Points[i];
220 spriteBatch.DrawPoint(point, Color.White, 25f);
221 GUI.DrawString(spriteBatch, point -
new Vector2(5f, 17f), i.ToString(), Color.Black, font: GUIStyle.LargeFont);
224 spriteBatch.DrawLine(skeleton.Points[0], skeleton.Points[1], GUIStyle.Green, thickness: 2f);
225 spriteBatch.DrawLine(skeleton.Points[1], skeleton.Points[2], GUIStyle.Green, thickness: 2f);
226 spriteBatch.DrawLine(skeleton.Points[2], skeleton.Points[3], GUIStyle.Green, thickness: 2f);
227 spriteBatch.DrawLine(skeleton.Points[3], skeleton.Points[4], GUIStyle.Green, thickness: 2f);
228 spriteBatch.DrawLine(skeleton.Points[4], skeleton.Points[5], GUIStyle.Green, thickness: 2f);
231 bool isSelected = wire.TryUnwrap(out var w) && w.IsSelected;
235 foreach (var colliderPolys
in colliders)
237 spriteBatch.DrawPolygon(Vector2.Zero, colliderPolys, selectionColor, 5f);
241 spriteBatch.Draw(texture, verts, 0f);
243 if (skeleton.Type is SquareLine.LineType.SixPointBackwardsLine)
246 Vector2 expandedEnd = skeleton.Points[1],
247 expandedStart = skeleton.Points[4];
249 expandedEnd.X += CircuitBoxSizes.WireWidth / 2f;
250 expandedStart.X -= CircuitBoxSizes.WireWidth / 2f;
252 spriteBatch.DrawLineWithTexture(texture, skeleton.Points[0], expandedEnd, lastColor, thickness: CircuitBoxSizes.WireWidth);
253 spriteBatch.DrawLineWithTexture(texture, expandedStart, skeleton.Points[5], lastColor, thickness: CircuitBoxSizes.WireWidth);
255 const float rectSize = CircuitBoxSizes.WireWidth * 1.5f;
256 RectangleF startKnob =
new RectangleF(skeleton.Points[1] -
new Vector2(rectSize / 2f),
new Vector2(rectSize)),
257 endKnob =
new RectangleF(skeleton.Points[4] -
new Vector2(rectSize / 2f),
new Vector2(rectSize));
259 GUI.DrawFilledRectangle(spriteBatch, startKnob, lastColor);
260 GUI.DrawFilledRectangle(spriteBatch, endKnob, lastColor);
263 if (!GameMain.DebugDraw) {
return; }
265 foreach (var colliderPolys
in colliders)
267 spriteBatch.DrawPolygonInner(Vector2.Zero, colliderPolys, Color.Lime, 1f);