Skip to content

Commit

Permalink
add EndType::ButtLeft
Browse files Browse the repository at this point in the history
  • Loading branch information
LarsSkibaModuleWorks committed Oct 18, 2024
1 parent ed98928 commit 77abf38
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 23 deletions.
13 changes: 7 additions & 6 deletions CPP/Clipper2Lib/include/clipper2/clipper.offset.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ enum class JoinType { Square, Bevel, Round, Miter };
//Square : Joins are 'squared' at exactly the offset distance (more complex code)
//Bevel : Similar to Square, but the offset distance varies with angle (simple code & faster)

enum class EndType {Polygon, Joined, Butt, Square, Round};
//Butt : offsets both sides of a path, with square blunt ends
//Square : offsets both sides of a path, with square extended ends
//Round : offsets both sides of a path, with round extended ends
//Joined : offsets both sides of a path, with joined ends
//Polygon: offsets only one side of a closed path
enum class EndType {Polygon, Joined, Butt, Square, Round, ButtLeft};
//Butt : offsets both sides of a path, with square blunt ends
//Square : offsets both sides of a path, with square extended ends
//Round : offsets both sides of a path, with round extended ends
//Joined : offsets both sides of a path, with joined ends
//Polygon : offsets only one side of a closed path
//ButtLeft: offsets open paths with square blunt ends to the left

typedef std::function<double(const Path64& path, const PathD& path_normals, size_t curr_idx, size_t prev_idx)> DeltaCallback64;

Expand Down
83 changes: 66 additions & 17 deletions CPP/Clipper2Lib/src/clipper.offset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,25 +319,53 @@ void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size

if (cos_a > -0.999 && (sin_a * group_delta_ < 0)) // test for concavity first (#593)
{
// is concave
// by far the simplest way to construct concave joins, especially those joining very
// short segments, is to insert 3 points that produce negative regions. These regions
// will be removed later by the finishing union operation. This is also the best way
// to ensure that path reversals (ie over-shrunk paths) are removed.
// in case of EndType::ButtLeft the rectangles, formed by a one-sided offset,
// will cause artifacts if the angle is sharper then 90 degree.
// to avoid these artifacts calculate the intersection between between the segments
// of the one-sided offset.
const size_t l = k > j ? j - 1 : j + 1; // k is always j + 1 or j - 1, l is the opposite
const bool intersect =
end_type_ == EndType::ButtLeft // one-sided offset
&& cos_a < 0 // < 90 degre
&& !path_out.empty() // safety check, can this never happen?
&& l < path.size(); // safety check, can this never happen?

if (!intersect)
{
// is concave
// by far the simplest way to construct concave joins, especially those joining very
// short segments, is to insert 3 points that produce negative regions. These regions
// will be removed later by the finishing union operation. This is also the best way
// to ensure that path reversals (ie over-shrunk paths) are removed.
#ifdef USINGZ
path_out.push_back(Point64(GetPerpendic(path[j], norms[k], group_delta_), path[j].z));
path_out.push_back(Point64(GetPerpendic(path[j], norms[k], group_delta_), path[j].z));
#else
path_out.push_back(GetPerpendic(path[j], norms[k], group_delta_));
path_out.push_back(GetPerpendic(path[j], norms[k], group_delta_));
#endif
// when the angle is almost flat (cos_a ~= 1), it's safe to skip this middle point
if (cos_a < 0.999) path_out.push_back(path[j]); // (#405, #873)

// when the angle is almost flat (cos_a ~= 1), it's safe to skip this middle point
if (cos_a < 0.999) path_out.push_back(path[j]); // (#405, #873)

#ifdef USINGZ
path_out.push_back(Point64(GetPerpendic(path[j], norms[j], group_delta_), path[j].z));
path_out.push_back(Point64(GetPerpendic(path[j], norms[j], group_delta_), path[j].z));
#else
path_out.push_back(GetPerpendic(path[j], norms[j], group_delta_));
path_out.push_back(GetPerpendic(path[j], norms[j], group_delta_));
#endif
}
else // point of intersection for one-sided offset
{
// calculate intersection point between the two line segments
// shifted in the offset direction at current point.
const PointD pt1a(path_out.back());
const PointD pt1b(GetPerpendic(path[j], norms[k], group_delta_));
const PointD pt2a(GetPerpendic(path[j], norms[j], group_delta_));
const PointD pt2b(GetPerpendic(path[l], norms[j], group_delta_));

PointD pt;
GetSegmentIntersectPt(pt1a, pt1b, pt2a, pt2b, pt);

path_out.push_back(Point64(pt));
}
}
else if (cos_a > 0.999 && join_type_ != JoinType::Round)
{
Expand Down Expand Up @@ -385,8 +413,11 @@ void ClipperOffset::OffsetOpenPath(Group& group, const Path64& path)
{
// do the line start cap
if (deltaCallback64_) group_delta_ = deltaCallback64_(path, norms, 0, 0);

const double abs_group_delta = std::fabs(group_delta_);
Point64 left_end;

if (std::fabs(group_delta_) <= floating_point_tolerance)
if (abs_group_delta <= floating_point_tolerance)
path_out.push_back(path[0]);
else
{
Expand All @@ -398,16 +429,23 @@ void ClipperOffset::OffsetOpenPath(Group& group, const Path64& path)
case EndType::Round:
DoRound(path, 0, 0, PI);
break;
case EndType::ButtLeft:
left_end = Point64(GetPerpendicD(path[0], norms[0], -abs_group_delta));
break;
default:
DoSquare(path, 0, 0);
break;
}
}

size_t highI = path.size() - 1;
// offset the left side going forward
for (Path64::size_type j = 1, k = 0; j < highI; k = j, ++j)
OffsetPoint(group, path, j, k);

if (end_type_ != EndType::ButtLeft)
{
// offset the left side going forward
for (Path64::size_type j = 1, k = 0; j < highI; k = j, ++j)
OffsetPoint(group, path, j, k);
}

// reverse normals
for (size_t i = highI; i > 0; --i)
Expand All @@ -418,7 +456,7 @@ void ClipperOffset::OffsetOpenPath(Group& group, const Path64& path)
if (deltaCallback64_)
group_delta_ = deltaCallback64_(path, norms, highI, highI);

if (std::fabs(group_delta_) <= floating_point_tolerance)
if (abs_group_delta <= floating_point_tolerance)
path_out.push_back(path[highI]);
else
{
Expand All @@ -430,6 +468,9 @@ void ClipperOffset::OffsetOpenPath(Group& group, const Path64& path)
case EndType::Round:
DoRound(path, highI, highI, PI);
break;
case EndType::ButtLeft:
path_out.push_back(Point64(GetPerpendicD(path[highI - 1], norms[highI - 1], abs_group_delta)));
break;
default:
DoSquare(path, highI, highI);
break;
Expand All @@ -438,6 +479,14 @@ void ClipperOffset::OffsetOpenPath(Group& group, const Path64& path)

for (size_t j = highI -1, k = highI; j > 0; k = j, --j)
OffsetPoint(group, path, j, k);

if (end_type_ == EndType::ButtLeft)
{
path_out.reserve(path_out.size() + 1 + path.size());
path_out.push_back(left_end);
path_out.insert(path_out.begin(), path.begin(), path.end());
}

solution->push_back(path_out);
}

Expand Down

0 comments on commit 77abf38

Please sign in to comment.