Undo/ ctrl + z

What can we do better.

Moderators: yajra1219, Jayem, kitty_webb, Ywsp, joanCruz03, shela

User avatar
joyanecito
Posts: 41
Joined: Tue Oct 16, 2018 4:38 am

Undo/ ctrl + z

Post by joyanecito » Wed Nov 14, 2018 6:11 am

While Im doing my FBCadCam task given by Maam Arjay I accidentally deleted what I did so I tried to press ctrl + z key but nothing happens . I hope that there's also a ctrl + z key FBCadCam.

annsherly
Posts: 31
Joined: Mon Oct 29, 2018 2:54 am

Fill option

Post by annsherly » Mon Nov 19, 2018 3:19 am

While waiting for other trainees, i tried to draw in Fbcadcam i used different shapes to complete my art then i noticed that there's no "fill" option to fill the shapes.I wish there is fill option to make my drawing colorful.

rexxitall
Posts: 8
Joined: Mon Mar 18, 2019 1:01 pm

Re: Undo/ ctrl + z

Post by rexxitall » Mon Mar 25, 2019 9:07 pm

Hi,
after looking in the code. It is not possible at this stage,
Best fast soloution would be a autosave after whatever time.
Like in the "good old days" :)

From the teaching aspect i tell you as a very old bag, it was 1980 like this, you got a pencil and china imk. If you need to change a china ink line you use a razor blade. That isnt a Joke :)
So you tink 5 times before u use china ink.

So first computers which simply could delete a line helps a lot.

Basicly the most easy soloution to the current Code would be make a Copy whenever a user Action is taken. Just in Memory
Copy the whole thing to a new list.

Not clever, waste of memory but workin.

Owen i would be as aconstruction guy totally pissed if m work would be lost.

I think the so called marked leader acting like that
he do not reroll a actoin , he just do a copy.
You can tell how much copys you wanna have in mem. He do not tell you that like that he just ask how many ;)


best regards
Thomas

User avatar
paul doe
Posts: 88
Joined: Thu Oct 18, 2018 10:07 pm
Location: Argentina

Re: Undo/ ctrl + z

Post by paul doe » Mon Mar 25, 2019 11:36 pm

rexxitall wrote:
Mon Mar 25, 2019 9:07 pm
...
Basicly the most easy soloution to the current Code would be make a Copy whenever a user Action is taken. Just in Memory
Copy the whole thing to a new list.
...
Easier said than done (seeing how every action is handled in code in an ad-hoc fashion).

