Skip to content

Commit efb96db

Browse files
More Vector2 usage
1 parent e87286e commit efb96db

4 files changed

Lines changed: 73 additions & 67 deletions

File tree

src/ImageSharp.Drawing/Shapes/PolygonClipper/ClipperUtils.cs

Lines changed: 30 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ internal static class ClipperUtils
1313
public const float FloatingPointTolerance = 1e-4F;
1414
public const float DefaultMinimumEdgeLength = .1F;
1515

16-
// TODO: rename to Pow2
16+
// TODO: rename to Pow2?
1717
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1818
public static float Sqr(float value) => value * value;
1919

@@ -80,16 +80,17 @@ public static PathF Ellipse(Vector2 center, float radiusX, float radiusY = 0, in
8080

8181
if (steps <= 2)
8282
{
83-
steps = (int)MathF.Ceiling(MathF.PI * MathF.Sqrt((radiusX + radiusY) / 2));
83+
steps = (int)MathF.Ceiling(MathF.PI * MathF.Sqrt((radiusX + radiusY) * .5F));
8484
}
8585

8686
float si = MathF.Sin(2 * MathF.PI / steps);
8787
float co = MathF.Cos(2 * MathF.PI / steps);
8888
float dx = co, dy = si;
8989
PathF result = new(steps) { new Vector2(center.X + radiusX, center.Y) };
90+
Vector2 radiusXY = new(radiusX, radiusY);
9091
for (int i = 1; i < steps; ++i)
9192
{
92-
result.Add(new Vector2(center.X + (radiusX * dx), center.Y + (radiusY * dy)));
93+
result.Add(center + (radiusXY * new Vector2(dx, dy)));
9394
float x = (dx * co) - (dy * si);
9495
dy = (dy * co) + (dx * si);
9596
dx = x;
@@ -123,16 +124,14 @@ public static bool IsAlmostZero(float value)
123124
[MethodImpl(MethodImplOptions.AggressiveInlining)]
124125
public static float PerpendicDistFromLineSqrd(Vector2 pt, Vector2 line1, Vector2 line2)
125126
{
126-
float a = pt.X - line1.X;
127-
float b = pt.Y - line1.Y;
128-
float c = line2.X - line1.X;
129-
float d = line2.Y - line1.Y;
130-
if (c == 0 && d == 0)
127+
Vector2 ab = pt - line1;
128+
Vector2 cd = line2 - line1;
129+
if (cd == Vector2.Zero)
131130
{
132131
return 0;
133132
}
134133

135-
return Sqr((a * d) - (c * b)) / ((c * c) + (d * d));
134+
return Sqr(CrossProduct(cd, ab) / DotProduct(cd, cd));
136135
}
137136

138137
public static bool SegsIntersect(Vector2 seg1a, Vector2 seg1b, Vector2 seg2a, Vector2 seg2b, bool inclusive = false)
@@ -166,53 +165,52 @@ public static bool SegsIntersect(Vector2 seg1a, Vector2 seg1b, Vector2 seg2a, Ve
166165
[MethodImpl(MethodImplOptions.AggressiveInlining)]
167166
internal static bool GetIntersectPt(Vector2 ln1a, Vector2 ln1b, Vector2 ln2a, Vector2 ln2b, out Vector2 ip)
168167
{
169-
float dy1 = ln1b.Y - ln1a.Y;
170-
float dx1 = ln1b.X - ln1a.X;
171-
float dy2 = ln2b.Y - ln2a.Y;
172-
float dx2 = ln2b.X - ln2a.X;
173-
float cp = (dy1 * dx2) - (dy2 * dx1);
168+
Vector2 dxy1 = ln1b - ln1a;
169+
Vector2 dxy2 = ln2b - ln2a;
170+
float cp = CrossProduct(dxy1, dxy2);
174171
if (cp == 0F)
175172
{
176173
ip = default;
177174
return false;
178175
}
179176

180-
float qx = (dx1 * ln1a.Y) - (dy1 * ln1a.X);
181-
float qy = (dx2 * ln2a.Y) - (dy2 * ln2a.X);
182-
ip = new Vector2(((dx1 * qy) - (dx2 * qx)) / cp, ((dy1 * qy) - (dy2 * qx)) / cp);
183-
return ip.X != float.MaxValue && ip.Y != float.MaxValue;
177+
float qx = CrossProduct(ln1a, dxy1);
178+
float qy = CrossProduct(ln2a, dxy2);
179+
180+
ip = ((dxy1 * qy) - (dxy2 * qx)) / cp;
181+
return ip != new Vector2(float.MaxValue);
184182
}
185183

186184
[MethodImpl(MethodImplOptions.AggressiveInlining)]
187185
public static bool GetIntersectPoint(Vector2 ln1a, Vector2 ln1b, Vector2 ln2a, Vector2 ln2b, out Vector2 ip)
188186
{
189-
float dy1 = ln1b.Y - ln1a.Y;
190-
float dx1 = ln1b.X - ln1a.X;
191-
float dy2 = ln2b.Y - ln2a.Y;
192-
float dx2 = ln2b.X - ln2a.X;
193-
float q1 = (dy1 * ln1a.X) - (dx1 * ln1a.Y);
194-
float q2 = (dy2 * ln2a.X) - (dx2 * ln2a.Y);
195-
float cross_prod = (dy1 * dx2) - (dy2 * dx1);
196-
if (cross_prod == 0F)
187+
Vector2 dxy1 = ln1b - ln1a;
188+
Vector2 dxy2 = ln2b - ln2a;
189+
float cp = CrossProduct(dxy1, dxy2);
190+
if (cp == 0F)
197191
{
198192
ip = default;
199193
return false;
200194
}
201195

202-
ip = new Vector2(((dx2 * q1) - (dx1 * q2)) / cross_prod, ((dy2 * q1) - (dy1 * q2)) / cross_prod);
196+
float q1 = CrossProduct(dxy1, ln1a);
197+
float q2 = CrossProduct(dxy2, ln2a);
198+
199+
ip = ((dxy2 * q1) - (dxy1 * q2)) / cp;
203200
return true;
204201
}
205202

206203
public static Vector2 GetClosestPtOnSegment(Vector2 offPt, Vector2 seg1, Vector2 seg2)
207204
{
208-
if (seg1.X == seg2.X && seg1.Y == seg2.Y)
205+
if (seg1 == seg2)
209206
{
210207
return seg1;
211208
}
212209

213-
float dx = seg2.X - seg1.X;
214-
float dy = seg2.Y - seg1.Y;
215-
float q = (((offPt.X - seg1.X) * dx) + ((offPt.Y - seg1.Y) * dy)) / ((dx * dx) + (dy * dy));
210+
Vector2 dxy = seg2 - seg1;
211+
Vector2 oxy = (offPt - seg1) * dxy;
212+
float q = (oxy.X + oxy.Y) / DotProduct(dxy, dxy);
213+
216214
if (q < 0)
217215
{
218216
q = 0;
@@ -222,7 +220,7 @@ public static Vector2 GetClosestPtOnSegment(Vector2 offPt, Vector2 seg1, Vector2
222220
q = 1;
223221
}
224222

225-
return new Vector2(seg1.X + (q * dx), seg1.Y + (q * dy));
223+
return seg1 + (dxy * q);
226224
}
227225

228226
public static PathF ReversePath(PathF path)

src/ImageSharp.Drawing/Shapes/PolygonClipper/PolygonClipper.cs

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,8 @@ private void AddNewIntersectNode(Active ae1, Active ae2, float topY)
170170

171171
if (ip.Y > this.currentBotY || ip.Y < topY)
172172
{
173-
float absDx1 = Math.Abs(ae1.dx);
174-
float absDx2 = Math.Abs(ae2.dx);
173+
float absDx1 = MathF.Abs(ae1.dx);
174+
float absDx2 = MathF.Abs(ae2.dx);
175175

176176
// TODO: Check threshold here once we remove upscaling.
177177
if (absDx1 > 100 && absDx2 > 100)
@@ -245,7 +245,7 @@ private static bool SetHorzSegHeadingForward(HorzSegment hs, OutPt opP, OutPt op
245245

246246
private static bool UpdateHorzSegment(HorzSegment hs)
247247
{
248-
OutPt op = hs.leftOp!;
248+
OutPt op = hs.leftOp;
249249
OutRec outrec = GetRealOutRec(op.OutRec);
250250
bool outrecHasEdges = outrec.frontEdge != null;
251251
float curr_y = op.Point.Y;
@@ -530,7 +530,7 @@ private void DoHorizontal(Active horz)
530530
this.AddToHorzSegList(op);
531531
}
532532

533-
OutRec currOutrec = horz.outrec!;
533+
OutRec currOutrec = horz.outrec;
534534

535535
while (true)
536536
{
@@ -1122,8 +1122,8 @@ private void ProcessHorzJoins()
11221122
{
11231123
foreach (HorzJoin j in this.horzJoinList)
11241124
{
1125-
OutRec or1 = GetRealOutRec(j.op1.OutRec)!;
1126-
OutRec or2 = GetRealOutRec(j.op2.OutRec)!;
1125+
OutRec or1 = GetRealOutRec(j.op1.OutRec);
1126+
OutRec or2 = GetRealOutRec(j.op2.OutRec);
11271127

11281128
OutPt op1b = j.op1.Next;
11291129
OutPt op2b = j.op2.Prev;
@@ -1206,7 +1206,7 @@ private void CleanCollinear(OutRec outrec)
12061206
return;
12071207
}
12081208

1209-
OutPt startOp = outrec.pts!;
1209+
OutPt startOp = outrec.pts;
12101210
OutPt op2 = startOp;
12111211
do
12121212
{
@@ -1226,7 +1226,7 @@ private void CleanCollinear(OutRec outrec)
12261226
return;
12271227
}
12281228

1229-
startOp = op2!;
1229+
startOp = op2;
12301230
continue;
12311231
}
12321232

@@ -1309,7 +1309,7 @@ private void DoSplitOp(OutRec outrec, OutPt splitOp)
13091309
[MethodImpl(MethodImplOptions.AggressiveInlining)]
13101310
private void FixSelfIntersects(OutRec outrec)
13111311
{
1312-
OutPt op2 = outrec.pts!;
1312+
OutPt op2 = outrec.pts;
13131313

13141314
// triangles can't self-intersect
13151315
while (op2.Prev != op2.Next.Next)
@@ -1575,7 +1575,13 @@ private bool BuildIntersectList(float topY)
15751575
// sort that ensures only adjacent edges are intersecting. Intersect info is
15761576
// stored in FIntersectList ready to be processed in ProcessIntersectList.
15771577
// Re merge sorts see https://stackoverflow.com/a/46319131/359538
1578-
Active left = this.flaggedHorizontal, right, lEnd, rEnd, currBase, prevBase, tmp;
1578+
Active left = this.flaggedHorizontal;
1579+
Active right;
1580+
Active lEnd;
1581+
Active rEnd;
1582+
Active currBase;
1583+
Active prevBase;
1584+
Active tmp;
15791585

15801586
while (left.jump != null)
15811587
{
@@ -1591,7 +1597,7 @@ private bool BuildIntersectList(float topY)
15911597
{
15921598
if (right.curX < left.curX)
15931599
{
1594-
tmp = right.prevInSEL!;
1600+
tmp = right.prevInSEL;
15951601
while (true)
15961602
{
15971603
this.AddNewIntersectNode(tmp, right, topY);
@@ -1600,7 +1606,7 @@ private bool BuildIntersectList(float topY)
16001606
break;
16011607
}
16021608

1603-
tmp = tmp.prevInSEL!;
1609+
tmp = tmp.prevInSEL;
16041610
}
16051611

16061612
tmp = right;
@@ -2397,17 +2403,20 @@ private static float GetDx(Vector2 pt1, Vector2 pt2)
23972403
[MethodImpl(MethodImplOptions.AggressiveInlining)]
23982404
private static float TopX(Active ae, float currentY)
23992405
{
2400-
if ((currentY == ae.top.Y) || (ae.top.X == ae.bot.X))
2406+
Vector2 top = ae.top;
2407+
Vector2 bottom = ae.bot;
2408+
2409+
if ((currentY == top.Y) || (top.X == bottom.X))
24012410
{
2402-
return ae.top.X;
2411+
return top.X;
24032412
}
24042413

2405-
if (currentY == ae.bot.Y)
2414+
if (currentY == bottom.Y)
24062415
{
2407-
return ae.bot.X;
2416+
return bottom.X;
24082417
}
24092418

2410-
return ae.bot.X + (ae.dx * (currentY - ae.bot.Y));
2419+
return bottom.X + (ae.dx * (currentY - bottom.Y));
24112420
}
24122421

24132422
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -2751,7 +2760,7 @@ private void InsertLeftEdge(Active ae)
27512760
// don't separate joined edges
27522761
if (ae2.joinWith == JoinWith.Right)
27532762
{
2754-
ae2 = ae2.nextInAEL!;
2763+
ae2 = ae2.nextInAEL;
27552764
}
27562765

27572766
ae.nextInAEL = ae2.nextInAEL;
@@ -2918,10 +2927,10 @@ private static OutPt AddOutPt(Active ae, Vector2 pt)
29182927
{
29192928
// Outrec.OutPts: a circular doubly-linked-list of POutPt where ...
29202929
// opFront[.Prev]* ~~~> opBack & opBack == opFront.Next
2921-
OutRec outrec = ae.outrec!;
2930+
OutRec outrec = ae.outrec;
29222931
bool toFront = IsFront(ae);
2923-
OutPt opFront = outrec.pts!;
2924-
OutPt opBack = opFront.Next!;
2932+
OutPt opFront = outrec.pts;
2933+
OutPt opBack = opFront.Next;
29252934

29262935
if (toFront && (pt == opFront.Point))
29272936
{
@@ -3284,7 +3293,7 @@ private OutPt AddLocalMaxPoly(Active ae1, Active ae2, Vector2 pt)
32843293
OutPt result = AddOutPt(ae1, pt);
32853294
if (ae1.outrec == ae2.outrec)
32863295
{
3287-
OutRec outrec = ae1.outrec!;
3296+
OutRec outrec = ae1.outrec;
32883297
outrec.pts = result;
32893298

32903299
if (this.usingPolytree)

src/ImageSharp.Drawing/Shapes/PolygonClipper/PolygonOffsetter.cs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,9 @@ private void DoGroupOffset(Group group)
204204
}
205205
else
206206
{
207-
float d = MathF.Ceiling(this.groupDelta);
208-
BoundsF r = new(path[0].X - d, path[0].Y - d, path[0].X - d, path[0].Y - d);
207+
Vector2 d = new(MathF.Ceiling(this.groupDelta));
208+
Vector2 xy = path[0] - d;
209+
BoundsF r = new(xy.X, xy.Y, xy.X, xy.Y);
209210
group.OutPath = r.AsPath();
210211
}
211212

@@ -317,9 +318,7 @@ private void OffsetOpenPath(Group group, PathF path)
317318
switch (this.endType)
318319
{
319320
case EndCapStyle.Butt:
320-
group.OutPath.Add(new Vector2(
321-
path[0].X - (this.normals[0].X * this.groupDelta),
322-
path[0].Y - (this.normals[0].Y * this.groupDelta)));
321+
group.OutPath.Add(path[0] - (this.normals[0] * this.groupDelta));
323322
group.OutPath.Add(this.GetPerpendic(path[0], this.normals[0]));
324323
break;
325324
case EndCapStyle.Round:
@@ -339,7 +338,7 @@ private void OffsetOpenPath(Group group, PathF path)
339338
// reverse normals ...
340339
for (int i = highI; i > 0; i--)
341340
{
342-
this.normals[i] = new Vector2(-this.normals[i - 1].X, -this.normals[i - 1].Y);
341+
this.normals[i] = Vector2.Negate(this.normals[i - 1]);
343342
}
344343

345344
this.normals[0] = this.normals[highI];
@@ -612,23 +611,23 @@ private static Vector2 IntersectPoint(Vector2 pt1a, Vector2 pt1b, Vector2 pt2a,
612611

613612
[MethodImpl(MethodImplOptions.AggressiveInlining)]
614613
private static Vector2 GetAvgUnitVector(Vector2 vec1, Vector2 vec2)
615-
=> NormalizeVector(new Vector2(vec1.X + vec2.X, vec1.Y + vec2.Y));
614+
=> NormalizeVector(vec1 + vec2);
616615

617616
[MethodImpl(MethodImplOptions.AggressiveInlining)]
618-
private static float Hypotenuse(float x, float y)
619-
=> MathF.Sqrt(MathF.Pow(x, 2) + MathF.Pow(y, 2));
617+
private static float Hypotenuse(Vector2 vector)
618+
=> MathF.Sqrt(Vector2.Dot(vector, vector));
620619

621620
[MethodImpl(MethodImplOptions.AggressiveInlining)]
622-
private static Vector2 NormalizeVector(Vector2 vec)
621+
private static Vector2 NormalizeVector(Vector2 vector)
623622
{
624-
float h = Hypotenuse(vec.X, vec.Y);
623+
float h = Hypotenuse(vector);
625624
if (ClipperUtils.IsAlmostZero(h))
626625
{
627626
return default;
628627
}
629628

630629
float inverseHypot = 1 / h;
631-
return new Vector2(vec.X * inverseHypot, vec.Y * inverseHypot);
630+
return vector * inverseHypot;
632631
}
633632

634633
private class Group

tests/ImageSharp.Drawing.Tests/Drawing/DrawingRobustnessTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ private static void CompareToSkiaResultsImpl(TestImageProvider<Rgba32> provider,
8080
throw new Exception(result.DifferencePercentageString);
8181
}
8282

83-
[Theory(Skip = "For local testing")]
83+
[Theory]//(Skip = "For local testing")]
8484
[WithSolidFilledImages(3600, 2400, "Black", PixelTypes.Rgba32, TestImages.GeoJson.States, 16, 30, 30)]
8585
public void LargeGeoJson_Lines(TestImageProvider<Rgba32> provider, string geoJsonFile, int aa, float sx, float sy)
8686
{

0 commit comments

Comments
 (0)