Get all CGPoint from the area of circle - ios

I have center points and radius of circle, I just wanna get all CGPoint that the area of the circle covers. Basically I need to get all pixels through these CGPoint, I have a code for getting pixel from UIImage through CGPoint. I'm getting center points of circle through UITapGestureRecognizer when user tap. And I already know about radius of circle.
Can anyone tell me how I can get all CGPoint or pixels from circle. Thanks
For Example
Origin of below circle is CGPoint(x: 206, y: 105), and diameter of a circle is 50.
Another Example
How I can get these all CGPoints

First, You get minX and minY maybe in radius from center.
Second, check point by point and calculate distance to center.
func distance(from: CGPoint, to: CGPoint) -> CGFloat {
return CGFloat(sqrt((from.x - to.x)*(from.x - to.x) + (from.y - to.y)*(from.y - to.y)))
}
func isInCircle(withCenter center: CGPoint, point: CGPoint, radius: CGFloat) -> Bool {
return distance(from: center, to: point) <= radius
}
func getPoints(withCenter center:CGPoint, radius: CGFloat) -> [CGPoint] {
let minX = Int(center.x - radius)
let minY = Int(center.y - radius)
let maxX = Int(center.x + radius)
let maxY = Int(center.y + radius)
var result = [CGPoint]()
for x in minX...maxX {
for y in minY...maxY {
let point = CGPoint(x: x, y: y)
if isInCircle(withCenter: center, point: point, radius: radius) {
result.append(point)
}
}
}
return result
}
let x = getPoints(withCenter: CGPoint.init(x: 206, y: 150), radius: 50)
print(x)

Related

UIBezierPath arc to create pie charts with rounded corners and spacing

