Reza.Ab Reza.Ab - 2 months ago 14
Objective-C Question

Efficient way to find the nearest point from a set of points to the touched point inbetween a range?

I have an array of CGPoints which are used to drawn a Catmull-Rom bezierpath using CGPoint interpolation by jnfisher What I want is to choose the nearest point on the path to the touch point when user touches anywhere inside a range around any point on the path. That range is specified as an imaginary square containing the point on the path with a tolerance before and after every point on the path along X and Y axis. My strategy is to 1. Set a tolerance for each point on the path which will observe if the location of touch is inside the square with these sides on X Axis:(discoveryPoint.x - tolerance to discoveryPoint.x + tolerance) and on Y axis:(discoveryPoint.y - tolerance to discoveryPoint.y + tolerance. 2.Calculate the distance the between the touched point and all the points in my array which contain the touched point inside their square. 3. choose the minimum amount from distances. 4. find the point that has the least distance from the touched point. Is there any easier and more efficient way? I highly appreciate any help.Thanks in advance.

-(void)drawRect:(CGRect)rect{
self.modifiablePath = [UIBezierPath interpolateCGPointsWithCatmullRom:self.pointsArray closed:YES alpha:0.9];
self.modifiablePath.lineWidth = 20.0;
[[UIColor yellowColor] setStroke];
[self.modifiablePath stroke];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
{

CGPoint touchedPoint = [[touches anyObject]locationInView:self];
CGPoint discoveryPoint;
CGFloat tolerance = 20;
int numOfPointsWithMinDistance=0;
NSDictionary* pointsWithMinDistanceWithKeys = [NSDictionary new];
NSDictionary* minDistancesWithKeys = [NSDictionary new];
// Had to Create these two arrays because there's no such thing as [NSDictionary objectAtIndex:]
NSArray *pointsWithMinDistancesArray = [NSArray new];
NSArray *minDistanceArray = [NSArray new];

for (NSValue * cgpointVal in self.pointsArray){
discoveryPoint = cgpointVal.CGPointValue;

if (fabs(touchedPoint.x - discoveryPoint.x)<tolerance && fabs(touchedPoint.y - discoveryPoint.y)<tolerance) {
numOfPointsWithMinDistance++;
//Adding the points which touchedPoint is inside their range(Square).
[pointsWithMinDistanceWithKeys setValue:[NSValue valueWithCGPoint:discoveryPoint] forKey:[NSString stringWithFormat:@"%d",numOfPointsWithMinDistance]];

//Calculating the distance between points with touchedPoint in their range(Square) and adding them to an array.
CGFloat distance = hypotf(touchedPoint.x - discoveryPoint.x, touchedPoint.y - discoveryPoint.y);
[minDistancesWithKeys setValue:[NSNumber numberWithFloat:distance] forKey:[NSString stringWithFormat:@"%d",numOfPointsWithMinDistance]];
}
}
//Finding the key for minimum distance between all distances to the touchedPoint.
minDistanceArray = [minDistancesWithKeys allValues];
pointsWithMinDistancesArray = [pointsWithMinDistanceWithKeys allValues];

CGFloat k = [[minDistanceArray objectAtIndex:0] floatValue];
int keyOfPointWithMinDistance =0;
for (unsigned i = 1; i <[minDistanceArray count]; i++){

if([[minDistanceArray objectAtIndex:i] floatValue] < k){
k = [[minDistanceArray objectAtIndex:i] floatValue];
keyOfPointWithMinDistance=i;
}else{
keyOfPointWithMinDistance=0;
}
}
//Getting the nearest CGPoint value with the smallest distance to the touchedPoint.
NSValue * valueOfNearestPoint =[pointsWithMinDistancesArray objectAtIndex:keyOfPointWithMinDistance];
CGPoint nearestPointToTouchedPoint =valueOfNearestPoint.CGPointValue;
}

Answer
  • (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ CGPoint touchedPoint = [[touches anyObject]locationInView:self]; CGPoint discoveryPoint; CGFloat tolerance = 20; // Had to Create these two arrays because there's no such thing as [NSDictionary objectAtIndex:] NSArray *pointsArray;

    CGFloat keyOfPointWithMinDistance = -1; CGPoint nearestPointToTouchedPoint = CGPointZero; Int index = 0; Int keyIndex = NSNotFound;

    for (NSValue * cgpointVal in self.pointsArray){

    discoveryPoint = cgpointVal.CGPointValue;
    
    if (fabs(touchedPoint.x - discoveryPoint.x)<tolerance && fabs(touchedPoint.y - discoveryPoint.y)<tolerance) {
        //Calculating the distance between points with touchedPoint in their range(Square) and adding them to an array.
        CGFloat distance = hypotf(touchedPoint.x - discoveryPoint.x, touchedPoint.y - discoveryPoint.y);
    
        if (keyOfPointWithMinDistance == -1 || keyOfPointWithMinDistance < distance) {
            keyOfPointWithMinDistance = distance;
            nearestPointToTouchedPoint = discoveryPoint;
            keyIndex = index;
        }
    
        index++;
    }
    

    }

    //Update self.pointsArray using keyIndex

    if keyIndex != NSNotFound {

    }

}