22// Licensed under the Six Labors Split License.
33
44using System . Buffers ;
5+ using System . Diagnostics . CodeAnalysis ;
56using System . Numerics ;
67
78namespace SixLabors . ImageSharp . Drawing ;
@@ -14,8 +15,9 @@ namespace SixLabors.ImageSharp.Drawing;
1415public sealed class ComplexPolygon : IPath , IPathInternals , IInternalPathOwner
1516{
1617 private readonly IPath [ ] paths ;
17- private readonly List < InternalPath > internalPaths ;
18- private readonly float length ;
18+ private List < InternalPath > ? internalPaths ;
19+ private float length ;
20+ private RectangleF ? bounds ;
1921
2022 /// <summary>
2123 /// Initializes a new instance of the <see cref="ComplexPolygon"/> class.
@@ -45,53 +47,10 @@ public ComplexPolygon(params IPath[] paths)
4547 Guard . NotNull ( paths , nameof ( paths ) ) ;
4648
4749 this . paths = paths ;
48- this . internalPaths = new List < InternalPath > ( this . paths . Length ) ;
49-
50- if ( paths . Length > 0 )
51- {
52- float minX = float . MaxValue ;
53- float maxX = float . MinValue ;
54- float minY = float . MaxValue ;
55- float maxY = float . MinValue ;
56- float length = 0 ;
57-
58- foreach ( IPath p in this . paths )
59- {
60- if ( p . Bounds . Left < minX )
61- {
62- minX = p . Bounds . Left ;
63- }
64-
65- if ( p . Bounds . Right > maxX )
66- {
67- maxX = p . Bounds . Right ;
68- }
69-
70- if ( p . Bounds . Top < minY )
71- {
72- minY = p . Bounds . Top ;
73- }
74-
75- if ( p . Bounds . Bottom > maxY )
76- {
77- maxY = p . Bounds . Bottom ;
78- }
79-
80- foreach ( ISimplePath s in p . Flatten ( ) )
81- {
82- InternalPath ip = new ( s . Points , s . IsClosed ) ;
83- length += ip . Length ;
84- this . internalPaths . Add ( ip ) ;
85- }
86- }
8750
88- this . length = length ;
89- this . Bounds = new RectangleF ( minX , minY , maxX - minX , maxY - minY ) ;
90- }
91- else
51+ if ( paths . Length == 0 )
9252 {
93- this . length = 0 ;
94- this . Bounds = RectangleF . Empty ;
53+ this . bounds = RectangleF . Empty ;
9554 }
9655
9756 this . PathType = PathTypes . Mixed ;
@@ -106,7 +65,7 @@ public ComplexPolygon(params IPath[] paths)
10665 public IEnumerable < IPath > Paths => this . paths ;
10766
10867 /// <inheritdoc/>
109- public RectangleF Bounds { get ; }
68+ public RectangleF Bounds => this . bounds ??= this . CalcBounds ( ) ;
11069
11170 /// <inheritdoc/>
11271 public IPath Transform ( Matrix3x2 matrix )
@@ -118,10 +77,10 @@ public IPath Transform(Matrix3x2 matrix)
11877 }
11978
12079 IPath [ ] shapes = new IPath [ this . paths . Length ] ;
121- int i = 0 ;
122- foreach ( IPath s in this . Paths )
80+
81+ for ( int i = 0 ; i < shapes . Length ; i ++ )
12382 {
124- shapes [ i ++ ] = s . Transform ( matrix ) ;
83+ shapes [ i ] = this . paths [ i ] . Transform ( matrix ) ;
12584 }
12685
12786 return new ComplexPolygon ( shapes ) ;
@@ -159,6 +118,11 @@ public IPath AsClosedPath()
159118 /// <inheritdoc/>
160119 SegmentInfo IPathInternals . PointAlongPath ( float distance )
161120 {
121+ if ( this . internalPaths == null )
122+ {
123+ this . InitInternalPaths ( ) ;
124+ }
125+
162126 distance %= this . length ;
163127 foreach ( InternalPath p in this . internalPaths )
164128 {
@@ -177,7 +141,49 @@ SegmentInfo IPathInternals.PointAlongPath(float distance)
177141
178142 /// <inheritdoc/>
179143 IReadOnlyList < InternalPath > IInternalPathOwner . GetRingsAsInternalPath ( )
180- => this . internalPaths ;
144+ {
145+ this . InitInternalPaths ( ) ;
146+ return this . internalPaths ;
147+ }
148+
149+ /// <summary>
150+ /// Initializes <see cref="internalPaths"/> and <see cref="length"/>.
151+ /// </summary>
152+ [ MemberNotNull ( nameof ( internalPaths ) ) ]
153+ private void InitInternalPaths ( )
154+ {
155+ this . internalPaths = new List < InternalPath > ( this . paths . Length ) ;
156+
157+ foreach ( IPath p in this . paths )
158+ {
159+ foreach ( ISimplePath s in p . Flatten ( ) )
160+ {
161+ InternalPath ip = new ( s . Points , s . IsClosed ) ;
162+ this . length += ip . Length ;
163+ this . internalPaths . Add ( ip ) ;
164+ }
165+ }
166+ }
167+
168+ private RectangleF CalcBounds ( )
169+ {
170+ float minX = float . MaxValue ;
171+ float maxX = float . MinValue ;
172+ float minY = float . MaxValue ;
173+ float maxY = float . MinValue ;
174+
175+ foreach ( IPath p in this . paths )
176+ {
177+ RectangleF pBounds = p . Bounds ;
178+
179+ minX = MathF . Min ( minX , pBounds . Left ) ;
180+ maxX = MathF . Max ( maxX , pBounds . Right ) ;
181+ minY = MathF . Min ( minY , pBounds . Top ) ;
182+ maxY = MathF . Max ( maxY , pBounds . Bottom ) ;
183+ }
184+
185+ return new RectangleF ( minX , minY , maxX - minX , maxY - minY ) ;
186+ }
181187
182188 private static InvalidOperationException ThrowOutOfRange ( ) => new ( "Should not be possible to reach this line" ) ;
183189}
0 commit comments