Indexing arrays - objective-c

Overview
I have painting APP. It registers mouse events and sends it to NSMutableArray. Later values from array are being transfered to GLfloat and drawn like vertex array. So all brush stroke is redrawn at every mouse event.
What I want to do?
I want to implement undo-redo functions and something like brush stroke variety (softness and etc), so I need somehow to do that every vertex (mouse event location) could have few additional settings. I want to ask if it is possible at all.
Explanation
If user draws 10 points with 100% softness (in one mouse drag) and then changes softness to 0% and draws, first 10 point's brush texture has to be set to img1 and second brush stroke brush's texture has to be set to img2.
If there would be possibility for indexing arrays (for example something like this NSMutableArray *array[i++] = [[NSMutableArray alloc] init]) I think it would be possible to do something similar to what I want pretty easy. But is it possible? Or maby you could suggest any other solution?
P.S. NSMutableArray *array[i++] = [[NSMutableArray alloc] init] doesn't shows me any error, but does't works too.
Attempt using NSMutableDictionary
At mousedown and mousedragged:
locll = [self convertPoint: [event locationInWindow] fromView:nil];
NSValue *locationValuell = [NSValue valueWithPoint:locll];
[vertices addObject:locationValuell];
at mouseUp
NSString *index = [NSString stringWithFormat:#"%d", aIndex++];
[aDict setObject:vertices forKey:index];
NSArray *allKeys = [aDict allKeys];
NSLog(#"dict count: %ld", [allKeys count]);
NSString *index1 = [NSString stringWithFormat:#"%d", aIndex];
NSMutableArray *a = [aDict objectForKey:index1];
NSLog(#"a count:%li", [a count]);
at initWithCoder
int aIndex = 0;
dict count returns how many objects are stored in dictionary. And it works. But later when I try to get array back from dictionary, I check how much objects array has and it returns 0.

If i understand your question right , here's what you can do :
Every mouse drag will be 1 path i.e an NSMutableArray of points
with same softness level.
Add this to a NSMutableDictionary with Key as the
softness-level and Value with this path array.
Iterate through the dictionary to get the corresponding Key-Value
pair and draw the strokes.
For UNDO just delete the last object of the dictionary and store in
a temporary dictionary.
For REDO fetch it back from the temporary dictionary and add to the main dictionary.
Thanx for posting the code. I tried this and it works absolutely fine :
- (void)viewDidLoad
{
[super viewDidLoad];
int aIndex = 0;
NSMutableDictionary *aDict =[[NSMutableDictionary alloc]init];
NSMutableArray *vertices = [[NSMutableArray alloc]init];
[vertices addObject:#"one"];
[vertices addObject:#"two"];
[vertices addObject:#"three"];
NSString *index = [NSString stringWithFormat:#"%d", aIndex++];
[aDict setObject:vertices forKey:index];
NSArray *allKeys = [aDict allKeys];
NSLog(#"dict count: %d", [allKeys count]); // Prints 1
NSMutableArray *a =[aDict objectForKey:index];
NSLog(#"a item count is :%d", [a count]); // prints 3
}
NOTE: Be careful with the index you are passing.

You can work with your array as other objects so, you can use [YourArray insertObject:YourArray atIndex:i++]] then use [YourArray objectAtIndex:i] to get your array back
Good Luck

You can create a vector or VAOs and add a VAO to it for each brush stroke. Wrap the VAOs in a class that also holds additional information such as brush type. That should let you identify each stroke's type and correctly render them.

Related

Trying to build polygon from NSString

So, I'm trying to build an array of CGPoints by breaking an NSString, typically look like this:
31.241854,34.788867;31.241716,34.788744;31.242547,34.787585;31.242661,34.787719
Using this code:
- (NSMutableArray *)buildPolygon:(NSString *)polygon
{
NSMutableArray *stringArray = [[NSMutableArray alloc] init];
[stringArray addObject:[polygon componentsSeparatedByString:#";"]];
NSMutableArray *polygonArray = [[NSMutableArray alloc] init];
for (int i=0; i < polygonArray.count; i++)
{
NSArray *polygonStringArray = [[NSArray alloc] init];
polygonStringArray = [[stringArray objectAtIndex:i] componentsSeparatedByString:#","];
CGFloat xCord = [[polygonStringArray objectAtIndex:0] floatValue];
CGFloat yCord = [[polygonStringArray objectAtIndex:1] floatValue];
CGPoint point = CGPointMake(xCord, yCord);
[polygonArray addObject:[NSValue valueWithCGPoint:point]];
}
NSLog(#"return polygonArray: %#", polygonArray);
return polygonArray;
}
But eventually I get an empty array.
What I'm doing wrong?
You're defining polygonArray as an empty array just before the start of your for loop. You should define polygonArray like:
NSArray *polygonArray = [polygon componentsSeparatedByString:#";"];
And you don't even need to bother with that stringArray variable.
You have confusion over alloc & init, and one simple typo...
The confusions first:
NSMutableArray *stringArray = [[NSMutableArray alloc] init];
This creates a new NSMutableArray and stores a reference to it in stringArray. All good so far.
[stringArray addObject:[polygon componentsSeparatedByString:#";"]];
And this obtains a reference to an NSArray ([polygon componentsSeparatedByString:#";"]) and adds it as a single element to the mutable array referenced by stringArray. There is nothing wrong per se with this, but it is not what you want in this case - you just want the array returned by componentsSeparatedByString:. You do this with:
NSArray *stringArray = [polygon componentsSeparatedByString:#";"];
Which takes the reference returned by componentsSeparatedByString: and stores it in the variable stringArray - no alloc or init required as you are not creating the array yourself. You don't even own this array, so if you are using MRC there is no need to release it later.
NSArray *polygonStringArray = [[NSArray alloc] init];
Now this allocates an immutable empty array and stores a reference to it in polygonStringArray. This is not a very useful array, as it contains nothing and cannot be modified! But you don't keep it around long...
polygonStringArray = [[stringArray objectAtIndex:i] componentsSeparatedByString:#","];
This obtains a reference to an array from componentsSeparatedByString: and stores it in polygonStringArray. If you are using MRC this will cause a leak - your pointless zero-length array created above will leak, and a new zero-length array will be created and leaked every time around the loop.
You are confused over allocation - you only need to allocate things you are creating; when you receive a reference to an already allocated object you only need to store that reference. (If using MRC you may also need to retain/release/autorelease it as well - but let's stick with ARC.) So all you needed here was:
NSArray *polygonStringArray = [[stringArray objectAtIndex:i] componentsSeparatedByString:#","];
Now your code is almost correct, just one typo:
for (int i=0; i < polygonArray.count; i++)
Well you are filling polygonArray in this loop and it starts off as empty, what you need is stringArray.count.
HTH

obj-c fetching strings from array

i'm new to obj-c (this is my first day class eheh) and i'm trying to change a label with a random string from a multidimensional array. plus, every time the button is hitten you switch the array. i know it's a bit odd eheh… this is the IBAction:
UIButton *button = (UIButton *)sender;
NSMutableArray *firstArray = [NSMutableArray array];
[firstArray addObject:#"foo"];
NSMutableArray *secondArray = [NSMutableArray array];
[secondArray addObject:#"bar"];
NSMutableArray *frasi = [NSMutableArray array];
[frasi addObject:firstArray];
[frasi addObject:secondArray];
NSMutableArray *array = [NSMutableArray arrayWithObjects:[frasi objectAtIndex:[button isSelected]], nil];
NSString *q = [array objectAtIndex: (arc4random()% [array count] )];
NSString *lab = [NSString stringWithFormat:#"%#", q];
self.label.text = lab;
all works, but the new label is
( "foo" )
instead of just foo (without quotes)... probably i mess in the last block of code...
ty
So, you create 2 mutable arrays, then add them to a new mutable array frasi. Then you get one of those two arrays and use it as the single element (because you use arrayWithObjects: instead of arrayWithArray:) of a new array array.
So array is an array that contains a single array element (instead of an array of strings as you may believe).
When you get an object from array, it's always the same single object that was used to initialize it: either firstArray or secondArray.
So you get an array of strings where you expect a string. When using stringWithFormat:, the specifier %# is replaced with the string description of that object.
A string returns itself as its own description. But the description of an array is the list of all its elements separated with commas and surrounded by parenthesis, which is why you get ( "foo" ).
So instead or creating unneeded arrays, you may just replace all the 8th last lines with this:
NSArray *array = [button isSelected] ? secondArray : firstArray;
self.label.text = [array objectAtIndex:arc4_uniform([array count])];
Actually u have array within array
Replace this line with yours:
NSString *q = [[array objectAtIndex: (arc4random()% [array count] )] objectAtIndex:0];

How does Object copying work in objective c

I'm a bit confused on how copying objects works in Objective C. Here's what i know: When you copy an object, you get a distinct object in memory that contains all the same elements from the object that you have just copied and increments the retain count for each element. Also, copying each element in the array object from the original to a new location meant just copying the reference from one element of the array to another. So, the old and the new are pointing to the same element.
look at the below code: why is it that when you remove an object it only affects one object and when you change the element, it affects both the original and the copy object? Shouldn't the remove affects both objects?
NSMutableArray *dataArray = [NSMutableArray arrayWithObjects:
[NSMutableString stringWithString:#"one"],
[NSMutableString stringWithString:#"two"],
[NSMutableString stringWithString:#"three"], nil];
NSMutableArray *dataArray2;
NSMutableString *mStr;
NSLog(#"1-dataArray: ");
for( NSString *elem in dataArray)
NSLog(#" %#", elem);
dataArray2 = [dataArray mutableCopy];
[dataArray2 removeObjectAtIndex:0];
NSLog(#"2-dataArray2: ");
for( NSString *elem in dataArray2)
NSLog(#" %#", elem);
mStr =[dataArray objectAtIndex:1];
[mStr appendString:#"ONE"];
NSLog(#"3-dataArray: ");
for( NSString *elem in dataArray)
NSLog(#" %#", elem);
NSLog(#"4-dataArray2: ");
for( NSString *elem in dataArray2)
NSLog(#" %#", elem);
[dataArray2 release];
mutableCopy performs a 'shallow copy' of the NSArray's contents. eg. it is copying the pointers (and presumably retaining them) from the origin array. It is not copying the data those pointers are pointing to.
If we were to do this explicitly it's essentially doing:
-(NSMutableArray*)mutableCopy
{
NSMutableArray *newArray = [[NSMutableArray alloc] init];
for (id elem in originalArray)
[newArray addObject: elem];
return newArray;
}
Though presumably it's doing it more efficiently by using its access to the internal data structures.

Getting an NSString out of an NSArray

I am trying to save and read back some application settings stored as NSStrings in an iPhone app and have been having some trouble.
The code to save looks like:
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:accountID];
...
[array writeToFile:[self dataFilePath] atomically:YES];
[array release];
And the code to read looks like (accountID is an NSString*):
NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];
accountID = [array objectAtIndex:0];
...
[array release];
NSLog(#"Loading settings for: %#", accountID);
The read code throws an exception because after the array is released the accountID variable also appears to have been released (moving the NSLog call before releasing the array works fine). So I'm guessing that I'm creating a reference to the array instead of pulling out the actual string contained in the array. I tried several things to create new strings using the array contents but haven't had any luck.
You guess is on the right lines although you have a reference to the 0th element of the array not the array. The array consists of pointers to NSString objects. The Strings will get get released when yhe array is released.
You need to retain the element you are using e/g/
NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];
NSString* accountID = [[array objectAtIndex:0]retain];
...
[array release];
NSLog(#"Loading settings for: %#", accountID);
When you release the array the reference to the accountID will also be released. You need to retain it.
accountID = [[array objectAtIndex:0] retain];
Then obviously at some point you need to release it.
try [accountID retain] before you release the array

Size of the NSMutable Array in objective C?

I want to ask the size of the NSMutable Array can be 2000? If not, is it possible to open an array to store 2000 elements. The elements is the user-defined object. Thank you.
The answer is that an NSMutableArray always starts with a size of zero. If you want it to be so you can do something like this:
NSMutableArray* anArray = [NSMutableArray arrayWithSize: 2000];
[anArray replaceObjectAtIndex: 1999 withObject: foo];
you need to prefill the array with NSNull objects e.g.
#implementation NSArray(MyArrayCategory)
+(NSMutableArray*) arrayWithSize: (NSUInteger) size
{
NSMutableArray* ret = [[NSMutableArray alloc] initWithCapacity: size];
for (size_t i = 0 ; i < size ; i++)
{
[ret addObject: [NSNull null]];
}
return [ret autorelease];
}
#end
Edit: some further clarification:
-initWithCapacity: provides a hint to the run time about how big you think the array might be. The run time is under no obligation to actually allocate that amount of memory straight away.
NSMutableArray* foo = [[NSMutableArray alloc] initWithCapacity: 1000000];
NSLog(#"foo count = %ld", (long) [foo count]);
will log a count of 0.
-initWithCapacity: does not limit the size of an array:
NSMutableArray* foo = [[NSMutableArray alloc] initWithCapacity: 1];
[foo addObject: #"one"];
[foo addObject: #"two"];
doesn't cause an error.