Why is my CGPoint returning other characters? - ios

Hi there I am trying to add a label to the centre of the thumb on a custom UIControl. I need to find the centre on the thumb.
I have a function that I'd like to return the thumb centre as CGPoint.
The function is returning really strange coordinates that look like:
thumb center coordinates: (5.534284385e-315, 5.356796015e-315)
Why am I getting these values?
Does UIControl have a way to return thumbRect like UISlider? Thank you!!
open func getThumbCenterRect() -> CGPoint {
return thumbCenter
}
fileprivate var thumbCenter: CGPoint {
var thumbCenter = viewCenter
let angle = rtlAwareAngleRadians(thumbAngle)
thumbCenter.x += CGFloat(cos(angle)) * controlRadius
thumbCenter.y += CGFloat(sin(angle)) * controlRadius
return thumbCenter
}
#IBAction func onSlideChange(_ sender: MTCircularSlider) {
let thumbCenter = knobWithLabelView.getThumbCenterRect()
print("thumb centre coordinates: \(thumbCenter)")
}
output:
thumb coordinates: (5.534284385e-315, 5.356796015e-315)

It's scientific notation. 5.534284385e-315 means 5.534284385 * 10^-315, which is a really small number (a 0., followed by 314 zeroes, followed by 55342...`).
In other words, it's a really small number, that's really close to 0 but not quite. Most probably, you'll want to do some rounding to get values that snap to more reasonable accepted values.

Related

SpriteKit stop spinning wheel in a defined angle

I have a spinning wheel rotating at an angular speed ω, no acceleration involved, implemented with SpriteKit.
When the user push a button I need to slowly decelerate the wheel from the current angle ∂0 and end-up in a specified angle (lets call it ∂f).
I created associated to it a mass of 2.
I already tried the angularDamping and the SKAction.rotate(toAngle: duration:) but they do not fit my needs because:
With the angularDamping I cannot specify easy the angle ∂f where I want to end up.
With the SKAction.rotate(toAngle: duration:) I cannot start slowing down from the current rotation speed and it doesn't behave natural.
The only remaining approach I tried is by using the SKAction.applyTorque(duration:).
This sounds interesting but I have problems calculating the formula to obtain the correct torque to apply and especially for the inertia and radius of the wheel.
Here is my approach:
I'm taking the starting angular velocity ω as:
wheelNode.physicsBody?.angularVelocity.
I'm taking the mass from wheelNode.physicsBody?.mass
The time t is a constant of 10 (this means that in 10 seconds I want the wheel decelerating to the final angle ∂f).
The deceleration that I calculated as:
let a = -1 * ω / t
The inertia should be: let I = 1/2 * mass * pow(r, 2)*. (see notes regarding the radius please)
Then, finally, I calculated the final torque to apply as: let t = I * a (taking care that is opposite of the current angular speed of the wheel).
NOTE:
Since I don't have clear how to have the radius of the wheel I tried to grab it both from:
the wheelNode.physicsBody?.area as let r = sqrt(wheelNode.physicsBody?.area ?? 0 / .pi)
by converting from pixel to meters as the area documentation says. Then I have let r = self.wheelNode.radius / 150.
Funny: I obtain 2 different values :(
UNFORTUNATLY something in this approach is not working because so far I have no idea how to end up in the specified angle and the wheel doesn't stop anyway as it should (or the torque is too much and spins in the other direction, or is not enough). So, also the torque applied seems to be wrong.
Do you know a better way to achieve the result I need? Is that the correct approach? If yes, what's wrong with my calculations?
Kinematics makes my head hurt, but here you go. I made it to where you can input the amount of rotations and the wheel will rotate that many times as its slowing down to the angle you specify. The other function and extension are there to keep the code relatively clean/readable. So if you just want one giant mess function go ahead and modify it.
• Make sure the node's angularDampening = 0.0
• Make sure the node has a circular physicsbody
// Stops a spinning SpriteNode at a specified angle within a certain amount of rotations
//NOTE: Node must have a circular physicsbody
// Damping should be from 0.0 to 1.0
func decelerate(node: SKSpriteNode, toAngle: CGFloat, rotations: Int) {
if node.physicsBody == nil { print("Node doesn't have a physicsbody"); return } //Avoid crash incase node's physicsbody is nil
var cw:CGFloat { if node.physicsBody!.angularVelocity < CGFloat(0.0) { return -1.0} else { return 1.0} } //Clockwise - using int to reduce if statments with booleans
let m = node.physicsBody!.mass // Mass
let r = CGFloat.squareRoot(node.physicsBody!.area / CGFloat.pi)() // Radius
let i = 0.5 * m * r.squared // Intertia
let wi = node.physicsBody!.angularVelocity // Initial Angular Velocity
let wf:CGFloat = 0 // Final Angular Velocity
let ti = CGFloat.unitCircle(node.zRotation) // Initial Theta
var tf = CGFloat.unitCircle(toAngle) // Final Theta
//Correction constant based on rate of rotation since there seems to be a delay between when the action is calcuated and when it is run
//Without the correction the node stops a little off from its desired stop angle
tf -= 0.00773889 * wi //Might need to change constn
let dt = deltaTheta(ti, tf, Int(cw), rotations)
let a = -cw * 0.5 * wi.squared / abs(dt) // Angular Acceleration - cw used to determine direction
print("A:\(a)")
let time:Double = Double(abs((wf-wi) / a)) // Time needed to stop
let torque:CGFloat = i * a // Torque needed to stop
node.run(SKAction.applyTorque(torque, duration: time))
}
func deltaTheta(_ ti:CGFloat, _ tf:CGFloat, _ clockwise: Int, _ rotations: Int) -> CGFloat {
let extra = CGFloat(rotations)*2*CGFloat.pi
if clockwise == -1 {
if tf>ti { return tf-ti-2*CGFloat.pi-extra }else{ return tf-ti-extra }
}else{
if tf>ti { return tf-ti+extra }else{ return tf+2*CGFloat.pi+extra-ti }
}
}
}
extension CGFloat {
public var squared:CGFloat { return self * self }
public static func unitCircle(_ value: CGFloat) -> CGFloat {
if value < 0 { return 2 * CGFloat.pi + value }
else{ return value }
}
}

