Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Subtract operation results in open path #968

Closed
iconexperience opened this issue Feb 14, 2016 · 21 comments · Fixed by #971
Closed

Subtract operation results in open path #968

iconexperience opened this issue Feb 14, 2016 · 21 comments · Fixed by #971

Comments

@iconexperience
Copy link
Contributor

It looks like 3348fb7 has introduced a regression. The following rather simple case now throws a Boolean operation resulted in open path message.

var p1 = new paper.Path({segments:[
    [352, 280, 0, -26.5, 0, 0],
    [352, 356, 0, 0, 0, 2.1999999999999886],
    [348, 360, 2.1999999999999886, 0, -72, 0]
    ], closed:true});
var p2 = new paper.Path({segments:[
   [352, 344, 0, 0, 0, 0],
   [352, 356, 0, 0, 0, 2.1999999999999886], 
   [348, 360, 2.1999999999999886, 0, 0, 0], 
   [232, 360, 0, 0, -2.1999999999999886, 0]
   ], closed:true});

p1.strokeColor = "green";
p2.strokeColor = "red";

var res = p1.subtract(p2);

res.fullySelected = true;

The result actually looks fine, but still the error message indicated that something is wrong.

@lehni
Copy link
Member

lehni commented Feb 14, 2016

This will just never end, will it : /

@iconexperience
Copy link
Contributor Author

Yes, it's getting a bit frustrating. Everything is working so well, but then a new case appears that fails. I always have the feeling that we are really close, but I had this already a few months ago...

@lehni
Copy link
Member

lehni commented Feb 14, 2016

Exactly. And changes become tough. I spent almost the whole of yesterday on that transition...

@iconexperience
Copy link
Contributor Author

Actually, I can live with this issue (not really causing problems), 953 #964 (a real edge case) and #904 (more a theoretical issue than a real world problem) unfixed.

We want to make it perfect, but maybe that is not possible? Anyway, I would still like to make this as stable as possible and whenever I have time I will continue working on these issues.

@lehni
Copy link
Member

lehni commented Feb 14, 2016

You mean #964? That one we should really fix... I think something strange is going on in isCrossings() there still.

@iconexperience
Copy link
Contributor Author

Yes, sorry.

@iconexperience
Copy link
Contributor Author

From the first glance it seems that with the new code you are now doing the check for identical paths for each child in a compound path. If so, there must be a small error in the new implementation, because my example does not even have a compound path. But then again I may be completely wrong with my interpretation of your changes.

@lehni
Copy link
Member

lehni commented Feb 14, 2016

There were some other necessary changes, and I actually solved a few undetected edge cases for which the unit tests where using wrong data for the results. I'll look into it.

@lehni
Copy link
Member

lehni commented Feb 14, 2016

First of all, it doesn't actually fail, it just reports that it had to abandon an open path, that's not a problem by itself.The reason why it now produces this error is that all the valid segments of the red path are overlaps, and we have to handle this case specially: Normally we don't allow a path to start in an overlap, but if the only valid segments are overlaps, we have to start in one. Which brings us to the more confusing part:

The winding calculations produce 1 for two of the overlapping segments that are not part of the solution, and that is wrong. I am not sure why yet. Here the output of the debug-boolean code. The segments in question are the red 5.1 and 5.2, which both have a wi: 1(winding), while it should really be 0.

@iconexperience would you want to help out with debugging this one?

screen shot 2016-02-14 at 12 55 02

@iconexperience
Copy link
Contributor Author

Yes. So you mention wrong winding calculation. That sounds pretty much like my part to investigate.

@lehni
Copy link
Member

lehni commented Feb 14, 2016

Cool : ) Note that we handle winding for subtraction operations separately in propagateWinding()

@iconexperience
Copy link
Contributor Author

Do you mean the getWinding() calculation is wrong? If I remove my isOnCurve check in getWinding() this test case does not fail anymore. This makes sure that the winding is always at least 1 if the point is on a curve. Is it doing the wrong thing here?

@lehni
Copy link
Member

lehni commented Feb 14, 2016

Well, since these are overlaps, they are on two curves. And I believe propagateWinding() should handle that... For subtraction, the result then should become 0. But maybe, the isOnCurvecode only works correctly for one of the two paths? But you need to look into what exactly it's doing, I haven't written that part of the code...

@iconexperience
Copy link
Contributor Author

Well, but I have written that part of the code.

Maybe the current implementation of getWinding() is still flawed. If there are several paths, we should probably calculate the winding for each path (not a problem), but we should also detect the orientation of the path (a bit tricky). If the point is on a curve but winding for the path is zero, we will add +1/-1 or -1/+1 to the leftWind/rightWind, depending on the path's orientation.

Detecting the path's orientation is quite simple. We use the winding of the lefmost non-horizontal curve that is crossed by the horizontal ray. If that curve's winding is negative (going up), the path is clockwise, if it is positive, the path is counter-clockwise.

I will give it a try.

@iconexperience
Copy link
Contributor Author

This is complicated and I am glad you have some many unit tests.

