views:

673

answers:

6

like what i want is if i move my finger fast on the iphone screen , then i want like something that it make a proper curve using quartz 2d or opengl es whatever.

i want to draw a path in curve style...... i had seen that GLPaint(OpenglES) example ,but it will not help me alot , considering if your finger movement is fast.....

something like making a smooth curve..... any one have some kind of example please tell me

thanks

+1  A: 

Do something like Shadow suggested. Get the position of the touch with some frequency and then make a Bézier curve out of it. This is how paths are drawn with a mouse (or tablet) in programs like Illustrator.

David Johnstone
Adding to David's comment, what you probably want to do is start from one end of the stroke, and build spline curves that are fit to the data points in piecewise fashion.
Mark Bessey
A: 

thanks to all.......

but i had tried the bezier curve algo with two control points but problem is first how to calculate the control points whether there is no predefined points....

as i mentioned my movement of finger is fast...... so most of the time i got straight line instead of curve, due to getting less number of touch points.......

now as mark said piecewise fashion, ihad tried it like considering first four touch points and render them on screen , then remove the first point then again go for next four points ex. step 1: 1,2,3,4 step 2: 2,3,4,5 like that where as in that approach i got an overlap , which is not the issue actually , but didn't get smooth curve........

but for fast movement of finger i have to find something else?????

rajSriStackOverFlow
+4  A: 

Depending on the number of sample points you are looking at, there are two approaches that I would recommend:

Simple Interpolation

You can simply sample the finger location at set intervals and then interpolate the sample points using something like a Catmull-Rom spline. This is easier than it sounds since you can easily convert a Catmull-Rom spline into a series of cubic Bezier curves.

Here's how. Say you have four consecutive sample points P0, P1, P2 and P3, the cubic Bezier curve that connects P1 to P2 is defined by the following control points:

B0 = P1
B1 = P1 + (P2 - P0)/6
B3 = P2 + (P1 - P3)/6
B4 = P2

This should work well as long as your sample points aren't too dense and it's super easy. The only problem might be at the beginning and end of your samples since the first and last sample point aren't interpolated in an open curve. One common work-around is to double-up your first and last sample point so that you have enough points for the curve to pass through each of the original samples.

To get an idea of how Catmull-Rom curves look, you can try out this Java applet demonstrating Catmull-Rom splines.

Fit a curve to your samples

A more advance (and more difficult) approach would be to do a Least Squares approximation to your sample points. If you want try this, the procedure looks something like the following:

  1. Collect sample points
  2. Define a NURBS curve (including its knot vector)
  3. Set up a system of linear equations for the samples & curve
  4. Solve the system in the Least Squares sense

Assuming you can pick a reasonable NURBS knot vector, this will give you a NURBS curve that closely approximates your sample points, minimizing the squared distance between the samples and your curve. The NURBS curve can even be decomposed into a series of Bezier curves if needed.

If you decide to explore this approach, then the book "Curves and Surfaces for CAGD" by Gerald Farin, or a similar reference, would be very helpful. In the 5th edition of Farin's book, section 9.2 deals specifically with this problem. Section 7.8 shows how to do this with a Bezier curve, but you'd probably need a high-degree curve to get a good fit.

Naaff
+2  A: 

Naaff gives a great overview of the NURBS technique. Unfortunately, I think generating a smooth bezier on-the-fly might be too much for the iPhone. I write drawing apps, and getting a large number of touchesMoved events per second is quite a challenge to begin with. You really need to optimize your drawing code just to get good performance while recording individual points - much less constructing a bezier path.

If you end up going with a bezier or NURBS curve representation - you'll probably have to wait until the user has finished touching the screen to compute the smoothed path. Doing the math continuously as the user moves their finger (and then redrawing the entire recomputed path using Quartz) is not going to give you a high enough data collection rate to do anything useful...

Good luck!

Ben Gotow
So what would you recommend then? -1 for lack of answer here. (But I guess +1 for adding some useful context... so net 0.)
livingtech
A: 

Here is some actual code that does just what Naaff says above - use the lovely Catmull-Rom algorithm. A couple of trivial definitions and functions are probably missing but just treat it as pseudocode, you'll have no trouble, it's easy.

So first collect an array of points just using touchesMoved in cocoa. So if there's 200 points we'll end up with 199 quad curves between each pair of points -- each one determined by the Catmull-Rom algorithm. Simply put all 199 bezier curves in to the one UIBezierPath, and then "stroke" it and you're done. Any old iPhone can do googles of these per frame, so it's nothing to process.

So there's an array of points. ("pearls.") The routine addCubicLeadingTo: will figure the bezier curve for the segment between the PREVIOUS number and that one. (So for example if you send it "6" it will do the segment between 5 and 6.)

    -(void)addCubicLeadingTo:(int)a
        {
        if (a==0) return;
        if (a<2) // just add a straight line
            {
            [longPath addLineToPoint:PEARLASPOINT(a)];
            return;
            }

        CGPoint cpA, cpB;

        /* the beautiful and crazy simple Catmull-Rom spline...
        "B0 = P1
        B1 = P1 + (P2 - P0)/6
        B3 = P2 + (P1 - P3)/6
        B4 = P2" */

    #define PVPV pearls[a-2]        // P0
    #define PREV pearls[a-1]        // P1
    #define THIS pearls[a]      // P2
    Pearl NEXT;                 // P3

        if (a == (pearlCount-1))
            NEXT = pearls[a];
        else
            NEXT = pearls[a+1];

        cpA.x = PREV.across + ( (THIS.across-PVPV.across) / 6.0 );
        cpA.y = PREV.down + ( (THIS.down-PVPV.down) / 6.0 );

        cpB.x = THIS.across + ( (PREV.across-NEXT.across) / 6.0 );
        cpB.y = THIS.down + ( (PREV.down-NEXT.down) / 6.0 );

        [longPath addCurveToPoint:PEARLASPOINT(a) controlPoint1:cpA controlPoint2:cpB];

    #undef THIS
    #undef PREV
    #undef PVPV
        }

    UIBezierPath            *longPath;
    #define PEARLASPOINT(n) CGPointMake(pearls[n].across, pearls[n].down)

    -(void)prep
        {
        pearlCount = 0;
        longPathIsClosed = NO;
        longPath = [[UIBezierPath bezierPath] retain];
        .. etc
        }

drawCubicThrills is of course called in your drawRect. So it's this easy ...

-(void) drawCubicThrills    // all at once!
    {
    if ( pearlCount < 2 ) return;
    [longPath removeAllPoints];

    CGContextSetRGBStrokeColor(everydayContext, 0.1,0.1,0.9, 0.7);
    longPath.lineWidth = 50;
    longPath.lineCapStyle = kCGLineCapRound;
    longPath.lineJoinStyle = kCGLineJoinRound;

    [longPath moveToPoint:PEARLASPOINT(0)];

    static int d;
    for (d=1; d<pearlCount; ++d)
        {
        [self addCubicLeadingTo:d];
        }

    // and finally ..
    if ( longPathIsClosed == YES )
        [longPath closePath];

    [longPath stroke];
    }

-(void)handleFingerUp
    {
    longPathIsClosed = YES;
    [self setNeedsDisplay];
    }

I hope it helps! In answer to your other much more difficult question, separately.

Joe Blow
A: 
Joe Blow