Calculate bearing in MKMapView gives wrong value while crossing 180 meridian

I need to draw lines to demonstrate transportation of goods on apple maps. To clarify start- and end-point, I draw a little arrowhead on the destination side.The arrowhead is drawn separately but it is reversed in one case.
>-->-->-->-
instead of
<--<--<--<-
I am using MKMapView and MKPolyline to draw lines. I am using MKOverlay to add direction arrows. The steps I follow are,
calculate bearing of
Source : CLLocationCoordinate2D(latitude: -33.8392932, longitude: 151.21519799999999)
Destination: CLLocationCoordinate2D(latitude: 39.645516999999998, longitude: -104.598724)
using the following function
open static func getDirectionOf( _ supplyLineWithCoordinates: [CLLocationCoordinate2D]) -> CGFloat {
guard let sourceCoordniate = supplyLineWithCoordinates.first,
let destinationCoordniate = supplyLineWithCoordinates.last else {
fatalError("Coordinates of supply line not found")
}
let sourcePoint: MKMapPoint = MKMapPointForCoordinate(sourceCoordniate)
let destinationPoint: MKMapPoint = MKMapPointForCoordinate(destinationCoordniate)
let x: Double = destinationPoint.x - sourcePoint.x
let y: Double = destinationPoint.y - sourcePoint.y
var arrowDirection = CGFloat(fmod(atan2(y, x), 360.0))
if arrowDirection < 0.0 {
arrowDirection += 2 * .pi
}
return arrowDirection
}
Rotate the arrow image and add it as the map overlay. The directions are calculated correctly in most of the cases, however, when I select the line shown below the direction is displayed 180 opposite. It starts from Sydney, Australia and ends in Denver, US
When trying to display the region with this two locations in mapView.setVisibleMapRect these region is not displayed, mapview tries to display region starting from Sydney (Australia) to Denver(US) through Asia and Europe, while it should display the map area I have attached above. If you have suggestions for optimisation, feel free to mention it.
I think this might be the reason, the direction should be calculated along the red line but it being calculated along the green line. Both lines are drawn by connecting same location coordinates in map. Any known workaround for this?
I solved it in a dirty way by converting coordinate to CGPoint and then calculating bearing between Points.
let destinationPoint = mapView.convert(destination, toPointTo: nil)
let sourcePoint = mapView.convert(source, toPointTo: nil)
let bearing = atan2(sourcePoint.y - destinationPoint.y, sourcePoint.x - destinationPoint.x) - .pi
Caution: This calculation will go wrong when map is rotated

UIBezierPath Percent of Length at Point

