Spritekit SKRange Zrotation SKConstraint lower/upper limit minus vs positive Radians - sprite-kit

I am trying to figure out how I should use SKRange with Zrotation when I need to go over 180 degrees.
This is what I have;
let higherlimit = CGFloat( 170 * 3.14 / 180 )
let lowerlimit = CGFloat( 0 * 3.14 / 180 )
let rotationRange = SKRange(lowerLimit: lowerlimit, upperLimit: higherlimit )
let rotationConstraint = SKConstraint.zRotation(rotationRange)
wheel.constraints = [rotationConstraint]
The above works great, I cannot turn the wheel over 170 degrees. However when I try to the following;
let higherlimit = CGFloat( -90 * 3.14 / 180 )
let lowerlimit = CGFloat( 0 * 3.14 / 180 )
let rotationRange = SKRange(lowerLimit: lowerlimit, upperLimit: higherlimit )
let rotationConstraint = SKConstraint.zRotation(rotationRange)
wheel.constraints = [rotationConstraint]
The above doesn't work, it will lock the wheel in a certain angle. I am aware about positive and negative radians but I cannot figure out how to accomplish the above.
Thanks very much in advance,
w//

I succeeded doing this by adding an extension;
depending on your 'flavour' ..
extension CGFloat {
var toDegreesNormalizedNegative : CGFloat {
let rad = -self
let deg = rad * 180 / .pi
return CGFloat(Int ( deg >= 0 ? deg : deg + 360 ))
}
}
extension CGFloat {
var toDegreesNormalized : CGFloat {
let deg = self * 180 / .pi
return CGFloat(Int ( deg >= 0 ? deg : deg + 360 ))
}
}
then simply check in update(_ .. or touchesBegan or etc that it wont go over the required limit(s)

Related

simd_quatF to euler angle

Im trying to convert my quats to euler, but out of x/y/z components, only my X has accurate value and y/z is incorrect :- ( can any1 have a look/help ?
func quatToEulerAngles(_ quat: simd_quatf) -> SIMD3<Double>{
var angles = SIMD3<Double>();
let qfloat = quat.vector
let q = SIMD4<Double>(Double(qfloat.x),Double(qfloat.y),Double(qfloat.z), Double(qfloat.w))
// roll (x-axis rotation)
let sinr_cosp : Double = 2.0 * (q.w * q.x + q.y * q.z);
let cosr_cosp : Double = 1.0 - 2.0 * (q.x * q.x + q.y * q.y);
angles.x = atan2(sinr_cosp, cosr_cosp);
// pitch (y-axis rotation)
let sinp : Double = 2 * (q.w * q.y - q.z * q.x);
if (abs(sinp) >= 1){
angles.y = copysign(Double.pi / 2, sinp); // use 90 degrees if out of range
}
else{
angles.y = asin(sinp);
}
// yaw (z-axis rotation)
let siny_cosp : Double = 2 * (q.w * q.z + q.x * q.y);
let cosy_cosp : Double = 1 - 2 * (q.y * q.y + q.z * q.z);
angles.z = atan2(siny_cosp, cosy_cosp);
return angles;
}
Wiki example converted to swifht.
TIA
My solution would be to let the (SceneKit) library do it:
func quatToEulerAngles(_ quat: simd_quatf) -> SIMD3<Float>{
let n = SCNNode()
n.simdOrientation = quat
return n.simdEulerAngles
}
I took a look at and converted it to Swift,
https://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/
It works for me.
func quatToEulerAngles(_ quat: simd_quatf) -> SIMD3<Float>{
var angles = SIMD3<Float>();
let qfloat = quat.vector
// heading = x, attitude = y, bank = z
let test = qfloat.x*qfloat.y + qfloat.z*qfloat.w;
if (test > 0.499) { // singularity at north pole
angles.x = 2 * atan2(qfloat.x,qfloat.w)
angles.y = (.pi / 2)
angles.z = 0
return angles
}
if (test < -0.499) { // singularity at south pole
angles.x = -2 * atan2(qfloat.x,qfloat.w)
angles.y = -(.pi / 2)
angles.z = 0
return angles
}
let sqx = qfloat.x*qfloat.x;
let sqy = qfloat.y*qfloat.y;
let sqz = qfloat.z*qfloat.z;
angles.x = atan2(2*qfloat.y*qfloat.w-2*qfloat.x*qfloat.z , 1 - 2*sqy - 2*sqz)
angles.y = asin(2*test)
angles.z = atan2(2*qfloat.x*qfloat.w-2*qfloat.y*qfloat.z , 1 - 2*sqx - 2*sqz)
return angles
}

Swift 5 timer countdown, while a value is in a range

I am trying to Countdown from, say, 5 to 0 WHILE the users bearing is between 20 and -20 radian from true north. If bearing remains between 20 and -20 for 5 seconds, segue to new controller. I've tried everything and I have mostly given up.
I've tried a bunch of things, including scheduleTimer(withInterval 1.0){}
I am new to Swift. I was unable to invalidate timer.
Here is the getting user heading
#objc func getUserHeading(_ latitude : Double, _ longitude : Double, _ userLocation : CLLocation){
let lat1 = (latitude) * .pi / 180
let long1 = (longitude) * .pi / 180
let lat2 = (yourLocation.coordinate.latitude) * .pi / 180
let long2 = (yourLocation.coordinate.longitude) * .pi / 180
print(lat1, long1, lat2, long2)
let dLon = long2 - long1
let y = sin(dLon) * cos(lat2)
let x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon)
var radiansBearing = atan2(y, x)
if radiansBearing < 0 {
radiansBearing += 2 * Double.pi
}
theta = (radiansBearing * 180 / .pi) - (angle)
print(theta)
if (theta < 20 && theta > = -20 ){
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
print("Timer Fired")
runCount += 1
if(theta > 20 && theta < -20)
{
self.timer.invalidate()
self.timerStarted = false
}
if (runCount == 5)
{
self.timer.invalidate()
self.segueToNewController()
}
}
else{
timer.invalidate()
timerStarted = false
}
}
This is probably a semantics thing, but I swear I have spent DAYS on this and I feel like I am getting no where.
Issues so far
It does trigger when between 20 and -20 but it triggers immediately and doesn't wait for 5 seconds
It triggers MANY times, which means the segue for the next controller is called many times (it caused like 5k writes to my firestore)
If you guys need anything more, please do let me know, will update question with more code.
All help appreciated! End of my wits here
There are some issues with your code
You have 2 contradicting if clauses, first you check if theta is inside the interval and then inside the if clause you check if the same value is outside the interval which will never happen.
The last else looks misplaced
There are variables you change but never read
I would rewrite it something like this (from the first if)
if theta < 20 && theta >= -20 {
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
print("Timer Fired")
runCount += 1
if runCount == 5 {
timer.invalidate()
}
//theta = ... here you need to somehow update this property to see if it has changed
if theta > 20 && theta < -20 {
timer.invalidate()
}
}
} else {
timer?.invalidate()
}
Note that I removed the timerStarted variable because I didn't see the point of having it and also not that parentheses are not needed around if conditions