So this is my algorithm that seems to work:

We calculate the windings as we did before. But for each loop we check if we are on a curve. At the end of a loop we add +1/-1 or -1/+1 to new leftWindOnCurve and rightWindOnCurve variables, depending on the path's orientation. We get the orientation has explained in my comment above.

After running through all loops, we check if windLeft and windRight are zero. If so, we use the windLeftOnCurve/windRightOnCurve values. These can be zero if a point is on two paths with opposite directions.

@iconexperience
Copy link
Contributor Author

What worries me a little bit is handling the cases for horizontal == true. We have not worked on that case at all while completely overhauling the cases for horizontal == false. Well, it worked so far...

@lehni
Copy link
Member

lehni commented Feb 14, 2016

Sounds like we need specific unit tests for those cases too...

@iconexperience
Copy link
Contributor Author

Yes, that would be nice. But the overall proplem in some cases is to know what the correct result is. Here is a little discussion about the winding number for points on a path, and it seems we have come further than they did.

http://stackoverflow.com/questions/8452078/integer-winding-number-algorithm-with-edge-cases

@iconexperience
Copy link
Contributor Author

To get a better understanding about the problems that we are facing here, please take a look at these examples.

image

In both cases the correct winding number of the red point is 1. But at the left we have to ignore the touching with the smaller circle, in the right example we have to count it. And this is a very basic example.

@lehni
Copy link
Member

lehni commented Feb 14, 2016

Wow, yeah... How bad is it going to be?

@iconexperience
Copy link
Contributor Author

I was thinking about this all the time and it really makes my head spinning. This is pobably because striktly speaking the points on the path are not defined, but that does not help us. We need a solution.

So I tested my implementation and it looks alright. Not sure if you agree with my code, but I will send the PR in a few minutes.

lehni added a commit that referenced this issue Feb 14, 2016
Fix for #968 - Improve handling of points on paths in getWinding()
bmacnaughton added a commit to bmacnaughton/paper.js that referenced this issue Mar 22, 2016
Commits not documented listed here, starting with 45595b2. They are not included in the changelog because the appeared to be internal or already documented or they were a fix for a bug that was introduced in "develop" branch. There were commits before 45595b2 but that's when I started recording what I didn't put in the changelog.

Not sure whether the curveAt/CurveAtTime functions are deprecated or removed.

Format: commit message, commit ID. In chronological order of commits:

