Create a re-sizable array of CGPoints in objective-c - objective-c

I am trying to create a re-sizable array of CGPoints in objective-c. I've have looked into using NSMutableArray however it doesnt seem to allow resizing. Is there something else I can use?
thank you

Use an NSMutableArray, but just box your CGPoint structs in NSValue objects. Example:
CGPoint myPoint = {0,0};
CGPoint anotherPoint = {42, 69};
NSMutableArray * array = [NSMutableArray array];
[array addObject:[NSValue valueWithCGPoint:myPoint]];
[array addObject:[NSValue valueWithCGPoint:anotherPoint]];
CGPoint retrievedPoint = [[array objectAtIndex:0] CGPointValue];
Note that the +valueWithCGPoint: and CGPointValue methods are only available on the iPhone. However if you need this for the Mac, there are similar methods for dealing with NSPoints, and it's trivial to convert a CGPoint to an NSPoint (you can cast or use NSPointFromCGPoint()).

NSMutableArray is for objects. For plain old datatypes, use NSMutableData and good old pointer typecasts. It's a resizable unstructured memory buffer, just what you need for a vector of PODs. As for static type safety, Objective C does not give you any of that anyway.
EDIT:
Creating a mutable data object with an initial size n CGPoint structs:
NSMutableData *Data = [NSMutableData dataWithLength: n*sizeof(CGPoint)];
Placing a CGPoint pt into the buffer at the i-th position:
CGPoint pt;
NSRange r = {i*sizeof(CGPoint), sizeof(CGPoint)};
[Data replaceBytesInRange: r withBytes:&pt];
Retrieving a CGPoint from the i-th position into pt:
CGPoint pt;
NSRange r = {i*sizeof(CGPoint), sizeof(CGPoint)};
[Data getBytes: &pt range:r];
Growing the array by n objects:
[Data increaseLengthBy:n*sizeof(CGPoint)];
Hope that covers it. See the NSMutableData reference, and keep in mind that all NSData methods apply to it.

Related

NSKeyedArchiver: How to archive an NSValue containing a custom struct

