Skip to content

Commit

Permalink
Added RectClipLines functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
AngusJohnson committed Oct 21, 2022
1 parent 383ddf4 commit cce4efd
Show file tree
Hide file tree
Showing 12 changed files with 705 additions and 147 deletions.
94 changes: 84 additions & 10 deletions CPP/Clipper2Lib/include/clipper2/clipper.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 15 October 2022 *
* Date : 21 October 2022 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2022 *
* Purpose : This module provides a simple interface to the Clipper Library *
Expand Down Expand Up @@ -130,11 +130,6 @@ namespace Clipper2Lib {
return BooleanOp(ClipType::Xor, fillrule, subjects, clips, decimal_prec);
}

inline bool IsFullOpenEndType(EndType et)
{
return (et != EndType::Polygon) && (et != EndType::Joined);
}

inline Paths64 InflatePaths(const Paths64& paths, double delta,
JoinType jt, EndType et, double miter_limit = 2.0)
{
Expand Down Expand Up @@ -255,14 +250,14 @@ namespace Clipper2Lib {
Rect64 pathRec = Bounds(path);
if (!rect.Intersects(pathRec)) return Path64();
if (rect.Contains(pathRec)) return path;
RectClip64 rc(rect);
class RectClip rc(rect);
return rc.Execute(path);
}

inline Paths64 RectClip(const Rect64& rect, const Paths64& paths)
{
if (rect.IsEmpty() || paths.empty()) return Paths64();
RectClip64 rc(rect);
class RectClip rc(rect);
Paths64 result;
result.reserve(paths.size());

Expand Down Expand Up @@ -290,7 +285,7 @@ namespace Clipper2Lib {
throw new Clipper2Exception(precision_error);
const double scale = std::pow(10, precision);
Rect64 r = ScaleRect<int64_t, double>(rect, scale);
RectClip64 rc(r);
class RectClip rc(r);
Path64 p = ScalePath<int64_t, double>(path, scale);
return ScalePath<double, int64_t>(rc.Execute(p), 1 / scale);
}
Expand All @@ -302,7 +297,7 @@ namespace Clipper2Lib {
throw new Clipper2Exception(precision_error);
const double scale = std::pow(10, precision);
Rect64 r = ScaleRect<int64_t, double>(rect, scale);
RectClip64 rc(r);
class RectClip rc(r);
PathsD result;
result.reserve(paths.size());
for (const PathD& path : paths)
Expand All @@ -323,6 +318,85 @@ namespace Clipper2Lib {
return result;
}

inline Paths64 RectClipLines(const Rect64& rect, const Path64& path)
{
Paths64 result;
if (rect.IsEmpty() || path.empty()) return result;
Rect64 pathRec = Bounds(path);
if (!rect.Intersects(pathRec)) return result;
if (rect.Contains(pathRec))
{
result.push_back(path);
return result;
}
class RectClipLines rcl(rect);
return rcl.Execute(path);
}

inline Paths64 RectClipLines(const Rect64& rect, const Paths64& paths)
{
Paths64 result;
if (rect.IsEmpty() || paths.empty()) return result;
class RectClipLines rcl(rect);
for (const Path64& p : paths)
{
Rect64 pathRec = Bounds(p);
if (!rect.Intersects(pathRec))
continue;
else if (rect.Contains(pathRec))
result.push_back(p);
else
{
Paths64 pp = rcl.Execute(p);
if (!pp.empty())
result.insert(result.end(), pp.begin(), pp.end());
}
}
return result;
}

inline PathsD RectClipLines(const RectD& rect, const PathD& path, int precision = 2)
{
if (rect.IsEmpty() || path.empty() ||
!rect.Contains(Bounds(path))) return PathsD();
if (precision < -8 || precision > 8)
throw new Clipper2Exception(precision_error);
const double scale = std::pow(10, precision);
Rect64 r = ScaleRect<int64_t, double>(rect, scale);
class RectClipLines rcl(r);
Path64 p = ScalePath<int64_t, double>(path, scale);
return ScalePaths<double, int64_t>(rcl.Execute(p), 1 / scale);
}

inline PathsD RectClipLines(const RectD& rect, const PathsD& paths, int precision = 2)
{
PathsD result;
if (rect.IsEmpty() || paths.empty()) return result;
if (precision < -8 || precision > 8)
throw new Clipper2Exception(precision_error);
const double scale = std::pow(10, precision);
Rect64 r = ScaleRect<int64_t, double>(rect, scale);
class RectClipLines rcl(r);
result.reserve(paths.size());
for (const PathD& path : paths)
{
RectD pathRec = Bounds(path);
if (!rect.Intersects(pathRec))
continue;
else if (rect.Contains(pathRec))
result.push_back(path);
else
{
Path64 p = ScalePath<int64_t, double>(path, scale);
Paths64 pp = rcl.Execute(p);
if (pp.empty()) continue;
PathsD ppd = ScalePaths<double, int64_t>(pp, 1 / scale);
result.insert(result.end(), ppd.begin(), ppd.end());
}
}
return result;
}

namespace details
{

Expand Down
14 changes: 9 additions & 5 deletions CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,31 @@ namespace Clipper2Lib

enum class Location { Left, Top, Right, Bottom, Inside };

class RectClip64 {
private:
class RectClip {
protected:
const Rect64 rect_;
const Point64 mp_;
const Path64 rectPath_;
Path64 result_;
std::vector<Location> start_locs_;

void Reset();
void GetNextLocation(const Path64& path,
Location& loc, int& i, int highI);
void AddCorner(Location prev, Location curr);
void AddCorner(Location& loc, bool isClockwise);

public:
RectClip64(const Rect64& rect) :
RectClip(const Rect64& rect) :
rect_(rect),
mp_(rect.MidPoint()),
rectPath_(rect.AsPath()) {}
Path64 Execute(const Path64& path);
};

class RectClipLines : public RectClip {
public:
RectClipLines(const Rect64& rect) : RectClip(rect) {};
Paths64 Execute(const Path64& path);
};

} // Clipper2Lib namespace
#endif // CLIPPER_RECTCLIP_H
5 changes: 3 additions & 2 deletions CPP/Clipper2Lib/src/clipper.engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,9 @@ namespace Clipper2Lib {
{
if ((currentY == ae.top.y) || (ae.top.x == ae.bot.x)) return ae.top.x;
else if (currentY == ae.bot.y) return ae.bot.x;
else return ae.bot.x + static_cast<int64_t>(std::round(ae.dx * (currentY - ae.bot.y)));
//nb: std::round above substantially *improves* performance
else return ae.bot.x + static_cast<int64_t>(std::nearbyint(ae.dx * (currentY - ae.bot.y)));
// nb: std::nearbyint (or std::round) substantially *improves* performance here
// as it greatly improves the likelihood of edge adjacency in ProcessIntersectList().
}


Expand Down
99 changes: 83 additions & 16 deletions CPP/Clipper2Lib/src/clipper.rectclip.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 15 October 2022 *
* Date : 21 October 2022 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2022 *
* Purpose : FAST rectangular clipping *
Expand Down Expand Up @@ -226,21 +226,15 @@ namespace Clipper2Lib {
// RectClip64
//----------------------------------------------------------------------------

inline void RectClip64::Reset()
{
result_.clear();
start_locs_.clear();
}

void RectClip64::AddCorner(Location prev, Location curr)
void RectClip::AddCorner(Location prev, Location curr)
{
if (HeadingClockwise(prev, curr))
result_.push_back(rectPath_[static_cast<int>(prev)]);
else
result_.push_back(rectPath_[static_cast<int>(curr)]);
}

void RectClip64::AddCorner(Location& loc, bool isClockwise)
void RectClip::AddCorner(Location& loc, bool isClockwise)
{
if (isClockwise)
{
Expand All @@ -254,7 +248,7 @@ namespace Clipper2Lib {
}
}

void RectClip64::GetNextLocation(const Path64& path,
void RectClip::GetNextLocation(const Path64& path,
Location& loc, int& i, int highI)
{
switch (loc)
Expand Down Expand Up @@ -309,11 +303,12 @@ namespace Clipper2Lib {
} //switch
}

Path64 RectClip64::Execute(const Path64& path)
Path64 RectClip::Execute(const Path64& path)
{
if (rect_.IsEmpty() || path.size() < 3) return Path64();

Reset();
result_.clear();
start_locs_.clear();
int i = 0, highI = static_cast<int>(path.size()) - 1;
Location prev = Location::Inside, loc;
Location crossing_loc = Location::Inside;
Expand Down Expand Up @@ -427,13 +422,13 @@ namespace Clipper2Lib {
if (starting_loc == Location::Inside) return path;
Rect64 tmp_rect = Bounds(path);
if (tmp_rect.Contains(rect_) &&
Path1ContainsPath2(path, rectPath_) !=
PointInPolygonResult::IsOutside) return rectPath_;
else
Path1ContainsPath2(path, rectPath_) !=
PointInPolygonResult::IsOutside) return rectPath_;
else
return Path64();
}

if (loc != Location::Inside &&
if (loc != Location::Inside &&
(loc != first_cross_ || start_locs_.size() > 2))
{
if (start_locs_.size() > 0)
Expand Down Expand Up @@ -477,4 +472,76 @@ namespace Clipper2Lib {
return res;
}

Paths64 RectClipLines::Execute(const Path64& path)
{
result_.clear();
Paths64 result;
if (rect_.IsEmpty() || path.size() == 0) return result;

int i = 1, highI = static_cast<int>(path.size()) - 1;

Location prev = Location::Inside, loc;
Location crossing_loc = Location::Inside;
if (!GetLocation(rect_, path[0], loc))
{
while (i <= highI && !GetLocation(rect_, path[i], prev)) ++i;
if (i > highI) {
result.push_back(path);
return result;
}
if (prev == Location::Inside) loc = Location::Inside;
i = 1;
}
if (loc == Location::Inside) result_.push_back(path[0]);

///////////////////////////////////////////////////
while (i <= highI)
{
prev = loc;
GetNextLocation(path, loc, i, highI);
if (i > highI) break;
Point64 ip, ip2;
Point64 prev_pt = path[static_cast<size_t>(i - 1)];

crossing_loc = loc;
if (!GetIntersection(rectPath_, path[i], prev_pt, crossing_loc, ip))
{
// ie remaining outside
++i;
continue;
}

////////////////////////////////////////////////////
// we must be crossing the rect boundary to get here
////////////////////////////////////////////////////

if (loc == Location::Inside) // path must be entering rect
{
result_.push_back(ip);
}
else if (prev != Location::Inside)
{
// passing right through rect. 'ip' here will be the second
// intersect pt but we'll also need the first intersect pt (ip2)
crossing_loc = prev;
GetIntersection(rectPath_, prev_pt, path[i], crossing_loc, ip2);
result_.push_back(ip2);
result_.push_back(ip);
result.push_back(result_);
result_.clear();
}
else // path must be exiting rect
{
result_.push_back(ip);
result.push_back(result_);
result_.clear();
}
} //while i <= highI
///////////////////////////////////////////////////

if (result_.size() > 1)
result.push_back(result_);
return result;
}

} // namespace
Loading

0 comments on commit cce4efd

Please sign in to comment.