Skip to content

Commit

Permalink
Added a new join type (JoinType::Bevel) for offsetting //AngusJohnson…
Browse files Browse the repository at this point in the history
  • Loading branch information
AngusJohnson committed Sep 24, 2023
1 parent b444ed3 commit b462b5b
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 113 deletions.
9 changes: 6 additions & 3 deletions CPP/Clipper2Lib/include/clipper2/clipper.offset.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 15 May 2023 *
* Date : 24 September 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : Path Offset (Inflate/Shrink) *
Expand All @@ -15,7 +15,9 @@

namespace Clipper2Lib {

enum class JoinType { Square, Round, Miter };
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
Expand Down Expand Up @@ -51,7 +53,7 @@ class ClipperOffset {
PathD norms;
Paths64 solution;
std::vector<Group> groups_;
JoinType join_type_ = JoinType::Square;
JoinType join_type_ = JoinType::Bevel;
EndType end_type_ = EndType::Polygon;

double miter_limit_ = 0.0;
Expand All @@ -64,6 +66,7 @@ class ClipperOffset {
#endif
DeltaCallback64 deltaCallback64_ = nullptr;

void DoBevel(Group& group, const Path64& path, size_t j, size_t k);
void DoSquare(Group& group, const Path64& path, size_t j, size_t k);
void DoMiter(Group& group, const Path64& path, size_t j, size_t k, double cos_a);
void DoRound(Group& group, const Path64& path, size_t j, size_t k, double angle);
Expand Down
53 changes: 27 additions & 26 deletions CPP/Clipper2Lib/src/clipper.offset.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 19 September 2023 *
* Date : 24 September 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : Path Offset (Inflate/Shrink) *
Expand Down Expand Up @@ -200,6 +200,24 @@ PointD IntersectPoint(const PointD& pt1a, const PointD& pt1b,
}
}

void ClipperOffset::DoBevel(Group& group, const Path64& path, size_t j, size_t k)
{
PointD pt1, pt2;
if (j == k)
{
double abs_delta = std::abs(group_delta_);
pt1 = PointD(path[j].x - abs_delta * norms[j].x, path[j].y - abs_delta * norms[j].y);
pt2 = PointD(path[j].x + abs_delta * norms[j].x, path[j].y + abs_delta * norms[j].y);
}
else
{
pt1 = PointD(path[j].x + group_delta_ * norms[k].x, path[j].y + group_delta_ * norms[k].y);
pt2 = PointD(path[j].x + group_delta_ * norms[j].x, path[j].y + group_delta_ * norms[j].y);
}
group.path.push_back(Point64(pt1));
group.path.push_back(Point64(pt2));
}

void ClipperOffset::DoSquare(Group& group, const Path64& path, size_t j, size_t k)
{
PointD vec;
Expand Down Expand Up @@ -342,10 +360,13 @@ void ClipperOffset::OffsetPoint(Group& group, Path64& path, size_t j, size_t k)
if (cos_a > temp_lim_ - 1) DoMiter(group, path, j, k, cos_a);
else DoSquare(group, path, j, k);
}
else if (cos_a > 0.99 || join_type_ == JoinType::Square) // 0.99 ~= 8.1 deg.
DoSquare(group, path, j, k);
else
else if (cos_a > 0.99 || join_type_ == JoinType::Bevel)
// ie > 2.5 deg (see above) but less than ~8 deg ( acos(0.99) )
DoBevel(group, path, j, k);
else if (join_type_ == JoinType::Round)
DoRound(group, path, j, k, std::atan2(sin_a, cos_a));
else
DoSquare(group, path, j, k);
}

void ClipperOffset::OffsetPolygon(Group& group, Path64& path)
Expand Down Expand Up @@ -394,17 +415,7 @@ void ClipperOffset::OffsetOpenPath(Group& group, Path64& path)
switch (end_type_)
{
case EndType::Butt:
#ifdef USINGZ
group.path.push_back(Point64(
path[0].x - norms[0].x * group_delta_,
path[0].y - norms[0].y * group_delta_,
path[0].z));
#else
group.path.push_back(Point64(
path[0].x - norms[0].x * group_delta_,
path[0].y - norms[0].y * group_delta_));
#endif
group.path.push_back(GetPerpendic(path[0], norms[0], group_delta_));
DoBevel(group, path, 0, 0);
break;
case EndType::Round:
DoRound(group, path, 0, 0, PI);
Expand Down Expand Up @@ -436,17 +447,7 @@ void ClipperOffset::OffsetOpenPath(Group& group, Path64& path)
switch (end_type_)
{
case EndType::Butt:
#ifdef USINGZ
group.path.push_back(Point64(
path[highI].x - norms[highI].x * group_delta_,
path[highI].y - norms[highI].y * group_delta_,
path[highI].z));
#else
group.path.push_back(Point64(
path[highI].x - norms[highI].x * group_delta_,
path[highI].y - norms[highI].y * group_delta_));
#endif
group.path.push_back(GetPerpendic(path[highI], norms[highI], group_delta_));
DoBevel(group, path, highI, highI);
break;
case EndType::Round:
DoRound(group, path, highI, highI, PI);
Expand Down
28 changes: 17 additions & 11 deletions CPP/Examples/Inflate/Inflate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ void System(const std::string& filename);

int main(int argc, char* argv[])
{
//DoSimpleShapes();
DoSimpleShapes();
DoRabbit();

std::getchar();
//std::getchar();
}

void DoSimpleShapes()
Expand All @@ -26,23 +26,29 @@ void DoSimpleShapes()

FillRule fr2 = FillRule::EvenOdd;
SvgWriter svg2;
op1.push_back(MakePath({ 80,60, 20,20, 180,20, 180,80, 20,180, 180,180 }));
op2 = InflatePaths(op1, 20, JoinType::Square, EndType::Butt);
op1.push_back(MakePath({ 80,60, 20,20, 180,20, 180,70, 25,150, 20,180, 180,180 }));
op2 = InflatePaths(op1, 20, JoinType::Miter, EndType::Square, 3);
SvgAddOpenSubject(svg2, op1, fr2, false);
SvgAddSolution(svg2, Paths64ToPathsD(op2), fr2, false);
SvgAddCaption(svg2, "Square Joins; Butt Ends", 20, 220);
SvgAddCaption(svg2, "Miter Joins; Square Ends", 20, 210);

op1 = TranslatePaths(op1, 250, 0);
op2 = InflatePaths(op1, 20, JoinType::Miter, EndType::Square, 3);
op1 = TranslatePaths<int64_t>(op1, 210, 0);
op2 = InflatePaths(op1, 20, JoinType::Square, EndType::Square);
SvgAddOpenSubject(svg2, op1, fr2, false);
SvgAddSolution(svg2, Paths64ToPathsD(op2), fr2, false);
SvgAddCaption(svg2, "Square Joins; Square Ends", 230, 210);

op1 = TranslatePaths<int64_t>(op1, 210, 0);
op2 = InflatePaths(op1, 20, JoinType::Bevel, EndType::Butt, 3);
SvgAddOpenSubject(svg2, op1, fr2, false);
SvgAddSolution(svg2, Paths64ToPathsD(op2), fr2, false);
SvgAddCaption(svg2, "Miter Joins; Square Ends", 300, 220);
SvgAddCaption(svg2, "Bevel Joins; Butt Ends", 440, 210);

op1 = TranslatePaths(op1, 250, 0);
op1 = TranslatePaths<int64_t>(op1, 210, 0);
op2 = InflatePaths(op1, 20, JoinType::Round, EndType::Round);
SvgAddOpenSubject(svg2, op1, fr2, false);
SvgAddSolution(svg2, Paths64ToPathsD(op2), fr2, false);
SvgAddCaption(svg2, "Round Joins; Round Ends", 580, 220);
SvgAddCaption(svg2, "Round Joins; Round Ends", 650, 210);

SvgSaveToFile(svg2, "open_paths.svg", 800, 600, 20);
System("open_paths.svg");
Expand All @@ -68,7 +74,7 @@ void DoSimpleShapes()
//different join types within the same offset operation
ClipperOffset co;
co.AddPaths(p, JoinType::Miter, EndType::Joined);
p = TranslatePaths(p, 120, 100);
p = TranslatePaths<int64_t>(p, 120, 100);
pp.insert(pp.end(), p.begin(), p.end());
co.AddPaths(p, JoinType::Round, EndType::Joined);
co.Execute(20, p);
Expand Down
2 changes: 1 addition & 1 deletion CPP/Utils/clipper.svg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ namespace Clipper2Lib {
file << " <g font-family=\"" << ti->font_name << "\" font-size=\"" <<
ti->font_size << "\" fill=\"" << ColorToHtml(ti->font_color) <<
"\" fill-opacity=\"" << GetAlphaAsFrac(ti->font_color) << "\">\n";
file << " <text x=\"" << (ti->x + margin) << "\" y=\"" << (ti->y+margin) << "\">" <<
file << " <text x=\"" << (ti->x * scale + offsetX) << "\" y=\"" << (ti->y * scale + offsetY) << "\">" <<
ti->text << "</text>\n </g>\n\n";
}

Expand Down
27 changes: 19 additions & 8 deletions CSharp/Clipper2Lib.Examples/InflateDemo/Main.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 19 July 2023 *
* Date : 24 September 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2022 *
* Copyright : Angus Johnson 2010-2023 *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/

Expand Down Expand Up @@ -32,28 +32,39 @@ public static void DoSimpleShapes()

for (int i = 0; i < 5; ++i)
{
//nb: the following '10' parameter greatly increases miter limit
//nb: the last parameter here (10) greatly increases miter limit
p = Clipper.InflatePaths(p, 5, JoinType.Miter, EndType.Polygon, 10);
pp.AddRange(p);
}

//rectangle offset - both squared and rounded
p.Clear();
p.Add(Clipper.MakePath(new int[] { 100, 0, 340, 0, 340, 200, 100, 200 }));
pp.AddRange(p);
ClipperOffset co = new();

//nb: using the ClipperOffest class directly here to control
//different join types within the same offset operation
ClipperOffset co = new();
p.Add(Clipper.MakePath(new int[] { 100, 0, 340, 0, 340, 200, 100, 200 }));
pp.AddRange(p);
co.AddPaths(p, JoinType.Bevel, EndType.Joined);

p = Clipper.TranslatePaths(p, 60, 50);
pp.AddRange(p);
co.AddPaths(p, JoinType.Square, EndType.Joined);
p = Clipper.TranslatePaths(p, 120, 100);

p = Clipper.TranslatePaths(p, 60, 50);
pp.AddRange(p);
co.AddPaths(p, JoinType.Round, EndType.Joined);


co.Execute(20, p);
pp.AddRange(p);

string filename = "../../../inflate.svg";
SvgWriter svg = new();
SvgUtils.AddSolution(svg, pp, false);
SvgUtils.AddCaption(svg, "Beveled join", 100, -27);
SvgUtils.AddCaption(svg, "Squared join", 160, 23);
SvgUtils.AddCaption(svg, "Rounded join", 220, 73);
SvgUtils.SaveToFile(svg, filename, FillRule.EvenOdd, 800, 600, 20);
ClipperFileIO.OpenFileWithDefaultApp(filename);
}
Expand All @@ -69,7 +80,7 @@ public static void DoRabbit()
pd = Clipper.InflatePaths(pd, -2.5, JoinType.Round, EndType.Polygon);
// SimplifyPaths - is not essential but it not only
// speeds up the loop but it also tidies the result
pd = Clipper.SimplifyPaths(pd, 0.2);
pd = Clipper.SimplifyPaths(pd, 0.25);
solution.AddRange(pd);
}

Expand Down
60 changes: 31 additions & 29 deletions CSharp/Clipper2Lib/Clipper.Offset.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 19 September 2023 *
* Date : 24 September 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : Path Offset (Inflate/Shrink) *
Expand All @@ -15,9 +15,10 @@ namespace Clipper2Lib
{
public enum JoinType
{
Miter,
Square,
Round,
Miter
Bevel,
Round
};

public enum EndType
Expand Down Expand Up @@ -323,6 +324,25 @@ private PointD GetPerpendicD(Point64 pt, PointD norm)
#endif
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DoBevel(Group group, Path64 path, int j, int k)
{
Point64 pt1, pt2;
if (j == k)
{
double absDelta = Math.Abs(_groupDelta);
pt1 = new Point64(path[j].X - absDelta * _normals[j].x, path[j].Y - absDelta * _normals[j].y);
pt2 = new Point64(path[j].X + absDelta * _normals[j].x, path[j].Y + absDelta * _normals[j].y);
}
else
{
pt1 = new Point64(path[j].X + _groupDelta * _normals[k].x, path[j].Y + _groupDelta * _normals[k].y);
pt2 = new Point64(path[j].X + _groupDelta * _normals[j].x, path[j].Y + _groupDelta * _normals[j].y);
}
group.outPath.Add(pt1);
group.outPath.Add(pt2);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DoSquare(Group group, Path64 path, int j, int k)
{
Expand Down Expand Up @@ -483,12 +503,14 @@ private void OffsetPoint(Group group, Path64 path, int j, ref int k)
if (cosA > _mitLimSqr - 1) DoMiter(group, path, j, k, cosA);
else DoSquare(group, path, j, k);
}
else if (cosA > 0.99 || _joinType == JoinType.Square)
else if (cosA > 0.99 || _joinType == JoinType.Bevel)
//angle less than 8 degrees or a squared join
DoSquare(group, path, j, k);
else
DoBevel(group, path, j, k);
else if (_joinType == JoinType.Round)
DoRound(group, path, j, k, Math.Atan2(sinA, cosA));

else
DoSquare(group, path, j, k);

k = j;
}

Expand Down Expand Up @@ -537,17 +559,7 @@ private void OffsetOpenPath(Group group, Path64 path)
switch (_endType)
{
case EndType.Butt:
#if USINGZ
group.outPath.Add(new Point64(
path[0].X - _normals[0].x * _groupDelta,
path[0].Y - _normals[0].y * _groupDelta,
path[0].Z));
#else
group.outPath.Add(new Point64(
path[0].X - _normals[0].x * _groupDelta,
path[0].Y - _normals[0].y * _groupDelta));
#endif
group.outPath.Add(GetPerpendic(path[0], _normals[0]));
DoBevel(group, path, 0, 0);
break;
case EndType.Round:
DoRound(group, path, 0, 0, Math.PI);
Expand Down Expand Up @@ -575,17 +587,7 @@ private void OffsetOpenPath(Group group, Path64 path)
switch (_endType)
{
case EndType.Butt:
#if USINGZ
group.outPath.Add(new Point64(
path[highI].X - _normals[highI].x * _groupDelta,
path[highI].Y - _normals[highI].y * _groupDelta,
path[highI].Z));
#else
group.outPath.Add(new Point64(
path[highI].X - _normals[highI].x * _groupDelta,
path[highI].Y - _normals[highI].y * _groupDelta));
#endif
group.outPath.Add(GetPerpendic(path[highI], _normals[highI]));
DoBevel(group, path, highI, highI);
break;
case EndType.Round:
DoRound(group, path, highI, highI, Math.PI);
Expand Down
2 changes: 1 addition & 1 deletion CSharp/Utils/SVG/Clipper.SVG.cs
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ public bool SaveToFile(string filename, int maxWidth = 0, int maxHeight = 0, int
{
writer.Write("<g font-family=\"Verdana\" font-style=\"normal\" " +
"font-weight=\"normal\" font-size=\"{0}\" fill=\"{1}\">\n", captionInfo.fontSize, ColorToHtml(captionInfo.fontColor));
writer.Write("<text x=\"{0}\" y=\"{1}\">{2}</text>\n</g>\n", captionInfo.posX + margin, captionInfo.posY + margin, captionInfo.text);
writer.Write("<text x=\"{0}\" y=\"{1}\">{2}</text>\n</g>\n", captionInfo.posX * scale + offsetX, captionInfo.posY * scale + offsetX, captionInfo.text);
}

writer.Write("</svg>\n");
Expand Down
Loading

0 comments on commit b462b5b

Please sign in to comment.