Loading ...
Sorry, an error occurred while loading the content.
 

Re: [canvas-developers] save() and restore() (again)

Expand Messages
  • Jerason Banes
    Ok, that s bizarre. Your code actually works in WebKit! (It s not supposed to do that. :P) Your problem is that you re using the pen state via moveTo. The
    Message 1 of 9 , Sep 24, 2008
      Ok, that's bizarre. Your code actually works in WebKit! (It's not supposed to do that. :P)

      Your problem is that you're using the pen state via moveTo. The state of the pen is not saved. What you want is to move the transformation matrix like this:

                  canvas = document.getElementById("myc");
                  context = canvas.getContext('2d');
                  
                  //Set the default pen location
                  context.moveTo(0,0);

                  // Save the current state
                  context.save();
                  
                  // Now go draw something
                  context.translate(100,100);
                  context.arc(0,0,10,0,6.28, false);
                  context.fill();
                  
                  // End up back at 0,0 ? Yup.
                  context.restore();

                  context.lineTo(300,300);
                  
                  context.stroke();

      Note that FireFox appears to have some odd bugs in its pen handling. If you don't set a location for the pen, it will default to the last point that was drawn at. Which appears to be 100,100 after the translate. That's why I added the moveTo(0, 0) at the top.

      Thanks!
      Jerason Banes

      On Wed, Sep 24, 2008 at 11:54 AM, Richard Heyes <richard.heyes@...> wrote:

      Hi,

      Apparently I didn't quite get it - I made an example here:

      http://www.phpguru.org/RGraph_dev/examples/text.html

      My understanding is this:

      1. We're at 0,0
      2. Save the current state
      3. Do some stuff, ie move to 100,100 and draw a circle and fill it.
      4. Call restore(). As I understand this will move us back to 0,0
      5. It doesn't.

      When the line is drawn I would have expected it to come from 0,0, ie
      the position at which save() was called, but it doesn't. It comes from
      where it finished drawing the circle. What am I doing wrong? Or have I
      completely misunderstood?

      Thanks!

      --
      Richard Heyes

      HTML5 Graphing for FF, Chrome, Opera and Safari:
      http://www.phpguru.org/RGraph.



    • Vladimir Vukicevic
      ... Hmm... not quite :) At context creation, there is no current point ; all the various path functions do specific things if there is no current point (which
      Message 2 of 9 , Sep 24, 2008

        On Sep 24, 2008, at 10:08 AM, Jerason Banes wrote:

        Ok, that's bizarre. Your code actually works in WebKit! (It's not supposed to do that. :P)

        Your problem is that you're using the pen state via moveTo. The state of the pen is not saved. What you want is to move the transformation matrix like this:

                    canvas = document.getElementById("myc");
                    context = canvas.getContext('2d');
                    
                    //Set the default pen location
                    context.moveTo(0,0);

                    // Save the current state
                    context.save();
                    
                    // Now go draw something
                    context.translate(100,100);
                    context.arc(0,0,10,0,6.28, false);
                    context.fill();
                    
                    // End up back at 0,0 ? Yup.
                    context.restore();

                    context.lineTo(300,300);
                    
                    context.stroke();

        Note that FireFox appears to have some odd bugs in its pen handling. If you don't set a location for the pen, it will default to the last point that was drawn at. Which appears to be 100,100 after the translate. That's why I added the moveTo(0, 0) at the top.

        Hmm... not quite :)

        At context creation, there is no "current point"; all the various path functions do specific things if there is no current point (which is the state you get to if you call beginPath).  Specifically: moveTo just sets the current point; lineTo will act as a moveTo if there is no current point, and the arc functions will add a line from the current point to the start of the arc (if there is no current point, then no line will be added).  Also, the current point is not affected by save/restore, and all path operations have the transform immediately applied to them.  So here's what's happening in the above snippet:

        moveTo(0,0);
        // current point is now (0,0);

        save();
        translate(100,100);

        // current point is still (0,0); -- if at this point you did a lineTo(0,0); you'd end up with a line from 0,0 to 100,100.

        arc(0,0, 10, 0, 2*Math.PI, false);

        // arc() draws a circle at 100,100 of radius 10.
        // But there is already a current point, because of the original moveTo() -- so arc() adds a line from the current point to the start of the arc.
        // After the arc(), the current point is at the end of the arc that was drawn -- a point at the right side of the circle, which in this case is 110,100 (same as the start of the arc)

        fill();

        // the current path (which is line + circle) is filled, but the line doesn't show up because it's just a line.. so only the circle is visible.  Change this to a stroke() to see the line.

        restore();

        // The current point now is not 0,0, but is still the right point of the circle.  The first line and circle are still part of the path, since beginPath wasn't called.

        lineTo(300,300);

        // A line is added from the rightmost point of the arc (110,100) to 300,300

        stroke();

        // Everything is stroked -- the line from 0,0 to the start of the circle, the circle, and the line from the end of the circle to 300,300.

        The line that's visible at the end of the snippet is not a line from 0,0 to 300,300 -- if you draw a separate line from 0,0 to 300,300 you can see that they don't line up; the line from 0,0 to 300,300 would pass right through the circle, whereas the one that the snippet draws goes to 110,100 and then bends slightly back to get to 300,300.

        Note that currently released versions of Safari have some differences in the handling of the current point... WebKit nightlies produce the same output as Firefox 3.

        Hope that helps!

            - Vlad

      • Jerason Banes
        ... // current point is now (0,0); ... translate(100,100); ... So what you re saying is that the path is not affected by the transform matrix until it is
        Message 3 of 9 , Sep 24, 2008
          moveTo(0,0);
          // current point is now (0,0);

          save();
          translate(100,100);

          // current point is still (0,0); -- if at this point you did a lineTo(0,0); you'd end up with a line from 0,0 to 100,100.

          So what you're saying is that the path is not affected by the transform matrix until it is restarted? So simply transforming it will not have the desired effect without calling beginPath()? That appears to be how it tests. Your code above + a lineTo() does indeed draw between (0,0)-(100,100). Restarting the path like this...

                      context.save();
                      context.translate(100,100);
                      context.beginPath();
                      context.moveTo(0,0);
                      context.lineTo(50,50);
                      context.stroke();
                      context.restore();

          ...appears to give the expected result of (100,100)-(150,150).

          In which case, the proper code that Mr. Hayes is looking for would be:

                      canvas = document.getElementById("myc");
                      context = canvas.getContext('2d');
                      
                      // Save the current state
                      context.save();
                      
                      // Now go draw something
                      context.translate(100,100);
                      context.beginPath();
                      context.moveTo(0,0);
                      context.arc(0,0,10,0,6.28, false);
                      context.fill();
                      
                      // End up back at 0,0 ? Apparently not...
                      context.restore();

                      context.beginPath();
                      context.moveTo(0,0);
                      context.lineTo(300,300);
                      
                      context.stroke();

          Correct?

          Thanks,
          Jerason Banes
        • Vladimir Vukicevic
          ... Each point/coordinate added to the path is affected by the current transformation matrix at the time that it s added; the matrix is never applied to the
          Message 4 of 9 , Sep 24, 2008

            On Sep 24, 2008, at 11:29 AM, Jerason Banes wrote:
            So what you're saying is that the path is not affected by the transform matrix until it is restarted? So simply transforming it will not have the desired effect without calling beginPath()? That appears to be how it tests. Your code above + a lineTo() does indeed draw between (0,0)-(100,100).

            Each point/coordinate added to the path is affected by the current transformation matrix at the time that it's added; the matrix is never applied to the entire path itself.  For example:

            moveTo(0,0);
            translate(100,0);
            lineTo(0,0);
            translate(0, 100);
            lineTo(0,0);
            translate(-100, 0);
            lineTo(0,0);
            translate(0, -100);
            lineTo(0,0);
            fill();

            Would draw a rectangle.  beginPath() by itself just clears the path and the current point; it doesn't interact with the CTM.


            In which case, the proper code that Mr. Hayes is looking for would be:

                        canvas = document.getElementById("myc");
                        context = canvas.getContext('2d');
                        
                        // Save the current state
                        context.save();
                        
                        // Now go draw something
                        context.translate(100,100);
                        context.beginPath();
                        context.moveTo(0,0);
                        context.arc(0,0,10,0,6.28, false);
                        context.fill();
                        
                        // End up back at 0,0 ? Apparently not...
                        context.restore();

                        context.beginPath();
                        context.moveTo(0,0);
                        context.lineTo(300,300);
                        
                        context.stroke();

            Correct?

            Yep, that looks right -- the first beginPath() there isn't strictly necessary in that snippet, but it's a good idea anyway since then you don't depend on there being no current point going into that block.

            (No idea why Mail.app generates strikethroughts in quoted code samples -- sorry if that's showing up for anyone else!)

               - Vlad


          • Richard Heyes
            ... Well, thank you. Still don t get it. Fortunately I haven t needed save() or restore() yet. Mind you, I wouldn t would I... :-) -- Richard Heyes HTML5
            Message 5 of 9 , Sep 25, 2008
              > ...

              Well, thank you. Still don't "get" it. Fortunately I haven't needed
              save() or restore() yet. Mind you, I wouldn't would I... :-)

              --
              Richard Heyes

              HTML5 Graphing for FF, Chrome, Opera and Safari:
              http://www.phpguru.org/RGraph
            • Matt Westcott
              ... I think the key to understanding it is realising that scale(), rotate(), translate() and transform() change the coordinate system (so that, for example,
              Message 6 of 9 , Sep 25, 2008
                --- In canvas-developers@yahoogroups.com, "Richard Heyes"
                <richard.heyes@...> wrote:
                >
                > > ...
                >
                > Well, thank you. Still don't "get" it. Fortunately I haven't needed
                > save() or restore() yet. Mind you, I wouldn't would I... :-)

                I think the key to understanding it is realising that scale(),
                rotate(), translate() and transform() change the coordinate system (so
                that, for example, the point (0,0) becomes somewhere else besides the
                top-left corner) - NOT the current pen position or the appearance of
                anything that's been drawn already. It's the coordinate system that is
                being remembered by save() and restore() (along with some other bits
                and pieces like fill style).
              • Richard Heyes
                Hi, ... Now that helps. I think based on that, I don t imagine I d need too much, if at all. -- Richard Heyes HTML5 Graphing for FF, Chrome, Opera and Safari:
                Message 7 of 9 , Sep 25, 2008
                  Hi,

                  > I think the key to understanding it is realising that scale(),
                  > rotate(), translate() and transform() change the coordinate system (so
                  > that, for example, the point (0,0) becomes somewhere else besides the
                  > top-left corner) - NOT the current pen position or the appearance of
                  > anything that's been drawn already. It's the coordinate system that is
                  > being remembered by save() and restore() (along with some other bits
                  > and pieces like fill style).

                  Now that helps. I think based on that, I don't imagine I'd need too
                  much, if at all.

                  --
                  Richard Heyes

                  HTML5 Graphing for FF, Chrome, Opera and Safari:
                  http://www.phpguru.org/RGraph
                Your message has been successfully submitted and would be delivered to recipients shortly.