Assertion failed when use bodyWithPolygonFromPath: method to egg shape - ios

I am simulating physics to an egg shape SKSprite node.
However, there is assertion failed error occur:
Assertion failed: (edge.LengthSquared() > 1.19209290e-7F * 1.19209290e-7F), function Set, file /SourceCache/PhysicsKit_Sim/PhysicsKit-6.5.4/PhysicsKit/Box2D/Collision/Shapes/b2PolygonShape.cpp, line 180.
the following is the code:
self.egg = [SKSpriteNode spriteNodeWithImageNamed:IMAGE_NAME_EGG];
[self.egg setScale:0.2];
self.egg.position = CGPointMake(self.size.width/2,self.size.height - self.egg.size.height/2);
self.egg.name = IMAGE_NAME_EGG;
CGPoint startPoint = CGPointMake(0, self.egg.size.height*0.4);
CGPoint endPoint = CGPointMake(self.egg.size.width, startPoint.y);
CGPoint controlPointLeft = CGPointMake(startPoint.x, self.egg.size.height);
CGPoint controlPointRight = CGPointMake(endPoint.x, controlPointLeft.y);
CGMutablePathRef pathRef = CGPathCreateMutable();
CGPathMoveToPoint(pathRef, NULL, startPoint.x, startPoint.y);
CGPathAddQuadCurveToPoint(pathRef, NULL, controlPointLeft.x, controlPointLeft.y, self.egg.size.width/2, controlPointLeft.y);
CGPathAddQuadCurveToPoint(pathRef, NULL, controlPointRight.x, controlPointRight.y, endPoint.x,endPoint.y);
CGPathAddArc(pathRef, NULL, self.egg.size.width/2, startPoint.y, self.egg.size.width/2, 0, M_PI, NO);
self.egg.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:pathRef];
self.egg.physicsBody.dynamic = YES;
self.egg.physicsBody.categoryBitMask = eggCategory;
self.egg.physicsBody.contactTestBitMask = rabbitCategory;
self.egg.physicsBody.collisionBitMask = rabbitCategory;
self.egg.physicsBody.allowsRotation = YES;
[self addChild:self.egg];
What's wrong? can anyone help me to fix? thank you very much!

when you use " bodyWithPolygonFromPath: ", you mut make sure that the path meats the following conditions:
1). convex polygonal path
2). counterclockwise winding
3). no self intersections.
//The points are specified relative to the owning node’s origin.
you can find it in apply class reference for SKPhysicsBody here:
https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKPhysicsBody_Ref/Reference/Reference.html#//apple_ref/occ/clm/SKPhysicsBody/bodyWithPolygonFromPath:

According to the assertion, one of the edges is shorter than the minimum size expected by sprite kit.
Assertion failed: (edge.LengthSquared() > 1.19209290e-7F * 1.19209290e-7F)
Check your path coordinates and ensure it's not too small.

I had this error on an older iOS7 iPhone when attempting to make a triangular path. It turns out that adding a final line to bring the path back to the starting point led to the error (though newer iOS versions allow this.) IE:
CGPathMoveToPoint(path, nil, self.size.width/2, self.size.height/2)
CGPathAddLineToPoint(path, nil, -self.size.width/2, 0.0)
CGPathAddLineToPoint(path, nil, self.size.width/2, -self.size.height/2)
CGPathAddLineToPoint(path, nil, self.size.width/2, self.size.height/2) //REMOVED THIS LINE TO FIX
CGPathCloseSubpath(path)
This explains why there was an edge that was too small to evaluate - the edge between two of the same points is 0!

Related

MKOverlayRenderer draw line using CGPath

I'm trying to draw a line using MKOverlayRenderer. My overlay renderer's drawMapRect is roughly:
let path = CGPathCreateMutable()
CGPathMoveToPoint(path, nil, x, y) // x, y = starting point
let remainingPoints: [CGPoint] = ... // remaining points
CGPathAddLines(path, nil, remainingPoints, remainingPoints.count)
CGContextAddPath(context, path)
CGContextSetStrokeColorWithColor(context, UIColor.blueColor().CGColor)
CGContextSetLineWidth(context, 2.0)
CGContextStrokePath(context)
This however doesn't work, nothing appears. I also tried stroking with:
CGContextDrawPath(context, .Stroke)
I know my path is defined correctly because if I use .FillStroke, it fills the polygons.
Closing the path using CGPathCloseSubpath(path) doesn't help.
My line width (2.0) was too small the for the zoom level.