swift trigonometric functions (cos, tan, arcsin, arcos, arctan)

hello I have to differenciate calculations in degrees and I have the following code but I doesn't return me the exact values. The only one right is the value of sin90 in degree = 1
//////***** DEGREES ******//////
var sinus = sin(90.0 * M_PI / 180)
var cosinus = cos(90 * M_PI / 180)
var tangent = tan(90 * M_PI / 180)
var arcsinus = asin(90 * M_PI / 180)
var arcosinus = acos(90 * M_PI / 180)
var arctangent = atan(90 * M_PI / 180)
What is the right operation to return the exact value for every operation in degree for cos, tan and their ARC functions?
This is more a math problem than a Swift problem:
let sinus = sin(90.0 * Double.pi / 180)
print("Sinus \(sinus)")
let cosinus = cos(90 * Double.pi / 180)
print("Cosinus \(cosinus)")
let tangent = tan(90 * Double.pi / 180)
print("Tangent \(tangent)")
prints
Sinus 1.0
Cosinus 6.12323399573677e-17
Tangent 1.63312393531954e+16
Sinus of 90 degrees is 1 (correct)
Cosinus of 90 degrees is 0. The value 6e-17 is a very very small value, any sensible rounding would consider it equal to zero (correct). The fact that you can't get exactly zero is due to rounding errors in the calculation.
Tangent of 90 degrees is not defined (sin/tan = 1/0, division by zero is not defined). If we had precise calculations, you would probably get an infinity. In this case we have 1 divided by 6e-17, which becomes a large number 1.6e16. The result is correct.
Regarding the inverse functions, note one thing - their parameters are neither in degrees or radians. Their result is in degrees/radians, for example:
let arcsinus = asin(1.0) * 180 / Double.pi
print("Arcsinus \(arcsinus)")
prints
Arcsinus 90.0
Swift 4 works out with a modified syntax:
let sinus = sin(90.0 * Double.pi / 180)
let cosinus = cos(90 * Double.pi / 180)
let tangent = tan(90 * Double.pi / 180)
let arcsinus = asin(1) * 180/ Double.pi
let arcosinus = acos(0) * 180/ Double.pi
let arctangent = atan(1) * 180/ Double.pi

