Skip to content

Commit b059f31

Browse files
Merge pull request #282 from SixLabors/js/clipper-upstream-fixes
Clipper upstream fixes
2 parents 072ecd7 + 85ec501 commit b059f31

File tree

4 files changed

+37
-215
lines changed

4 files changed

+37
-215
lines changed

src/ImageSharp.Drawing/Processing/PathGradientBrush.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ protected override void Dispose(bool disposing)
349349
Vector2 ip = default;
350350
Vector2 closestIntersection = default;
351351
Edge closestEdge = null;
352-
float minDistance = float.MaxValue;
352+
const float minDistance = float.MaxValue;
353353
foreach (Edge edge in this.edges)
354354
{
355355
if (!edge.Intersect(start, end, ref ip))

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,38 +52,38 @@ public BoundsF(bool isValid)
5252

5353
public float Width
5454
{
55-
get => this.Right - this.Left;
55+
readonly get => this.Right - this.Left;
5656
set => this.Right = this.Left + value;
5757
}
5858

5959
public float Height
6060
{
61-
get => this.Bottom - this.Top;
61+
readonly get => this.Bottom - this.Top;
6262
set => this.Bottom = this.Top + value;
6363
}
6464

65-
public bool IsEmpty()
65+
public readonly bool IsEmpty()
6666
=> this.Bottom <= this.Top || this.Right <= this.Left;
6767

68-
public Vector2 MidPoint()
68+
public readonly Vector2 MidPoint()
6969
=> new Vector2(this.Left + this.Right, this.Top + this.Bottom) * .5F;
7070

71-
public bool Contains(Vector2 pt)
71+
public readonly bool Contains(Vector2 pt)
7272
=> pt.X > this.Left
7373
&& pt.X < this.Right
7474
&& pt.Y > this.Top && pt.Y < this.Bottom;
7575

76-
public bool Contains(BoundsF bounds)
76+
public readonly bool Contains(BoundsF bounds)
7777
=> bounds.Left >= this.Left
7878
&& bounds.Right <= this.Right
7979
&& bounds.Top >= this.Top
8080
&& bounds.Bottom <= this.Bottom;
8181

82-
public bool Intersects(BoundsF bounds)
82+
public readonly bool Intersects(BoundsF bounds)
8383
=> (Math.Max(this.Left, bounds.Left) < Math.Min(this.Right, bounds.Right))
8484
&& (Math.Max(this.Top, bounds.Top) < Math.Min(this.Bottom, bounds.Bottom));
8585

86-
public PathF AsPath()
86+
public readonly PathF AsPath()
8787
=> new(4)
8888
{
8989
new Vector2(this.Left, this.Top),

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

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,17 +184,27 @@ public static bool GetIntersectPoint(Vector2 ln1a, Vector2 ln1b, Vector2 ln2a, V
184184
{
185185
Vector2 dxy1 = ln1b - ln1a;
186186
Vector2 dxy2 = ln2b - ln2a;
187-
float cp = CrossProduct(dxy1, dxy2);
188-
if (cp == 0F)
187+
float det = CrossProduct(dxy1, dxy2);
188+
if (det == 0F)
189189
{
190190
ip = default;
191191
return false;
192192
}
193193

194-
float q1 = CrossProduct(dxy1, ln1a);
195-
float q2 = CrossProduct(dxy2, ln2a);
194+
float t = (((ln1a.X - ln2a.X) * dxy2.Y) - ((ln1a.Y - ln2a.Y) * dxy2.X)) / det;
195+
if (t <= 0F)
196+
{
197+
ip = ln1a;
198+
}
199+
else if (t >= 1F)
200+
{
201+
ip = ln1b;
202+
}
203+
else
204+
{
205+
ip = ln1a + (t * dxy1);
206+
}
196207

197-
ip = ((dxy2 * q1) - (dxy1 * q2)) / cp;
198208
return true;
199209
}
200210

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

Lines changed: 13 additions & 201 deletions
Original file line numberDiff line numberDiff line change
@@ -333,13 +333,9 @@ private void ConvertHorzSegsToJoins()
333333
for (int j = i + 1; j < k; j++)
334334
{
335335
HorzSegment hs2 = this.horzSegList[j];
336-
if (hs2.LeftOp.Point.X >= hs1.RightOp.Point.X)
337-
{
338-
break;
339-
}
340-
341-
if (hs2.LeftToRight == hs1.LeftToRight ||
342-
(hs2.RightOp.Point.X <= hs1.LeftOp.Point.X))
336+
if ((hs2.LeftOp.Point.X >= hs1.RightOp.Point.X) ||
337+
(hs2.LeftToRight == hs1.LeftToRight) ||
338+
(hs2.RightOp.Point.X <= hs1.LeftOp.Point.X))
343339
{
344340
continue;
345341
}
@@ -674,7 +670,7 @@ private void DoHorizontal(Active horz)
674670

675671
if (IsHotEdge(horz))
676672
{
677-
AddOutPt(horz, horz.Top);
673+
this.AddToHorzSegList(AddOutPt(horz, horz.Top));
678674
}
679675

680676
this.UpdateEdgeIntoAEL(horz); // this is the end of an intermediate horiz.
@@ -929,190 +925,6 @@ private static OutPt DisposeOutPt(OutPt op)
929925
return result;
930926
}
931927

932-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
933-
private static BoundsF GetBounds(OutPt op)
934-
{
935-
BoundsF result = new(op.Point.X, op.Point.Y, op.Point.X, op.Point.Y);
936-
OutPt op2 = op.Next;
937-
while (op2 != op)
938-
{
939-
if (op2.Point.X < result.Left)
940-
{
941-
result.Left = op2.Point.X;
942-
}
943-
else if (op2.Point.X > result.Right)
944-
{
945-
result.Right = op2.Point.X;
946-
}
947-
948-
if (op2.Point.Y < result.Top)
949-
{
950-
result.Top = op2.Point.Y;
951-
}
952-
else if (op2.Point.Y > result.Bottom)
953-
{
954-
result.Bottom = op2.Point.Y;
955-
}
956-
957-
op2 = op2.Next;
958-
}
959-
960-
return result;
961-
}
962-
963-
private static PointInPolygonResult PointInOpPolygon(Vector2 pt, OutPt op)
964-
{
965-
if (op == op.Next || op.Prev == op.Next)
966-
{
967-
return PointInPolygonResult.IsOutside;
968-
}
969-
970-
OutPt op2 = op;
971-
do
972-
{
973-
if (op.Point.Y != pt.Y)
974-
{
975-
break;
976-
}
977-
978-
op = op.Next;
979-
}
980-
while (op != op2);
981-
982-
// not a proper polygon
983-
if (op.Point.Y == pt.Y)
984-
{
985-
return PointInPolygonResult.IsOutside;
986-
}
987-
988-
// must be above or below to get here
989-
bool isAbove = op.Point.Y < pt.Y, startingAbove = isAbove;
990-
int val = 0;
991-
992-
op2 = op.Next;
993-
while (op2 != op)
994-
{
995-
if (isAbove)
996-
{
997-
while (op2 != op && op2.Point.Y < pt.Y)
998-
{
999-
op2 = op2.Next;
1000-
}
1001-
}
1002-
else
1003-
{
1004-
while (op2 != op && op2.Point.Y > pt.Y)
1005-
{
1006-
op2 = op2.Next;
1007-
}
1008-
}
1009-
1010-
if (op2 == op)
1011-
{
1012-
break;
1013-
}
1014-
1015-
// must have touched or crossed the pt.Y horizonal
1016-
// and this must happen an even number of times
1017-
// touching the horizontal
1018-
if (op2.Point.Y == pt.Y)
1019-
{
1020-
if (op2.Point.X == pt.X || (op2.Point.Y == op2.Prev.Point.Y
1021-
&& (pt.X < op2.Prev.Point.X) != (pt.X < op2.Point.X)))
1022-
{
1023-
return PointInPolygonResult.IsOn;
1024-
}
1025-
1026-
op2 = op2.Next;
1027-
if (op2 == op)
1028-
{
1029-
break;
1030-
}
1031-
1032-
continue;
1033-
}
1034-
1035-
if (op2.Point.X <= pt.X || op2.Prev.Point.X <= pt.X)
1036-
{
1037-
if (op2.Prev.Point.X < pt.X && op2.Point.X < pt.X)
1038-
{
1039-
val = 1 - val; // toggle val
1040-
}
1041-
else
1042-
{
1043-
float d = ClipperUtils.CrossProduct(op2.Prev.Point, op2.Point, pt);
1044-
if (d == 0)
1045-
{
1046-
return PointInPolygonResult.IsOn;
1047-
}
1048-
1049-
if ((d < 0) == isAbove)
1050-
{
1051-
val = 1 - val;
1052-
}
1053-
}
1054-
}
1055-
1056-
isAbove = !isAbove;
1057-
op2 = op2.Next;
1058-
}
1059-
1060-
if (isAbove != startingAbove)
1061-
{
1062-
float d = ClipperUtils.CrossProduct(op2.Prev.Point, op2.Point, pt);
1063-
if (d == 0)
1064-
{
1065-
return PointInPolygonResult.IsOn;
1066-
}
1067-
1068-
if ((d < 0) == isAbove)
1069-
{
1070-
val = 1 - val;
1071-
}
1072-
}
1073-
1074-
if (val == 0)
1075-
{
1076-
return PointInPolygonResult.IsOutside;
1077-
}
1078-
else
1079-
{
1080-
return PointInPolygonResult.IsInside;
1081-
}
1082-
}
1083-
1084-
private static bool Path1InsidePath2(OutPt op1, OutPt op2)
1085-
{
1086-
// we need to make some accommodation for rounding errors
1087-
// so we won't jump if the first vertex is found outside
1088-
int outside_cnt = 0;
1089-
OutPt op = op1;
1090-
do
1091-
{
1092-
PointInPolygonResult result = PointInOpPolygon(op.Point, op2);
1093-
if (result == PointInPolygonResult.IsOutside)
1094-
{
1095-
++outside_cnt;
1096-
}
1097-
else if (result == PointInPolygonResult.IsInside)
1098-
{
1099-
--outside_cnt;
1100-
}
1101-
1102-
op = op.Next;
1103-
}
1104-
while (op != op1 && Math.Abs(outside_cnt) < 2);
1105-
1106-
if (Math.Abs(outside_cnt) > 1)
1107-
{
1108-
return outside_cnt < 0;
1109-
}
1110-
1111-
// since path1's location is still equivocal, check its midpoint
1112-
Vector2 mp = GetBounds(op).MidPoint();
1113-
return PointInOpPolygon(mp, op2) == PointInPolygonResult.IsInside;
1114-
}
1115-
1116928
private void ProcessHorzJoins()
1117929
{
1118930
foreach (HorzJoin j in this.horzJoinList)
@@ -1127,6 +939,7 @@ private void ProcessHorzJoins()
1127939
op1b.Prev = op2b;
1128940
op2b.Next = op1b;
1129941

942+
// 'join' is really a split
1130943
if (or1 == or2)
1131944
{
1132945
or2 = new OutRec
@@ -1215,7 +1028,6 @@ private void DoSplitOp(OutRec outrec, OutPt splitOp)
12151028
OutPt prevOp = splitOp.Prev;
12161029
OutPt nextNextOp = splitOp.Next.Next;
12171030
outrec.Pts = prevOp;
1218-
OutPt result = prevOp;
12191031

12201032
ClipperUtils.GetIntersectPoint(
12211033
prevOp.Point, splitOp.Point, splitOp.Next.Point, nextNextOp.Point, out Vector2 ip);
@@ -1229,11 +1041,6 @@ private void DoSplitOp(OutRec outrec, OutPt splitOp)
12291041
return;
12301042
}
12311043

1232-
// nb: area1 is the path's area *before* splitting, whereas area2 is
1233-
// the area of the triangle containing splitOp & splitOp.next.
1234-
// So the only way for these areas to have the same sign is if
1235-
// the split triangle is larger than the path containing prevOp or
1236-
// if there's more than one self=intersection.
12371044
float area2 = AreaTriangle(ip, splitOp.Point, splitOp.Next.Point);
12381045
float absArea2 = Math.Abs(area2);
12391046

@@ -1256,6 +1063,11 @@ private void DoSplitOp(OutRec outrec, OutPt splitOp)
12561063
prevOp.Next = newOp2;
12571064
}
12581065

1066+
// nb: area1 is the path's area *before* splitting, whereas area2 is
1067+
// the area of the triangle containing splitOp & splitOp.next.
1068+
// So the only way for these areas to have the same sign is if
1069+
// the split triangle is larger than the path containing prevOp or
1070+
// if there's more than one self=intersection.
12591071
if (absArea2 > 1 && (absArea2 > absArea1 || ((area2 > 0) == (area1 > 0))))
12601072
{
12611073
OutRec newOutRec = this.NewOutRec();
@@ -3312,7 +3124,7 @@ private static bool IsFront(Active ae)
33123124

33133125
private struct LocMinSorter : IComparer<LocalMinima>
33143126
{
3315-
public int Compare(LocalMinima locMin1, LocalMinima locMin2)
3127+
public readonly int Compare(LocalMinima locMin1, LocalMinima locMin2)
33163128
=> locMin2.Vertex.Point.Y.CompareTo(locMin1.Vertex.Point.Y);
33173129
}
33183130

@@ -3363,7 +3175,7 @@ public IntersectNode(Vector2 pt, Active edge1, Active edge2)
33633175

33643176
private struct HorzSegSorter : IComparer<HorzSegment>
33653177
{
3366-
public int Compare(HorzSegment hs1, HorzSegment hs2)
3178+
public readonly int Compare(HorzSegment hs1, HorzSegment hs2)
33673179
{
33683180
if (hs1 == null || hs2 == null)
33693181
{
@@ -3387,7 +3199,7 @@ public int Compare(HorzSegment hs1, HorzSegment hs2)
33873199

33883200
private struct IntersectListSort : IComparer<IntersectNode>
33893201
{
3390-
public int Compare(IntersectNode a, IntersectNode b)
3202+
public readonly int Compare(IntersectNode a, IntersectNode b)
33913203
{
33923204
if (a.Point.Y == b.Point.Y)
33933205
{

0 commit comments

Comments
 (0)