Objective-C Method Parameters Problem - objective-c

I'm attempting to define an extremely simple utility method that will save me from having to use a calculator to define RGB values as percentages. When I look into Apple's sample code called "QuartzCache", in the DrawView.m file, line 96, I see this:
float whiteColor[4] = {1, 1, 1, 1};
However, when I attempt to created a method like the following, the compiler hates me. A half-hour of intensive Googling has not produced any help.
+(float[])percentagesRGBArray:(float[])rgbArray{
float red = rgbArray[0];
float green = rgbArray[1];
float blue = rgbArray[2];
float alpha = rgbArray[3];
red = red/255;
green = green/255;
blue = blue/255;
alpha = alpha;
float percentagesRGBArray[4] = {red, green, blue, alpha};
return percentagesRGBArray;
}
What is the proper way to define such a method? What am I doing wrong here?

Define a struct that contains all of the components, or wrap up each individual component in an NSNumber. Alternatively, use an NSColor instance to contain your colour components.
struct way:
typedef struct
{
float red;
float green;
float blue;
float alpha;
} MyColor;
- (MyColor) percentagesRGBArray:(MyColor) incoming
{
MyColor result;
result.red = incoming.red / 255;
result.green = incoming.green / 255;
result.blue = incoming.blue / 255;
result.alpha = incoming.alpha;
return result;
}
NSNumber way:
- (NSArray *) percentagesRGBArray:(float[]) rgbArray
{
NSNumber *red = [NSNumber numberWithFloat:rgbArray[0] / 255];
NSNumber *green = [NSNumber numberWithFloat:rgbArray[1] / 255];
NSNumber *blue = [NSNumber numberWithFloat:rgbArray[2] / 255];
NSNumber *alpha = [NSNumber numberWithFloat:rgbArray[3]];
return [NSArray arrayWithObjects:red, green, blue, alpha, nil];
}
NSColor way:
- (NSColor *) percentagesRGBArray:(float[]) rgbArray
{
CGFloat red = rgbArray[0] / 255;
CGFloat green = rgbArray[1] / 255;
CGFloat blue = rgbArray[2] / 255;
CGFloat alpha = rgbArray[3];
return [NSColor colorWithDeviceRed:red
green:green
blue:blue
alpha:alpha];
}

Normally, you would use Cocoa's NSColor class to handle this sort of thing, but it looks like you are doing something a little more low-level.
In that case, I would do the following:
typedef struct
{
float red;
float green;
float blue;
float alpha;
}
RGBAData;
RGBAData ConvertRGBAToPercentages(const RGBAData source)
{
RGBAData percentages;
percentages.red = source.red/255;
percentages.green = source.green/255;
percentages.blue = source.blue/255;
percentages.alpha = source.alpha/255;
return percentages;
}
To be used as follows:
RGBAData original = { 0xFF, 0xFF, 0x00, 0x80 }; // 50% transparent yellow
RGBAData percents = ConvertRGBAToPercentages(original);

Both e.James and dreamlax's answers give good approaches for doing this. But to answer what was wrong with your original code:
Basically, it has to do with how C arrays work. An array is essentially equivalent to a pointer to its first element. In fact, when you pass an array to a function, it decays into a pointer. You're still allowed to name the argument float myArray[4] (you have to declare the number of elements) just to make it clear that the pointer is supposed to be to an array of 4 elements — but you're still getting a pointer. Now consider the return value. What are you returning? We already established that you can't return an array by value, because it decays into a pointer. But even if you change the return type to be a pointer, it still won't work, because the array will have gone out of scope once the function returns. In order to return an array, you have to malloc the memory, and then you're responsible for freeing it later.
This is why you should avoid working with C arrays when at all possible. They're really low-level and fiddly. Even when you do use them, it's usually a good idea to hide them behind an API that takes care of the low-level details for you.

I think i'm late) but i have just found this thread.
the C way to do this is to create an array before invoking a function;
+(void) percentagesRGBArray:(float[])inArray toArray:(float*)outArray {
...
}
float array1[4];
float array2[4];
[MyClass percentagesRGBArray:array1 toArray:array2];

Related

Objective-c: Adding a custom object to a NSMutableArray