Draw SceneKit object between two points

Having made some progress in the geometry side of things I'm moving on to putting together an entire scene. That scene has a couple dozen objects, each defined by a bounding cube whose corners are specified by two SCNVector3s (originally two sets of x,y,z).
Here's an example of what I have so far - it's an 11-element log-periodic antenna, like the old school TV antennas from the 70s. Each of the grey lines is an "element", typically made of aluminum rod. I used SCNCylinders from +ve to -ve Y and the entire thing is less than 100 lines (SK is pretty amazing).
The problem is what happens if the elements are not symmetrical across X and thus the SCNCylinder has to be rotated. I found this example, but I can't understand the specifics... it appears to take advantage of the fact that a sphere is symmetric so angles kind of "go away".
Does anyone have a general function that will take two 3D points and return the SCNVector3 suitable for setting the node's eulerAngle, or a similar solution?
Both solutions mentioned above work very well and I can contribute third solution to this question.
//extension code starts
func normalizeVector(_ iv: SCNVector3) -> SCNVector3 {
let length = sqrt(iv.x * iv.x + iv.y * iv.y + iv.z * iv.z)
if length == 0 {
return SCNVector3(0.0, 0.0, 0.0)
}
return SCNVector3( iv.x / length, iv.y / length, iv.z / length)
}
extension SCNNode {
func buildLineInTwoPointsWithRotation(from startPoint: SCNVector3,
to endPoint: SCNVector3,
radius: CGFloat,
color: UIColor) -> SCNNode {
let w = SCNVector3(x: endPoint.x-startPoint.x,
y: endPoint.y-startPoint.y,
z: endPoint.z-startPoint.z)
let l = CGFloat(sqrt(w.x * w.x + w.y * w.y + w.z * w.z))
if l == 0.0 {
// two points together.
let sphere = SCNSphere(radius: radius)
sphere.firstMaterial?.diffuse.contents = color
self.geometry = sphere
self.position = startPoint
return self
}
let cyl = SCNCylinder(radius: radius, height: l)
cyl.firstMaterial?.diffuse.contents = color
self.geometry = cyl
//original vector of cylinder above 0,0,0
let ov = SCNVector3(0, l/2.0,0)
//target vector, in new coordination
let nv = SCNVector3((endPoint.x - startPoint.x)/2.0, (endPoint.y - startPoint.y)/2.0,
(endPoint.z-startPoint.z)/2.0)
// axis between two vector
let av = SCNVector3( (ov.x + nv.x)/2.0, (ov.y+nv.y)/2.0, (ov.z+nv.z)/2.0)
//normalized axis vector
let av_normalized = normalizeVector(av)
let q0 = Float(0.0) //cos(angel/2), angle is always 180 or M_PI
let q1 = Float(av_normalized.x) // x' * sin(angle/2)
let q2 = Float(av_normalized.y) // y' * sin(angle/2)
let q3 = Float(av_normalized.z) // z' * sin(angle/2)
let r_m11 = q0 * q0 + q1 * q1 - q2 * q2 - q3 * q3
let r_m12 = 2 * q1 * q2 + 2 * q0 * q3
let r_m13 = 2 * q1 * q3 - 2 * q0 * q2
let r_m21 = 2 * q1 * q2 - 2 * q0 * q3
let r_m22 = q0 * q0 - q1 * q1 + q2 * q2 - q3 * q3
let r_m23 = 2 * q2 * q3 + 2 * q0 * q1
let r_m31 = 2 * q1 * q3 + 2 * q0 * q2
let r_m32 = 2 * q2 * q3 - 2 * q0 * q1
let r_m33 = q0 * q0 - q1 * q1 - q2 * q2 + q3 * q3
self.transform.m11 = r_m11
self.transform.m12 = r_m12
self.transform.m13 = r_m13
self.transform.m14 = 0.0
self.transform.m21 = r_m21
self.transform.m22 = r_m22
self.transform.m23 = r_m23
self.transform.m24 = 0.0
self.transform.m31 = r_m31
self.transform.m32 = r_m32
self.transform.m33 = r_m33
self.transform.m34 = 0.0
self.transform.m41 = (startPoint.x + endPoint.x) / 2.0
self.transform.m42 = (startPoint.y + endPoint.y) / 2.0
self.transform.m43 = (startPoint.z + endPoint.z) / 2.0
self.transform.m44 = 1.0
return self
}
}
//extension ended.
//in your code, you can like this.
let twoPointsNode1 = SCNNode()
scene.rootNode.addChildNode(twoPointsNode1.buildLineInTwoPointsWithRotation(
from: SCNVector3(1,-1,3), to: SCNVector3( 7,11,7), radius: 0.2, color: .cyan))
//end
you can reference http://danceswithcode.net/engineeringnotes/quaternions/quaternions.html
BTW, you will get same result when you use a cylinder to make a line between two points from above 3 methods. But indeed, they will have different normal lines. In another words, if you use box between two points, sides of box, except top and bottom, will face different direction from above 3 methods.
let me know pls if you need further explanation.
EDIT: For under or equal to IOS 11
I've good news for you ! You can link two points and put a SCNNode on this Vector !
Take this and enjoy drawing line between two point !
class CylinderLine: SCNNode
{
init( parent: SCNNode,//Needed to add destination point of your line
v1: SCNVector3,//source
v2: SCNVector3,//destination
radius: CGFloat,//somes option for the cylinder
radSegmentCount: Int, //other option
color: UIColor )// color of your node object
{
super.init()
//Calcul the height of our line
let height = v1.distance(v2)
//set position to v1 coordonate
position = v1
//Create the second node to draw direction vector
let nodeV2 = SCNNode()
//define his position
nodeV2.position = v2
//add it to parent
parent.addChildNode(nodeV2)
//Align Z axis
let zAlign = SCNNode()
zAlign.eulerAngles.x = Float(M_PI_2)
//create our cylinder
let cyl = SCNCylinder(radius: radius, height: CGFloat(height))
cyl.radialSegmentCount = radSegmentCount
cyl.firstMaterial?.diffuse.contents = color
//Create node with cylinder
let nodeCyl = SCNNode(geometry: cyl )
nodeCyl.position.y = -height/2
zAlign.addChildNode(nodeCyl)
//Add it to child
addChildNode(zAlign)
//set contrainte direction to our vector
constraints = [SCNLookAtConstraint(target: nodeV2)]
}
override init() {
super.init()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
private extension SCNVector3{
func distance(receiver:SCNVector3) -> Float{
let xd = receiver.x - self.x
let yd = receiver.y - self.y
let zd = receiver.z - self.z
let distance = Float(sqrt(xd * xd + yd * yd + zd * zd))
if (distance < 0){
return (distance * -1)
} else {
return (distance)
}
}
}
#maury-markowitz's answer worked for me, here is the latest (Swift4) version of it.
To anyone working with SCNVector3 in Swift I can only recommend to add the +-*/ operator overloads somewhere in your code (e.g. from here).
extension SCNNode {
static func lineNode(from: SCNVector3, to: SCNVector3, radius: CGFloat = 0.25) -> SCNNode {
let vector = to - from
let height = vector.length()
let cylinder = SCNCylinder(radius: radius, height: CGFloat(height))
cylinder.radialSegmentCount = 4
let node = SCNNode(geometry: cylinder)
node.position = (to + from) / 2
node.eulerAngles = SCNVector3.lineEulerAngles(vector: vector)
return node
}
}
extension SCNVector3 {
static func lineEulerAngles(vector: SCNVector3) -> SCNVector3 {
let height = vector.length()
let lxz = sqrtf(vector.x * vector.x + vector.z * vector.z)
let pitchB = vector.y < 0 ? Float.pi - asinf(lxz/height) : asinf(lxz/height)
let pitch = vector.z == 0 ? pitchB : sign(vector.z) * pitchB
var yaw: Float = 0
if vector.x != 0 || vector.z != 0 {
let inner = vector.x / (height * sinf(pitch))
if inner > 1 || inner < -1 {
yaw = Float.pi / 2
} else {
yaw = asinf(inner)
}
}
return SCNVector3(CGFloat(pitch), CGFloat(yaw), 0)
}
}
For the sake of another method, I achieved this through trigonometry. This made the code very minimal. Here is the end result:
In my case the nodes are always placed on a fixed plane that slices the Y-Axis.
// Create Cylinder Geometry
let line = SCNCylinder(radius: 0.002, height: node1.distance(to: node2))
// Create Material
let material = SCNMaterial()
material.diffuse.contents = UIColor.red
material.lightingModel = .phong
line.materials = [material]
// Create Cylinder(line) Node
let newLine = SCNNode()
newLine.geometry = line
newLine.position = posBetween(first: node1, second: node2)
// This is the change in x,y and z between node1 and node2
let dirVector = SCNVector3Make(node2.x - node1.x, node2.y - node1.y, node2.z - node1.z)
// Get Y rotation in radians
let yAngle = atan(dirVector.x / dirVector.z)
// Rotate cylinder node about X axis so cylinder is laying down
currentLine.eulerAngles.x = .pi / 2
// Rotate cylinder node about Y axis so cylinder is pointing to each node
currentLine.eulerAngles.y = yAngle
This is the function to get the position between two nodes, place it within your class:
func posBetween(first: SCNVector3, second: SCNVector3) -> SCNVector3 {
return SCNVector3Make((first.x + second.x) / 2, (first.y + second.y) / 2, (first.z + second.z) / 2)
}
This is the extension to get the distance between nodes for the cylinder height, place it somewhere outside of your class:
extension SCNVector3 {
func distance(to destination: SCNVector3) -> CGFloat {
let dx = destination.x - x
let dy = destination.y - y
let dz = destination.z - z
return CGFloat(sqrt(dx*dx + dy*dy + dz*dz))
}
}
If you don't have one fixed axis like myself then you could do the extra trig to use this method.
Here's a solution using simd and quaternions for the rotation. I based the extension off of the answer by #Bersaelor.
I used this derivation (https://stackoverflow.com/a/1171995/6693924) to create the quaternion from two vectors. Hope this helps.
extension SCNNode {
static func lineNode(from: simd_float3, to: simd_float3, radius : CGFloat = 0.25) -> SCNNode
{
let vector = to - from
let height = simd_length(vector)
//cylinder
let cylinder = SCNCylinder(radius: radius, height: CGFloat(height))
cylinder.firstMaterial?.diffuse.contents = UIColor.white
//line node
let lineNode = SCNNode(geometry: cylinder)
//adjust line position
let line_axis = simd_float3(0, height/2, 0)
lineNode.simdPosition = from + line_axis
let vector_cross = simd_cross(line_axis, vector)
let qw = simd_length(line_axis) * simd_length(vector) + simd_dot(line_axis, vector)
let q = simd_quatf(ix: vector_cross.x, iy: vector_cross.y, iz: vector_cross.z, r: qw).normalized
lineNode.simdRotate(by: q, aroundTarget: from)
return lineNode
}
}
Sprout's (wow, the autocorrect will not allow me to actually type in his name!) post is indeed a solution, but I have implemented a very different solution in my code.
What I do is calculate the length of the line and the two endpoints, based on the X, Y and Z locations from the two ends:
let w = SCNVector3(x: CGFloat(x2m-x1m), y: CGFloat(y2m-y1m), z: CGFloat(z2m-z1m))
let l = w.length()
The length is simply pythag. Now I make an SCNNode that will hold the SCNCylinder, and position it in the middle of the line:
let node = SCNNode(geometry: cyl)
node.position = SCNVector3(x: CGFloat((x1m+x2m)/2.0), y: CGFloat((y1m+y2m)/2.0), z: CGFloat((z1m+z2m)/2.0))
And now the nasty part, where we calculate the Euler angles and rotate the node:
let lxz = (Double(w.x)**2 + Double(w.z)**2)**0.5
var pitch, pitchB: Double
if w.y < 0 {
pitchB = M_PI - asin(Double(lxz)/Double(l))
} else {
pitchB = asin(Double(lxz)/Double(l))
}
if w.z == 0 {
pitch = pitchB
} else {
pitch = sign(Double(w.z)) * pitchB
}
var yaw: Double
if w.x == 0 && w.z == 0 {
yaw = 0
} else {
let inner = Double(w.x) / (Double(l) * sin (pitch))
if inner > 1 {
yaw = M_PI_2
} else if inner < -1 {
yaw = M_PI_2
} else {
yaw = asin(inner)
}
}
node.eulerAngles = SCNVector3(CGFloat(pitch), CGFloat(yaw), 0)
I suspect there is a much simpler way to do this using one of the other rotation inputs, but this works and working is a feature!
Draw the line between two nodes:
func generateLine( startPoint: SCNVector3, endPoint: SCNVector3) -> SCNGeometry {
let vertices: [SCNVector3] = [startPoint, endPoint]
let data = NSData(bytes: vertices, length: MemoryLayout<SCNVector3>.size * vertices.count) as Data
let vertexSource = SCNGeometrySource(data: data,
semantic: .vertex,
vectorCount: vertices.count,
usesFloatComponents: true,
componentsPerVector: 3,
bytesPerComponent: MemoryLayout<Float>.size,
dataOffset: 0,
dataStride: MemoryLayout<SCNVector3>.stride)
let indices: [Int32] = [ 0, 1]
let indexData = NSData(bytes: indices, length: MemoryLayout<Int32>.size * indices.count) as Data
let element = SCNGeometryElement(data: indexData,
primitiveType: .line,
primitiveCount: indices.count/2,
bytesPerIndex: MemoryLayout<Int32>.size)
return SCNGeometry(sources: [vertexSource], elements: [element])
}
How To Use
let line = generateLine(startPoint: SCNVector3Make(1, 1, 1), endPoint: SCNVector3Make(8, 8, 8))
let lineNode = SCNNode(geometry: line)
lineNode.position = SCNVector3Make(15, 15, 10)
scene.rootNode.addChildNode(lineNode)
The thickness of the line requires implementing the SCNSceneRendererDelegate, in particular:
func renderer(_ renderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: TimeInterval){
glLineWidth(10)
}
Objective-C version of Winchill's answer:
-(void)lineNodeFrom:(SCNVector3)to to:(SCNVector3)from radius:(float)radius{
SCNVector3 w = SCNVector3Make(to.x - from.x, to.y - from.y, from.z - to.z);
float l = sqrtf(powf(w.x, 2) + powf(w.y, 2) + powf(w.z, 2.0f));
SCNCylinder * cylinder = [SCNCylinder cylinderWithRadius:radius height:l];
SCNMaterial * material = [SCNMaterial material];
material.diffuse.contents = [[UIColor darkGrayColor] colorWithAlphaComponent:0.75f];
cylinder.materials = #[material];
[self setGeometry:cylinder];
//original vector of cylinder above 0,0,0
SCNVector3 ov = SCNVector3Make(0, l/2.0,0);
//target vector, in new coordination
SCNVector3 nv = SCNVector3Make((from.x - to.x)/2.0, (from.y - to.y)/2.0, (from.z-to.z)/2.0);
// axis between two vector
SCNVector3 av = SCNVector3Make((ov.x + nv.x)/2.0, (ov.y+nv.y)/2.0, (ov.z+nv.z)/2.0);
//normalized axis vector
SCNVector3 av_normalized = [self normaliseVector:av];
float q0 = 0.0f; //cos(angel/2), angle is always 180 or M_PI
float q1 = av_normalized.x; // x' * sin(angle/2)
float q2 = av_normalized.y; // y' * sin(angle/2)
float q3 = av_normalized.z; // z' * sin(angle/2)
float r_m11 = q0 * q0 + q1 * q1 - q2 * q2 - q3 * q3;
float r_m12 = 2 * q1 * q2 + 2 * q0 * q3;
float r_m13 = 2 * q1 * q3 - 2 * q0 * q2;
float r_m21 = 2 * q1 * q2 - 2 * q0 * q3;
float r_m22 = q0 * q0 - q1 * q1 + q2 * q2 - q3 * q3;
float r_m23 = 2 * q2 * q3 + 2 * q0 * q1;
float r_m31 = 2 * q1 * q3 + 2 * q0 * q2;
float r_m32 = 2 * q2 * q3 - 2 * q0 * q1;
float r_m33 = q0 * q0 - q1 * q1 - q2 * q2 + q3 * q3;
SCNMatrix4 transform;
transform.m11 = r_m11;
transform.m12 = r_m12;
transform.m13 = r_m13;
transform.m14 = 0.0;
transform.m21 = r_m21;
transform.m22 = r_m22;
transform.m23 = r_m23;
transform.m24 = 0.0;
transform.m31 = r_m31;
transform.m32 = r_m32;
transform.m33 = r_m33;
transform.m34 = 0.0;
transform.m41 = (to.x + from.x) / 2.0;
transform.m42 = (to.y + from.y) / 2.0;
transform.m43 = (to.z + from.z) / 2.0;
transform.m44 = 1.0;
self.transform = transform;
}
-(SCNVector3)normaliseVector:(SCNVector3)iv{
float length = sqrt(iv.x * iv.x + iv.y * iv.y + iv.z * iv.z);
if (length == 0){
return SCNVector3Make(0.0, 0.0, 0.0);
}
return SCNVector3Make(iv.x / length, iv.y / length, iv.z / length);
}

How can i check two angles if they are the same using radians

What i am trying to do is check if two Nodes(objects) are pointing in the same direction. But the problem is that SpriteKit rotates nodes in radians so it keeps adding to the zRotation of the node.
what i do for the rotation is:
if(touched_location.x >= self.frame.size.width/2)
{
node1.runAction(SKaction.RotateByAngle(CGFloat(-M_PI_2), 1.0)
}
else if(touched_location.x <= self.frame.size.width/2)
{
node1.runAction(SKAction.RotateByAngle(CGFloat(M_PI_2), 1.0)
}
so this rotates the node for 90° but RotateByAngle uses radians which is fine it sill works.
But radians keep adding to zRotation so i end up with something like 12,45 radians (not accurate number just for refrence) and this does not match the radians of my second Node(node2) that i am comparing the zRotation to.
For checking the rotation im using:
var first = CGFloat(round(100*node1.zRotation)/100)
var second = CGFloat(round(100*node2.zRoation)/100)
if(first == second)
{
do stuff....
}
But this obviously doesn't work cause if you keep rotation node in one direction the radian is to high (or to low if other direction) but visually the direction is the same.
How do i fix this?
the whole circle (360 deg) has angle 2 * PI, so 0 and 2 * PI represents the same 'normalized' angle.
import Foundation
func normalizeDifferenceAngleInRadians(a1: Double, _ a2: Double)->Double {
let twoPi = 2 * M_PI
return (a2 - a1) % twoPi
}
let a1 = M_PI_2
let a2 = 5 * M_PI_2
let diff = normalizeDifferenceAngleInRadians(a1, a2) // 0
based on Martin's note
import Foundation
func angleDifferenceInRadians(a1: Double, _ a2: Double)->Double {
let twoPi = 2 * M_PI
return -((a2 - a1 + M_PI) % twoPi - M_PI)
}
let a1 = M_PI_2
let a2 = 5 * M_PI_2 + 0.001
let a3 = 5 * M_PI_2 - 0.001
let diff1 = angleDifferenceInRadians(a1, a2) // -0.001
let diff2 = angleDifferenceInRadians(a1, a3) // 0.001
#0x141E thank you! I hope, the code below will be the final and correct implementation of my 'new year mathematical exercise'. Sometimes the simple trouble and quick correction leads to bigger trouble ...
func angleDifferenceInRadians(a1: Double, _ a2: Double)->Double {
let twoPi = 2 * M_PI
let d = (a2 - a1) % twoPi
let s = d < 0 ? -1.0 : 1.0
return d * s < M_PI ? d : (d - s * twoPi)
}