One solution implemented in old programs (3D Studio up to its release 4 for DOS) was to include two commands (directly accessable from the UI): 'hold' and 'fetch'. 'Hold' saved the current state of the program, and 'Fetch' restored the last saved state. It was very useful when you did eg. boolean operations on solids (since they are very error-prone, depending on the operand's representation):

Image

The process is simple: whenever you're going to do something that you may need to undo, you 'hold' the state. If you make a mistake, 'fetch' and repeat. This is a quick and dirty solution, but it may work really well given the premises.
"Immobility,
experienced far too long,
sets me in motion"

owen
Site Admin
Posts: 656
Joined: Thu Apr 13, 2017 12:14 pm

Re: Undo/ ctrl + z

Post by owen » Mon Apr 01, 2019 12:47 pm

The undo feature is complex that's for sure.

One thing I do is I do not delete entity information from the two arrays:
Lines()
Circles()
Which contain all entity information as it is currently in this program.

When lines or circles are deleted I just change one of its elements to inactive.

In the process of doing an undue is just a matter of changing that element back to active.

User avatar
paul doe
Posts: 88
Joined: Thu Oct 18, 2018 10:07 pm
Location: Argentina

Re: Undo/ ctrl + z

Post by paul doe » Mon Apr 01, 2019 3:29 pm

owen wrote:
Mon Apr 01, 2019 12:47 pm
The undo feature is complex that's for sure.

One thing I do is I do not delete entity information from the two arrays:
Lines()
Circles()
Which contain all entity information as it is currently in this program.

When lines or circles are deleted I just change one of its elements to inactive.

In the process of doing an undue is just a matter of changing that element back to active.
That's a reasonable workaround (considering the premises), but it's inefficient and only allows you to 'undo' a delete command. What about other commands that change app/entity state, such as transformations, fill/stroke patterns, and others that may need to be undone?

Implementing undo/redo functionality is not hard, but you need to contemplate it right from the onset. Otherwise, it can be nigh impossible to refactor the code to accomodate it. Hence, a good abstraction like the Command pattern can prove invaluable (not only to implement undo/redo, but also macros/scripts/automation).
"Immobility,
experienced far too long,
sets me in motion"

User avatar
paul doe
Posts: 88
Joined: Thu Oct 18, 2018 10:07 pm
Location: Argentina

Re: Undo/ ctrl + z

Post by paul doe » Mon Apr 01, 2019 10:25 pm

This is a trivial implementation of the Command pattern for drawing commands. It only shows how the abstraction works, it doesn't do anything meaningful, and in fact, it may seem that's just a very convoluted way of drawing to the screen:

Code: Select all

/'
  Command design pattern.
  
  This little example shows how you can implement the Command pattern in
  FreeBasic.
'/
type ICommand extends Object
  /'
    If you're going to derive other classes from this one, the destructor
    is to be called 'virtual', for other classes to override them. Note that
    the semantics for 'override', when applied to destructors, actually
    mean that the destructors are chained in inheritance order, not that
    they're actually overriden as in abstract/virtual methods.
  '/
  declare virtual destructor()
  
  /'
    Methods declared 'abstract' MUST NOT HAVE an implementation. Together
    they define the common interface for all classes that derive from them.
    Virtual methods, on the other hand, NEED TO HAVE a default implementation,
    which can be overriden by derived classes (either to implement their
    own method or to augment the one already provided).
  '/
  declare abstract sub _
    execute()
end type

destructor _
  ICommand()
end destructor

/'
  All other commands are derived from the ICommand class. Note that, even though
  all classes implement the 'execute()' method, they can have widely different
  purposes and data, which is handled to them through their constructors.
  
  Most people that begin to study OOP techniques are oblivious to the fact that
  CONSTRUCTORS ARE THE MOST IMPORTANT ASPECT OF CLASSES, since they give classes
  __their identities and the data they'll need to operate__.
'/
type LineCommand extends ICommand
  public:
    declare constructor( _
      byval as single, _
      byval as single, _
      byval as single, _
      byval as single, _
      byval as ulong )
    
    /'
      The destructor for derived classes need to be overriden, so they're
      correctly chained up the inheritance tree.
    '/
    declare destructor() override
    
    /'
      In derived classes, we implement the semantics for abstract methods. In
      other words, we'll tell FreeBasic what it means to 'execute()' each
      command derived from the ICommand interface. The 'override' clause is
      not really needed, but it's good practice to use it (both to convey
      intent and to prevent defining a method that doesn't override anything).
    '/
    declare sub _
      execute() override
    
  private:
    /'
      A constructor without parameters is called the 'default constructor'.
      Since they don't take any parameters, they can be declared 'private:' to
      prevent instantiation of objects with invalid parameters. If this class
      is meant to be derived from, the default constructor needs to be
      declared 'protected:' instead.
    '/
    declare constructor()
    
    /'
      Data members declared 'private:' can't be accessed from outside the
      class. To modify them, you either declare them 'public:', or expose them
      by using properties.
    '/
    as single _
      m_X1, m_Y1, _
      m_X2, m_Y2
    as ulong _
      m_color
end type

constructor _
  LineCommand()
end constructor

constructor _
  LineCommand( _
    byval X1 as single, _
    byval Y1 as single, _
    byval X2 as single, _
    byval Y2 as single, _
    byval aColor as ulong )
  
  m_X1 => X1
  m_Y1 => Y1
  m_X2 => X2
  m_Y2 => Y2
  m_color => aColor
end constructor

destructor _
  LineCommand()
end destructor

sub _
  LineCommand.execute()
  
  line _
    ( m_X1, m_Y1 ) - _
    ( m_X2, m_Y2 ), _
    m_color
end sub

type CircleCommand extends ICommand
  public:
    declare constructor( _
      byval as single, _
      byval as single, _
      byval as single, _
      byval as ulong )
    declare destructor() override
    
    declare sub _
      execute() override
  
  private:
    declare constructor()
    
    as single _
      m_X, m_Y, _
      m_radius
    as ulong _
      m_color
end type

constructor _
  CircleCommand()
end constructor

constructor _
  CircleCommand( _
    byval X as single, _
    byval Y as single, _
    byval radius as single, _
    byval aColor as ulong )
  
  m_X => X
  m_Y => Y
  m_radius => radius
  m_color => aColor
end constructor

destructor _
  CircleCommand()
end destructor

sub _
  CircleCommand.execute()
  
  circle _
    ( m_X, m_y ), _
    m_radius, m_color
end sub

/'
  Test code
'/
dim as integer _
  screenWidth => 800, _
  screenHeight => 600

screenRes( screenWidth, screenHeight, 32 )
color( rgb( 0, 0, 0 ), rgb( 255, 255, 255 ) )
cls()

dim as integer _
  numCommands => 100

dim as ICommand ptr _
  commands( 0 to numCommands - 1 )

randomize()

/'
  Randomly select between circle and line commands to add to the
  commands list.
'/
for _
  i as integer => 0 to numCommands - 1
  
  dim as integer _
    selection => cint( rnd() )
  
  select case as const( selection )
    case 0
      commands( i ) => new LineCommand( _
        rnd() * screenWidth, rnd() * screenHeight, _
        rnd() * screenWidth, rnd() * screenHeight, _
        rnd() * &hffffff )
      
    case 1
      commands( i ) => new CircleCommand( _
        rnd() * screenWidth, rnd() * screenHeight, _
        rnd() * 100.0, _
        rnd() * &hffffff )
  end select
next


/'
  Now, we iterate through the array of commands and execute() them. This
  will effectively draw them on screen (since that was the semantic we
  defined when we derived the classes).
'/
for _
  i as integer => 0 to numCommands - 1
  
  commands( i )->execute()
next

sleep()

/'
  Since we created them with 'new()', we need to destroy them using
  'delete()'
'/
for _
  i as integer => 0 to numCommands - 1
  
  delete( commands( i ) )
next
Of course, registering commands in a data structure (stacks are the sensible choice here) is the basis for implementing undo/redo functionality, since you can define Commands to be any operation that can be done with the app. Then, implementing undo/redo is just a matter of pushing commands to be undone to the 'undo stack', and popping them when the undo functionality requires. Redo is implemented in a similar way: the popped command is pushed to a 'redo stack', and popped and executed again when the user redoes the action.

It isn't really difficult, but does require some forward thinking from an implementation perspective (especially when dealing with complex state changes).
"Immobility,
experienced far too long,
sets me in motion"

User avatar
paul doe
Posts: 88
Joined: Thu Oct 18, 2018 10:07 pm
Location: Argentina

Re: Undo/ ctrl + z

Post by paul doe » Tue Apr 02, 2019 4:10 am

A simple implementation of what I talked about in the previous post. Use the mouse to click on a rectangle to randomly change its color, and 'z' and 'r' keys to undo and redo changes made to them, respectively.

It uses the Command pattern and two stacks to implement the undo/redo functionality. Written to show the basic technique, a real implementation is far more complex than this:

Code: Select all

#include once "fbgfx.bi"

/'
  Command design pattern.
  
  This little example shows how you can implement the Command pattern in
  FreeBasic. This snippet shows how you to implement a simple undo/redo
  mechanism using the Command pattern and some simple data structures
  (stacks).
'/
type ICommand extends Object
  declare virtual destructor()
  
  declare abstract sub _
    execute()
  declare abstract sub _
    undo()
end type

destructor _
  ICommand()
end destructor

/'
  Just a simple type that represent state (in this case, it's just a
  rectangle that will change color upon clicking it).
'/
type Rectangle
  public:
    declare constructor( _
      byval as single, _
      byval as single, _
      byval as single, _
      byval as single, _
      byval as ulong )
    declare destructor()
    
    declare function _
      inside( _
        byval as single, _
        byval as single ) _
      as boolean
    declare sub _
      render()
    
    as single _
      X, Y, _
      width, height
    as ulong _
      color
    
  private:
    declare constructor()
end type

constructor _
  Rectangle()
end constructor

constructor _
  Rectangle( _
    byval nX as single, _
    byval nY as single, _
    byval aWidth as single, _
    byval aHeight as single, _
    byval aColor as ulong )
  
  X => nX
  Y => nY
  width => aWidth
  height => aHeight
  color => aColor
end constructor

destructor _
  Rectangle()
end destructor

/'
  Returns whether or not the specified coordinate is inside a
  Rectangle.
'/
function _
  Rectangle.inside( _
    byval px as single, _
    byval py as single ) _
  as boolean
  
  return( cbool( _
    px >= X andAlso _
    py >= Y andAlso _
    px <= X + width - 1 andAlso _
    py <= Y + height - 1 ) )
end function

sub _
  Rectangle.render()
  
  line _
    ( X, Y ) - _
    ( X + width - 1, Y + height - 1 ), _
    color, bf
end sub

/'
  A very simple stack of ICommands.
  
  Used to implement the undo/redo functionality.
'/
type Stack
  public:
    declare constructor()
    declare destructor()
    
    declare property _
      count() as integer
    
    declare sub _
      clear()
    declare sub _
      push( _
        byval as ICommand ptr )
    declare function _
      pop() as ICommand ptr
    
  private:
    as integer _
      m_count
    as ICommand ptr _
      m_commands( any )
end type

constructor _
  Stack()
end constructor

destructor _
  Stack()
  
  clear()
end destructor

property _
  Stack.count() _
  as integer
  
  return( m_count )
end property

sub _
  Stack.clear()
  
  for _
    i as integer => 0 to m_count - 1
    
    delete( m_commands( i ) )
  next
  
  m_count => 0
end sub

sub _
  Stack.push( _
    byval aCommand as ICommand ptr )
    
  m_count +=> 1
  
  redim preserve _
    m_commands( 0 to count - 1 )    
  
  m_commands( count - 1 ) => aCommand
end sub

function _
  Stack.pop() as ICommand ptr
  
  var popped => m_commands( m_count - 1 )
  
  m_count -=> 1
  
  redim preserve _
    m_commands( 0 to count - 1 )
  
  return( popped )
end function

/'
  And this class represents a command for the app. In this case,
  the command simply records the state needed to undo/redo changing
  the color of a rectangle (which is performed in the main loop by
  clicking on it).
'/
type ChangeColorCommand extends ICommand
  public:
    declare constructor( _
      byval as Rectangle ptr, _
      byval as ulong )
    declare destructor() override
    
    declare sub _
      execute() override
    declare sub _
      undo() override
    
  private:
    declare constructor()
    
    as Rectangle ptr _
      m_rectangle
    as ulong _
      m_oldColor, _
      m_newColor
end type

constructor _
  ChangeColorCommand()
end constructor

constructor _
  ChangeColorCommand( _
    byval aRectangle as Rectangle ptr, _
    byval aNewColor as ulong )
  
  m_rectangle => aRectangle
  m_newColor => aNewColor
end constructor

destructor _
  ChangeColorCommand()
end destructor

sub _
  ChangeColorCommand.execute()
  
  m_oldColor => m_rectangle->color
  m_rectangle->color => m_newColor
end sub

sub _
  ChangeColorCommand.undo()
  
  m_rectangle->color => m_oldColor
end sub

/'
  This function is called whenever the user clicks on a rectangle.
  It retrieves the index into an array of Rectangles, if the queried
  point is inside one of them. Nothing particularly fancy, it just
  iterates through the array and checks every rectangle within it.
'/
function _
  onClick( _
    byval x as single, _
    byval y as single, _
    rectangles() as Rectangle ptr ) _
  as integer
  
  for _
    i as integer => 0 to ubound( rectangles )
    
    if( rectangles( i )->inside( x, y ) ) then
      return( i )
    end if
  next
  
  return( -1 )
end function

/'
  Called whenever a command is to be executed. It executes the
  Command passed as argument and stores it in the specified
  undo stack.
'/
sub _
  performCommand( _
    byval aCommand as ICommand ptr, _
    byval anUndoStack as Stack ptr )
  
  aCommand->execute()
  anUndoStack->push( aCommand )
end sub

/'
  Undoes the last command performed. It pops the last command
  from the specified undo stack, undo()es it and pushes it into
  the specified redo stack.
'/
sub _
  undoCommand( _
    anUndoStack as Stack ptr, _
    aRedoStack as Stack ptr )
  
  var aCommand => _
    anUndoStack->pop()
  
  aCommand->undo()
  aRedoStack->push( aCommand )
end sub

/'
  Test code
'/
dim as integer _
  screenWidth => 800, _
  screenHeight => 600

screenRes( screenWidth, screenHeight, 32 )
color( rgb( 0, 0, 0 ), rgb( 255, 255, 255 ) )
cls()

randomize()

dim as integer _
  rows => 5, _
  cols => 5

dim as single _
  rectangleWidth => screenWidth / cols, _
  rectangleHeight => screenHeight / rows

dim as Rectangle ptr _
  rectangles( 0 to rows * cols - 1 )

/'
  Just creates some rectangles and colors them with an easily
  recognizable pattern.
'/
for _
  y as integer => 0 to rows - 1
  
  for _
    x as integer => 0 to cols - 1
    
    rectangles( y * rows + x ) => new Rectangle( _
      x * rectangleWidth, y * rectangleHeight, _
      rectangleWidth, rectangleHeight, _
      rgb( ( 255 / rows ) * y, 0, ( 255 / cols ) * ( cols - x ) ) )
  next
next

var _
  undoStack => new Stack(), _
  redoStack => new Stack()

dim as Fb.Event _
  e
dim as string _
  keyPress
dim as single _
  mouseX, mouseY

/'
  And the main loop simply polls the event queue and randomly changes
  the color of the clicked rectangle.
  
  To undo a command, press 'z'
  To redo a command, press 'r'
'/
do
  if( screenEvent( @e ) ) then
    if( e.type = Fb.EVENT_MOUSE_MOVE ) then
      mouseX => e.x
      mouseY => e.y
    end if
    
    if( _
      e.type = Fb.EVENT_MOUSE_BUTTON_PRESS ) then
      
      if( e.button = Fb.BUTTON_LEFT ) then
        dim as integer _
          index => onClick( _
            mouseX, mouseY, rectangles() )
          
        if( index >= 0 ) then
          '' New command issued, clear the redo stack
          redoStack->clear()
          
          '' And perform the new command
          performCommand( _
            new ChangeColorCommand( _
              rectangles( index ), _
              rnd() * &hffffff ), _
            undoStack )
        end if
      end if
    end if
  end if
   
  keyPress => lcase( inkey() )
  
  '' Undo
  if( _
    keyPress = "z" ) then
    
    if( undoStack->count > 0 ) then
      undoCommand( _
        undoStack, _
        redoStack )
    end if
  end if
  
  '' Redo
  if( _
    keyPress = "r" ) then
    
    if( redoStack->count > 0 ) then
      /'
        Redoing a command merely pops() it from the redo stack
        and executes it again.
      '/
      performCommand( _
        redoStack->pop(), _
        undoStack )
    end if
  end if
  
  '' Render the screen
  screenLock()
    cls()
    
    for _
      i as integer => 0 to ubound( rectangles )
      
      rectangles( i )->render()
    next
  screenUnlock()
  
  sleep( 1, 1 )
loop _
  until( e.type = Fb.EVENT_WINDOW_CLOSE )

'' Clean up array of rectangles
for _
  i as integer => 0 to ubound( rectangles )
  
  delete( rectangles( i ) )
next

'' Delete stacks
delete( undoStack )
delete( redoStack )
"Immobility,
experienced far too long,
sets me in motion"

owen
Site Admin
Posts: 656
Joined: Thu Apr 13, 2017 12:14 pm

Re: Undo/ ctrl + z

Post by owen » Wed Apr 03, 2019 3:51 am

Most excellent Paul Doe. I will review your code and learn from it. Thanks for putting all those comments in the code.

So when I mentioned the undo functionality is complex was because there are many things to undo besides delete to include things like repositioning move copy rotate would be unmove uncopy unrotate. Any action that you do in the CAD program would need to be kept track of and to which entities those actions affected... that's the gist of it and when I check out your code no doubt it will help me understand how to implement such a idea thank you very much for those last two posts.

User avatar
paul doe
Posts: 88
Joined: Thu Oct 18, 2018 10:07 pm
Location: Argentina

Re: Undo/ ctrl + z

Post by paul doe » Wed Apr 03, 2019 9:45 am

owen wrote:
Wed Apr 03, 2019 3:51 am
...
So when I mentioned the undo functionality is complex was because there are many things to undo besides delete to include things like repositioning move copy rotate would be unmove uncopy unrotate...
Indeed. You could add that to the current version of FbCADCAM, but as I said before, the difficulty lies in that every action is spreaded over the whole codebase in an ad-hoc fashion. Not to mention that, since you tied almost all state to globals, keeping track of state changes is just impossible. Also note that the above sequence is recorded move/copy/rotate and is unrolled in reverse: unrotate/uncopy/unmove, by using stacks. That way, the commands are unwinded in the correct order. Look here for more info about them, because they're foundational data structures, useful for a lot of other tasks.

See the performCommand() function there? In real code, that is bound to be a much more complex abstraction than here. It's sole purpose is precisely to handle the execution of commands in the app transparently, so it doesn't matter whether you execute them by using the UI, writing commands directly to the command window, or using a custom script/macro: the result is guaranteed to be the same (and all the book keeping is done in one place).

Same thing can be said from the onClick() function (which is actually an event handler, and is part of the GUI layer of the app).
owen wrote:
Wed Apr 03, 2019 3:51 am
...
Any action that you do in the CAD program would need to be kept track of and to which entities those actions affected... that's the gist of it and when I check out your code no doubt it will help me understand how to implement such a idea thank you very much for those last two posts.
Here's an invaluable resource that has stood the test of time, that'll help you understand what a 'design pattern' is, in Object-Oriented programming:

Design Patterns: Elements of reusable Object-Oriented software

The code in the book is in C++ and SmallTalk, but simply ask if there's some code you'll like to see in FreeBasic and I'll provide an implementation. The most important things are the concepts, since concepts are universal. Glad you found it useful.
"Immobility,
experienced far too long,
sets me in motion"

owen
Site Admin
Posts: 656
Joined: Thu Apr 13, 2017 12:14 pm

Re: Undo/ ctrl + z

Post by owen » Sat Apr 06, 2019 12:30 am

I haven't had the time to review any of your code yet. I will be home for 34 hours, grab a six pack, unwind and then try to wrap my head around all of your examples. Might even have time to look at it right now but I'm pretty tired and that's my problem is I got to work you know how goes.

User avatar
paul doe
Posts: 88
Joined: Thu Oct 18, 2018 10:07 pm
Location: Argentina

Re: Undo/ ctrl + z

Post by paul doe » Sat Apr 06, 2019 10:28 am

owen wrote:
Sat Apr 06, 2019 12:30 am
I haven't had the time to review any of your code yet. I will be home for 34 hours, grab a six pack, unwind and then try to wrap my head around all of your examples. Might even have time to look at it right now but I'm pretty tired and that's my problem is I got to work you know how goes.
Take your time. The learning curve for OOP concepts is not steep because they're particularly complicated to grasp, but because you need to know everything, or you don't know nothing ;)
"Immobility,
experienced far too long,
sets me in motion"