- Fix unit test error on Node.js 45595b2
- Implement unit tests for SVG Importing, based on visual comparison. … a12e99e
- Fix wrongly copied attributes in Item#reduce() … 8e25327
- Events: paper namespace may not be initialized when key evens are emi… … b2f3b58
- SVG: Some renaming omitted in previous commit. 1c4ff31
- SVG: Rename 'SVG' prefix to 'Svg' … af59847
- Implement tests for paperjs#69721dce1a
- Move Path_Bounds tests to Item_Bounds. … a02d724
- Fix accidentally leaked global variable. 74d1889
- Simplify getWindings(), The first curve of a loop always has the 'las… … 0716ebb
- Merge pull request paperjs#938 from iconexperience/getWinding-simplification … 53269ab
- Optimize Emitter._installEvents() … 0f084ea
- JSON: Prevent `name: undefined` exports. 7888d1d
- Change the way we determine the winding in getWinding(). Now the wind… … aed9d05  (paperjs#936)
- Clean-up changes from paperjs#939 41aca10
- Define unit test for #internalBounds regression. 336460b
- Fix internalBounds regression caused by 1ac8e46 0152439
- Revert "Change the way we determine the winding in getWinding()." f7b1aca
- Use correct SVG namespace again. … fc4bdf4
- Replace the "straight curves with zero-winding" test with a more comp… … e03c8cd
- Change the implementation of getWinding() again, so we pass all tests… … 5b31aee
- Merge pull request paperjs#944 from iconexperience/fix-getWinding … ec75985
- Merge pull request paperjs#943 from iconexperience/replace-path-contains-test … 23045bb
- Do not snap curve points to t = 0 / 1 with epsilon … 0371f66 (in boolean ops)
- Clean-up unit test for paperjs#943 and add edge case from paperjs#944a59a535
- Some comment cleanup. ffe42a0
- Merge branch 'winding-fix' into develop … 5a46620
- Some code cleanup for winding-fix. 55909b8
- Include NPM and Bower badges. 80e6246
- Improve handling of view updates and detection of invisible documents. … da216aa
- Fix new exception in unit tests. de9653a
- Rearrange method sequence in Path. d1b11c6
- Clean-up PathFitter code. e5d139c
- No need to pass normalized tangents to PathFitter#fitCubic() … c793538
- Fix JSDoc warning on Style class. a48d138
- Implement PathItem#getNearestLocation() / #getNearestPoint() … 717bc4b
- Implement Item#_hitTestChildren() … 740c94e
- Some CurveLocation cleanup. … 00d2e2a
- Remove duplicate unit tests. ed43477
- SVGExport: Remove unnecessary calls to Point#transform() in exportGra… … adc5b86
- Remove unnecessary double-spaces. 98fc513
- Improve fix for paperjs#650c1b7366
- SVGImport: Further improve handling of gradients … d9e09b9
- Gulp: Add test:browser task, to solve CORS issues on Chrome. 8542eb6
- SVGImport: Improve consistency of style handling. df57c4a
- SVGImport: Inherit default styles on Node.js too. e38a33f
- SvgImport: Always create a clip-item when viewBox is specified. 68c4541
- Tests: Implement additional tests for SvgImport. c0b39c4
- Travis CI: Use Arial in all tests. cb79232
- Shortcut Curve.evaluate() for t === 1 to avoid imprecision. aa1f219 (paperjs#960)
- Part 1 of large refactoring of bounds handling. 55c5f42
- Update straps.js 892e567
- Part 2 of large refactoring of bounds handling. 12f829c
- Travis CI: Switch to g++ 4.8 to see if this solves strange new buildi… … 5ec5c26
- Introduce Base.filter(), to copy and filter object properties. 6d5d1ce
- Implement unit tests for Item#getItems() with overlapping / inside pr… … 80c8aae
- Merge pull request paperjs#962 from iconexperience/fix-issue-960 … 7c24fc9
- Add test for paperjs#960 and improve fix a bit. … e2bc83a
- Remove unnecessary edge-case handling in CurveLocation#isCrossing() … 84a75e3 (paperjs#951, paperjs#959 - but falls into boolean fixes in general).
- Implement consistent checks for fill / stroke / shadow styles in test… … c6bcf43
- Clean-up previous commit. 0a196da
- Boolean: Implement proper handling of fully overlapping (identical) p… … 3348fb7 (paperjs#923, paperjs#958 - but falls into boolean fixes in general).
- Boolean: Only compare segments when determining if paths are identitcal. 009761d (boolean fixes in general).
- Switch from new Base() to Base.set({}) where possible. c3fff9f
- Docs: Fix warning about isFlatEnough() 27197bd
- Better detect code that requires a tool object. … dbd7a90 (in example fixes)
- Matrix: Switch to a better implementation of #decompose() … 3ee46ff
- Implement failing test for paperjs#968 9c9f43d
- Hit-Test: Pass viewMatrix as argument instead of in options object. fa6c1f4
- Update Curve.js … fb76065
- Update Path.js … add2866
- Clean up PR paperjs#93179d4461
- Improve handling of points on paths in getWinding() e2eaf87
- Adjust comments to match new implementation 406e6c9
- Fix regression introduced in 4e7fa2f 55e7689
- Implement more unit tests for PaperScope#settings.insertItems 01fade8
- Merge pull request paperjs#971 from iconexperience/fix-issue-968 … 4c72d98 (boolean fixes in general)
- Simplify code from paperjs#971 and activate unit test for it again. 8d5c922
- Document options.insert in #importSVG() 3c3c8d9
- List all supported events in event methods on View. 9f9222f
- Switch to PathItem.create() in unit tests. 0e2498b
- Fix failing SVG unit test. 08e51b5
- Fix failing unit tests. 3d330da
- SvgImport: Fix issues introduced in 6f4890c 16a7baa
- Merge pull request paperjs#976 from iconexperience/patch-2 … 7f48486
- SVG Export: Do not filter out empty paths. 6975690
- Fix paperjs#977: Apply hit-testing tolerance to fills in Shape. 4081afb
- Fix paperjs#977: Implement unit-tests. 6df4602
- Fix paperjs#982: Make sure `self` points to the global scope on Webpack. b5c837b
- Extend mapping of attribute names to required namespaces a4757b3
- Add trailing slashes to svg related namespaces (xmlns, xlink) 49104c5
- Merge pull request paperjs#984 from aschmi/fix-namespaces-of-exported-svg … 623ec73
- SVG Import: Fix namespacing issues introduced by paperjs#984. acb1e40
- SVG: Add comments explaining IE related changes in paperjs#984. 50bd5be
- Implement unit tests for paperjs#9911cb2916
- fix paperjs#994: Revert commit b5af47a69c3470
- Rename SegmentSelection related internal objects and properties.  … 1db419a
- Simplify Path#getArea()  … 7dd110f
- jsdom v8.0.0 equires Node.js v4.0.0 or newer. 7300260
- Implement Path#splitAt(offset)  … da7d0d8
- Add more unit tests for SvgImport.  … 7a4794d
- SVG Import: Add more tests.  … d52a6f3
- Refactor GradientStop: Improve handling of optionally defined color a…  … d93aca6
- Travis CI: See if using Arial solves the failing test. 17555b1
- Travis CI: Use Arial in all SVG tests and reduce tolerance. d6ce470
- Travis CI: Adjust SVG test tolerances. beabd6b
- Travis CI: More SVG test adjustments. bb19fad
- Replace Item#_boundsSelected with #_selectBounds  … 336bc10
- More clean-up of selection handling refactoring. 00b2102
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants