Continuously rotate a graphic at a rate that matches compass rotation data - swift

I'm building an iOS application that needs to smoothly rotate an image based on incoming compass heading data (actually, GPS ground track data arriving at 10 Hz from an aviation GPS system). I can easily animate rotation to the new desired angle with the correct duration (0.1 seconds) but the effect is still rough - you can tell that it's actually a series of discrete animations rather than one fluid motion. Here's the current very simple animation code:
// rotate the compass ring
if let cv = self.compassView {
UIView.animate(withDuration: 0.1, animations: {
cv.transform = CGAffineTransform(rotationAngle: CGFloat(newAngle))
})
}
Does anyone have a good working algorithm for fluidly spinning things like this? Some clever means of creating a single animation (perhaps one that assumes a full 360) the speed of which is then modified as the rate of rotation changes? Apple's own compass app appears to do something similar.

Related

I am trying to make my SCNNode rotate but I cannot seem to get it to work

My SCNNode is a compass that I want to rotate towards north.
compassNode = scene?.rootNode.childNodes[0]
I am successfully getting heading data and converting it to radians and storing it in a variable called angle.
angle = newHeading.trueHeading.toRadians
I have tried three methods now:
rotate = SCNAction.rotate(by: CGFloat(angle), around: SCNVector3(0, 1, 0), duration: 0.5)
compassNode?.runAction(rotate)
and
compassNode?.rotation = SCNVector4Make(0, 1, 0, Float(angle))
and
let oldTransform = compassNode?.transform
let newTransform = SCNMatrix4MakeRotation(Float(angle), 0, 1, 0)
SCNTransaction.begin()
SCNTransaction.animationDuration = 0.5
compassNode?.transform = SCNMatrix4Mult(newTransform, oldTransform!)
SCNTransaction.commit()
None of them makes any noticeable transformation to the compass. It appears to be still.
Running out of comment characters... Sry - have to put it in answer section.
Convert 90 degrees to radians, rotateTo that value. Rotating models can be confusing in 3D (for me anyway), sometimes it's easier to just use an SCNBox at first - it will have the correct rotation by default provided you're looking down -Z if you didn't change the camera view. You can turn on the the 3D outlines and sometimes that helps provide a better visual OR just texture map the box with different colors so that Red is the top, Green is front or whatever.
Then you can reshape the box, rotated it with different angles until you get the hang of rotateTo, rotateBy, rotateAround. I'm not a math guru, so unfortunately trial and error for me sometimes, but once I understand what's happening, then I can put the model in and know that my math works - then if I have to do some work on the model, such as pivot point or need to change the model - set Z-UP or whatever (this post: 59766878), then I'm focused on one thing at a time and not having a combination of problems to figure out.
I've also used lookAt and placed various (hidden) nodes at particular positions, so that I can see what X,Y,Z rotations look like at various points in a game and from different view points.

Add smoothing to camera mover

