0
public function drag(e:MouseEvent)
    {
        lineDraw(mouseX, mouseY);
        e.updateAfterEvent();

    }
    public function lineDraw(X:int, Y:int):void
    {
        currentX = X;
        currentY = Y;

        graphics.lineStyle(size, color)
        graphics.moveTo(previousX, previousY)
        graphics.lineTo(currentX, currentY)

        previousX = currentX;
        previousY = currentY;
    }

Very simple code I made that allows me to draw lines with my mouse. Function drag is triggered on MOUSE_MOVE after MOUSE_DOWN.

My question is: how would I go about clearing the last line drawn? Basically, a ctrl+z/undo function that can be repeat as many times as i want.

Would I have to rewrite my code completely and push every single line drawn into an array, and then work my way backwards through the array, removing lines as I click "undo"? Or is there another better, easier solution to this?

Thanks! :)

BadFeelingAboutThis
  • 14,445
  • 2
  • 33
  • 40
Benjamin
  • 13
  • 1
  • 1
    Yes, an array of separate Sprites (or whatever DisplayObject you use) of would be the best way to go. The `graphics` property does have a `clear()` function, but this will erase the entire thing, not just the last line. Another way would be to store only the coordinates of each line in a array, and then draw over the line with a white color (or whatever you need) and the same coordinates, but that is a pretty sloppy approach. –  Oct 26 '14 at 19:21
  • the `Graphics` class's `copyFrom` method could be very handy here for "cloning" – golyo Oct 26 '14 at 20:02
  • The advice from DodgerThud is plain wrong, of course do not draw using a bunch of displayobject or you will reach CPU limits and heavy lag in no time. Do not display the object that is drawn into but draw to bitmapdata and display that (why? cos vector drawing is expensive and lag will appear) – BotMaster Oct 27 '14 at 12:53
  • BotMaster is correct. Vector drawing is very expensive in terms of CPU. People typically go wrong with vector graphics because they think they need to use them on the Web. The only reason vector graphics are good on the Web is because of initial loading. They take up a very small data footprint in terms of adding to a SWF file size. But they are mathematically recalculated every draw cycle, for each DisplayObject, which is where it gets bad. `cacheAsBitmap` is a good thing as long as the vector objects are mostly static. – C. Parcell Oct 27 '14 at 15:13
  • @DodgerThud - Using separate sprites would be unwise for performance (I wouldn't call it "plain wrong" though), but it would work ok if you only allowed a few lines on the screen at once. – BadFeelingAboutThis Oct 27 '14 at 17:02

1 Answers1

1

Here is an example: See code comments for explanations

private var undoStates:Vector.<Shape> = new Vector.<Shape>(); //holds all your undo states (HAS TO BE SHAPE SINCE AS3 DOESN"T LET YOU ISNTANTIATE A GRAPHICS OBJECT DIRECTLY)
private var redoStates:Vector.<Shape> = new Vector.<Shape>(); //holds all your redo states
private var undoLevels:int = 1; //how many undo levels you'd like to have
private var redoLevels:int = 1;

public function undo() {
    if (undoStates.length > 0) {  //make sure there is an undo state before preceeding
        //add redo state
        redoStates.push(getState());

        //if redo states are more than redoLevels, remove the oldest one
        if (redoStates.length > redoLevels) redoStates.splice(0, 1);

        //make the the last undo state the current graphics
        graphics.clear(); //not sure if this line is required, can't recall if copyFrom clears first
        graphics.copyFrom(undoStates.pop().graphics); //pop removes the last item from the array and returns it
    }
}

public function redo() {
    if (redoStates.length > 0) {
        //add undo state
        addUndo();

        //make the the last undo state the current graphics
        graphics.clear(); //not sure if copy from (the next line) clears or not
        graphics.copyFrom(redoStates.pop().graphics);
    }
}

private function getState():Shape{
    var state:Shape = new Shape();
    state.graphics.copyFrom(graphics);  //copy the current graphics into a new Graphics object
    return state;
}

private function addUndo():void {
    undoStates.push(getState()); //add the current state to the undo array

    //if more undo states than undoLevels, remove the oldest one
    if (undoStates.length > undoLevels) undoStates.splice(0, 1);
}

public function lineDraw(X:int, Y:int):void {
    currentX = X;
    currentY = Y;


    //create undo state before we draw more
    addUndo();

    graphics.lineStyle(size, color)
    graphics.moveTo(previousX, previousY)
    graphics.lineTo(currentX, currentY)


    previousX = currentX;
    previousY = currentY;
}
BadFeelingAboutThis
  • 14,445
  • 2
  • 33
  • 40
  • I'll definitely look into this. Looks very interesting indeed. Currently I solved the undo function by pushing all the Shapes into an array, and then using a for-loop I clear the undoArray[s].graphics.clear(); and then splice it. I still haven't found a way to recreate the shapes however, but I'll look into the copyFrom function you're using here, I think it might help with redo'ing :) Thanks a bunch! – Benjamin Oct 28 '14 at 12:15