I'm building an app that features some graphical manipulation. I'm storing shapes as UIBezierPaths, and I want to allow users to touch points along the line to create saved locations. Using the wonderful answer to this question, and more specifically, this project, I'm able to place a point on a line knowing the percentage of its length the point rests on. This is half of my problem.
I want a way to take a point on a path, and derive the percent of its length.
My math-fu is extremely weak. I've studied bezier curves but I simply don't have the math to understand it.
I would humbly submit that "go back and learn geometry and trigonometry" is a correct answer, but sadly one I don't have time for at present. What I need is a way to fill in this method:
- (CGFloat)percentOfLengthAtPoint:(CGPoint)point onPath:(UIBezierPath*)path
Any help appreciated!
I have working code that solves my problem. I'm not particularly proud of it; the overall technique is essentially a brute-force attack on a UIBezierPath, which is kind of funny if you think about it. (Please don't think about it).
As I mentioned, I have access to a method that allows me to get a point from a given percentage of a line. I have taken advantage of that power to find the closest percentage to the given point by running through 1000 percentage values. To wit:
Start with a CGPoint that represents where on the line the user touched.
let pointA = // the incoming CGPoint
Run through the 0-1 range in the thousands. This is the set of percentages we're going to brute-force and see if we have a match. For each, we run pointAtPercentOfLength, from the linked project above.
var pointArray:[[String:Any]] = []
for (var i:Int = 0; i <= 1000; i++) {
let value = CGFloat(round((CGFloat(i) / CGFloat(1000)) * 1000) / 1000)
let testPoint = path.pointAtPercentOfLength(value)
let pointB = CGPoint(x: floor(testPoint.x), y: floor(testPoint.y))
pointArray.append(["point" : pointB, "percent" : value])
}
That was the hard part. Now we take the returning values and calculate the distance between each point and the touched point. Closest one is our winner.
// sort the damned array by distance so we find the closest
var distanceArray:[[String:Any]] = []
for point in pointArray {
distanceArray.append([
"distance" : self.distanceFrom(point["point"] as! CGPoint, point2: pointA),
"point" : point["point"],
"percent" : point["percent"] as! CGFloat
])
}
Here's the sorting function if you're interested:
func distanceFrom(point1:CGPoint, point2:CGPoint) -> CGFloat {
let xDist = (point2.x - point1.x);
let yDist = (point2.y - point1.y);
return sqrt((xDist * xDist) + (yDist * yDist));
}
Finally, I sort the array by the distance of the values, and pick out the winner as our closest percent.
let ordered = distanceArray.sort { return CGFloat($0["distance"] as! CGFloat) < CGFloat($1["distance"] as! CGFloat) }
ordered is a little dictionary that includes percent, the correct value for a percentage of a line's length.
This is not pretty code, I know. I know. But it gets the job done and doesn't appear to be computationally expensive.
As a postscript, I should point to what appears to be a proper resource for doing this. During my research I read this beautiful article by David Rönnqvist, which included an equation for calculating the percentage distance along a path:
start⋅(1-t)3 + 3⋅c1⋅t(1-t)2 + 3⋅c2⋅t2(1-t) + end⋅t3
I was just about to try implementing that before my final solution occurred to me. Math, man. I can't even brain it. But if you're more ambitious than I, and wish to override my 30 lines of code with a five-line alternative, everyone would appreciate it!
I think your approach is sound, but you could do this far more efficiently.
Instead of creating an two arrays of dicts (with a thousand elements each) and then sorting the array - just use a while loop to move from 0.0 to 1.0, calculate the distance to the touch point and keep track of the minimum distance.
For example:
var t:CGFloat = 0.0
let step:CGFloat = 0.001
var minDistance:CGFloat = -1.0
var minPoint:CGPoint = CGPointZero
var minT:CGFloat = -1;
while (t<1.0) {
let point = pointAtPercentOfLength(t)
let distance:CGFloat = self.distanceFrom(point, point2: pointA)
if (minDistance == -1.0 || distance < minDistance) {
minDistance = distance
minPoint = point
minT = t
}
t += step
}
print("minDistance: \(minDistance) minPoint: \(minPoint.x) \(minPoint.y) t\(minT)\n")

How to attach sprites that collide?

