If I select a sub group, they rotate. But if I leave the main group, they stop spinning. what exactly happened.
def spin(arr)
x = 0
my_timer = UI.start_timer(0.02,true){
total = 360
step = 10
interval = total/step #=36
i = x/interval
x += 1
if arr.size > 1
UI.stop_timer my_timer if i == arr.size
else
UI.stop_timer my_timer if x == interval
end
target = arr[i]
if target != nil
rot = Geom::Transformation.rotation(target.bounds.center, [0,0,1], step.degrees)
target.transform! rot
end
}
end
mod = Sketchup.active_model # Open model
ent = mod.entities # All entities in model
sel = mod.selection # Current selection
arr_target = sel
spin(arr_target)
(1) You should be using #move! for animations, not #transform! as it creates many undo steps.
(2) Probably it is better to use an abstract Animation class than a timer block.
Most likely because your arr reference in the method is pointing at the model.selection object, which is cleared (has 0 entities) when you āleave the main groupā.
thank you @DanRathbun
When I was still in the main group with different axes with the model, they rotated correctly. But when I left the main group, they rotated incorrectly.
def spin(arr)
view = Sketchup.active_model.active_view
x = 0
my_timer = UI.start_timer(0.02,true){
total = 360
step = 10
interval = total/step #=36
i = x/interval
x += 1
if arr.size > 1
UI.stop_timer my_timer if i == arr.size
else
UI.stop_timer my_timer if x == interval
end
target = arr[i]
if target != nil
tran = target.transformation
rot = Geom::Transformation.rotation(target.bounds.center, [0,0,1], (step).degrees)
#target.transform! rot #depreceat
target.move! rot*tran
view.refresh
end
}
end
mod = Sketchup.active_model # Open model
ent = mod.entities # All entities in model
sel = mod.selection # Current selection
#Make an array copy of selection
arr_target = sel.to_a
#Pass the array into the mthod:
spin(arr_target)
You are rotating about an axis [0,0,1] aka Z_AXIS
But the groupās z axis might not be the same.
?
You can get the groupās transformation z axis and use that instead⦠axis = group.transformation.zaxis
??
But your code appears to rotate a selection which can include many thingsā¦
So the z axis should be that of the ācontextā.
So something like axis = sel[0].parent.transformation.zaxis might be appropriateā¦
???
I think you miss what i want. I want to rotate subgroup about an global Axis. That happen when i still in main group. But when i out from main group contex, subgroup rotated incorrectly. That all make me think what are all different from active_contex. Like as @DanRathbun say. Sel collection is one off them.
There are no
sel[0].parent.transformation.zaxis = nil
That become error.
it must :
sel[0].parent.instances[0].transformation.zaxis if !se[0].parent.is_a?(Sketcthup::Model)
class FloatUpAnimation
def nextFrame(view)
mod = Sketchup.active_model # Open model
ent = mod.entities # All entities in model
sel = mod.selection # Current selection
arr_target = sel.to_a
arr_target.each{|e|
if e != nil
step = 10
vec= Z_AXIS
tran = e.transformation
rot = Geom::Transformation.rotation(e.bounds.center, vec, (step).degrees)
e.move! rot*tran
view.refresh
end
}
end
end
# This adds an item to the Camera menu to activate our custom animation.
UI.menu("Camera").add_item("Run Float Up Animation") {
Sketchup.active_model.active_view.animation = FloatUpAnimation.new
}
Well, it depends upon the number of steps (ie, the smaller the angle, the slower the rotation).
You also need to do a little as possible in the next_frame() method. Do as much calculation ONCE in the classā initialize method and assign results to @instance_variables that can be accessed from the next_frame() method.
The file contents for those not wanting to download the Ruby file ... (click here)
# encoding: UTF-8
module Seklik # top-level namespace module
module RotateSelection
@default_step ||= 10 # angle in degrees
@default_spin ||= 1 # number of rotations
# A custom abstract animation class. (Abstract classes have no API superclass
# from which they inherit specific functionality. They will be like any other
# standalone Ruby class whose ancestors are: Object, Kernel, & BasicObject.)
class Animation
# The initialize method is automatically called by Ruby from a class'
# ::new() constructor method after it instantiates the new instance.
# So this method is evaluated within the new instance object. It receives
# any arguments that were passed into the class constructor method.
def initialize(
sel, # current selection object
arr, # the array copy of all selected objects
targets, # transformable objects from current selection
step = 10,
rotations = 1
)
@sel = sel
@arr = arr
@targets = targets
@step = step
@stop = rotations * 360
angle = step.degrees
# A hash to hold the object rotational transforms:
@rotate = {}
# Load the @rotate hash with transformations:
@targets.each { |e|
@rotate[e]= Geom::Transformation.rotation(
e.bounds.center, Z_AXIS, angle
)
}
@position = 0
@sel.clear
# next_frame() gets called here ONCE after the view animation object is
# instantiated by the View#animation=() method call.
end
def nextFrame(view)
@targets.each { |e|
e.move!( @rotate[e] * e.transformation )
}
# Determine whether we've spun enough:
@position += @step
# Return false to stop the animation:
if @position >= @stop
@sel.add(@arr) # restore the original selection
return false
else
# Show the next frame:
view.show_frame
return true
end
end
end # class Animation
# The method encapsulating the animation command. Commands are best
# wrapped up within a method so that the code can be reloaded during
# development which redefines the method if any changes were made.
# Blocks for UI menu items and commands cannot be redefined at runtime
# for security reasons. So this is why the block only calls this method.
def self.rotate_selection
model = Sketchup.active_model # reference the active model
sel = model.selection # Current selection
arr = sel.to_a
targets = arr.select { |e| e.respond_to?(:transformation) }
unless targets.empty?
prompts = ["Step Angle (degrees)", "Total Rotations"]
defaults = [@default_step, @default_spin]
input = UI.inputbox(prompts, defaults, "Rotate Selection")
return unless input
step = input[0]
rots = input[1]
@default_step = step if @default_step != step
@default_spin = rots if @default_spin != rots
view = model.active_view
view.animation= RotateSelection::Animation.new(sel, arr, targets, step, rots)
else
UI.messagebox("No objects selected.")
end
end
unless defined?(@loaded)
# Adds an item to the Camera menu to activate our custom animation:
UI.menu("Camera").add_item("Rotate Selection Animation") {
rotate_selection()
}
@loaded = true
end
end # extension submodule
end # namespace module
Thank alot @DanRathbun, that is awesome!! great job.
i must lear more your code to make it just stop animation if object(group/component) transformation same before animation.
Finally, i finish learn your code @DanRathbun
There are 9 step to do rotate animation
1.stop_animasi
2.get_old_targets
3.get_pos_origin
5.validation_old_target
6.set_new_targets
7.set_pos_origin
8.set_speed == set_step
9.start_animation
I need advice, what is wrong or better way. this is my code
I donāt know, what exactly is the speed of rotation.
increasing the angle, it looks unstable in Sketchup
module SklikAnimation # top-level namespace module
module RotateSelection
@default_step ||= 1 # angle in degrees
class Animation
def set_old_targets=(targets)
@targets = targets
end
def set_pos_origin=(pos_origin)
@pos_origin = pos_origin
end
def get_old_targets
@targets
end
def get_pos_origin
@pos_origin
end
def initialize(targets,pos_origin,step)
@targets = targets
@pos_origin = pos_origin
@step = step
angle = step.degrees
# A hash to hold the object rotational transforms:
@rotate = {}
# Load the @rotate hash with transformations:
@targets.each {|e|
@rotate[e]= Geom::Transformation.rotation(e.bounds.center, Z_AXIS, angle)
}
end
def nextFrame(view)
@targets.each {|e|
e.move!( @rotate[e] * e.transformation ) if e.valid?
}
view.show_frame
return true
end
end # class Animation
def self.restore_position
model = Sketchup.active_model # reference the active model
view = model.active_view
#stop_animasi
view.animation = nil
#get old_target
old_target = []
old_target = @animation_rotation.get_old_targets if @animation_rotation != nil
#get pos_origin
pos_origin = []
pos_origin = @animation_rotation.get_pos_origin if @animation_rotation != nil
#restore_all_target
old_target.each_index{|i| old_target[i].move! pos_origin[i] if old_target[i].valid? }
view.invalidate
#validation_old_target
old_target = old_target.select { |e| e.valid? }
return old_target
end
def self.start_rotation
model = Sketchup.active_model # reference the active model
view = model.active_view
sel = model.selection # Current selection
arr = sel.to_a.select { |e| e.respond_to?(:transformation) }
sel.clear
unless arr.empty?
#stop_animasi,get old_target,get pos_origin,restore_all_target,validation_old_target
old_target = restore_position()
#set_new_targets
targets = arr + old_target
targets = targets.uniq
#set_pos_origin
pos_origin = []
targets.each{|e| pos_origin << e.transformation}
#set_speed == set_step
step = 1
#start_animation
@animation_rotation = RotateSelection::Animation.new(targets,pos_origin,step)
view.animation = @animation_rotation
else
UI.messagebox("No objects selected.")
end
end
def self.stop_rotation
model = Sketchup.active_model # reference the active model
view = model.active_view
sel = model.selection # Current selection
arr = sel.to_a.select { |e| e.respond_to?(:transformation) }
sel.clear
unless arr.empty?
#stop_animasi,get old_target,get pos_origin,restore_all_target,validation_old_target
old_target = restore_position()
#set_new_targets
targets = old_target
arr.each{|e| targets.delete(e)}
#set_pos_origin
pos_origin = []
targets.each{|e| pos_origin << e.transformation}
#set_speed == set_step
step = 1
#start_animation
@animation_rotation = RotateSelection::Animation.new(targets,pos_origin,step)
view.animation = @animation_rotation
else
UI.messagebox("No objects selected.")
end
end
def self.stop_all_rotation
restore_position()
@animation_rotation.set_old_targets = [] if @animation_rotation != nil
@animation_rotation.set_pos_origin = [] if @animation_rotation != nil
end
def self.set_speed
model = Sketchup.active_model # reference the active model
view = model.active_view
prompts = ["Step Angle (degrees)"]
defaults = [@default_step]
input = UI.inputbox(prompts, defaults, "Set Speed Rotation")
if input
#set_speed == set_step
step = input[0]
@default_step = step if @default_step != step
#stop_animasi,get old_target,get pos_origin,restore_all_target,validation_old_target
old_target = restore_position()
#set_new_targets
targets = old_target
#set_pos_origin
pos_origin = []
targets.each{|e| pos_origin << e.transformation}
#start_animation
@animation_rotation = RotateSelection::Animation.new(targets,pos_origin,step)
view.animation = @animation_rotation
end
end
unless defined?(@loaded)
# Adds an item to the Camera menu to activate our custom animation:
UI.menu("Camera").add_item("Start Rotate Selection") {start_rotation()}
UI.menu("Camera").add_item("Stop Rotate Selection") {stop_rotation()}
UI.menu("Camera").add_item("Stop All Rotation") {stop_all_rotation()}
UI.menu("Camera").add_item("Set Speed Rotation") {set_speed()}
@loaded = true
end
end # extension submodule
end # namespace module
But I really think it is easier if you have a conditional expression within the next_frame() callback method to cause the animation to stop by returning false, even if it is only checking a @continue variable set elsewhere in the code.