GithubHelp home page GithubHelp logo

angusjohnson / clipper2 Goto Github PK

View Code? Open in Web Editor NEW
1.3K 46.0 251.0 11.39 MB

Polygon Clipping and Offsetting - C++, C# and Delphi

License: Boost Software License 1.0

Pascal 32.33% C# 27.66% C++ 38.71% CMake 1.25% C 0.06%
cpp csharp delphi geometry pascal polygon-intersection polygon-union offsetting polygon polygon-boolean

clipper2's People

Contributors

aismann avatar angusjohnson avatar balagansky avatar bubnikv avatar cwangbh avatar damiandixon avatar djgosnell avatar freza-tm avatar gornhoth avatar guuskuiper avatar jamesruan avatar jmalinza avatar jschuh avatar jspam avatar klauslh avatar lederernc avatar loumalouomega avatar lutz avatar masbug avatar mattdev avatar mmassing avatar philstopford avatar phytolizer avatar r-barnes avatar reunanen avatar sebastiandirks avatar sergey-239 avatar sethhillbrand avatar thehans avatar zeronsix avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

clipper2's Issues

Polytree build error

Like I mentioned in #27 there now seems to be a crash when building the solution tree after the clip operation in the function:
BuildTree
It seems to be related to the way owner_polypath gets assigned?

When it gets to the last line outrec->polypath = owner_polypath->AddChild(path); and it tries to add the path as a child. It looks like the owner_polypath is invalid and the operation crashes.

This error shows up in the example I shared with you in #27

C++ fails to compile with REVERSE_ORIENTATION

./Clipper2/CPP/Clipper2Lib/clipper.engine.cpp:3296:6: error: no viable overloaded '='
                op = op.next;
                ~~ ^ ~~~~~~~