I essentially want the "sprites" to collide when they stick together. However, I don't want the "joint" to be rigid; I essentially want the sprites to be able to move around as long as they are in contact with each other. Imagine two circles connected, and you can move one circle around the other, as long as it remains in contact.
I found this question: How to make one body stick to another moving object in SpriteKit and a lot of other resources that explain how to make sprites stick upon collision, but they all use SKJoints, which are rigid are not really flexible.
I guess another way to phrase it would be to say that I want the sprites to stick, but I want them to be able to "slide" on each other.
Well, I can think of one workaround, but this wouldn't work with non-normal polygons.
Sticking (pun unintended) with your circles example, what if you lock the position of the circle?
let circle1 = center circle
let circle2 = movable circle
Knowing the width of both circles, you can place in the update function that the position should be exactly the distance of:
((circle1.frame.width / 2) + (circle2.frame.width / 2))
If you're up to it, here's some code to help you on your way.
override func update(currentTime: CFTimeInterval) {
{
let distance = hypotf(Float(circle1.position.x - circle2.position.x), Float(circle1.position.y - circle2.position.y))
//calculate circle distances from each other
let radius = ((circle1.frame.width / 2) + (circle2.frame.width / 2))
//distance of circle positions
if distance != radius
{
//if distance is less or more than radius
let pointA = circle1.position
let pointB = circle2.position
let pointC = CGPointMake(pointB.x + 2, pointB.y)
let angle_ab = atan2(pointA.y - pointB.y, pointA.x - pointB.x)
let angle_cb = atan2(pointC.y - pointB.y, pointC.x - pointB.x)
let angle_abc = angle_ab - angle_cb
//get angle of circles from each other using atan2
let vectorx = cos(angle_abc)
let vectory = sin(angle_abc)
//convert angle into vectors
let x = circle1.position.x + radius * vectorx
let y = circle1.position.y + radius * vectory
//get new coordinates from vector, radius and center circle position
circle2.position = CGPointMake(x, y)
//set new position
}
}
Well you need to write code to make sure the movable circle, is well movable.
But, this should work.
I haven't tested this yet though, and I haven't even learned geometry let alone trig in school yet.
If I'm reading your question as you intended it, you can still use joints- just create actions with Inverse Kinematic constraints that allow rotation and translation around the contacting circles' joint.
https://developer.apple.com/library/prerelease/ios/documentation/SpriteKit/Reference/SKAction_Ref/index.html#//apple_ref/doc/uid/TP40013017-CH1-SW72

ios - Spritekit - How to calculate the distance between two nodes?

I have two sknodes on the screen. What is the best way to calculate the distance ('as the crow flies' type of distance, I don't need a vector etc)?
I've had a google and search on here and can't find something that covers this (there aren't too many threads on stackoverflow about sprite kit)
Here's a function that will do it for you. This is from an Apple's Adventure example code:
CGFloat SDistanceBetweenPoints(CGPoint first, CGPoint second) {
return hypotf(second.x - first.x, second.y - first.y);
}
To call this function from your code:
CGFloat distance = SDistanceBetweenPoints(nodeA.position, nodeB.position);
Another swift method, also since we are dealing with distance, I added abs() so that the result would always be positive.
extension CGPoint {
func distance(point: CGPoint) -> CGFloat {
return abs(CGFloat(hypotf(Float(point.x - x), Float(point.y - y))))
}
}
Ain't swift grand?
joshd and Andrey Gordeev are both correct, with Gordeev's solution spelling out what the hypotf function does.
But the square root function is an expensive function. You'll have to use it if you need to know the actual distance, but if you only need relative distance, you can skip the square root. You may want to know which sprite is closest, or furtherest, or just if any sprites are within a radius. In these cases just compare distance squared.
- (float)getDistanceSquared:(CGPoint)p1 and:(CGPoint)p2 {
return pow(p2.x - p1.x, 2) + pow(p2.y - p1.y, 2);
}
To use this for calculating if any sprites are within a radius from the center of the view in the update: method of an SKScene subclass:
-(void)update:(CFTimeInterval)currentTime {
CGFloat radiusSquared = pow (self.closeDistance, 2);
CGPoint center = self.view.center;
for (SKNode *node in self.children) {
if (radiusSquared > [self getDistanceSquared:center and:node.position]) {
// This node is close to the center.
};
}
}
Swift:
extension CGPoint {
/**
Calculates a distance to the given point.
:param: point - the point to calculate a distance to
:returns: distance between current and the given points
*/
func distance(point: CGPoint) -> CGFloat {
let dx = self.x - point.x
let dy = self.y - point.y
return sqrt(dx * dx + dy * dy);
}
}
Pythagorean theorem:
- (float)getDistanceBetween:(CGPoint)p1 and:(CGPoint)p2 {
return sqrt(pow(p2.x-p1.x,2)+pow(p2.y-p1.y,2));
}

Resources