Grouping Pixels in Objective-C - ios

I am making a drawing app, and I have a NSArray with a list of all the pixels the user forget or didn't clean, what i want is make groups of that list pixels using proximity. so i can highlight all the pixel area with a black circle. this is what i got for the moment but is not grouping well
-(NSMutableArray*)orderMissingPixelsByGroups:(NSMutableArray*)missingPixels
{
NSMutableArray *finalArray=[[NSMutableArray alloc]init];
NSMutableArray *auxArray = [[NSMutableArray alloc]init];
for (int i=0; i<[missingPixels count]; i++) {
NSValue *value = [missingPixels objectAtIndex:i];
if (i<[missingPixels count]-1) {
NSValue *nextValue = [missingPixels objectAtIndex:i+1];
CGPoint point = value.CGPointValue;
CGPoint point2 = nextValue.CGPointValue;
if (abs(point.x-point2.x)<5 ) {
[auxArray addObject:[NSValue valueWithCGPoint:point]];
}
else if (abs(point.y-point2.y)<5){
[auxArray addObject:[NSValue valueWithCGPoint:point]];
}
else
{
[auxArray addObject:[NSValue valueWithCGPoint:point]];
[finalArray addObject:auxArray];
auxArray = [[NSMutableArray alloc]init];
}
}
else
{
[auxArray addObject:value];
[finalArray addObject:auxArray];
}
}
return finalArray;
}
If you have advices I will very thankful.

Yes I can imagine this is not the best approach. Try doing something like this. Note I did not test it also there is just too much room for optimizing this approach but I hope it will help you get a bit better understanding on how to create "blobs". Otherwise you can try to search for some fast approach under "blob" keyword...
- (NSMutableArray *)createBlobsFromPixels:(NSMutableArray *)pixels {
NSMutableArray *initialBlobs = [[NSMutableArray alloc] init];
for(NSValue *pixel in pixels)
{
[initialBlobs addObject:#[pixel]];
}
while (YES) {
NSIndexSet *indexSet = [self joinBlobs:initialBlobs minimumRadius:5.0f];
if(indexSet.count < 2) {
break;
}
else {
[self mergeBlobs:initialBlobs atIndexA:indexSet.firstIndex andIndexB:indexSet.lastIndex];
}
}
return initialBlobs;
}
- (NSIndexSet *)joinBlobs:(NSMutableArray *)blobs minimumRadius:(CGFloat)minRadius {
// this method will try to join 2 blobs
// if successfull it will return an index set with 2 blob indices which should be joined
// if no blobs are able to join the method will return nil
if(blobs.count < 2) {
// can not join a single blub
return nil;
}
NSUInteger count = blobs.count;
// iterate through every pair
for(NSUInteger i=0; i<count-1; i++) {
for(NSUInteger j=i+1; j<count; j++) {
NSArray *blobA = blobs[i];
NSArray *blobB = blobs[j];
// check if any of the pixels from blob A are close enough from any pixel in blob B and merge those two blobs
for(NSValue *pixelA in blobA) {
for(NSValue *pixelB in blobB) {
CGPoint pointA = [pixelA CGPointValue];
CGPoint pointB = [pixelB CGPointValue];
if(fabsf(pointA.x-pointB.x)<minRadius && fabsf(pointA.y-pointB.y)<minRadius) {
// can join these 2 blobs
NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] init];
[indexSet addIndex:i];
[indexSet addIndex:j];
return indexSet; // returns YES as the blob can been joined
}
}
}
}
}
return nil;
}
- (void)mergeBlobs:(NSMutableArray *)blobs atIndexA:(NSUInteger)indexA andIndexB:(NSUInteger)indexB {
NSArray *blobA = blobs[indexA];
NSArray *blobB = blobs[indexB];
[blobs removeObject:blobA];
[blobs removeObject:blobB];
[blobs addObject:[blobA arrayByAddingObjectsFromArray:blobB]];
}

Related