./Clipper2/CPP/Clipper2Lib/clipper.engine.h:63:9: note: candidate function (the implicit copy assignment operator) not viable: no known conversion from 'Clipper2Lib::OutPt *' to
      'const Clipper2Lib::OutPt' for 1st argument; dereference the argument with *
        struct OutPt {
               ^
1 error generated.

Clipping line goes into an infinite loop

This simple example will cause Execute to loop endlessly:

Clipper64 clipper;

Paths64 squares;
Path64 square = MakePath( "3, 3, 3, 0, 0, 0, 0, 3" );
squares.push_back( square );
clipper.AddClip( squares );

Paths64 lines;
Path64 line = MakePath( "2, 0, 1, 0, 0, 0" );
lines.push_back( line );
clipper.AddOpenSubject( lines );

PolyTree64 clippedPolygons;
Paths64 clippedLines;

clipper.Execute( ClipType::Difference, FillRule::EvenOdd, clippedPolygons, clippedLines );

TClipperD.Create(8) with Range Check Error

The double clipper with precision=8 produces a range check error. I can provide you with the path data. Is there no upload opportunity in github?

Clp := TClipperD.Create(8);

function LocMinListSort(item1, item2: Pointer): Integer;
var
dy: Int64;
lm1: PLocalMinima absolute item1;
lm2: PLocalMinima absolute item2;
begin
dy := lm2.Vertex.Pt.Y - lm1.Vertex.Pt.Y;
if dy < 0 then Result := -1
else if dy > 0 then Result := 1
else Result := lm2.Vertex.Pt.X - lm1.Vertex.Pt.X; //<--Range Check Error, res too big for Integer
end;

Open Path fail in current code

Path64 lPoly = Clipper.MakePath(new [] {
    0,0,
    0,500000,
    100000,500000,
    100000,200000,
    600000,200000,
    600000,0,
    0,0

});

Path64 t = Clipper.MakePath(new[] {
    100000,200000,
    100000,-9800000

});

c.AddOpenSubject(t);
c.AddClip(lPoly);

PolyTree64 pt = new();
Paths64 p = new();

c.Execute(ClipType.Intersection, FillRule.EvenOdd, pt, p);

As of Jul 18, this yielded :

[0] = {Point64} 100000,200000,0
[1] = {Point64} 100000,0,0 

but now the result in p is the same as the input t, which would appear to be wrong.

DoMaxima Unreachable Code

I enabled the Nullable checks for the project which has the unfortunate issue of causing warnings to appear throughout the codebase. One of the nice benefits is it helps the compiler determine what can be null and what can't be.

I ran into the code below for the private Active? DoMaxima(Active ae) method and it appears that on line 2356, the compiler is telling me that maxPair can never be null at this point due to the return if null above on line 2340. See the code snippets below.

Since there appears there is a desire for private OutPt AddOutPt(Active ae, Point64 pt) to be run in some circumstance, but it never can actually run in the code, I figured I would raise the issue for discussion instead of just removing the un-runnable code.

          if (maxPair != null) // <-- Is always true
            AddLocalMaxPoly(ae, maxPair, ae.top);
          else
            AddOutPt(ae, ae.top); // <- Never runs

image

Open Path Clipping issue

I've been using a version of Clipper2 from April 19, to clip open polylines with a polygon to find the intersection points of the lines on the clipping polygon. It worked fine--but I just downloaded the latest commit and my code now fails and returns a null solution. The example test code below should return an intersection in openPaths64 of (340,50),(340,250),(140,50),(140,250).

Doing comparisions of previous versions, it seems the problem I'm having was introduced on 5/4 somewhere in Commit 8e8925b which fails in my code. However, Commit 04c591e also on 5/4, works Ok and returns the expected solution.

...
var
clipPoly64,subjOpen64,closedPaths64,openPaths64: tpaths64;

setlength(clipPoly64,1);
setlength(subjOpen64,4);

clipPoly64[0] := MakePath([350,50,50,50,50,250,350,250,350,50]);
subjOpen64[0] := MakePath([-60,-20,-60,380]);
subjOpen64[1] := MakePath([140,-20,140,380]);
subjOpen64[2] := MakePath([340,-20,340,380]);
subjOpen64[3] := MakePath([540,-20,540,380]);

with TClipper.Create do
try
AddOpenSubject(subjOpen64);
AddClip(clipPoly64);
Execute(ctIntersection,frEvenOdd,closedPaths64,openPaths64);
finally
Free;
end;

Integer overflow with float path data

function DistanceSqr(const pt1, pt2: TPoint64): double;
var
  d: Double;
begin
  //Result := Sqr(pt1.X - pt2.X) + Sqr(pt1.Y - pt2.Y);  //integer overflow

  d := Sqr(pt1.X - pt2.X);
  Result := d + Sqr(pt1.Y - pt2.Y);                     //okay
end;

ClipperD with Range Check Error

function IntersectListSort(node1, node2: Pointer): Integer;
var
i1: PIntersectNode absolute node1;
i2: PIntersectNode absolute node2;
begin
result := i2.Pt.Y - i1.Pt.Y;
//Sort by X too. While not essential, this significantly speed up the
//secondary sort in ProcessIntersectList (which ensures edge adjacency).
if (result = 0) and (i1 <> i2) then
result := i1.Pt.X - i2.Pt.X; //<------------------Range Check Error
end;

Performance: multiplication is cheaper than division

function GetIntersectPoint(const ln1a, ln1b, ln2a, ln2b: TPoint64): TPointD;

-->
Result.X := (ln1a.X + ln1b.X)/2;
Result.Y := (ln1a.Y + ln1b.Y)/2;
-->
Result.X := (ln1a.X + ln1b.X) * 0.5;
Result.Y := (ln1a.Y + ln1b.Y) * 0.5;

procedure TClipperOffset.DoGroupOffset(pathGroup: TPathGroup; delta: double);
-->
fStepsPerRad := steps / Two_Pi;
-->
const
Two_Pi_Invers = 1/2*PI;

fStepsPerRad := steps * Two_Pi_Invers;

Couple of regressions in last commit

Hi Angus,

Referring the the C-Sharp implementation, the last commit (fixed my reported other issue: THANKS!), however it also introduced some regressions:

  • Clipper.Engine.cs, line 3535: call to this function leads to stack overflow due to endless recursive call (ClipperFunc.ScalePaths returns exactly the signature this functions needs, so it's called again)
  • Clipper.Engine.cs, line 3599: creates a new "solutionClosed" object, and the passed in solutionClosed object remains null, so result is empty
  • noticed you replaced come for-loops with foreach. Guess because code looks cleaner, but it also generates more garbage (GC collection could cause performance degradation). This might be irrelevant for what you aim for, but just FYI I made a port of the Clipper2Lib CSharp class to a struct to make it compatible with Unity BURST (and here a blog). SO I have to get rid of all managed code, foreach is among the incompatible stuff. This class-->struct conversation and BURST compiler alone leads to a ~10x speedup. Adding in the Unity Job system I can saturate now any number of worker thread clipping polygons in parallel (another 10x speadup)..so 50-100 times speedup. Will upload the Unity package + some class/struct performance comparison soon on GitHub in case you are interested.

-Fribur

Request for Performance Dataset (Not an Issue)

@AngusJohnson
I am writing to request what random intersecting polygon generator algorithm you used for the performance so that I can compare performance on my end.

I apologize as the Issues system is not the best way to contact someone , but I don't see any other method to contact you since there not discussions enabled for the project.

Rename members of `Rect`

I suggest to rename left, right, top, bottom to something that makes sense regardless of y-axis orientation / REVERSE_ORIENTATION.

Examples

Hello!

I follow this project with interest, but I am not able to write examples by myself. I still see no examples in the repository. I tried to convert an example shipped with the first version of the library, but without success.

Could you please include some examples, and (if possible) some simple examples, so we can understand the principles?

Regards.

Roland

P.-S. In case you are interested, I attach a modified version of the Cairo demo shipped with Clipper 1. I changed the Windows application in a simple console program, because I wished to be able to test the library under Linux.

clipper1-cairo-demo-simple-program.zip

Polygons are not being added to the Polytree correctly.

I made a simple test case of a small rectangle inside a larger one. Then substracted the smaller one from the large one.
I expected the resulting tree to have a large rectangle at the root of the tree (not hole) and the smaller one to be a child of the bigger one (is hole).

Instead they are both at the root and they are both (not hole).

Clipper 2 C++ crash

I was trying to do a boolean operation on a very large polygon array that runs fine on Clipper 1 C++, but takes around 3min.

When I tried the same operation with the same settings (Intersection, closed polygons (Path64), NonZero, polytree output) the code is crashing in the following function:

inline bool IsOuter(const OutRec& outrec)
	{
		return (outrec.state == OutRecState::Outer);
	}

It seems to be trying to read outrec even though it is set to NULL.

I realize a minimum working example would be nice. But I have this pretty integrated into much larger code that reads the polygons off a GDS file. So I'm not sure how to share the polygon structures with you. (If you have any preferences let me know)

PS. When running the operation on a smaller set of polygons, there are no issues.

Compile on Delphi 6

Does not compile:

{$IFDEF REVERSE_ORIENTATION}
const fillPos = TFillRule.frNegative;
const fillNeg = TFillRule.frPositive;
{$ELSE}
const fillPos = TFillRule.frPositive;
const fillNeg = TFillRule.frNegative;
{$ENDIF}

Does compile:

{$IFDEF REVERSE_ORIENTATION}
const fillPos = frNegative;
const fillNeg = frPositive;
{$ELSE}
const fillPos = frPositive;
const fillNeg = frNegative;
{$ENDIF}

Unit Classes

Unit Classes is not needed in Clipper.core.pas

Funky outputs from today's updated version

I think today's commit (51979a43) may have broken something (in the C++ version).

I will try to isolate the issue to some reproducible example. In the meanwhile, however, here is how the problem is appearing: one half of the results are missing (top left in my visualizations, but I guess that depends on the coordinate system), as if clipped by a triangle:
after

The expected result, from the previous version:
before

(Please disregard any pixel-level differences between the above images. This is from a larger test, originally intended to test something completely different. That thing is a bit stochastic, so the results are not completely deterministic.)

I'm certainly not saying I'm doing everything right. But anyway nothing except ClipperLib version changed between obtaining these two results.

Question: How do I offset a line to left and right which is open at both ends not closed

I'm getting differing results when attempting to create offset lines.

I am attempting to offset to the left and right to a center line without closing the ends.

The following does not actually display any offset lines., but does kind of work for more complex lines but with lines closing.

  Clipper2Lib::PathD linePath;
  linePath.emplace_back(PointD(0, 0));
  linePath.emplace_back(PointD(1000, 1000));
  linePath.emplace_back(PointD(2000, 2000));
  linePath.emplace_back(PointD(3000, 3000));

  // center line
  svg.AddPath(linePath, true, 0x00000000, 0x50ff0050, 0.8, false);

  ClipperOffset co;
  co.AddPath(linePath, JoinType::Square, EndType::Polygon);
  const auto leftLine = co.Execute(1000);
  svg.AddPaths(leftLine, true, 0x00000000, 0xffff0050, 0.8, false);

  co.Clear();
  co.AddPath(linePath, JoinType::Square, EndType::Polygon);
  const auto rightLine = co.Execute(-1000);
  svg.AddPaths(rightLine, true, 0x00000000, 0xff00ff50, 0.8, false);

  SvgSaveToFile(svg, "offset_line_test.svg", 300, 300, 0);

The following is the result:
offset_line_test

With a more complex line I actually get the left and right lines but they are closed, where I'd just like the individual lines openended and butted. I gather Clipper1 had the ability to do this.

5_test_line_offset

Just wondering if I'm doing something wrong or its just not possible to do yet.

Join bug

Angus, when you look at the following picture, would you have an idea what might have went wrong during joining? The upper left join looks like 2 points off. Unrelated, I have a hunch line 853 should be _vertexList.Add(curr_v); and not _vertexList.Add(v0); ?
image

Problems with InflatePaths

When I do an InflatePaths operation on a closed rectangular path. I expect a rectangle with a hole, instead I get a single outer rectangle.

I'm using the endtype "Butt"

building with clang on linux highlights some problems

Compiler: clang version 14.0.0
OS: Linux (Ubuntu 22.04)

Build steps:

cd CPP
mkdir build_clang
cd build_clang
export CC=/usr/bin/clang
export CXX=/usr/bin/clang++
cmake ../ -DCMAKE_BUILD_TYPE=Debug

Errors produced:

[  3%] Building CXX object CMakeFiles/Clipper2.dir/Clipper2Lib/clipper.engine.cpp.o
In file included from /home/damian/projects/checks/Clipper2/CPP/Clipper2Lib/clipper.engine.cpp:17:
/home/damian/projects/checks/Clipper2/CPP/Clipper2Lib/clipper.engine.h:422:8: error: 'Clipper2Lib::ClipperD::Execute' hides overloaded virtual functions [-Werror,-Woverloaded-virtual]
                bool Execute(ClipType clip_type, FillRule fill_rule, PathsD& closed_paths)
                     ^
/home/damian/projects/checks/Clipper2/CPP/Clipper2Lib/clipper.engine.h:255:16: note: hidden overloaded virtual function 'Clipper2Lib::ClipperBase::Execute' declared here: type mismatch at 3rd parameter ('Clipper2Lib::Paths64 &' (aka 'vector<vector<Point<long>>> &') vs 'Clipper2Lib::PathsD &' (aka 'vector<vector<Point<double>>> &'))
                virtual bool Execute(ClipType clip_type,
                             ^
/home/damian/projects/checks/Clipper2/CPP/Clipper2Lib/clipper.engine.h:257:16: note: hidden overloaded virtual function 'Clipper2Lib::ClipperBase::Execute' declared here: different number of parameters (4 vs 3)
                virtual bool Execute(ClipType clip_type,
                             ^
/home/damian/projects/checks/Clipper2/CPP/Clipper2Lib/clipper.engine.h:259:16: note: hidden overloaded virtual function 'Clipper2Lib::ClipperBase::Execute' declared here: different number of parameters (4 vs 3)
                virtual bool Execute(ClipType clip_type,
                             ^
/home/damian/projects/checks/Clipper2/CPP/Clipper2Lib/clipper.engine.h:430:8: error: 'Clipper2Lib::ClipperD::Execute' hides overloaded virtual functions [-Werror,-Woverloaded-virtual]
                bool Execute(ClipType clip_type,
                     ^
/home/damian/projects/checks/Clipper2/CPP/Clipper2Lib/clipper.engine.h:255:16: note: hidden overloaded virtual function 'Clipper2Lib::ClipperBase::Execute' declared here: different number of parameters (3 vs 4)
                virtual bool Execute(ClipType clip_type,
                             ^
/home/damian/projects/checks/Clipper2/CPP/Clipper2Lib/clipper.engine.h:257:16: note: hidden overloaded virtual function 'Clipper2Lib::ClipperBase::Execute' declared here: type mismatch at 3rd parameter ('Clipper2Lib::Paths64 &' (aka 'vector<vector<Point<long>>> &') vs 'Clipper2Lib::PathsD &' (aka 'vector<vector<Point<double>>> &'))
                virtual bool Execute(ClipType clip_type,
                             ^
/home/damian/projects/checks/Clipper2/CPP/Clipper2Lib/clipper.engine.h:259:16: note: hidden overloaded virtual function 'Clipper2Lib::ClipperBase::Execute' declared here: type mismatch at 3rd parameter ('Clipper2Lib::PolyTree64 &' (aka 'PolyPath<long> &') vs 'Clipper2Lib::PathsD &' (aka 'vector<vector<Point<double>>> &'))
                virtual bool Execute(ClipType clip_type,
                             ^
/home/damian/projects/checks/Clipper2/CPP/Clipper2Lib/clipper.engine.h:442:8: error: 'Clipper2Lib::ClipperD::Execute' hides overloaded virtual functions [-Werror,-Woverloaded-virtual]
                bool Execute(ClipType clip_type,
                     ^
/home/damian/projects/checks/Clipper2/CPP/Clipper2Lib/clipper.engine.h:255:16: note: hidden overloaded virtual function 'Clipper2Lib::ClipperBase::Execute' declared here: different number of parameters (3 vs 4)
                virtual bool Execute(ClipType clip_type,
                             ^
/home/damian/projects/checks/Clipper2/CPP/Clipper2Lib/clipper.engine.h:257:16: note: hidden overloaded virtual function 'Clipper2Lib::ClipperBase::Execute' declared here: type mismatch at 3rd parameter ('Clipper2Lib::Paths64 &' (aka 'vector<vector<Point<long>>> &') vs 'Clipper2Lib::PolyTreeD &' (aka 'PolyPath<double> &'))
                virtual bool Execute(ClipType clip_type,
                             ^
/home/damian/projects/checks/Clipper2/CPP/Clipper2Lib/clipper.engine.h:259:16: note: hidden overloaded virtual function 'Clipper2Lib::ClipperBase::Execute' declared here: type mismatch at 3rd parameter ('Clipper2Lib::PolyTree64 &' (aka 'PolyPath<long> &') vs 'Clipper2Lib::PolyTreeD &' (aka 'PolyPath<double> &'))
                virtual bool Execute(ClipType clip_type,
                             ^
3 errors generated.

This is a little bit difficult to solve correctly as what I think needs to be done is to template ClipperBase.

The other approach is to ignore the issue for now using the following approach:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Woverloaded-virtual"

code....

#pragma clang diagnostic pop

There are a number of other issues that clang finds.

Problems with recent Delphi code changes

I've just discovered several problems with the recent (major) changes to the Delphi code that relate to merging solution polygons.
These will be fixed ASAP but until further notice I'd recommend holding off downloading any commits later than 25 March (if you intend to use the Delphi code).

Offset of Line does not work as expected.

A couple of test cases.

A line with a positive offset:
offset_line_test_positive

A line with a negative offset:
offset_line_test_negative

Some observations:

  • I was expecting the line to be on one side or the other based on the offset being positive or negative.
  • The line continues round the end point of the line being offset.
  • I'm not sure if the offset line should continue beyound the end points of the line being offset.
  • note the little tick on the left side at the top. I'm seeing a similar artifact on inflate.

What I'm looking for is to be able to offset a line on one side or the other based on the offset being positive or negative.

The test code is as follows:

void testLineOffsetsPositive()
{
  using namespace Clipper2Lib;
  SvgWriter svg(FillRule::EvenOdd);
  svg.Clear();

  Clipper2Lib::PathD linePath;
  linePath.emplace_back(PointD(0, 0));
  linePath.emplace_back(PointD(0, 1000));
  linePath.emplace_back(PointD(1000, 1000));
  linePath.emplace_back(PointD(1000, 0));
  linePath.emplace_back(PointD(2500, 0));
  linePath.emplace_back(PointD(2000, 3000));

  // center line
  svg.AddPath(linePath, true, 0x00000000, 0xffff00ff, 0.8, false);

  ClipperOffset co;
  co.AddPath(linePath, JoinType::Square, EndType::Square);
  const auto leftLine = co.Execute(1000);
  svg.AddPaths(leftLine, true, 0x00000000, 0xffff0050, 0.8, false);

  SvgSaveToFile(svg, "offset_line_test_positive.svg", 300, 300, 0);
}

void testLineOffsetsNegative()
{
  using namespace Clipper2Lib;
  SvgWriter svg(FillRule::EvenOdd);
  svg.Clear();

  Clipper2Lib::PathD linePath;
  linePath.emplace_back(PointD(0, 0));
  linePath.emplace_back(PointD(0, 1000));
  linePath.emplace_back(PointD(1000, 1000));
  linePath.emplace_back(PointD(1000, 0));
  linePath.emplace_back(PointD(2500, 0));
  linePath.emplace_back(PointD(2000, 3000));

  // center line
  svg.AddPath(linePath, true, 0x00000000, 0xffff00ff, 0.8, false);

  ClipperOffset co;
  co.AddPath(linePath, JoinType::Square, EndType::Square);
  const auto rightLine = co.Execute(-1000);
  svg.AddPaths(rightLine, true, 0x00000000, 0xff00ff50, 0.8, false);
  SvgSaveToFile(svg, "offset_line_test_negative.svg", 300, 300, 0);
}

Changing to EndType::Butt I get the following:
offset_line_test_negative

Changing to EndType::Round or EndType::Joined I get:
offset_line_test_positive

Changing to EndType::Polygon I get:
offset_line_test_positive
This one does not look correct.

Note that the above three images show that the generated offset line has an open point on the top right which seems odd.

The clipping code and polygon union is exceptionally fast and accurate ๐Ÿ‘

Clipper Offset yields no result when downsizing this test case.

I have a comparison test here that is simplified from a real world use case where I run into trouble with Clipper 2 compared to Clipper 1

namespace ClipperLibTest;

using Path64 = List<ClipperLib2.Point64>;
using Paths64 = List<List<ClipperLib2.Point64>>;
using Path = List<ClipperLib1.IntPoint>;
using Paths = List<List<ClipperLib1.IntPoint>>;

public static class OffsetTest
{
    private static Path64 test = new()
    {
        new (0, 0),
        new (0, 8),
        new (4, 8),
        new (4, 4),
        new (16, 4),
        new (16, 16),
        new (4, 16),
        new (4, 10),
        new (0, 10),
        new (0,20),
        new (20,20),
        new (20,0)

    };
    
    public static void compare()
    {
        Path test1 = new();
        for (int pt = 0; pt < test.Count; pt++)
        {
            test1.Add(new ClipperLib1.IntPoint(test[pt].X, test[pt].Y));
        }
        ClipperLib1.ClipperOffset co1 = new();
        co1.AddPath(test1, ClipperLib1.JoinType.jtMiter, ClipperLib1.EndType.etClosedPolygon);
        Paths c1up = new();
        co1.Execute(ref c1up, 2.0);
        co1.Clear();
        co1.AddPaths(c1up, ClipperLib1.JoinType.jtMiter, ClipperLib1.EndType.etClosedPolygon);
        Paths c1down = new();
        co1.Execute(ref c1down, -2.0);
        
        ClipperLib2.ClipperOffset co2 = new();
        co2.AddPath(test, ClipperLib2.JoinType.Miter, ClipperLib2.EndType.Polygon);
        Paths64 c2up = ClipperLib2.ClipperFunc.Paths64(co2.Execute(2.0));
        co2.Clear();
        co2.AddPaths(c2up, ClipperLib2.JoinType.Miter, ClipperLib2.EndType.Polygon);
        Paths64 c2down = ClipperLib2.ClipperFunc.Paths64(co2.Execute(-2.0));
    }
}

`using_polytree` is not reset

using_polytree remains true after Execute was called with a PolyTree. So subsequent Executes without PolyTree run with using_polytree = true.

Getting a crash on the BuildTree function

I'm now getting a crash on the BuildTree function. It's at the following line at the end of the function:

outrec->polypath = owner_polypath->AddChild(path);

owner_polypath seems to be invalid.

Error shows up in the example I previously shared in #27

Little error in GetBounds() ?

On line 3365 of clipper.engine.cpp, you have:
if (pt.x < result.left) result.left = pt.x;
else if (pt.x > result.right) result.right = pt.x;

If the very first vertex is also the right-most one, that "else" prevents result.right from being updated, and the final bounding box is incorrect. Same thing for result.bottom, two lines below.

(caught that little error through my C rewrite producing slightly different results)

Clipping square and line by square causes crash

This code will cause a crash at line 1511 of clipper.engine.cpp because op_front is nullptr.

Clipper64 clipper;

Paths64 squares0;
Path64 square0 = MakePath( "6, 8, 4, 8, 4, 6, 6, 6" );
squares0.push_back( square0 );
clipper.AddClip( squares0 );

Paths64 squares1;
Path64 square1 = MakePath( "5, 5, 7, 5, 7, 7, 5, 7" );
squares1.push_back( square1 );
clipper.AddSubject( squares1 );

Paths64 lines;
Path64 line = MakePath( "5, 7, 5, 5" );
lines.push_back( line );
clipper.AddOpenSubject( lines );

PolyTree64 clippedPolygons;
Paths64 clippedLines;
clipper.Execute( ClipType::Difference, FillRule::EvenOdd, clippedPolygons, clippedLines );

Clipper 2 returns the clip shape rather than the difference in this test case

Clipper 1 seems to handle this correctly, but Clipper 2 appears to return iPoly as the result of the difference

namespace ClipperLibTest;

using Paths64 = List<List<ClipperLib2.Point64>>;
using Paths = List<List<ClipperLib1.IntPoint>>;

public class STest
{
    public static void compare()
    {
        List<ClipperLib2.Point64> BP = new()
        {
            new(1000, 27000),
            new(1000, 2000),
            new(27000, 2000),
            new(27000, 27000),
            new(1000, 27000)
        };

        Paths64 iPoly = new()
        {
            new()
            {
                new(1000, 2000),
                new(1000, 7000),
                new(6000, 7000),
                new(6000, 14000),
                new(1000, 14000),
                new(1000, 27000),
                new(27000, 27000),
                new(27000, 23000),
                new(16000, 23000),
                new(16000, 16000),
                new(27000, 16000),
                new(27000, 2000),
            }
        };

        List<ClipperLib1.IntPoint> BP1 = new();
        for (int pt = 0; pt < BP.Count; pt++)
        {
            BP1.Add(new (BP[pt].X, BP[pt].Y));
        }

        Paths iPoly1 = new();
        for (int p = 0; p < iPoly.Count; p++)
        {
            List<ClipperLib1.IntPoint> t = new();
            for (int pt = 0; pt < iPoly[p].Count; pt++)
            {
                t.Add(new (iPoly[p][pt].X, iPoly[p][pt].Y));
            }
            iPoly1.Add(t);
        }
        
        ClipperLib1.Clipper c1 = new() {PreserveCollinear = false};
        c1.AddPath(BP1, ClipperLib1.PolyType.ptSubject, true);
        c1.AddPaths(iPoly1, ClipperLib1.PolyType.ptClip, true);

        Paths o1 = new();
        c1.Execute(ClipperLib1.ClipType.ctDifference, o1);
        
        ClipperLib2.Clipper c2 = new() {PreserveCollinear = false};
        c2.AddSubject(BP);
        c2.AddClip(iPoly);

        Paths64 o2 = new();
        c2.Execute(ClipperLib2.ClipType.Difference, ClipperLib2.FillRule.EvenOdd, o2);

    }
}

Minkowski

hi Angus!
please port MinkowskiSum and MinkowskiDiff from clipper1

Inflate removing precision from Double

I'm using InflatePaths on a triangle, and using the PathsD format doesn't work properly. Using Paths64 and upscaling the polygon gives the expected result, a bigger triangle with 'rounded edges'

PathsD polyD = new PathsD();
PathsD resD;
Paths64 poly64 = new Paths64();
Paths64 res64;

polyD.Add(ClipperFunc.MakePath(new double[] { 218.57, 500, 618.57, 500, 218.57, 800 }));
resD = ClipperFunc.InflatePaths(polyD, 5, JoinType.Round, EndType.Joined);

poly64.Add(ClipperFunc.MakePath(new int[] { 2185700, 5000000, 6185700, 5000000, 2185700, 8000000 }));
res64 = ClipperFunc.InflatePaths(poly64, 50000, JoinType.Round, EndType.Joined);

resD:

res64:

The result using double is snapped into integer values, so I'm guessing this is caused by converting PathsD into Paths64 inside the InflatePaths function

Wrong orientation if `DEFAULT_ORIENTATION_IS_REVERSED` is false

If DEFAULT_ORIENTATION_IS_REVERSED is set to false, the union of a single positive path with positive fill rule is negative and the union of a single negative path with negative fill rule is negative. Both results should be positive paths.

No issues if DEFAULT_ORIENTATION_IS_REVERSED is true.

Delphi: not used items

D:\Delphi\Clipper2\Delphi\Clipper2Lib\Clipper.Engine.pas(356,3) Hint: (5028) Local const "rsClipper_OpenPathErr" is not used
D:\Delphi\Clipper2\Delphi\Clipper2Lib\Clipper.Offset.pas(450,3) Note: (5025) Local variable "scale" not used
D:\Delphi\Clipper2\Delphi\Clipper2Lib\Clipper.pas(288,3) Note: (5025) Local variable "co" not used
D:\Delphi\Clipper2\Delphi\Clipper2Lib\Clipper.pas(18,3) Hint: (5023) Unit "Math" not used in Clipper
D:\Delphi\Clipper2\Delphi\Clipper2Lib\Clipper.Minkowski.pas(17,42) Hint: (5023) Unit "Clipper.Engine" not used in Clipper.Minkowski

IsClockwise and Orientation

So unless REVERSE_ORIENTATION is defined, Clipper2 paths' orientation is opposite compared to Clipper1. In Clipper1 paths with mathematical counter-clockwise orientation are positive and paths with mathematical clockwise orientation are negative. This is the behavior I want to have, and therefore I define REVERSE_ORIENTATION. To me clockwise/counter-clockwise does not depend on the orientation of the y-axis of the display. So I expect IsClockwise to return false for positively oriented paths, which are mathematically counter-clockwise. Of course on a y-axis down display it looks clockwise to the observer. So it depends on how you look at it. I like the Orientation function of Clipper1 better, because the name makes sense regardless of how you look at it.

remove aliases in c# code

consider using classes instead of aliases.

this

  using Path64 = List<Point64>;
  using PathD = List<PointD>;
  using Paths64 = List<List<Point64>>;
  using PathsD = List<List<PointD>>;

replaced with this, no more need to define aliases in every file

public class Path64 : List<Point64>
   {
       public Path64()
       {

       }

       public Path64(int capacity) : base(capacity)
       {
       }
   }

   public class Paths64 : List<Path64>
   {
       public Paths64()
       {
       }

       public Paths64(int capacity) : base(capacity)
       {
       }
   }

   public class PathD : List<PointD>
   {
       public PathD()
       {
       }

       public PathD(int capacity) : base(capacity)
       {
       }
   }

   public class PathsD : List<PathD>
   {
       public PathsD()
       {
       }

       public PathsD(int capacity) : base(capacity)
       {
       }
   }

Distance function is malformed

As a template, the Distance function won't compile if used. It should be this instead:

template <typename T>
inline double Distance(const Point<T> pt1, const Point<T> pt2)
{
	return std::sqrt(DistanceSqr(pt1, pt2));
}

By the way, I've written my own line-length functions, shown here (and I hope they're correct). Would these be a nice addition to Clipper2?

double MyLength( const Path64& path )
{
	double length = 0.0;
	Point64 point;

	for ( auto it = path.begin();  it != path.end();  ++it ) {
		if ( it != path.begin() )
			length += Distance( point, *it );
		point = *it;
	}

	return length;
}

double MyLength( const Paths64& paths )
{
	double length = 0.0;

	for ( auto it = paths.begin();  it != paths.end();  ++it )
		length += MyLength( *it );

	return length;
}

Polygon Union error

When performing a union of overlapping polygons some are being "xor'd" see picture.
PolygonError1

Any simple way to triangulate clipping results?

It seems that structures built internally during clipping solution generation already contain most of the information needed to perform triangulation, but that is lost when polytree is built. Is there some simple way to generate triangulation that I'm missing? If not, is such a feature planned?

Basic build-and-test CI pipeline

Dear Angus,

Thank you for your amazing library. I'm a long-time user of Clipper1, and generally very happy with it. However, will certainly welcome the speed and other improvements that may come as part of Clipper2.

I'm really on C++ only. Is there anything I can help with?

In particular, I would propose that I could create CI tests that would automatically be run by GitHub Actions (or similar) for each and every new commit (and/or pull request).

I'll need to test any new versions on my side anyway. So I thought I would happily contribute the necessary bits upstream, so that you and other contributors could get feedback as quickly as possible, hopefully making your job easier. Having a framework ready should also encourage adding more tests for specific corner cases and whatnot.

I would personally add it for C++ only (at least initially), but I'd leave room for doing something similar for Delphi and C#.

Here is an example from another library I've been working with. So it could look more or less like this: https://github.com/reunanen/dlib/actions

If any thoughts, please let me know. I can also just post a proposal (as a pull request), and then we can take it from there.

Or if there's already something like that working or under construction, and I have missed it, please let me know.


On a related note, I noticed that you wrote:

Things like Makefiles are beyond me and I'm hope others will contribute so Clipper2 C++ will more easily compile in environments other than MS Visual Studio.

I'm regularly compiling Clipper1 on Linux (gcc), and all is good. When adopting Clipper2, I'll definitely make sure that is still the case. Since it's just a couple of .cpp files, I think non-Windows users should not require any special treatment, as long as the code itself will compile. In order to make sure that that continues to be the case, I could add Linux (and maybe even MacOS) jobs to the CI pipelines, if you want. But I could well start with Visual Studio, so it'll be more accessible to you.

Wrong orientation for clipped square

With all the discussion about orientation I'm not sure I have this right, but what used to work for me no longer works. Consider this code:

Clipper64 clipper;

Paths64 squares0;
Path64 square0 = MakePath( "4, 6, 4, 8, 6, 8, 6, 6" );
squares0.push_back( square0 );
clipper.AddClip( squares0 );

ASSERT( Area( square0 ) > 0.0 );
ASSERT( Area( squares0 ) > 0.0 );

Paths64 squares1;
Path64 square1 = MakePath( "7, 5, 5, 5, 5, 7, 7, 7" );
squares1.push_back( square1 );
clipper.AddSubject( squares1 );

ASSERT( Area( square1 ) > 0.0 );
ASSERT( Area( squares1 ) > 0.0 );

PolyTree64 clippedPolygons;
Paths64 clippedLines;
clipper.Execute( ClipType::Difference, FillRule::EvenOdd, clippedPolygons, clippedLines );

ASSERT( MyArea( clippedPolygons ) < 0.0 );
ASSERT( clippedPolygons.polygon.size() == 0 );

All ASSERTS execute without error, but the last two appear to be wrong, and would have failed in the previous release. The result seems to be inside-out. By the way, I'm using my own area function for polytrees; wouldn't this be a nice addition to Clipper2?

double MyArea( const PolyTree64& polytree )
{
	double area = Area( polytree.polygon );

	for ( auto it = polytree.childs.begin();  it != polytree.childs.end();  ++it )
		area += MyArea( **it );

	return area;
}

Single path annular structures

In my use of Clipper, I routinely encounter annular structures (i.e., a circle within a circle, or a circle with a hole). Using these in Clipper1 works fine regardless of how they are defined. It doesn't seem to matter if do a "SimplifyPolygon" before doing a Union or Difference operation. However, I am having issues trying to process these annular structures in Clipper2.

If I use 2 unconnected concentric circles, with the inner circle oriented opposite the outer circle, Clipper2 seems to work fine. However, most of my annular structures are defined as a single path with the inner circle connected to the outer circle with a radial edge (actually 2 coincident edges, one pointing outward and one pointing inward). If I try to use such an annular structure in Clipper2, it does not work at all. Am I missing something? I don't see a "SimplifyPolygon" routine as you had in Clipper1 or I would try that.

FYI, I use Lazarus/FreePascal.

Calling `ClipperOffset::Execute` multiple times produces incorrect results

Clipper2Lib::Paths64 paths;
Clipper2Lib::ClipperOffset co;

co.AddPath(Clipper2Lib::MakePath("0,0, 10,0, 10,10, 0,10"), Clipper2Lib::JoinType::Miter, Clipper2Lib::EndType::Polygon);

paths = co.Execute(+1);
paths = co.Execute(-1);

for (auto &path : paths)
{
    for (auto &point : path)
    {
        std::cout << point << "  ";
    }

    std::cout << std::endl;
}

Output:

11,-1  -1,-1  -1,11  11,11  
9,9  10,9  10,10  9,10  
0,9  1,9  1,10  0,10  
9,0  10,0  10,1  9,1  
0,0  1,0  1,1  0,1

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.