Skip to content

Commit

Permalink
Improved IsCollinear function (#777)
Browse files Browse the repository at this point in the history
  • Loading branch information
AngusJohnson committed Apr 27, 2024
1 parent 99a9706 commit cf841d7
Show file tree
Hide file tree
Showing 12 changed files with 112 additions and 105 deletions.
26 changes: 22 additions & 4 deletions CPP/Clipper2Lib/include/clipper2/clipper.core.h
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,17 @@ namespace Clipper2Lib
CheckPrecisionRange(precision, error_code);
}

template <typename T>
inline bool IsCollinear(const Point<T>& pt1,
const Point<T>& sharedPt, const Point<T>& pt2) // #777
{
const double a = static_cast<double>(sharedPt.x - pt1.x);
const double b = static_cast<double>(pt2.y - sharedPt.y);
const double c = static_cast<double>(sharedPt.y - pt1.y);
const double d = static_cast<double>(pt2.x - sharedPt.x);
return a * b == c * d;
}

template <typename T>
inline double CrossProduct(const Point<T>& pt1, const Point<T>& pt2, const Point<T>& pt3) {
return (static_cast<double>(pt2.x - pt1.x) * static_cast<double>(pt3.y -
Expand Down Expand Up @@ -846,6 +857,13 @@ namespace Clipper2Lib
#endif
}

template<typename T>
inline int GetSign(const T& val)
{
if (!val) return 0;
return (val > 0) ? 1 : -1;
}

inline bool SegmentsIntersect(const Point64& seg1a, const Point64& seg1b,
const Point64& seg2a, const Point64& seg2b, bool inclusive = false)
{
Expand All @@ -860,10 +878,10 @@ namespace Clipper2Lib
return (res1 || res2 || res3 || res4); // ensures not collinear
}
else {
return (CrossProduct(seg1a, seg2a, seg2b) *
CrossProduct(seg1b, seg2a, seg2b) < 0) &&
(CrossProduct(seg2a, seg1a, seg1b) *
CrossProduct(seg2b, seg1a, seg1b) < 0);
return (GetSign(CrossProduct(seg1a, seg2a, seg2b)) *
GetSign(CrossProduct(seg1b, seg2a, seg2b)) < 0) &&
(GetSign(CrossProduct(seg2a, seg1a, seg1b)) *
GetSign(CrossProduct(seg2b, seg1a, seg1b)) < 0);
}
}

Expand Down
14 changes: 7 additions & 7 deletions CPP/Clipper2Lib/include/clipper2/clipper.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 13 December 2023 *
* Date : 27 April 2024 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : This module provides a simple interface to the Clipper Library *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
Expand Down Expand Up @@ -510,9 +510,9 @@ namespace Clipper2Lib {

if (!is_open_path)
{
while (srcIt != stop && !CrossProduct(*stop, *srcIt, *(srcIt + 1)))
while (srcIt != stop && IsCollinear(*stop, *srcIt, *(srcIt + 1)))
++srcIt;
while (srcIt != stop && !CrossProduct(*(stop - 1), *stop, *srcIt))
while (srcIt != stop && IsCollinear(*(stop - 1), *stop, *srcIt))
--stop;
if (srcIt == stop) return Path64();
}
Expand All @@ -521,7 +521,7 @@ namespace Clipper2Lib {
dst.push_back(*prevIt);
for (; srcIt != stop; ++srcIt)
{
if (CrossProduct(*prevIt, *srcIt, *(srcIt + 1)))
if (!IsCollinear(*prevIt, *srcIt, *(srcIt + 1)))
{
prevIt = srcIt;
dst.push_back(*prevIt);
Expand All @@ -530,12 +530,12 @@ namespace Clipper2Lib {

if (is_open_path)
dst.push_back(*srcIt);
else if (CrossProduct(*prevIt, *stop, dst[0]))
else if (!IsCollinear(*prevIt, *stop, dst[0]))
dst.push_back(*stop);
else
{
while (dst.size() > 2 &&
!CrossProduct(dst[dst.size() - 1], dst[dst.size() - 2], dst[0]))
IsCollinear(dst[dst.size() - 1], dst[dst.size() - 2], dst[0]))
dst.pop_back();
if (dst.size() < 3) return Path64();
}
Expand Down
17 changes: 3 additions & 14 deletions CPP/Clipper2Lib/src/clipper.engine.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 17 April 2024 *
* Date : 27 April 2024 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : This is the main polygon clipping module *
Expand Down Expand Up @@ -1118,17 +1118,6 @@ namespace Clipper2Lib {
}
}

inline bool IsCollinear(const Point64& pt1,
const Point64& sharedPt, const Point64& pt2) // #777
{
#ifdef __aarch64__
double cp = CrossProduct(pt1, sharedPt, pt2);
return std::fabs(cp) < 0.0000001;
#else
return CrossProduct(pt1, sharedPt, pt2) == 0;
#endif
}

bool IsValidAelOrder(const Active& resident, const Active& newcomer)
{
if (newcomer.curr_x != resident.curr_x)
Expand Down Expand Up @@ -2828,7 +2817,7 @@ namespace Clipper2Lib {
if (PerpendicDistFromLineSqrd(pt, prev->bot, prev->top) > 0.25) return;
}
else if (e.curr_x != prev->curr_x) return;
if (CrossProduct(e.top, pt, prev->top)) return;
if (!IsCollinear(e.top, pt, prev->top)) return;

if (e.outrec->idx == prev->outrec->idx)
AddLocalMaxPoly(*prev, e, pt);
Expand Down Expand Up @@ -2856,7 +2845,7 @@ namespace Clipper2Lib {
if (PerpendicDistFromLineSqrd(pt, next->bot, next->top) > 0.35) return;
}
else if (e.curr_x != next->curr_x) return;
if (CrossProduct(e.top, pt, next->top)) return;
if (!IsCollinear(e.top, pt, next->top)) return;

if (e.outrec->idx == next->outrec->idx)
AddLocalMaxPoly(e, *next, pt);
Expand Down
11 changes: 5 additions & 6 deletions CPP/Clipper2Lib/src/clipper.rectclip.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 8 September 2023 *
* Date : 27 April 2024 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : FAST rectangular clipping *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
Expand Down Expand Up @@ -589,8 +589,7 @@ namespace Clipper2Lib {
OutPt2* op2 = op;
do
{
if (!CrossProduct(op2->prev->pt,
op2->pt, op2->next->pt))
if (IsCollinear(op2->prev->pt, op2->pt, op2->next->pt))
{
if (op2 == op)
{
Expand Down Expand Up @@ -825,8 +824,8 @@ namespace Clipper2Lib {
OutPt2* op2 = op->next;
while (op2 && op2 != op)
{
if (CrossProduct(op2->prev->pt,
op2->pt, op2->next->pt) == 0)
if (!IsCollinear(op2->prev->pt,
op2->pt, op2->next->pt))
{
op = op2->prev;
op2 = UnlinkOp(op2);
Expand Down
10 changes: 9 additions & 1 deletion CSharp/Clipper2Lib/Clipper.Core.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 24 March 2024 *
* Date : 27 April 2024 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : Core structures and functions for the Clipper Library *
Expand Down Expand Up @@ -601,6 +601,14 @@ internal static double CrossProduct(Point64 pt1, Point64 pt2, Point64 pt3)
(double) (pt2.Y - pt1.Y) * (pt3.X - pt2.X));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool IsCollinear(Point64 pt1, Point64 pt2, Point64 pt3)
{
// typecast to double to avoid potential int overflow
return (double) (pt2.X - pt1.X) * (double) (pt3.Y - pt2.Y) ==
(double) (pt2.Y - pt1.Y) * (double) (pt3.X - pt2.X);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static double DotProduct(Point64 pt1, Point64 pt2, Point64 pt3)
{
Expand Down
13 changes: 6 additions & 7 deletions CSharp/Clipper2Lib/Clipper.Engine.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 17 April 2024 *
* Date : 27 April 2024 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : This is the main polygon clipping module *
Expand Down Expand Up @@ -1112,8 +1112,8 @@ private static bool IsValidAelOrder(Active resident, Active newcomer)
// resident must also have just been inserted
if (resident.isLeftBound != newcomerIsLeft)
return newcomerIsLeft;
if (InternalClipper.CrossProduct(PrevPrevVertex(resident).pt,
resident.bot, resident.top) == 0) return true;
if (InternalClipper.IsCollinear(PrevPrevVertex(resident).pt,
resident.bot, resident.top)) return true;
// compare turning direction of the alternate bound
return (InternalClipper.CrossProduct(PrevPrevVertex(resident).pt,
newcomer.bot, PrevPrevVertex(newcomer).pt) > 0) == newcomerIsLeft;
Expand Down Expand Up @@ -2395,7 +2395,7 @@ private void CheckJoinLeft(Active e,
if (Clipper.PerpendicDistFromLineSqrd(pt, prev.bot, prev.top) > 0.25) return;
}
else if (e.curX != prev.curX) return;
if (InternalClipper.CrossProduct(e.top, pt, prev.top) != 0) return;
if (!InternalClipper.IsCollinear(e.top, pt, prev.top)) return;

if (e.outrec!.idx == prev.outrec!.idx)
AddLocalMaxPoly(prev, e, pt);
Expand Down Expand Up @@ -2424,8 +2424,7 @@ private void CheckJoinRight(Active e,
if (Clipper.PerpendicDistFromLineSqrd(pt, next.bot, next.top) > 0.25) return;
}
else if (e.curX != next.curX) return;
if (InternalClipper.CrossProduct(e.top, pt, next.top) != 0)
return;
if (!InternalClipper.IsCollinear(e.top, pt, next.top)) return;

if (e.outrec!.idx == next.outrec!.idx)
AddLocalMaxPoly(e, next, pt);
Expand Down Expand Up @@ -2821,7 +2820,7 @@ private void CleanCollinear(OutRec? outrec)
for (; ; )
{
// NB if preserveCollinear == true, then only remove 180 deg. spikes
if ((InternalClipper.CrossProduct(op2!.prev.pt, op2.pt, op2.next!.pt) == 0) &&
if ((InternalClipper.IsCollinear(op2!.prev.pt, op2.pt, op2.next!.pt)) &&
((op2.pt == op2.prev.pt) || (op2.pt == op2.next.pt) || !PreserveCollinear ||
(InternalClipper.DotProduct(op2.prev.pt, op2.pt, op2.next.pt) < 0)))
{
Expand Down
12 changes: 6 additions & 6 deletions CSharp/Clipper2Lib/Clipper.RectClip.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 8 September 2023 *
* Date : 27 April 2024 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : FAST rectangular clipping *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
Expand Down Expand Up @@ -694,8 +694,8 @@ private void CheckEdges()
if (op == null) continue;
do
{
if (InternalClipper.CrossProduct(
op2!.prev!.pt, op2.pt, op2.next!.pt) == 0)
if (InternalClipper.IsCollinear(
op2!.prev!.pt, op2.pt, op2.next!.pt))
{
if (op2 == op)
{
Expand Down Expand Up @@ -930,8 +930,8 @@ private Path64 GetPath(OutPt2? op)
OutPt2? op2 = op.next;
while (op2 != null && op2 != op)
{
if (InternalClipper.CrossProduct(
op2.prev!.pt, op2.pt, op2.next!.pt) == 0)
if (InternalClipper.IsCollinear(
op2.prev!.pt, op2.pt, op2.next!.pt))
{
op = op2.prev;
op2 = UnlinkOp(op2);
Expand Down
21 changes: 9 additions & 12 deletions CSharp/Clipper2Lib/Clipper.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 18 October 2023 *
* Date : 27 April 2024 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : This module contains simple functions that will likely cover *
* most polygon boolean and offsetting needs, while also avoiding *
* the inherent complexities of the other modules. *
Expand Down Expand Up @@ -1022,10 +1022,9 @@ public static Path64 TrimCollinear(Path64 path, bool isOpen = false)
int i = 0;
if (!isOpen)
{
while (i < len - 1 && InternalClipper.CrossProduct(
path[len - 1], path[i], path[i + 1]) == 0) i++;
while (i < len - 1 && InternalClipper.CrossProduct(
path[len - 2], path[len - 1], path[i]) == 0) len--;
while (i < len - 1 &&
InternalClipper.IsCollinear(path[len - 1], path[i], path[i + 1])) i++;
while (i < len - 1 && InternalClipper.IsCollinear(path[len - 2], path[len - 1], path[i])) len--;
}

if (len - i < 3)
Expand All @@ -1040,21 +1039,19 @@ public static Path64 TrimCollinear(Path64 path, bool isOpen = false)
result.Add(last);
for (i++; i < len - 1; i++)
{
if (InternalClipper.CrossProduct(
last, path[i], path[i + 1]) == 0) continue;
if (InternalClipper.IsCollinear(last, path[i], path[i + 1])) continue;
last = path[i];
result.Add(last);
}

if (isOpen)
result.Add(path[len - 1]);
else if (InternalClipper.CrossProduct(
last, path[len - 1], result[0]) != 0)
else if (!InternalClipper.IsCollinear(last, path[len - 1], result[0]))
result.Add(path[len - 1]);
else
{
while (result.Count > 2 && InternalClipper.CrossProduct(
result[result.Count - 1], result[result.Count - 2], result[0]) == 0)
while (result.Count > 2 && InternalClipper.IsCollinear(
result[result.Count - 1], result[result.Count - 2], result[0]))
{
result.RemoveAt(result.Count - 1);
}
Expand Down
Loading

0 comments on commit cf841d7

Please sign in to comment.