Does CHCSVParser have a column parsing limit?

In my application you can import data through a tab separated values file. I don't have any challenge until I parse "locations" that have multiple items attached to them. If you scroll to the very bottom of the second method you can see how I create a relationship between items and the locations that contain them inside Core Data. The problem occurs when I parse past column 31 in a location. It doesn't attach those items to the location. So my question is this; is there a limit to columns in the NSArray that is parsed by CHCSVParser? If not, what would cause this limiting to 31 columns?
I've posted the two methods that I encounter the bug with below.
+ (void) importDatabaseTSVURL:(NSURL*)url {
// First check if there is already a database. If so, stop import.
if ([XSELLocation locations].count > 0) return;
if ([XSELItem items].count > 0) return;
if ([XSELVendor vendors].count > 0) return;
NSError *error;
NSArray *array = [NSArray arrayWithContentsOfDelimitedURL:url options:CHCSVParserOptionsSanitizesFields delimiter:'\t' error:&error];
if ([[[array firstObject] firstObject] isEqualToString:#"XSELINVENTORYTSV"]) {
for (NSArray *row in array) {
[XSELSettings parseImportDataRow:row];
}
}
}
+ (void) parseImportDataRow:(NSArray*)array {
// Create logic to seperate data entered next
static NSString *operation = #"none";
if ([array.firstObject isEqualToString:#"ITEMLIST"]) {
operation = #"items";
return;
}
else if ([array.firstObject isEqualToString:#"LOCATIONLIST"]) {
operation = #"locations";
return;
}
else if ([array.firstObject isEqualToString:#"VENDORLIST"]) {
operation = #"vendors";
return;
}
else if ([array.firstObject isEqualToString:#"ENDLIST"]) { // Create database, relate objects, and clean up the data
operation = #"none";
return;
}
// Parse rows to the correct array.
if ([operation isEqualToString:#"vendors"]) {
NSLog(#"adding vendor");
XSELVendor *vendor = [XSELVendor addVendor];
vendor.vendorID = [NSNumber numberWithInteger:[[array objectAtIndex:0] integerValue]];
[XSELSettings nextVendorID];
vendor.name = [array objectAtIndex:1];
vendor.contactID = [array objectAtIndex:2];
}
else if ([operation isEqualToString:#"items"]) {
NSLog(#"adding item");
XSELItem *item = [XSELItem addItem];
item.itemID = [NSNumber numberWithDouble:[[array objectAtIndex:0] integerValue]];
[XSELSettings nextItemID];
item.name = [array objectAtIndex:1];
item.smallPackageName = [array objectAtIndex:2];
item.bigPackageName = [array objectAtIndex:3];
item.smallPerBig = [NSNumber numberWithDouble:[[array objectAtIndex:4] integerValue]];
item.buildTo = [NSNumber numberWithDouble:[[array objectAtIndex:5] integerValue]];
item.price = [NSNumber numberWithDouble:[[array objectAtIndex:6] integerValue]];
// Relate preferred vendor to item
for (XSELVendor *vendor in [XSELVendor vendors]) {
if ([vendor.vendorID.stringValue isEqualToString:[array objectAtIndex:7]]) {
item.preferredVendor = vendor;
break;
}
}
}
else if ([operation isEqualToString:#"locations"]) {
NSLog(#"adding location");
XSELLocation *location = [XSELLocation addLocation:[array objectAtIndex:1]];
location.locationID = [NSNumber numberWithInteger:[[array objectAtIndex:0] integerValue]];
[XSELSettings nextLocationID];
location.position = [NSNumber numberWithInteger:[[array objectAtIndex:2] integerValue]];
// Relate location with items
unsigned long itemsRelatedCount = array.count - 3;
NSLog(#"\n\nitemsRelated: %lu\n\n", itemsRelatedCount);
NSMutableOrderedSet *items = [NSMutableOrderedSet orderedSet];
for (int i = 0; i < itemsRelatedCount; i++) {
NSString *itemID = [array objectAtIndex:i];
for (XSELItem *item in [XSELItem items]) {
if ([item.itemID.stringValue isEqualToString:itemID]) {
[items addObject:item];
break;
}
}
}
location.items = items;
}
}

Remove array elements and add them at the same index iOS

I am sorting an array.
There are three types of elements in the array.
1. featured
2. organic and
3. claimed.
Among them, I want to sort only organic elements and keep the featured and claimed elements at their own index.
Below is my code in which, I am extracting the claimed and featured indices in a dictionary as key being the index and value is the array element.
//Initialization
NSMutableArray *sortedArray = nil;
NSMutableDictionary *tempFeaturedDictionary = [[NSMutableDictionary alloc]init];
NSMutableDictionary *tempClaimedDictionary = [[NSMutableDictionary alloc]init];
NSMutableArray *tempOrganicArray = [[NSMutableArray alloc]init];
for (int i = 0; i < array.count; i++) {
DRListing *isFeaturedObj = (DRListing*)[array objectAtIndex:i];
if (isFeaturedObj.featured) {
[tempFeaturedDictionary setObject:isFeaturedObj forKey:[#(i)stringValue]];
}else if (isFeaturedObj.claimed)
{
[tempClaimedDictionary setObject:isFeaturedObj forKey:[#(i)stringValue]];
}else
[tempOrganicArray addObject:isFeaturedObj];
}
Again I am adding the claimed and featured back to their original indices after sorting as:
sortedArray = [NSMutableArray arrayWithArray:[tempOrganicArray sortedArrayUsingDescriptors:sortDescriptorsArray]];
for (int i = 0; i<sortedArray.count; i++) {
for (NSString *key in tempFeaturedDictionary) {
if ( [[#(i)stringValue] isEqualToString: key] ) {
[sortedArray insertObject:[tempFeaturedDictionary objectForKey:[#(i)stringValue]] atIndex:i];
}}
for (NSString *key in tempClaimedDictionary) {
if ([[#(i)stringValue]isEqualToString:key ]) {
[sortedArray insertObject:[tempClaimedDictionary objectForKey:[#(i)stringValue]] atIndex:i];
}
}
}
The code works good. Except there is claimed/(and)featured elements at the last index of the 'array'. Because the 'sortedArray' index remains less than the 'array.count' in this scenario.
Thanks in advance.
Update -
I receive response array of type:
[{featured1 featured2}, {organic1, organic2..}, {claimed1}, {featured11, featured12}, {organic11, organic12..}, {claimed2}, ..]
and I am allowed to sort only organic elements within this array. Featured and claimed should not loose their original index position.
I would iterate through the array, extracting the organics to sort. Then sort your organic array. Then iterate through the original array taking either the element from the original array or an element from the sorted organics array as appropriate.
NSMutableArray *organicsArray = [NSMutableArray new];
for (int i = 0; i < array.count; i++) {
DRListing *isFeaturedObj = (DRListing*)array[i];
if ((!isFeaturedObj.featured) && (!isFeaturedObj.claimed)) {
[organicsArray addObject:isFeaturedObj];
}
}
NSMutableArray *sortedOrganicsArray = [[organicsArray sortedArrayUsingDescriptors:sortDescriptorsArray] mutableCopy];
NSMutableArray *outputArray = [NSMutableArray new];
for (int i = 0; i < array.count; i++) {
DRListing *isFeaturedObj = (DRListing*)array[i];
if ((!isFeaturedObj.featured) && (!isFeaturedObj.claimed)) {
[outputArray addObject:sortedOrganicsArray[0]];
[sortedOrganicsArray removeObjectAtIndex:0];
} else {
[outputArray addObject:isFeaturedObject];
}
}
You could possibly make it a little more efficient if you reversed your sort order for the organics array since then you could say
[outputArray addObject:[sortedOrganicsArray lastObject]];
[sortedOrganicsArray removeLastObject];
But if your array isn't particularly large then the performance improvement will probably be negligible.
Maybe this is an alternative:
NSMutableArray *organics = [NSMutableArray new];
NSMutableArray *others = [NSMutableArray new];
for (DRListing *isFeaturedObj in array) {
if (isFeaturedObj.organic) {
[organics addObject:isFeaturedObj];
} else {
[others addObject:isFeaturedObj];
}
}
NSMutableArray *sorted = [NSMutableArray alloc]initWithObjects:organics,others, nil];
You can take the first 2 functions. The others are what I used for testing.
- (DRListing *)getNextObjectFromArray:(NSArray *)array WithStartingIndex:(int)index
{
for (int i=index; i<array.count; i++) {
DRListing *obj = (DRListing*)[array objectAtIndex:i];
if (!obj.featured && !obj.claimed)
{
return obj;
}
}
return nil;
}
- (void)sortArray:(NSMutableArray *)array
{
for (int pass = 0; pass<array.count-1; pass++) {
for (int i=0; i<array.count-1; i++) {
DRListing *obj = [self getNextObjectFromArray:array WithStartingIndex:i];
int foundIndex = (int)[array indexOfObject:obj];
DRListing *obj2 = [self getNextObjectFromArray:array WithStartingIndex:foundIndex+1];
int foundIndex2 = (int)[array indexOfObject:obj2];
if (obj!=nil && obj2 !=nil) {
if (obj.value >= obj2.value) {
[array exchangeObjectAtIndex:foundIndex withObjectAtIndex:foundIndex2];
}
i = foundIndex;
}
}
}
NSLog(#"Sorted Data: %#",array);
}
- (NSMutableArray *)testData
{
NSMutableArray *array = [NSMutableArray new];
for (int i=0; i<20; i++) {
DRListing *obj = [DRListing new];
obj.featured = i*i%2;
obj.claimed = i%2;
obj.value = i*3%10;
[array addObject:obj];
}
NSLog(#"Test Data: %#",array);
return array;
}
#interface DRListing : NSObject
#property (nonatomic) BOOL featured;
#property (nonatomic) BOOL claimed;
#property (nonatomic) int value;
#end

NodesAtPoint does not find a node

I'm trying to create 10 balloons in my app. I create a random CGPoint to set my node's position and check, in case that already exist a node at this point I try again.
I don't know why, the balloons keep being placed over another balloon.
Any ideia of what I am doing wrong?
Here is my code:
-(void)gerarBaloes:(int)quantidade
{
balloons = [NSMutableArray new];
u_int32_t maxHorizontal = 900;
u_int32_t minHorizontal = 100;
u_int32_t maxVertical = 650;
u_int32_t minVertical = 100;
for (int i=1; i<= quantidade; i++) {
CGPoint posicao = CGPointMake(arc4random_uniform(maxHorizontal - minHorizontal + 1) + minHorizontal, arc4random_uniform(maxVertical - minVertical + 1) + minVertical);
while (![self validPosition:posicao]) {
posicao = CGPointMake(arc4random_uniform(maxHorizontal - minHorizontal + 1) + minHorizontal, arc4random_uniform(maxVertical - minVertical + 1) + minVertical);
}
balao* nBalao = [[balao alloc]initWithPosition:posicao];
SKLabelNode* numero = [[SKLabelNode alloc]initWithFontNamed:#"Arial Rounded MT Bold"];
numero.text = [NSString stringWithFormat:#"%d",i];
numero.zPosition = 1;
nBalao.body.zPosition = 0;
[nBalao addChild:numero];
nBalao.body.name = #"balloon";
[balloons addObject:nBalao];
[self addChild:nBalao];
}
}
And this is my validation method:
-(BOOL)validPosition:(CGPoint)position
{
NSArray* nodes = [self nodesAtPoint:position];
NSLog(#"total de nodes %d",nodes.count);
if (nodes.count > 0) {
for (SKNode* n in nodes) {
if ([n isKindOfClass:[balao class]]) {
return NO;
}
}
return YES;
}else{
return YES;
}
}
Balao.m class:
#implementation balao
-(id)initWithPosition:(CGPoint)pos
{
if (self = [super init]) {
self.position = pos;
int n = arc4random_uniform(4);
_balloonAtlas = [SKTextureAtlas atlasNamed:[NSString stringWithFormat:#"balloon%d",n]];
_explosionFrames = [NSMutableArray array];
int numImages = _balloonAtlas.textureNames.count;
for (int i=1; i<= numImages; i++){
NSString *textureName = [NSString stringWithFormat:#"balloon_%d",i];
SKTexture *temp = [_balloonAtlas textureNamed:textureName];
[_explosionFrames addObject:temp];
}
SKTexture *textInicial = [_explosionFrames[0] copy];
[_explosionFrames removeObjectAtIndex:0];
self.body = [SKSpriteNode spriteNodeWithTexture:textInicial];
[self addChild:_body];
}
return self;
}
- (void)explodeBalloon
{
SKAction* explosao = [SKAction animateWithTextures:_explosionFrames timePerFrame:0.02f resize:NO restore:YES];
[self.body runAction:explosao completion:^(void){
[self removeFromParent];
}];
[self runAction:[SKAction playSoundFileNamed:#"popSound.mp3" waitForCompletion:NO]];
}
This is how the balloons appears:
Edit:
I tried the suggested method, with CGRectCointainsPoint, but it didn't work too. :/
Checking for nodes at a given point is not enough to prevent overlap. You need to check whether the point falls inside the frame of another balloon. You can modify the function like this
-(BOOL)validPosition:(CGPoint)position
{
NSArray* nodes = [self children];
if (nodes.count > 0) {
for (SKNode* n in nodes) {
if ([n isKindOfClass:[balao class]]) {
if (CGRectContainsPoint([n getBalloonFrame], position)) // Changed line
{
return NO
}
}
}
return YES;
}
return YES;
}
CGRectContainsPoint checks whether a given rectangle contains a point.
Inside balao class define the following function. Also note the changed line in the above code.
-(void)getBalloonFrame
{
return CGRectOffset(self.body.frame, self.frame.origin.x, self.frame.origin.y)
}

Removing Duplicate Custom Objects from two arrays and combining them

First off i have searched a lot but all methods seems to be for primitives or for whole custom objects.
My situation is this. I have a type custom objects in two different arrays. However the fields of every single objects is quite different to another with the exception of only 2 fields.
What i want is combine both of these arrays and then remove duplicates with respect to only those two fields.How can i do that. My Code so far
NSMutableArray* testArray = [eventHandler returnAllEvents];
NSMutableArray* combinedArray = [[NSMutableArray alloc]init];
NSArray* finalArray = [[NSArray alloc]init];
if (testArray.count==0) {
for (int i = 0; i<facebookData.count; i++) {
LSEvent* event = [facebookData objectAtIndex:i];
[combinedArray addObject:event];
}
finalArray = [combinedArray arrayByAddingObjectsFromArray:calendarData];
}
NSMutableArray *uniqueArray = [NSMutableArray array];
NSMutableSet *names = [NSMutableSet set];
for (id obj in finalArray) {
NSString *destinationName = [obj destinationname];
if (![names containsObject:destinationName]) {
[uniqueArray addObject:obj];
[names addObject:destinationName];
}
}
You can do sth like this
NSArray first = ...
NSMutableArray second = ... // this will be combine array
for (id someObj in first) {
if ( [second filteredArrayUsingPredicate:[self predicateForObject:someObj ]].count == 0 ){
[second addObject: someObj];
}
}
If you want to check that object exists in array using containsObject: you need to implement - (BOOL)isEqual:(id)other in your custom object.
- (BOOL)isEqual:(id)other {
if (other == self) {
return YES;
}
if (!other || ![other isKindOfClass:[self class]]) {
return NO;
}
if (self.identifier == other.identifier) {
return NO;
}
return YES;
}

How to delete the number from non repeating number array?

I am trying this code for generating a random number and saving the list of numbers in an array, then i am trying to delete those numbers from the list one by one which appeared once, e.g
1, 5, 9 , 4, 3, 7 ,6 ,10, 11, 8, 2 are the list of integers now 9 is appeared once and now i do not need 9 again.. this is my code of random non repeating numbers array.
NSMutableArray *storeArray = [[NSMutableArray alloc] init];
BOOL record = NO;
int x;
for (int i=0; [storeArray count] < 10; i++) //Loop for generate different random values
{
x = arc4random() % 10;//generating random number
if(i==0)//for first time
{
[storeArray addObject:[NSNumber numberWithInt:x]];
}
else
{
for (int j=0; j<= [storeArray count]-1; j++)
{
if (x ==[[storeArray objectAtIndex:j] intValue])
record = YES;
}
if (record == YES)
{
record = NO;
}
else
{
[storeArray addObject:[NSNumber numberWithInt:x]];
}
}
}
Try this,
NSArray *arrRandoms = [[NSArray alloc]initWithObjects:1,5,8,7,36,17,96,32,5,7,8,13,36,nil] ; // This contains your random numbers
NSMutableArray *arrFresh = [[NSMutableArray alloc]init];
// Now removing the duplicate numbers
BOOL checkRepeat = NO;
int _Current;
for (int i=0; i<[arrRandoms count]; i++)
{
_Current = [arrRandoms objectAtIndex:i];
if (i == 0)
[arrFresh addObjects:_Current];
else
{
checkRepeat = NO;
for(int j=0; j< [arrFresh count]; j++)
{
if ( _Current == [arrFresh objectAtIndex:j])
checkRepeat = YES;
}
if (checkRepeat == NO)
[arrFresh addObjects:_Current];
}
}
I think this code will work. Check It.
try it
//**************remove repeat objects from array***************************//
NSArray *noDuplicates = [[NSSet setWithArray: yourArray] allObjects];
you add
.h file
BOOL isSame;
NSMutableArray *countArray;
NSInteger randomNumber;
.m file
countArray=[[NSMutableArray alloc]init];
//get randon no
-(NSInteger)getRandomNo:(NSInteger)range
{
isSame=TRUE;
while (isSame){
isSame = FALSE;
randomNumber = arc4random() % range;
for (NSNumber *number in countArray){
if([number intValue] ==randomNumber){
isSame = TRUE;
break;
}
}
}
[countArray addObject:[NSNumber numberWithInt:randomNumber]];
return randomNumber;
}
Tested Please try this,
NSMutableArray *storeArray = [[NSMutableArray alloc] init];
NSMutableSet * setUnique = [[NSMutableSet alloc] init];
for (int i=0; [setUnique count] < 10; i++) //Loop for generate different random values
{
[setUnique addObject:[NSNumber numberWithInt:arc4random() % 10]];
}
storeArray = [[setUnique allObjects] mutableCopy];
// check this how to get random number in array (i.e.. this array(below arr_numbers) contain non repeated number)
// for general purpose i am posting this.. so people cannot check for another answer
int num_count = 10;
int RandomNumber;
NSMutableArray *arr_numbers = [[NSMutableArray alloc] init];
for (int j =0; j < num_count; j++)
{
RandomNumber = 0 + arc4random() % num_count;
NSLog(#"%d",RandomNumber);
if ([arr_numbers count]>0)
{
if (![arr_numbers containsObject:[NSNumber numberWithInt:RandomNumber]])//
{
[arr_numbers addObject:[NSNumber numberWithInt:RandomNumber]];
}
if (j == num_count-1)
{
if ([arr_numbers count] != num_count)
{
j = 0;
}
}
}
else
{
[arr_numbers addObject:[NSNumber numberWithInt:RandomNumber]];
}
}
NSLog(#"%#",arr_numbers);

Resources