I'm trying to get the rotation of the device in rubymotion. I added this piece of code in the viewDidLoad.
#motionManager = CMMotionManager.alloc.init
#motionManager.deviceMotionUpdateInterval = 1.0
if (#motionManager.isDeviceMotionAvailable)
queue = NSOperationQueue.currentQueue
#motionManager.startDeviceMotionUpdatesToQueue(queue, withHandler:lambda do |motion, error|
NSLog "error = %#", error
NSLog "rotation rate = [%f, %f, %f]", motion.rotationRate.x, motion.rotationRate.y, motion.rotationRate.z
end)
else
NSLog "Device Motion is not available: You're likely running this in a simulator"
end
But it always logs this:
rotation rate = [0.000000, 0.000000, 0.000000]
What is wrong with what I do?
In RubyMotion, floats are actually objects, so you need to use %# instead:
NSLog "rotation rate = [%#, %#, %#]", motion.rotationRate.x, motion.rotationRate.y, motion.rotationRate.z
If you wanted to format the numbers (e.g. to 3 decimal places), you would need to format the arguments separately:
NSLog "rotation rate = [%#, %#, %#]", "%.3f" % motion.rotationRate.x, "%.3f" % motion.rotationRate.y, "%.3f" % motion.rotationRate.z
See this twitter conversation with one of the RubyMotion developers.
Related
I am working on an app that exposes manual controls for the camera with the new APIs introduced in iOS 8, and I am using this sample app from WWDC 2014 as a reference.
However I noticed a strange bahaviour (on my 5s and on a 6): after setting the exposure mode to "custom" and then back to "auto" the image continues to lag as if the exposure duration was not affected by this change.
Here is the code involved in each step (from the sample app, without any modification):
- (IBAction)changeExposureMode:(id)sender
{
UISegmentedControl *control = sender;
NSError *error = nil;
AVCaptureExposureMode mode = (AVCaptureExposureMode)[self.exposureModes[control.selectedSegmentIndex] intValue];
if ([self.videoDevice lockForConfiguration:&error])
{
if ([self.videoDevice isExposureModeSupported:mode])
{
[self.videoDevice setExposureMode:mode];
}
else
{
NSLog(#"Exposure mode %# is not supported. Exposure mode is %#.", [self stringFromExposureMode:mode], [self stringFromExposureMode:self.videoDevice.exposureMode]);
}
}
else
{
NSLog(#"%#", error);
}
}
- (IBAction)changeExposureDuration:(id)sender
{
UISlider *control = sender;
NSError *error = nil;
double p = pow( control.value, EXPOSURE_DURATION_POWER ); // Apply power function to expand slider's low-end range
double minDurationSeconds = MAX(CMTimeGetSeconds(self.videoDevice.activeFormat.minExposureDuration), EXPOSURE_MINIMUM_DURATION);
double maxDurationSeconds = CMTimeGetSeconds(self.videoDevice.activeFormat.maxExposureDuration);
double newDurationSeconds = p * ( maxDurationSeconds - minDurationSeconds ) + minDurationSeconds; // Scale from 0-1 slider range to actual duration
if (self.videoDevice.exposureMode == AVCaptureExposureModeCustom)
{
if ( newDurationSeconds < 1 )
{
int digits = MAX( 0, 2 + floor( log10( newDurationSeconds ) ) );
self.exposureDurationValueLabel.text = [NSString stringWithFormat:#"1/%.*f", digits, 1/newDurationSeconds];
}
else
{
self.exposureDurationValueLabel.text = [NSString stringWithFormat:#"%.2f", newDurationSeconds];
}
}
if ([self.videoDevice lockForConfiguration:&error])
{
[self.videoDevice setExposureModeCustomWithDuration:CMTimeMakeWithSeconds(newDurationSeconds, 1000*1000*1000) ISO:AVCaptureISOCurrent completionHandler:nil];
}
else
{
NSLog(#"%#", error);
}
}
I noticed this too. It seems to be related to slow shutter speeds. Try this: Go to custom. Set a fast shutter speed. Then go back to Auto. Boom, you're right there. Now, go to custom, set a slow shutter speed (slider to the right). Go back to auto and you can watch the shutter speed gradually move back to a reasonable setting.
This is the case in the sample code and in the app that I wrote based on the sample code. It is also the same for my 4s and 5s.
I believe that this is because the sensor needs to catch a certain number of images in order to pick the right auto setting. With a very slow shutter speed (up to 1 second long max) this means it could take several seconds to find the right setting. Sort of makes sense, even if not what we'd like. Fortunately for me my app never needs a shutter speed more than quarter of second, if that.
I have found in my own code that the setExposureModeCustomWithDuration method has some issue. Though it has a completion handler which is supposed to be called AFTER the duration and ISO are set in the device, it doesn't always work.
There are times, for instance when switching from Auto expose to manual exposure, that if you grab a still from within setExposureModeCustomWithDuration's completion handler, the still is taken with an auto expose setting. If you take another still imedeately after that, it has the correct manual exposure set on it.
I found that a 1 second delay at the beginning of the completion handler works around this issue, but that can't be a proper solution.
I have also tried placing a wait/sleep loop at the beginning of the completion handler where it waits until the device is not adjusting exposure -- that does not help.
I tried the same sample app and tried to reproduce the issue but was not able to it looks like they have fixed it now.
I'm currently parsing NSString values to NSNumbers and then adding them into a NSMutableArray called operands in an object called "data" like so:
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber * myNumber = [f numberFromString:*operandString];
[data.operands addObject:myNumber];
I then retrieve those numbers, perform some math on them, then update the array:
double x = [[data.operands objectAtIndex: i]doubleValue];
double y = [[data.operands objectAtIndex: i + 1]doubleValue];
double answer = x * y;
[data.operands replaceObjectAtIndex:(i) withObject:[NSNumber numberWithDouble:answer]];
When I get the answer, everything looks fine eg: ( 3.33 * 5 = 16.65)
BUT, when I look in the debugger I'm seeing some crazy values for x and answer, such as:
x = 3.3300000000000001
answer = 16.649999999999999
Why is this happening? Am I loosing some precision with parsing these back and fourth? Is it how I've used the NSNumberFormatter to parse the string?
The reason I'm in trouble with this is because I'm trying to ensure there's no double overflow errors so I'm using this simple test to check the integrity:
if (answer / y != x){
//THROW OVERFLOW ERROR
}
With the above crazy numbers this is always inconsistent. When I NSLog the answer it comes out fine:
NSLog (#"%g", [[data.operands objectAtIndex:i]doubleValue]]);
Same for
NSLog (#"%f", [[data.operands objectAtIndex:i]doubleValue]]);
You are not losing any precision that you need to worry about. Those are the correct values. There are only about 2^60 different double numbers, that finite set has to try to approximately cover the infinite 'number of numbers' in the range that doubles cover.
In other words, there are no exact answers in computer land and your
if (answer / y != x){
//THROW OVERFLOW ERROR
}
Will not work. Or it may work much of the time, but fail if you push it. Instead you need to acknowledge the limited precision (which is pretty high precision) of doubles:
//Don't waste time worrying like this...
if (fabs(answer / y - x) > 1e-12*fabs(answer)){
//Not correct or useful thing to check don't use this - i did not check
}
// let the math package handle it:
if (isnan(answer)){
// we gots problems
}
if (!isnormal(answer)){
// we gots some other problems
}
Also don't forget that 10^300 is a very large number, doubles work pretty well. To use 32 bit floats you need to pay much more attention to order of execution, etc.
NSLog is likely outputting with fewer decimals of precision, and rounds to the nearest thing, so the answers look better.
So I have this app that tracks your speed and altitude. I have a UILabel that shows the current speed you are at and the current altitude you are at. What I want to do is have a label that shows the max altitude and the max speed. So basically if you are running and your speed reaches 5 mph the max label will say 5 mph and then if you get to 6 mph the max label will change to 6 etc... Same thing for the altitude label. So how do I do this?
I'm assuming that you have either a float or an integer value for your currentSpeed/currentAltitude. Have another set of the same type, (I'll use int for my example) and name them maxSpeed/maxAltitude.
And add this code to the function which updates speed.
if(currentSpeed > maxSpeed){
maxSpeed = currentSpeed;
maxSpeedLabel.text = [NSString stringWithFormat: #"%d mph", maxSpeed];
}
Same goes with altitude.
EDIT: Given your statement: speedLabel.text = [NSString stringWithFormat:#"%.2f", [location speed]*2.236936284];
Here is the updated code:
float currentSpeed = [location speed] * 2.236936284;
if(currentSpeed - maxSpeed >= 0.01){
maxSpeed = currentSpeed;
maxSpeedLabel.text = [NSString stringWithFormat: #"%.2f mph", maxSpeed];
}
Also, now, currentSpeed and maxSpeed are both floats. Note that I have used a different type of comparison here. Since floats go into a great deal of precision (that we don't always need) I've restricted yours to 2 decimal places. So the text and maxSpeed will only update if it is greater by a factor of 0.01.
I'm adding a bunch of coordinates into quad tree and when I'm asking for the closest coordinate near my location, sometimes I've coordinate with 0 at the end, added automatically perhaps by the quad tree or I don't know how.
The problem is when I'm asking the double value in my core data using predicate it won't match because of the 0 digit addition to the number.
I thought about removing it when I've 0 but I'm sure there is a better way doing it.
For example:
Near location 31.123456, 34.123456, the nearest is 31.123444, 34.123450
when '34.123450' is actually 34.12345 in the database.
//Convert float to String
NSString *str_lat = #"34.123450";
NSString *trimmedString=[str_lat substringFromIndex:MAX((int)[str_lat length]-1, 0)];
if([trimmedString isEqualToString:#"0"])
{
str_lat = [str_lat substringToIndex:[str_lat length] - 1];
}
else
{
}
NSLog(#"%#",str_lat);
First: You should not store numbers as strings. 7.3 and 7.30 are the same values with simply different representations. You should not compare the representations, but the value.
Second: You should not compare floating-point numbers with == but their difference to a delta. In a calculation precision might get lost, rounding is applied and so on. The "mathematical" equal values might be physical different by a more or less small amount.
// remove the zeros from values (if you have them as floats)
NSString *valueFromTheDataBase = [NSString stringWithFormat:#"%g", 34.123450];
NSString *yourValue = [NSString stringWithFormat:#"%g", 34.12345];
if([yourValue isEqualToString:valueFromDataBase]) {
// they are equal
}
OR Make Them floats and compare them
// make them floats and compare them
CGFloat floatFromDB = [valueFromDB floatValue];
CGFloat yourFloat = [yourString floatValue];
if((floatFromDB - yourFloat) == 0) {
// they are equal
}
UPDATED as #Amin Negm says
I am trying to submit a float of two decimal length to my Game Center leaderboard, however the only format allowed to submit with is int64_t. I am using the default Apple report score method:
- (void)reportScore:(int64_t)score forCategory:(NSString *)category {
GKScore *scoreReporter = [[GKScore alloc] initWithCategory:category];
scoreReporter.value = score;
[scoreReporter reportScoreWithCompletionHandler: ^(NSError *error) {
[self callDelegateOnMainThread: #selector(scoreReported:) withArg: NULL error: error];
}];
}
I am trying to use this method to provide the score to the report score method:
- (IBAction)increaseScore {
self.currentScore = self.currentScore + 1;
currentScoreLabel.text = [NSString stringWithFormat: #"%lld", self.currentScore];
NSLog(#"%lld", self.currentScore);
}
Please help, I have been googling like crazy and cannot find the answer to this.
GameCenter only accepts int64_t
The only difference between values that appear like floats or decimal values and those that appear as integers is the position of the decimal mark, while in fact all of them are int64_t.
If your internal representation is a double and you configured game center to show 3 digits after the decimal mark you have to convert it to an integer by multiplying with 10^3 and casting to integer.
int64_t gameCenterScore = (int64_t)(doubleValue * 1000.0f)
You can only submit 64 bit integers as scores to a leaderboard. From the documentation:
To Game Center, a score is just a
64-bit integer value reported by your
application. You are free to decide
what a score means, and how your
application calculates it. When you
are ready to add the leaderboard to
your application, you configure
leaderboards on iTunes Connect to tell
Game Center how a score should be
formatted and displayed to the player.
Further, you provide localized strings
so that the scores can be displayed
correctly in different languages. A
key advantage of configuring
leaderboards in iTunes Connect is that
the Game Center application can show
your game’s scores without you having
to write any code.
That doc page should tell you about formatting your score. It sounds like in order to display float-like scores you will have to tinker with the format settings in iTunes Connect.
Update
Try this for increaseScore:
- (IBAction) increaseScore {
self.currentScore = self.currentScore + 5;
float score = (float)self.currentScore / 100.0f;
currentScoreLabel.text = [NSString stringWithFormat: #"%f", score];
NSLog(#"%lld", self.currentScore);
}
You can see the GKScore.h file.
#property(nonatomic, assign) int64_t value; // The score value as a 64bit integer.
So float value now is not available.