Skip to content

Commit 9896d33

Browse files
authored
Merge pull request #109 from DaveGreen-Games/striped-points-generation-functions
Striped Segments Generation Functions
2 parents 490fecf + 0030712 commit 9896d33

File tree

7 files changed

+1876
-29
lines changed

7 files changed

+1876
-29
lines changed

ShapeEngine/Geometry/StripedDrawingDef/StripedDrawing.cs

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1+
using System.Drawing;
2+
using System.Numerics;
3+
using ShapeEngine.Geometry.CircleDef;
14
using ShapeEngine.Geometry.CollisionSystem;
5+
using ShapeEngine.Geometry.PolygonDef;
6+
using ShapeEngine.Geometry.QuadDef;
7+
using ShapeEngine.Geometry.RectDef;
8+
using ShapeEngine.Geometry.SegmentDef;
9+
using ShapeEngine.Geometry.SegmentsDef;
10+
using ShapeEngine.Geometry.TriangleDef;
211

312
namespace ShapeEngine.Geometry.StripedDrawingDef;
413

@@ -9,4 +18,177 @@ namespace ShapeEngine.Geometry.StripedDrawingDef;
918
public static partial class StripedDrawing
1019
{
1120
private static IntersectionPoints intersectionPointsReference = new IntersectionPoints(6);
21+
22+
23+
/// <summary>
24+
/// Draws every segment in <see cref="Segments"/> using the provided <see cref="LineDrawingInfo"/>.
25+
/// </summary>
26+
/// <param name="segments">The segments collection to render.</param>
27+
/// <param name="info">Line drawing parameters applied to each segment.</param>
28+
public static void DrawGeneratedSegments(this Segments segments, LineDrawingInfo info)
29+
{
30+
foreach (var segment in segments)
31+
{
32+
segment.Draw(info);
33+
}
34+
}
35+
/// <summary>
36+
/// Draws the given <see cref="Segments"/> using two alternating <see cref="LineDrawingInfo"/> instances.
37+
/// The first segment uses <paramref name="striped"/>, the second uses <paramref name="alternatingStriped"/>,
38+
/// and the pattern repeats for the remaining segments.
39+
/// </summary>
40+
/// <param name="segments">The collection of segments to render.</param>
41+
/// <param name="striped">Line drawing parameters applied to even-indexed segments (0-based).</param>
42+
/// <param name="alternatingStriped">Line drawing parameters applied to odd-indexed segments.</param>
43+
public static void DrawGeneratedSegments(this Segments segments, LineDrawingInfo striped, LineDrawingInfo alternatingStriped)
44+
{
45+
int i = 0;
46+
foreach (var segment in segments)
47+
{
48+
var info = i % 2 == 0 ? striped : alternatingStriped;
49+
segment.Draw(info);
50+
i++;
51+
}
52+
}
53+
/// <summary>
54+
/// Draws the given <see cref="Segments"/> using a repeating sequence of <see cref="LineDrawingInfo"/>
55+
/// instances supplied in <paramref name="alternatingInfo"/>. The segment at index <c>i</c> uses
56+
/// <c>alternatingInfo[i % alternatingInfo.Length]</c>.
57+
/// </summary>
58+
/// <param name="segments">The collection of segments to render.</param>
59+
/// <param name="alternatingInfo">One or more <see cref="LineDrawingInfo"/> instances used in round-robin order.
60+
/// Must contain at least one element.</param>
61+
public static void DrawGeneratedSegments(this Segments segments, params LineDrawingInfo[] alternatingInfo)
62+
{
63+
if (alternatingInfo.Length == 0) return;
64+
65+
var i = 0;
66+
foreach (var segment in segments)
67+
{
68+
var infoIndex = i % alternatingInfo.Length;
69+
var info = alternatingInfo[infoIndex];
70+
segment.Draw(info);
71+
i++;
72+
}
73+
}
74+
/// <summary>
75+
/// Generates striped segments for an outer shape while optionally excluding areas covered by an inner shape.
76+
/// The method dispatches to specific overloads for supported shape types (Circle, Triangle, Rectangle, Quad, Polygon).
77+
/// </summary>
78+
/// <typeparam name="TO">Type of the outside shape.
79+
/// Only allowed shapes are: <see cref="Circle"/>, <see cref="Triangle"/>, <see cref="Rect"/>, <see cref="Quad"/>, <see cref="Polygon"/>).
80+
/// </typeparam>
81+
/// <typeparam name="TI">Type of the inside (excluded) shape.
82+
/// Only allowed shapes are: <see cref="Circle"/>, <see cref="Triangle"/>, <see cref="Rect"/>, <see cref="Quad"/>, <see cref="Polygon"/>).
83+
/// </typeparam>
84+
/// <param name="outsideShape">The outer shape to fill with stripe lines.</param>
85+
/// <param name="insideShape">The inner shape to exclude from stripes (may be null or of a supported type).</param>
86+
/// <param name="spacing">Distance between adjacent stripe lines in the same units as shape coordinates.</param>
87+
/// <param name="angleDeg">Stripe angle in degrees, measured from the x-axis.</param>
88+
/// <param name="spacingOffset">Optional offset to shift the stripe pattern along its perpendicular direction.</param>
89+
/// <returns>A <see cref="Segments"/> collection containing the generated stripe segments; may be empty if no stripes are produced.</returns>
90+
public static Segments GenerateStripedSegments<TO, TI>(TO outsideShape, TI insideShape, float spacing, float angleDeg, float spacingOffset = 0f)
91+
{
92+
if (outsideShape is Circle circle) return GenerateStripedSegments(circle, insideShape, spacing, angleDeg, spacingOffset);
93+
if(outsideShape is Triangle triangle) return GenerateStripedSegments(triangle, insideShape, spacing, angleDeg, spacingOffset);
94+
if(outsideShape is Rect rect) return GenerateStripedSegments(rect, insideShape, spacing, angleDeg, spacingOffset);
95+
if(outsideShape is Quad quad) return GenerateStripedSegments(quad, insideShape, spacing, angleDeg, spacingOffset);
96+
if(outsideShape is Polygon polygon) return GenerateStripedSegments(polygon, insideShape, spacing, angleDeg, spacingOffset);
97+
98+
return [];
99+
}
100+
101+
102+
103+
/// <summary>
104+
/// Adds one or two segments to the given <see cref="Segments"/> collection based on the
105+
/// intersection points from an outside shape and an inside shape for a single stripe line.
106+
/// </summary>
107+
/// <param name="cur">Current sample point (origin) used to determine distances for sorting intersections.</param>
108+
/// <param name="outsideShapePoints">Tuple of two intersection points where the stripe intersects the outside shape.</param>
109+
/// <param name="insideShapePoints">Tuple of up to two intersection points where the stripe intersects the inside shape.</param>
110+
/// <param name="segments">Reference to the <see cref="Segments"/> collection to which resulting segments will be added.</param>
111+
/// <remarks>
112+
/// The method compares squared distances from <paramref name="cur"/> to determine closest/furthest
113+
/// points of both outside and inside intersections. Based on those comparisons it decides which
114+
/// sub-segments of the outside shape should be drawn to exclude regions inside the inside shape.
115+
/// If the inside shape fully covers the outside segment, no segment is added.
116+
/// </remarks>
117+
private static void AddSegmentsHelper(Vector2 cur, (IntersectionPoint a, IntersectionPoint b) outsideShapePoints, (IntersectionPoint a, IntersectionPoint b) insideShapePoints, ref Segments segments)
118+
{
119+
var outsideDisA = (outsideShapePoints.a.Point - cur).LengthSquared();
120+
var outsideDisB = (outsideShapePoints.b.Point - cur).LengthSquared();
121+
float outsideFurthestDis;
122+
float outsideClosestDis;
123+
Vector2 outsideFurthestPoint;
124+
Vector2 outsideClosestPoint;
125+
126+
if (outsideDisA < outsideDisB)
127+
{
128+
outsideFurthestDis = outsideDisB;
129+
outsideClosestDis = outsideDisA;
130+
outsideFurthestPoint = outsideShapePoints.b.Point;
131+
outsideClosestPoint = outsideShapePoints.a.Point;
132+
}
133+
else
134+
{
135+
outsideFurthestDis = outsideDisA;
136+
outsideClosestDis = outsideDisB;
137+
outsideFurthestPoint = outsideShapePoints.a.Point;
138+
outsideClosestPoint = outsideShapePoints.b.Point;
139+
}
140+
141+
var insideDisA = insideShapePoints.a.Valid ? (insideShapePoints.a.Point - cur).LengthSquared() : 0f;
142+
var insideDisB = insideShapePoints.b.Valid ? (insideShapePoints.b.Point - cur).LengthSquared() : 0f;
143+
float insideFurthestDis;
144+
float insideClosestDis;
145+
Vector2 insideFurthestPoint;
146+
Vector2 insideClosestPoint;
147+
148+
if (insideDisA < insideDisB)
149+
{
150+
insideFurthestDis = insideDisB;
151+
insideClosestDis = insideDisA;
152+
insideFurthestPoint = insideShapePoints.b.Point;
153+
insideClosestPoint = insideShapePoints.a.Point;
154+
}
155+
else
156+
{
157+
insideFurthestDis = insideDisA;
158+
insideClosestDis = insideDisB;
159+
insideFurthestPoint = insideShapePoints.a.Point;
160+
insideClosestPoint = insideShapePoints.b.Point;
161+
}
162+
163+
//both outside shape points are inside the inside shape -> no drawing possible
164+
if (insideFurthestDis > outsideFurthestDis && insideClosestDis < outsideClosestDis)
165+
{
166+
return;
167+
}
168+
169+
if (insideClosestDis > outsideFurthestDis || insideFurthestDis < outsideClosestDis)
170+
{
171+
var segment = new Segment(outsideClosestPoint, outsideFurthestPoint);
172+
segments.Add(segment);
173+
}
174+
else if (insideFurthestDis > outsideFurthestDis)
175+
{
176+
var segment = new Segment(outsideClosestPoint, insideClosestPoint);
177+
segments.Add(segment);
178+
}
179+
else if (insideClosestDis < outsideClosestDis)
180+
{
181+
var segment = new Segment(insideFurthestPoint, outsideFurthestPoint);
182+
segments.Add(segment);
183+
}
184+
else
185+
{
186+
var segment1 = new Segment(outsideClosestPoint, insideClosestPoint);
187+
segments.Add(segment1);
188+
189+
var segment2 = new Segment(insideFurthestPoint, outsideFurthestPoint);
190+
segments.Add(segment2);
191+
}
192+
}
193+
12194
}

0 commit comments

Comments
 (0)