adamwulf / ClippingBezier

ClippingBezier calculates intersection points, paths, and shapes between two UIBezierPaths
http://getlooseleaf.com/opensource/
MIT License
254 stars 34 forks source link

Not all subShapes returned from uniqueShapesCreatedFromSlicing for some Bezier paths #12

Open prasad1120 opened 4 years ago

prasad1120 commented 4 years ago

Hi, I am trying to split the green part into 2 by considering the black box as the splitter path using uniqueShapesCreatedFromSlicing. It is working in most cases but there are some corner cases where it doesn't. Here, uniqueShapesCreatedFromSlicing returns 1 value in the array instead of 2.

Simulator Screen Shot - iPhone X - 2020-06-08 at 19 48 25

Note: Consider the black box to be complete.

let splitterPath = UIBezierPath(rect: someRect)
UIColor.black.setStroke()
splitterPath.lineWidth = 3
splitter.stroke()

UIColor.green.setFill()
splittingPath.fill()

// returns array with 1 element
let subShapes = splittingPath.uniqueShapesCreatedFromSlicing(withUnclosedPath: splitterPath) 

subShapes[0]!.fullPath().debugDescription:

path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(208.000000, 24.970562)];
[path addLineToPoint:CGPointMake(208.000000, 31.999998)];
[path addLineToPoint:CGPointMake(220.000000, 32.000000)];
[path addLineToPoint:CGPointMake(220.000000, 32.000000)];
[path addCurveToPoint:CGPointMake(211.514719, 28.485281) controlPoint1:CGPointMake(216.817402, 32.000000) controlPoint2:CGPointMake(213.765155, 30.735718)];
[path addLineToPoint:CGPointMake(208.000000, 24.970562)];
[path closePath];

splitterPath.debugDescription:

path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(12.000000, 16.970563)];
[path addLineToPoint:CGPointMake(208.000000, 16.970563)];
[path addLineToPoint:CGPointMake(208.000000, 128.000000)];
[path addLineToPoint:CGPointMake(12.000000, 128.000000)];
[path closePath];

splittingPath.debugDescription:

path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(12.000000, 32.000000)];
[path addCurveToPoint:CGPointMake(12.000000, 32.000000) controlPoint1:CGPointMake(12.000000, 32.000000) controlPoint2:CGPointMake(12.000000, 32.000000)];
[path addLineToPoint:CGPointMake(12.000000, 12.000000)];
[path addLineToPoint:CGPointMake(12.000000, 128.000000)];
[path addCurveToPoint:CGPointMake(12.000000, 128.000000) controlPoint1:CGPointMake(12.000000, 128.000000) controlPoint2:CGPointMake(12.000000, 128.000000)];
[path addLineToPoint:CGPointMake(208.000000, 128.000000)];
[path addLineToPoint:CGPointMake(208.000000, 128.000000)];
[path addCurveToPoint:CGPointMake(208.000000, 128.000000) controlPoint1:CGPointMake(208.000000, 128.000000) controlPoint2:CGPointMake(208.000000, 128.000000)];
[path addLineToPoint:CGPointMake(208.000000, 32.000000)];
[path addLineToPoint:CGPointMake(208.000000, 32.000000)];
[path addCurveToPoint:CGPointMake(208.000000, 32.000000) controlPoint1:CGPointMake(208.000000, 32.000000) controlPoint2:CGPointMake(208.000000, 32.000000)];
[path addLineToPoint:CGPointMake(220.000000, 32.000000)];
[path addLineToPoint:CGPointMake(220.000000, 32.000000)];
[path addCurveToPoint:CGPointMake(211.514719, 28.485281) controlPoint1:CGPointMake(216.817402, 32.000000) controlPoint2:CGPointMake(213.765155, 30.735718)];
[path addLineToPoint:CGPointMake(200.000000, 16.970563)];
[path addLineToPoint:CGPointMake(190.828427, 26.142136)];
[path addLineToPoint:CGPointMake(190.828427, 26.142136)];
[path addCurveToPoint:CGPointMake(176.686292, 32.000000) controlPoint1:CGPointMake(187.077700, 29.892863) controlPoint2:CGPointMake(181.990621, 32.000000)];
[path addLineToPoint:CGPointMake(12.000000, 32.000000)];
[path closePath];
adamwulf commented 4 years ago

This is a strange one. It may take me a while to find the root cause, but in playing around with it I found that reversing the splitterPath with bezierPathByReversingPath gives the correct results of 2 shapes.

prasad1120 commented 4 years ago