Cannot create two CGPathAddArc on the same CGMutablePathRef

I'm trying to understand why i'm seeing only one of mine CGPathAddArc.
Code :
var r: CGRect = self.myView.bounds
var lay: CAShapeLayer = CAShapeLayer()
var path: CGMutablePathRef = CGPathCreateMutable()
CGPathAddArc(path, nil, 30, 30, 30, 0, (360 * CGFloat(M_PI))/180, true )
CGPathAddArc(path, nil, 70, 30, 30, 0, (360 * CGFloat(M_PI))/180, true )
CGPathAddRect(path, nil, r2)
CGPathAddRect(path, nil, r)
lay.path = path
lay.fillRule = kCAFillRuleEvenOdd
self.myView.layer.mask = lay
result :
Any suggestions? Thanks!
If you push down the command key and click on CGPathAddArc function, you will see documentation.
/* Note that using values very near 2π can be problematic. For example,
setting `startAngle' to 0, `endAngle' to 2π, and `clockwise' to true will
draw nothing. (It's easy to see this by considering, instead of 0 and 2π,
the values ε and 2π - ε, where ε is very small.) Due to round-off error,
however, it's possible that passing the value `2 * M_PI' to approximate
2π will numerically equal to 2π + δ, for some small δ; this will cause a
full circle to be drawn.
If you want a full circle to be drawn clockwise, you should set
`startAngle' to 2π, `endAngle' to 0, and `clockwise' to true. This avoids
the instability problems discussed above. */
Setting startAngle to 0, endAngle to 2π, and clockwise to true will
draw nothing. If you want a full circle to be drawn clockwise, you should set
startAngle to 2π, endAngle to 0, and clockwise to true. So that you can see all circles.
What you need to do is debug. How? Well, when in doubt, your first step should be to simplify. In this case, you should start by testing your code outside the context of a mask and a fill rule. When you do, you'll see that the arcs are in fact both present. I ran this reduced version of your code:
let lay = CAShapeLayer()
lay.frame = CGRectMake(20,20,400,400)
let path = CGPathCreateMutable()
CGPathAddArc(path, nil, 30, 30, 30, 0,
(360 * CGFloat(M_PI))/180, true)
CGPathAddArc(path, nil, 70, 30, 30, 0,
(360 * CGFloat(M_PI))/180, true)
lay.path = path
self.view.layer.addSublayer(lay)
And this is what I got:
As you can see, both arcs are present. So your results must be due to some complication beyond the drawing of the arcs.
If we add the fill rule...
lay.fillRule = kCAFillRuleEvenOdd
...we get this:
And if we introduce the mask element...
// self.view.layer.addSublayer(lay)
self.view.layer.mask = lay
...we get this:
Thus, using basic tests, you should be able to convince yourself of what this part of your code does. You can now introduce more and more of your actual code until you start getting undesirable results, and then you'll know what's causing the problem.

In iOS, arcs are malformed for certain start angles

I use the following code to draw an arc
double radius = 358.40001058578491;
startAngle = 0.13541347644783652;
double center_x= 684;
double center_y = 440;
std::complex<double> start1( std::polar(radius,startAngle) );
CGPoint targetStart1 = CGPointMake(start1.real() + center_x, start1.imag() +center_y);
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, targetStart1.x, targetStart1.y);
CGPathAddArc(path, NULL, center_x, center_y, radius, startAngle, 0.785, 0 );
CGContextAddPath(context, path);
CGContextSetLineWidth( context, 30 );
CGContextSetStrokeColorWithColor( context, targetColor.CGColor);
CGContextStrokePath(context);
CGPathRelease(path);
If u check it in retina, it looks like this:
My arc is the green arc. I have shown the place that the start angle is with a orange line. As I have shown in the red rectangle, there is an extra thing drawn in the very beginning of the arc. This happens not for all start angles, but only for certain start angles.
Do you have any idea why it happens?
Thanks.
In your original question, you specified a literal starting point that was not quite right and, as a result, Core Graphics will draw a line from that point to the start of the arc. And because that starting point was just a few pixels away from the actual start of the arc, it results in that curious rendering you illustrate in your question.
In your revised question, you're calculating the starting point, but I might suggest calculating it programmatically like so:
CGFloat centerX = 684.0;
CGFloat centerY = 440.0;
CGFloat radius = 360.0;
CGFloat startAngle = 0.135;
CGFloat endAngle = 0.785;
CGFloat startingX = centerX + radius * cosf(startAngle);
CGFloat startingY = centerY + radius * sinf(startAngle);
CGContextMoveToPoint(context, startingX, startingY);
CGContextAddArc(context, centerX, centerY, radius, startAngle, endAngle, 0);
CGContextSetLineWidth(context, 30);
CGContextSetStrokeColorWithColor(context, targetColor.CGColor);
CGContextStrokePath(context);
When I calculated it this way, there was no rounding errors that resulted in the artifact illustrated in your original question.
Note, if you're not drawing anything before the arc, you can just omit the CGContextMoveToPoint call altogether. You only need that "move to point" call if you've drawn something before the arc and don't want the path connecting from that CGContextGetPathCurrentPoint to the start of the arc.