owen
Site Admin
Posts: 656
Joined: Thu Apr 13, 2017 12:14 pm

Re: Undo/ ctrl + z

Post by owen » Sun Apr 07, 2019 1:36 am

how does the assignment operator => ie ( X => nX ) work in this snippet or your code?

Code: Select all

constructor _
  Rectangle( _
    byval nX as single, _
    byval nY as single, _
    byval aWidth as single, _
    byval aHeight as single, _
    byval aColor as ulong )
  
  X => nX
  Y => nY
  width => aWidth
  height => aHeight
  color => aColor
end constructor

owen
Site Admin
Posts: 656
Joined: Thu Apr 13, 2017 12:14 pm

Re: Undo/ ctrl + z

Post by owen » Sun Apr 07, 2019 1:39 am

i changed
X => nX
Y => nY
to
X = nX
Y = nY

and it works fine (i think)

owen
Site Admin
Posts: 656
Joined: Thu Apr 13, 2017 12:14 pm

Re: Undo/ ctrl + z

Post by owen » Sun Apr 07, 2019 1:49 am

the alternative symbol '=>' can be used for assignments (in place of '=') from fbc version 0.90

ok the fb documentation explains its simply an alternative to =

i thought it had something to do with pointer stuff (which u know i'm just a newbie when it comes to pointers)

owen
Site Admin
Posts: 656
Joined: Thu Apr 13, 2017 12:14 pm

Re: Undo/ ctrl + z

Post by owen » Sun Apr 07, 2019 2:02 am

on line 334
rectangles( y * rows + x ) => new Rectangle(

means to me in my own verbiage... just thinking out loud...
array of pointers called rectangles equals new rectangle object

owen
Site Admin
Posts: 656
Joined: Thu Apr 13, 2017 12:14 pm

Re: Undo/ ctrl + z

Post by owen » Sun Apr 07, 2019 2:56 am

i got to tell ya, this is slick
return( cbool( _
px >= X andAlso _
py >= Y andAlso _
px <= X + width - 1 andAlso _
py <= Y + height - 1 ) )

sure saves on the if thens...

owen
Site Admin
Posts: 656
Joined: Thu Apr 13, 2017 12:14 pm

Re: Undo/ ctrl + z

Post by owen » Sun Apr 07, 2019 4:04 am

the mcommand is an array of icommand pointers which are a udt
type ICommand extends Object

Which object? (Thinking out loud)
...

with member subroutines called execute and undo

so you call the obects execute or undo ... that is the rectangles execute or undo dot changecolor...



as define in the udt
type ChangeColorCommand extends ICommand
...
And somewhere in there is something to do with a stack..

or something like that... probably not even close to correct...

ok maybe if you could add a second operation like:
type ChangeSizeCommand extends ICommand
maybe scrolling the mouse wheel can make the rectange shrink and expand (not shrinking so small you cant see it and not expanding larger then is original size) or whatever you want to do so that i can see how this might work with two commands

User avatar
paul doe
Posts: 88
Joined: Thu Oct 18, 2018 10:07 pm
Location: Argentina

Re: Undo/ ctrl + z

Post by paul doe » Sun Apr 07, 2019 5:55 am

owen wrote:
Sun Apr 07, 2019 4:04 am
the mcommand is an array of icommand pointers which are a udt
type ICommand extends Object

Which object? (Thinking out loud)
...
https://www.freebasic.net/wiki/wikka.ph ... eyPgObject
owen wrote:
Sun Apr 07, 2019 4:04 am
...
with member subroutines called execute and undo

so you call the obects execute or undo ... that is the rectangles execute or undo dot changecolor...
undo() and execute() are members of the abstract class ICommand, not the Rectangle class. They're two different, unrelated classes.
owen wrote:
Sun Apr 07, 2019 4:04 am
as define in the udt
type ChangeColorCommand extends ICommand
...
And somewhere in there is something to do with a stack..
Somewhere? :roll-eyes:
owen wrote:
Sun Apr 07, 2019 4:04 am
...
or something like that... probably not even close to correct...
...
Coming along fine. Remember that each abstract method has to be implemented on a derived class. In the case of undo() and redo(), it is the ChangeColorCommand class that does it, not the Rectangle one.
owen wrote:
Sun Apr 07, 2019 4:04 am
...
ok maybe if you could add a second operation like:
type ChangeSizeCommand extends ICommand
maybe scrolling the mouse wheel can make the rectange shrink and expand (not shrinking so small you cant see it and not expanding larger then is original size) or whatever you want to do so that i can see how this might work with two commands
Very well:

Code: Select all

#include once "fbgfx.bi"

/'
  Command design pattern.
  
  This little example shows how you can implement the Command pattern in
  FreeBasic. This snippet shows how you to implement a simple undo/redo
  mechanism using the Command pattern and some simple data structures
  (stacks).
'/
type ICommand extends Object
  declare virtual destructor()
  
  declare abstract sub _
    execute()
  declare abstract sub _
    undo()
end type

destructor _
  ICommand()
end destructor

/'
  Just a simple type that represent state (in this case, it's just a
  rectangle that will change color upon clicking it).
'/
type Rectangle
  public:
    declare constructor( _
      byval as single, _
      byval as single, _
      byval as single, _
      byval as single, _
      byval as ulong )
    declare destructor()
    
    declare property _
      width() as single
    declare property _
      width( _
        byval as single )
    declare property _
      height() as single
    declare property _
      height( _
        byval as single )
      
    declare function _
      canResizeUp() as boolean
    declare function _
      canResizeDown() as boolean
    declare function _
      inside( _
        byval as single, _
        byval as single ) _
      as boolean
    declare sub _
      render()
    
    as single _
      X, Y
    as ulong _
      color
    
  private:
    declare constructor()
    
    as single _
      m_width, m_height, _
      m_initialWidth, m_initialHeight
end type

constructor _
  Rectangle()
end constructor

constructor _
  Rectangle( _
    byval nX as single, _
    byval nY as single, _
    byval aWidth as single, _
    byval aHeight as single, _
    byval aColor as ulong )
  
  X => nX
  Y => nY
  m_width => aWidth
  m_height => aHeight
  color => aColor
  
  m_initialWidth => m_width
  m_initialHeight => m_height
end constructor

destructor _
  Rectangle()
end destructor

property _
  Rectangle.width() _
  as single
  
  return( m_width )
end property

property _
  Rectangle.width( _
    byval value as single )
  
  m_width => iif( value < 10.0, _
    10.0, iif( value > m_initialWidth, _
    m_initialWidth, value ) )
end property

property _
  Rectangle.height() _
  as single
  
  return( m_height )
end property

property _
  Rectangle.height( _
    byval value as single )
  
  m_height => iif( value < 10.0, _
    10.0, iif( value > m_initialHeight, _
    m_initialHeight, value ) )
end property

/'
  Returns whether or not the rectangle can be resized. Useful to
  avoid registering useless commands.
'/
function _
  Rectangle.canResizeUp() _
  as boolean
  
  return( cbool( _
    m_width > 10.0 andAlso _
    m_height > 10.0 andAlso _
    m_width < m_initialWidth andAlso _
    m_height < m_initialHeight ) )
end function

function _
  Rectangle.canResizeDown() _
  as boolean
  
  return( cbool( _
    m_width > 10.0 andAlso _
    m_height > 10.0 andAlso _
    m_width <= m_initialWidth andAlso _
    m_height <= m_initialHeight ) )
end function

/'
  Returns whether or not the specified coordinate is inside a
  Rectangle.
'/
function _
  Rectangle.inside( _
    byval px as single, _
    byval py as single ) _
  as boolean
  
  return( cbool( _
    px >= X andAlso _
    py >= Y andAlso _
    px <= X + width - 1 andAlso _
    py <= Y + height - 1 ) )
end function

sub _
  Rectangle.render()
  
  line _
    ( X, Y ) - _
    ( X + width - 1, Y + height - 1 ), _
    color, bf
end sub

/'
  A very simple stack of ICommands.
  
  Used to implement the undo/redo functionality.
'/
type Stack
  public:
    declare constructor()
    declare destructor()
    
    declare property _
      count() as integer
    
    declare sub _
      clear()
    declare sub _
      push( _
        byval as ICommand ptr )
    declare function _
      pop() as ICommand ptr
    
  private:
    as integer _
      m_count
    as ICommand ptr _
      m_commands( any )
end type

constructor _
  Stack()
end constructor

destructor _
  Stack()
  
  clear()
end destructor

property _
  Stack.count() _
  as integer
  
  return( m_count )
end property

sub _
  Stack.clear()
  
  for _
    i as integer => 0 to m_count - 1
    
    delete( m_commands( i ) )
  next
  
  m_count => 0
end sub

sub _
  Stack.push( _
    byval aCommand as ICommand ptr )
    
  m_count +=> 1
  
  redim preserve _
    m_commands( 0 to count - 1 )    
  
  m_commands( count - 1 ) => aCommand
end sub

function _
  Stack.pop() as ICommand ptr
  
  var popped => m_commands( m_count - 1 )
  
  m_count -=> 1
  
  redim preserve _
    m_commands( 0 to count - 1 )
  
  return( popped )
end function

/'
  And this class represents a command for the app. In this case,
  the command simply records the state needed to undo/redo changing
  the color of a rectangle (which is performed in the main loop by
  clicking on it).
'/
type ChangeColorCommand extends ICommand
  public:
    declare constructor( _
      byval as Rectangle ptr, _
      byval as ulong )
    declare destructor() override
    
    declare sub _
      execute() override
    declare sub _
      undo() override
    
  private:
    declare constructor()
    
    as Rectangle ptr _
      m_rectangle
    as ulong _
      m_oldColor, _
      m_newColor
end type

constructor _
  ChangeColorCommand()
end constructor

constructor _
  ChangeColorCommand( _
    byval aRectangle as Rectangle ptr, _
    byval aNewColor as ulong )
  
  m_rectangle => aRectangle
  m_newColor => aNewColor
end constructor

destructor _
  ChangeColorCommand()
end destructor

sub _
  ChangeColorCommand.execute()
  
  m_oldColor => m_rectangle->color
  m_rectangle->color => m_newColor
end sub

sub _
  ChangeColorCommand.undo()
  
  m_rectangle->color => m_oldColor
end sub

type ChangeSizeCommand extends ICommand
  public:
    declare constructor( _
      byval as Rectangle ptr, _
      byval as single, _
      byval as single )
    declare destructor() override
    
    declare sub _
      execute() override
    declare sub _
      undo() override
    
  private:
    declare constructor()
    
    as Rectangle ptr _
      m_rectangle
    as single _
      m_widthChange, _
      m_heightChange
    as long _
      m_oldWidth, _
      m_oldHeight
end type

constructor _
  ChangeSizeCommand()
end constructor

constructor _
  ChangeSizeCommand( _
    byval aRectangle as Rectangle ptr, _
    byval aWidthChange as single, _
    byval aHeightChange as single )
  
  m_rectangle => aRectangle
  m_widthChange => aWidthChange
  m_heightChange => aHeightChange
end constructor

destructor _
  ChangeSizeCommand()
end destructor

sub _
  ChangeSizeCommand.execute()
  
  m_oldWidth => m_rectangle->width
  m_oldHeight => m_rectangle->height
  
  m_rectangle->width => m_rectangle->width * m_widthChange
  m_rectangle->height => m_rectangle->height * m_heightChange
end sub

sub _
  ChangeSizeCommand.undo()
  
  m_rectangle->width => m_oldWidth
  m_rectangle->height => m_oldHeight
end sub

/'
  I changed the name of this function to better reflect how it's used in
  code, the function is unchanged.
'/
function _
  onHover( _
    byval x as single, _
    byval y as single, _
    rectangles() as Rectangle ptr ) _
  as integer
  
  for _
    i as integer => 0 to ubound( rectangles )
    
    if( rectangles( i )->inside( x, y ) ) then
      return( i )
    end if
  next
  
  return( -1 )
end function

/'
  Called whenever a command is to be executed. It executes the
  Command passed as argument and stores it in the specified
  undo stack.
'/
sub _
  performCommand( _
    byval aCommand as ICommand ptr, _
    byval anUndoStack as Stack ptr )
  
  aCommand->execute()
  anUndoStack->push( aCommand )
end sub

/'
  Undoes the last command performed. It pops the last command
  from the specified undo stack, undo()es it and pushes it into
  the specified redo stack.
'/
sub _
  undoCommand( _
    anUndoStack as Stack ptr, _
    aRedoStack as Stack ptr )
  
  var aCommand => _
    anUndoStack->pop()
  
  aCommand->undo()
  aRedoStack->push( aCommand )
end sub

/'
  Test code
'/
dim as integer _
  screenWidth => 800, _
  screenHeight => 600

screenRes( screenWidth, screenHeight, 32 )
color( rgb( 0, 0, 0 ), rgb( 255, 255, 255 ) )
cls()

randomize()

dim as integer _
  rows => 5, _
  cols => 5

dim as single _
  rectangleWidth => screenWidth / cols, _
  rectangleHeight => screenHeight / rows

dim as Rectangle ptr _
  rectangles( 0 to rows * cols - 1 )

/'
  Just creates some rectangles and colors them with an easily
  recognizable pattern.
'/
for _
  y as integer => 0 to rows - 1
  
  for _
    x as integer => 0 to cols - 1
    
    rectangles( y * rows + x ) => new Rectangle( _
      x * rectangleWidth, y * rectangleHeight, _
      rectangleWidth, rectangleHeight, _
      rgb( ( 255 / rows ) * y, 0, ( 255 / cols ) * ( cols - x ) ) )
  next
next

var _
  undoStack => new Stack(), _
  redoStack => new Stack()

dim as Fb.Event _
  e
dim as string _
  keyPress
dim as integer _
  mouseX, mouseY

/'
  And the main loop simply polls the event queue and randomly changes
  the color of the clicked rectangle.
  
  To undo a command, press 'z'
  To redo a command, press 'r'
'/

'' Assume we aren't hovering on a rectangle at the start of the loop
dim as integer _
  index => -1

do
  getMouse( mouseX, mouseY )
  
  index => onHover( _
    mouseX, mouseY, rectangles() )
  
  if( screenEvent( @e ) ) then
    if( _
      e.type = Fb.EVENT_MOUSE_BUTTON_PRESS ) then
      
      if( e.button = Fb.BUTTON_LEFT ) then
        if( index >= 0 ) then
          '' New command issued, clear the redo stack
          redoStack->clear()
          
          '' And perform the new command
          performCommand( _
            new ChangeColorCommand( _
              rectangles( index ), _
              rnd() * &hffffff ), _
            undoStack )
        end if
      end if
    end if
  end if
   
  keyPress => lcase( inkey() )
  
  '' Increase size by 10%
  if( _
    cbool( keyPress = "+" ) andAlso _
    cbool( index >= 0 ) andAlso _
    rectangles( index )->canResizeUp() ) then
    
    '' New command issued, clear the redo stack
    redoStack->clear()
    
    performCommand( _
      new ChangeSizeCommand( _
        rectangles( index ), _
        1.10, 1.10 ), _
      undoStack )
  end if
  
  '' Decrease size by 10%
  if( _
    cbool( keyPress = "-" ) andAlso _
    cbool( index >= 0 ) andAlso _
    rectangles( index )->canResizeDown() ) then
    
    '' New command issued, clear the redo stack
    redoStack->clear()
    
    performCommand( _
      new ChangeSizeCommand( _
        rectangles( index ), _
        0.90, 0.90 ), _
      undoStack )
  end if
  
  '' Undo
  if( keyPress = "z" ) then
    if( undoStack->count > 0 ) then
      undoCommand( _
        undoStack, _
        redoStack )
    end if
  end if
  
  '' Redo
  if( keyPress = "r" ) then
    if( redoStack->count > 0 ) then
      /'
        Redoing a command merely pops() it from the redo stack
        and executes it again.
      '/
      performCommand( _
        redoStack->pop(), _
        undoStack )
    end if
  end if
  
  '' Render the screen
  screenLock()
    cls()
    
    for _
      i as integer => 0 to ubound( rectangles )
      
      rectangles( i )->render()
    next
  screenUnlock()
  
  sleep( 1, 1 )
loop _
  until( e.type = Fb.EVENT_WINDOW_CLOSE )

'' Clean up array of rectangles
for _
  i as integer => 0 to ubound( rectangles )
  
  delete( rectangles( i ) )
next

'' Delete stacks
delete( undoStack )
delete( redoStack )
Use '+' and '-' to increase and decrease the size of each rectangle. As you requested, it doesn't resize a rectangle beyond its original size, and its size cannot go below 10 pixels.
Last edited by paul doe on Sun Apr 07, 2019 2:53 pm, edited 2 times in total.
"Immobility,
experienced far too long,
sets me in motion"

User avatar
paul doe
Posts: 88
Joined: Thu Oct 18, 2018 10:07 pm
Location: Argentina

Re: Undo/ ctrl + z

Post by paul doe » Sun Apr 07, 2019 5:58 am

As per usual, you started to build a house from the roof XD

Why don't you try to dissect the first example I posted? (the one that draws circles and lines using the Command pattern)

That's why I posted it with a trivial implementation: so you can see how the pattern works, before introducing more complexity in the mix, since the Command pattern is the most basic design pattern you can study...
Last edited by paul doe on Sun Apr 07, 2019 6:26 am, edited 1 time in total.
"Immobility,
experienced far too long,
sets me in motion"

User avatar
paul doe
Posts: 88
Joined: Thu Oct 18, 2018 10:07 pm
Location: Argentina

Re: Undo/ ctrl + z

Post by paul doe » Sun Apr 07, 2019 6:22 am

owen wrote:3 hours...
maybe more...
awwwww...
who ever thought up oop should be shot.
i'm gona invent a time machine and give that guy a piece of my mind...
The primary problem here is that you're trying to grasp the Object-Oriented paradigm through code, instead of starting with the fundamentals. Once the basic concepts are understood (or at least, you have a rough idea of what they stand for), you can start examining code that implements those concepts in detail...

For example, how do you conceptualize 'objects'?
"Immobility,
experienced far too long,
sets me in motion"

owen
Site Admin
Posts: 656
Joined: Thu Apr 13, 2017 12:14 pm

Re: Undo/ ctrl + z

Post by owen » Sun Apr 07, 2019 7:06 pm

thanks im checking out your last.
i made it home with a budwiser at the ready

User avatar
paul doe
Posts: 88
Joined: Thu Oct 18, 2018 10:07 pm
Location: Argentina

Re: Undo/ ctrl + z

Post by paul doe » Sun Apr 07, 2019 9:10 pm

owen wrote:
Sun Apr 07, 2019 7:06 pm
thanks im checking out your last.
...
Check out the first!

Also, would you be so kind as to answer my question? How do you conceptualize objects? What do you think of when you think about 'Object-Oriented Programming'?
"Immobility,
experienced far too long,
sets me in motion"

owen
Site Admin
Posts: 656
Joined: Thu Apr 13, 2017 12:14 pm

Re: Undo/ ctrl + z

Post by owen » Sun Apr 07, 2019 11:18 pm

to answer your question:
1. i dont know what i think about oop cuz i have always been happy programming the way i do in fact i always unooped examples if i could in order to get what i was looking for out of the example (usually the concept of an algorithm or something)
3. i will learn oop soon enogh cuz of this wiki statement and not only that be because you recommend it:
from the wiki page you linked:
A program may create many instances of the same class as it runs, which operate independently. This is an easy way for the same procedures to be used on different sets of data.

create many instances of the same class sounds cool... i want to put tabs in fbcadcam so the user can switch between multiple drawing areas and although i have thought to do this in the past i did not envision the idea of using opp because they say it's easier, rather i would have done it my way.

they say it's an easy way but for me it 'sa hard way because i am unfamiliar with it i reckon. if i were to learn oop well enough to where its second nature then that would help me see it as easier but until then its interrupts my line of thought.

owen
Site Admin
Posts: 656
Joined: Thu Apr 13, 2017 12:14 pm

Re: Undo/ ctrl + z

Post by owen » Sun Apr 07, 2019 11:22 pm

take for example this code which does nothing an has errors

Code: Select all

Type test
	x As Integer 'i just put this here cuz types gota have something other then subs
	Declare Sub action1(ByRef b As Integer)
	Declare Sub action2()
End Type

Sub test.action1(ByRef b As Integer)
	Print "action1"
	b = 1
End Sub
Sub test.action2()
	Print "action2"
End Sub


Dim a As test
Dim As Integer b(10)
'h1->sig
a.action1(b)
Print b
'*atypesubptr.action
'
Sleep
i can see that you code uses pointers a lot so im experimenting with utds and pointers trying to figure out how to do stuff.

Post Reply