I think I have found what was wrong. In the splittingPath, there is a place where the end of addLine doesn't end at the point from where addArc is supposed to start. I'm not aware how this fix that I made affected the splitting process. I'm able to get desired results now even without using bezierPathByReversingPath.

adamwulf commented 4 years ago

Glad you found a way to get it to work. Can you post the splitter and splitting paths that don't work unless you make that 1 adjustment to the addLine call? and post the fixed splitting path too? that'll help me track down the problem when i have the test case for fail/succeed with that one small change. thanks!

prasad1120 commented 4 years ago

No change was made in splitterPath

splitterPath.debugDescription:

path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(35.000000, 49.497475)];
[path addLineToPoint:CGPointMake(185.000000, 49.497475)];
[path addLineToPoint:CGPointMake(185.000000, 105.000000)];
[path addLineToPoint:CGPointMake(35.000000, 105.000000)];
[path closePath];

(before correction) splittingPath that returns 1 value after splitting:

path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(35.000000, 55.000000)];
[path addCurveToPoint:CGPointMake(35.000000, 55.000000) controlPoint1:CGPointMake(35.000000, 55.000000) controlPoint2:CGPointMake(35.000000, 55.000000)];
[path addLineToPoint:CGPointMake(35.000000, 35.000000)];
[path addLineToPoint:CGPointMake(35.000000, 105.000000)];
[path addCurveToPoint:CGPointMake(35.000000, 105.000000) controlPoint1:CGPointMake(35.000000, 105.000000) controlPoint2:CGPointMake(35.000000, 105.000000)];
[path addLineToPoint:CGPointMake(185.000000, 105.000000)];
[path addLineToPoint:CGPointMake(185.000000, 105.000000)];
[path addCurveToPoint:CGPointMake(185.000000, 105.000000) controlPoint1:CGPointMake(185.000000, 105.000000) controlPoint2:CGPointMake(185.000000, 105.000000)];
[path addLineToPoint:CGPointMake(185.000000, 55.000000)];
[path addLineToPoint:CGPointMake(185.000000, 55.000000)];
[path addCurveToPoint:CGPointMake(185.000000, 55.000000) controlPoint1:CGPointMake(185.000000, 55.000000) controlPoint2:CGPointMake(185.000000, 55.000000)];
[path addLineToPoint:CGPointMake(220.000000, 55.000000)];
[path addLineToPoint:CGPointMake(200.000000, 47.577787)];
[path addCurveToPoint:CGPointMake(173.786797, 55.000000) controlPoint1:CGPointMake(192.117733, 52.430540) controlPoint2:CGPointMake(183.043110, 55.000000)];
[path addLineToPoint:CGPointMake(35.000000, 55.000000)];
[path closePath];

(after correction) splittingPath that returns 2 values after splitting:

path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(35.000000, 55.000000)];
[path addCurveToPoint:CGPointMake(35.000000, 55.000000) controlPoint1:CGPointMake(35.000000, 55.000000) controlPoint2:CGPointMake(35.000000, 55.000000)];
[path addLineToPoint:CGPointMake(35.000000, 105.000000)];
[path addLineToPoint:CGPointMake(35.000000, 105.000000)];
[path addCurveToPoint:CGPointMake(35.000000, 105.000000) controlPoint1:CGPointMake(35.000000, 105.000000) controlPoint2:CGPointMake(35.000000, 105.000000)];
[path addLineToPoint:CGPointMake(185.000000, 105.000000)];
[path addLineToPoint:CGPointMake(185.000000, 105.000000)];
[path addCurveToPoint:CGPointMake(185.000000, 105.000000) controlPoint1:CGPointMake(185.000000, 105.000000) controlPoint2:CGPointMake(185.000000, 105.000000)];
[path addLineToPoint:CGPointMake(185.000000, 55.000000)];
[path addLineToPoint:CGPointMake(185.000000, 55.000000)];
[path addCurveToPoint:CGPointMake(185.000000, 55.000000) controlPoint1:CGPointMake(185.000000, 55.000000) controlPoint2:CGPointMake(185.000000, 55.000000)];
[path addLineToPoint:CGPointMake(220.000000, 55.000000)];
[path addLineToPoint:CGPointMake(200.000000, 47.577787)];
[path addCurveToPoint:CGPointMake(173.786797, 55.000000) controlPoint1:CGPointMake(192.117733, 52.430540) controlPoint2:CGPointMake(183.043110, 55.000000)];
[path addLineToPoint:CGPointMake(35.000000, 55.000000)];
[path closePath];

I hope this helps :)

adamwulf commented 4 years ago

awesome :) many thanks for this test case