How to create a elliptical CGPathRef?

I am new to core graphic in cocoa.
I have a picture of a egg and I am using Sprite kit to simulation physics of free falling of an egg.
I need to set the egg's physicsBody property.
How to create an elliptical CGPathRef for the purpose?
For an egg shape path, I have to create an half of circle with half of oval. How to do it?
Allow me to point you to the reference documentation of CGPath, where you will find CGPathCreateWithEllipseInRect.
Actually, I just ran into this problem in an app that I was writing to create some graphics assets.
CGPathMoveToPoint(path, nil, 10, 95);
CGPathAddLineToPoint(path, nil, (5 + SIZE) * 1.5, 95 - SIZE * 1 / 3);
CGAffineTransform t = CGAffineTransformMakeScale(1.5, 1.0);
CGPathAddArc(path, &t, SIZE + 5, 95 - SIZE * 4 / 3, SIZE, M_PI / 2, -M_PI * 5 / 4, YES);
CGPathCloseSubpath(path);
TL;DR
You have to use CGAffineTransform in order to get an arc of an ellipse.

Why doesn't CGPathIsRect work? Why doesn't CGPathContainsPoint work?

I am making a CGPoint and a CGPathRef then trying to find if the CGPoint is inside the CGPathRef. Here is the code:
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 0, 0);
CGPathMoveToPoint(path, NULL, 200, 0);
CGPathMoveToPoint(path, NULL, 200, 200);
CGPathMoveToPoint(path, NULL, 0, 200);
CGPathCloseSubpath(path);
CGPoint hitPoint = CGPointMake(77, 77);
if ( CGPathIsEmpty(path) )
NSLog(#"Path Is Empty!");
else
{
if ( CGPathIsRect(path, NULL) )
NSLog(#"Path is a Rectangle!");
else
{
NSLog(#"Path is NOT a Rectangle!");
if (CGPathContainsPoint(path, NULL, hitPoint, FALSE)) // FALSE or TRUE - same result
NSLog(#"Hit Point Inside: x=%f, y=%f", hitPoint.x, hitPoint.y);
else
NSLog(#"Hit Point Outside: x=%f, y=%f", hitPoint.x, hitPoint.y);
}
}
The output reads:
Path is NOT a Rectangle!
Hit Point Outside: x=77.000000, y=77.000000
The path obviously is a rectangle and the point is inside the closed path. Please clue me in on what I am doing wrong here.
CGRectIsPath only returns true if the path was created by CGPathCreateWithRect (with a transform parameter that doesn't rotate or skew the rectangle), or if the path was created by CGPathCreateMutable and had a single rectangle added to it with CGPathAddRect.
It would be much more work for it to figure out whether any arbitrary path is exactly a rectangle. The path might contain bezier curve segments that are actually straight lines, or sides that are built from consecutive straight line segments.
If you need to detect whether any arbitrary path is actually just a rectangle, you will have to do it yourself using CGPathApply. It will be complicated.
As for why your point-inside test isn't working: you need to use CGPathAddLineToPoint to create the sides of your rectangle:
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 0, 0);
CGPathAddLineToPoint(path, NULL, 200, 0);
CGPathAddLineToPoint(path, NULL, 200, 200);
CGPathAddLineToPoint(path, NULL, 0, 200);
CGPathCloseSubpath(path);

Resources