Track touch points in Multitouch
Asked Answered
M

2

6

I am working on a drawing project, I want to support multitouch, I have gone through documentation online, which suggest to track the touch points, I did it , But I am not getting the desired behaviour. I am not getting straight lines.

Below is my code

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    red = 0.0/255.0;
    green = 0.0/255.0;
    blue = 0.0/255.0;
    brush = 10.0;
    opacity = 1.0;

     self.view.multipleTouchEnabled = YES;

     touchPaths = [NSMutableDictionary dictionary];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {


    for (UITouch *touch in touches)
    {
        NSString *key = [NSString stringWithFormat:@"%d", (int) touch];
        lastPoint = [touch locationInView:self.view];

        [touchPaths setObject:[NSValue valueWithCGPoint:lastPoint] forKey:key];
    }

}


- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{

    for (UITouch *touch in touches)
    {
        NSString *key = [NSString stringWithFormat:@"%d", (int) touch];

        lastPoint = [[touchPaths objectForKey:key] CGPointValue];


        CGPoint currentPoint1 = [touch locationInView:self.view];

        UIGraphicsBeginImageContext(self.view.frame.size);
        [self.tempDrawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
        CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
        CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint1.x, currentPoint1.y);
        CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
        CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brush );
        CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), red, green, blue, 1.0);
        CGContextSetBlendMode(UIGraphicsGetCurrentContext(),kCGBlendModeNormal);

        CGContextStrokePath(UIGraphicsGetCurrentContext());
        self.tempDrawImage.image = UIGraphicsGetImageFromCurrentImageContext();
        [self.tempDrawImage setAlpha:opacity];
        UIGraphicsEndImageContext();

        lastPoint = currentPoint1;

    }
}

But when I draw using this code, I get this. enter image description here

So friends, please help me out, what I am doing wrong.

Regards Ranjit

Munford answered 24/12, 2013 at 13:18 Comment(0)
M
4

You are not populating the touchPaths properly. Try setting it after each drawing instead, something like this:

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{

    for (UITouch *touch in touches)
    {
        NSString *key = [NSString stringWithFormat:@"%d", (int) touch];

        CGPoint lastPoint = [[touchPaths objectForKey:key] CGPointValue];


        CGPoint currentPoint1 = [touch locationInView:self.view];

        UIGraphicsBeginImageContext(self.view.frame.size);
        [self.tempDrawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
        CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
        CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint1.x, currentPoint1.y);
        CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
        CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brush );
        CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), red, green, blue, 1.0);
        CGContextSetBlendMode(UIGraphicsGetCurrentContext(),kCGBlendModeNormal);

        CGContextStrokePath(UIGraphicsGetCurrentContext());
        self.tempDrawImage.image = UIGraphicsGetImageFromCurrentImageContext();
        [self.tempDrawImage setAlpha:opacity];
        UIGraphicsEndImageContext();
        // I changed your code here
        [touchPaths setObject:[NSValue valueWithCGPoint:currentPoint1] forKey:key];

    }
}

You are currently setting lastPoint here but you do not seem to use it (and it would only work with one touch). There is no need to have lastPoint as a field either.

Mireillemireles answered 30/12, 2013 at 13:34 Comment(12)
thanks, it works, I am doing this because, I wanted to able to write whenever I rest my hand on ipad screen and right. Can guide me how I reject that hand touch and only draw with finger/stylus and whatever is drawn with wrist touch should be cancelled/rejectedMunford
Also , this was just a sample example with two points i.e last point and current point, Now In my project , to draw smooth lines and curves , I have two last points and one current point in touches began, So should I store all of them in separate dictionaries?Munford
See this page for hints on how to draw smoother paths: mobile.tutsplus.com/tutorials/iphone/ios-sdk_freehand-drawingMireillemireles
And regarding finger/hand detection, there are plenty of questions already: #11793010, #4303015, etcMireillemireles
Hey thanks @Krumelur, firstly,the link you suggested for smooth drawing uses UIBezeirPath. Secondly my question is not about smooth drawing, my question is whether I need to add all touch points to separate NSDictionaries.Munford
I am not sure why you need to store more than one point per touch unless you want to use something like bezier paths. I did not get your comment about smooth lines then. May I kindly suggest that you rephrase this problem in a new question?Mireillemireles
let us continue this discussion in chatMunford
can you throw some light on how we can take only the touch points with higher y value.Munford
Hello @Krumelur, are you there, please look at my coomentMunford
Did you see that I responded in the chat?Mireillemireles
can you provide me a code snippet, how to sort it according to y -axis and discard remaining ones, I am getting confused. Here is my new question #20854265Munford
I answered your other question with a snippet. Please check that out.Mireillemireles
H
3

I always try to use gesture-recognizers when possible. Here I use the UIPanGestureRecognizer:

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.view addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(didPan:)]];
}

- (void)didPan:(UIPanGestureRecognizer*)panGesture {
    for (NSUInteger touchIndex = 0; touchIndex < panGesture.numberOfTouches; touchIndex++) {
        // touchIndex is basically the "source" (the finger) from which the point comes from
        CGPoint p = [panGesture locationOfTouch:touchIndex inView:self.view];
        [self drawAtPoint:p withIndex:touchIndex];
    }
}


- (void)drawAtPoint:(CGPoint)point withIndex:(NSUInteger)index{
    UIView *smallPoint = [[UIView alloc] initWithFrame:CGRectMake(point.x, point.y, 3, 3)];
    [smallPoint setBackgroundColor:[self colorForIndex:index]];
    [self.view addSubview:smallPoint];
}

- (UIColor*)colorForIndex:(NSUInteger)index {
    switch (index) {
        case 0: return [UIColor redColor];
        case 1: return [UIColor orangeColor];
        case 2: return [UIColor yellowColor];
        case 3: return [UIColor blueColor];
        case 4: return [UIColor greenColor];
    }
    return [UIColor clearColor];
}

I don't draw a bezier path, but if you drop it in an empty ViewController and run it, you will see that when multi-touching the screen, each finger draws a different colour.

So if you take into consideration the touchIndex, basically you can keep track of different paths for different fingers.

Let's say you use two fingers to draw: you will have panGesture.numberOfTouches == 2, and touchIndex 0 will represent the first finger, and touchIndex 1 will represent the second finger. You can accumulate the points in different arrays and add the points to their corresponding path.

Hydroid answered 30/12, 2013 at 15:24 Comment(1)
nice one, this is clever... (except for the allocating a new uiview for lazy drawing :D ) very smartEuphroe

© 2022 - 2024 — McMap. All rights reserved.