I wonder how can we create a pie chart with rounded edges and spaces between pie as shown in the photo.
My first approach: I move the pies out of its center point an offset = 10 to make it look like the photo. But It seems like the radius of the biggest pie is smaller than the smaller ones.
Then I make a change on Radius, but the spacing a bit weird
And since the newCenter point is not in the center of superview, It’s cut off on a side.
outerRadius = outerRadius - offset * 2 * (1 - percentage)
(Percentage is the proportion of pie in the chart)
My second approach: I calculate the center point for each pie instead of moving it out of its original center point. Imagine there’s an empty middle as a circle and a new center point for each pie is in that circle.
The issues still occur with large pies.
The new center point for each slide on my tries:
let middleAngle = ((startAngle + endAngle) / 2.0).toRadians()
let center = CGPoint(x: bounds.midX, y: bounds.midY)
let newCenter = CGPoint(x: center.x + cos(middleAngle) * offset, y: center.y + sin(middleAngle) * offset)
Issues with radius and center point | Expected result
Here’s my code
https://gist.github.com/phongngo511/dfd416aaad45fc0241cd4526d80d94d6
Hi is this what you're trying to achieve? If so, I think your approach had a couple of issues. Firstly, looking at your code gist, I changed a couple of things from the way you were doing it:
Changed the pie segment sizes (so I could test >180° segments) and colours.
I added a convenience toRadians() function to the CGFloat extension (which is just the opposite of the toRadians() function you'd already added).
I changed the radius variable to be the min (not max as you were doing) of the bounds width / height, so that it fits in view without cropping. This is just personal preference & wouldn't change the overall functioning of the code (you might need it to be bigger & scrollable, for instance, whereas I just wanted to debug this particular problem). I also added padding so that it would still fit the segments when they've been spaced apart.
I went down your original route of solving the problem; draw all segments at the centre of the pie chart, and space them out afterwards, rather than trying to draw each one off centre. You could do either route although keeping them centred while constructing them is simpler and leads to more readable code. The spacing out is achieved by an affine transform at the end of the createPath: function, which spaces them out by a given segment's mid angle. You'd probably want to do it slightly more intelligently than this in real life (it's a bit primitive) as, as per the screenshot, very large segments will appear to be separated further than small segments are from each other (the red segment appears further away from the green and blue than the green and blue are from each other). So you might want to develop an algorithm that not only incorporates a segment's mid angle, but also how big that segment is, in order to determine not only the direction but also the distance to separate it? Or maybe factor in a segment's neighbours' mid angles when determining the direction of separation? Personal taste.
In your layoutSubviews(), you were supplying your createPath() with a different oRadius for each segment. That's why your segments had different radii from each other. I just supplied "radius" for all of them. If you comment out the affine transform in my createPath() function (which spaces them out), you'll see the segments in my version are all the same size radius.
I moved the path.close() into the createPath() function, rather than after calling it. Seems neater.
In terms of drawing a given segment, I've taken a different approach entirely (aside from drawing it centred in the pie chart and then moving it afterwards). I've drawn it with 2 straight lines and an arc for the outer circumference of the pie chart. For the rounded corners, rather than drawing an arc (N.B.: your centre rounded corner for a segment wasn't drawing correctly, causing weird graphical artefacts), I've used quadratic Bézier curves. These take only 1 control point, not 2 control points like a cubic Bézier curve takes. As a result, you can specify the corner of the segment as that control point, and it will give you a rounded corner suitable for the corner of the triangle that you're rounding. Because of this, I only draw the lines / arc up to near each corner, then do a quad Bézier curve to round the corner, then carry on with the rest of the segment.
Let me know if anything needs clarification, hope this helps!
import UIKit
class PieChartView: UIView {
var onTouchPie: ((_ sliceIndex: Int) -> ())?
var shouldHighlightPieOnTouch = false
var shouldShowLabels: Bool = false {
didSet { setNeedsLayout() }
}
var labelTextFont = UIFont.systemFont(ofSize: 12) {
didSet { setNeedsLayout() }
}
var labelTextColor = UIColor.black {
didSet { setNeedsLayout() }
}
var shouldShowTextPercentageFromFieFilledFigures = false {
didSet { setNeedsLayout() }
}
var pieGradientColors: [[UIColor]] = [[.red,.red], [.cyan,.cyan], [.green,.green]] {
didSet { setNeedsLayout() }
}
var pieFilledPercentages:[CGFloat] = [1, 1, 1] {
didSet { setNeedsLayout() }
}
//var segments:[CGFloat] = [40, 30, 30] {
var segments:[CGFloat] = [70, 20, 10] {
didSet { setNeedsLayout() }
}
var offset:CGFloat = 15 {
didSet { setNeedsLayout() }
}
var spaceLineColor: UIColor = .white {
didSet { setNeedsLayout() }
}
private var labels: [UILabel] = []
private var labelSize = CGSize(width: 100, height: 50)
private var shapeLayers = [CAShapeLayer]()
private var gradientLayers = [CAGradientLayer]()
override func layoutSubviews() {
super.layoutSubviews()
labels.forEach({$0.removeFromSuperview()})
labels.removeAll()
shapeLayers.forEach({$0.removeFromSuperlayer()})
shapeLayers.removeAll()
gradientLayers.forEach({$0.removeFromSuperlayer()})
gradientLayers.removeAll()
let valueCount = segments.reduce(CGFloat(0), {$0 + $1})
guard pieFilledPercentages.count >= 3, segments.count >= 3, pieGradientColors.count >= 3 , valueCount > 0 else { return }
let radius = min(bounds.width / 2, bounds.height / 2) * 0.9 //KEN CHANGED
var startAngle: CGFloat = 360
let proportions = segments.map({ ($0 / valueCount * 100).rounded()})
for i in 0..<segments.count {
let endAngle = startAngle - proportions[i] / 100 * 360
let path = createPath(from: startAngle, to: endAngle, oRadius: radius, percentage: proportions[i])
//path.close() //KEN CHANGED
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayers.append(shapeLayer)
let gradientLayer = CAGradientLayer()
gradientLayer.colors = pieGradientColors[i].map({$0.cgColor})
if i == 0 {
gradientLayer.locations = [0.5, 1]
} else {
gradientLayer.locations = [0, 0.5]
}
gradientLayer.mask = shapeLayer
gradientLayer.frame = bounds
if proportions[i] != 0 && pieFilledPercentages[i] != 0 {
layer.addSublayer(gradientLayer)
gradientLayers.append(gradientLayer)
}
let label = labelFromPoint(point: getCenterPointOfArc(startAngle: startAngle, endAngle: endAngle), andText: String(format: "%.f", shouldShowTextPercentageFromFieFilledFigures ? pieFilledPercentages[i] * 100 :segments[i]) + "%")
label.isHidden = !shouldShowLabels
if proportions[i] != 0 {
addSubview(label)
labels.append(label)
}
startAngle = endAngle
}
}
private func labelFromPoint(point: CGPoint, andText text: String) -> UILabel {
let label = UILabel(frame: CGRect(origin: point, size: labelSize))
label.font = labelTextFont
label.textColor = labelTextColor
label.text = text
return label
}
private func getCenterPointOfArc(startAngle: CGFloat, endAngle: CGFloat) -> CGPoint {
let oRadius = max(bounds.width / 2, bounds.height / 2) * 0.8
let center = CGPoint(x: oRadius, y: oRadius)
let centerAngle = ((startAngle + endAngle) / 2.0).toRadians()
let arcCenter = CGPoint(x: center.x + oRadius * cos(centerAngle), y: center.y - oRadius * sin(centerAngle))
return CGPoint(x: (center.x + arcCenter.x) / 2, y: (center.y + arcCenter.y) / 2)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first, shouldHighlightPieOnTouch {
shapeLayers.enumerated().forEach { (item) in
if let path = item.element.path, path.contains(touch.location(in: self)) {
item.element.opacity = 1
onTouchPie?(item.offset)
} else {
item.element.opacity = 0.3
}
}
}
super.touchesBegan(touches, with: event)
}
private func highlightLayer(index: Int) {
shapeLayers.enumerated().forEach({$0.element.opacity = $0.offset == index ? 1: 0.3 })
}
private func createPath(from startAngle: CGFloat, to endAngle: CGFloat, oRadius: CGFloat, cornerRadius: CGFloat = 10, percentage: CGFloat) -> UIBezierPath {
let radius: CGFloat = min(bounds.width, bounds.height) / 2.0 - (2.0 * offset)
let center = CGPoint(x: bounds.midX, y: bounds.midY)
let midPointAngle = ((startAngle + endAngle) / 2.0).toRadians() //used to spread the segment away from its neighbours after creation
let startAngle = (360.0 - startAngle).toRadians()
let endAngle = (360.0 - endAngle).toRadians()
let circumference: CGFloat = CGFloat(2.0 * (Double.pi * Double(radius)))
let arcLengthPerDegree = circumference / 360.0 //how many pixels long the outer arc is of the pie chart, per 1° of a pie segment
let pieSegmentOuterCornerRadiusInDegrees: CGFloat = 4.0 //for a given segment (and if it's >4° in size), use up 2 of its outer arc's degrees as rounded corners.
let pieSegmentOuterCornerRadius = arcLengthPerDegree * pieSegmentOuterCornerRadiusInDegrees
let path = UIBezierPath()
//move to the centre of the pie chart, offset by the corner radius (so the corner of the segment can be rounded in a bit)
path.move(to: CGPoint(x: center.x + (cos(startAngle - CGFloat(360).toRadians()) * cornerRadius), y: center.y + (sin(startAngle - CGFloat(360).toRadians()) * cornerRadius)))
//if the size of the pie segment isn't big enough to warrant rounded outer corners along its outer arc, don't round them off
if ((endAngle - startAngle).toDegrees() <= (pieSegmentOuterCornerRadiusInDegrees * 2.0)) {
//add line from centre of pie chart to 1st outer corner of segment
path.addLine(to: CGPoint(x: center.x + (cos(startAngle - CGFloat(360).toRadians()) * radius), y: center.y + (sin(startAngle - CGFloat(360).toRadians()) * radius)))
//add arc for segment's outer edge on pie chart
path.addArc(withCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
//move down to the centre of the pie chart, leaving room for rounded corner at the end
path.addLine(to: CGPoint(x: center.x + (cos(endAngle - CGFloat(360).toRadians()) * cornerRadius), y: center.y + (sin(endAngle - CGFloat(360).toRadians()) * cornerRadius)))
//add final rounded corner in middle of pie chart
path.addQuadCurve(to: CGPoint(x: center.x + (cos(startAngle - CGFloat(360).toRadians()) * cornerRadius), y: center.y + (sin(startAngle - CGFloat(360).toRadians()) * cornerRadius)), controlPoint: center)
} else { //round the corners on the outer arc
//add line from centre of pie chart to circumference of segment, minus the space needed for the rounded corner
path.addLine(to: CGPoint(x: center.x + (cos(startAngle - CGFloat(360).toRadians()) * (radius - pieSegmentOuterCornerRadius)), y: center.y + (sin(startAngle - CGFloat(360).toRadians()) * (radius - pieSegmentOuterCornerRadius))))
//add rounded corner onto start of outer arc
let firstRoundedCornerEndOnArc = CGPoint(x: center.x + (cos(startAngle + pieSegmentOuterCornerRadiusInDegrees.toRadians() - CGFloat(360).toRadians()) * radius), y: center.y + (sin(startAngle + pieSegmentOuterCornerRadiusInDegrees.toRadians() - CGFloat(360).toRadians()) * radius))
path.addQuadCurve(to: firstRoundedCornerEndOnArc, controlPoint: CGPoint(x: center.x + (cos(startAngle - CGFloat(360).toRadians()) * radius), y: center.y + (sin(startAngle - CGFloat(360).toRadians()) * radius)))
//add arc for segment's outer edge on pie chart
path.addArc(withCenter: center, radius: radius, startAngle: startAngle + pieSegmentOuterCornerRadiusInDegrees.toRadians(), endAngle: endAngle - pieSegmentOuterCornerRadiusInDegrees.toRadians(), clockwise: true)
//add rounded corner onto end of outer arc
let secondRoundedCornerEndOnLine = CGPoint(x: center.x + (cos(endAngle - CGFloat(360).toRadians()) * (radius - pieSegmentOuterCornerRadius)), y: center.y + (sin(endAngle - CGFloat(360).toRadians()) * (radius - pieSegmentOuterCornerRadius)))
path.addQuadCurve(to: secondRoundedCornerEndOnLine, controlPoint: CGPoint(x: center.x + (cos(endAngle - CGFloat(360).toRadians()) * radius), y: center.y + (sin(endAngle - CGFloat(360).toRadians()) * radius)))
//add line back to centre point of pie chart, leaving room for rounded corner at the end
path.addLine(to: CGPoint(x: center.x + (cos(endAngle - CGFloat(360).toRadians()) * cornerRadius), y: center.y + (sin(endAngle - CGFloat(360).toRadians()) * cornerRadius)))
//add final rounded corner in middle of pie chart
path.addQuadCurve(to: CGPoint(x: center.x + (cos(startAngle - CGFloat(360).toRadians()) * cornerRadius), y: center.y + (sin(startAngle - CGFloat(360).toRadians()) * cornerRadius)), controlPoint: center)
}
path.close()
//spread the segments out around the pie chart centre
path.apply(CGAffineTransform(translationX: cos(midPointAngle) * offset, y: -sin(midPointAngle) * offset))
return path
}
}
extension CGFloat {
func toRadians() -> CGFloat {
return self * CGFloat(Double.pi) / 180.0
}
func toDegrees() -> CGFloat {
return self / (CGFloat(Double.pi) / 180.0)
}
}

Swift: make rotation of point in 2D

I want to make a rotation of point by -90 degrees
Initial
Final
Let's take a look on top left and top right points of Initial. Their coordinates are:
let topLeft = CGPoint(x: 2, y: 1)
let topRight = CGPoint(x: 3, y: 1)
And after rotation coordinates of them should become:
topLeft 1:0
topRight 2:0
How can i do it ?
I have tried several answers but none of them give me my final results.
did not work:
Rotating a CGPoint around another CGPoint
What is the best way to rotate a CGPoint on a grid?
Here are some code from my playground:
let topLeft = CGPoint(x: 2, y: 1)
let topRight = CGPoint(x: 3, y: 1)
func rotatePoint1(_ point: CGPoint, _ degrees: CGFloat) -> CGPoint {
let s = CGFloat(sinf(Float(degrees)))
let c = CGFloat(cosf(Float(degrees)));
return CGPoint(x: c * point.x - s * point.y, y: s * point.x + c * point.y)
}
func rotatePoint2(_ point: CGPoint, _ degrees: CGFloat, _ origin: CGPoint) -> CGPoint {
let dx = point.x - origin.x
let dy = point.y - origin.y
let radius = sqrt(dx * dx + dy * dy)
let azimuth = atan2(dy, dx) // in radians
let newAzimuth = azimuth + degrees * CGFloat(M_PI / 180.0) // convert it to radians
let x = origin.x + radius * cos(newAzimuth)
let y = origin.y + radius * sin(newAzimuth)
return CGPoint(x: x, y: y)
}
func rotatePoint3(_ point: CGPoint, _ degrees: CGFloat) -> CGPoint {
let translateTransform = CGAffineTransform(translationX: point.x, y: point.y)
let rotationTransform = CGAffineTransform(rotationAngle: degrees)
let customRotation = (rotationTransform.concatenating(translateTransform.inverted())).concatenating(translateTransform)
return point.applying(customRotation)
}
print(rotatePoint1(topLeft, -90))
print(rotatePoint1(topRight, -90))
You are really describing two rotations with your example:
The points are rotated by -90 degrees around the center of the 3x3 grid. When this happens, the topLeft point becomes bottomLeft, and topRight becomes topLeft.
Then you rotate those points around the center of the square 90 degrees (ie. the other direction) to make them topLeft and topRight again.
Using this function from this answer:
func rotatePoint(target: CGPoint, aroundOrigin origin: CGPoint, byDegrees: CGFloat) -> CGPoint {
let dx = target.x - origin.x
let dy = target.y - origin.y
let radius = sqrt(dx * dx + dy * dy)
let azimuth = atan2(dy, dx) // in radians
let newAzimuth = azimuth + byDegrees * .pi / 180 // convert it to radians
let x = origin.x + radius * cos(newAzimuth)
let y = origin.y + radius * sin(newAzimuth)
return CGPoint(x: x, y: y)
}
let topLeft = CGPoint(x: 2, y: 1)
let topRight = CGPoint(x: 3, y: 1)
let squareCenter = CGPoint(x: 2.5, y: 1.5)
// First rotate around the center of the 3 x 3 square
let centerOfRotation = CGPoint(x: 1.5, y: 1.5)
let tl1 = rotatePoint(target: topLeft, aroundOrigin: centerOfRotation, byDegrees: -90) // (x 1 y 1)
let tr1 = rotatePoint(target: topRight, aroundOrigin: centerOfRotation, byDegrees: -90) // (x 1 y 0)
let sc1 = rotatePoint(target: squareCenter, aroundOrigin: centerOfRotation, byDegrees: -90) // (x 1.5 y 0.5)
// Now rotate the 1x1 square the other way around new position of square center
let tl2 = rotatePoint(target: tl1, aroundOrigin: sc1, byDegrees: 90) // (x 1 y 0)
let tr2 = rotatePoint(target: tr1, aroundOrigin: sc1, byDegrees: 90) // (x 2 y 0)
Note: As #MBo noted in the comments, if your cell is always 1x1, it is sufficient to rotate the center of your cell and then just add and subtract the 0.5 offsets to find the four corners.
You can just transform you view like so
yourView.transform = CGAffineTransform(rotationAngle: -CGFloat.pi/2)
EDIT:
My bad.
Use CGFloat.pi when working with degrees
print(rotatePoint1(topLeft, -CGFloat.pi/2))
Use sin and cos functions directly
let s = sin(degrees)
let c = cos(degrees)
iOS coordinate system is a bit flipped compared to standard one so you will have to adjust the angle (you can see simulation)
print(rotatePoint1(topLeft, -CGFloat.pi/2)) // (1.0000000000000002, -2.0)
print(rotatePoint1(topRight, -CGFloat.pi/2)) // (1.0000000000000002, -3.0)

Place UICollectionViewCells along an arc (UIBezierPath)

I would like to place UICollectionViewCells along the circular arc (UIBezierPath). Please suggest a formula to calculate center of these cells.
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
}
All these cells are of same constant size, say (100, 100) and x distance (horizontal) between two cells is also constant, say 20.
let startAngle: Double = 0
let endAngle: Double = 180
var arcRadius: CGFloat = 414 // Actually updated dynamically when layout changes
var arcCenter: CGPoint {
get {
return CGPoint(x: contentView.bounds.width/2, y: contentView.bounds.height/2 - arcRadius)
}
}
func drawArc() {
contentView.layer.sublayers?.forEach {
if $0 is CAShapeLayer {
$0.removeFromSuperlayer()
}
}
// Create an arc
let path = UIBezierPath(arcCenter: arcCenter,
radius: arcRadius,
startAngle: degreesToRadians(value: startAngle),
endAngle: degreesToRadians(value: endAngle),
clockwise: true)
// Create a shape layer and add path
let shapeLayer = createShapeLayer()
shapeLayer.path = path.cgPath
contentView.layer.addSublayer(shapeLayer)
}
It can be achieved using parametric equation of circle. Here is my code -
Create a Circle object and then call its func point(startAngle: forItemAtIndex:) to find the center point for these cells.
struct Circle {
var center: CGPoint
var radius: CGFloat
func point(startAngle: Double, forItemAtIndex index: Int) -> CGPoint {
/*
This is based on parametric equation of a circle with center (cx, cy) -
x = cx + radius*cos(angle)
y = cy + radius*sin(angle)
*/
var deltaX: CGFloat = 122 // Fixed horizontal (x) distance between 2 cells' centers
var deltaY = radius - sqrt(pow(radius, 2) - pow(deltaX, 2)) // formula to calculate vertical distance between cells' centers
var actualY = center.y + radius - deltaY
var deltaAngleRadians = asin((actualY - center.y)/radius)
var deltaAngle = radiansToDegrees(value: deltaAngleRadians)
var stepAngle: Double = deltaAngle - startAngle
var actualAngle = startAngle + (Double(index)*stepAngle)
let viewCenterX = radius * cos(degreesToRadians(value: actualAngle)) + center.x
let viewCenterY = radius * sin(degreesToRadians(value: actualAngle)) + center.y
var position = CGPoint(x: viewCenterX, y: viewCenterY)
return position
}
func bezierPath(startAngle: Double = 0, endAngle: Double = 180, clockwise: Bool = true) -> UIBezierPath {
return UIBezierPath(arcCenter: center, radius: radius, startAngle: degreesToRadians(value: startAngle), endAngle: degreesToRadians(value: endAngle), clockwise: true)
}
func degreesToRadians(value: Double) -> CGFloat {
return CGFloat(value * .pi / 180.0)
}
func radiansToDegrees(value: CGFloat) -> Double {
return Double(value * 180.0 / .pi)
}
}

How to draw a circle diagram looking like a clock face using UIKit

I've been trying to figure this out for too long. With the help of this blog I managed to draw the diagram itself, but it can't show me any data, because it seems like my idea of creating a context array is not possible and I can have only one context per view, is that right? So how can I change the color of each marker individually? I've seen the solution using SpriteKit, but I don't know anything at all about SpriteKit.
func degree2Radian(a:CGFloat)->CGFloat {
let b = CGFloat(M_PI) * a/180
return b
}
override func draw(_ rect: CGRect) {
color.set()
pathForCircleCenteredAtPoint(midPoint: circleCenter, withRadius: circleRadius).stroke()
color = UIColor.white
color.set()
pathForCircleCenteredAtPoint(midPoint: CGPoint(x: bounds.midX, y: bounds.midY), withRadius: circleRadius).fill()
color = UIColor(red: 0.93, green: 0.93, blue: 0.94, alpha: 1)
color.set()
let ctx = UIGraphicsGetCurrentContext()
for i in 0...100 {
secondMarkers(ctx: ctx!, x: circleCenter.x, y: circleCenter.y, radius: circleRadius - 4, sides: 100, color: color)
}
diagramArray[0].strokePath()
}
func degree2radian(a:CGFloat)->CGFloat {
let b = CGFloat(M_PI) * a/180
return b
}
func circleCircumferencePoints(sides:Int,x:CGFloat,y:CGFloat,radius:CGFloat,adjustment:CGFloat=0)->[CGPoint] {
let angle = degree2radian(a: 360/CGFloat(sides))
let cx = x // x origin
let cy = y // y origin
let r = radius // radius of circle
var i = sides
var points = [CGPoint]()
while points.count <= sides {
let xpo = cx - r * cos(angle * CGFloat(i)+degree2radian(a: adjustment))
let ypo = cy - r * sin(angle * CGFloat(i)+degree2radian(a: adjustment))
points.append(CGPoint(x: xpo, y: ypo))
i -= 1;
}
return points
}
func secondMarkers(ctx:CGContext, x:CGFloat, y:CGFloat, radius:CGFloat, sides:Int, color:UIColor) {
// retrieve points
let points = circleCircumferencePoints(sides: sides,x: x,y: y,radius: radius)
// create path
// determine length of marker as a fraction of the total radius
var divider:CGFloat = 1/16
//for p in points {
let path = CGMutablePath()
divider = 1/10
let xn = points[counter].x + divider * (x-points[counter].x)
let yn = points[counter].y + divider * (y-points[counter].y)
// build path
path.move(to: CGPoint(x: points[counter].x, y: points[counter].y))
//path, nil, p.x, p.y)
path.addLine(to: CGPoint(x: xn, y: yn))
//CGPathAddLineToPoint(path, nil, xn, yn)
path.closeSubpath()
// add path to context
ctx.addPath(path)
ctx.setStrokeColor(color.cgColor)
ctx.setLineWidth(2.0)
//ctx.strokePath()
diagramArray.append(ctx)
counter += 1
//}
// set path color
}
So basically I'm trying to append context for each marker to an array, but when I draw one element of this array, it draws the whole diagram. This is what I need to achieve.
You shouldn't need to create more than one CGContext - you should just be reusing the same one to draw all graphics. Also, your method to calculate the secondMarkers seems unnecessarily complex. I believe this does what you want:
private func drawTicks(context: CGContext, tickCount: Int, center: CGPoint, startRadius: CGFloat, endRadius: CGFloat, ticksToColor: Int) {
for i in 0 ... tickCount {
let color: UIColor = i < ticksToColor ? .blue : .lightGray
context.setStrokeColor(color.cgColor)
let angle = .pi - degree2Radian(a: (CGFloat(360.0) / CGFloat(tickCount)) * CGFloat(i))
let path = CGMutablePath()
path.move(to: circleCircumferencePoint(center: center, angle: angle, radius: startRadius))
path.addLine(to: circleCircumferencePoint(center: center, angle: angle, radius: endRadius))
context.addPath(path)
context.strokePath()
}
}
private func circleCircumferencePoint(center: CGPoint, angle: CGFloat, radius: CGFloat) -> CGPoint {
return CGPoint(x: radius * sin(angle) + center.x, y: radius * cos(angle) + center.y)
}

SpriteKit: Uniform Circular Motion

I'm trying to move a sprite in a circular motion using a reusable function (that I need to use on different objects).
The first result is given by this function:
func circularMotion(angle: CGFloat, radius: CGFloat, center: CGPoint) -> CGPoint{
let x = radius * cos(angle) + center.x
let y = radius * sin(angle) + center.y
return CGPoint(x: x, y: y)
}
And then I update the position of my sprite like this:
override func update(currentTime: NSTimeInterval) {
player.position = circularMotion(ang, radius: 200, center: CGPoint(x: size.width/2, y: size.height/2))
ang += speed
}
Where ang (the angle) and speed (0.05) are two initialized properties.
And this works pretty fine, but the actual problem is that I need to have the velocity of the sprite that rotates, since it has a PhysicBody.
My approach, though, changes only the position of the sprite. So I decided to do something like this:
override func update(currentTime: NSTimeInterval) {
let prevCirc = circularMotion(ang, radius: 200, center: CGPoint(x: size.width/2, y: size.height/2))
let Circ = circularMotion(ang + 0.05, radius: 200, center: CGPoint(x: size.width/2, y: size.height/2))
let v = CGVector(dx: Circ.x - prevCirc.x, dy: Circ.y - prevCirc.y )
player.physicsBody!.velocity = v
}
I just calculated the difference of position, finding the perpendicular Vector respect to the position vector. This still "works", the body follows a circular motion, but it has not the radius and center given (it's a smaller circle).
I know it's because of the length of the vector that it gives this result.
So the real question is, having the radius and the center of the circle I want (and the speed of how the angle changes) how can I calculate the perpendicular velocity so that it gives me a motion with those properties?

Resources