Issue with start_operation, commit_operation

The following code allows you to create an ‘animal’, a line for a ‘simple animal’ and a line with some lines angled off it (legs) for an ‘animal’.

Run Animal.make_animal in the Ruby console and it makes an animal with legs. The user can select the thorax length and number of legs.
The user can select the resultant animal in the model (it goes blue as selected) and run Animal.edit_animal and this lets him/her edit the thorax length and number of legs.

So far so OK.

The issue is, if I do an undo it undoes everything all the way back to before I made the animal. The animal disappears, not just the edit.

Is there something wrong with the way I implemented start_operation, commit_operation in the Animal.edit_animal method code?

Thank you for any help you can give me.

class Simple_animal 
 def initialize(animal = "worm", thorax_length = 1, *arg) 
  puts "In initialize in Simple_animal class and @animal_group is #{@animal_group}"
  model = Sketchup.active_model
  entities = model.entities
  if arg[0].is_a? Sketchup::Group
  then
    @animal = arg[0].definition.get_attribute("Animal", "animal")
    @thorax_length = arg[0].definition.get_attribute("Animal", "thorax_length")
    @animal_group = arg[0]
  else
    @animal = animal
    @thorax_length = thorax_length.to_f
    @animal_group = entities.add_group
  end
 end  
 def show_animal
  @animal_group.entities.clear!
  puts "In show_animal method in Simple_animal and @animal_group is #{@animal_group}"
  @animal_group.entities.add_line [0, 0, 0], [@thorax_length, 0, 0]
  @animal_group.definition.set_attribute("Animal", "animal", @animal)
  @animal_group.definition.set_attribute("Animal", "thorax_length", @thorax_length)
 end
 def get_thorax
  old_tl = @thorax_length
  data=inputbox(["Enter new thorax length"],[old_tl],"Edit length of thorax")
  if data
  then
    @thorax_length = data.first
  else
    get_thorax
  end
 end
 def Simple_animal.make_animal
  model = Sketchup.active_model
  model.start_operation("Make simple animal", true, false, false)
  data=inputbox(["Animal name", "Body length"],["Worm", "4"],["", ""], "Simple Animal Data")
  name = data[0]
  thorax = data[1]
  simple_animal = Simple_animal.new name, thorax#String, Numeric
  simple_animal.show_animal
  model.commit_operation
 end
 def Simple_animal.edit_animal
  model = Sketchup.active_model
  selection = model.selection
  animal_as_SU_Group = selection.first
  puts"animal_as_SU_Group is #{animal_as_SU_Group}"
  animal = Simple_animal.new(nil, nil, animal_as_SU_Group)
  animal.get_thorax
  model.start_operation("Edit simple animal", true, false, false)
  animal.show_animal
  model.commit_operation
 end
end  
  
# class Animal sub-class of Simple_animal  
class Animal < Simple_animal
 def initialize(animal = "Insect", thorax_length = 1.0, legs = 0, *arg)
  puts "In initialize in Animal class and @animal_group is #{@animal_group}"
  if arg[0].is_a? Sketchup::Group
  then
    @animal = arg[0].definition.get_attribute("Animal", "animal")
    @thorax_length = arg[0].definition.get_attribute("Animal", "thorax_length")
    @legs = arg[0].definition.get_attribute("Animal", "legs")
    @animal_group = arg[0]
  else
    super(animal, thorax_length, *arg)
    @animal = animal
    @thorax_length = thorax_length.to_f
    @animal_group = Sketchup.active_model.entities.add_group
    @legs = legs
  end
 end 
 def show_animal
  @animal_group.entities.clear!
  puts "In show_animal method in Animal and animal is a #{@animal} and @animal_group is #{@animal_group}"
  @animal_group.entities.add_line [0, 0, 0], [@thorax_length, 0, 0]
  posneg=1
  @legs.times do |i|
    posneg=-1*posneg
    x = @thorax_length / @legs * i
    y = posneg * @thorax_length/2.0
    z = -@thorax_length/2.0
    @animal_group.entities.add_line [x, 0, 0], [x, y, z]
  end
  @animal_group.definition.set_attribute("Animal", "animal", @animal)
  @animal_group.definition.set_attribute("Animal", "thorax_length", @thorax_length)
  @animal_group.definition.set_attribute("Animal", "legs", @legs)
 end 
 def get_legs
  old_legs = @legs
  data=inputbox(["Enter number of legs"],[old_legs],[""],"Edit number of legs")
  if data
  then
    @legs = data[0].to_i
  else
    get_legs
  end
 end 
 def Animal.make_animal
  model = Sketchup.active_model
  model.start_operation("make animal", true, false, false)
  data=inputbox(["Animal name", "Body length", "Number of legs"],["Insect", 4, 6],["", "", ""], "Animal Data")
  name = data[0]
  thorax = data[1].to_f
  legs = data[2].to_i
  animal = Animal.new(name, thorax, legs)
  animal.show_animal
  model.commit_operation
 end
 def Animal.edit_animal
  model = Sketchup.active_model
  selection = model.selection
  animal_as_SU_Group = selection.first
  puts"animal_as_SU_Group is #{animal_as_SU_Group}"
  animal = Animal.new(nil, nil, nil, animal_as_SU_Group)
  animal.get_thorax
  animal.get_legs
  model.start_operation("animal_edit", true, false, true)
  animal.show_animal
  model.commit_operation
 end
end
1 Like

The actual question is about using transparent operations.

The third parameter “next_transparent” means this operation will be merged with the next one, whatever it is (you cannot know, it might be a totally different tool, this is in control of the user).
The fourth parameter “transparent” means this operation will be merged with the previous one, whatever it is (there may be ways to check that).

You assume that when the method edit_animal is called, the animal has been created (but the user could select any entity, I would add a check that the selection is valid). And you also assume that make_animal always was the previous operation but the user could have switched to the move tool for example.

When the user makes the animal, then immediately edits it, the operation from edit_animal is merged with the operation make_animal and it becomes a single (big) operation on the undo stack. You can then only undo it completely. If you want to undo only the edit, you need to set the fourth parameter to false.

2 Likes

You are of course correct. I should have spotted that!
Thank You!