1+ using System . Drawing ;
2+ using System . Numerics ;
3+ using ShapeEngine . Geometry . CircleDef ;
14using 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
312namespace ShapeEngine . Geometry . StripedDrawingDef ;
413
@@ -9,4 +18,177 @@ namespace ShapeEngine.Geometry.StripedDrawingDef;
918public 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