Is there any way I can use an NSKeyedArchiver to encode an NSValue that contains a custom struct?. I have an NSDictionary which contains structs wrapped in NSValues and I want to archive this as part of a larger object graph but I receive the following error:
-[NSKeyedArchiver encodeValueOfObjCType:at:]: this archiver cannot encode structs
However, consider the following:
//Copy of the CGPoint declaration
struct MYPoint {
CGFloat x;
CGFloat y;
};
typedef struct MYPoint MYPoint;
CGPoint point = {1.0, 1.0};
MYPoint myPoint = {1.0, 1.0};
NSLog(#"CGPoint: %s", #encode(CGPoint)); //CGPoint: {CGPoint=ff}
NSLog(#"MYPoint: %s", #encode(MYPoint)); //MYPoint: {MYPoint=ff}
NSValue *CGPointValue = [NSValue valueWithBytes:&point objCType:#encode(CGPoint)];
NSData *CGPointData = [NSKeyedArchiver archivedDataWithRootObject:CGPointValue];
//NO ERROR
NSValue *MYPointValue = [NSValue valueWithBytes:&myPoint objCType:#encode(MYPoint)];
NSData *MYPointData = [NSKeyedArchiver archivedDataWithRootObject:MYPointValue];
//ERROR: -[NSKeyedArchiver encodeValueOfObjCType:at:]: this archiver cannot encode structs
Is it just the case that "this archiver cannot encode your structs" and thats the end of the story or is it possible to get the same behaviour as CGPoint but for custom structs?
I will probably just create a small custom object that wraps an NSValue and implements NSCoding to work around it, but I am curious about the contradiction shown in the code above with CGPoint and wonder if there is a way to extend NSValue to get the same behaviour.
There are issues with something to do with #encode and anonymous structs. Try changing the struct definition to:
typedef struct MYPoint {
CGFloat x;
CGFloat y;
} MYPoint;
If that doesn't work, you can wrap the struct using NSData:
[NSData dataWithBytes:&myPoint length:sizeof(MYPoint)];
The following example illustrates encoding a custom C structure.
//assume ImaginaryNumber defined:
typedef struct {
float real;
float imaginary;
} ImaginaryNumber;
ImaginaryNumber miNumber;
miNumber.real = 1.1;
miNumber.imaginary = 1.41;
NSValue *miValue = [NSValue valueWithBytes: &miNumber withObjCType:#encode(ImaginaryNumber)];
ImaginaryNumber
miNumber2;
[miValue getValue:&miNumber2
];
The type you specify must be of constant length. You cannot store C strings, variable-length arrays and structures, and other data types of indeterminate length in an NSValue—you should use NSString or NSData objects for these types. You can store a pointer to variable-length item in an NSValue object. The following code excerpt incorrectly attempts to place a C string directly into an NSValue object:
/* INCORRECT! */
char *myCString = "This is a string.";
NSValue *theValue = [NSValue valueWithBytes:myCString withObjCType:#encode(char *)];
In this code excerpt the contents of myCString are interpreted as a pointer to a char, so the first four bytes contained in the string are treated as a pointer (the actual number of bytes used may vary with the hardware architecture). That is, the sequence “This” is interpreted as a pointer value, which is unlikely to be a legal address. The correct way to store such a data item is to use an NSString object (if you need to contain the characters in an object), or to pass the address of its pointer, not the pointer itself:
/* Correct. */
char *myCString = "This is a string.";
NSValue *theValue = [NSValue valueWithBytes:&myCString withObjCType:#encode(char **)];
Here the address of myCString is passed (&myCString), so the address of the first character of the string is stored in theValue.
Important: The NSValue object doesn’t copy the contents of the string, but the pointer itself. If you create an NSValue object with an allocated data item, don’t free the data’s memory while the NSValue object exists.

Most efficient way of changing the value of a CGPoint in a CGPoint array?

I have constructed a CG Point array as follows:
NSMutableArray *_startPositions = [NSMutableArray array];
[_startPositions addObject:[NSValue valueWithCGPoint:CGPointMake(0.0, 0.0)]];
[_startPositions addObject:[NSValue valueWithCGPoint:CGPointMake(0.0, 0.0)]];
How can i most efficiently change the x and y values of the points in the array?
If I retrieve the value using:
CGPoint myPoint0 = [[_startPositions objectAtIndex:0] CGPointValue];
I get back a copy of the point. How can I change the x and y values of the CGPoints in the array? I could remove the CGPoint at any position and replace it with one with the correct x,y values, but is there a more efficient way?
Use C array instead of NSMutableArray. If required use C++ STL collection classes such as std::vector or other C/C++ collection library. In this way, you can manipulate the array content without any Obj-C method call.
I suggest you create your own class as
.h
#interface MyPoint : NSObject {
CGPoint point;
}
#property (nonatomic,assign) CGPoint point;
#end
.m
#implementation MyPoint
#synthesize point;
#end
Then add your MyPoint Object in your array.
Change it like
MyPoint *myPoint = [_startPositions objectAtIndex:0];
myPoint.point = myPoint0;
From the docs:
NSValue objects are always immutable.
So your only option here is to create a new NSValue to replace the old one when the CGPoint value changes. If you are looking for a more efficient way to store an array of CGPoints, then a C array is the easiest and fastest way to go.
If you are changing just the 'x' or 'y' value of CGPoint point, you can do this:
((CGPoint *)&point)->x += dx;
or
((CGPoint *)&point)->y += dy;

Draw filled dynamic polygon in Cocos2D, iOS

How can i draw polygon with points read from NSMutableArray object in cocos2D framework?
I am able to draw polygon using this function: filled antialiased poly cocos2d
The problem is becouse points argument *(CGPoint poli) must be static object.
Any ideas or suggestions?
What is type of NSMutableArray objects?
You need to extract raw data of points and set it as poli argument.
If it's NSValue with CGPoint then:
NSMutableArray* yourPointsArray;
...
CGPoint* poli = malloc (sizeof (CGPoint) * [yourPointsArray count]);
for (uint i=0; i<[yourPointsArray count]; i++)
{
poli[i] = [[yourPointsArray objectAtIndex: i] CGPointValue];
}
ccFillPoly (poli, [yourPointsArray count], YES);
free (poly);

How to use NSPointArray?

So I want to use the method appendBezierPathWithPoints:count: in NSBezierPath. But the method requires me to use NSPointArray. The documentary doesn't really talk much about it and all I could get about it is that it's an array of NSPoints and I'm not sure how to do it. I think that it uses the c array mechanism, but I'm not sure.
Thanks.
Yes, you need a C-style array of points to pass to appendBezierPathWithPoints:count:. For example you might do something like this:
NSPoint pointArray[3];
pointArray[0] = NSMakePoint(0, 0);
pointArray[1] = NSMakePoint(0.5, 0.25);
pointArray[2] = NSMakePoint(1, 1);
[lines appendBezierPathWithPoints:pointArray count:3];
where lines is an instance of NSBezierPath.
In a more complicated case you'll use a variable number of points say.
If you want to use Objective-C style array, then you have to use NSValue class for this purpose.
NSMutableArray *array = [NSMutableArray array];
CGPoint myPoint;
myPoint.x = 100;
myPoint.y = 200;
[array addObject:[NSValue valueWithPoint:myPoint]];
To retrieve an NSPoint back from the array:
myPoint = [array[0] pointValue];
Hope it helps.

Using CGPoints in an NSArray

I have been trying to create an array stating the location of a UIImageView in an app I've been working on. What I am trying to do is by using an array I can store the location of my "player" image by using its x,y and z coordinates. The script I am trying to accomplish would look like
NSArray *location[3];
-(IBAction)startup;{
[location addObject: player.center.x];
[location addObject: player.center.y];
[location addObject: playerheight];
}
So I will be able to access this array to move my "player" on the screen in "3-dimensions", but I don't know how to convert the CGpoint values to NSValues so they can be used in the array, is there a simple way to do this inside of the array?
To convert floating point values to objects, use NSNumber. NSValue has wrappers for geometric types like CGPoint. Either would work for you.
[NSValue valueWithCGPoint:player.center];
[NSNumber numberWithFloat:player.center.x];
[NSNumber numberWithFloat:player.center.y];
To addition for the first answer.
When you'll need to read CGPoint back from your array, you can use something like that:
CGPoint point = [(NSValue *)[pointsArray objectAtIndex:i] CGPointValue];
Also note that there's no addObject method for NSArray (you can't add objects to an NSArray after its been created); you want NSMutableArray.
Instead of:
NSArray *location[3];
you probably want something more like:
NSMutableArray *location = [NSMutableArray arrayWithCapacity:3];
Does it have to be an NSArray? Why not use an array of structs?
typedef struct {
CGPoint location;
CGFloat height;
} PlayerLocation;
PlayerLocation players[3];
players[0].location = player.center;
players[0].height = playerheight;
Or depending on your design it may make more sense to declare an objective-C class that contains the x,y,z coordinates as ivars and store those objects into an NSArray.
#interface PlayerLocation : NSObject {
CGPoint location;
CGFloat height;
}
#end