Skip to content

Commit 5448713

Browse files
committed
Lazy-init InternalPaths and Bounds in ComplexPolygon
1 parent 975c453 commit 5448713

File tree

2 files changed

+78
-69
lines changed

2 files changed

+78
-69
lines changed

src/ImageSharp.Drawing/Shapes/ComplexPolygon.cs

Lines changed: 58 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Six Labors Split License.
33

44
using System.Buffers;
5+
using System.Diagnostics.CodeAnalysis;
56
using System.Numerics;
67

78
namespace SixLabors.ImageSharp.Drawing;
@@ -14,8 +15,9 @@ namespace SixLabors.ImageSharp.Drawing;
1415
public 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
}

src/ImageSharp.Drawing/Shapes/PathCollection.cs

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ namespace SixLabors.ImageSharp.Drawing;
1313
public class PathCollection : IPathCollection
1414
{
1515
private readonly IPath[] paths;
16+
private RectangleF? bounds;
1617

1718
/// <summary>
1819
/// Initializes a new instance of the <see cref="PathCollection"/> class.
@@ -33,28 +34,30 @@ public PathCollection(params IPath[] paths)
3334

3435
if (this.paths.Length == 0)
3536
{
36-
this.Bounds = new RectangleF(0, 0, 0, 0);
37+
this.bounds = new RectangleF(0, 0, 0, 0);
3738
}
38-
else
39-
{
40-
float minX, minY, maxX, maxY;
41-
minX = minY = float.MaxValue;
42-
maxX = maxY = float.MinValue;
39+
}
4340

44-
foreach (IPath path in this.paths)
45-
{
46-
minX = Math.Min(path.Bounds.Left, minX);
47-
minY = Math.Min(path.Bounds.Top, minY);
48-
maxX = Math.Max(path.Bounds.Right, maxX);
49-
maxY = Math.Max(path.Bounds.Bottom, maxY);
50-
}
41+
/// <inheritdoc />
42+
public RectangleF Bounds => this.bounds ??= this.CalcBounds();
43+
44+
private RectangleF CalcBounds()
45+
{
46+
float minX, minY, maxX, maxY;
47+
minX = minY = float.MaxValue;
48+
maxX = maxY = float.MinValue;
5149

52-
this.Bounds = new RectangleF(minX, minY, maxX - minX, maxY - minY);
50+
foreach (IPath path in this.paths)
51+
{
52+
RectangleF bounds = path.Bounds;
53+
minX = Math.Min(bounds.Left, minX);
54+
minY = Math.Min(bounds.Top, minY);
55+
maxX = Math.Max(bounds.Right, maxX);
56+
maxY = Math.Max(bounds.Bottom, maxY);
5357
}
54-
}
5558

56-
/// <inheritdoc />
57-
public RectangleF Bounds { get; }
59+
return new RectangleF(minX, minY, maxX - minX, maxY - minY);
60+
}
5861

5962
/// <inheritdoc />
6063
public IEnumerator<IPath> GetEnumerator() => ((IEnumerable<IPath>)this.paths).GetEnumerator();

0 commit comments

Comments
 (0)