Comments (14)
Thanks for your help. If that can make its way into the next general release that would be great.
It might be worth taking a look at whether some types within the library would be more efficient being structs; things like polygon edges or horizontal segments. There is a high cost for allocations on iOS and Android and we've made huge performance improvements in our app by keeping cached buffers around and limiting the amount of class instantiations, simply by giving the GC less to do.
from clipper2.
Sorry, I don't have iOS to test this.
And while it's very unlikely to be the cause, could the compiler be misbehaving while parsing your very long path statement?
Wouldn't it be better wrapping it over a number of lines?
public static void Test()
{
PathsD subject = new();
subject.Add(Clipper.MakePath(new double[] {
-8470.4501953125, -42813.28125, -8460.7470703125, -42814.30078125, -8440.5146484375, -42820.4609375,
-8420.0205078125, -42825.6875, -8399.310546875, -42829.984375, -8378.4248046875, -42833.3359375,
-8357.4091796875, -42835.73046875, -8336.3046875, -42837.17578125, -8315.15625, -42837.65234375,
-8294.0087890625, -42837.17578125, -8272.904296875, -42835.73046875, -8251.8876953125, -42833.3359375,
-8231.001953125, -42829.984375, -8210.2919921875, -42825.6875, -8189.798828125, -42820.4609375,
-8169.56591796875, -42814.30078125, -8149.63525390625, -42807.234375, -8130.0478515625, -42799.26953125,
-8110.84375, -42790.42578125, -8092.0634765625, -42780.71484375, -8073.74462890625, -42770.1640625,
-8055.92626953125, -42758.80078125, -8038.64501953125, -42746.63671875, -8021.93505859375, -42733.69921875,
-8005.83203125, -42720.02734375, -7990.3681640625, -42705.63671875, -7975.576171875, -42690.55859375,
-7961.4853515625, -42674.8359375, -7948.12451171875, -42658.48828125, -7935.52099609375, -42641.5546875,
-7923.7001953125, -42624.0703125, -7912.68603515625, -42606.078125, -7902.5, -42587.6015625,
-7893.16357421875, -42568.69140625, -7884.6953125, -42549.3828125, -7877.11181640625, -42529.70703125,
-7870.42822265625, -42509.71875, -7864.65673828125, -42489.45703125, -7859.8095703125, -42468.94921875,
-7855.8955078125, -42448.2578125, -7852.92236328125, -42427.41015625, -7850.8955078125, -42406.453125,
-7849.81884765625, -42385.43359375, -7849.0029296875, -42371.94921875, -7849.0029296875, -42012.6640625,
-7849.0029296875, -41318.671875, -8224.318359375, -40734.03125, -8492.20703125, -40526.49609375,
-8609.970703125, -40434.828125, -8811.7705078125, -40277.3125, -9790.8564453125, -40277.3125,
-9026.5986328125, -40790.921875, -9027.2783203125, -40791.44140625, -9029.970703125, -40792.84765625,
-9027.005859375, -40794.21875, -9027.822265625, -40795.77734375, -9026.9111328125, -40796.8046875,
-9028.0400390625, -40796.8359375, -9024.7216796875, -40798.8203125, -9025.673828125, -40802.96875,
-9022.1513671875, -40804.66796875, -9025.197265625, -40807.984375, -9025.224609375, -40809.875,
-9017.21484375, -40822.0625, -9017.9501953125, -40826.51953125, -9014.9580078125, -40830.203125,
-9013.326171875, -40836.69921875, -9009.1923828125, -40838.5390625, -9009.369140625, -40841.125,
-9006.486328125, -40842.79296875, -9006.5673828125, -40846.52734375, -9001.658203125, -40853.26171875,
-9047.294921875, -41015.08984375, -9048.568359375, -41014.8203125, -9066.6923828125, -41005.09765625,
-9085.611328125, -40997.03125, -9105.1748046875, -40990.69140625, -9125.2265625, -40986.125,
-9145.6025390625, -40983.375, -9166.14453125, -40982.453125, -9186.6865234375, -40983.375,
-9207.0634765625, -40986.125, -9227.1142578125, -40990.69140625, -9246.677734375, -40997.03125,
-9265.59765625, -41005.09765625, -9283.7216796875, -41014.8203125, -9300.9052734375, -41026.125,
-9317.009765625, -41038.9296875, -9331.9091796875, -41053.12109375, -9345.478515625, -41068.59375,
-9357.611328125, -41085.22265625, -9368.2099609375, -41102.87109375, -9377.1875, -41121.40234375,
-9384.470703125, -41140.671875, -9390, -41160.51171875, -9393.73046875, -41180.78125,
-9395.6298828125, -41201.30078125, -9395.6826171875, -41221.91796875, -9393.88671875, -41242.45703125,
-9390.255859375, -41262.76171875, -9388.34375, -41269.80859375, -9254.806640625, -41225.50390625,
-9246.5263671875, -41223.62109375, -9230.2265625, -41220.1015625, -9213.6455078125, -41218.3359375,
-9196.970703125, -41218.3359375, -9180.3896484375, -41220.1015625, -9164.08984375, -41223.62109375,
-9148.2548828125, -41228.8515625, -9133.0615234375, -41235.73046875, -9118.6845703125, -41244.1875,
-9105.2841796875, -41254.12109375, -9093.8017578125, -41264.2265625, -9093.013671875, -41265.421875,
-9082.009765625, -41277.9609375, -9072.3984375, -41291.60546875, -9064.2890625, -41306.1953125,
-9061.708984375, -41311.69140625, -8998.6123046875, -41463.6875, -8992.357421875, -41478.72265625,
-8900.16015625, -41700.05078125, -8731.5400390625, -41968.6875, -8534.0908203125, -42289.3125,
-8437.814453125, -42444.76171875, -8470.4501953125, -42813.28125
}));
PathsD solution = Clipper.InflatePaths(subject, 0.5, Clipper2Lib.JoinType.Round, Clipper2Lib.EndType.Polygon);
SvgWriter svg = new SvgWriter();
SvgUtils.AddSubject(svg, subject);
SvgUtils.AddSolution(svg, solution, false);
string filename = @"..\..\..\test.svg";
SvgUtils.SaveToFile(svg, filename, FillRule.NonZero, 400, 400, 10);
ClipperFileIO.OpenFileWithDefaultApp(filename);
}
from clipper2.
It's nothing to do with the long statement; I just made that up for the repro. In our actual code, the polygons are not created that way (it's an entity on a map). I was hoping that because you know what's going on inside ClipperBase.ExecuteInternal (and everything called by it) you might recognize something that could cause the runtime to fail so spectacularly.
from clipper2.
I was hoping that because you know what's going on inside ClipperBase.ExecuteInternal (and everything called by it) you might recognize something
LOL 🤣.
There is just about the entire clipping algorithm encapsulated there, so it could be just about anywhere.
I can't debug this myself, since I don't have any Apple Inc products to debug this.
Anyhow, I'm surprised you can't localise the problem by viewing a callstack.
Assuming you don't have a callstack, I suggest the following (that will require a little patience 😜):
- Within ExecuteInternal there's a loop where you can set debugging breakpoint neat the top.
- Set it to break when Y equals roughly the middle Y in your sample path.
- Manually (binary) adjust that Y value up or down to quickly determine the Y where the crash occurs.
- Step over each function call within ExecuteInternal until the crash occurs.
Recursively repeat step 4 inside each problematic function.
from clipper2.
Yeah, that makes sense. There's no callstack because the entire managed runtime seems to crash. Breakpoints sometimes work, they sometimes don't. I spent an hour yesterday narrowing options down, then stepping through ExecuteInternal. I traced the crash reliably, ascertaining it happened when stepping over a call to ConvertHorzSegsToJoins. Then I stepped into that method, and it crashed inside _horzSegList.Sort(new HorzSegSorter());. Then I stepped into that method, and stepped through it, and everything worked, the function returned successfully.
I do wonder if the number of allocations made during these options is causing a problem. I've never spent much time looking at Clipper code, but having tried to do lots of debugging lately, there are a LOT of allocations that could perhaps be avoided with some pooling/caching. It might not be a problem under most environments but mobile (iOS/Android) is where allocations really start to cause problems.
I've opened an issue against the .net runtime itself because this sort of random crashing behaviour suggests that something is simply making the runtime unstable.
from clipper2.
it happened when stepping over a call to ConvertHorzSegsToJoins.
Unfortunately ConvertHorzSegsToJoins
is truely ugly. Even I have to work hard to follow what I'm doing there.
And that code is non-essential, except that library users seem to prefer that polygons are in their simplest forms (ie avoiding polygons that share touching collinear edges).
I do wonder if the number of allocations made during these options is causing a problem.
I seriously doubt that the problem is due to insufficient memory if the code sample above is crashing. I guess it's possible you're somehow encountering an endless loop but that should be picked up doing the steps outlined above.
from clipper2.
I wonder if mono is using std::sort from libc++ directly (libc++ is the C++ implementation of STL that is used on MacOS by default).
Because libc++'s sort is very intolerant to incorrect (non-strict-weak) sorting predicates, it is the main cause of crashing std::sort in C++.
But I am not sure, problem with bad predicate in this project would have been noticed long ago, feels too obvious.
from clipper2.
Are you forced to use Mono? I had no end of trouble with it on macOS and that all went away when I moved to the Microsoft dotnet packages - stuff that would randomly or persistently fail only under mono and worked perfectly under dotnet proper. The mono developers never showed much interest in resolving them, so I would not be entirely surprised if this is coming from mono itself.
from clipper2.
I share your thoughts on Mono, however this is happening using the latest and greatest net8.0-ios and net7.0-ios runtimes. I've tested both.
from clipper2.
I suggest you try (temporarily) disabling some of this clipping code since it appears to be happening in non-essential routines. Doing this can help localise where it's crashing.
For example:
- in
ClipperBase::ConvertHorzSegsToJoins()
, place a return statement just above thestd::stable_sort()
call. - See your test samples still crash.
If that stops the crashing, then repeat the above steps, except to place the return statement just below the sort function call.
from clipper2.
Thanks Angus. Not being familiar with the internal workings of the library, I didn't realise that this function was non-essential.
Returning the ConvertHorzSegsToJoins function just before the sort appears to solve the issue.
Returning the ConvertHorzSegsToJoins function just after the sort and we still get the crash.
Having just run the test above again, I've confirmed the results, and preventing the HorzSegSorter dedicated type and its function from being executed definitely appears to make the runtime issue go away.
As an experiment, I removed the HorzSegSorter type altogether (there's no need to a distinct type here) and instead just passed a new HorzSegSorter method (in ClipperBase) to the Sort function instead. This also seems to work, and I didn't change the method contents:
`_horzSegList.Sort(HorzSegSorter);
private int HorzSegSorter(HorzSegment hs1, HorzSegment hs2)
{
if (hs1 == null || hs2 == null) return 0;
if (hs1.rightOp == null)
{
return hs2.rightOp == null ? 0 : 1;
}
else if (hs2.rightOp == null)
return -1;
else
return hs1.leftOp!.pt.X.CompareTo(hs2.leftOp!.pt.X);
}`
So there's some progress, anyway. I'm not really comfortable shipping like this, but perhaps you could take a look at it. Maybe the runtime didn't like the fact that a dedicated struct type was there only to serve as a comparer (it is pretty unusual, I would expect it to be a class).
from clipper2.
Returning the ConvertHorzSegsToJoins function just before the sort appears to solve the issue.
Returning the ConvertHorzSegsToJoins function just after the sort and we still get the crash.
OK! Progress indeed!!
Evidently the List.Sort() routine that calls this function is implemented differently in the iOS C# compiler.
And yes, making the sort function a private method of ClipperBase and passing it as the parameter of _horzSegList List.Sort() seems to be the way to go. (I only implemented it the other way because of my relative inexperience in C# programming.)
private int HorzSegSort(HorzSegment hs1, HorzSegment hs2)
{
if (hs1 == null || hs2 == null) return 0;
if (hs1.rightOp == null)
{
return hs2.rightOp == null ? 0 : 1;
}
else if (hs2.rightOp == null)
return -1;
else
return hs1.leftOp!.pt.X.CompareTo(hs2.leftOp!.pt.X);
}
_horzSegList.Sort(HorzSegSort);
from clipper2.
It might be worth taking a look at whether some types within the library would be more efficient being structs; things like polygon edges or horizontal segments.
Unfortunately these have to be classes because they may be null
referenced, and my understanding of C# is that structs must always point to assigned memory.
from clipper2.
That's kind of true, but Nullable<T>
was invented to solve exactly this problem.
from clipper2.
Related Issues (20)
- Regression when removing type casting in IsCollinear() HOT 4
- Minkowski in export header HOT 4
- Clipper2 slower than clipper1 HOT 2
- New release? HOT 1
- can this library cut a hole in a polygon?
- How to handle path value of double type HOT 1
- [new API request] API using PolyTree for RectClip HOT 1
- Dose Clipper support to get Inscribed rectangle ?
- How Clipper2 is used in iOS project(Cocoaspod)
- Union returns self-intersecting polygon HOT 1
- Why is Rect empty when bottom <= top?
- Union of forms that touch but don't intersect
- Union of forms that share an edge
- Empty polytree when taking the union of 3 closed polygons. HOT 6
- Is there a plan for the clipper to support circular arc structures
- `RectClip` not always preserving orientation HOT 3
- Range error in TClipperOffset.BuildNormals (Delphi) HOT 1
- update vcpkg & docs HOT 1
- Negative offset should be empty but is not HOT 4
- Handling Large Shrinking Deltas for Polygons
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from clipper2.