Hi I have a script that adjusts the distance of an camera in unity to make sure an object is always fully in the view of the camera. I do this like so:
Vector3 characterSize = UpdateBounds(totalPoints).size;
float objectSize = Mathf.Max(Mathf.Max(characterSize.x / 2, characterSize.y / 2), characterSize.z / 2);
float cameraView = 2f * Mathf.Tan(0.5f * Mathf.Deg2Rad * Camera.main.fieldOfView);
float rigRadius = cameraPadding * objectSize / cameraView;
In this case the rigRadius is the distance from the subject to make sure the camera view contains the total object.
The problem i am having is that when the object has a big change in size over a relativly small time period. The camera movement feels jerky and not smooth at all.
So how do i adjust this code to add some sort of a smoothing value? I just can't seem to figure it out.
As far as i managed to figure out I need to smooth the rigRadius value but i dont know how :(
Have a target radius that your current radius smoothly moves towards.
For best(-ish) results, use a formula where the "speed" of the smoothing is dependent on how far the current situation is from target situation (Note: In this case, situation = radius). In other words, zoom speed depends on the current zoom state rather than a fixed starting point.
So really far (for example after a big, fast change) = really fast (the start of the smoothing is quick), but really close = really slow (so the end of the smoothing is slow).
Here's a decent tutorial on this for 2D: https://youtu.be/AvnrywsoTe0
Note how the lerp uses current camera ortho size rather than a fixed "starting" ortho size to apply the zoom, effectively making the effect I described, where zooming speed depends on current zoom and its "distance" to target zoom.

Trying to display the velocity of a GameObject in Unity, numbers bouncing all over

Ok I'm developing a game in which you land a rocket ship while avoiding asteroids... Pretty simple. I have two UI Text elements that are meant to display the current vertical and horizontal velocity. I searched around and came up empty in terms of unity having a built in function to get the current velocity of a 2D rigidbody so I came up with this:
void Update () {
shipVelocity = Mathf.Abs((lastPosition.y - spaceShip.transform.position.y) / Time.deltaTime);
shipAngularVelocity = Mathf.Abs((lastPosition.x - spaceShip.transform.position.x) / Time.deltaTime);
uiText = "Vertical Velocity: " + shipVelocity;
currentVelocityText.text = uiText;
uiText = "Horizontal Velocity: " + shipAngularVelocity;
currentAngularVelocityText.text = uiText;
lastPosition = spaceShip.transform.position;
}
Basically it just does your standard velocity = delta position / delta time but something is wrong. The numbers bounce all over the place and after the ship has landed and is completely stationary the numbers still bounce around from 0-1. While it's flying the numbers rapidly change from what I think is an accurate velocity to 0 and back - so fast it basically just looks like it's blinking.
Could it be it's just calling it too fast? I.e. frame rate too high? If so how can I slow it down?
Thnx!
If you are updating a Rigidbody, it is important to put your update logic in "FixedUpdate()". This version of update is called at a set rate (default 50x/sec) and therefore should be used for updating physics logic. Link: https://docs.unity3d.com/ScriptReference/MonoBehaviour.FixedUpdate.html
This is common in games as it separates the framerate (draws/sec) from the update logic (updates/sec), meaning that of the framerate drops, the games underlying physics are not altered.

UIImageView rotation affecting position?

I have a UIImageView that is set to move up and down the screen with the value of the accelerometer, using the following code:
ship.center = CGPointMake(ship.center.x, ship.center.y+shipPosition.y);
Where shipPosition is a CGPoint set in the accelerometerDidAccelerate method using:
shipPosition.y = acceleration.x*60;
Obviously this works fine, it is very simple. I run into trouble when I try to something equally simple, vary the rotation of the image depending on its acceleration. I do this using:
ship.transform = CGAffineTransformMakeRotation(shipPosition.y);
For some reason this causes a very strange thing to happen, in that the image snaps back to its origin every time the main method is called. I can see frames where the image moves to where it should be, but then instantly snaps back.
This problem only happens when I have the rotation line in, commented out it works fine. I have no idea what is going on here, I have done this many times for different apps and i never had such a problem. In fact I copied my code from a different app I created where it works fine.
EDIT:
What really confuses me is when I change the angle of the rotation from the acceleration to the position of the ship using:
ship.transform = CGAffineTransformMakeRotation(ship.center.y/10);
When I do this, the ship actually rotates based on the accelerometer but does not move, which is crazy because a changing ship.center.y means the position of the ship is changing, but it's not!!
You should set the transform of you view back to CGAffineTransformIdentity before you set his center coordinates or frame and after that apply the new transformation.
The frame property returns the transformed coordinates of a view if it is transformed and not the true (well actually the transformed are true) coordinates.
Quote from the docs:
Warning: If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.
Update/Actual Answer:
Well the actual problem is
shipPosition.y = acceleration.x*60;
Since you set the y pos in accelerometerDidAccelerate.
The acceleration won't remember it's old value. So if you move your device it will get a peak and as you slow down it will decelerate again.
Your ship will be +/-60 at the highest acceleration speed but will be 0 when you stop moving your device and shipPosition.y will be 0.
CGAffineTransformMakeRotation expects angle in radians, not in degrees.
1 radian = M_PI / 180.0 degrees

Distance moved by Accelerometer

I want to move objects on iPhone screen (rectangle, circle and so on) by moving iPhone.
For example, I move iPhone along X-axis and the object moves along X-axis. The same for Y,Z-axis.
How can I do this?
Can I get algorithm for it?
Thank you.
P.S:
I looked for a while and seems like it is possible using accelerometer.
You get position by integrating the linear acceleration twice but the error is horrible. It is useless in practice.
Here is an explanation why (Google Tech Talk) at 23:20. I highly recommend this video.
However the gyro mouse might work for your application, see between 37:00-38:25 in the video.
Similar questions:
track small movements of iphone with no GPS
What is the real world accuracy of phone accelerometers when used for positioning?
how to calculate phone's movement in the vertical direction from rest?
iOS: Movement Precision in 3D Space
How to use Accelerometer to measure distance for Android Application Development
How can I find distance traveled with a gyroscope and accelerometer?
Not easy to do accurately. An accelerometer only reports acceleration, not movement. You can try (numerically) integrating the acceleration over time, but you're likely to end up with cumulative errors adding up and leading to unintended motion.
Pseudocode, obviously untested:
init:
loc = {0, 0, 0} ; location of object on screen
vel = {0, 0, 0} ; velocity of object on screen
t = 0 ; last time measured
step:
t0 = time ; get amount of time since last measurement
dt = t0 - t
accel = readAccelerometer()
vel += accel * dt ; update velocity
loc += vel * dt ; update position
displayObjectAt(loc)
Why don't you use the acceleration itself instead distance moved? For example;
If your acceleration is 1.4G east, your object should move to east 14pixels per second until your acceleration changes.
So that object's movement is will be like it is under a relative force and I think it's more realistic. Faster moving device, bigger force to effect object.
Also look:
http://www.freescale.com/files/sensors/doc/app_note/AN3397.pdf
and
Using accelerometer, gyroscope and compass to calculate device's movement in 3D world