Troubles Using Cgpathcontainspoint Swift

Troubles using CGPathContainsPoint SWIFT

As of Swift 3, many Core Graphics functions are now methods
on the corresponding type. In your example:

if my_sprite_path.contains(my_point) {

}

For more information see
SE-044 Import as member.

CGPathContainsPoint broken in iOS 12? Is a workaround possible?

I see the same behavior you describe. Interestingly, if you manually close the path, it seems to work. E.g., this is the same as your path except adding the line with the comment:

CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, nil, 318.43, 183.47);
CGPathAddCurveToPoint(path, nil, 322.98, 189.32, 327, 194.93, 329.81, 201.62);
CGPathAddLineToPoint(path, nil, 339.36, 182.58);
CGPathAddLineToPoint(path, nil, 318.43, 183.47); // manually add line back to starting point
CGPathCloseSubpath(path);

CGPathMoveToPoint(path, nil, 327.8, 193.04);
CGPathAddCurveToPoint(path, nil, 323.06, 193.04, 323.06, 185.5, 327.8, 185.5);
CGPathAddCurveToPoint(path, nil, 332.54, 185.5, 332.54, 193.04, 327.8, 193.04);
CGPathCloseSubpath(path);

I might suggest trying keeping track of the starting point yourself and adding a line back to it wherever you close the subpath and see if that fixes it.


Even more inexplicably, doing CGPathMoveToPoint twice (lol) also seems to solve it.

CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, nil, 318.43, 183.47);
CGPathMoveToPoint(path, nil, 318.43, 183.47); // move the the starting point twice?!?
CGPathAddCurveToPoint(path, nil, 322.98, 189.32, 327, 194.93, 329.81, 201.62);
CGPathAddLineToPoint(path, nil, 339.36, 182.58);
CGPathCloseSubpath(path);

CGPathMoveToPoint(path, nil, 327.8, 193.04);
CGPathAddCurveToPoint(path, nil, 323.06, 193.04, 323.06, 185.5, 327.8, 185.5);
CGPathAddCurveToPoint(path, nil, 332.54, 185.5, 332.54, 193.04, 327.8, 193.04);
CGPathCloseSubpath(path);

Detect if CGPoint within polygon

You can create a CG(Mutable)PathRef (or a UIBezierPath that wraps a CGPathRef) from your points and use the CGPathContainsPoint function to check if a point is inside that path. If you use UIBezierPath, you could also use the containsPoint: method.

Swift 3 CGAffineTransform on CGRect

I modified the function found here and produced the following function:

func createPathRotatedAroundBoundingBoxCenter(path: CGPath, radians: CGFloat) -> CGPath {
let bounds = path.boundingBox; // might want to use CGPathGetPathBoundingBox
let center = CGPoint(x: bounds.midX, y: bounds.midY)
var transform = CGAffineTransform(translationX: 0, y: 0)
transform = transform.translatedBy(x: center.x, y: center.y)
transform = transform.rotated(by: radians)
transform = transform.translatedBy(x: -center.x, y: -center.y)
return path.copy(using: &transform)!
}

I then modified my code for the desired result.

let rect = CGRect(origin: CGPoint(x: Logo.position.x - (Logo.frame.width/3.6), y: Logo.position.y - (Logo.frame.height/2) - kViewSize.height/32), size: CGSize(width: Logo.frame.width/2, height: Logo.frame.height + kViewSize.height/16))
let ellipse = CGPath(ellipseIn: rect, transform: nil)
let ellipse2a = CGPath(ellipseIn: rect, transform: nil)
let ellipse3a = CGPath(ellipseIn: rect, transform: nil)
let ellipse2 = createPathRotatedAroundBoundingBoxCenter(path: ellipse2b, radians: CGFloat(M_PI/4.0))
let ellipse3 = createPathRotatedAroundBoundingBoxCenter(path: ellipse3b, radians: CGFloat(-M_PI/4.0))
YellowMagic.run(SKAction.repeatForever(SKAction.follow(ellipse, asOffset: false, orientToPath: false, duration: 1)))
RedMagic.run(SKAction.repeatForever(SKAction.follow(ellipse2, asOffset: false, orientToPath: false, duration: 1)))
GreenMagic.run(SKAction.repeatForever(SKAction.follow(ellipse3, asOffset: false, orientToPath: false, duration: 1)))

Swift - how to fill a path (opaque path)

Your code is correct but you should probably look at the origins of your scene. Are your sure you can see your shape?

By default, a scene’s origin is placed in the lower-left corner of the view. So, a default scene is initialized with a height of 1024 and a width of 768, has the origin (0,0) in the lower-left corner, and the (1024,768) coordinate in the upper-right corner. The frame property holds (0,0)-(1024,768).

Suppose you have this example using your question code:

let tileSize = CGSizeMake(150.0,150.0)
// Your code:
let isometricPath = CGPathCreateMutable()
CGPathMoveToPoint(isometricPath, nil, 0, -(tileSize.height / 2))
CGPathAddLineToPoint(isometricPath, nil, (tileSize.width / 2), 0)
CGPathAddLineToPoint(isometricPath, nil, 0, (tileSize.height / 2))
CGPathAddLineToPoint(isometricPath, nil, -(tileSize.width / 2), 0)
CGPathCloseSubpath(isometricPath)

This path represent a rhombus shape.

Now if I want to draw this shape I can do:

let shape = SKShapeNode.init(path: isometricPath)
shape.strokeColor = SKColor.yellowColor()
shape.fillColor = SKColor.blueColor()
self.addChild(shape)

But nothing is showed because my scene origin start from 0.0 and your shape have negative values.

To show my rhombus I can simply change the scene anchorPoint to:

 self.anchorPoint = CGPointMake(0.5,0.5)

This centers the scene’s origin in the middle of the view (Apple docs) and you can see your tile:

Sample Image

Now suppose you want to know if a point is inside your shape:

let locationInNode = CGPointMake(tileSize.width/15,tileSize.height/15)
// this point is definitely inside the rhombus

I want to show this point simply for debug:

let circle = SKShapeNode.init(circleOfRadius: 5)
circle.strokeColor = SKColor.whiteColor()
circle.fillColor = SKColor.whiteColor()
self.addChild(circle)
circle.position = locationInNode

Sample Image

Now we can check if this point is inside the rhombus:

let isometricPathRef = isometricPath as CGPathRef
print(CGPathContainsPoint(isometricPathRef, nil, locationInNode, true))

Output:

Sample Image

Detecting a tap on a CAShapeLayer in Swift?

Heres imo the best way to do what you want to achieve:

// First add the shapelayer
let layer = CAShapeLayer()
layer.anchorPoint = CGPointZero
layer.path = UIBezierPath(ovalInRect: CGRect(x: 0, y: 0, width: 100, height: 200)).CGPath
layer.bounds = CGPathGetBoundingBox(layer.path) // IMPORTANT, without this hitTest wont work
layer.fillColor = UIColor.redColor().CGColor
self.view.layer.addSublayer(layer)

// Check for touches
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let point = touches.anyObject()!.locationInView(self.view) // Where you pressed

if let layer = self.view.layer.hitTest(point) as? CAShapeLayer { // If you hit a layer and if its a Shapelayer
if CGPathContainsPoint(layer.path, nil, point, false) { // Optional, if you are inside its content path
println("Hit shapeLayer") // Do something
}
}
}


Related Topics



Leave a reply



Submit