How to start & end a path at top of the circle? - ios

Given the following code:
let startAngle: CGFloat = CGFloat( (3 * M_PI) / 2 )
let endAngle:CGFloat = CGFloat( (3 * M_PI) / 2 )
let path = UIBezierPath(arcCenter: theCenter, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true).CGPath
And knowing that this is how iOS draws its circles as such:
What do I have to specify to start my circle at top (12:00) & end it at 90 degrees? If I wanted to start it & end it at 0, I would set start & end to 0 & 2π respectively.

You'll want to go from -π/2 to 3π/2 (in a clockwise arc).
You can't go from 3π/2 to 3π/2, as you tried in the question, as iOS will see that as the same angle - and therefore won't generate any arc. You'll therefore want to use the negative representation of 3π/2 (-π/2).
Although note that the unit circle is continuous. Therefore 3π/2 to 7π/2 is just as valid a range.
Also note that the unit circle's positive angle direction is actually anti-clockwise. iOS does it in a clockwise manner as the coordinate system's y-axis is flipped.

Related

Swift: How to set round UIBezierPath position

I just created a round line with UIBezierPath using this code: let circularPath = UIBezierPath(arcCenter: timerLabel.layer.position, radius: frame.size.width / 2 - 80, startAngle: -0.5 * CGFloat.pi, endAngle: 1.5 * CGFloat.pi, clockwise: true)
And I want my circle to have the position of my TimerLabel. But as you can see in the image, my circle just doesn't want to be placed in the middle of my timer label.
I have already tried to set the arcCenter parameter to timerlabel.center, CGPoit (x: timerlabel.x, y: timerlabel.y) and so on. But the circle is still not centered. Can someone help me? Warm greetings

ios-UIBezierPath stroke by percentage for anti-clockwise with start for arc is from vertical downward position

I am trying to create anticlockwise UIBezierPath with start of arc is negative y axis (just for visualization).
let arcPathForLayer = UIBezierPath(arcCenter: upperCenterPoint, radius: radius, startAngle: arcPathStartAngle, endAngle: (arcPathEndAngle), clockwise: false)
Normally for clockwise UIBezierPath, start and end angle is 0.0pi and 2.0pi respectively -for if the path is to start form positive x axis.
But for this case where UIBezierPath is anti clockwise and start of arc is negative y axis -the start angle and end angle is little confusing to calculate.
So I will post the answer for the same.
Here is the visualization for solution:
So the start angle is 0.5pi and end is -1.5pi, now for drawing the stroke for 25% --> the value of end angle = 0pi, for 50% --> end angle = -0.5pi, and on same note for 100% --> end angle = -1.5pi
here is the formula to calculate the same:
endAngle = (0.5 - (2 * percentageForProgress)) * .pi

SKSpriteNode will not center

I have this inside my GameScene which is called in the didMove()
for i in 1...5 {
// path to create the circle
let path = UIBezierPath(arcCenter: CGPoint(x: center.x, y: center.y), radius: CGFloat(((43 * i) + 140)), startAngle: CGFloat(GLKMathDegreesToRadians(-50)), endAngle: CGFloat(M_PI * 2), clockwise: false)
// the inside edge of the circle used for creating its physics body
let innerPath = UIBezierPath(arcCenter: CGPoint(x: center.x, y: center.y), radius: CGFloat(((43 * i) + 130)), startAngle: CGFloat(GLKMathDegreesToRadians(-50)), endAngle: CGFloat(M_PI * 2), clockwise: false)
// create a shape from the path and customize it
let shape = SKShapeNode(path: path.cgPath)
shape.lineWidth = 20
shape.strokeColor = UIColor(red:0.98, green:0.99, blue:0.99, alpha:1.00)
// create a texture and apply it to the sprite
let trackViewTexture = self.view!.texture(from: shape)
let trackViewSprite = SKSpriteNode(texture: trackViewTexture)
trackViewSprite.physicsBody = SKPhysicsBody(edgeChainFrom: innerPath.cgPath)
self.addChild(trackViewSprite)
}
It uses UIBezierPaths to make a few circles. It converts the path into a SKShapeNode then a SKTexture and then applies it to the final SKSpriteNode.
When I do this, the SKSpriteNode is not where it should be, it is a few to the right:
But when I add the SKShapeNode I created, it is set perfectly fine to where it should be:
Even doing this does not center it!
trackViewSprite.position = CGPoint(x: 0, y: 0)
No matter what I try it just will not center.
Why is this happening? Some sort of bug when converting to a texture?
P.S - This has something to do with this also Keep relative positions of SKSpriteNode from SKShapeNode from CGPath
But there is also no response :(
Edit, When I run this:
let testSprite = SKSpriteNode(color: UIColor.yellow, size: trackViewSprite.size)
self.addChild(testSprite)
It shows it has the same frame also:
After a long discussion, we determined that the problem is due to the frame size not being the expected size of the shape.
To combat this, the OP created an outer path of his original path, and calculated the frame that would surround this. Now this approach may not work for everybody.
If anybody else comes across this issue, they will need to do these things:
1) Check the frame of the SKShapeNode to make sure that it is correct
2) Determine what method is best to calculate the correct desired frame
3) Use this new frame when getting textureFromNode to extract only the desired texture size

How to draw a line based on giving bearing/heading value at current location in iOS/swift?

I have been doing a ton of research but found nothing. With MapKit, I have got a map that shows current location and elsewhere a function that calculates a heading/bearing value (not necessarily the actual heading). How can I draw a line on the map that will start at current location, and point in direction of the given heading ? (Does not matter how long the line is, as in it has no meaningful end point). I am not asking you to write the code for me but would appreciate some detailed direction. Hope this helps others too.
Cheers
Your coordinates are polar, which means you have a direction and a length. You just need to convert them to Cartesian, which gives you a horizontal offset and a vertical offset. You do that with a little trigonometry.
let origin = CGPoint(x: 10, y: 10)
let heading: CGFloat = CGFloat.pi
let length: CGFloat = 20
let endpoint = CGPoint(x: origin.x + cos(heading)*length,
y: origin.y + sin(heading)*length)
let path = UIBezierPath()
path.move(to: origin)
path.addLine(to: endpoint)
Note that trigonometric functions generally work in radians (2*PI = one revolution). Bearings are often in degrees (360 degrees = one revolution). Converting is straightforward, however:
func radians(forDegrees angle: CGFloat) -> CGFloat {
return CGFloat.pi * angle / 180.0
}

Does CGPathAddArc include the center pixel of a circle?

I use CGPathAddArc to create a circle, if the radius is 5 pixel, will the whole circle end up with 10 pixels width or 11 pixels?
It's also confusing if it does end up with 10 pixels. In that case, what does it mean that center of a radius?
The CGPathAddArc defined here:
void CGPathAddArc (
CGMutablePathRef path,
const CGAffineTransform *m,
CGFloat x,
CGFloat y,
CGFloat radius,
CGFloat startAngle,
CGFloat endAngle,
bool clockwise
);
It does end up with 10 pixels as the diameter, which includes the center point.
As the Apple reference doc says:
When you call this function, you provide the center point, radius, and
two angles in radians. Quartz uses this information to determine the
end points of the arc
The end points are determined 'including' the center point, so that the entire diameter is 10.

Resources