I usually program in java or c++ and I recently started with objective-c. Looking for vectors in objective-c, I found NSMutableArray which seems to be the best option. I'm working on an opengl game and I'm trying to create an NSMutableArray of textured quads for my sprites. Here is the relevant code:
I define textured quads:
typedef struct {
CGPoint geometryVertex;
CGPoint textureVertex;
} TexturedVertex;
typedef struct {
TexturedVertex bl;
TexturedVertex br;
TexturedVertex tl;
TexturedVertex tr;
} TexturedQuad;
I create an array in the interface:
#interface Sprite() {
NSMutableArray *quads;
}
I initiate the array and I create the texturedQuads based on "width" and "height", which are the dimensions of a single sprite, and "self.textureInfo.width" and "self.textureInfo.height", which are the dimensions of the entire sprite sheet:
quads = [NSMutableArray arrayWithCapacity:1];
for(int x = 0; x < self.textureInfo.width/width; x++) {
for(int y = 0; y < self.textureInfo.height/height; y++) {
TexturedQuad q;
q.bl.geometryVertex = CGPointMake(0, 0);
q.br.geometryVertex = CGPointMake(width, 0);
q.tl.geometryVertex = CGPointMake(0, height);
q.tr.geometryVertex = CGPointMake(width, height);
int x0 = (x*width)/self.textureInfo.width;
int x1 = (x*width + width)/self.textureInfo.width;
int y0 = (y*height)/self.textureInfo.height;
int y1 = (y*height + height)/self.textureInfo.height;
q.bl.textureVertex = CGPointMake(x0, y0);
q.br.textureVertex = CGPointMake(x1, y0);
q.tl.textureVertex = CGPointMake(x0, y1);
q.tr.textureVertex = CGPointMake(x1, y1);
//add q to quads
}
}
The problem is I don't know how to add the quad "q" to the array "quads". Simple writing [quads addObject:q] doesn't work because the parameter should be an id not a TexturedQuad. I've seen examples of how to make an id from an int etc, but I don't know how to do it with an object like my TexturedQuad.
The essence of it is that you wrap your C struct in an Obj-C class. The Obj-C class to use is NSValue.
// assume ImaginaryNumber defined:
typedef struct {
float real;
float imaginary;
} ImaginaryNumber;
ImaginaryNumber miNumber;
miNumber.real = 1.1;
miNumber.imaginary = 1.41;
// encode using the type name
NSValue *miValue = [NSValue value: &miNumber withObjCType:#encode(ImaginaryNumber)];
ImaginaryNumber miNumber2;
[miValue getValue:&miNumber2];
See here for more information.
As #Bersaelor pointed out, if you need better performance use pure C or switch to Obj-C++ and use vectors instead of Obj-C objects.
An NSMutableArray takes any NSObject* but not just structs.
If you're serious about programming in Objective-C, take a look at some tutorials.
Furthermore, NSMutableArrays are meant for convenience, if your adding/deleting a lot of objects to that Array, use plain C-stacks.
Especially for your use-case that more low-level approach will get better performance.
Keep in mind, Objective-C(++) is just a superset of C(++), so you can use any C(++) code you are already familiar with.
When I wrote my game tactica for iOS, I switched to C-Code whenever I had to do heavy lifting (i.e. recursive AI-functions that get called hundreds of times per second).

Store a NSColor as a string

I currently have a Core Data database that stores data and I wish to also store an NSColor into it but It does not accept NSColor as an object. My solution would be to store it as a string in the database and have it read into a NSColor when loaded. How would I do this?
For example, If I had a colour like [NSColor redColor] how would I store it in a database (as a string) and then retrieve it. This is a basic example and it would be more complicated RGB colors in the end.
Thanks.
You should consider using NSData as container for storing unsupported data types in Core Data. To access NSColor as NSData you will need to mark attribute as transformable and create reversible NSValueTransformer class to transform NSColor as NSData.
Useful Link: Non-Standard Persistent Attributes
I agree with the answers that recommend using NSData for storing colors in a Core Data store. That said, there may be times when it might be useful to store a color in a string, and it's certainly not difficult to do. I'd suggest creating a category on NSColor:
#interface NSColor (NSString)
- (NSString*)stringRepresentation;
+ (NSColor*)colorFromString:(NSString*)string forColorSpace:(NSColorSpace*)colorSpace;
#end
#implementation NSColor (NSString)
- (NSString*)stringRepresentation
{
CGFloat components[10];
[self getComponents:components];
NSMutableString *string = [NSMutableString string];
for (int i = 0; i < [self numberOfComponents]; i++) {
[string appendFormat:#"%f ", components[i]];
}
[string deleteCharactersInRange:NSMakeRange([string length]-1, 1)]; // trim the trailing space
return string;
}
+ (NSColor*)colorFromString:(NSString*)string forColorSpace:(NSColorSpace*)colorSpace
{
CGFloat components[10]; // doubt any color spaces need more than 10 components
NSArray *componentStrings = [string componentsSeparatedByString:#" "];
int count = [componentStrings count];
NSColor *color = nil;
if (count <= 10) {
for (int i = 0; i < count; i++) {
components[i] = [[componentStrings objectAtIndex:i] floatValue];
}
color = [NSColor colorWithColorSpace:colorSpace components:components count:count];
}
return color;
}
#end
I've checked that the code above compiles and works about as advertised. A small sample program produces appropriate output:
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSLog(#"Red is: %#", [[NSColor redColor] stringRepresentation]);
NSLog(#"Cyan is: %#", [[NSColor cyanColor] stringRepresentation]);
NSLog(#"Read in: %#", [NSColor colorFromString:[[NSColor redColor] stringRepresentation]
forColorSpace:[NSColorSpace deviceRGBColorSpace]]);
}
return 0;
}
Output:
Red is: 1.000000 0.000000 0.000000 1.000000
Cyan is: 0.000000 1.000000 1.000000 1.000000
Read in: NSCustomColorSpace Generic RGB colorspace 1 0 0 1
It might make sense to store the color space in the string so you don't have to specify it when you go from string to color. Then again, if you're just going to store these strings and read them again, you should be using NSData anyway. Using strings makes more sense if you need to write colors into some sort of human-readable file, or perhaps as a debugging aid.
NSColor supports the NSCoding protocol, so you can use the -encodeWithCoder: method to save it to an archive, and you can use -initWithCoder: to load it from an archive.
Property lists do not store colors and Apple recommends you store them as NSData not as NSString, you should probably do the same. See Apple's instructions here.
Here are simple functions for converting an NSColor to and from an NSString. This example assumes we're using an RGB color space, but
it can be easily adapted for others. For example, NSStringFromColor() could include the color space in the string and use that information when converting back to a color in NSColorFromString().
Usage:
NSString *theColorString = NSStringFromColor(theColor);
NSColor *theColor = NSColorFromString(theColorString);
The functions:
NSString *NSStringFromColor(NSColor *theColor)
{
CGFloat red, green, blue, alpha;
[theColor getRed:&red green:&green blue:&blue alpha:&alpha]; // assumes RGB color space
NSString *theColorString = [NSString stringWithFormat:#"%f %f %f %f",red,green,blue,alpha];
return theColorString;
}
NSColor *NSColorFromString(NSString *theColorString)
{
if ( theColorString.length == 0 ) {
theColorString = #"0.9 0.9 0.95 1.0"; // a default color
}
NSArray <NSString *> *theColors = [theColorString componentsSeparatedByString:#" "];
if ( theColors.count == 4 ) { // sanity
// unpack the color
NSColor *theColor = [NSColor colorWithSRGBRed:theColors[0].floatValue
green:theColors[1].floatValue
blue:theColors[2].floatValue
alpha:theColors[3].floatValue];
return theColor;
}
return nil; // theColorString format error
}

Cocos 2D CCColor3b not assignable to property.color

I am probably staring the answer in the face, however.
I want to assign a random colour to a CCLabelTTF string. When I try to set the return type of (CCColor3B *) and assign it CCLabelTTF.color = [self randomColor] I get incompatible assignment errors, both in the method, and at the above assignment. Method code:
-(ccColor3B *)randomColor
{
float r = arc4random() % 255;
float g = arc4random() % 255;
float b = arc4random() % 255;
ccColor3B *color = {r,g,b,1};
return color;
}
I think I am trying to obtain a return value which is the wrong type, or assign a read-only value, but information on CCColor3B is scarce. Thanks in advance.
From Cocos2d class documentation, the property color of CCSprite is not a pointer (it is a struct)
-(ccColor3B) color [read, write, assign]
You need to change your method as follows
-(ccColor3B)randomColor
{
float r = arc4random() % 255;
float g = arc4random() % 255;
float b = arc4random() % 255;
return ccc3(r,g,b);
}
You can find the definition of ccColor3B in the docs for CCTypes.h (line 43)
typedef struct _ccColor3B {
GLubyte r;
GLubyte g;
GLubyte b; } ccColor3B;

Incompatible type 'double'

I'm assigning a CGFloat animatedDistance and I'm getting this error.
Here I'm assigning value to animatedDistance
static const CGFloat KEYBOARD_ANIMATION_DURATION = 0.3;
static const CGFloat LANDSCAPE_KEYBOARD_HEIGHT = 140;
heightFraction is CGFloat as well.
if (orientation == UIInterfaceOrientationPortrait ||
orientation == UIInterfaceOrientationPortraitUpsideDown)
{
animatedDistance = floor(PORTRAIT_KEYBOARD_HEIGHT * heightFraction);
}
else
{
animatedDistance = floor(LANDSCAPE_KEYBOARD_HEIGHT * heightFraction);
}
What type should be animatedDistace? Can someone help me?
floor returns a double. On some platforms, CGFloat is a float. animatedDistance should be typed as a double (you can cast it to a CGFloat if needed).
Use this to get a vector... CGpoint vector = ccpSub(cgpoint 1, cgpoint 2);
And if you want double/float values then do this:
CGpoint.location
for whatever you are trying to find the coordinates of, then assign a float to CGPoint.location.y and another float to CGPoint.location.x
You need cocos2d for this by the way. I think.
Sounds to me like you declared animatedDistance as holding some kind of pointer, such as NSNumber *, or a structure, such as CGSize. Either way, you can't assign a CGFloat there.
If animatedDistance holds an NSNumber object, create one around the value. Back when you asked this question, the way to do this was [NSNumber numberWithDouble:floor(…)]. Now, you can just use #(floor(…)).
If animatedDistance holds a CGSize or other structure, you're going to have to decide for yourself how to meaningfully convert from the single number you have to the kind of structure you want.

Am I using Objective-C collections properly here?

I'm attempting to write an iPhone game. This function is intended to apply gravitational force to several objects. I'm porting it from Python and I'm wondering if my use of dictionaries and arrays as tuples makes sense and is typical/idiomatic in Objective C. Any comments on the code appreciated.
+ (void)updateBodies:(NSMutableArray*)bodies {
NSMutableDictionary* totals = [NSMutableDictionary dictionaryWithCapacity:[bodies count]];
for (Body* body in bodies) {
if (body.fixed) {
continue;
}
float tx;
float ty;
for (Body* other in bodies) {
if (other == body) {
continue;
}
float dx = other.x - body.x;
float dy = other.y - body.y;
float dist2 = pow(dx, 2) + pow(dy, 2);
float dist = sqrt(dist2);
float mass = pow(other.radius, 3);
float magnitude = G * mass / dist2;
float ux = dx / dist;
float uy = dy / dist;
tx += ux * magnitude;
ty += uy * magnitude;
}
NSNumber* ntx = [NSNumber numberWithFloat:tx];
NSNumber* nty = [NSNumber numberWithFloat:ty];
NSArray* tuple = [NSArray arrayWithObjects:ntx, nty, nil];
[totals setObject:tuple forKey:body];
}
for (Body* body in [totals allKeys]) {
NSArray* tuple = [totals objectForKey:body];
float tx = [[tuple objectAtIndex:0] floatValue];
float ty = [[tuple objectAtIndex:1] floatValue];
body.dx += tx;
body.dy += ty;
}
}
The only problem you should be aware of is that NSDictionary copies its keys. So Body needs to implement NSCopying and the instances of Body in totals are not necessarily the same instances in the passed in bodies array depending on how you implement NSCopying.
The approach I would use would be to consider velocity as a property of the body. That way you don't need a dictionary to associate the body to its velocity, you can just iterate through the array itself.
Talking of iterating. You can halve the number of iterations and some calculations by calculating the velocity of the other body at the same time as the first body. i.e. your inner loop would only iterate through the bodies that come after the outer loop body in the array.
It would mean you can't use fast iteration, so you'd have to profile to figure out which approach is faster.
On a minor note, I think
for ....
{
if (!condition)
{
continue;
}
// do stuff
}
is really ugly. What's wrong with:
for ....
{
if (condition)
{
// do stuff
}
}
You could used block enumeration for final update:
[totals enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
Body* body = key;
NSArray* tuple = key;
body.dx += [[tuple objectAtIndex:0] floatValue];
body.dy += [[tuple objectAtIndex:1] floatValue];
}];
An other solution could be to not used NSDictionary and NSArray and use a C array. It should be faster than using (and create) objects.